From 4286d7942bfcc80099284ca96131c2d2f2711b14 Mon Sep 17 00:00:00 2001 From: emo-coder <122784380+emo-coder@users.noreply.github.com> Date: Mon, 31 Jul 2023 16:12:02 +0800 Subject: [PATCH 01/63] refactor: rm boost (#3397) --- src/sdk/file_option_parser.h | 9 ++++--- src/sdk/mini_cluster_batch_bm.cc | 10 +++---- src/sdk/mini_cluster_bm.cc | 11 ++++---- src/sdk/sql_cluster_router.cc | 5 ++-- src/sdk/sql_cluster_test.cc | 9 ++++--- src/sdk/sql_sdk_base_test.cc | 45 ++++++++++++++++---------------- 6 files changed, 47 insertions(+), 42 deletions(-) diff --git a/src/sdk/file_option_parser.h b/src/sdk/file_option_parser.h index 87602764288..7080c7e9c7c 100644 --- a/src/sdk/file_option_parser.h +++ b/src/sdk/file_option_parser.h @@ -22,6 +22,7 @@ #include #include +#include "absl/strings/ascii.h" #include "base/status.h" #include "node/node_manager.h" @@ -44,7 +45,7 @@ class FileOptionsParser { ::openmldb::base::Status Parse(const std::shared_ptr& options_map) { for (const auto& item : *options_map) { std::string key = item.first; - boost::to_lower(key); + absl::AsciiStrToLower(&key); auto pair = check_map_.find(key); if (pair == check_map_.end()) { return {openmldb::base::kSQLCmdRunError, "this option " + key + " is not currently supported"}; @@ -102,7 +103,7 @@ class FileOptionsParser { std::function CheckFormat() { return [this](const hybridse::node::ConstNode* node) { format_ = node->GetAsString(); - boost::to_lower(format_); + absl::AsciiStrToLower(&format_); if (format_ != "csv" && format_ != "parquet") { return false; } @@ -149,7 +150,7 @@ class FileOptionsParser { std::function CheckMode() { return [this](const hybridse::node::ConstNode* node) { mode_ = node->GetAsString(); - boost::to_lower(mode_); + absl::AsciiStrToLower(&mode_); if (mode_ != "error_if_exists" && mode_ != "overwrite" && mode_ != "append") { return false; } @@ -180,7 +181,7 @@ class ReadFileOptionsParser : public FileOptionsParser { std::function CheckLoadMode() { return [this](const hybridse::node::ConstNode* node) { load_mode_ = node->GetAsString(); - boost::to_lower(load_mode_); + absl::AsciiStrToLower(&load_mode_); if (load_mode_ != "local" && load_mode_ != "cluster") { return false; } diff --git a/src/sdk/mini_cluster_batch_bm.cc b/src/sdk/mini_cluster_batch_bm.cc index 3132e9b88d2..1b5227f3367 100644 --- a/src/sdk/mini_cluster_batch_bm.cc +++ b/src/sdk/mini_cluster_batch_bm.cc @@ -17,8 +17,8 @@ #include #include +#include "absl/strings/str_replace.h" #include "benchmark/benchmark.h" -#include "boost/algorithm/string.hpp" #include "codec/fe_row_codec.h" #include "schema/schema_adapter.h" #include "sdk/base.h" @@ -690,7 +690,7 @@ FROM {0} last join {1} order by {1}.x7 on {0}.c1 = {1}.x1 and {0}.c7 - {ts_diff} >= {1}.x7 last join {2} order by {2}.x7 on {0}.c2 = {2}.x2 and {0}.c7 - {ts_diff} >= {2}.x7; )"); - boost::replace_all(sql_case.sql_str_, "{ts_diff}", std::to_string(state.range(0) * 1000 / 2)); + absl::StrReplaceAll({{"{ts_diff}", std::to_string(state.range(0) * 1000 / 2)}}, &sql_case.sql_str_); BM_RequestQuery(state, sql_case, mc); } static void BM_SimpleLastJoinTable4(benchmark::State& state) { // NOLINT @@ -705,7 +705,7 @@ last join {2} order by {2}.x7 on {0}.c2 = {2}.x2 and {0}.c7 - {ts_diff} >= {2}.x last join {3} order by {3}.x7 on {0}.c3 = {3}.x3 and {0}.c7 - {ts_diff} >= {3}.x7 last join {4} order by {4}.x7 on {0}.c4 = {4}.x4 and {0}.c7 - {ts_diff} >= {4}.x7; )"; - boost::replace_all(sql_case.sql_str_, "{ts_diff}", std::to_string(state.range(0) * 1000 / 2)); + absl::StrReplaceAll({{"{ts_diff}", std::to_string(state.range(0) * 1000 / 2)}}, &sql_case.sql_str_); BM_RequestQuery(state, sql_case, mc); } @@ -731,7 +731,7 @@ window w1 as (PARTITION BY {0}.c1 ORDER BY {0}.c7 ROWS_RANGE BETWEEN 10d PRECEDI last join {1} as t2 order by t2.x7 on c2 = t2.x2 and c7 - {ts_diff} >= t2.x7 ; )"; - boost::replace_all(sql_case.sql_str_, "{ts_diff}", std::to_string(state.range(0) * 1000 / 2)); + absl::StrReplaceAll({{"{ts_diff}", std::to_string(state.range(0) * 1000 / 2)}}, &sql_case.sql_str_); BM_RequestQuery(state, sql_case, mc); } static void BM_SimpleWindowOutputLastJoinTable4(benchmark::State& state) { // NOLINT @@ -757,7 +757,7 @@ static void BM_SimpleWindowOutputLastJoinTable4(benchmark::State& state) { // N last join {1} as t3 order by t3.x7 on c3 = t3.x3 and c7 - {ts_diff} >= t3.x7 last join {1} as t4 order by t4.x7 on c4 = t4.x4 and c7 - {ts_diff} >= t4.x7; )"; - boost::replace_all(sql_case.sql_str_, "{ts_diff}", std::to_string(state.range(0) * 1000 / 2)); + absl::StrReplaceAll({{"{ts_diff}", std::to_string(state.range(0) * 1000 / 2)}}, &sql_case.sql_str_); BM_RequestQuery(state, sql_case, mc); } diff --git a/src/sdk/mini_cluster_bm.cc b/src/sdk/mini_cluster_bm.cc index 24d50076a97..7f771ed3e3c 100644 --- a/src/sdk/mini_cluster_bm.cc +++ b/src/sdk/mini_cluster_bm.cc @@ -19,7 +19,8 @@ #include #include -#include "boost/algorithm/string.hpp" +#include "absl/strings/ascii.h" +#include "absl/strings/str_replace.h" #include "codec/fe_row_codec.h" #include "sdk/base.h" #include "sdk/sql_router.h" @@ -58,9 +59,9 @@ void BM_RequestQuery(benchmark::State& state, hybridse::sqlcase::SqlCase& sql_ca std::string sql = sql_case.sql_str(); for (size_t i = 0; i < sql_case.inputs().size(); i++) { std::string placeholder = "{" + std::to_string(i) + "}"; - boost::replace_all(sql, placeholder, sql_case.inputs()[i].name_); + absl::StrReplaceAll({{placeholder, sql_case.inputs()[i].name_}}, &sql); } - boost::to_lower(sql); + absl::AsciiStrToLower(&sql); LOG(INFO) << sql; auto request_row = router->GetRequestRow(sql_case.db(), sql, &status); if (status.code != 0) { @@ -224,9 +225,9 @@ void BM_BatchRequestQuery(benchmark::State& state, hybridse::sqlcase::SqlCase& s std::string sql = sql_case.sql_str(); for (size_t i = 0; i < sql_case.inputs().size(); i++) { std::string placeholder = "{" + std::to_string(i) + "}"; - boost::replace_all(sql, placeholder, sql_case.inputs()[i].name_); + absl::StrReplaceAll({{placeholder, sql_case.inputs()[i].name_}}, &sql); } - boost::to_lower(sql); + absl::AsciiStrToLower(&sql); LOG(INFO) << sql; auto request_row = router->GetRequestRow(sql_case.db(), sql, &status); if (status.code != 0) { diff --git a/src/sdk/sql_cluster_router.cc b/src/sdk/sql_cluster_router.cc index 2c7f473d6eb..3c768151aa2 100644 --- a/src/sdk/sql_cluster_router.cc +++ b/src/sdk/sql_cluster_router.cc @@ -27,6 +27,7 @@ #include "absl/strings/ascii.h" #include "absl/strings/numbers.h" #include "absl/strings/str_cat.h" +#include "absl/strings/str_split.h" #include "absl/strings/strip.h" #include "absl/strings/substitute.h" #include "absl/types/span.h" @@ -3649,10 +3650,10 @@ hybridse::sdk::Status SQLClusterRouter::HandleLongWindows( std::string base_db = table_pair.begin()->first; std::string base_table = table_pair.begin()->second; std::vector windows; - boost::split(windows, long_window_param, boost::is_any_of(",")); + windows = absl::StrSplit(long_window_param, ","); for (auto& window : windows) { std::vector window_info; - boost::split(window_info, window, boost::is_any_of(":")); + window_info = absl::StrSplit(window, ":"); if (window_info.size() == 2) { long_window_map[window_info[0]] = window_info[1]; diff --git a/src/sdk/sql_cluster_test.cc b/src/sdk/sql_cluster_test.cc index 6d794692846..b63f26feaf0 100644 --- a/src/sdk/sql_cluster_test.cc +++ b/src/sdk/sql_cluster_test.cc @@ -20,6 +20,7 @@ #include #include +#include "absl/strings/match.h" #include "absl/strings/str_cat.h" #include "base/glog_wrapper.h" #include "codec/fe_row_codec.h" @@ -883,10 +884,10 @@ TEST_P(SQLSDKBatchRequestQueryTest, SqlSdkDistributeBatchRequestSinglePartitionT /* TEST_P(SQLSDKQueryTest, sql_sdk_distribute_request_single_partition_test) { auto sql_case = GetParam(); LOG(INFO) << "ID: " << sql_case.id() << ", DESC: " << sql_case.desc(); - if (boost::contains(sql_case.mode(), "rtidb-unsupport") || - boost::contains(sql_case.mode(), "rtidb-request-unsupport") || - boost::contains(sql_case.mode(), "request-unsupport") || - boost::contains(sql_case.mode(), "cluster-unsupport")) { + if (absl::StrContains(sql_case.mode(), "rtidb-unsupport") || + absl::StrContains(sql_case.mode(), "rtidb-request-unsupport") || + absl::StrContains(sql_case.mode(), "request-unsupport") || + absl::StrContains(sql_case.mode(), "cluster-unsupport")) { LOG(WARNING) << "Unsupport mode: " << sql_case.mode(); return; } diff --git a/src/sdk/sql_sdk_base_test.cc b/src/sdk/sql_sdk_base_test.cc index d50eb440e46..5d715f37fc4 100644 --- a/src/sdk/sql_sdk_base_test.cc +++ b/src/sdk/sql_sdk_base_test.cc @@ -70,7 +70,7 @@ void SQLSDKTest::CreateTables(hybridse::sqlcase::SqlCase& sql_case, // NOLINT std::string create; if (sql_case.BuildCreateSqlFromInput(i, &create, partition_num) && !create.empty()) { std::string placeholder = "{" + std::to_string(i) + "}"; - boost::replace_all(create, placeholder, sql_case.inputs()[i].name_); + absl::StrReplaceAll({{placeholder, sql_case.inputs()[i].name_}}, &create); LOG(INFO) << create; router->ExecuteDDL(input_db_name, create, &status); ASSERT_TRUE(router->RefreshCatalog()); @@ -112,12 +112,12 @@ void SQLSDKTest::CreateProcedure(hybridse::sqlcase::SqlCase& sql_case, // NOLIN std::string sql = sql_case.sql_str(); for (size_t i = 0; i < sql_case.inputs().size(); i++) { std::string placeholder = "{" + std::to_string(i) + "}"; - boost::replace_all(sql, placeholder, sql_case.inputs()[i].name_); + absl::StrReplaceAll({{placeholder, sql_case.inputs()[i].name_}}, &sql); } - boost::replace_all( - sql, "{auto}", - hybridse::sqlcase::SqlCase::GenRand("auto_t") + std::to_string(static_cast(time(NULL)))); - boost::trim(sql); + absl::StrReplaceAll( + {{"{auto}", + hybridse::sqlcase::SqlCase::GenRand("auto_t") + std::to_string(static_cast(time(NULL)))}}, &sql); + absl::RemoveExtraAsciiWhitespace(&sql); LOG(INFO) << sql; sql_case.sp_name_ = hybridse::sqlcase::SqlCase::GenRand("auto_sp") + std::to_string(static_cast(time(NULL))); @@ -139,7 +139,7 @@ void SQLSDKTest::CreateProcedure(hybridse::sqlcase::SqlCase& sql_case, // NOLIN for (size_t i = 0; i < sql_case.inputs_.size(); i++) { std::string placeholder = "{" + std::to_string(i) + "}"; - boost::replace_all(create_sp, placeholder, sql_case.inputs()[i].name_); + absl::StrReplaceAll({{placeholder, sql_case.inputs()[i].name_}}, &create_sp); } LOG(INFO) << create_sp; if (!create_sp.empty()) { @@ -220,7 +220,7 @@ void SQLSDKTest::InsertTables(hybridse::sqlcase::SqlCase& sql_case, // NOLINT } auto insert = inserts[row_idx]; std::string placeholder = "{" + std::to_string(i) + "}"; - boost::replace_all(insert, placeholder, sql_case.inputs()[i].name_); + absl::StrReplaceAll({{placeholder, sql_case.inputs()[i].name_}}, &insert); DLOG(INFO) << insert; if (!insert.empty()) { for (int j = 0; j < sql_case.inputs()[i].repeat_; j++) { @@ -294,15 +294,16 @@ void SQLSDKTest::BatchExecuteSQL(hybridse::sqlcase::SqlCase& sql_case, // NOLIN std::string sql = sql_case.sql_str(); for (size_t i = 0; i < sql_case.inputs().size(); i++) { std::string placeholder = "{" + std::to_string(i) + "}"; - boost::replace_all(sql, placeholder, sql_case.inputs()[i].name_); + absl::StrReplaceAll({{placeholder, sql_case.inputs()[i].name_}}, &sql); } - boost::replace_all( - sql, "{auto}", - hybridse::sqlcase::SqlCase::GenRand("auto_t") + std::to_string(static_cast(time(NULL)))); + absl::StrReplaceAll( + {{"{auto}", + hybridse::sqlcase::SqlCase::GenRand("auto_t") + std::to_string(static_cast(time(NULL)))}}, &sql); for (size_t endpoint_id = 0; endpoint_id < tbEndpoints.size(); endpoint_id++) { - boost::replace_all(sql, "{tb_endpoint_" + std::to_string(endpoint_id) + "}", tbEndpoints.at(endpoint_id)); + absl::StrReplaceAll({{"{tb_endpoint_" + std::to_string(endpoint_id) + "}", tbEndpoints.at(endpoint_id)}}, &sql); } - std::string lower_sql = boost::to_lower_copy(sql); + std::string lower_sql = sql; + absl::AsciiStrToLower(&lower_sql); if (absl::StartsWith(lower_sql, "select") || absl::StartsWith(lower_sql, "with")) { std::shared_ptr rs; // parameterized batch query @@ -381,12 +382,12 @@ void SQLSDKQueryTest::RequestExecuteSQL(hybridse::sqlcase::SqlCase& sql_case, / std::string sql = sql_case.sql_str(); for (size_t i = 0; i < sql_case.inputs().size(); i++) { std::string placeholder = "{" + std::to_string(i) + "}"; - boost::replace_all(sql, placeholder, sql_case.inputs()[i].name_); + absl::StrReplaceAll({{placeholder, sql_case.inputs()[i].name_}}, &sql); } - boost::replace_all(sql, "{auto}", hybridse::sqlcase::SqlCase::GenRand("auto_t") + - std::to_string(static_cast(time(NULL)))); + absl::StrReplaceAll({{"{auto}", hybridse::sqlcase::SqlCase::GenRand("auto_t") + + std::to_string(static_cast(time(NULL)))}}, &sql); std::string lower_sql = sql; - boost::to_lower(lower_sql); + absl::AsciiStrToLower(&lower_sql); if (absl::StartsWith(lower_sql, "select") || absl::StartsWith(lower_sql, "with")) { auto request_row = router->GetRequestRow(sql_case.db(), sql, &status); // success check @@ -526,13 +527,13 @@ void SQLSDKQueryTest::BatchRequestExecuteSQLWithCommonColumnIndices(hybridse::sq std::string sql = sql_case.sql_str(); for (size_t i = 0; i < sql_case.inputs().size(); i++) { std::string placeholder = "{" + std::to_string(i) + "}"; - boost::replace_all(sql, placeholder, sql_case.inputs()[i].name_); + absl::StrReplaceAll({{placeholder, sql_case.inputs()[i].name_}}, &sql); } - boost::replace_all(sql, "{auto}", hybridse::sqlcase::SqlCase::GenRand("auto_t") + - std::to_string(static_cast(time(NULL)))); + absl::StrReplaceAll({{"{auto}", hybridse::sqlcase::SqlCase::GenRand("auto_t") + + std::to_string(static_cast(time(NULL)))}}, &sql); LOG(INFO) << sql; std::string lower_sql = sql; - boost::to_lower(lower_sql); + absl::AsciiStrToLower(&lower_sql); if (!(absl::StartsWith(lower_sql, "select") || absl::StartsWith(lower_sql, "with"))) { FAIL() << "sql not support in request mode"; } From 7fd58aed011d1a06dc5f701e2481b07800f3868c Mon Sep 17 00:00:00 2001 From: aceforeverd Date: Thu, 3 Aug 2023 19:44:46 +0800 Subject: [PATCH 02/63] feat(udf): json functions (#3414) --- CMakeLists.txt | 3 + CPPLINT.cfg | 2 +- contrib/CMakeLists.txt | 28 + contrib/simdjson/CMakeLists.txt | 10 + contrib/simdjson/README | 1 + contrib/simdjson/simdjson.cpp | 16585 +++++++++ contrib/simdjson/simdjson.h | 32088 ++++++++++++++++++ hybridse/src/CMakeLists.txt | 3 +- hybridse/src/codegen/udf_ir_builder_test.cc | 10 + hybridse/src/udf/default_defs/json_defs.cc | 77 + hybridse/src/udf/default_udf_library.cc | 1 + hybridse/src/udf/default_udf_library.h | 2 + 12 files changed, 48808 insertions(+), 2 deletions(-) create mode 100644 contrib/CMakeLists.txt create mode 100644 contrib/simdjson/CMakeLists.txt create mode 100644 contrib/simdjson/README create mode 100644 contrib/simdjson/simdjson.cpp create mode 100644 contrib/simdjson/simdjson.h create mode 100644 hybridse/src/udf/default_defs/json_defs.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index 394ca89e329..62ff8443380 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -137,6 +137,9 @@ include(FetchContent) set(FETCHCONTENT_QUIET OFF) include(farmhash) +# contrib libs +add_subdirectory(contrib EXCLUDE_FROM_ALL) + if (CMAKE_SYSTEM_NAME STREQUAL "Linux") set(OS_LIB ${CMAKE_THREAD_LIBS_INIT} rt) set(BRPC_LIBS ${BRPC_LIBRARY} ${Protobuf_LIBRARIES} ${GLOG_LIBRARY} ${GFLAGS_LIBRARY} ${UNWIND_LIBRARY} ${OPENSSL_LIBRARIES} ${LEVELDB_LIBRARY} ${Z_LIBRARY} ${SNAPPY_LIBRARY} dl pthread ${OS_LIB}) diff --git a/CPPLINT.cfg b/CPPLINT.cfg index 47ceb0d5347..be7f74a1e2d 100644 --- a/CPPLINT.cfg +++ b/CPPLINT.cfg @@ -1,2 +1,2 @@ linelength=120 -filter=-build/c++11 +filter=-build/c++11,-build/include_subdir diff --git a/contrib/CMakeLists.txt b/contrib/CMakeLists.txt new file mode 100644 index 00000000000..78ce52e026b --- /dev/null +++ b/contrib/CMakeLists.txt @@ -0,0 +1,28 @@ +set_property(DIRECTORY PROPERTY EXCLUDE_FROM_ALL 1) + +# add_contrib cmake_folder[ base_folder1[, ...base_folderN]] +# or add_contrib base_folder +function(add_contrib cmake_folder) + if (ARGN) + set(base_folders ${ARGN}) + else() + set(base_folders ${cmake_folder}) + endif() + + foreach (base_folder ${base_folders}) + if (NOT IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${base_folder}") + message(FATAL_ERROR "No such base folder '${base_folder}' (for '${cmake_folder}' cmake folder). Typo in the base folder name?") + endif() + + file(GLOB contrib_files "${base_folder}/*") + if (NOT contrib_files) + message(FATAL_ERROR "submodule ${base_folder} is missing or empty.") + endif() + endforeach() + + message(STATUS "Adding contrib module ${base_folders} (configuring with ${cmake_folder})") + add_subdirectory (${cmake_folder}) +endfunction() + +add_contrib(simdjson) + diff --git a/contrib/simdjson/CMakeLists.txt b/contrib/simdjson/CMakeLists.txt new file mode 100644 index 00000000000..c626e2ec863 --- /dev/null +++ b/contrib/simdjson/CMakeLists.txt @@ -0,0 +1,10 @@ +add_library(_simdjson ${CMAKE_CURRENT_SOURCE_DIR}/simdjson.cpp) +target_include_directories(_simdjson SYSTEM PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/") + +# simdjson is using its own CPU dispatching and get confused if we enable AVX/AVX2 flags. +if(ARCH_AMD64) + target_compile_options(_simdjson PRIVATE -mno-avx -mno-avx2) +endif() + +add_library(op_contrib::simdjson ALIAS _simdjson) + diff --git a/contrib/simdjson/README b/contrib/simdjson/README new file mode 100644 index 00000000000..16007e4600b --- /dev/null +++ b/contrib/simdjson/README @@ -0,0 +1 @@ +smidjson v3.2.1: https://github.com/simdjson/simdjson/releases/tag/v3.2.1 diff --git a/contrib/simdjson/simdjson.cpp b/contrib/simdjson/simdjson.cpp new file mode 100644 index 00000000000..bb265297e21 --- /dev/null +++ b/contrib/simdjson/simdjson.cpp @@ -0,0 +1,16585 @@ +/* auto-generated on 2023-07-06 21:34:14 -0400. Do not edit! */ +/* begin file src/simdjson.cpp */ +#include "simdjson.h" + +SIMDJSON_PUSH_DISABLE_WARNINGS +SIMDJSON_DISABLE_UNDESIRED_WARNINGS + +/* begin file src/to_chars.cpp */ +#include +#include +#include +#include + +namespace simdjson { +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 { + +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; +} + +struct diyfp // f * 2^e +{ + static constexpr int kPrecision = 64; // = q + + std::uint64_t f = 0; + int e = 0; + + 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 { + + 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"); + + // 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 + + 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_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; + + // 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 + + const std::uint64_t h = p3 + p2_hi + p1_hi + (Q >> 32u); + + 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 { + + while ((x.f >> 63u) == 0) { + x.f <<= 1u; + x.e--; + } + + 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; + + return {x.f << delta, target_exponent}; + } +}; + +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) { + + // 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"); + + 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; + + 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); + + // 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) + + // 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); + + 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 + +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; +}; + +/*! +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. + + 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}, + }}; + + // 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)) / + kCachedPowersDecStep; + + const cached_power cached = kCachedPowers[static_cast(index)]; + + 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; + } +} + +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. + + // 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; + } +} + +/*! +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+]. + + 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 + + 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 + + // 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); + + // 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 + // + + // 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; + + // 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; + } + + 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 + + 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; + } + } + + // V = buffer * 10^-m, with M- <= V <= 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); + + // 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) { + + // --------(-----------------------+-----------------------)-------- (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 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); + + // ----(---+---)---------------(---+---)---------------(---+---)---- + // 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 + + 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"); + + // 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)); +#else + const boundaries w = compute_boundaries(value); +#endif + + 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) { + + if (e < 0) { + e = -e; + *buf++ = '-'; + } else { + *buf++ = '+'; + } + + 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); + } + + 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) { + + 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. + + 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; + } + + 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 + + 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 + + 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); + } + + *buf++ = 'e'; + return append_exponent(buf, n - 1); +} + +} // 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++ = '-'; + } + + 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 +} // namespace simdjson +/* end file src/to_chars.cpp */ +/* begin file src/from_chars.cpp */ +#include +namespace simdjson { +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. + **/ + +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 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 <> 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::sign_index() { return 63; } + +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; + } + + 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; + } + + 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 { + +// 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; +} + +} // 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; +} + +// 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); +} + +// 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; + + 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; + } + + 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); + + 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, 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, 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 +} // simdjson +/* end file src/from_chars.cpp */ +/* begin file src/internal/error_tables.cpp */ + +namespace simdjson { +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" }, + { 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[] + +} // namespace internal +} // namespace simdjson +/* end file src/internal/error_tables.cpp */ +/* begin file src/internal/jsoncharutils_tables.cpp */ + +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 + +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, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 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 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 simdjson +/* end file src/internal/jsoncharutils_tables.cpp */ +/* begin file src/internal/numberparsing_tables.cpp */ + +namespace simdjson { +namespace internal { + +// Precomputed powers of ten from 10^0 to 10^22. These +// can be represented exactly using the double type. +SIMDJSON_DLLIMPORTEXPORT const double 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 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,}; + +} // namespace internal +} // namespace simdjson +/* end file src/internal/numberparsing_tables.cpp */ +/* begin file src/internal/simdprune_tables.cpp */ +#if SIMDJSON_IMPLEMENTATION_ARM64 || SIMDJSON_IMPLEMENTATION_ICELAKE || SIMDJSON_IMPLEMENTATION_HASWELL || SIMDJSON_IMPLEMENTATION_WESTMERE || SIMDJSON_IMPLEMENTATION_PPC64 + +#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}; + +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] + +} // namespace internal +} // namespace simdjson + +#endif // SIMDJSON_IMPLEMENTATION_ARM64 || SIMDJSON_IMPLEMENTATION_ICELAKE || SIMDJSON_IMPLEMENTATION_HASWELL || SIMDJSON_IMPLEMENTATION_WESTMERE || SIMDJSON_IMPLEMENTATION_PPC64 +/* end file src/internal/simdprune_tables.cpp */ +/* begin file src/implementation.cpp */ +#include + +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); +} + +namespace internal { + +// Static array of known implementations. We're hoping these get baked into the executable +// without requiring a static initializer. + +#if SIMDJSON_IMPLEMENTATION_ICELAKE +static const icelake::implementation* get_icelake_singleton() { + static const icelake::implementation icelake_singleton{}; + return &icelake_singleton; +} +#endif +#if SIMDJSON_IMPLEMENTATION_HASWELL +static const haswell::implementation* get_haswell_singleton() { + static const haswell::implementation haswell_singleton{}; + return &haswell_singleton; +} +#endif +#if SIMDJSON_IMPLEMENTATION_WESTMERE +static const westmere::implementation* get_westmere_singleton() { + static const westmere::implementation westmere_singleton{}; + return &westmere_singleton; +} +#endif // SIMDJSON_IMPLEMENTATION_WESTMERE +#if SIMDJSON_IMPLEMENTATION_ARM64 +static const arm64::implementation* get_arm64_singleton() { + static const arm64::implementation arm64_singleton{}; + return &arm64_singleton; +} +#endif // SIMDJSON_IMPLEMENTATION_ARM64 +#if SIMDJSON_IMPLEMENTATION_PPC64 +static const ppc64::implementation* get_ppc64_singleton() { + static const ppc64::implementation ppc64_singleton{}; + return &ppc64_singleton; +} +#endif // SIMDJSON_IMPLEMENTATION_PPC64 +#if SIMDJSON_IMPLEMENTATION_FALLBACK +static const fallback::implementation* get_fallback_singleton() { + static const fallback::implementation fallback_singleton{}; + return &fallback_singleton; +} +#endif // SIMDJSON_IMPLEMENTATION_FALLBACK + +/** + * @private Detects best supported implementation on first use, and sets it + */ +class detect_best_supported_implementation_on_first_use final : public implementation { +public: + const std::string &name() const noexcept final { return set_best()->name(); } + const 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 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_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) {} +}; + +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? +} + +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; +} + +SIMDJSON_DLLIMPORTEXPORT internal::atomic_ptr& get_active_implementation() { + static const internal::detect_best_supported_implementation_on_first_use detect_best_supported_implementation_on_first_use_singleton; + static internal::atomic_ptr active_implementation{&detect_best_supported_implementation_on_first_use_singleton}; + return active_implementation; +} + +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 +/* end file src/implementation.cpp */ + +#if SIMDJSON_IMPLEMENTATION_ARM64 +/* begin file src/arm64/implementation.cpp */ +/* begin file include/simdjson/arm64/begin.h */ +// redefining SIMDJSON_IMPLEMENTATION to "arm64" +// #define SIMDJSON_IMPLEMENTATION arm64 +/* end file include/simdjson/arm64/begin.h */ + +namespace simdjson { +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; +} + +} // namespace arm64 +} // namespace simdjson + +/* begin file include/simdjson/arm64/end.h */ +/* end file include/simdjson/arm64/end.h */ +/* end file src/arm64/implementation.cpp */ +/* begin file src/arm64/dom_parser_implementation.cpp */ +/* begin file include/simdjson/arm64/begin.h */ +// redefining SIMDJSON_IMPLEMENTATION to "arm64" +// #define SIMDJSON_IMPLEMENTATION arm64 +/* end file include/simdjson/arm64/begin.h */ + +// +// Stage 1 +// +namespace simdjson { +namespace arm64 { +namespace { + +using namespace simd; + +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; +}; + +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) + ); + + + // 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 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 }; +} + +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_inline simd8 must_be_2_3_continuation(const simd8 prev2, const simd8 prev3) { + simd8 is_third_byte = prev2 >= uint8_t(0xe0u); + simd8 is_fourth_byte = prev3 >= uint8_t(0xf0u); + return is_third_byte ^ is_fourth_byte; +} + +} // unnamed namespace +} // namespace arm64 +} // namespace simdjson + +/* begin file src/generic/stage1/utf8_lookup4_algorithm.h */ +namespace simdjson { +namespace arm64 { +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 = simd8(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 + }; +#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; + } + +#ifndef SIMDJSON_IF_CONSTEXPR +#if SIMDJSON_CPLUSPLUS17 +#define SIMDJSON_IF_CONSTEXPR if constexpr +#else +#define SIMDJSON_IF_CONSTEXPR if +#endif +#endif + + 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]); + } 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; + } + + }; // struct utf8_checker +} // namespace utf8_validation + +using utf8_validation::utf8_checker; + +} // unnamed namespace +} // namespace arm64 +} // namespace simdjson +/* end file src/generic/stage1/utf8_lookup4_algorithm.h */ +/* begin file src/generic/stage1/json_structural_indexer.h */ +// 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) + +/* begin file src/generic/stage1/buf_block_reader.h */ +namespace simdjson { +namespace arm64 { +namespace { + +// 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_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; +} + +} // unnamed namespace +} // namespace arm64 +} // namespace simdjson +/* end file src/generic/stage1/buf_block_reader.h */ +/* begin file src/generic/stage1/json_string_scanner.h */ +namespace simdjson { +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_inline json_string_block(uint64_t backslash, uint64_t escaped, uint64_t quote, uint64_t in_string) : + _backslash(backslash), _escaped(escaped), _quote(quote), _in_string(in_string) {} + + // Escaped characters (characters following an escape() character) + simdjson_inline uint64_t escaped() const { return _escaped; } + // Escape characters (backslashes that are not escaped--i.e. in \\, includes only the first \) + simdjson_inline uint64_t escape() const { return _backslash & ~_escaped; } + // Real (non-backslashed) quotes + simdjson_inline uint64_t quote() const { return _quote; } + // Start quotes of strings + simdjson_inline uint64_t string_start() const { return _quote & _in_string; } + // End quotes of strings + simdjson_inline uint64_t string_end() const { return _quote & ~_in_string; } + // Only characters inside the string (not including the quotes) + simdjson_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_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_inline uint64_t non_quote_outside_string(uint64_t mask) const { return mask & ~_in_string; } + // Tail of string (everything except the start quote) + simdjson_inline uint64_t string_tail() const { return _in_string ^ _quote; } + + // backslash characters + uint64_t _backslash; + // escaped characters (backslashed--does not include the hex characters after \u) + uint64_t _escaped; + // real quotes (non-backslashed 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_inline json_string_block next(const simd::simd8x64& in); + // Returns either UNCLOSED_STRING or SUCCESS + simdjson_inline error_code finish(); + +private: + // Intended to be defined by the implementation + simdjson_inline uint64_t find_escaped(uint64_t escape); + simdjson_inline uint64_t find_escaped_branchless(uint64_t escape); + + // Whether the last iteration was still inside a string (all 1's = true, all 0's = false). + uint64_t prev_in_string = 0ULL; + // Whether the first character of the next iteration is escaped. + uint64_t prev_escaped = 0ULL; +}; + +// +// Finds escaped characters (characters following \). +// +// Handles runs of backslashes like \\\" and \\\\" correctly (yielding 0101 and 01010, respectively). +// +// Does this by: +// - Shift the escape mask to get potentially escaped characters (characters after backslashes). +// - Mask escaped sequences that start on *even* bits with 1010101010 (odd bits are escaped, even bits are not) +// - Mask escaped sequences that start on *odd* bits with 0101010101 (even bits are escaped, odd bits are not) +// +// To distinguish between escaped sequences starting on even/odd bits, it finds the start of all +// escape sequences, filters out the ones that start on even bits, and adds that to the mask of +// escape sequences. This causes the addition to clear out the sequences starting on odd bits (since +// the start bit causes a carry), and leaves even-bit sequences alone. +// +// Example: +// +// text | \\\ | \\\"\\\" \\\" \\"\\" | +// escape | xxx | xx xxx xxx xx xx | Removed overflow backslash; will | it into follows_escape +// odd_starts | x | x x x | escape & ~even_bits & ~follows_escape +// even_seq | c| cxxx c xx c | c = carry bit -- will be masked out later +// invert_mask | | cxxx c xx c| even_seq << 1 +// follows_escape | xx | x xx xxx xxx xx xx | Includes overflow bit +// escaped | x | x x x x x x x x | +// desired | x | x x x x x x x x | +// text | \\\ | \\\"\\\" \\\" \\"\\" | +// +simdjson_inline uint64_t json_string_scanner::find_escaped_branchless(uint64_t backslash) { + // If there was overflow, pretend the first character isn't a backslash + backslash &= ~prev_escaped; + uint64_t follows_escape = backslash << 1 | prev_escaped; + + // Get sequences starting on even bits by clearing out the odd series using + + const uint64_t even_bits = 0x5555555555555555ULL; + uint64_t odd_sequence_starts = backslash & ~even_bits & ~follows_escape; + uint64_t sequences_starting_on_even_bits; + prev_escaped = add_overflow(odd_sequence_starts, backslash, &sequences_starting_on_even_bits); + uint64_t invert_mask = sequences_starting_on_even_bits << 1; // The mask we want to return is the *escaped* bits, not escapes. + + // Mask every other backslashed character as an escaped character + // Flip the mask for sequences that start on even bits, to correct them + return (even_bits ^ invert_mask) & follows_escape; +} + +// +// 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_inline json_string_block json_string_scanner::next(const simd::simd8x64& in) { + const uint64_t backslash = in.eq('\\'); + const uint64_t escaped = find_escaped(backslash); + 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 + // + // right shift of a signed value expected to be well-defined and standard + // compliant as of C++20, John Regher from Utah U. says this is fine code + // + 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( + backslash, + escaped, + quote, + in_string + ); +} + +simdjson_inline error_code json_string_scanner::finish() { + if (prev_in_string) { + return UNCLOSED_STRING; + } + return SUCCESS; +} + +} // namespace stage1 +} // unnamed namespace +} // namespace arm64 +} // namespace simdjson +/* end file src/generic/stage1/json_string_scanner.h */ +/* begin file src/generic/stage1/json_scanner.h */ +namespace simdjson { +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) {} + + /** + * 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_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_inline error_code json_scanner::finish() { + return string_scanner.finish(); +} + +} // namespace stage1 +} // unnamed namespace +} // namespace arm64 +} // namespace simdjson +/* end file src/generic/stage1/json_scanner.h */ +/* begin file src/generic/stage1/json_minifier.h */ +// 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 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; + +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; +}; + +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; +} + +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 arm64 +} // namespace simdjson +/* end file src/generic/stage1/json_minifier.h */ +/* begin file src/generic/stage1/find_next_document_index.h */ +namespace simdjson { +namespace arm64 { +namespace { + +/** + * 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; +} + +} // unnamed namespace +} // namespace arm64 +} // namespace simdjson +/* end file src/generic/stage1/find_next_document_index.h */ + +namespace simdjson { +namespace arm64 { +namespace { +namespace stage1 { + +class bit_indexer { +public: + uint32_t *tail; + + simdjson_inline bit_indexer(uint32_t *index_buf) : tail(index_buf) {} + + // 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_CUSTOM_BIT_INDEXER, then it will provide its own + // version of the code. +#ifdef SIMDJSON_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; +#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. + */ + + uint64_t rev_bits = reverse_bits(bits); + int cnt = static_cast(count_ones(bits)); + int i = 0; + // Do the first 8 all together + for (; i<8; i++) { + int lz = leading_zeroes(rev_bits); + this->tail[i] = static_cast(idx) + lz; + rev_bits = zero_leading_bit(rev_bits, lz); + } + // Do the next 8 all together (we hope in most cases it won't happen at all + // and the branch is easily predicted). + if (simdjson_unlikely(cnt > 8)) { + i = 8; + for (; i<16; i++) { + int lz = leading_zeroes(rev_bits); + this->tail[i] = static_cast(idx) + lz; + rev_bits = zero_leading_bit(rev_bits, lz); + } + + + // Most files don't have 16+ structurals per block, so we take several basically guaranteed + // branch mispredictions here. 16+ structurals per block means either punctuation ({} [] , :) + // or the start of a value ("abc" true 123) every four characters. + if (simdjson_unlikely(cnt > 16)) { + i = 16; + while (rev_bits != 0) { + int lz = leading_zeroes(rev_bits); + this->tail[i++] = static_cast(idx) + lz; + rev_bits = zero_leading_bit(rev_bits, lz); + } + } + } + this->tail += cnt; +#else // SIMDJSON_PREFER_REVERSE_BITS + /** + * 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. + */ + + int cnt = static_cast(count_ones(bits)); + // Do the first 8 all together + for (int i=0; i<8; i++) { + this->tail[i] = idx + trailing_zeroes(bits); + bits = clear_lowest_bit(bits); + } + + // Do the next 8 all together (we hope in most cases it won't happen at all + // and the branch is easily predicted). + if (simdjson_unlikely(cnt > 8)) { + for (int i=8; i<16; i++) { + this->tail[i] = idx + trailing_zeroes(bits); + bits = clear_lowest_bit(bits); + } + + // Most files don't have 16+ structurals per block, so we take several basically guaranteed + // branch mispredictions here. 16+ structurals per block means either punctuation ({} [] , :) + // or the start of a value ("abc" true 123) every four characters. + if (simdjson_unlikely(cnt > 16)) { + int i = 16; + do { + this->tail[i] = idx + trailing_zeroes(bits); + bits = clear_lowest_bit(bits); + i++; + } while (i < cnt); + } + } + + this->tail += cnt; +#endif + } +#endif // SIMDJSON_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_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 doesn't 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 arm64 +} // namespace simdjson +/* end file src/generic/stage1/json_structural_indexer.h */ +/* begin file src/generic/stage1/utf8_validator.h */ +namespace simdjson { +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; +} + +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 simdjson +/* end file src/generic/stage1/utf8_validator.h */ + +// +// Stage 2 +// + +/* begin file src/generic/stage2/stringparsing.h */ +// This file contains the common code every implementation uses +// It is intended to be included multiple times and compiled multiple times + +namespace simdjson { +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, + + 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 isn't 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 isn't 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_nullptrptr 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; + } + } + /* can't be reached */ + return nullptr; +} + +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; + } + } + /* can't be reached */ + return nullptr; +} + +} // namespace stringparsing +} // unnamed namespace +} // namespace arm64 +} // namespace simdjson +/* end file src/generic/stage2/stringparsing.h */ +/* begin file src/generic/stage2/tape_builder.h */ +/* begin file src/generic/stage2/json_iterator.h */ +/* begin file src/generic/stage2/logger.h */ +// 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 { + + 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 + 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 { + switch (*value) { + case '"': return visitor.visit_string(*this, 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); + case '-': + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + return visitor.visit_number(*this, value); + default: + log_error("Non-value found when value was expected!"); + return TAPE_ERROR; + } +} + +} // namespace stage2 +} // unnamed namespace +} // namespace arm64 +} // namespace simdjson +/* end file src/generic/stage2/json_iterator.h */ +/* begin file src/generic/stage2/tape_writer.h */ +namespace simdjson { +namespace arm64 { +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 number_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 arm64 +} // namespace simdjson +/* end file src/generic/stage2/tape_writer.h */ + +namespace simdjson { +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; + + /** 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; +}; // class 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 arm64 +} // namespace simdjson +/* end file src/generic/stage2/tape_builder.h */ + +// +// Implementation-specific overrides +// +namespace simdjson { +namespace arm64 { +namespace { +namespace stage1 { + +simdjson_inline uint64_t json_string_scanner::find_escaped(uint64_t backslash) { + // On ARM, we don't short-circuit this if there are no backslashes, because the branch gives us no + // benefit and therefore makes things worse. + // if (!backslash) { uint64_t escaped = prev_escaped; prev_escaped = 0; return escaped; } + return find_escaped_branchless(backslash); +} + +} // namespace stage1 +} // unnamed namespace + +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 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_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_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); +} + +} // namespace arm64 +} // namespace simdjson + +/* begin file include/simdjson/arm64/end.h */ +/* end file include/simdjson/arm64/end.h */ +/* end file src/arm64/dom_parser_implementation.cpp */ +#endif +#if SIMDJSON_IMPLEMENTATION_FALLBACK +/* begin file src/fallback/implementation.cpp */ +/* begin file include/simdjson/fallback/begin.h */ +// redefining SIMDJSON_IMPLEMENTATION to "fallback" +// #define SIMDJSON_IMPLEMENTATION fallback +/* end file include/simdjson/fallback/begin.h */ +namespace simdjson { +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) 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 fallback +} // namespace simdjson + +/* begin file include/simdjson/fallback/end.h */ +/* end file include/simdjson/fallback/end.h */ +/* end file src/fallback/implementation.cpp */ +/* begin file src/fallback/dom_parser_implementation.cpp */ +/* begin file include/simdjson/fallback/begin.h */ +// redefining SIMDJSON_IMPLEMENTATION to "fallback" +// #define SIMDJSON_IMPLEMENTATION fallback +/* end file include/simdjson/fallback/begin.h */ + +// +// Stage 1 +// +/* begin file src/generic/stage1/find_next_document_index.h */ +namespace simdjson { +namespace fallback { +namespace { + +/** + * 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; +} + +} // unnamed namespace +} // namespace fallback +} // namespace simdjson +/* end file src/generic/stage1/find_next_document_index.h */ + +namespace simdjson { +namespace fallback { +namespace { +namespace stage1 { + +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 void add_structural() { + *next_structural_index = idx; + next_structural_index++; +} + +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; + } + + // 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; + } + + // 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; +} + +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; + } +} + +// +// Parse the entire input in STEP_SIZE-byte chunks. +// +simdjson_inline error_code scan() { + bool unclosed_string = false; + for (;idx 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; +} + +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 + +} // namespace stage1 +} // unnamed namespace + +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(); +} + +// 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 + +// +// Stage 2 +// +/* begin file src/generic/stage2/stringparsing.h */ +// This file contains the common code every implementation uses +// It is intended to be included multiple times and compiled multiple times + +namespace simdjson { +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, + + 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 isn't 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 isn't 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_nullptrptr 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; + } + } + /* can't be reached */ + return nullptr; +} + +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; + } + } + /* can't be reached */ + return nullptr; +} + +} // namespace stringparsing +} // unnamed namespace +} // namespace fallback +} // namespace simdjson +/* end file src/generic/stage2/stringparsing.h */ +/* begin file src/generic/stage2/tape_builder.h */ +/* begin file src/generic/stage2/json_iterator.h */ +/* begin file src/generic/stage2/logger.h */ +// 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 { + + 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 + 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 { + switch (*value) { + case '"': return visitor.visit_string(*this, 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); + case '-': + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + return visitor.visit_number(*this, value); + default: + log_error("Non-value found when value was expected!"); + return TAPE_ERROR; + } +} + +} // namespace stage2 +} // unnamed namespace +} // namespace fallback +} // namespace simdjson +/* end file src/generic/stage2/json_iterator.h */ +/* begin file src/generic/stage2/tape_writer.h */ +namespace simdjson { +namespace fallback { +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 number_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 fallback +} // namespace simdjson +/* end file src/generic/stage2/tape_writer.h */ + +namespace simdjson { +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; + + /** 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; +}; // class 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 fallback +} // namespace simdjson +/* end file src/generic/stage2/tape_builder.h */ + +namespace simdjson { +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_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_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); +} + +} // namespace fallback +} // namespace simdjson + +/* begin file include/simdjson/fallback/end.h */ +/* end file include/simdjson/fallback/end.h */ +/* end file src/fallback/dom_parser_implementation.cpp */ +#endif +#if SIMDJSON_IMPLEMENTATION_ICELAKE +/* begin file src/icelake/implementation.cpp */ +/* begin file include/simdjson/icelake/begin.h */ +// redefining SIMDJSON_IMPLEMENTATION to "icelake" +// #define SIMDJSON_IMPLEMENTATION icelake +SIMDJSON_TARGET_ICELAKE +/* end file include/simdjson/icelake/begin.h */ + +namespace simdjson { +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; +} + +} // namespace icelake +} // namespace simdjson + +/* begin file include/simdjson/icelake/end.h */ +SIMDJSON_UNTARGET_ICELAKE +/* end file include/simdjson/icelake/end.h */ + +/* end file src/icelake/implementation.cpp */ +/* begin file src/icelake/dom_parser_implementation.cpp */ +/* begin file include/simdjson/icelake/begin.h */ +// redefining SIMDJSON_IMPLEMENTATION to "icelake" +// #define SIMDJSON_IMPLEMENTATION icelake +SIMDJSON_TARGET_ICELAKE +/* end file include/simdjson/icelake/begin.h */ + +// +// Stage 1 +// + +namespace simdjson { +namespace icelake { +namespace { + +using namespace simd; + +struct json_character_block { + static simdjson_inline json_character_block classify(const simd::simd8x64& in); + // ASCII white-space ('\r','\n','\t',' ') + simdjson_inline uint64_t whitespace() const noexcept; + // non-quote structural characters (comma, colon, braces, brackets) + simdjson_inline uint64_t op() const noexcept; + // neither a structural character nor a white-space, so letters, numbers and quotes + simdjson_inline uint64_t scalar() const noexcept; + + uint64_t _whitespace; // ASCII white-space ('\r','\n','\t',' ') + uint64_t _op; // structural characters (comma, colon, braces, brackets but not quotes) +}; + +simdjson_inline uint64_t json_character_block::whitespace() const noexcept { return _whitespace; } +simdjson_inline uint64_t json_character_block::op() const noexcept { return _op; } +simdjson_inline uint64_t json_character_block::scalar() const noexcept { return ~(op() | whitespace()); } + +// 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 + ); + + // 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]) + }); + + return { whitespace, op }; +} + +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_inline simd8 must_be_2_3_continuation(const simd8 prev2, const simd8 prev3) { + 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_third_byte | is_fourth_byte) > int8_t(0); +} + +} // unnamed namespace +} // namespace icelake +} // namespace simdjson + +/* begin file src/generic/stage1/utf8_lookup4_algorithm.h */ +namespace simdjson { +namespace icelake { +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 = simd8(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 + }; +#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; + } + +#ifndef SIMDJSON_IF_CONSTEXPR +#if SIMDJSON_CPLUSPLUS17 +#define SIMDJSON_IF_CONSTEXPR if constexpr +#else +#define SIMDJSON_IF_CONSTEXPR if +#endif +#endif + + 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]); + } 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; + } + + }; // struct utf8_checker +} // namespace utf8_validation + +using utf8_validation::utf8_checker; + +} // unnamed namespace +} // namespace icelake +} // namespace simdjson +/* end file src/generic/stage1/utf8_lookup4_algorithm.h */ +// defining SIMDJSON_CUSTOM_BIT_INDEXER allows us to provide our own bit_indexer::write +#define SIMDJSON_CUSTOM_BIT_INDEXER +/* begin file src/generic/stage1/json_structural_indexer.h */ +// 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) + +/* begin file src/generic/stage1/buf_block_reader.h */ +namespace simdjson { +namespace icelake { +namespace { + +// 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_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; +} + +} // unnamed namespace +} // namespace icelake +} // namespace simdjson +/* end file src/generic/stage1/buf_block_reader.h */ +/* begin file src/generic/stage1/json_string_scanner.h */ +namespace simdjson { +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_inline json_string_block(uint64_t backslash, uint64_t escaped, uint64_t quote, uint64_t in_string) : + _backslash(backslash), _escaped(escaped), _quote(quote), _in_string(in_string) {} + + // Escaped characters (characters following an escape() character) + simdjson_inline uint64_t escaped() const { return _escaped; } + // Escape characters (backslashes that are not escaped--i.e. in \\, includes only the first \) + simdjson_inline uint64_t escape() const { return _backslash & ~_escaped; } + // Real (non-backslashed) quotes + simdjson_inline uint64_t quote() const { return _quote; } + // Start quotes of strings + simdjson_inline uint64_t string_start() const { return _quote & _in_string; } + // End quotes of strings + simdjson_inline uint64_t string_end() const { return _quote & ~_in_string; } + // Only characters inside the string (not including the quotes) + simdjson_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_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_inline uint64_t non_quote_outside_string(uint64_t mask) const { return mask & ~_in_string; } + // Tail of string (everything except the start quote) + simdjson_inline uint64_t string_tail() const { return _in_string ^ _quote; } + + // backslash characters + uint64_t _backslash; + // escaped characters (backslashed--does not include the hex characters after \u) + uint64_t _escaped; + // real quotes (non-backslashed 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_inline json_string_block next(const simd::simd8x64& in); + // Returns either UNCLOSED_STRING or SUCCESS + simdjson_inline error_code finish(); + +private: + // Intended to be defined by the implementation + simdjson_inline uint64_t find_escaped(uint64_t escape); + simdjson_inline uint64_t find_escaped_branchless(uint64_t escape); + + // Whether the last iteration was still inside a string (all 1's = true, all 0's = false). + uint64_t prev_in_string = 0ULL; + // Whether the first character of the next iteration is escaped. + uint64_t prev_escaped = 0ULL; +}; + +// +// Finds escaped characters (characters following \). +// +// Handles runs of backslashes like \\\" and \\\\" correctly (yielding 0101 and 01010, respectively). +// +// Does this by: +// - Shift the escape mask to get potentially escaped characters (characters after backslashes). +// - Mask escaped sequences that start on *even* bits with 1010101010 (odd bits are escaped, even bits are not) +// - Mask escaped sequences that start on *odd* bits with 0101010101 (even bits are escaped, odd bits are not) +// +// To distinguish between escaped sequences starting on even/odd bits, it finds the start of all +// escape sequences, filters out the ones that start on even bits, and adds that to the mask of +// escape sequences. This causes the addition to clear out the sequences starting on odd bits (since +// the start bit causes a carry), and leaves even-bit sequences alone. +// +// Example: +// +// text | \\\ | \\\"\\\" \\\" \\"\\" | +// escape | xxx | xx xxx xxx xx xx | Removed overflow backslash; will | it into follows_escape +// odd_starts | x | x x x | escape & ~even_bits & ~follows_escape +// even_seq | c| cxxx c xx c | c = carry bit -- will be masked out later +// invert_mask | | cxxx c xx c| even_seq << 1 +// follows_escape | xx | x xx xxx xxx xx xx | Includes overflow bit +// escaped | x | x x x x x x x x | +// desired | x | x x x x x x x x | +// text | \\\ | \\\"\\\" \\\" \\"\\" | +// +simdjson_inline uint64_t json_string_scanner::find_escaped_branchless(uint64_t backslash) { + // If there was overflow, pretend the first character isn't a backslash + backslash &= ~prev_escaped; + uint64_t follows_escape = backslash << 1 | prev_escaped; + + // Get sequences starting on even bits by clearing out the odd series using + + const uint64_t even_bits = 0x5555555555555555ULL; + uint64_t odd_sequence_starts = backslash & ~even_bits & ~follows_escape; + uint64_t sequences_starting_on_even_bits; + prev_escaped = add_overflow(odd_sequence_starts, backslash, &sequences_starting_on_even_bits); + uint64_t invert_mask = sequences_starting_on_even_bits << 1; // The mask we want to return is the *escaped* bits, not escapes. + + // Mask every other backslashed character as an escaped character + // Flip the mask for sequences that start on even bits, to correct them + return (even_bits ^ invert_mask) & follows_escape; +} + +// +// 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_inline json_string_block json_string_scanner::next(const simd::simd8x64& in) { + const uint64_t backslash = in.eq('\\'); + const uint64_t escaped = find_escaped(backslash); + 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 + // + // right shift of a signed value expected to be well-defined and standard + // compliant as of C++20, John Regher from Utah U. says this is fine code + // + 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( + backslash, + escaped, + quote, + in_string + ); +} + +simdjson_inline error_code json_string_scanner::finish() { + if (prev_in_string) { + return UNCLOSED_STRING; + } + return SUCCESS; +} + +} // namespace stage1 +} // unnamed namespace +} // namespace icelake +} // namespace simdjson +/* end file src/generic/stage1/json_string_scanner.h */ +/* begin file src/generic/stage1/json_scanner.h */ +namespace simdjson { +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) {} + + /** + * 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_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_inline error_code json_scanner::finish() { + return string_scanner.finish(); +} + +} // namespace stage1 +} // unnamed namespace +} // namespace icelake +} // namespace simdjson +/* end file src/generic/stage1/json_scanner.h */ +/* begin file src/generic/stage1/json_minifier.h */ +// 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 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; + +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; +}; + +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; +} + +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 icelake +} // namespace simdjson +/* end file src/generic/stage1/json_minifier.h */ +/* begin file src/generic/stage1/find_next_document_index.h */ +namespace simdjson { +namespace icelake { +namespace { + +/** + * 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; +} + +} // unnamed namespace +} // namespace icelake +} // namespace simdjson +/* end file src/generic/stage1/find_next_document_index.h */ + +namespace simdjson { +namespace icelake { +namespace { +namespace stage1 { + +class bit_indexer { +public: + uint32_t *tail; + + simdjson_inline bit_indexer(uint32_t *index_buf) : tail(index_buf) {} + + // 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_CUSTOM_BIT_INDEXER, then it will provide its own + // version of the code. +#ifdef SIMDJSON_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; +#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. + */ + + uint64_t rev_bits = reverse_bits(bits); + int cnt = static_cast(count_ones(bits)); + int i = 0; + // Do the first 8 all together + for (; i<8; i++) { + int lz = leading_zeroes(rev_bits); + this->tail[i] = static_cast(idx) + lz; + rev_bits = zero_leading_bit(rev_bits, lz); + } + // Do the next 8 all together (we hope in most cases it won't happen at all + // and the branch is easily predicted). + if (simdjson_unlikely(cnt > 8)) { + i = 8; + for (; i<16; i++) { + int lz = leading_zeroes(rev_bits); + this->tail[i] = static_cast(idx) + lz; + rev_bits = zero_leading_bit(rev_bits, lz); + } + + + // Most files don't have 16+ structurals per block, so we take several basically guaranteed + // branch mispredictions here. 16+ structurals per block means either punctuation ({} [] , :) + // or the start of a value ("abc" true 123) every four characters. + if (simdjson_unlikely(cnt > 16)) { + i = 16; + while (rev_bits != 0) { + int lz = leading_zeroes(rev_bits); + this->tail[i++] = static_cast(idx) + lz; + rev_bits = zero_leading_bit(rev_bits, lz); + } + } + } + this->tail += cnt; +#else // SIMDJSON_PREFER_REVERSE_BITS + /** + * 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. + */ + + int cnt = static_cast(count_ones(bits)); + // Do the first 8 all together + for (int i=0; i<8; i++) { + this->tail[i] = idx + trailing_zeroes(bits); + bits = clear_lowest_bit(bits); + } + + // Do the next 8 all together (we hope in most cases it won't happen at all + // and the branch is easily predicted). + if (simdjson_unlikely(cnt > 8)) { + for (int i=8; i<16; i++) { + this->tail[i] = idx + trailing_zeroes(bits); + bits = clear_lowest_bit(bits); + } + + // Most files don't have 16+ structurals per block, so we take several basically guaranteed + // branch mispredictions here. 16+ structurals per block means either punctuation ({} [] , :) + // or the start of a value ("abc" true 123) every four characters. + if (simdjson_unlikely(cnt > 16)) { + int i = 16; + do { + this->tail[i] = idx + trailing_zeroes(bits); + bits = clear_lowest_bit(bits); + i++; + } while (i < cnt); + } + } + + this->tail += cnt; +#endif + } +#endif // SIMDJSON_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_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 doesn't 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 icelake +} // namespace simdjson +/* end file src/generic/stage1/json_structural_indexer.h */ +// We must not forget to undefine it now: +#undef SIMDJSON_CUSTOM_BIT_INDEXER + +/** + * We provide a custom version of bit_indexer::write using + * 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. +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; } + + 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)); + + 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 + +/* begin file src/generic/stage1/utf8_validator.h */ +namespace simdjson { +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; +} + +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 simdjson +/* end file src/generic/stage1/utf8_validator.h */ + +// +// Stage 2 +// +/* begin file src/generic/stage2/stringparsing.h */ +// This file contains the common code every implementation uses +// It is intended to be included multiple times and compiled multiple times + +namespace simdjson { +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, + + 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 isn't 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 isn't 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_nullptrptr 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; + } + } + /* can't be reached */ + return nullptr; +} + +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; + } + } + /* can't be reached */ + return nullptr; +} + +} // namespace stringparsing +} // unnamed namespace +} // namespace icelake +} // namespace simdjson +/* end file src/generic/stage2/stringparsing.h */ +/* begin file src/generic/stage2/tape_builder.h */ +/* begin file src/generic/stage2/json_iterator.h */ +/* begin file src/generic/stage2/logger.h */ +// 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 { + + 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 + 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 { + switch (*value) { + case '"': return visitor.visit_string(*this, 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); + case '-': + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + return visitor.visit_number(*this, value); + default: + log_error("Non-value found when value was expected!"); + return TAPE_ERROR; + } +} + +} // namespace stage2 +} // unnamed namespace +} // namespace icelake +} // namespace simdjson +/* end file src/generic/stage2/json_iterator.h */ +/* begin file src/generic/stage2/tape_writer.h */ +namespace simdjson { +namespace icelake { +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 number_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 icelake +} // namespace simdjson +/* end file src/generic/stage2/tape_writer.h */ + +namespace simdjson { +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; + + /** 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; +}; // class 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 icelake +} // namespace simdjson +/* end file src/generic/stage2/tape_builder.h */ + +// +// Implementation-specific overrides +// +namespace simdjson { +namespace icelake { +namespace { +namespace stage1 { + +simdjson_inline uint64_t json_string_scanner::find_escaped(uint64_t backslash) { + if (!backslash) { uint64_t escaped = prev_escaped; prev_escaped = 0; return escaped; } + return find_escaped_branchless(backslash); +} + +} // namespace stage1 +} // unnamed namespace + +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 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_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_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); +} + +} // namespace icelake +} // namespace simdjson + +/* begin file include/simdjson/icelake/end.h */ +SIMDJSON_UNTARGET_ICELAKE +/* end file include/simdjson/icelake/end.h */ +/* end file src/icelake/dom_parser_implementation.cpp */ +#endif +#if SIMDJSON_IMPLEMENTATION_HASWELL +/* begin file src/haswell/implementation.cpp */ +/* begin file include/simdjson/haswell/begin.h */ +// redefining SIMDJSON_IMPLEMENTATION to "haswell" +// #define SIMDJSON_IMPLEMENTATION haswell +SIMDJSON_TARGET_HASWELL +/* end file include/simdjson/haswell/begin.h */ + +namespace simdjson { +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; +} + +} // namespace haswell +} // namespace simdjson + +/* begin file include/simdjson/haswell/end.h */ +SIMDJSON_UNTARGET_HASWELL +/* end file include/simdjson/haswell/end.h */ + +/* end file src/haswell/implementation.cpp */ +/* begin file src/haswell/dom_parser_implementation.cpp */ +/* begin file include/simdjson/haswell/begin.h */ +// redefining SIMDJSON_IMPLEMENTATION to "haswell" +// #define SIMDJSON_IMPLEMENTATION haswell +SIMDJSON_TARGET_HASWELL +/* end file include/simdjson/haswell/begin.h */ + +// +// Stage 1 +// + +namespace simdjson { +namespace haswell { +namespace { + +using namespace simd; + +struct json_character_block { + static simdjson_inline json_character_block classify(const simd::simd8x64& in); + // ASCII white-space ('\r','\n','\t',' ') + simdjson_inline uint64_t whitespace() const noexcept; + // non-quote structural characters (comma, colon, braces, brackets) + simdjson_inline uint64_t op() const noexcept; + // neither a structural character nor a white-space, so letters, numbers and quotes + simdjson_inline uint64_t scalar() const noexcept; + + uint64_t _whitespace; // ASCII white-space ('\r','\n','\t',' ') + uint64_t _op; // structural characters (comma, colon, braces, brackets but not quotes) +}; + +simdjson_inline uint64_t json_character_block::whitespace() const noexcept { return _whitespace; } +simdjson_inline uint64_t json_character_block::op() const noexcept { return _op; } +simdjson_inline uint64_t json_character_block::scalar() const noexcept { return ~(op() | whitespace()); } + +// 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 + ); + + // 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]) + }); + + return { whitespace, op }; +} + +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_inline simd8 must_be_2_3_continuation(const simd8 prev2, const simd8 prev3) { + 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_third_byte | is_fourth_byte) > int8_t(0); +} + +} // unnamed namespace +} // namespace haswell +} // namespace simdjson + +/* begin file src/generic/stage1/utf8_lookup4_algorithm.h */ +namespace simdjson { +namespace haswell { +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 = simd8(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 + }; +#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; + } + +#ifndef SIMDJSON_IF_CONSTEXPR +#if SIMDJSON_CPLUSPLUS17 +#define SIMDJSON_IF_CONSTEXPR if constexpr +#else +#define SIMDJSON_IF_CONSTEXPR if +#endif +#endif + + 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]); + } 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; + } + + }; // struct utf8_checker +} // namespace utf8_validation + +using utf8_validation::utf8_checker; + +} // unnamed namespace +} // namespace haswell +} // namespace simdjson +/* end file src/generic/stage1/utf8_lookup4_algorithm.h */ +/* begin file src/generic/stage1/json_structural_indexer.h */ +// 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) + +/* begin file src/generic/stage1/buf_block_reader.h */ +namespace simdjson { +namespace haswell { +namespace { + +// 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_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; +} + +} // unnamed namespace +} // namespace haswell +} // namespace simdjson +/* end file src/generic/stage1/buf_block_reader.h */ +/* begin file src/generic/stage1/json_string_scanner.h */ +namespace simdjson { +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_inline json_string_block(uint64_t backslash, uint64_t escaped, uint64_t quote, uint64_t in_string) : + _backslash(backslash), _escaped(escaped), _quote(quote), _in_string(in_string) {} + + // Escaped characters (characters following an escape() character) + simdjson_inline uint64_t escaped() const { return _escaped; } + // Escape characters (backslashes that are not escaped--i.e. in \\, includes only the first \) + simdjson_inline uint64_t escape() const { return _backslash & ~_escaped; } + // Real (non-backslashed) quotes + simdjson_inline uint64_t quote() const { return _quote; } + // Start quotes of strings + simdjson_inline uint64_t string_start() const { return _quote & _in_string; } + // End quotes of strings + simdjson_inline uint64_t string_end() const { return _quote & ~_in_string; } + // Only characters inside the string (not including the quotes) + simdjson_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_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_inline uint64_t non_quote_outside_string(uint64_t mask) const { return mask & ~_in_string; } + // Tail of string (everything except the start quote) + simdjson_inline uint64_t string_tail() const { return _in_string ^ _quote; } + + // backslash characters + uint64_t _backslash; + // escaped characters (backslashed--does not include the hex characters after \u) + uint64_t _escaped; + // real quotes (non-backslashed 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_inline json_string_block next(const simd::simd8x64& in); + // Returns either UNCLOSED_STRING or SUCCESS + simdjson_inline error_code finish(); + +private: + // Intended to be defined by the implementation + simdjson_inline uint64_t find_escaped(uint64_t escape); + simdjson_inline uint64_t find_escaped_branchless(uint64_t escape); + + // Whether the last iteration was still inside a string (all 1's = true, all 0's = false). + uint64_t prev_in_string = 0ULL; + // Whether the first character of the next iteration is escaped. + uint64_t prev_escaped = 0ULL; +}; + +// +// Finds escaped characters (characters following \). +// +// Handles runs of backslashes like \\\" and \\\\" correctly (yielding 0101 and 01010, respectively). +// +// Does this by: +// - Shift the escape mask to get potentially escaped characters (characters after backslashes). +// - Mask escaped sequences that start on *even* bits with 1010101010 (odd bits are escaped, even bits are not) +// - Mask escaped sequences that start on *odd* bits with 0101010101 (even bits are escaped, odd bits are not) +// +// To distinguish between escaped sequences starting on even/odd bits, it finds the start of all +// escape sequences, filters out the ones that start on even bits, and adds that to the mask of +// escape sequences. This causes the addition to clear out the sequences starting on odd bits (since +// the start bit causes a carry), and leaves even-bit sequences alone. +// +// Example: +// +// text | \\\ | \\\"\\\" \\\" \\"\\" | +// escape | xxx | xx xxx xxx xx xx | Removed overflow backslash; will | it into follows_escape +// odd_starts | x | x x x | escape & ~even_bits & ~follows_escape +// even_seq | c| cxxx c xx c | c = carry bit -- will be masked out later +// invert_mask | | cxxx c xx c| even_seq << 1 +// follows_escape | xx | x xx xxx xxx xx xx | Includes overflow bit +// escaped | x | x x x x x x x x | +// desired | x | x x x x x x x x | +// text | \\\ | \\\"\\\" \\\" \\"\\" | +// +simdjson_inline uint64_t json_string_scanner::find_escaped_branchless(uint64_t backslash) { + // If there was overflow, pretend the first character isn't a backslash + backslash &= ~prev_escaped; + uint64_t follows_escape = backslash << 1 | prev_escaped; + + // Get sequences starting on even bits by clearing out the odd series using + + const uint64_t even_bits = 0x5555555555555555ULL; + uint64_t odd_sequence_starts = backslash & ~even_bits & ~follows_escape; + uint64_t sequences_starting_on_even_bits; + prev_escaped = add_overflow(odd_sequence_starts, backslash, &sequences_starting_on_even_bits); + uint64_t invert_mask = sequences_starting_on_even_bits << 1; // The mask we want to return is the *escaped* bits, not escapes. + + // Mask every other backslashed character as an escaped character + // Flip the mask for sequences that start on even bits, to correct them + return (even_bits ^ invert_mask) & follows_escape; +} + +// +// 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_inline json_string_block json_string_scanner::next(const simd::simd8x64& in) { + const uint64_t backslash = in.eq('\\'); + const uint64_t escaped = find_escaped(backslash); + 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 + // + // right shift of a signed value expected to be well-defined and standard + // compliant as of C++20, John Regher from Utah U. says this is fine code + // + 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( + backslash, + escaped, + quote, + in_string + ); +} + +simdjson_inline error_code json_string_scanner::finish() { + if (prev_in_string) { + return UNCLOSED_STRING; + } + return SUCCESS; +} + +} // namespace stage1 +} // unnamed namespace +} // namespace haswell +} // namespace simdjson +/* end file src/generic/stage1/json_string_scanner.h */ +/* begin file src/generic/stage1/json_scanner.h */ +namespace simdjson { +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) {} + + /** + * 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_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_inline error_code json_scanner::finish() { + return string_scanner.finish(); +} + +} // namespace stage1 +} // unnamed namespace +} // namespace haswell +} // namespace simdjson +/* end file src/generic/stage1/json_scanner.h */ +/* begin file src/generic/stage1/json_minifier.h */ +// 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 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; + +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; +}; + +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; +} + +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 haswell +} // namespace simdjson +/* end file src/generic/stage1/json_minifier.h */ +/* begin file src/generic/stage1/find_next_document_index.h */ +namespace simdjson { +namespace haswell { +namespace { + +/** + * 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; +} + +} // unnamed namespace +} // namespace haswell +} // namespace simdjson +/* end file src/generic/stage1/find_next_document_index.h */ + +namespace simdjson { +namespace haswell { +namespace { +namespace stage1 { + +class bit_indexer { +public: + uint32_t *tail; + + simdjson_inline bit_indexer(uint32_t *index_buf) : tail(index_buf) {} + + // 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_CUSTOM_BIT_INDEXER, then it will provide its own + // version of the code. +#ifdef SIMDJSON_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; +#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. + */ + + uint64_t rev_bits = reverse_bits(bits); + int cnt = static_cast(count_ones(bits)); + int i = 0; + // Do the first 8 all together + for (; i<8; i++) { + int lz = leading_zeroes(rev_bits); + this->tail[i] = static_cast(idx) + lz; + rev_bits = zero_leading_bit(rev_bits, lz); + } + // Do the next 8 all together (we hope in most cases it won't happen at all + // and the branch is easily predicted). + if (simdjson_unlikely(cnt > 8)) { + i = 8; + for (; i<16; i++) { + int lz = leading_zeroes(rev_bits); + this->tail[i] = static_cast(idx) + lz; + rev_bits = zero_leading_bit(rev_bits, lz); + } + + + // Most files don't have 16+ structurals per block, so we take several basically guaranteed + // branch mispredictions here. 16+ structurals per block means either punctuation ({} [] , :) + // or the start of a value ("abc" true 123) every four characters. + if (simdjson_unlikely(cnt > 16)) { + i = 16; + while (rev_bits != 0) { + int lz = leading_zeroes(rev_bits); + this->tail[i++] = static_cast(idx) + lz; + rev_bits = zero_leading_bit(rev_bits, lz); + } + } + } + this->tail += cnt; +#else // SIMDJSON_PREFER_REVERSE_BITS + /** + * 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. + */ + + int cnt = static_cast(count_ones(bits)); + // Do the first 8 all together + for (int i=0; i<8; i++) { + this->tail[i] = idx + trailing_zeroes(bits); + bits = clear_lowest_bit(bits); + } + + // Do the next 8 all together (we hope in most cases it won't happen at all + // and the branch is easily predicted). + if (simdjson_unlikely(cnt > 8)) { + for (int i=8; i<16; i++) { + this->tail[i] = idx + trailing_zeroes(bits); + bits = clear_lowest_bit(bits); + } + + // Most files don't have 16+ structurals per block, so we take several basically guaranteed + // branch mispredictions here. 16+ structurals per block means either punctuation ({} [] , :) + // or the start of a value ("abc" true 123) every four characters. + if (simdjson_unlikely(cnt > 16)) { + int i = 16; + do { + this->tail[i] = idx + trailing_zeroes(bits); + bits = clear_lowest_bit(bits); + i++; + } while (i < cnt); + } + } + + this->tail += cnt; +#endif + } +#endif // SIMDJSON_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_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 doesn't 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 haswell +} // namespace simdjson +/* end file src/generic/stage1/json_structural_indexer.h */ +/* begin file src/generic/stage1/utf8_validator.h */ +namespace simdjson { +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; +} + +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 simdjson +/* end file src/generic/stage1/utf8_validator.h */ + +// +// Stage 2 +// +/* begin file src/generic/stage2/stringparsing.h */ +// This file contains the common code every implementation uses +// It is intended to be included multiple times and compiled multiple times + +namespace simdjson { +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, + + 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 isn't 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 isn't 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_nullptrptr 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; + } + } + /* can't be reached */ + return nullptr; +} + +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; + } + } + /* can't be reached */ + return nullptr; +} + +} // namespace stringparsing +} // unnamed namespace +} // namespace haswell +} // namespace simdjson +/* end file src/generic/stage2/stringparsing.h */ +/* begin file src/generic/stage2/tape_builder.h */ +/* begin file src/generic/stage2/json_iterator.h */ +/* begin file src/generic/stage2/logger.h */ +// 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 { + + 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 + 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 { + switch (*value) { + case '"': return visitor.visit_string(*this, 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); + case '-': + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + return visitor.visit_number(*this, value); + default: + log_error("Non-value found when value was expected!"); + return TAPE_ERROR; + } +} + +} // namespace stage2 +} // unnamed namespace +} // namespace haswell +} // namespace simdjson +/* end file src/generic/stage2/json_iterator.h */ +/* begin file src/generic/stage2/tape_writer.h */ +namespace simdjson { +namespace haswell { +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 number_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 haswell +} // namespace simdjson +/* end file src/generic/stage2/tape_writer.h */ + +namespace simdjson { +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; + + /** 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; +}; // class 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 haswell +} // namespace simdjson +/* end file src/generic/stage2/tape_builder.h */ + +// +// Implementation-specific overrides +// +namespace simdjson { +namespace haswell { +namespace { +namespace stage1 { + +simdjson_inline uint64_t json_string_scanner::find_escaped(uint64_t backslash) { + if (!backslash) { uint64_t escaped = prev_escaped; prev_escaped = 0; return escaped; } + return find_escaped_branchless(backslash); +} + +} // namespace stage1 +} // unnamed namespace + +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 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_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_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); +} + +} // namespace haswell +} // namespace simdjson + +/* begin file include/simdjson/haswell/end.h */ +SIMDJSON_UNTARGET_HASWELL +/* end file include/simdjson/haswell/end.h */ +/* end file src/haswell/dom_parser_implementation.cpp */ +#endif +#if SIMDJSON_IMPLEMENTATION_PPC64 +/* begin file src/ppc64/implementation.cpp */ +/* begin file include/simdjson/ppc64/begin.h */ +// redefining SIMDJSON_IMPLEMENTATION to "ppc64" +// #define SIMDJSON_IMPLEMENTATION ppc64 +/* end file include/simdjson/ppc64/begin.h */ + +namespace simdjson { +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; +} + +} // namespace ppc64 +} // namespace simdjson + +/* begin file include/simdjson/ppc64/end.h */ +/* end file include/simdjson/ppc64/end.h */ +/* end file src/ppc64/implementation.cpp */ +/* begin file src/ppc64/dom_parser_implementation.cpp */ +/* begin file include/simdjson/ppc64/begin.h */ +// redefining SIMDJSON_IMPLEMENTATION to "ppc64" +// #define SIMDJSON_IMPLEMENTATION ppc64 +/* end file include/simdjson/ppc64/begin.h */ + +// +// Stage 1 +// +namespace simdjson { +namespace ppc64 { +namespace { + +using namespace simd; + +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; +}; + +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) + ); + + 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(); + + 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_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-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_third_byte | is_fourth_byte) > int8_t(0); +} + +} // unnamed namespace +} // namespace ppc64 +} // namespace simdjson + +/* begin file src/generic/stage1/utf8_lookup4_algorithm.h */ +namespace simdjson { +namespace ppc64 { +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 = simd8(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 + }; +#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; + } + +#ifndef SIMDJSON_IF_CONSTEXPR +#if SIMDJSON_CPLUSPLUS17 +#define SIMDJSON_IF_CONSTEXPR if constexpr +#else +#define SIMDJSON_IF_CONSTEXPR if +#endif +#endif + + 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]); + } 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; + } + + }; // struct utf8_checker +} // namespace utf8_validation + +using utf8_validation::utf8_checker; + +} // unnamed namespace +} // namespace ppc64 +} // namespace simdjson +/* end file src/generic/stage1/utf8_lookup4_algorithm.h */ +/* begin file src/generic/stage1/json_structural_indexer.h */ +// 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) + +/* begin file src/generic/stage1/buf_block_reader.h */ +namespace simdjson { +namespace ppc64 { +namespace { + +// 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_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; +} + +} // unnamed namespace +} // namespace ppc64 +} // namespace simdjson +/* end file src/generic/stage1/buf_block_reader.h */ +/* begin file src/generic/stage1/json_string_scanner.h */ +namespace simdjson { +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_inline json_string_block(uint64_t backslash, uint64_t escaped, uint64_t quote, uint64_t in_string) : + _backslash(backslash), _escaped(escaped), _quote(quote), _in_string(in_string) {} + + // Escaped characters (characters following an escape() character) + simdjson_inline uint64_t escaped() const { return _escaped; } + // Escape characters (backslashes that are not escaped--i.e. in \\, includes only the first \) + simdjson_inline uint64_t escape() const { return _backslash & ~_escaped; } + // Real (non-backslashed) quotes + simdjson_inline uint64_t quote() const { return _quote; } + // Start quotes of strings + simdjson_inline uint64_t string_start() const { return _quote & _in_string; } + // End quotes of strings + simdjson_inline uint64_t string_end() const { return _quote & ~_in_string; } + // Only characters inside the string (not including the quotes) + simdjson_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_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_inline uint64_t non_quote_outside_string(uint64_t mask) const { return mask & ~_in_string; } + // Tail of string (everything except the start quote) + simdjson_inline uint64_t string_tail() const { return _in_string ^ _quote; } + + // backslash characters + uint64_t _backslash; + // escaped characters (backslashed--does not include the hex characters after \u) + uint64_t _escaped; + // real quotes (non-backslashed 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_inline json_string_block next(const simd::simd8x64& in); + // Returns either UNCLOSED_STRING or SUCCESS + simdjson_inline error_code finish(); + +private: + // Intended to be defined by the implementation + simdjson_inline uint64_t find_escaped(uint64_t escape); + simdjson_inline uint64_t find_escaped_branchless(uint64_t escape); + + // Whether the last iteration was still inside a string (all 1's = true, all 0's = false). + uint64_t prev_in_string = 0ULL; + // Whether the first character of the next iteration is escaped. + uint64_t prev_escaped = 0ULL; +}; + +// +// Finds escaped characters (characters following \). +// +// Handles runs of backslashes like \\\" and \\\\" correctly (yielding 0101 and 01010, respectively). +// +// Does this by: +// - Shift the escape mask to get potentially escaped characters (characters after backslashes). +// - Mask escaped sequences that start on *even* bits with 1010101010 (odd bits are escaped, even bits are not) +// - Mask escaped sequences that start on *odd* bits with 0101010101 (even bits are escaped, odd bits are not) +// +// To distinguish between escaped sequences starting on even/odd bits, it finds the start of all +// escape sequences, filters out the ones that start on even bits, and adds that to the mask of +// escape sequences. This causes the addition to clear out the sequences starting on odd bits (since +// the start bit causes a carry), and leaves even-bit sequences alone. +// +// Example: +// +// text | \\\ | \\\"\\\" \\\" \\"\\" | +// escape | xxx | xx xxx xxx xx xx | Removed overflow backslash; will | it into follows_escape +// odd_starts | x | x x x | escape & ~even_bits & ~follows_escape +// even_seq | c| cxxx c xx c | c = carry bit -- will be masked out later +// invert_mask | | cxxx c xx c| even_seq << 1 +// follows_escape | xx | x xx xxx xxx xx xx | Includes overflow bit +// escaped | x | x x x x x x x x | +// desired | x | x x x x x x x x | +// text | \\\ | \\\"\\\" \\\" \\"\\" | +// +simdjson_inline uint64_t json_string_scanner::find_escaped_branchless(uint64_t backslash) { + // If there was overflow, pretend the first character isn't a backslash + backslash &= ~prev_escaped; + uint64_t follows_escape = backslash << 1 | prev_escaped; + + // Get sequences starting on even bits by clearing out the odd series using + + const uint64_t even_bits = 0x5555555555555555ULL; + uint64_t odd_sequence_starts = backslash & ~even_bits & ~follows_escape; + uint64_t sequences_starting_on_even_bits; + prev_escaped = add_overflow(odd_sequence_starts, backslash, &sequences_starting_on_even_bits); + uint64_t invert_mask = sequences_starting_on_even_bits << 1; // The mask we want to return is the *escaped* bits, not escapes. + + // Mask every other backslashed character as an escaped character + // Flip the mask for sequences that start on even bits, to correct them + return (even_bits ^ invert_mask) & follows_escape; +} + +// +// 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_inline json_string_block json_string_scanner::next(const simd::simd8x64& in) { + const uint64_t backslash = in.eq('\\'); + const uint64_t escaped = find_escaped(backslash); + 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 + // + // right shift of a signed value expected to be well-defined and standard + // compliant as of C++20, John Regher from Utah U. says this is fine code + // + 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( + backslash, + escaped, + quote, + in_string + ); +} + +simdjson_inline error_code json_string_scanner::finish() { + if (prev_in_string) { + return UNCLOSED_STRING; + } + return SUCCESS; +} + +} // namespace stage1 +} // unnamed namespace +} // namespace ppc64 +} // namespace simdjson +/* end file src/generic/stage1/json_string_scanner.h */ +/* begin file src/generic/stage1/json_scanner.h */ +namespace simdjson { +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) {} + + /** + * 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_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_inline error_code json_scanner::finish() { + return string_scanner.finish(); +} + +} // namespace stage1 +} // unnamed namespace +} // namespace ppc64 +} // namespace simdjson +/* end file src/generic/stage1/json_scanner.h */ +/* begin file src/generic/stage1/json_minifier.h */ +// 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 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; + +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; +}; + +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; +} + +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 ppc64 +} // namespace simdjson +/* end file src/generic/stage1/json_minifier.h */ +/* begin file src/generic/stage1/find_next_document_index.h */ +namespace simdjson { +namespace ppc64 { +namespace { + +/** + * 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; +} + +} // unnamed namespace +} // namespace ppc64 +} // namespace simdjson +/* end file src/generic/stage1/find_next_document_index.h */ + +namespace simdjson { +namespace ppc64 { +namespace { +namespace stage1 { + +class bit_indexer { +public: + uint32_t *tail; + + simdjson_inline bit_indexer(uint32_t *index_buf) : tail(index_buf) {} + + // 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_CUSTOM_BIT_INDEXER, then it will provide its own + // version of the code. +#ifdef SIMDJSON_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; +#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. + */ + + uint64_t rev_bits = reverse_bits(bits); + int cnt = static_cast(count_ones(bits)); + int i = 0; + // Do the first 8 all together + for (; i<8; i++) { + int lz = leading_zeroes(rev_bits); + this->tail[i] = static_cast(idx) + lz; + rev_bits = zero_leading_bit(rev_bits, lz); + } + // Do the next 8 all together (we hope in most cases it won't happen at all + // and the branch is easily predicted). + if (simdjson_unlikely(cnt > 8)) { + i = 8; + for (; i<16; i++) { + int lz = leading_zeroes(rev_bits); + this->tail[i] = static_cast(idx) + lz; + rev_bits = zero_leading_bit(rev_bits, lz); + } + + + // Most files don't have 16+ structurals per block, so we take several basically guaranteed + // branch mispredictions here. 16+ structurals per block means either punctuation ({} [] , :) + // or the start of a value ("abc" true 123) every four characters. + if (simdjson_unlikely(cnt > 16)) { + i = 16; + while (rev_bits != 0) { + int lz = leading_zeroes(rev_bits); + this->tail[i++] = static_cast(idx) + lz; + rev_bits = zero_leading_bit(rev_bits, lz); + } + } + } + this->tail += cnt; +#else // SIMDJSON_PREFER_REVERSE_BITS + /** + * 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. + */ + + int cnt = static_cast(count_ones(bits)); + // Do the first 8 all together + for (int i=0; i<8; i++) { + this->tail[i] = idx + trailing_zeroes(bits); + bits = clear_lowest_bit(bits); + } + + // Do the next 8 all together (we hope in most cases it won't happen at all + // and the branch is easily predicted). + if (simdjson_unlikely(cnt > 8)) { + for (int i=8; i<16; i++) { + this->tail[i] = idx + trailing_zeroes(bits); + bits = clear_lowest_bit(bits); + } + + // Most files don't have 16+ structurals per block, so we take several basically guaranteed + // branch mispredictions here. 16+ structurals per block means either punctuation ({} [] , :) + // or the start of a value ("abc" true 123) every four characters. + if (simdjson_unlikely(cnt > 16)) { + int i = 16; + do { + this->tail[i] = idx + trailing_zeroes(bits); + bits = clear_lowest_bit(bits); + i++; + } while (i < cnt); + } + } + + this->tail += cnt; +#endif + } +#endif // SIMDJSON_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_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 doesn't 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 ppc64 +} // namespace simdjson +/* end file src/generic/stage1/json_structural_indexer.h */ +/* begin file src/generic/stage1/utf8_validator.h */ +namespace simdjson { +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; +} + +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 simdjson +/* end file src/generic/stage1/utf8_validator.h */ + +// +// Stage 2 +// +/* begin file src/generic/stage2/stringparsing.h */ +// This file contains the common code every implementation uses +// It is intended to be included multiple times and compiled multiple times + +namespace simdjson { +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, + + 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 isn't 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 isn't 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_nullptrptr 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; + } + } + /* can't be reached */ + return nullptr; +} + +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; + } + } + /* can't be reached */ + return nullptr; +} + +} // namespace stringparsing +} // unnamed namespace +} // namespace ppc64 +} // namespace simdjson +/* end file src/generic/stage2/stringparsing.h */ +/* begin file src/generic/stage2/tape_builder.h */ +/* begin file src/generic/stage2/json_iterator.h */ +/* begin file src/generic/stage2/logger.h */ +// 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 { + + 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 + 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 { + switch (*value) { + case '"': return visitor.visit_string(*this, 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); + case '-': + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + return visitor.visit_number(*this, value); + default: + log_error("Non-value found when value was expected!"); + return TAPE_ERROR; + } +} + +} // namespace stage2 +} // unnamed namespace +} // namespace ppc64 +} // namespace simdjson +/* end file src/generic/stage2/json_iterator.h */ +/* begin file src/generic/stage2/tape_writer.h */ +namespace simdjson { +namespace ppc64 { +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 number_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 ppc64 +} // namespace simdjson +/* end file src/generic/stage2/tape_writer.h */ + +namespace simdjson { +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; + + /** 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; +}; // class 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 ppc64 +} // namespace simdjson +/* end file src/generic/stage2/tape_builder.h */ + +// +// Implementation-specific overrides +// +namespace simdjson { +namespace ppc64 { +namespace { +namespace stage1 { + +simdjson_inline uint64_t json_string_scanner::find_escaped(uint64_t backslash) { + // On PPC, we don't short-circuit this if there are no backslashes, because the branch gives us no + // benefit and therefore makes things worse. + // if (!backslash) { uint64_t escaped = prev_escaped; prev_escaped = 0; return escaped; } + return find_escaped_branchless(backslash); +} + +} // namespace stage1 +} // unnamed namespace + +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 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_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_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); +} + +} // namespace ppc64 +} // namespace simdjson + +/* begin file include/simdjson/ppc64/end.h */ +/* end file include/simdjson/ppc64/end.h */ +/* end file src/ppc64/dom_parser_implementation.cpp */ +#endif +#if SIMDJSON_IMPLEMENTATION_WESTMERE +/* begin file src/westmere/implementation.cpp */ +/* begin file include/simdjson/westmere/begin.h */ +// redefining SIMDJSON_IMPLEMENTATION to "westmere" +// #define SIMDJSON_IMPLEMENTATION westmere +SIMDJSON_TARGET_WESTMERE +/* end file include/simdjson/westmere/begin.h */ + +namespace simdjson { +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; +} + +} // namespace westmere +} // namespace simdjson + +/* begin file include/simdjson/westmere/end.h */ +SIMDJSON_UNTARGET_WESTMERE +/* end file include/simdjson/westmere/end.h */ +/* end file src/westmere/implementation.cpp */ +/* begin file src/westmere/dom_parser_implementation.cpp */ +/* begin file include/simdjson/westmere/begin.h */ +// redefining SIMDJSON_IMPLEMENTATION to "westmere" +// #define SIMDJSON_IMPLEMENTATION westmere +SIMDJSON_TARGET_WESTMERE +/* end file include/simdjson/westmere/begin.h */ + +// +// Stage 1 +// + +namespace simdjson { +namespace westmere { +namespace { + +using namespace simd; + +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; +}; + +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 + ); + + // 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 }; +} + +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_inline simd8 must_be_2_3_continuation(const simd8 prev2, const simd8 prev3) { + 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_third_byte | is_fourth_byte) > int8_t(0); +} + +} // unnamed namespace +} // namespace westmere +} // namespace simdjson + +/* begin file src/generic/stage1/utf8_lookup4_algorithm.h */ +namespace simdjson { +namespace westmere { +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 = simd8(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 + }; +#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; + } + +#ifndef SIMDJSON_IF_CONSTEXPR +#if SIMDJSON_CPLUSPLUS17 +#define SIMDJSON_IF_CONSTEXPR if constexpr +#else +#define SIMDJSON_IF_CONSTEXPR if +#endif +#endif + + 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]); + } 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; + } + + }; // struct utf8_checker +} // namespace utf8_validation + +using utf8_validation::utf8_checker; + +} // unnamed namespace +} // namespace westmere +} // namespace simdjson +/* end file src/generic/stage1/utf8_lookup4_algorithm.h */ +/* begin file src/generic/stage1/json_structural_indexer.h */ +// 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) + +/* begin file src/generic/stage1/buf_block_reader.h */ +namespace simdjson { +namespace westmere { +namespace { + +// 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_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; +} + +} // unnamed namespace +} // namespace westmere +} // namespace simdjson +/* end file src/generic/stage1/buf_block_reader.h */ +/* begin file src/generic/stage1/json_string_scanner.h */ +namespace simdjson { +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_inline json_string_block(uint64_t backslash, uint64_t escaped, uint64_t quote, uint64_t in_string) : + _backslash(backslash), _escaped(escaped), _quote(quote), _in_string(in_string) {} + + // Escaped characters (characters following an escape() character) + simdjson_inline uint64_t escaped() const { return _escaped; } + // Escape characters (backslashes that are not escaped--i.e. in \\, includes only the first \) + simdjson_inline uint64_t escape() const { return _backslash & ~_escaped; } + // Real (non-backslashed) quotes + simdjson_inline uint64_t quote() const { return _quote; } + // Start quotes of strings + simdjson_inline uint64_t string_start() const { return _quote & _in_string; } + // End quotes of strings + simdjson_inline uint64_t string_end() const { return _quote & ~_in_string; } + // Only characters inside the string (not including the quotes) + simdjson_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_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_inline uint64_t non_quote_outside_string(uint64_t mask) const { return mask & ~_in_string; } + // Tail of string (everything except the start quote) + simdjson_inline uint64_t string_tail() const { return _in_string ^ _quote; } + + // backslash characters + uint64_t _backslash; + // escaped characters (backslashed--does not include the hex characters after \u) + uint64_t _escaped; + // real quotes (non-backslashed 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_inline json_string_block next(const simd::simd8x64& in); + // Returns either UNCLOSED_STRING or SUCCESS + simdjson_inline error_code finish(); + +private: + // Intended to be defined by the implementation + simdjson_inline uint64_t find_escaped(uint64_t escape); + simdjson_inline uint64_t find_escaped_branchless(uint64_t escape); + + // Whether the last iteration was still inside a string (all 1's = true, all 0's = false). + uint64_t prev_in_string = 0ULL; + // Whether the first character of the next iteration is escaped. + uint64_t prev_escaped = 0ULL; +}; + +// +// Finds escaped characters (characters following \). +// +// Handles runs of backslashes like \\\" and \\\\" correctly (yielding 0101 and 01010, respectively). +// +// Does this by: +// - Shift the escape mask to get potentially escaped characters (characters after backslashes). +// - Mask escaped sequences that start on *even* bits with 1010101010 (odd bits are escaped, even bits are not) +// - Mask escaped sequences that start on *odd* bits with 0101010101 (even bits are escaped, odd bits are not) +// +// To distinguish between escaped sequences starting on even/odd bits, it finds the start of all +// escape sequences, filters out the ones that start on even bits, and adds that to the mask of +// escape sequences. This causes the addition to clear out the sequences starting on odd bits (since +// the start bit causes a carry), and leaves even-bit sequences alone. +// +// Example: +// +// text | \\\ | \\\"\\\" \\\" \\"\\" | +// escape | xxx | xx xxx xxx xx xx | Removed overflow backslash; will | it into follows_escape +// odd_starts | x | x x x | escape & ~even_bits & ~follows_escape +// even_seq | c| cxxx c xx c | c = carry bit -- will be masked out later +// invert_mask | | cxxx c xx c| even_seq << 1 +// follows_escape | xx | x xx xxx xxx xx xx | Includes overflow bit +// escaped | x | x x x x x x x x | +// desired | x | x x x x x x x x | +// text | \\\ | \\\"\\\" \\\" \\"\\" | +// +simdjson_inline uint64_t json_string_scanner::find_escaped_branchless(uint64_t backslash) { + // If there was overflow, pretend the first character isn't a backslash + backslash &= ~prev_escaped; + uint64_t follows_escape = backslash << 1 | prev_escaped; + + // Get sequences starting on even bits by clearing out the odd series using + + const uint64_t even_bits = 0x5555555555555555ULL; + uint64_t odd_sequence_starts = backslash & ~even_bits & ~follows_escape; + uint64_t sequences_starting_on_even_bits; + prev_escaped = add_overflow(odd_sequence_starts, backslash, &sequences_starting_on_even_bits); + uint64_t invert_mask = sequences_starting_on_even_bits << 1; // The mask we want to return is the *escaped* bits, not escapes. + + // Mask every other backslashed character as an escaped character + // Flip the mask for sequences that start on even bits, to correct them + return (even_bits ^ invert_mask) & follows_escape; +} + +// +// 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_inline json_string_block json_string_scanner::next(const simd::simd8x64& in) { + const uint64_t backslash = in.eq('\\'); + const uint64_t escaped = find_escaped(backslash); + 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 + // + // right shift of a signed value expected to be well-defined and standard + // compliant as of C++20, John Regher from Utah U. says this is fine code + // + 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( + backslash, + escaped, + quote, + in_string + ); +} + +simdjson_inline error_code json_string_scanner::finish() { + if (prev_in_string) { + return UNCLOSED_STRING; + } + return SUCCESS; +} + +} // namespace stage1 +} // unnamed namespace +} // namespace westmere +} // namespace simdjson +/* end file src/generic/stage1/json_string_scanner.h */ +/* begin file src/generic/stage1/json_scanner.h */ +namespace simdjson { +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) {} + + /** + * 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_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_inline error_code json_scanner::finish() { + return string_scanner.finish(); +} + +} // namespace stage1 +} // unnamed namespace +} // namespace westmere +} // namespace simdjson +/* end file src/generic/stage1/json_scanner.h */ +/* begin file src/generic/stage1/json_minifier.h */ +// 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 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; + +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; +}; + +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; +} + +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 westmere +} // namespace simdjson +/* end file src/generic/stage1/json_minifier.h */ +/* begin file src/generic/stage1/find_next_document_index.h */ +namespace simdjson { +namespace westmere { +namespace { + +/** + * 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; +} + +} // unnamed namespace +} // namespace westmere +} // namespace simdjson +/* end file src/generic/stage1/find_next_document_index.h */ + +namespace simdjson { +namespace westmere { +namespace { +namespace stage1 { + +class bit_indexer { +public: + uint32_t *tail; + + simdjson_inline bit_indexer(uint32_t *index_buf) : tail(index_buf) {} + + // 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_CUSTOM_BIT_INDEXER, then it will provide its own + // version of the code. +#ifdef SIMDJSON_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; +#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. + */ + + uint64_t rev_bits = reverse_bits(bits); + int cnt = static_cast(count_ones(bits)); + int i = 0; + // Do the first 8 all together + for (; i<8; i++) { + int lz = leading_zeroes(rev_bits); + this->tail[i] = static_cast(idx) + lz; + rev_bits = zero_leading_bit(rev_bits, lz); + } + // Do the next 8 all together (we hope in most cases it won't happen at all + // and the branch is easily predicted). + if (simdjson_unlikely(cnt > 8)) { + i = 8; + for (; i<16; i++) { + int lz = leading_zeroes(rev_bits); + this->tail[i] = static_cast(idx) + lz; + rev_bits = zero_leading_bit(rev_bits, lz); + } + + + // Most files don't have 16+ structurals per block, so we take several basically guaranteed + // branch mispredictions here. 16+ structurals per block means either punctuation ({} [] , :) + // or the start of a value ("abc" true 123) every four characters. + if (simdjson_unlikely(cnt > 16)) { + i = 16; + while (rev_bits != 0) { + int lz = leading_zeroes(rev_bits); + this->tail[i++] = static_cast(idx) + lz; + rev_bits = zero_leading_bit(rev_bits, lz); + } + } + } + this->tail += cnt; +#else // SIMDJSON_PREFER_REVERSE_BITS + /** + * 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. + */ + + int cnt = static_cast(count_ones(bits)); + // Do the first 8 all together + for (int i=0; i<8; i++) { + this->tail[i] = idx + trailing_zeroes(bits); + bits = clear_lowest_bit(bits); + } + + // Do the next 8 all together (we hope in most cases it won't happen at all + // and the branch is easily predicted). + if (simdjson_unlikely(cnt > 8)) { + for (int i=8; i<16; i++) { + this->tail[i] = idx + trailing_zeroes(bits); + bits = clear_lowest_bit(bits); + } + + // Most files don't have 16+ structurals per block, so we take several basically guaranteed + // branch mispredictions here. 16+ structurals per block means either punctuation ({} [] , :) + // or the start of a value ("abc" true 123) every four characters. + if (simdjson_unlikely(cnt > 16)) { + int i = 16; + do { + this->tail[i] = idx + trailing_zeroes(bits); + bits = clear_lowest_bit(bits); + i++; + } while (i < cnt); + } + } + + this->tail += cnt; +#endif + } +#endif // SIMDJSON_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_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 doesn't 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 westmere +} // namespace simdjson +/* end file src/generic/stage1/json_structural_indexer.h */ +/* begin file src/generic/stage1/utf8_validator.h */ +namespace simdjson { +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; +} + +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 simdjson +/* end file src/generic/stage1/utf8_validator.h */ + +// +// Stage 2 +// +/* begin file src/generic/stage2/stringparsing.h */ +// This file contains the common code every implementation uses +// It is intended to be included multiple times and compiled multiple times + +namespace simdjson { +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, + + 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 isn't 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 isn't 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_nullptrptr 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; + } + } + /* can't be reached */ + return nullptr; +} + +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; + } + } + /* can't be reached */ + return nullptr; +} + +} // namespace stringparsing +} // unnamed namespace +} // namespace westmere +} // namespace simdjson +/* end file src/generic/stage2/stringparsing.h */ +/* begin file src/generic/stage2/tape_builder.h */ +/* begin file src/generic/stage2/json_iterator.h */ +/* begin file src/generic/stage2/logger.h */ +// 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 { + + 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 + 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 { + switch (*value) { + case '"': return visitor.visit_string(*this, 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); + case '-': + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + return visitor.visit_number(*this, value); + default: + log_error("Non-value found when value was expected!"); + return TAPE_ERROR; + } +} + +} // namespace stage2 +} // unnamed namespace +} // namespace westmere +} // namespace simdjson +/* end file src/generic/stage2/json_iterator.h */ +/* begin file src/generic/stage2/tape_writer.h */ +namespace simdjson { +namespace westmere { +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 number_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 westmere +} // namespace simdjson +/* end file src/generic/stage2/tape_writer.h */ + +namespace simdjson { +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; + + /** 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; +}; // class 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 westmere +} // namespace simdjson +/* end file src/generic/stage2/tape_builder.h */ + +// +// Implementation-specific overrides +// + +namespace simdjson { +namespace westmere { +namespace { +namespace stage1 { + +simdjson_inline uint64_t json_string_scanner::find_escaped(uint64_t backslash) { + if (!backslash) { uint64_t escaped = prev_escaped; prev_escaped = 0; return escaped; } + return find_escaped_branchless(backslash); +} + +} // namespace stage1 +} // unnamed namespace + +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 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_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_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 +} // namespace simdjson + +/* begin file include/simdjson/westmere/end.h */ +SIMDJSON_UNTARGET_WESTMERE +/* end file include/simdjson/westmere/end.h */ +/* end file src/westmere/dom_parser_implementation.cpp */ +#endif + +SIMDJSON_POP_DISABLE_WARNINGS +/* end file src/simdjson.cpp */ diff --git a/contrib/simdjson/simdjson.h b/contrib/simdjson/simdjson.h new file mode 100644 index 00000000000..d57a890d176 --- /dev/null +++ b/contrib/simdjson/simdjson.h @@ -0,0 +1,32088 @@ +/* auto-generated on 2023-07-06 21:34:14 -0400. Do not edit! */ +/* begin file include/simdjson.h */ +#ifndef SIMDJSON_H +#define SIMDJSON_H + +/** + * @mainpage + * + * Check the [README.md](https://github.com/simdjson/simdjson/blob/master/README.md#simdjson--parsing-gigabytes-of-json-per-second). + * + * Sample code. See https://github.com/simdjson/simdjson/blob/master/doc/basics.md for more examples. + + #include "simdjson.h" + + int main(void) { + // load from `twitter.json` file: + simdjson::dom::parser parser; + simdjson::dom::element tweets = parser.load("twitter.json"); + std::cout << tweets["search_metadata"]["count"] << " results." << std::endl; + + // Parse and iterate through an array of objects + auto abstract_json = R"( [ + { "12345" : {"a":12.34, "b":56.78, "c": 9998877} }, + { "12545" : {"a":11.44, "b":12.78, "c": 11111111} } + ] )"_padded; + + for (simdjson::dom::object obj : parser.parse(abstract_json)) { + for(const auto key_value : obj) { + cout << "key: " << key_value.key << " : "; + simdjson::dom::object innerobj = key_value.value; + cout << "a: " << double(innerobj["a"]) << ", "; + cout << "b: " << double(innerobj["b"]) << ", "; + cout << "c: " << int64_t(innerobj["c"]) << endl; + } + } + } + */ + +/* begin file include/simdjson/simdjson_version.h */ +// /include/simdjson/simdjson_version.h automatically generated by release.py, +// do not change by hand +#ifndef SIMDJSON_SIMDJSON_VERSION_H +#define SIMDJSON_SIMDJSON_VERSION_H + +/** The version of simdjson being used (major.minor.revision) */ +#define SIMDJSON_VERSION "3.2.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 = 2, + /** + * The revision (major.minor.REVISION) of simdjson being used. + */ + SIMDJSON_VERSION_REVISION = 1 +}; +} // namespace simdjson + +#endif // SIMDJSON_SIMDJSON_VERSION_H +/* end file include/simdjson/simdjson_version.h */ +/* begin file include/simdjson/dom.h */ +#ifndef SIMDJSON_DOM_H +#define SIMDJSON_DOM_H + +/* begin file include/simdjson/base.h */ +#ifndef SIMDJSON_BASE_H +#define SIMDJSON_BASE_H + +/* begin file include/simdjson/compiler_check.h */ +#ifndef SIMDJSON_COMPILER_CHECK_H +#define SIMDJSON_COMPILER_CHECK_H + +#ifndef __cplusplus +#error simdjson requires a C++ compiler +#endif + +#ifndef SIMDJSON_CPLUSPLUS +#if defined(_MSVC_LANG) && !defined(__clang__) +#define SIMDJSON_CPLUSPLUS (_MSC_VER == 1900 ? 201103L : _MSVC_LANG) +#else +#define SIMDJSON_CPLUSPLUS __cplusplus +#endif +#endif + +// C++ 17 +#if !defined(SIMDJSON_CPLUSPLUS17) && (SIMDJSON_CPLUSPLUS >= 201703L) +#define SIMDJSON_CPLUSPLUS17 1 +#endif + +// C++ 14 +#if !defined(SIMDJSON_CPLUSPLUS14) && (SIMDJSON_CPLUSPLUS >= 201402L) +#define SIMDJSON_CPLUSPLUS14 1 +#endif + +// C++ 11 +#if !defined(SIMDJSON_CPLUSPLUS11) && (SIMDJSON_CPLUSPLUS >= 201103L) +#define SIMDJSON_CPLUSPLUS11 1 +#endif + +#ifndef SIMDJSON_CPLUSPLUS11 +#error simdjson requires a compiler compliant with the C++11 standard +#endif + +#endif // SIMDJSON_COMPILER_CHECK_H +/* end file include/simdjson/compiler_check.h */ +/* begin file include/simdjson/common_defs.h */ +#ifndef SIMDJSON_COMMON_DEFS_H +#define SIMDJSON_COMMON_DEFS_H + +#include +/* begin file include/simdjson/portability.h */ +#ifndef SIMDJSON_PORTABILITY_H +#define SIMDJSON_PORTABILITY_H + +#include +#include +#include +#include +#include +#ifndef _WIN32 +// strcasecmp, strncasecmp +#include +#endif + +#ifdef _MSC_VER +#define SIMDJSON_VISUAL_STUDIO 1 +/** + * We want to differentiate carefully between + * clang under visual studio and regular visual + * studio. + * + * Under clang for Windows, we enable: + * * target pragmas so that part and only part of the + * code gets compiled for advanced instructions. + * + */ +#ifdef __clang__ +// clang under visual studio +#define SIMDJSON_CLANG_VISUAL_STUDIO 1 +#else +// just regular visual studio (best guess) +#define SIMDJSON_REGULAR_VISUAL_STUDIO 1 +#endif // __clang__ +#endif // _MSC_VER + +#if defined(__x86_64__) || defined(_M_AMD64) +#define SIMDJSON_IS_X86_64 1 +#elif defined(__aarch64__) || defined(_M_ARM64) +#define SIMDJSON_IS_ARM64 1 +#elif defined(__PPC64__) || defined(_M_PPC64) +#if defined(__ALTIVEC__) +#define SIMDJSON_IS_PPC64_VMX 1 +#endif // defined(__ALTIVEC__) +#else +#define SIMDJSON_IS_32BITS 1 + +// We do not support 32-bit platforms, but it can be +// handy to identify them. +#if defined(_M_IX86) || defined(__i386__) +#define SIMDJSON_IS_X86_32BITS 1 +#elif defined(__arm__) || defined(_M_ARM) +#define SIMDJSON_IS_ARM_32BITS 1 +#elif defined(__PPC__) || defined(_M_PPC) +#define SIMDJSON_IS_PPC_32BITS 1 +#endif + +#endif // defined(__x86_64__) || defined(_M_AMD64) +#ifndef SIMDJSON_IS_32BITS +#define SIMDJSON_IS_32BITS 0 +#endif + +#if SIMDJSON_IS_32BITS +#ifndef SIMDJSON_NO_PORTABILITY_WARNING +#pragma message("The simdjson library is designed \ +for 64-bit processors and it seems that you are not \ +compiling for a known 64-bit platform. All fast kernels \ +will be disabled and performance may be poor. Please \ +use a 64-bit target such as x64, 64-bit ARM or 64-bit PPC.") +#endif // SIMDJSON_NO_PORTABILITY_WARNING +#endif // SIMDJSON_IS_32BITS + +// this is almost standard? +#undef SIMDJSON_STRINGIFY_IMPLEMENTATION_ +#undef SIMDJSON_STRINGIFY +#define SIMDJSON_STRINGIFY_IMPLEMENTATION_(a) #a +#define SIMDJSON_STRINGIFY(a) SIMDJSON_STRINGIFY_IMPLEMENTATION_(a) + +// Our fast kernels require 64-bit systems. +// +// On 32-bit x86, we lack 64-bit popcnt, lzcnt, blsr instructions. +// Furthermore, the number of SIMD registers is reduced. +// +// On 32-bit ARM, we would have smaller registers. +// +// The simdjson users should still have the fallback kernel. It is +// slower, but it should run everywhere. + +// +// Enable valid runtime implementations, and select SIMDJSON_BUILTIN_IMPLEMENTATION +// + +// We are going to use runtime dispatch. +#if SIMDJSON_IS_X86_64 +#ifdef __clang__ +// clang does not have GCC push pop +// warning: clang attribute push can't be used within a namespace in clang up +// til 8.0 so SIMDJSON_TARGET_REGION and SIMDJSON_UNTARGET_REGION must be *outside* of a +// namespace. +#define SIMDJSON_TARGET_REGION(T) \ + _Pragma(SIMDJSON_STRINGIFY( \ + clang attribute push(__attribute__((target(T))), apply_to = function))) +#define SIMDJSON_UNTARGET_REGION _Pragma("clang attribute pop") +#elif defined(__GNUC__) +// GCC is easier +#define SIMDJSON_TARGET_REGION(T) \ + _Pragma("GCC push_options") _Pragma(SIMDJSON_STRINGIFY(GCC target(T))) +#define SIMDJSON_UNTARGET_REGION _Pragma("GCC pop_options") +#endif // clang then gcc + +#endif // x86 + +// Default target region macros don't do anything. +#ifndef SIMDJSON_TARGET_REGION +#define SIMDJSON_TARGET_REGION(T) +#define SIMDJSON_UNTARGET_REGION +#endif + +// Is threading enabled? +#if defined(_REENTRANT) || defined(_MT) +#ifndef SIMDJSON_THREADS_ENABLED +#define SIMDJSON_THREADS_ENABLED +#endif +#endif + +// workaround for large stack sizes under -O0. +// https://github.com/simdjson/simdjson/issues/691 +#ifdef __APPLE__ +#ifndef __OPTIMIZE__ +// Apple systems have small stack sizes in secondary threads. +// Lack of compiler optimization may generate high stack usage. +// Users may want to disable threads for safety, but only when +// in debug mode which we detect by the fact that the __OPTIMIZE__ +// macro is not defined. +#undef SIMDJSON_THREADS_ENABLED +#endif +#endif + + +#if defined(__clang__) +#define SIMDJSON_NO_SANITIZE_UNDEFINED __attribute__((no_sanitize("undefined"))) +#elif defined(__GNUC__) +#define SIMDJSON_NO_SANITIZE_UNDEFINED __attribute__((no_sanitize_undefined)) +#else +#define SIMDJSON_NO_SANITIZE_UNDEFINED +#endif + + +#if defined(__clang__) || defined(__GNUC__) +#if defined(__has_feature) +# if __has_feature(memory_sanitizer) +#define SIMDJSON_NO_SANITIZE_MEMORY __attribute__((no_sanitize("memory"))) +# endif // if __has_feature(memory_sanitizer) +#endif // defined(__has_feature) +#endif +// make sure it is defined as 'nothing' if it is unapplicable. +#ifndef SIMDJSON_NO_SANITIZE_MEMORY +#define SIMDJSON_NO_SANITIZE_MEMORY +#endif + +#if SIMDJSON_VISUAL_STUDIO +// This is one case where we do not distinguish between +// regular visual studio and clang under visual studio. +// clang under Windows has _stricmp (like visual studio) but not strcasecmp (as clang normally has) +#define simdjson_strcasecmp _stricmp +#define simdjson_strncasecmp _strnicmp +#else +// The strcasecmp, strncasecmp, and strcasestr functions do not work with multibyte strings (e.g. UTF-8). +// So they are only useful for ASCII in our context. +// https://www.gnu.org/software/libunistring/manual/libunistring.html#char-_002a-strings +#define simdjson_strcasecmp strcasecmp +#define simdjson_strncasecmp strncasecmp +#endif + +#if defined(NDEBUG) || defined(__OPTIMIZE__) || (defined(_MSC_VER) && !defined(_DEBUG)) +// If NDEBUG is set, or __OPTIMIZE__ is set, or we are under MSVC in release mode, +// then do away with asserts and use __assume. +#if SIMDJSON_VISUAL_STUDIO +#define SIMDJSON_UNREACHABLE() __assume(0) +#define SIMDJSON_ASSUME(COND) __assume(COND) +#else +#define SIMDJSON_UNREACHABLE() __builtin_unreachable(); +#define SIMDJSON_ASSUME(COND) do { if (!(COND)) __builtin_unreachable(); } while (0) +#endif + +#else // defined(NDEBUG) || defined(__OPTIMIZE__) || (defined(_MSC_VER) && !defined(_DEBUG)) +// This should only ever be enabled in debug mode. +#define SIMDJSON_UNREACHABLE() assert(0); +#define SIMDJSON_ASSUME(COND) assert(COND) + +#endif + +#endif // SIMDJSON_PORTABILITY_H +/* end file include/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; + +} + +#ifndef SIMDJSON_EXCEPTIONS +#if __cpp_exceptions +#define SIMDJSON_EXCEPTIONS 1 +#else +#define SIMDJSON_EXCEPTIONS 0 +#endif +#endif + +/** 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; + +/** + * 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; + +} // namespace simdjson + +#if defined(__GNUC__) + // Marks a block with a name so that MCA analysis can see it. + #define SIMDJSON_BEGIN_DEBUG_BLOCK(name) __asm volatile("# LLVM-MCA-BEGIN " #name); + #define SIMDJSON_END_DEBUG_BLOCK(name) __asm volatile("# LLVM-MCA-END " #name); + #define SIMDJSON_DEBUG_BLOCK(name, block) BEGIN_DEBUG_BLOCK(name); block; END_DEBUG_BLOCK(name); +#else + #define SIMDJSON_BEGIN_DEBUG_BLOCK(name) + #define SIMDJSON_END_DEBUG_BLOCK(name) + #define SIMDJSON_DEBUG_BLOCK(name, block) +#endif + +// Align to N-byte boundary +#define SIMDJSON_ROUNDUP_N(a, n) (((a) + ((n)-1)) & ~((n)-1)) +#define SIMDJSON_ROUNDDOWN_N(a, n) ((a) & ~((n)-1)) + +#define SIMDJSON_ISALIGNED_N(ptr, n) (((uintptr_t)(ptr) & ((n)-1)) == 0) + +#if SIMDJSON_REGULAR_VISUAL_STUDIO + + #define simdjson_really_inline __forceinline + #define simdjson_never_inline __declspec(noinline) + + #define simdjson_unused + #define simdjson_warn_unused + + #ifndef simdjson_likely + #define simdjson_likely(x) x + #endif + #ifndef simdjson_unlikely + #define simdjson_unlikely(x) x + #endif + + #define SIMDJSON_PUSH_DISABLE_WARNINGS __pragma(warning( push )) + #define SIMDJSON_PUSH_DISABLE_ALL_WARNINGS __pragma(warning( push, 0 )) + #define SIMDJSON_DISABLE_VS_WARNING(WARNING_NUMBER) __pragma(warning( disable : WARNING_NUMBER )) + // Get rid of Intellisense-only warnings (Code Analysis) + // Though __has_include is C++17, it is supported in Visual Studio 2017 or better (_MSC_VER>=1910). + #ifdef __has_include + #if __has_include() + #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 + + #define SIMDJSON_DISABLE_DEPRECATED_WARNING SIMDJSON_DISABLE_VS_WARNING(4996) + #define SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING + #define SIMDJSON_POP_DISABLE_WARNINGS __pragma(warning( pop )) + +#else // SIMDJSON_REGULAR_VISUAL_STUDIO + + #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)) + + #ifndef simdjson_likely + #define simdjson_likely(x) __builtin_expect(!!(x), 1) + #endif + #ifndef simdjson_unlikely + #define simdjson_unlikely(x) __builtin_expect(!!(x), 0) + #endif + + #define SIMDJSON_PUSH_DISABLE_WARNINGS _Pragma("GCC diagnostic push") + // gcc doesn't seem to disable all warnings with all and extra, add warnings here as necessary + // We do it separately for clang since it has different warnings. + #ifdef __clang__ + // clang is missing -Wmaybe-uninitialized. + #define SIMDJSON_PUSH_DISABLE_ALL_WARNINGS SIMDJSON_PUSH_DISABLE_WARNINGS \ + SIMDJSON_DISABLE_GCC_WARNING(-Weffc++) \ + SIMDJSON_DISABLE_GCC_WARNING(-Wall) \ + SIMDJSON_DISABLE_GCC_WARNING(-Wconversion) \ + SIMDJSON_DISABLE_GCC_WARNING(-Wextra) \ + SIMDJSON_DISABLE_GCC_WARNING(-Wattributes) \ + SIMDJSON_DISABLE_GCC_WARNING(-Wimplicit-fallthrough) \ + SIMDJSON_DISABLE_GCC_WARNING(-Wnon-virtual-dtor) \ + SIMDJSON_DISABLE_GCC_WARNING(-Wreturn-type) \ + SIMDJSON_DISABLE_GCC_WARNING(-Wshadow) \ + SIMDJSON_DISABLE_GCC_WARNING(-Wunused-parameter) \ + SIMDJSON_DISABLE_GCC_WARNING(-Wunused-variable) + #else // __clang__ + #define SIMDJSON_PUSH_DISABLE_ALL_WARNINGS SIMDJSON_PUSH_DISABLE_WARNINGS \ + SIMDJSON_DISABLE_GCC_WARNING(-Weffc++) \ + SIMDJSON_DISABLE_GCC_WARNING(-Wall) \ + SIMDJSON_DISABLE_GCC_WARNING(-Wconversion) \ + SIMDJSON_DISABLE_GCC_WARNING(-Wextra) \ + SIMDJSON_DISABLE_GCC_WARNING(-Wattributes) \ + SIMDJSON_DISABLE_GCC_WARNING(-Wimplicit-fallthrough) \ + SIMDJSON_DISABLE_GCC_WARNING(-Wnon-virtual-dtor) \ + SIMDJSON_DISABLE_GCC_WARNING(-Wreturn-type) \ + SIMDJSON_DISABLE_GCC_WARNING(-Wshadow) \ + SIMDJSON_DISABLE_GCC_WARNING(-Wunused-parameter) \ + SIMDJSON_DISABLE_GCC_WARNING(-Wunused-variable) \ + SIMDJSON_DISABLE_GCC_WARNING(-Wmaybe-uninitialized) + #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") + + + +#endif // MSC_VER + +#if defined(simdjson_inline) + // Prefer the user's definition of simdjson_inline; don't define it ourselves. +#elif defined(__GNUC__) && !defined(__OPTIMIZE__) + // If optimizations are disabled, forcing inlining can lead to significant + // code bloat and high compile times. Don't use simdjson_really_inline for + // unoptimized builds. + #define simdjson_inline inline +#else + // Force inlining for most simdjson functions. + #define simdjson_inline simdjson_really_inline +#endif + +#if SIMDJSON_VISUAL_STUDIO + /** + * Windows users need to do some extra work when building + * or using a dynamic library (DLL). When building, we need + * to set SIMDJSON_DLLIMPORTEXPORT to __declspec(dllexport). + * When *using* the DLL, the user needs to set + * SIMDJSON_DLLIMPORTEXPORT __declspec(dllimport). + * + * Static libraries not need require such work. + * + * It does not matter here whether you are using + * the regular visual studio or clang under visual + * studio, you still need to handle these issues. + * + * Non-Windows systems do not have this complexity. + */ + #if SIMDJSON_BUILDING_WINDOWS_DYNAMIC_LIBRARY + // We set SIMDJSON_BUILDING_WINDOWS_DYNAMIC_LIBRARY when we build a DLL under Windows. + // It should never happen that both SIMDJSON_BUILDING_WINDOWS_DYNAMIC_LIBRARY and + // SIMDJSON_USING_WINDOWS_DYNAMIC_LIBRARY are set. + #define SIMDJSON_DLLIMPORTEXPORT __declspec(dllexport) + #elif SIMDJSON_USING_WINDOWS_DYNAMIC_LIBRARY + // Windows user who call a dynamic library should set SIMDJSON_USING_WINDOWS_DYNAMIC_LIBRARY to 1. + #define SIMDJSON_DLLIMPORTEXPORT __declspec(dllimport) + #else + // We assume by default static linkage + #define SIMDJSON_DLLIMPORTEXPORT + #endif + +/** + * 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 +#endif + +// C++17 requires string_view. +#if SIMDJSON_CPLUSPLUS17 +#define SIMDJSON_HAS_STRING_VIEW +#include // by the standard, this has to be safe. +#endif + +// This macro (__cpp_lib_string_view) has to be defined +// for C++17 and better, but if it is otherwise defined, +// we are going to assume that string_view is available +// even if we do not have C++17 support. +#ifdef __cpp_lib_string_view +#define SIMDJSON_HAS_STRING_VIEW +#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 SIMDJSON_HAS_STRING_VIEW +#if defined __has_include +// do not combine the next #if with the previous one (unsafe) +#if __has_include () +// now it is safe to trigger the include +#include // though the file is there, it does not follow that we got the implementation +#if defined(_LIBCPP_STRING_VIEW) +// Ah! So we under libc++ which under its Library Fundamentals Technical Specification, which preceded C++17, +// included string_view. +// This means that we have string_view *even though* we may not have C++17. +#define SIMDJSON_HAS_STRING_VIEW +#endif // _LIBCPP_STRING_VIEW +#endif // __has_include () +#endif // defined __has_include +#endif // def SIMDJSON_HAS_STRING_VIEW +// end of complicated but important routine to try to detect string_view. + +// +// Backfill std::string_view using nonstd::string_view on systems where +// we expect that string_view is missing. Important: if we get this wrong, +// we will end up with two string_view definitions and potential trouble. +// That is why we work so hard above to avoid it. +// +#ifndef SIMDJSON_HAS_STRING_VIEW +SIMDJSON_PUSH_DISABLE_ALL_WARNINGS +/* begin file include/simdjson/nonstd/string_view.hpp */ +// Copyright 2017-2020 by Martin Moene +// +// string-view lite, a C++17-like string_view for C++98 and later. +// For more information see https://github.com/martinmoene/string-view-lite +// +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#pragma once + +#ifndef NONSTD_SV_LITE_H_INCLUDED +#define NONSTD_SV_LITE_H_INCLUDED + +#define string_view_lite_MAJOR 1 +#define string_view_lite_MINOR 7 +#define string_view_lite_PATCH 0 + +#define string_view_lite_VERSION nssv_STRINGIFY(string_view_lite_MAJOR) "." nssv_STRINGIFY(string_view_lite_MINOR) "." nssv_STRINGIFY(string_view_lite_PATCH) + +#define nssv_STRINGIFY( x ) nssv_STRINGIFY_( x ) +#define nssv_STRINGIFY_( x ) #x + +// string-view lite configuration: + +#define nssv_STRING_VIEW_DEFAULT 0 +#define nssv_STRING_VIEW_NONSTD 1 +#define nssv_STRING_VIEW_STD 2 + +// tweak header support: + +#ifdef __has_include +# if __has_include() +# include +# endif +#define nssv_HAVE_TWEAK_HEADER 1 +#else +#define nssv_HAVE_TWEAK_HEADER 0 +//# pragma message("string_view.hpp: Note: Tweak header not supported.") +#endif + +// string_view selection and configuration: + +#if !defined( nssv_CONFIG_SELECT_STRING_VIEW ) +# define nssv_CONFIG_SELECT_STRING_VIEW ( nssv_HAVE_STD_STRING_VIEW ? nssv_STRING_VIEW_STD : nssv_STRING_VIEW_NONSTD ) +#endif + +#ifndef nssv_CONFIG_STD_SV_OPERATOR +# define nssv_CONFIG_STD_SV_OPERATOR 0 +#endif + +#ifndef nssv_CONFIG_USR_SV_OPERATOR +# define nssv_CONFIG_USR_SV_OPERATOR 1 +#endif + +#ifdef nssv_CONFIG_CONVERSION_STD_STRING +# define nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS nssv_CONFIG_CONVERSION_STD_STRING +# define nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS nssv_CONFIG_CONVERSION_STD_STRING +#endif + +#ifndef nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS +# define nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS 1 +#endif + +#ifndef nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS +# define nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS 1 +#endif + +#ifndef nssv_CONFIG_NO_STREAM_INSERTION +# define nssv_CONFIG_NO_STREAM_INSERTION 0 +#endif + +// Control presence of exception handling (try and auto discover): + +#ifndef nssv_CONFIG_NO_EXCEPTIONS +# if defined(_MSC_VER) +# include // for _HAS_EXCEPTIONS +# endif +# if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || (_HAS_EXCEPTIONS) +# define nssv_CONFIG_NO_EXCEPTIONS 0 +# else +# define nssv_CONFIG_NO_EXCEPTIONS 1 +# endif +#endif + +// C++ language version detection (C++23 is speculative): +// Note: VC14.0/1900 (VS2015) lacks too much from C++14. + +#ifndef nssv_CPLUSPLUS +# if defined(_MSVC_LANG ) && !defined(__clang__) +# define nssv_CPLUSPLUS (_MSC_VER == 1900 ? 201103L : _MSVC_LANG ) +# else +# define nssv_CPLUSPLUS __cplusplus +# endif +#endif + +#define nssv_CPP98_OR_GREATER ( nssv_CPLUSPLUS >= 199711L ) +#define nssv_CPP11_OR_GREATER ( nssv_CPLUSPLUS >= 201103L ) +#define nssv_CPP11_OR_GREATER_ ( nssv_CPLUSPLUS >= 201103L ) +#define nssv_CPP14_OR_GREATER ( nssv_CPLUSPLUS >= 201402L ) +#define nssv_CPP17_OR_GREATER ( nssv_CPLUSPLUS >= 201703L ) +#define nssv_CPP20_OR_GREATER ( nssv_CPLUSPLUS >= 202002L ) +#define nssv_CPP23_OR_GREATER ( nssv_CPLUSPLUS >= 202300L ) + +// use C++17 std::string_view if available and requested: + +#if nssv_CPP17_OR_GREATER && defined(__has_include ) +# if __has_include( ) +# define nssv_HAVE_STD_STRING_VIEW 1 +# else +# define nssv_HAVE_STD_STRING_VIEW 0 +# endif +#else +# define nssv_HAVE_STD_STRING_VIEW 0 +#endif + +#define nssv_USES_STD_STRING_VIEW ( (nssv_CONFIG_SELECT_STRING_VIEW == nssv_STRING_VIEW_STD) || ((nssv_CONFIG_SELECT_STRING_VIEW == nssv_STRING_VIEW_DEFAULT) && nssv_HAVE_STD_STRING_VIEW) ) + +#define nssv_HAVE_STARTS_WITH ( nssv_CPP20_OR_GREATER || !nssv_USES_STD_STRING_VIEW ) +#define nssv_HAVE_ENDS_WITH nssv_HAVE_STARTS_WITH + +// +// Use C++17 std::string_view: +// + +#if nssv_USES_STD_STRING_VIEW + +#include + +// Extensions for std::string: + +#if nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS + +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::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: + +#if nssv_CONFIG_STD_SV_OPERATOR + +using namespace std::literals::string_view_literals; + +#endif + +#if nssv_CONFIG_USR_SV_OPERATOR + +inline namespace literals { +inline namespace string_view_literals { + + +constexpr std::string_view operator "" _sv( const char* str, size_t len ) noexcept // (1) +{ + return std::string_view{ str, len }; +} + +constexpr std::u16string_view operator "" _sv( const char16_t* str, size_t len ) noexcept // (2) +{ + return std::u16string_view{ str, len }; +} + +constexpr std::u32string_view operator "" _sv( const char32_t* str, size_t len ) noexcept // (3) +{ + return std::u32string_view{ str, len }; +} + +constexpr std::wstring_view operator "" _sv( const wchar_t* str, size_t len ) noexcept // (4) +{ + return std::wstring_view{ str, len }; +} + +}} // namespace literals::string_view_literals + +#endif // nssv_CONFIG_USR_SV_OPERATOR + +} // namespace nonstd + +#endif // nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS + +namespace nonstd { + +using std::string_view; +using std::wstring_view; +using std::u16string_view; +using std::u32string_view; +using std::basic_string_view; + +// literal "sv" and "_sv", see above + +using std::operator==; +using std::operator!=; +using std::operator<; +using std::operator<=; +using std::operator>; +using std::operator>=; + +using std::operator<<; + +} // namespace nonstd + +#else // nssv_HAVE_STD_STRING_VIEW + +// +// Before C++17: use string_view lite: +// + +// Compiler versions: +// +// MSVC++ 6.0 _MSC_VER == 1200 nssv_COMPILER_MSVC_VERSION == 60 (Visual Studio 6.0) +// MSVC++ 7.0 _MSC_VER == 1300 nssv_COMPILER_MSVC_VERSION == 70 (Visual Studio .NET 2002) +// MSVC++ 7.1 _MSC_VER == 1310 nssv_COMPILER_MSVC_VERSION == 71 (Visual Studio .NET 2003) +// MSVC++ 8.0 _MSC_VER == 1400 nssv_COMPILER_MSVC_VERSION == 80 (Visual Studio 2005) +// MSVC++ 9.0 _MSC_VER == 1500 nssv_COMPILER_MSVC_VERSION == 90 (Visual Studio 2008) +// MSVC++ 10.0 _MSC_VER == 1600 nssv_COMPILER_MSVC_VERSION == 100 (Visual Studio 2010) +// MSVC++ 11.0 _MSC_VER == 1700 nssv_COMPILER_MSVC_VERSION == 110 (Visual Studio 2012) +// MSVC++ 12.0 _MSC_VER == 1800 nssv_COMPILER_MSVC_VERSION == 120 (Visual Studio 2013) +// MSVC++ 14.0 _MSC_VER == 1900 nssv_COMPILER_MSVC_VERSION == 140 (Visual Studio 2015) +// MSVC++ 14.1 _MSC_VER >= 1910 nssv_COMPILER_MSVC_VERSION == 141 (Visual Studio 2017) +// MSVC++ 14.2 _MSC_VER >= 1920 nssv_COMPILER_MSVC_VERSION == 142 (Visual Studio 2019) + +#if defined(_MSC_VER ) && !defined(__clang__) +# define nssv_COMPILER_MSVC_VER (_MSC_VER ) +# define nssv_COMPILER_MSVC_VERSION (_MSC_VER / 10 - 10 * ( 5 + (_MSC_VER < 1900 ) ) ) +#else +# define nssv_COMPILER_MSVC_VER 0 +# define nssv_COMPILER_MSVC_VERSION 0 +#endif + +#define nssv_COMPILER_VERSION( major, minor, patch ) ( 10 * ( 10 * (major) + (minor) ) + (patch) ) + +#if defined( __apple_build_version__ ) +# define nssv_COMPILER_APPLECLANG_VERSION nssv_COMPILER_VERSION(__clang_major__, __clang_minor__, __clang_patchlevel__) +# define nssv_COMPILER_CLANG_VERSION 0 +#elif defined( __clang__ ) +# define nssv_COMPILER_APPLECLANG_VERSION 0 +# define nssv_COMPILER_CLANG_VERSION nssv_COMPILER_VERSION(__clang_major__, __clang_minor__, __clang_patchlevel__) +#else +# define nssv_COMPILER_APPLECLANG_VERSION 0 +# define nssv_COMPILER_CLANG_VERSION 0 +#endif + +#if defined(__GNUC__) && !defined(__clang__) +# define nssv_COMPILER_GNUC_VERSION nssv_COMPILER_VERSION(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) +#else +# define nssv_COMPILER_GNUC_VERSION 0 +#endif + +// half-open range [lo..hi): +#define nssv_BETWEEN( v, lo, hi ) ( (lo) <= (v) && (v) < (hi) ) + +// Presence of language and library features: + +#ifdef _HAS_CPP0X +# define nssv_HAS_CPP0X _HAS_CPP0X +#else +# define nssv_HAS_CPP0X 0 +#endif + +// Unless defined otherwise below, consider VC14 as C++11 for variant-lite: + +#if nssv_COMPILER_MSVC_VER >= 1900 +# undef nssv_CPP11_OR_GREATER +# define nssv_CPP11_OR_GREATER 1 +#endif + +#define nssv_CPP11_90 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1500) +#define nssv_CPP11_100 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1600) +#define nssv_CPP11_110 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1700) +#define nssv_CPP11_120 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1800) +#define nssv_CPP11_140 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1900) +#define nssv_CPP11_141 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1910) + +#define nssv_CPP14_000 (nssv_CPP14_OR_GREATER) +#define nssv_CPP17_000 (nssv_CPP17_OR_GREATER) + +// Presence of C++11 language features: + +#define nssv_HAVE_CONSTEXPR_11 nssv_CPP11_140 +#define nssv_HAVE_EXPLICIT_CONVERSION nssv_CPP11_140 +#define nssv_HAVE_INLINE_NAMESPACE nssv_CPP11_140 +#define nssv_HAVE_IS_DEFAULT nssv_CPP11_140 +#define nssv_HAVE_IS_DELETE nssv_CPP11_140 +#define nssv_HAVE_NOEXCEPT nssv_CPP11_140 +#define nssv_HAVE_NULLPTR nssv_CPP11_100 +#define nssv_HAVE_REF_QUALIFIER nssv_CPP11_140 +#define nssv_HAVE_UNICODE_LITERALS nssv_CPP11_140 +#define nssv_HAVE_USER_DEFINED_LITERALS nssv_CPP11_140 +#define nssv_HAVE_WCHAR16_T nssv_CPP11_100 +#define nssv_HAVE_WCHAR32_T nssv_CPP11_100 + +#if ! ( ( nssv_CPP11_OR_GREATER && nssv_COMPILER_CLANG_VERSION ) || nssv_BETWEEN( nssv_COMPILER_CLANG_VERSION, 300, 400 ) ) +# define nssv_HAVE_STD_DEFINED_LITERALS nssv_CPP11_140 +#else +# define nssv_HAVE_STD_DEFINED_LITERALS 0 +#endif + +// Presence of C++14 language features: + +#define nssv_HAVE_CONSTEXPR_14 nssv_CPP14_000 + +// Presence of C++17 language features: + +#define nssv_HAVE_NODISCARD nssv_CPP17_000 + +// Presence of C++ library features: + +#define nssv_HAVE_STD_HASH nssv_CPP11_120 + +// Presence of compiler intrinsics: + +// Providing char-type specializations for compare() and length() that +// use compiler intrinsics can improve compile- and run-time performance. +// +// The challenge is in using the right combinations of builtin availability +// and its constexpr-ness. +// +// | compiler | __builtin_memcmp (constexpr) | memcmp (constexpr) | +// |----------|------------------------------|---------------------| +// | clang | 4.0 (>= 4.0 ) | any (? ) | +// | clang-a | 9.0 (>= 9.0 ) | any (? ) | +// | gcc | any (constexpr) | any (? ) | +// | msvc | >= 14.2 C++17 (>= 14.2 ) | any (? ) | + +#define nssv_HAVE_BUILTIN_VER ( (nssv_CPP17_000 && nssv_COMPILER_MSVC_VERSION >= 142) || nssv_COMPILER_GNUC_VERSION > 0 || nssv_COMPILER_CLANG_VERSION >= 400 || nssv_COMPILER_APPLECLANG_VERSION >= 900 ) +#define nssv_HAVE_BUILTIN_CE ( nssv_HAVE_BUILTIN_VER ) + +#define nssv_HAVE_BUILTIN_MEMCMP ( (nssv_HAVE_CONSTEXPR_14 && nssv_HAVE_BUILTIN_CE) || !nssv_HAVE_CONSTEXPR_14 ) +#define nssv_HAVE_BUILTIN_STRLEN ( (nssv_HAVE_CONSTEXPR_11 && nssv_HAVE_BUILTIN_CE) || !nssv_HAVE_CONSTEXPR_11 ) + +#ifdef __has_builtin +# define nssv_HAVE_BUILTIN( x ) __has_builtin( x ) +#else +# define nssv_HAVE_BUILTIN( x ) 0 +#endif + +#if nssv_HAVE_BUILTIN(__builtin_memcmp) || nssv_HAVE_BUILTIN_VER +# define nssv_BUILTIN_MEMCMP __builtin_memcmp +#else +# define nssv_BUILTIN_MEMCMP memcmp +#endif + +#if nssv_HAVE_BUILTIN(__builtin_strlen) || nssv_HAVE_BUILTIN_VER +# define nssv_BUILTIN_STRLEN __builtin_strlen +#else +# define nssv_BUILTIN_STRLEN strlen +#endif + +// C++ feature usage: + +#if nssv_HAVE_CONSTEXPR_11 +# define nssv_constexpr constexpr +#else +# define nssv_constexpr /*constexpr*/ +#endif + +#if nssv_HAVE_CONSTEXPR_14 +# define nssv_constexpr14 constexpr +#else +# define nssv_constexpr14 /*constexpr*/ +#endif + +#if nssv_HAVE_EXPLICIT_CONVERSION +# define nssv_explicit explicit +#else +# define nssv_explicit /*explicit*/ +#endif + +#if nssv_HAVE_INLINE_NAMESPACE +# define nssv_inline_ns inline +#else +# define nssv_inline_ns /*inline*/ +#endif + +#if nssv_HAVE_NOEXCEPT +# define nssv_noexcept noexcept +#else +# define nssv_noexcept /*noexcept*/ +#endif + +//#if nssv_HAVE_REF_QUALIFIER +//# define nssv_ref_qual & +//# define nssv_refref_qual && +//#else +//# define nssv_ref_qual /*&*/ +//# define nssv_refref_qual /*&&*/ +//#endif + +#if nssv_HAVE_NULLPTR +# define nssv_nullptr nullptr +#else +# define nssv_nullptr NULL +#endif + +#if nssv_HAVE_NODISCARD +# define nssv_nodiscard [[nodiscard]] +#else +# define nssv_nodiscard /*[[nodiscard]]*/ +#endif + +// Additional includes: + +#include +#include +#include +#include +#include // std::char_traits<> + +#if ! nssv_CONFIG_NO_STREAM_INSERTION +# include +#endif + +#if ! nssv_CONFIG_NO_EXCEPTIONS +# include +#endif + +#if nssv_CPP11_OR_GREATER +# include +#endif + +// Clang, GNUC, MSVC warning suppression macros: + +#if defined(__clang__) +# pragma clang diagnostic ignored "-Wreserved-user-defined-literal" +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wuser-defined-literals" +#elif defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wliteral-suffix" +#endif // __clang__ + +#if nssv_COMPILER_MSVC_VERSION >= 140 +# define nssv_SUPPRESS_MSGSL_WARNING(expr) [[gsl::suppress(expr)]] +# define nssv_SUPPRESS_MSVC_WARNING(code, descr) __pragma(warning(suppress: code) ) +# define nssv_DISABLE_MSVC_WARNINGS(codes) __pragma(warning(push)) __pragma(warning(disable: codes)) +#else +# define nssv_SUPPRESS_MSGSL_WARNING(expr) +# define nssv_SUPPRESS_MSVC_WARNING(code, descr) +# define nssv_DISABLE_MSVC_WARNINGS(codes) +#endif + +#if defined(__clang__) +# define nssv_RESTORE_WARNINGS() _Pragma("clang diagnostic pop") +#elif defined(__GNUC__) +# define nssv_RESTORE_WARNINGS() _Pragma("GCC diagnostic pop") +#elif nssv_COMPILER_MSVC_VERSION >= 140 +# define nssv_RESTORE_WARNINGS() __pragma(warning(pop )) +#else +# define nssv_RESTORE_WARNINGS() +#endif + +// Suppress the following MSVC (GSL) warnings: +// - C4455, non-gsl : 'operator ""sv': literal suffix identifiers that do not +// start with an underscore are reserved +// - C26472, gsl::t.1 : don't use a static_cast for arithmetic conversions; +// use brace initialization, gsl::narrow_cast or gsl::narow +// - C26481: gsl::b.1 : don't use pointer arithmetic. Use span instead + +nssv_DISABLE_MSVC_WARNINGS( 4455 26481 26472 ) +//nssv_DISABLE_CLANG_WARNINGS( "-Wuser-defined-literals" ) +//nssv_DISABLE_GNUC_WARNINGS( -Wliteral-suffix ) + +namespace nonstd { namespace sv_lite { + +// +// basic_string_view declaration: +// + +template +< + class CharT, + class Traits = std::char_traits +> +class basic_string_view; + +namespace detail { + +// support constexpr comparison in C++14; +// for C++17 and later, use provided traits: + +template< typename CharT > +inline nssv_constexpr14 int compare( CharT const * s1, CharT const * s2, std::size_t count ) +{ + while ( count-- != 0 ) + { + if ( *s1 < *s2 ) return -1; + if ( *s1 > *s2 ) return +1; + ++s1; ++s2; + } + return 0; +} + +#if nssv_HAVE_BUILTIN_MEMCMP + +// specialization of compare() for char, see also generic compare() above: + +inline nssv_constexpr14 int compare( char const * s1, char const * s2, std::size_t count ) +{ + return nssv_BUILTIN_MEMCMP( s1, s2, count ); +} + +#endif + +#if nssv_HAVE_BUILTIN_STRLEN + +// specialization of length() for char, see also generic length() further below: + +inline nssv_constexpr std::size_t length( char const * s ) +{ + return nssv_BUILTIN_STRLEN( s ); +} + +#endif + +#if defined(__OPTIMIZE__) + +// gcc, clang provide __OPTIMIZE__ +// Expect tail call optimization to make length() non-recursive: + +template< typename CharT > +inline nssv_constexpr std::size_t length( CharT * s, std::size_t result = 0 ) +{ + return *s == '\0' ? result : length( s + 1, result + 1 ); +} + +#else // OPTIMIZE + +// non-recursive: + +template< typename CharT > +inline nssv_constexpr14 std::size_t length( CharT * s ) +{ + std::size_t result = 0; + while ( *s++ != '\0' ) + { + ++result; + } + return result; +} + +#endif // OPTIMIZE + +#if nssv_CPP11_OR_GREATER && ! nssv_CPP17_OR_GREATER +#if defined(__OPTIMIZE__) + +// gcc, clang provide __OPTIMIZE__ +// Expect tail call optimization to make search() non-recursive: + +template< class CharT, class Traits = std::char_traits > +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: + +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() ); +} + +#endif // OPTIMIZE +#endif // nssv_CPP11_OR_GREATER && ! nssv_CPP17_OR_GREATER + +} // namespace detail + +// +// basic_string_view: +// + +template +< + class CharT, + class Traits /* = std::char_traits */ +> +class basic_string_view +{ +public: + // Member types: + + typedef Traits traits_type; + typedef CharT value_type; + + typedef CharT * pointer; + typedef CharT const * const_pointer; + typedef CharT & reference; + typedef CharT const & const_reference; + + typedef const_pointer iterator; + typedef const_pointer const_iterator; + typedef std::reverse_iterator< const_iterator > reverse_iterator; + typedef std::reverse_iterator< const_iterator > const_reverse_iterator; + + typedef std::size_t size_type; + typedef std::ptrdiff_t difference_type; + + // 24.4.2.1 Construction and assignment: + + nssv_constexpr basic_string_view() nssv_noexcept + : data_( nssv_nullptr ) + , size_( 0 ) + {} + +#if nssv_CPP11_OR_GREATER + nssv_constexpr basic_string_view( basic_string_view const & other ) nssv_noexcept = default; +#else + nssv_constexpr basic_string_view( basic_string_view const & other ) nssv_noexcept + : data_( other.data_) + , size_( other.size_) + {} +#endif + + nssv_constexpr basic_string_view( CharT const * s, size_type count ) nssv_noexcept // non-standard noexcept + : data_( s ) + , size_( count ) + {} + + nssv_constexpr basic_string_view( CharT const * s) nssv_noexcept // non-standard noexcept + : data_( s ) +#if nssv_CPP17_OR_GREATER + , size_( Traits::length(s) ) +#elif nssv_CPP11_OR_GREATER + , size_( detail::length(s) ) +#else + , size_( Traits::length(s) ) +#endif + {} + +#if nssv_HAVE_NULLPTR +# if nssv_HAVE_IS_DELETE + nssv_constexpr basic_string_view( std::nullptr_t ) nssv_noexcept = delete; +# else + private: nssv_constexpr basic_string_view( std::nullptr_t ) nssv_noexcept; public: +# endif +#endif + + // Assignment: + +#if nssv_CPP11_OR_GREATER + nssv_constexpr14 basic_string_view & operator=( basic_string_view const & other ) nssv_noexcept = default; +#else + nssv_constexpr14 basic_string_view & operator=( basic_string_view const & other ) nssv_noexcept + { + data_ = other.data_; + size_ = other.size_; + return *this; + } +#endif + + // 24.4.2.2 Iterator support: + + nssv_constexpr const_iterator begin() const nssv_noexcept { return data_; } + nssv_constexpr const_iterator end() const nssv_noexcept { return data_ + size_; } + + nssv_constexpr const_iterator cbegin() const nssv_noexcept { return begin(); } + nssv_constexpr const_iterator cend() const nssv_noexcept { return end(); } + + nssv_constexpr const_reverse_iterator rbegin() const nssv_noexcept { return const_reverse_iterator( end() ); } + nssv_constexpr const_reverse_iterator rend() const nssv_noexcept { return const_reverse_iterator( begin() ); } + + nssv_constexpr const_reverse_iterator crbegin() const nssv_noexcept { return rbegin(); } + nssv_constexpr const_reverse_iterator crend() const nssv_noexcept { return rend(); } + + // 24.4.2.3 Capacity: + + nssv_constexpr size_type size() const nssv_noexcept { return size_; } + nssv_constexpr size_type length() const nssv_noexcept { return size_; } + nssv_constexpr size_type max_size() const nssv_noexcept { return (std::numeric_limits< size_type >::max)(); } + + // since C++20 + nssv_nodiscard nssv_constexpr bool empty() const nssv_noexcept + { + return 0 == size_; + } + + // 24.4.2.4 Element access: + + nssv_constexpr const_reference operator[]( size_type pos ) const + { + return data_at( pos ); + } + + nssv_constexpr14 const_reference at( size_type pos ) const + { +#if nssv_CONFIG_NO_EXCEPTIONS + assert( pos < size() ); +#else + if ( pos >= size() ) + { + throw std::out_of_range("nonstd::string_view::at()"); + } +#endif + return data_at( pos ); + } + + nssv_constexpr const_reference front() const { return data_at( 0 ); } + nssv_constexpr const_reference back() const { return data_at( size() - 1 ); } + + nssv_constexpr const_pointer data() const nssv_noexcept { return data_; } + + // 24.4.2.5 Modifiers: + + nssv_constexpr14 void remove_prefix( size_type n ) + { + assert( n <= size() ); + data_ += n; + size_ -= n; + } + + nssv_constexpr14 void remove_suffix( size_type n ) + { + assert( n <= size() ); + size_ -= n; + } + + nssv_constexpr14 void swap( basic_string_view & other ) nssv_noexcept + { + const basic_string_view tmp(other); + other = *this; + *this = tmp; + } + + // 24.4.2.6 String operations: + + size_type copy( CharT * dest, size_type n, size_type pos = 0 ) const + { +#if nssv_CONFIG_NO_EXCEPTIONS + assert( pos <= size() ); +#else + if ( pos > size() ) + { + throw std::out_of_range("nonstd::string_view::copy()"); + } +#endif + const size_type rlen = (std::min)( n, size() - pos ); + + (void) Traits::copy( dest, data() + pos, rlen ); + + return rlen; + } + + nssv_constexpr14 basic_string_view substr( size_type pos = 0, size_type n = npos ) const + { +#if nssv_CONFIG_NO_EXCEPTIONS + assert( pos <= size() ); +#else + if ( pos > size() ) + { + throw std::out_of_range("nonstd::string_view::substr()"); + } +#endif + return basic_string_view( data() + pos, (std::min)( n, size() - pos ) ); + } + + // compare(), 6x: + + nssv_constexpr14 int compare( basic_string_view other ) const nssv_noexcept // (1) + { +#if nssv_CPP17_OR_GREATER + if ( const int result = Traits::compare( data(), other.data(), (std::min)( size(), other.size() ) ) ) +#else + if ( const int result = detail::compare( data(), other.data(), (std::min)( size(), other.size() ) ) ) +#endif + { + return result; + } + + return size() == other.size() ? 0 : size() < other.size() ? -1 : 1; + } + + nssv_constexpr int compare( size_type pos1, size_type n1, basic_string_view other ) const // (2) + { + return substr( pos1, n1 ).compare( other ); + } + + nssv_constexpr int compare( size_type pos1, size_type n1, basic_string_view other, size_type pos2, size_type n2 ) const // (3) + { + return substr( pos1, n1 ).compare( other.substr( pos2, n2 ) ); + } + + nssv_constexpr int compare( CharT const * s ) const // (4) + { + return compare( basic_string_view( s ) ); + } + + nssv_constexpr int compare( size_type pos1, size_type n1, CharT const * s ) const // (5) + { + return substr( pos1, n1 ).compare( basic_string_view( s ) ); + } + + nssv_constexpr int compare( size_type pos1, size_type n1, CharT const * s, size_type n2 ) const // (6) + { + return substr( pos1, n1 ).compare( basic_string_view( s, n2 ) ); + } + + // 24.4.2.7 Searching: + + // starts_with(), 3x, since C++20: + + nssv_constexpr bool starts_with( basic_string_view v ) const nssv_noexcept // (1) + { + return size() >= v.size() && compare( 0, v.size(), v ) == 0; + } + + nssv_constexpr bool starts_with( CharT c ) const nssv_noexcept // (2) + { + return starts_with( basic_string_view( &c, 1 ) ); + } + + nssv_constexpr bool starts_with( CharT const * s ) const // (3) + { + return starts_with( basic_string_view( s ) ); + } + + // ends_with(), 3x, since C++20: + + nssv_constexpr bool ends_with( basic_string_view v ) const nssv_noexcept // (1) + { + return size() >= v.size() && compare( size() - v.size(), npos, v ) == 0; + } + + nssv_constexpr bool ends_with( CharT c ) const nssv_noexcept // (2) + { + return ends_with( basic_string_view( &c, 1 ) ); + } + + nssv_constexpr bool ends_with( CharT const * s ) const // (3) + { + return ends_with( basic_string_view( s ) ); + } + + // find(), 4x: + + nssv_constexpr size_type find( basic_string_view v, size_type pos = 0 ) const nssv_noexcept // (1) + { + return assert( v.size() == 0 || v.data() != nssv_nullptr ) + , pos >= size() + ? npos : to_pos( +#if nssv_CPP11_OR_GREATER && ! nssv_CPP17_OR_GREATER + detail::search( substr(pos), v ) +#else + std::search( cbegin() + pos, cend(), v.cbegin(), v.cend(), Traits::eq ) +#endif + ); + } + + nssv_constexpr size_type find( CharT c, size_type pos = 0 ) const nssv_noexcept // (2) + { + return find( basic_string_view( &c, 1 ), pos ); + } + + nssv_constexpr size_type find( CharT const * s, size_type pos, size_type n ) const // (3) + { + return find( basic_string_view( s, n ), pos ); + } + + nssv_constexpr size_type find( CharT const * s, size_type pos = 0 ) const // (4) + { + return find( basic_string_view( s ), pos ); + } + + // rfind(), 4x: + + nssv_constexpr14 size_type rfind( basic_string_view v, size_type pos = npos ) const nssv_noexcept // (1) + { + if ( size() < v.size() ) + { + return npos; + } + + if ( v.empty() ) + { + return (std::min)( size(), pos ); + } + + const_iterator last = cbegin() + (std::min)( size() - v.size(), pos ) + v.size(); + const_iterator result = std::find_end( cbegin(), last, v.cbegin(), v.cend(), Traits::eq ); + + return result != last ? size_type( result - cbegin() ) : npos; + } + + nssv_constexpr14 size_type rfind( CharT c, size_type pos = npos ) const nssv_noexcept // (2) + { + return rfind( basic_string_view( &c, 1 ), pos ); + } + + nssv_constexpr14 size_type rfind( CharT const * s, size_type pos, size_type n ) const // (3) + { + return rfind( basic_string_view( s, n ), pos ); + } + + nssv_constexpr14 size_type rfind( CharT const * s, size_type pos = npos ) const // (4) + { + return rfind( basic_string_view( s ), pos ); + } + + // find_first_of(), 4x: + + nssv_constexpr size_type find_first_of( basic_string_view v, size_type pos = 0 ) const nssv_noexcept // (1) + { + return pos >= size() + ? npos + : to_pos( std::find_first_of( cbegin() + pos, cend(), v.cbegin(), v.cend(), Traits::eq ) ); + } + + nssv_constexpr size_type find_first_of( CharT c, size_type pos = 0 ) const nssv_noexcept // (2) + { + return find_first_of( basic_string_view( &c, 1 ), pos ); + } + + nssv_constexpr size_type find_first_of( CharT const * s, size_type pos, size_type n ) const // (3) + { + return find_first_of( basic_string_view( s, n ), pos ); + } + + nssv_constexpr size_type find_first_of( CharT const * s, size_type pos = 0 ) const // (4) + { + return find_first_of( basic_string_view( s ), pos ); + } + + // find_last_of(), 4x: + + nssv_constexpr size_type find_last_of( basic_string_view v, size_type pos = npos ) const nssv_noexcept // (1) + { + return empty() + ? npos + : pos >= size() + ? find_last_of( v, size() - 1 ) + : to_pos( std::find_first_of( const_reverse_iterator( cbegin() + pos + 1 ), crend(), v.cbegin(), v.cend(), Traits::eq ) ); + } + + nssv_constexpr size_type find_last_of( CharT c, size_type pos = npos ) const nssv_noexcept // (2) + { + return find_last_of( basic_string_view( &c, 1 ), pos ); + } + + nssv_constexpr size_type find_last_of( CharT const * s, size_type pos, size_type count ) const // (3) + { + return find_last_of( basic_string_view( s, count ), pos ); + } + + nssv_constexpr size_type find_last_of( CharT const * s, size_type pos = npos ) const // (4) + { + return find_last_of( basic_string_view( s ), pos ); + } + + // find_first_not_of(), 4x: + + nssv_constexpr size_type find_first_not_of( basic_string_view v, size_type pos = 0 ) const nssv_noexcept // (1) + { + return pos >= size() + ? npos + : to_pos( std::find_if( cbegin() + pos, cend(), not_in_view( v ) ) ); + } + + nssv_constexpr size_type find_first_not_of( CharT c, size_type pos = 0 ) const nssv_noexcept // (2) + { + return find_first_not_of( basic_string_view( &c, 1 ), pos ); + } + + nssv_constexpr size_type find_first_not_of( CharT const * s, size_type pos, size_type count ) const // (3) + { + return find_first_not_of( basic_string_view( s, count ), pos ); + } + + nssv_constexpr size_type find_first_not_of( CharT const * s, size_type pos = 0 ) const // (4) + { + return find_first_not_of( basic_string_view( s ), pos ); + } + + // find_last_not_of(), 4x: + + nssv_constexpr size_type find_last_not_of( basic_string_view v, size_type pos = npos ) const nssv_noexcept // (1) + { + return empty() + ? npos + : pos >= size() + ? find_last_not_of( v, size() - 1 ) + : to_pos( std::find_if( const_reverse_iterator( cbegin() + pos + 1 ), crend(), not_in_view( v ) ) ); + } + + nssv_constexpr size_type find_last_not_of( CharT c, size_type pos = npos ) const nssv_noexcept // (2) + { + return find_last_not_of( basic_string_view( &c, 1 ), pos ); + } + + nssv_constexpr size_type find_last_not_of( CharT const * s, size_type pos, size_type count ) const // (3) + { + return find_last_not_of( basic_string_view( s, count ), pos ); + } + + nssv_constexpr size_type find_last_not_of( CharT const * s, size_type pos = npos ) const // (4) + { + return find_last_not_of( basic_string_view( s ), pos ); + } + + // Constants: + +#if nssv_CPP17_OR_GREATER + static nssv_constexpr size_type npos = size_type(-1); +#elif nssv_CPP11_OR_GREATER + enum : size_type { npos = size_type(-1) }; +#else + enum { npos = size_type(-1) }; +#endif + +private: + struct not_in_view + { + const basic_string_view v; + + nssv_constexpr explicit not_in_view( basic_string_view v_ ) : v( v_ ) {} + + nssv_constexpr bool operator()( CharT c ) const + { + return npos == v.find_first_of( c ); + } + }; + + nssv_constexpr size_type to_pos( const_iterator it ) const + { + return it == cend() ? npos : size_type( it - cbegin() ); + } + + nssv_constexpr size_type to_pos( const_reverse_iterator it ) const + { + return it == crend() ? npos : size_type( crend() - it - 1 ); + } + + nssv_constexpr const_reference data_at( size_type pos ) const + { +#if nssv_BETWEEN( nssv_COMPILER_GNUC_VERSION, 1, 500 ) + return data_[pos]; +#else + return assert( pos < size() ), data_[pos]; +#endif + } + +private: + const_pointer data_; + size_type size_; + +public: +#if nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS + + template< class Allocator > + basic_string_view( std::basic_string 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() ); + } + +#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 ); + } + +#else + + 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 ); + } + +#endif // nssv_CPP11_OR_GREATER + +#endif // nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS +}; + +// +// Non-member functions: +// + +// 24.4.3 Non-member comparison functions: +// lexicographically compare two string views (function template): + +template< class CharT, class Traits > +nssv_constexpr bool operator== ( + basic_string_view 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.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. + +#if ! nssv_CPP11_OR_GREATER || nssv_BETWEEN( nssv_COMPILER_MSVC_VERSION, 100, 141 ) + +// accommodate for older compilers: + +// == + +template< class CharT, class Traits> +nssv_constexpr bool operator==( + basic_string_view 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==( + 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!=( + 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!=( + 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<( + 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<( + 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<=( + 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<=( + 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>( + 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>( + 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>=( + 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>=( + 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; } + +#else // newer compilers: + +#define nssv_BASIC_STRING_VIEW_I(T,U) typename std::decay< basic_string_view >::type + +#if defined(_MSC_VER) // issue 40 +# define nssv_MSVC_ORDER(x) , int=x +#else +# define nssv_MSVC_ORDER(x) /*, int=x*/ +#endif + +// == + +template< class CharT, class Traits nssv_MSVC_ORDER(1) > +nssv_constexpr bool operator==( + basic_string_view 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(1) > +nssv_constexpr bool operator!= ( + basic_string_view < CharT, Traits > lhs, + nssv_BASIC_STRING_VIEW_I( CharT, Traits ) rhs ) nssv_noexcept +{ return !( lhs == rhs ); } + +template< class CharT, class Traits nssv_MSVC_ORDER(2) > +nssv_constexpr bool operator!= ( + nssv_BASIC_STRING_VIEW_I( CharT, Traits ) lhs, + basic_string_view < CharT, Traits > rhs ) nssv_noexcept +{ return !( lhs == rhs ); } + +// < + +template< class CharT, class Traits nssv_MSVC_ORDER(1) > +nssv_constexpr bool operator< ( + basic_string_view < CharT, Traits > lhs, + nssv_BASIC_STRING_VIEW_I( CharT, Traits ) rhs ) nssv_noexcept +{ return lhs.compare( rhs ) < 0; } + +template< class CharT, class Traits nssv_MSVC_ORDER(2) > +nssv_constexpr bool operator< ( + nssv_BASIC_STRING_VIEW_I( CharT, Traits ) lhs, + basic_string_view < CharT, Traits > rhs ) nssv_noexcept +{ return lhs.compare( rhs ) < 0; } + +// <= + +template< class CharT, class Traits nssv_MSVC_ORDER(1) > +nssv_constexpr bool operator<= ( + basic_string_view < CharT, Traits > lhs, + nssv_BASIC_STRING_VIEW_I( CharT, Traits ) rhs ) nssv_noexcept +{ return lhs.compare( rhs ) <= 0; } + +template< class CharT, class Traits nssv_MSVC_ORDER(2) > +nssv_constexpr bool operator<= ( + nssv_BASIC_STRING_VIEW_I( CharT, Traits ) lhs, + basic_string_view < CharT, Traits > rhs ) nssv_noexcept +{ return lhs.compare( rhs ) <= 0; } + +// > + +template< class CharT, class Traits nssv_MSVC_ORDER(1) > +nssv_constexpr bool operator> ( + basic_string_view < CharT, Traits > lhs, + nssv_BASIC_STRING_VIEW_I( CharT, Traits ) rhs ) nssv_noexcept +{ return lhs.compare( rhs ) > 0; } + +template< class CharT, class Traits nssv_MSVC_ORDER(2) > +nssv_constexpr bool operator> ( + nssv_BASIC_STRING_VIEW_I( CharT, Traits ) lhs, + basic_string_view < CharT, Traits > rhs ) nssv_noexcept +{ return lhs.compare( rhs ) > 0; } + +// >= + +template< class CharT, class Traits nssv_MSVC_ORDER(1) > +nssv_constexpr bool operator>= ( + basic_string_view < CharT, Traits > lhs, + nssv_BASIC_STRING_VIEW_I( CharT, Traits ) rhs ) nssv_noexcept +{ return lhs.compare( rhs ) >= 0; } + +template< class CharT, class Traits nssv_MSVC_ORDER(2) > +nssv_constexpr bool operator>= ( + nssv_BASIC_STRING_VIEW_I( CharT, Traits ) lhs, + basic_string_view < CharT, Traits > rhs ) nssv_noexcept +{ return lhs.compare( rhs ) >= 0; } + +#undef nssv_MSVC_ORDER +#undef nssv_BASIC_STRING_VIEW_I + +#endif // compiler-dependent approach to comparisons + +// 24.4.4 Inserters and extractors: + +#if ! nssv_CONFIG_NO_STREAM_INSERTION + +namespace detail { + +template< class Stream > +void write_padding( Stream & os, std::streamsize n ) +{ + for ( std::streamsize i = 0; i < n; ++i ) + os.rdbuf()->sputc( os.fill() ); +} + +template< class Stream, class View > +Stream & write_to_stream( Stream & os, View const & sv ) +{ + typename Stream::sentry sentry( os ); + + if ( !sentry ) + return os; + + const std::streamsize length = static_cast( sv.length() ); + + // Whether, and how, to pad: + const bool pad = ( length < os.width() ); + const bool left_pad = pad && ( os.flags() & std::ios_base::adjustfield ) == std::ios_base::right; + + if ( left_pad ) + write_padding( os, os.width() - length ); + + // Write span characters: + os.rdbuf()->sputn( sv.begin(), length ); + + if ( pad && !left_pad ) + write_padding( os, os.width() - length ); + + // Reset output stream width: + os.width( 0 ); + + return os; +} + +} // namespace detail + +template< class CharT, class Traits > +std::basic_ostream & +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: + +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; +#endif + +}} // namespace nonstd::sv_lite + +// +// 24.4.6 Suffix for basic_string_view literals: +// + +#if nssv_HAVE_USER_DEFINED_LITERALS + +namespace nonstd { +nssv_inline_ns namespace literals { +nssv_inline_ns namespace string_view_literals { + +#if nssv_CONFIG_STD_SV_OPERATOR && nssv_HAVE_STD_DEFINED_LITERALS + +nssv_constexpr nonstd::sv_lite::string_view operator "" sv( const char* str, size_t len ) nssv_noexcept // (1) +{ + return nonstd::sv_lite::string_view{ str, len }; +} + +nssv_constexpr nonstd::sv_lite::u16string_view operator "" sv( const char16_t* str, size_t len ) nssv_noexcept // (2) +{ + return nonstd::sv_lite::u16string_view{ str, len }; +} + +nssv_constexpr nonstd::sv_lite::u32string_view operator "" sv( const char32_t* str, size_t len ) nssv_noexcept // (3) +{ + return nonstd::sv_lite::u32string_view{ str, len }; +} + +nssv_constexpr nonstd::sv_lite::wstring_view operator "" sv( const wchar_t* str, size_t len ) nssv_noexcept // (4) +{ + return nonstd::sv_lite::wstring_view{ str, len }; +} + +#endif // nssv_CONFIG_STD_SV_OPERATOR && nssv_HAVE_STD_DEFINED_LITERALS + +#if nssv_CONFIG_USR_SV_OPERATOR + +nssv_constexpr nonstd::sv_lite::string_view operator "" _sv( const char* str, size_t len ) nssv_noexcept // (1) +{ + return nonstd::sv_lite::string_view{ str, len }; +} + +nssv_constexpr nonstd::sv_lite::u16string_view operator "" _sv( const char16_t* str, size_t len ) nssv_noexcept // (2) +{ + return nonstd::sv_lite::u16string_view{ str, len }; +} + +nssv_constexpr nonstd::sv_lite::u32string_view operator "" _sv( const char32_t* str, size_t len ) nssv_noexcept // (3) +{ + return nonstd::sv_lite::u32string_view{ str, len }; +} + +nssv_constexpr nonstd::sv_lite::wstring_view operator "" _sv( const wchar_t* str, size_t len ) nssv_noexcept // (4) +{ + return nonstd::sv_lite::wstring_view{ str, len }; +} + +#endif // nssv_CONFIG_USR_SV_OPERATOR + +}}} // namespace nonstd::literals::string_view_literals + +#endif + +// +// Extensions for std::string: +// + +#if nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS + +namespace nonstd { +namespace sv_lite { + +// Exclude MSVC 14 (19.00): it yields ambiguous to_string(): + +#if nssv_CPP11_OR_GREATER && nssv_COMPILER_MSVC_VERSION != 140 + +template< class CharT, class Traits, class Allocator = std::allocator > +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, 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() ); +} + +}} // namespace nonstd::sv_lite + +#endif // nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS + +// +// make types and algorithms available in namespace nonstd: +// + +namespace nonstd { + +using sv_lite::basic_string_view; +using sv_lite::string_view; +using sv_lite::wstring_view; + +#if nssv_HAVE_WCHAR16_T +using sv_lite::u16string_view; +#endif +#if nssv_HAVE_WCHAR32_T +using sv_lite::u32string_view; +#endif + +// literal "sv" + +using sv_lite::operator==; +using sv_lite::operator!=; +using sv_lite::operator<; +using sv_lite::operator<=; +using sv_lite::operator>; +using sv_lite::operator>=; + +#if ! nssv_CONFIG_NO_STREAM_INSERTION +using sv_lite::operator<<; +#endif + +#if nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS +using sv_lite::to_string; +using sv_lite::to_string_view; +#endif + +} // namespace nonstd + +// 24.4.5 Hash support (C++11): + +// Note: The hash value of a string view object is equal to the hash value of +// the corresponding string object. + +#if nssv_HAVE_STD_HASH + +#include + +namespace std { + +template<> +struct hash< nonstd::string_view > +{ +public: + std::size_t operator()( nonstd::string_view v ) const nssv_noexcept + { + return std::hash()( std::string( v.data(), v.size() ) ); + } +}; + +template<> +struct hash< nonstd::wstring_view > +{ +public: + std::size_t operator()( nonstd::wstring_view v ) const nssv_noexcept + { + return std::hash()( std::wstring( v.data(), v.size() ) ); + } +}; + +template<> +struct hash< nonstd::u16string_view > +{ +public: + std::size_t operator()( nonstd::u16string_view v ) const nssv_noexcept + { + return std::hash()( std::u16string( v.data(), v.size() ) ); + } +}; + +template<> +struct hash< nonstd::u32string_view > +{ +public: + std::size_t operator()( nonstd::u32string_view v ) const nssv_noexcept + { + return std::hash()( std::u32string( v.data(), v.size() ) ); + } +}; + +} // namespace std + +#endif // nssv_HAVE_STD_HASH + +nssv_RESTORE_WARNINGS() + +#endif // nssv_HAVE_STD_STRING_VIEW +#endif // NONSTD_SV_LITE_H_INCLUDED +/* end file include/simdjson/nonstd/string_view.hpp */ +SIMDJSON_POP_DISABLE_WARNINGS + +namespace std { + using string_view = nonstd::string_view; +} +#endif // SIMDJSON_HAS_STRING_VIEW +#undef SIMDJSON_HAS_STRING_VIEW // We are not going to need this macro anymore. + +/// If EXPR is an error, returns it. +#define SIMDJSON_TRY(EXPR) { auto _err = (EXPR); if (_err) { return _err; } } + +// Unless the programmer has already set SIMDJSON_DEVELOPMENT_CHECKS, +// we want to set it under debug builds. We detect a debug build +// under Visual Studio when the _DEBUG macro is set. Under the other +// compilers, we use the fact that they define __OPTIMIZE__ whenever +// they allow optimizations. +// It is possible that this could miss some cases where SIMDJSON_DEVELOPMENT_CHECKS +// is helpful, but the programmer can set the macro SIMDJSON_DEVELOPMENT_CHECKS. +// It could also wrongly set SIMDJSON_DEVELOPMENT_CHECKS (e.g., if the programmer +// sets _DEBUG in a release build under Visual Studio, or if some compiler fails to +// set the __OPTIMIZE__ macro). +#ifndef SIMDJSON_DEVELOPMENT_CHECKS +#ifdef _MSC_VER +// Visual Studio seems to set _DEBUG for debug builds. +#ifdef _DEBUG +#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__ +#define SIMDJSON_DEVELOPMENT_CHECKS 1 +#endif // __OPTIMIZE__ +#endif // _MSC_VER +#endif // SIMDJSON_DEVELOPMENT_CHECKS + +// The SIMDJSON_CHECK_EOF macro is a feature flag for the "don't require padding" +// feature. + +#if SIMDJSON_CPLUSPLUS17 +// if we have C++, then fallthrough is a default attribute +# define simdjson_fallthrough [[fallthrough]] +// check if we have __attribute__ support +#elif defined(__has_attribute) +// check if we have the __fallthrough__ attribute +#if __has_attribute(__fallthrough__) +// we are good to go: +# define simdjson_fallthrough __attribute__((__fallthrough__)) +#endif // __has_attribute(__fallthrough__) +#endif // SIMDJSON_CPLUSPLUS17 +// on some systems, we simply do not have support for fallthrough, so use a default: +#ifndef simdjson_fallthrough +# define simdjson_fallthrough do {} while (0) /* fallthrough */ +#endif // simdjson_fallthrough + + +#if SIMDJSON_DEVELOPMENT_CHECKS +#define SIMDJSON_DEVELOPMENT_ASSERT(expr) do { assert ((expr)); } while (0) +#else +#define SIMDJSON_DEVELOPMENT_ASSERT(expr) do { } while (0) +#endif + +#ifndef SIMDJSON_UTF8VALIDATION +#define SIMDJSON_UTF8VALIDATION 1 +#endif + +#endif // SIMDJSON_COMMON_DEFS_H +/* end file include/simdjson/common_defs.h */ + +SIMDJSON_PUSH_DISABLE_WARNINGS +SIMDJSON_DISABLE_UNDESIRED_WARNINGS + +// Public API +/* begin file include/simdjson/error.h */ +#ifndef SIMDJSON_ERROR_H +#define SIMDJSON_ERROR_H + +#include + +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 + 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 reference + 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 + 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 +}; + +/** + * Get the error message for the given error code. + * + * dom::parser parser; + * dom::element doc; + * auto error = parser.parse("foo",3).get(doc); + * if (error) { printf("Error: %s\n", error_message(error)); } + * + * @return The error message. + */ +inline const char *error_message(error_code error) noexcept; + +/** + * Write the error message to the output stream + */ +inline std::ostream& operator<<(std::ostream& out, error_code error) noexcept; + +/** + * Exception thrown when an exception-supporting simdjson method is called + */ +struct simdjson_error : public std::exception { + /** + * Create an exception from a simdjson error code. + * @param error The error code + */ + simdjson_error(error_code error) noexcept : _error{error} { } + /** The error message */ + const char *what() const noexcept { return error_message(error()); } + /** The error code */ + error_code error() const noexcept { return _error; } +private: + /** The error code that was used */ + error_code _error; +}; + +namespace internal { + +/** + * The result of a simdjson operation that could fail. + * + * 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 empty result with error = UNINITIALIZED. + */ + simdjson_inline simdjson_result_base() noexcept; + + /** + * Create a new error result. + */ + simdjson_inline simdjson_result_base(error_code error) noexcept; + + /** + * Create a new successful result. + */ + simdjson_inline simdjson_result_base(T &&value) noexcept; + + /** + * Create a new result with both things (use if you don't want to branch when creating the result). + */ + simdjson_inline simdjson_result_base(T &&value, error_code error) noexcept; + + /** + * Move the value and the error to the provided variables. + * + * @param value The variable to assign the value to. May not be set if there is an error. + * @param error The variable to assign the error to. Set to SUCCESS if there is no error. + */ + simdjson_inline void tie(T &value, error_code &error) && noexcept; + + /** + * Move the value to the provided variable. + * + * @param value The variable to assign the value to. May not be set if there is an error. + */ + simdjson_inline error_code get(T &value) && noexcept; + + /** + * The error. + */ + simdjson_inline error_code error() const noexcept; + +#if SIMDJSON_EXCEPTIONS + + /** + * Get the result value. + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T& value() & noexcept(false); + + /** + * Take the result value (move it). + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T&& value() && noexcept(false); + + /** + * Take the result value (move it). + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T&& take_value() && noexcept(false); + + /** + * Cast to the value (will throw on error). + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline operator T&&() && noexcept(false); +#endif // SIMDJSON_EXCEPTIONS + + /** + * Get the result value. This function is safe if and only + * the error() method returns a value that evaluates to false. + */ + simdjson_inline const T& value_unsafe() const& noexcept; + + /** + * Take the result value (move it). This function is safe if and only + * the error() method returns a value that evaluates to false. + */ + simdjson_inline T&& value_unsafe() && noexcept; + +}; // struct simdjson_result_base + +} // namespace internal + +/** + * The result of a simdjson operation that could fail. + * + * Gives the option of reading error codes, or throwing an exception by casting to the desired result. + */ +template +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 error result. + */ + simdjson_inline simdjson_result(T &&value) noexcept; + /** + * @private Create a new successful 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; + + /** + * The error. + */ + simdjson_inline error_code error() const noexcept; + +#if SIMDJSON_EXCEPTIONS + + /** + * Get the result value. + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T& value() & noexcept(false); + + /** + * Take the result value (move it). + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T&& value() && noexcept(false); + + /** + * Take the result value (move it). + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T&& take_value() && noexcept(false); + + /** + * Cast to the value (will throw on error). + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline operator T&&() && noexcept(false); +#endif // SIMDJSON_EXCEPTIONS + + /** + * Get the result value. This function is safe if and only + * the error() method returns a value that evaluates to false. + */ + simdjson_inline const T& value_unsafe() const& noexcept; + + /** + * Take the result value (move it). This function is safe if and only + * the error() method returns a value that evaluates to false. + */ + simdjson_inline T&& value_unsafe() && noexcept; + +}; // struct simdjson_result + +#if SIMDJSON_EXCEPTIONS + +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 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 include/simdjson/error.h */ +/* begin file include/simdjson/minify.h */ +#ifndef SIMDJSON_MINIFY_H +#define SIMDJSON_MINIFY_H + +/* begin file include/simdjson/padded_string.h */ +#ifndef SIMDJSON_PADDED_STRING_H +#define SIMDJSON_PADDED_STRING_H + +#include +#include +#include +#include + +namespace simdjson { + +class padded_string_view; + +/** + * String with extra allocation for ease of use with parser::parse() + * + * This is a move-only class, it cannot be copied. + */ +struct padded_string final { + + /** + * Create a new, empty padded string. + */ + explicit inline padded_string() noexcept; + /** + * Create a new padded string buffer. + * + * @param length the size of the string. + */ + explicit inline padded_string(size_t length) noexcept; + /** + * Create a new padded string by copying the given input. + * + * @param data the buffer to copy + * @param length the number of bytes to copy + */ + explicit inline padded_string(const char *data, size_t length) noexcept; + /** + * Create a new padded string by copying the given input. + * + * @param str_ the string to copy + */ + inline padded_string(const std::string & str_ ) noexcept; + /** + * Create a new padded string by copying the given input. + * + * @param sv_ the string to copy + */ + inline padded_string(std::string_view sv_) noexcept; + /** + * Move one padded string into another. + * + * The original padded string will be reduced to zero capacity. + * + * @param o the string to move. + */ + inline padded_string(padded_string &&o) noexcept; + /** + * Move one padded string into another. + * + * The original padded string will be reduced to zero capacity. + * + * @param o the string to move. + */ + inline padded_string &operator=(padded_string &&o) noexcept; + inline void swap(padded_string &o) noexcept; + ~padded_string() noexcept; + + /** + * The length of the string. + * + * Does not include padding. + */ + size_t size() const noexcept; + + /** + * The length of the string. + * + * Does not include padding. + */ + size_t length() const noexcept; + + /** + * The string data. + **/ + const char *data() const noexcept; + const uint8_t *u8data() const noexcept { return static_cast(static_cast(data_ptr));} + + /** + * The string data. + **/ + char *data() noexcept; + + /** + * Create a std::string_view with the same content. + */ + operator std::string_view() const; + + /** + * Create a padded_string_view with the same content. + */ + operator padded_string_view() const noexcept; + + /** + * Load this padded string from a file. + * + * @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; + +private: + padded_string &operator=(const padded_string &o) = delete; + padded_string(const padded_string &o) = delete; + + size_t viable_size{0}; + char *data_ptr{nullptr}; + +}; // padded_string + +/** + * 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(); } +#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) { + return simdjson::padded_string(str, len); +} + +namespace simdjson { +namespace internal { + +// The allocate_padded_buffer function is a low-level function to allocate memory +// with padding so we can read past the "length" bytes safely. It is used by +// the padded_string class automatically. It returns nullptr in case +// of error: the caller should check for a null pointer. +// The length parameter is the maximum size in bytes of the string. +// The caller is responsible to free the memory (e.g., delete[] (...)). +inline char *allocate_padded_buffer(size_t length) noexcept; + +} // namespace internal +} // namespace simdjson + +#endif // SIMDJSON_PADDED_STRING_H +/* end file include/simdjson/padded_string.h */ +#include +#include +#include + +namespace simdjson { + + + +/** + * + * Minify the input string assuming that it represents a JSON string, does not parse or validate. + * This function is much faster than parsing a JSON string and then writing a minified version of it. + * However, it does not validate the input. It will merely return an error in simple cases (e.g., if + * there is a string that was never terminated). + * + * + * @param buf the json document to minify. + * @param len the length of the json document. + * @param dst the buffer to write the minified document to. *MUST* be allocated up to len bytes. + * @param dst_len the number of bytes written. Output only. + * @return the error code, or SUCCESS if there was no error. + */ +simdjson_warn_unused error_code minify(const char *buf, size_t len, char *dst, size_t &dst_len) noexcept; + +} // namespace simdjson + +#endif // SIMDJSON_MINIFY_H +/* end file include/simdjson/minify.h */ +/* begin file include/simdjson/padded_string_view.h */ +#ifndef SIMDJSON_PADDED_STRING_VIEW_H +#define SIMDJSON_PADDED_STRING_VIEW_H + + +#include +#include +#include +#include + +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; + +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. + */ + 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 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. + */ + 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 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) { return out << s.value(); } +#endif + +} // namespace simdjson + +#endif // SIMDJSON_PADDED_STRING_VIEW_H +/* end file include/simdjson/padded_string_view.h */ +/* begin file include/simdjson/implementation.h */ +#ifndef SIMDJSON_IMPLEMENTATION_H +#define SIMDJSON_IMPLEMENTATION_H + +/* begin file include/simdjson/internal/dom_parser_implementation.h */ +#ifndef SIMDJSON_INTERNAL_DOM_PARSER_IMPLEMENTATION_H +#define SIMDJSON_INTERNAL_DOM_PARSER_IMPLEMENTATION_H + +#include + +namespace simdjson { + +namespace dom { +class document; +} // namespace dom + +/** +* This enum is used with the dom_parser_implementation::stage1 function. +* 1) The regular mode expects a fully formed JSON document. +* 2) The streaming_partial mode expects a possibly truncated +* input within a stream on JSON documents. +* 3) The stream_final mode allows us to truncate final +* unterminated strings. It is useful in conjunction with streaming_partial. +*/ +enum class stage1_mode { regular, streaming_partial, streaming_final}; + +/** + * Returns true if mode == streaming_partial or mode == streaming_final + */ +inline bool is_streaming(stage1_mode mode) { + // performance note: it is probably faster to check that mode is different + // from regular than checking that it is either streaming_partial or streaming_final. + return (mode != stage1_mode::regular); + // return (mode == stage1_mode::streaming_partial || mode == stage1_mode::streaming_final); +} + + +namespace internal { + + +/** + * An implementation of simdjson's DOM parser for a particular CPU architecture. + * + * This class is expected to be accessed only by pointer, and never move in memory (though the + * pointer can move). + */ +class dom_parser_implementation { +public: + + /** + * @private For internal implementation use + * + * Run a full JSON parse on a single document (stage1 + stage2). + * + * Guaranteed only to be called when capacity > document length. + * + * Overridden by each implementation. + * + * @param buf The json document to parse. *MUST* be allocated up to len + SIMDJSON_PADDING bytes. + * @param len The length of the json document. + * @return The error code, or SUCCESS if there was no error. + */ + simdjson_warn_unused virtual error_code parse(const uint8_t *buf, size_t len, dom::document &doc) noexcept = 0; + + /** + * @private For internal implementation use + * + * Stage 1 of the document parser. + * + * Guaranteed only to be called when capacity > document length. + * + * Overridden by each implementation. + * + * @param buf The json document to parse. + * @param len The length of the json document. + * @param streaming Whether this is being called by parser::parse_many. + * @return The error code, or SUCCESS if there was no error. + */ + simdjson_warn_unused virtual error_code stage1(const uint8_t *buf, size_t len, stage1_mode streaming) noexcept = 0; + + /** + * @private For internal implementation use + * + * Stage 2 of the document parser. + * + * Called after stage1(). + * + * Overridden by each implementation. + * + * @param doc The document to output to. + * @return The error code, or SUCCESS if there was no error. + */ + simdjson_warn_unused virtual error_code stage2(dom::document &doc) noexcept = 0; + + /** + * @private For internal implementation use + * + * Stage 2 of the document parser for parser::parse_many. + * + * Guaranteed only to be called after stage1(). + * Overridden by each implementation. + * + * @param doc The document to output to. + * @return The error code, SUCCESS if there was no error, or EMPTY if all documents have been parsed. + */ + simdjson_warn_unused virtual error_code stage2_next(dom::document &doc) noexcept = 0; + + /** + * Unescape a valid UTF-8 string from src to dst, stopping at a final unescaped quote. There + * must be an unescaped quote terminating the string. It returns the final output + * position as pointer. In case of error (e.g., the string has bad escaped codes), + * then null_nullptrptr 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_nullptrptr is returned. It is assumed that the output buffer is large + * enough. E.g., if src points at 'joe"', then dst needs to have four free bytes + + * SIMDJSON_PADDING bytes. + * + * Overridden by each implementation. + * + * @param str pointer to the beginning of a possibly invalid UTF-8 JSON string, must end with an unescaped quote. + * @param dst pointer to a destination buffer, it must point a region in memory of sufficient size. + * @return end of the of the written region (exclusive) or nullptr in case of error. + */ + simdjson_warn_unused virtual uint8_t *parse_wobbly_string(const uint8_t *src, uint8_t *dst) const noexcept = 0; + + /** + * Change the capacity of this parser. + * + * The capacity can never exceed SIMDJSON_MAXSIZE_BYTES (e.g., 4 GB) + * and an CAPACITY error is returned if it is attempted. + * + * Generally used for reallocation. + * + * @param capacity The new capacity. + * @param max_depth The new max_depth. + * @return The error code, or SUCCESS if there was no error. + */ + virtual error_code set_capacity(size_t capacity) noexcept = 0; + + /** + * Change the max depth of this parser. + * + * Generally used for reallocation. + * + * @param capacity The new capacity. + * @param max_depth The new max_depth. + * @return The error code, or SUCCESS if there was no error. + */ + virtual error_code set_max_depth(size_t max_depth) noexcept = 0; + + /** + * Deallocate this parser. + */ + virtual ~dom_parser_implementation() = default; + + /** Number of structural indices passed from stage 1 to stage 2 */ + uint32_t n_structural_indexes{0}; + /** Structural indices passed from stage 1 to stage 2 */ + std::unique_ptr 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_inline size_t capacity() const noexcept; + + /** + * The maximum level of nested object and arrays supported by this parser. + * + * @return Maximum depth, in bytes. + */ + simdjson_inline size_t max_depth() const noexcept; + + /** + * Ensure this parser has enough memory to process JSON documents up to `capacity` bytes in length + * and `max_depth` depth. + * + * @param capacity The new capacity. + * @param max_depth The new max_depth. Defaults to DEFAULT_MAX_DEPTH. + * @return The error, if there is one. + */ + simdjson_warn_unused inline error_code allocate(size_t capacity, size_t max_depth) noexcept; + + +protected: + /** + * The maximum document length this parser supports. + * + * Buffers are large enough to handle any document up to this length. + */ + size_t _capacity{0}; + + /** + * The maximum depth (number of nested objects and arrays) supported by this parser. + * + * Defaults to DEFAULT_MAX_DEPTH. + */ + size_t _max_depth{0}; + + // Declaring these so that subclasses can use them to implement their constructors. + simdjson_inline dom_parser_implementation() noexcept; + simdjson_inline dom_parser_implementation(dom_parser_implementation &&other) noexcept; + simdjson_inline dom_parser_implementation &operator=(dom_parser_implementation &&other) noexcept; + + simdjson_inline dom_parser_implementation(const dom_parser_implementation &) noexcept = delete; + simdjson_inline dom_parser_implementation &operator=(const dom_parser_implementation &other) noexcept = delete; +}; // class dom_parser_implementation + +simdjson_inline dom_parser_implementation::dom_parser_implementation() noexcept = default; +simdjson_inline dom_parser_implementation::dom_parser_implementation(dom_parser_implementation &&other) noexcept = default; +simdjson_inline dom_parser_implementation &dom_parser_implementation::operator=(dom_parser_implementation &&other) noexcept = default; + +simdjson_inline size_t dom_parser_implementation::capacity() const noexcept { + return _capacity; +} + +simdjson_inline size_t dom_parser_implementation::max_depth() const noexcept { + return _max_depth; +} + +simdjson_warn_unused +inline error_code dom_parser_implementation::allocate(size_t capacity, size_t max_depth) noexcept { + if (this->max_depth() != max_depth) { + error_code err = set_max_depth(max_depth); + if (err) { return err; } + } + if (_capacity != capacity) { + error_code err = set_capacity(capacity); + if (err) { return err; } + } + return SUCCESS; +} + +} // namespace internal +} // namespace simdjson + +#endif // SIMDJSON_INTERNAL_DOM_PARSER_IMPLEMENTATION_H +/* end file include/simdjson/internal/dom_parser_implementation.h */ +/* begin file include/simdjson/internal/isadetection.h */ +/* From +https://github.com/endorno/pytorch/blob/master/torch/lib/TH/generic/simd/simd.h +Highly modified. + +Copyright (c) 2016- Facebook, Inc (Adam Paszke) +Copyright (c) 2014- Facebook, Inc (Soumith Chintala) +Copyright (c) 2011-2014 Idiap Research Institute (Ronan Collobert) +Copyright (c) 2012-2014 Deepmind Technologies (Koray Kavukcuoglu) +Copyright (c) 2011-2012 NEC Laboratories America (Koray Kavukcuoglu) +Copyright (c) 2011-2013 NYU (Clement Farabet) +Copyright (c) 2006-2010 NEC Laboratories America (Ronan Collobert, Leon Bottou, +Iain Melvin, Jason Weston) Copyright (c) 2006 Idiap Research Institute +(Samy Bengio) Copyright (c) 2001-2004 Idiap Research Institute (Ronan Collobert, +Samy Bengio, Johnny Mariethoz) + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the names of Facebook, Deepmind Technologies, NYU, NEC Laboratories +America and IDIAP Research Institute nor the names of its contributors may be + used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef SIMDJSON_INTERNAL_ISADETECTION_H +#define SIMDJSON_INTERNAL_ISADETECTION_H + +#include +#include +#if defined(_MSC_VER) +#include +#elif defined(HAVE_GCC_GET_CPUID) && defined(USE_GCC_GET_CPUID) +#include +#endif + +namespace simdjson { +namespace internal { + +enum instruction_set { + DEFAULT = 0x0, + NEON = 0x1, + AVX2 = 0x4, + SSE42 = 0x8, + PCLMULQDQ = 0x10, + BMI1 = 0x20, + BMI2 = 0x40, + ALTIVEC = 0x80, + AVX512F = 0x100, + AVX512DQ = 0x200, + AVX512IFMA = 0x400, + AVX512PF = 0x800, + AVX512ER = 0x1000, + AVX512CD = 0x2000, + AVX512BW = 0x4000, + AVX512VL = 0x8000, + AVX512VBMI2 = 0x10000 +}; + +#if defined(__PPC64__) + +static inline uint32_t detect_supported_architectures() { + return instruction_set::ALTIVEC; +} + +#elif defined(__aarch64__) || defined(_M_ARM64) + +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 +} + + + +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]; +#elif defined(HAVE_GCC_GET_CPUID) && defined(USE_GCC_GET_CPUID) + 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; +#endif +} + + +static inline uint64_t xgetbv() { +#if defined(_MSC_VER) + 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); +#endif +} + +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); + + 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_osxsave) != cpuid_osxsave) { + return host_isa; + } + + // xgetbv for checking if the OS saves registers + uint64_t xcr0 = xgetbv(); + + 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; + } + + if (ebx & cpuid_bmi2_bit) { + host_isa |= instruction_set::BMI2; + } + + if (!((xcr0 & cpuid_avx512_saved) == cpuid_avx512_saved)) { + return host_isa; + } + + if (ebx & cpuid_avx512f_bit) { + host_isa |= instruction_set::AVX512F; + } + + if (ebx & cpuid_avx512dq_bit) { + host_isa |= instruction_set::AVX512DQ; + } + + if (ebx & cpuid_avx512ifma_bit) { + host_isa |= instruction_set::AVX512IFMA; + } + + if (ebx & cpuid_avx512pf_bit) { + host_isa |= instruction_set::AVX512PF; + } + + if (ebx & cpuid_avx512er_bit) { + host_isa |= instruction_set::AVX512ER; + } + + if (ebx & cpuid_avx512cd_bit) { + host_isa |= instruction_set::AVX512CD; + } + + if (ebx & cpuid_avx512bw_bit) { + host_isa |= instruction_set::AVX512BW; + } + + if (ebx & cpuid_avx512vl_bit) { + host_isa |= instruction_set::AVX512VL; + } + + if (ecx & cpuid_avx512vbmi2_bit) { + host_isa |= instruction_set::AVX512VBMI2; + } + + return host_isa; +} +#else // fallback + + +static inline uint32_t detect_supported_architectures() { + return instruction_set::DEFAULT; +} + + +#endif // end SIMD extension detection code + +} // namespace internal +} // namespace simdjson + +#endif // SIMDJSON_INTERNAL_ISADETECTION_H +/* end file include/simdjson/internal/isadetection.h */ +#include +#include +#include + +namespace simdjson { + +/** + * Validate the UTF-8 string. + * + * @param buf the string to validate. + * @param len the length of the string in bytes. + * @return true if the string is valid UTF-8. + */ +simdjson_warn_unused bool validate_utf8(const char * buf, size_t len) noexcept; +/** + * Validate the UTF-8 string. + * + * @param sv the string_view to validate. + * @return true if the string is valid UTF-8. + */ +simdjson_inline simdjson_warn_unused bool validate_utf8(const std::string_view sv) noexcept { + return validate_utf8(sv.data(), sv.size()); +} + +/** + * Validate the UTF-8 string. + * + * @param p the string to validate. + * @return true if the string is valid UTF-8. + */ +simdjson_inline simdjson_warn_unused bool validate_utf8(const std::string& s) noexcept { + return validate_utf8(s.data(), s.size()); +} + +namespace dom { + class document; +} // namespace dom + +/** + * 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 const std::string &name() const { return _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 const std::string &description() const { return _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) + { + } + virtual ~implementation()=default; + +private: + /** + * The name of this implementation. + */ + const std::string _name; + + /** + * The description of this implementation. + */ + const std::string _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; +}; + +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 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; +}; + +} // 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(); + +} // namespace simdjson + +#endif // SIMDJSON_IMPLEMENTATION_H +/* end file include/simdjson/implementation.h */ + +// Inline functions +/* begin file include/simdjson/error-inl.h */ +#ifndef SIMDJSON_INLINE_ERROR_H +#define SIMDJSON_INLINE_ERROR_H + +#include +#include +#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 const char *error_message(error_code error) noexcept { + // If you're using error_code, we're trusting you got it from the enum. + return internal::error_codes[int(error)].message; +} + +// deprecated function +#ifndef SIMDJSON_DISABLE_DEPRECATED_API +inline const std::string error_message(int error) noexcept { + if (error < 0 || error >= error_code::NUM_ERROR_CODES) { + return internal::error_codes[UNEXPECTED_ERROR].message; + } + return internal::error_codes[error].message; +} +#endif // SIMDJSON_DISABLE_DEPRECATED_API + +inline std::ostream& operator<<(std::ostream& out, error_code error) noexcept { + return out << error_message(error); +} + +namespace internal { + +// +// internal::simdjson_result_base 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_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; +} + +#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::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 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) {} + +} // namespace internal + +/// +/// simdjson_result inline implementation +/// + +template +simdjson_inline void simdjson_result::tie(T &value, error_code &error) && noexcept { + std::forward>(*this).tie(value, 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 std::forward>(*this).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(); +} + +#endif // SIMDJSON_EXCEPTIONS + +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 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 + +#endif // SIMDJSON_INLINE_ERROR_H +/* end file include/simdjson/error-inl.h */ +/* begin file include/simdjson/padded_string-inl.h */ +#ifndef SIMDJSON_INLINE_PADDED_STRING_H +#define SIMDJSON_INLINE_PADDED_STRING_H + + +#include +#include +#include +#include + +namespace simdjson { +namespace internal { + +// The allocate_padded_buffer function is a low-level function to allocate memory +// with padding so we can read past the "length" bytes safely. It is used by +// the padded_string class automatically. It returns nullptr in case +// of error: the caller should check for a null pointer. +// The length parameter is the maximum size in bytes of the string. +// The caller is responsible to free the memory (e.g., delete[] (...)). +inline char *allocate_padded_buffer(size_t length) noexcept { + const size_t totalpaddedlength = length + SIMDJSON_PADDING; + if(totalpaddedlength(1UL<<20)) { + return nullptr; + } +#endif + + char *padded_buffer = new (std::nothrow) char[totalpaddedlength]; + if (padded_buffer == nullptr) { + return nullptr; + } + // We write nulls in the padded region to avoid having uninitialized + // content which may trigger warning for some sanitizers + std::memset(padded_buffer + length, 0, totalpaddedlength - length); + return padded_buffer; +} // allocate_padded_buffer() + +} // namespace internal + + +inline padded_string::padded_string() noexcept = default; +inline padded_string::padded_string(size_t length) noexcept + : viable_size(length), data_ptr(internal::allocate_padded_buffer(length)) { +} +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); + } +} +// 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 +} + +inline padded_string &padded_string::operator=(padded_string &&o) noexcept { + delete[] data_ptr; + data_ptr = o.data_ptr; + viable_size = o.viable_size; + o.data_ptr = nullptr; // we take ownership + o.viable_size = 0; + return *this; +} + +inline void padded_string::swap(padded_string &o) noexcept { + size_t tmp_viable_size = viable_size; + char *tmp_data_ptr = data_ptr; + viable_size = o.viable_size; + data_ptr = o.data_ptr; + o.data_ptr = tmp_data_ptr; + o.viable_size = tmp_viable_size; +} + +inline padded_string::~padded_string() noexcept { + delete[] data_ptr; +} + +inline size_t padded_string::size() const noexcept { return viable_size; } + +inline size_t padded_string::length() const noexcept { return viable_size; } + +inline const char *padded_string::data() const noexcept { return data_ptr; } + +inline char *padded_string::data() noexcept { return data_ptr; } + +inline padded_string::operator std::string_view() const { return std::string_view(data(), length()); } + +inline padded_string::operator padded_string_view() const noexcept { + return padded_string_view(data(), length(), length() + SIMDJSON_PADDING); +} + +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 + + 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; +} + +} // namespace simdjson + +#endif // SIMDJSON_INLINE_PADDED_STRING_H +/* end file include/simdjson/padded_string-inl.h */ +/* begin file include/simdjson/padded_string_view-inl.h */ +#ifndef SIMDJSON_PADDED_STRING_VIEW_INL_H +#define SIMDJSON_PADDED_STRING_VIEW_INL_H + + +#include +#include +#include +#include + +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) +{ +} + +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 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) +{ +} + +inline size_t padded_string_view::capacity() const noexcept { return _capacity; } + +inline size_t padded_string_view::padding() const noexcept { return capacity() - length(); } + +} // namespace simdjson + +#endif // SIMDJSON_PADDED_STRING_VIEW_INL_H +/* end file include/simdjson/padded_string_view-inl.h */ + +SIMDJSON_POP_DISABLE_WARNINGS + +#endif // SIMDJSON_BASE_H +/* end file include/simdjson/base.h */ + +SIMDJSON_PUSH_DISABLE_WARNINGS +SIMDJSON_DISABLE_UNDESIRED_WARNINGS + +/* begin file include/simdjson/dom/array.h */ +#ifndef SIMDJSON_DOM_ARRAY_H +#define SIMDJSON_DOM_ARRAY_H + +/* begin file include/simdjson/internal/tape_ref.h */ +#ifndef SIMDJSON_INTERNAL_TAPE_REF_H +#define SIMDJSON_INTERNAL_TAPE_REF_H + +/* begin file include/simdjson/internal/tape_type.h */ +#ifndef SIMDJSON_INTERNAL_TAPE_TYPE_H +#define SIMDJSON_INTERNAL_TAPE_TYPE_H + +namespace simdjson { +namespace internal { + +/** + * The possible types in the tape. + */ +enum class tape_type { + ROOT = 'r', + START_ARRAY = '[', + START_OBJECT = '{', + END_ARRAY = ']', + END_OBJECT = '}', + STRING = '"', + INT64 = 'l', + UINT64 = 'u', + DOUBLE = 'd', + TRUE_VALUE = 't', + FALSE_VALUE = 'f', + NULL_VALUE = 'n' +}; // enum class tape_type + +} // namespace internal +} // namespace simdjson + +#endif // SIMDJSON_INTERNAL_TAPE_TYPE_H +/* end file include/simdjson/internal/tape_type.h */ + +namespace simdjson { + +namespace dom { + class document; +} + +namespace internal { + +constexpr const uint64_t JSON_VALUE_MASK = 0x00FFFFFFFFFFFFFF; +constexpr const uint32_t JSON_COUNT_MASK = 0xFFFFFF; + +/** + * 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 index of this element on `doc.tape[]` */ + size_t json_index; +}; + +} // namespace internal +} // namespace simdjson + +#endif // SIMDJSON_INTERNAL_TAPE_REF_H +/* end file include/simdjson/internal/tape_ref.h */ + +namespace simdjson { + +namespace internal { +template +class string_builder; +} +namespace dom { + +class document; +class element; + +/** + * 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; + + /** + * Get the actual value + */ + inline value_type operator*() const noexcept; + /** + * Get the next value. + * + * Part of the std::iterator interface. + */ + inline iterator& operator++() noexcept; + /** + * Get the next value. + * + * Part of the std::iterator interface. + */ + inline iterator operator++(int) noexcept; + /** + * Check if these values come from the same place in the JSON. + * + * Part of the std::iterator interface. + */ + inline bool operator!=(const iterator& other) const noexcept; + inline bool operator==(const iterator& other) const noexcept; + + inline bool operator<(const iterator& other) const noexcept; + inline bool operator<=(const iterator& other) const noexcept; + inline bool operator>=(const iterator& other) const noexcept; + inline bool operator>(const iterator& other) const noexcept; + + iterator() noexcept = default; + iterator(const iterator&) noexcept = default; + iterator& operator=(const iterator&) noexcept = default; + private: + simdjson_inline iterator(const internal::tape_ref &tape) noexcept; + internal::tape_ref tape; + friend class array; + }; + + /** + * 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; + +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 simdjson_result at(size_t index) 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); +#endif // SIMDJSON_EXCEPTIONS +}; + + + +} // namespace simdjson + +#if defined(__cpp_lib_ranges) +#include + +namespace std { +namespace ranges { +template<> +inline constexpr bool enable_view = true; +#if SIMDJSON_EXCEPTIONS +template<> +inline constexpr bool enable_view> = true; +#endif // SIMDJSON_EXCEPTIONS +} // namespace ranges +} // namespace std +#endif // defined(__cpp_lib_ranges) + +#endif // SIMDJSON_DOM_ARRAY_H +/* end file include/simdjson/dom/array.h */ +/* begin file include/simdjson/dom/document_stream.h */ +#ifndef SIMDJSON_DOCUMENT_STREAM_H +#define SIMDJSON_DOCUMENT_STREAM_H + +/* begin file include/simdjson/dom/parser.h */ +#ifndef SIMDJSON_DOM_PARSER_H +#define SIMDJSON_DOM_PARSER_H + +/* begin file include/simdjson/dom/document.h */ +#ifndef SIMDJSON_DOM_DOCUMENT_H +#define SIMDJSON_DOM_DOCUMENT_H + +#include +#include + +namespace simdjson { +namespace dom { + +class element; + +/** + * A parsed JSON document. + * + * This class cannot be copied, only moved, to avoid unintended allocations. + */ +class document { +public: + /** + * Create a document container with zero capacity. + * + * The parser will allocate capacity as needed. + */ + document() noexcept = default; + ~document() noexcept = default; + + /** + * Take another document's buffers. + * + * @param other The document to take. Its capacity is zeroed and it is invalidated. + */ + document(document &&other) noexcept = default; + /** @private */ + document(const document &) = delete; // Disallow copying + /** + * Take another document's buffers. + * + * @param other The document to take. Its capacity is zeroed. + */ + document &operator=(document &&other) noexcept = default; + /** @private */ + document &operator=(const document &) = delete; // Disallow copying + + /** + * Get the root element of this document as a JSON array. + */ + element root() const noexcept; + + /** + * @private Dump the raw tape for debugging. + * + * @param os the stream to output to. + * @return false if the tape is likely wrong (e.g., you did not parse a valid JSON). + */ + bool dump_raw_tape(std::ostream &os) const noexcept; + + /** @private Structural values. */ + std::unique_ptr 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: + size_t allocated_capacity{0}; + friend class parser; +}; // class document + +} // namespace dom +} // namespace simdjson + +#endif // SIMDJSON_DOM_DOCUMENT_H +/* end file include/simdjson/dom/document.h */ +#include +#include +#include + +namespace simdjson { + +namespace dom { + +class document_stream; +class element; + +/** 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; + +/** + * 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; + + /** + * 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). + * + * @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 ; + /** + * 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; + + /** @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; + + /** @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. + * + * ### 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; + + /** + * 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; + * } + * + * ### 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 + + /** @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; + +#ifndef SIMDJSON_DISABLE_DEPRECATED_API + /** + * @private deprecated because it returns bool instead of error_code, which is our standard for + * failures. Use allocate() instead. + * + * Ensure this parser has enough memory to process JSON documents up to `capacity` bytes in length + * and `max_depth` depth. + * + * @param capacity The new capacity. + * @param max_depth The new max_depth. Defaults to DEFAULT_MAX_DEPTH. + * @return true if successful, false if allocation failed. + */ + [[deprecated("Use allocate() instead.")]] + simdjson_warn_unused inline bool allocate_capacity(size_t capacity, size_t max_depth = DEFAULT_MAX_DEPTH) noexcept; +#endif // SIMDJSON_DISABLE_DEPRECATED_API + /** + * The largest document this parser can support without reallocating. + * + * @return Current capacity, in bytes. + */ + simdjson_inline size_t capacity() const noexcept; + + /** + * The largest document this parser can automatically support. + * + * The parser may reallocate internal buffers as needed up to this amount. + * + * @return Maximum capacity, in bytes. + */ + simdjson_inline size_t max_capacity() const noexcept; + + /** + * The maximum level of nested object and arrays supported by this parser. + * + * @return Maximum depth, in bytes. + */ + simdjson_inline size_t max_depth() const noexcept; + + /** + * Set max_capacity. This is the largest document this parser can automatically support. + * + * The parser may reallocate internal buffers as needed up to this amount as documents are passed + * to it. + * + * Note: To avoid limiting the memory to an absurd value, such as zero or two bytes, + * iff you try to set max_capacity to a value lower than MINIMAL_DOCUMENT_CAPACITY, + * then the maximal capacity is set to MINIMAL_DOCUMENT_CAPACITY. + * + * This call will not allocate or deallocate, even if capacity is currently above max_capacity. + * + * @param max_capacity The new maximum capacity, in bytes. + */ + simdjson_inline void set_max_capacity(size_t max_capacity) noexcept; + +#ifdef SIMDJSON_THREADS_ENABLED + /** + * The parser instance can use threads when they are available to speed up some + * operations. It is enabled by default. Changing this attribute will change the + * behavior of the parser for future operations. + */ + bool threaded{true}; +#endif + /** @private Use the new DOM API instead */ + class Iterator; + /** @private Use simdjson_error instead */ + using InvalidJSON [[deprecated("Use simdjson_error instead")]] = simdjson_error; + + /** @private [for benchmarking access] The implementation to use */ + std::unique_ptr implementation{}; + + /** @private Use `if (parser.parse(...).error())` instead */ + bool valid{false}; + /** @private Use `parser.parse(...).error()` instead */ + error_code error{UNINITIALIZED}; + + /** @private Use `parser.parse(...).value()` instead */ + document doc{}; + + /** @private returns true if the document parsed was valid */ + [[deprecated("Use the result of parser.parse() instead")]] + inline bool is_valid() const noexcept; + + /** + * @private return an error code corresponding to the last parsing attempt, see + * simdjson.h will return UNINITIALIZED if no parsing was attempted + */ + [[deprecated("Use the result of parser.parse() instead")]] + inline int get_error_code() const noexcept; + + /** @private return the string equivalent of "get_error_code" */ + [[deprecated("Use error_message() on the result of parser.parse() instead, or cout << error")]] + inline std::string get_error_message() const noexcept; + + /** @private */ + [[deprecated("Use cout << on the result of parser.parse() instead")]] + inline bool print_json(std::ostream &os) const noexcept; + + /** @private Private and deprecated: use `parser.parse(...).doc.dump_raw_tape()` instead */ + inline bool dump_raw_tape(std::ostream &os) const noexcept; + + +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; + + /** Capacity of loaded_bytes buffer. */ + size_t _loaded_bytes_capacity{0}; + + // all nodes are stored on the doc.tape using a 64-bit word. + // + // strings, double and ints are stored as + // a 64-bit word with a pointer to the actual value + // + // + // + // for objects or arrays, store [ or { at the beginning and } and ] at the + // end. For the openings ([ or {), we annotate them with a reference to the + // location on the doc.tape of the end, and for then closings (} and ]), we + // annotate them with a reference to the location of the opening + // + // + + /** + * Ensure we have enough capacity to handle at least desired_capacity bytes, + * and auto-allocate if not. This also allocates memory if needed in the + * internal document. + */ + inline error_code ensure_capacity(size_t desired_capacity) noexcept; + /** + * Ensure we have enough capacity to handle at least desired_capacity bytes, + * and auto-allocate if not. This also allocates memory if needed in the + * provided document. + */ + inline error_code ensure_capacity(document& doc, size_t desired_capacity) noexcept; + + /** Read the file into loaded_bytes */ + inline simdjson_result read_file(const std::string &path) noexcept; + + friend class parser::Iterator; + friend class document_stream; + + +}; // class parser + +} // namespace dom +} // namespace simdjson + +#endif // SIMDJSON_DOM_PARSER_H +/* end file include/simdjson/dom/parser.h */ +#ifdef SIMDJSON_THREADS_ENABLED +#include +#include +#include +#endif + +namespace simdjson { +namespace dom { + + +#ifdef SIMDJSON_THREADS_ENABLED +/** @private Custom worker class **/ +struct stage1_worker { + stage1_worker() noexcept = default; + stage1_worker(const stage1_worker&) = delete; + stage1_worker(stage1_worker&&) = delete; + stage1_worker operator=(const stage1_worker&) = delete; + ~stage1_worker(); + /** + * We only start the thread when it is needed, not at object construction, this may throw. + * You should only call this once. + **/ + void start_thread(); + /** + * Start a stage 1 job. You should first call 'run', then 'finish'. + * You must call start_thread once before. + */ + void run(document_stream * ds, dom::parser * stage1, size_t next_batch_start); + /** Wait for the run to finish (blocking). You should first call 'run', then 'finish'. **/ + void finish(); + +private: + + /** + * Normally, we would never stop the thread. But we do in the destructor. + * This function is only safe assuming that you are not waiting for results. You + * should have called run, then finish, and be done. + **/ + void stop_thread(); + + std::thread thread{}; + /** These three variables define the work done by the thread. **/ + dom::parser * stage1_thread_parser{}; + size_t _next_batch_start{}; + document_stream * owner{}; + /** + * We have two state variables. This could be streamlined to one variable in the future but + * we use two for clarity. + */ + bool has_work{false}; + bool can_work{true}; + + /** + * We lock using a mutex. + */ + std::mutex locking_mutex{}; + std::condition_variable cond_var{}; +}; +#endif + +/** + * A forward-only stream of documents. + * + * Produced by parser::parse_many. + * + */ +class document_stream { +public: + /** + * Construct an uninitialized document_stream. + * + * ```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; + + 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 iterator_category = std::input_iterator_tag; + + /** + * Default constructor. + */ + simdjson_inline iterator() noexcept; + /** + * Get the current document (or error). + */ + simdjson_inline reference operator*() noexcept; + /** + * Advance to the next document (prefix). + */ + inline iterator& operator++() noexcept; + /** + * Check if we're at the end yet. + * @param other the end iterator to compare to. + */ + simdjson_inline bool operator!=(const iterator &other) const noexcept; + /** + * @private + * + * Gives the current index in the input document in bytes. + * + * document_stream stream = parser.parse_many(json,window); + * for(auto i = stream.begin(); i != stream.end(); ++i) { + * auto doc = *i; + * size_t index = i.current_index(); + * } + * + * This function (current_index()) is experimental and the usage + * may change in future versions of simdjson: we find the API somewhat + * awkward and we would like to offer something friendlier. + */ + simdjson_inline size_t current_index() const noexcept; + /** + * @private + * + * Gives a view of the current document. + * + * document_stream stream = parser.parse_many(json,window); + * for(auto i = stream.begin(); i != stream.end(); ++i) { + * auto doc = *i; + * std::string_view v = i->source(); + * } + * + * The returned string_view instance is simply a map to the (unparsed) + * source string: it may thus include white-space characters and all manner + * of padding. + * + * This function (source()) is experimental and the usage + * may change in future versions of simdjson: we find the API somewhat + * awkward and we would like to offer something friendlier. + */ + simdjson_inline std::string_view source() const noexcept; + + private: + simdjson_inline iterator(document_stream *s, bool finished) noexcept; + /** The document_stream we're iterating through. */ + document_stream* stream; + /** Whether we're finished or not. */ + bool finished; + friend class document_stream; + }; + + /** + * Start iterating the documents in the stream. + */ + simdjson_inline iterator begin() noexcept; + /** + * The end of the stream, for iterator comparison purposes. + */ + simdjson_inline iterator end() noexcept; + +private: + + document_stream &operator=(const document_stream &) = delete; // Disallow copying + document_stream(const document_stream &other) = delete; // Disallow copying + + /** + * Construct a document_stream. Does not allocate or parse anything until the iterator is + * used. + * + * @param parser is a reference to the parser instance used to generate this document_stream + * @param buf is the raw byte buffer we need to process + * @param len is the length of the raw byte buffer in bytes + * @param batch_size is the size of the windows (must be strictly greater or equal to the largest JSON document) + */ + 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 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 caller is responsible for omitting it, UTF-8 BOM are + * discouraged. + * + * You do NOT need to pre-allocate a parser. This function takes care of + * pre-allocating a capacity defined by the batch_size defined when creating the + * document_stream object. + * + * The function returns simdjson::EMPTY if there is no more data to be parsed. + * + * The function returns simdjson::SUCCESS (as integer = 0) in case of success + * and indicates that the buffer has successfully been parsed to the end. + * Every document it contained has been parsed without error. + * + * The function returns an error code from simdjson/simdjson.h in case of failure + * such as simdjson::CAPACITY, simdjson::MEMALLOC, simdjson::DEPTH_ERROR and so forth; + * the simdjson::error_message function converts these error codes into a string). + * + * You can also check validity by calling parser.is_valid(). The same parser can + * and should be reused for the other documents in the buffer. + */ + inline void next() noexcept; + + /** + * Pass the next batch through stage 1 and return when finished. + * When threads are enabled, this may wait for the stage 1 thread to finish. + */ + inline void load_batch() noexcept; + + /** Get the next document index. */ + inline size_t next_batch_start() const noexcept; + + /** Pass the next batch through stage 1 with the given parser. */ + inline error_code run_stage1(dom::parser &p, size_t batch_start) noexcept; + + dom::parser *parser; + const uint8_t *buf; + size_t len; + size_t batch_size; + /** The error (or lack thereof) from the current document. */ + error_code error; + size_t batch_start{0}; + size_t doc_index{}; +#ifdef SIMDJSON_THREADS_ENABLED + /** Indicates whether we use threads. Note that this needs to be a constant during the execution of the parsing. */ + bool use_thread; + + inline void load_from_stage1_thread() noexcept; + + /** Start a thread to run stage 1 on the next batch. */ + inline void start_stage1_thread() noexcept; + + /** Wait for the stage 1 thread to finish and capture the results. */ + inline void finish_stage1_thread() noexcept; + + /** The error returned from the stage 1 thread. */ + error_code stage1_thread_error{UNINITIALIZED}; + /** The thread used to run stage 1 against the next batch in the background. */ + friend struct stage1_worker; + std::unique_ptr 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; + +}; // class document_stream + +} // 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 + +#if SIMDJSON_EXCEPTIONS + simdjson_inline dom::document_stream::iterator begin() noexcept(false); + simdjson_inline dom::document_stream::iterator end() noexcept(false); +#else // SIMDJSON_EXCEPTIONS +#ifndef SIMDJSON_DISABLE_DEPRECATED_API + [[deprecated("parse_many() and load_many() may return errors. Use document_stream stream; error = parser.parse_many().get(doc); instead.")]] + simdjson_inline dom::document_stream::iterator begin() noexcept; + [[deprecated("parse_many() and load_many() may return errors. Use document_stream stream; error = parser.parse_many().get(doc); instead.")]] + simdjson_inline dom::document_stream::iterator end() noexcept; +#endif // SIMDJSON_DISABLE_DEPRECATED_API +#endif // SIMDJSON_EXCEPTIONS +}; // struct simdjson_result + +} // namespace simdjson + +#endif // SIMDJSON_DOCUMENT_STREAM_H +/* end file include/simdjson/dom/document_stream.h */ +/* begin file include/simdjson/dom/element.h */ +#ifndef SIMDJSON_DOM_ELEMENT_H +#define SIMDJSON_DOM_ELEMENT_H + +#include + +namespace simdjson { +namespace internal { +template +class string_builder; +} +namespace dom { +class array; +class document; +class object; + +/** + * 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; + + /** 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; + + /** + * 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 `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; + + /** + * 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."); + } + + /** + * 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; + +#if SIMDJSON_EXCEPTIONS + /** + * Read this element as a boolean. + * + * @return The boolean value + * @exception simdjson_error(INCORRECT_TYPE) if the JSON element is not a boolean. + */ + inline operator bool() const noexcept(false); + + /** + * Read this element as a null-terminated UTF-8 string. + * + * Be mindful that JSON allows strings to contain null characters. + * + * Does *not* convert other types to a string; requires that the JSON type of the element was + * an actual string. + * + * @return The string value. + * @exception simdjson_error(INCORRECT_TYPE) if the JSON element is not a string. + */ + inline explicit operator const char*() const noexcept(false); + + /** + * Read this element as a null-terminated UTF-8 string. + * + * Does *not* convert other types to a string; requires that the JSON type of the element was + * an actual string. + * + * @return The string value. + * @exception simdjson_error(INCORRECT_TYPE) if the JSON element is not a string. + */ + inline operator std::string_view() const noexcept(false); + + /** + * Read this element as an unsigned integer. + * + * @return The integer value. + * @exception simdjson_error(INCORRECT_TYPE) if the JSON element is not an integer + * @exception simdjson_error(NUMBER_OUT_OF_RANGE) if the integer doesn't 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 doesn't 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 + * @exception simdjson_error(NUMBER_OUT_OF_RANGE) if the integer doesn't fit in 64 bits or is negative + */ + inline operator double() const noexcept(false); + /** + * Read this element as a JSON array. + * + * @return The JSON array. + * @exception simdjson_error(INCORRECT_TYPE) if the JSON element is not an array + */ + inline operator array() const noexcept(false); + /** + * Read this element as a JSON object (key/value pairs). + * + * @return The JSON object. + * @exception simdjson_error(INCORRECT_TYPE) if the JSON element is not an object + */ + inline operator object() const noexcept(false); + + /** + * Iterate over each element in this array. + * + * @return The beginning of the iteration. + * @exception simdjson_error(INCORRECT_TYPE) if the JSON element is not an array + */ + inline dom::array::iterator begin() const noexcept(false); + + /** + * Iterate over each element in this array. + * + * @return The end of the iteration. + * @exception simdjson_error(INCORRECT_TYPE) if the JSON element is not an array + */ + inline dom::array::iterator end() const noexcept(false); +#endif // SIMDJSON_EXCEPTIONS + + /** + * Get the value associated with the given key. + * + * The key will be matched against **unescaped** JSON: + * + * dom::parser parser; + * int64_t(parser.parse(R"({ "a\n": 1 })"_padded)["a\n"]) == 1 + * parser.parse(R"({ "a\n": 1 })"_padded)["a\\n"].get_uint64().error() == NO_SUCH_FIELD + * + * @return The value associated with this field, or: + * - NO_SUCH_FIELD if the field does not exist in the object + * - INCORRECT_TYPE if this is not an object + */ + inline simdjson_result 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 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; + +#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; +#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 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; + + /** @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; + +}; + +} // 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 + + 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 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; + +#if SIMDJSON_EXCEPTIONS + simdjson_inline operator bool() const noexcept(false); + simdjson_inline explicit operator const char*() const noexcept(false); + simdjson_inline operator std::string_view() const noexcept(false); + simdjson_inline operator uint64_t() const noexcept(false); + simdjson_inline operator int64_t() const noexcept(false); + simdjson_inline operator double() const noexcept(false); + simdjson_inline operator dom::array() const noexcept(false); + simdjson_inline operator dom::object() const noexcept(false); + + simdjson_inline dom::array::iterator begin() const noexcept(false); + simdjson_inline dom::array::iterator end() const noexcept(false); +#endif // SIMDJSON_EXCEPTIONS +}; + + +} // namespace simdjson + +#endif // SIMDJSON_DOM_DOCUMENT_H +/* end file include/simdjson/dom/element.h */ +/* begin file include/simdjson/dom/object.h */ +#ifndef SIMDJSON_DOM_OBJECT_H +#define SIMDJSON_DOM_OBJECT_H + + +namespace simdjson { +namespace internal { +template +class string_builder; +} +namespace dom { + +class document; +class element; +class key_value_pair; + +/** + * JSON object. + */ +class object { +public: + /** Create a new, invalid object */ + simdjson_inline object() noexcept; + + class iterator { + public: + using value_type = key_value_pair; + using difference_type = std::ptrdiff_t; + + /** + * Get the actual key/value pair + */ + inline const value_type operator*() const noexcept; + /** + * Get the next key/value pair. + * + * Part of the std::iterator interface. + * + */ + inline iterator& operator++() noexcept; + /** + * Get the next key/value pair. + * + * Part of the std::iterator interface. + * + */ + inline iterator operator++(int) noexcept; + /** + * Check if these values come from the same place in the JSON. + * + * Part of the std::iterator interface. + */ + inline bool operator!=(const iterator& other) const noexcept; + inline bool operator==(const iterator& other) const noexcept; + + inline bool operator<(const iterator& other) const noexcept; + inline bool operator<=(const iterator& other) const noexcept; + inline bool operator>=(const iterator& other) const noexcept; + inline bool operator>(const iterator& other) const noexcept; + /** + * Get the key of this key/value pair. + */ + inline std::string_view key() const noexcept; + /** + * Get the length (in bytes) of the key in this key/value pair. + * You should expect this function to be faster than key().size(). + */ + inline uint32_t key_length() const noexcept; + /** + * Returns true if the key in this key/value pair is equal + * to the provided string_view. + */ + inline bool key_equals(std::string_view o) const noexcept; + /** + * Returns true if the key in this key/value pair is equal + * to the provided string_view in a case-insensitive manner. + * Case comparisons may only be handled correctly for ASCII strings. + */ + inline bool key_equals_case_insensitive(std::string_view o) const noexcept; + /** + * Get the key of this key/value pair. + */ + inline const char *key_c_str() const noexcept; + /** + * Get the value of this key/value pair. + */ + inline element value() const noexcept; + + iterator() noexcept = default; + iterator(const iterator&) noexcept = default; + iterator& operator=(const iterator&) noexcept = default; + private: + simdjson_inline iterator(const internal::tape_ref &tape) noexcept; + + internal::tape_ref tape; + + friend class object; + }; + + /** + * Return the first key/value pair. + * + * Part of the std::iterable interface. + */ + inline iterator begin() const noexcept; + /** + * One past the last key/value pair. + * + * Part of the std::iterable interface. + */ + inline iterator end() const noexcept; + /** + * Get the size of the object (number of keys). + * It is a saturated value with a maximum of 0xFFFFFF: if the value + * is 0xFFFFFF then the size is 0xFFFFFF or greater. + */ + inline size_t size() const noexcept; + /** + * Get the value associated with the given key. + * + * The key will be matched against **unescaped** JSON: + * + * dom::parser parser; + * int64_t(parser.parse(R"({ "a\n": 1 })"_padded)["a\n"]) == 1 + * parser.parse(R"({ "a\n": 1 })"_padded)["a\\n"].get_uint64().error() == NO_SUCH_FIELD + * + * This function has linear-time complexity: the keys are checked one by one. + * + * @return The value associated with this field, or: + * - NO_SUCH_FIELD if the field does not exist in the object + * - INCORRECT_TYPE if this is not an object + */ + inline simdjson_result 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 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; + + /** + * 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; + +private: + simdjson_inline object(const internal::tape_ref &tape) noexcept; + + internal::tape_ref tape; + + friend class element; + friend struct simdjson_result; + template + friend class simdjson::internal::string_builder; +}; + +/** + * Key/value pair in an object. + */ +class key_value_pair { +public: + /** key in the key-value pair **/ + std::string_view key; + /** value in the key-value pair **/ + element value; + +private: + simdjson_inline key_value_pair(std::string_view _key, element _value) noexcept; + friend class object; +}; + +} // namespace dom + +/** The result of a JSON conversion that may fail. */ +template<> +struct simdjson_result : 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; + 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; + +#if SIMDJSON_EXCEPTIONS + inline dom::object::iterator begin() const noexcept(false); + inline dom::object::iterator end() const noexcept(false); + inline size_t size() const noexcept(false); +#endif // SIMDJSON_EXCEPTIONS +}; + +} // namespace simdjson + +#if defined(__cpp_lib_ranges) +#include + +namespace std { +namespace ranges { +template<> +inline constexpr bool enable_view = true; +#if SIMDJSON_EXCEPTIONS +template<> +inline constexpr bool enable_view> = true; +#endif // SIMDJSON_EXCEPTIONS +} // namespace ranges +} // namespace std +#endif // defined(__cpp_lib_ranges) + +#endif // SIMDJSON_DOM_OBJECT_H +/* end file include/simdjson/dom/object.h */ +/* begin file include/simdjson/dom/serialization.h */ +#ifndef SIMDJSON_SERIALIZATION_H +#define SIMDJSON_SERIALIZATION_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 { + +class mini_formatter; + +/** + * @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{}; +}; + +/** + * @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: + mini_formatter() = default; + /** 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; + +private: + // implementation details (subject to change) + /** Prints one character **/ + simdjson_inline void one_char(char c); + /** Backing buffer **/ + std::vector buffer{}; // not ideal! +}; + +} // 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) { + 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()); +} +#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) { + 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()); +} +#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) { + 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()); +} +#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); +} + +#if SIMDJSON_EXCEPTIONS +template +std::string minify(simdjson_result x) { + if (x.error()) { throw simdjson_error(x.error()); } + return to_string(x.value()); +} +#endif + + +} // namespace simdjson + + +#endif +/* end file include/simdjson/dom/serialization.h */ + +// Deprecated API +/* begin file include/simdjson/dom/jsonparser.h */ +// TODO Remove this -- deprecated API and files + +#ifndef SIMDJSON_DOM_JSONPARSER_H +#define SIMDJSON_DOM_JSONPARSER_H + +/* begin file include/simdjson/dom/parsedjson.h */ +// TODO Remove this -- deprecated API and files + +#ifndef SIMDJSON_DOM_PARSEDJSON_H +#define SIMDJSON_DOM_PARSEDJSON_H + + +namespace simdjson { + +/** + * @deprecated Use `dom::parser` instead. + */ +using ParsedJson [[deprecated("Use dom::parser instead")]] = dom::parser; + +} // namespace simdjson + +#endif // SIMDJSON_DOM_PARSEDJSON_H +/* end file include/simdjson/dom/parsedjson.h */ +/* begin file include/simdjson/jsonioutil.h */ +#ifndef SIMDJSON_JSONIOUTIL_H +#define SIMDJSON_JSONIOUTIL_H + + +namespace simdjson { + +#if SIMDJSON_EXCEPTIONS +#ifndef SIMDJSON_DISABLE_DEPRECATED_API +[[deprecated("Use padded_string::load() instead")]] +inline padded_string get_corpus(const char *path) { + return padded_string::load(path); +} +#endif // SIMDJSON_DISABLE_DEPRECATED_API +#endif // SIMDJSON_EXCEPTIONS + +} // namespace simdjson + +#endif // SIMDJSON_JSONIOUTIL_H +/* end file include/simdjson/jsonioutil.h */ + +namespace simdjson { + +// +// C API (json_parse and build_parsed_json) declarations +// + +#ifndef SIMDJSON_DISABLE_DEPRECATED_API +[[deprecated("Use parser.parse() instead")]] +inline int json_parse(const uint8_t *buf, size_t len, dom::parser &parser, bool realloc_if_needed = true) noexcept { + error_code code = parser.parse(buf, len, realloc_if_needed).error(); + // The deprecated json_parse API is a signal that the user plans to *use* the error code / valid + // bits in the parser instead of heeding the result code. The normal parser unsets those in + // anticipation of making the error code ephemeral. + // Here we put the code back into the parser, until we've removed this method. + parser.valid = code == SUCCESS; + parser.error = code; + return code; +} +[[deprecated("Use parser.parse() instead")]] +inline int json_parse(const char *buf, size_t len, dom::parser &parser, bool realloc_if_needed = true) noexcept { + error_code code = parser.parse(buf, len, realloc_if_needed).error(); + // The deprecated json_parse API is a signal that the user plans to *use* the error code / valid + // bits in the parser instead of heeding the result code. The normal parser unsets those in + // anticipation of making the error code ephemeral. + // Here we put the code back into the parser, until we've removed this method. + parser.valid = code == SUCCESS; + parser.error = code; + return code; +} +[[deprecated("Use parser.parse() instead")]] +inline int json_parse(const std::string &s, dom::parser &parser, bool realloc_if_needed = true) noexcept { + error_code code = parser.parse(s.data(), s.length(), realloc_if_needed).error(); + // The deprecated json_parse API is a signal that the user plans to *use* the error code / valid + // bits in the parser instead of heeding the result code. The normal parser unsets those in + // anticipation of making the error code ephemeral. + // Here we put the code back into the parser, until we've removed this method. + parser.valid = code == SUCCESS; + parser.error = code; + return code; +} +[[deprecated("Use parser.parse() instead")]] +inline int json_parse(const padded_string &s, dom::parser &parser) noexcept { + error_code code = parser.parse(s).error(); + // The deprecated json_parse API is a signal that the user plans to *use* the error code / valid + // bits in the parser instead of heeding the result code. The normal parser unsets those in + // anticipation of making the error code ephemeral. + // Here we put the code back into the parser, until we've removed this method. + parser.valid = code == SUCCESS; + parser.error = code; + return code; +} + +[[deprecated("Use parser.parse() instead")]] +simdjson_warn_unused inline dom::parser build_parsed_json(const uint8_t *buf, size_t len, bool realloc_if_needed = true) noexcept { + dom::parser parser; + error_code code = parser.parse(buf, len, realloc_if_needed).error(); + // The deprecated json_parse API is a signal that the user plans to *use* the error code / valid + // bits in the parser instead of heeding the result code. The normal parser unsets those in + // anticipation of making the error code ephemeral. + // Here we put the code back into the parser, until we've removed this method. + parser.valid = code == SUCCESS; + parser.error = code; + return parser; +} +[[deprecated("Use parser.parse() instead")]] +simdjson_warn_unused inline dom::parser build_parsed_json(const char *buf, size_t len, bool realloc_if_needed = true) noexcept { + dom::parser parser; + error_code code = parser.parse(buf, len, realloc_if_needed).error(); + // The deprecated json_parse API is a signal that the user plans to *use* the error code / valid + // bits in the parser instead of heeding the result code. The normal parser unsets those in + // anticipation of making the error code ephemeral. + // Here we put the code back into the parser, until we've removed this method. + parser.valid = code == SUCCESS; + parser.error = code; + return parser; +} +[[deprecated("Use parser.parse() instead")]] +simdjson_warn_unused inline dom::parser build_parsed_json(const std::string &s, bool realloc_if_needed = true) noexcept { + dom::parser parser; + error_code code = parser.parse(s.data(), s.length(), realloc_if_needed).error(); + // The deprecated json_parse API is a signal that the user plans to *use* the error code / valid + // bits in the parser instead of heeding the result code. The normal parser unsets those in + // anticipation of making the error code ephemeral. + // Here we put the code back into the parser, until we've removed this method. + parser.valid = code == SUCCESS; + parser.error = code; + return parser; +} +[[deprecated("Use parser.parse() instead")]] +simdjson_warn_unused inline dom::parser build_parsed_json(const padded_string &s) noexcept { + dom::parser parser; + error_code code = parser.parse(s).error(); + // The deprecated json_parse API is a signal that the user plans to *use* the error code / valid + // bits in the parser instead of heeding the result code. The normal parser unsets those in + // anticipation of making the error code ephemeral. + // Here we put the code back into the parser, until we've removed this method. + parser.valid = code == SUCCESS; + parser.error = code; + return parser; +} +#endif // SIMDJSON_DISABLE_DEPRECATED_API + +/** @private We do not want to allow implicit conversion from C string to std::string. */ +int json_parse(const char *buf, dom::parser &parser) noexcept = delete; +/** @private We do not want to allow implicit conversion from C string to std::string. */ +dom::parser build_parsed_json(const char *buf) noexcept = delete; + +} // namespace simdjson + +#endif // SIMDJSON_DOM_JSONPARSER_H +/* end file include/simdjson/dom/jsonparser.h */ +/* begin file include/simdjson/dom/parsedjson_iterator.h */ +// TODO Remove this -- deprecated API and files + +#ifndef SIMDJSON_DOM_PARSEDJSON_ITERATOR_H +#define SIMDJSON_DOM_PARSEDJSON_ITERATOR_H + +#include +#include +#include +#include +#include +#include + +/* begin file include/simdjson/internal/jsonformatutils.h */ +#ifndef SIMDJSON_INTERNAL_JSONFORMATUTILS_H +#define SIMDJSON_INTERNAL_JSONFORMATUTILS_H + +#include +#include +#include + +namespace simdjson { +namespace internal { + +class escape_json_string; + +inline std::ostream& operator<<(std::ostream& out, const escape_json_string &str); + +class escape_json_string { +public: + escape_json_string(std::string_view _str) noexcept : str{_str} {} + operator std::string() const noexcept { std::stringstream s; s << *this; return s.str(); } +private: + std::string_view str; + friend std::ostream& operator<<(std::ostream& out, const escape_json_string &unescaped); +}; + +inline std::ostream& operator<<(std::ostream& out, const escape_json_string &unescaped) { + for (size_t i=0; i(unescaped.str[i]) <= 0x1F) { + // TODO can this be done once at the beginning, or will it mess up << char? + std::ios::fmtflags f(out.flags()); + out << "\\u" << std::hex << std::setw(4) << std::setfill('0') << int(unescaped.str[i]); + out.flags(f); + } else { + out << unescaped.str[i]; + } + } + } + return out; +} + +} // namespace internal +} // namespace simdjson + +#endif // SIMDJSON_INTERNAL_JSONFORMATUTILS_H +/* end file include/simdjson/internal/jsonformatutils.h */ + +#ifndef SIMDJSON_DISABLE_DEPRECATED_API + +namespace simdjson { +/** @private **/ +class [[deprecated("Use the new DOM navigation API instead (see doc/basics.md)")]] dom::parser::Iterator { +public: + inline Iterator(const dom::parser &parser) noexcept(false); + inline Iterator(const Iterator &o) noexcept; + inline ~Iterator() noexcept; + + inline Iterator& operator=(const Iterator&) = delete; + + inline bool is_ok() const; + + // useful for debugging purposes + inline size_t get_tape_location() const; + + // useful for debugging purposes + inline size_t get_tape_length() const; + + // returns the current depth (start at 1 with 0 reserved for the fictitious + // root node) + inline size_t get_depth() const; + + // A scope is a series of nodes at the same depth, typically it is either an + // object ({) or an array ([). The root node has type 'r'. + inline uint8_t get_scope_type() const; + + // move forward in document order + inline bool move_forward(); + + // retrieve the character code of what we're looking at: + // [{"slutfn are the possibilities + inline uint8_t get_type() const { + return current_type; // short functions should be inlined! + } + + // get the int64_t value at this node; valid only if get_type is "l" + inline int64_t get_integer() const { + if (location + 1 >= tape_length) { + return 0; // default value in case of error + } + return static_cast(doc.tape[location + 1]); + } + + // get the value as uint64; valid only if if get_type is "u" + inline uint64_t get_unsigned_integer() const { + if (location + 1 >= tape_length) { + return 0; // default value in case of error + } + return doc.tape[location + 1]; + } + + // get the string value at this node (NULL ended); valid only if get_type is " + // note that tabs, and line endings are escaped in the returned value (see + // print_with_escapes) return value is valid UTF-8, it may contain NULL chars + // within the string: get_string_length determines the true string length. + inline const char *get_string() const { + return reinterpret_cast( + doc.string_buf.get() + (current_val & internal::JSON_VALUE_MASK) + sizeof(uint32_t)); + } + + // return the length of the string in bytes + inline uint32_t get_string_length() const { + uint32_t answer; + std::memcpy(&answer, + reinterpret_cast(doc.string_buf.get() + + (current_val & internal::JSON_VALUE_MASK)), + sizeof(uint32_t)); + return answer; + } + + // get the double value at this node; valid only if + // get_type() is "d" + inline double get_double() const { + if (location + 1 >= tape_length) { + return std::numeric_limits::quiet_NaN(); // default value in + // case of error + } + double answer; + std::memcpy(&answer, &doc.tape[location + 1], sizeof(answer)); + return answer; + } + + inline bool is_object_or_array() const { return is_object() || is_array(); } + + inline bool is_object() const { return get_type() == '{'; } + + inline bool is_array() const { return get_type() == '['; } + + inline bool is_string() const { return get_type() == '"'; } + + // Returns true if the current type of the node is an signed integer. + // You can get its value with `get_integer()`. + inline bool is_integer() const { return get_type() == 'l'; } + + // Returns true if the current type of the node is an unsigned integer. + // You can get its value with `get_unsigned_integer()`. + // + // NOTE: + // Only a large value, which is out of range of a 64-bit signed integer, is + // represented internally as an unsigned node. On the other hand, a typical + // positive integer, such as 1, 42, or 1000000, is as a signed node. + // Be aware this function returns false for a signed node. + inline bool is_unsigned_integer() const { return get_type() == 'u'; } + // Returns true if the current type of the node is a double floating-point number. + inline bool is_double() const { return get_type() == 'd'; } + // Returns true if the current type of the node is a number (integer or floating-point). + inline bool is_number() const { + return is_integer() || is_unsigned_integer() || is_double(); + } + // Returns true if the current type of the node is a bool with true value. + inline bool is_true() const { return get_type() == 't'; } + // Returns true if the current type of the node is a bool with false value. + inline bool is_false() const { return get_type() == 'f'; } + // Returns true if the current type of the node is null. + inline bool is_null() const { return get_type() == 'n'; } + // Returns true if the type byte represents an object of an array + static bool is_object_or_array(uint8_t type) { + return ((type == '[') || (type == '{')); + } + + // when at {, go one level deep, looking for a given key + // if successful, we are left pointing at the value, + // if not, we are still pointing at the object ({) + // (in case of repeated keys, this only finds the first one). + // We seek the key using C's strcmp so if your JSON strings contain + // NULL chars, this would trigger a false positive: if you expect that + // to be the case, take extra precautions. + // Furthermore, we do the comparison character-by-character + // without taking into account Unicode equivalence. + inline bool move_to_key(const char *key); + + // as above, but case insensitive lookup (strcmpi instead of strcmp) + inline bool move_to_key_insensitive(const char *key); + + // when at {, go one level deep, looking for a given key + // if successful, we are left pointing at the value, + // if not, we are still pointing at the object ({) + // (in case of repeated keys, this only finds the first one). + // The string we search for can contain NULL values. + // Furthermore, we do the comparison character-by-character + // without taking into account Unicode equivalence. + inline bool move_to_key(const char *key, uint32_t length); + + // when at a key location within an object, this moves to the accompanying + // value (located next to it). This is equivalent but much faster than + // calling "next()". + inline void move_to_value(); + + // when at [, go one level deep, and advance to the given index. + // if successful, we are left pointing at the value, + // if not, we are still pointing at the array ([) + inline bool move_to_index(uint32_t index); + + // Moves the iterator to the value corresponding to the json pointer. + // Always search from the root of the document. + // if successful, we are left pointing at the value, + // if not, we are still pointing the same value we were pointing before the + // call. The json pointer follows the rfc6901 standard's syntax: + // https://tools.ietf.org/html/rfc6901 However, the standard says "If a + // referenced member name is not unique in an object, the member that is + // referenced is undefined, and evaluation fails". Here we just return the + // first corresponding value. The length parameter is the length of the + // jsonpointer string ('pointer'). + inline bool move_to(const char *pointer, uint32_t length); + + // Moves the iterator to the value corresponding to the json pointer. + // Always search from the root of the document. + // if successful, we are left pointing at the value, + // if not, we are still pointing the same value we were pointing before the + // call. The json pointer implementation follows the rfc6901 standard's + // syntax: https://tools.ietf.org/html/rfc6901 However, the standard says + // "If a referenced member name is not unique in an object, the member that + // is referenced is undefined, and evaluation fails". Here we just return + // the first corresponding value. + inline bool move_to(const std::string &pointer) { + return move_to(pointer.c_str(), uint32_t(pointer.length())); + } + + private: + // Almost the same as move_to(), except it searches from the current + // position. The pointer's syntax is identical, though that case is not + // handled by the rfc6901 standard. The '/' is still required at the + // beginning. However, contrary to move_to(), the URI Fragment Identifier + // Representation is not supported here. Also, in case of failure, we are + // left pointing at the closest value it could reach. For these reasons it + // is private. It exists because it is used by move_to(). + inline bool relative_move_to(const char *pointer, uint32_t length); + + public: + // throughout return true if we can do the navigation, false + // otherwise + + // Within a given scope (series of nodes at the same depth within either an + // array or an object), we move forward. + // Thus, given [true, null, {"a":1}, [1,2]], we would visit true, null, { + // and [. At the object ({) or at the array ([), you can issue a "down" to + // visit their content. valid if we're not at the end of a scope (returns + // true). + inline bool next(); + + // Within a given scope (series of nodes at the same depth within either an + // array or an object), we move backward. + // Thus, given [true, null, {"a":1}, [1,2]], we would visit ], }, null, true + // when starting at the end of the scope. At the object ({) or at the array + // ([), you can issue a "down" to visit their content. + // Performance warning: This function is implemented by starting again + // from the beginning of the scope and scanning forward. You should expect + // it to be relatively slow. + inline bool prev(); + + // Moves back to either the containing array or object (type { or [) from + // within a contained scope. + // Valid unless we are at the first level of the document + inline bool up(); + + // Valid if we're at a [ or { and it starts a non-empty scope; moves us to + // start of that deeper scope if it not empty. Thus, given [true, null, + // {"a":1}, [1,2]], if we are at the { node, we would move to the "a" node. + inline bool down(); + + // move us to the start of our current scope, + // a scope is a series of nodes at the same level + inline void to_start_scope(); + + inline void rewind() { + while (up()) + ; + } + + + + // print the node we are currently pointing at + inline bool print(std::ostream &os, bool escape_strings = true) const; + + private: + const document &doc; + size_t max_depth{}; + size_t depth{}; + size_t location{}; // our current location on a tape + size_t tape_length{}; + uint8_t current_type{}; + uint64_t current_val{}; + typedef struct { + size_t start_of_scope; + uint8_t scope_type; + } scopeindex_t; + + scopeindex_t *depth_index{}; +}; + +} // namespace simdjson +#endif // SIMDJSON_DISABLE_DEPRECATED_API + +#endif // SIMDJSON_DOM_PARSEDJSON_ITERATOR_H +/* end file include/simdjson/dom/parsedjson_iterator.h */ + +// Inline functions +/* begin file include/simdjson/dom/array-inl.h */ +#ifndef SIMDJSON_INLINE_ARRAY_H +#define SIMDJSON_INLINE_ARRAY_H + +// Inline implementations go in here. + +#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::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(); +} + +#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(size_t index) const noexcept { + if (error()) { return error(); } + return first.at(index); +} + +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(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; +} + +// +// 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 + +/* begin file include/simdjson/dom/element-inl.h */ +#ifndef SIMDJSON_INLINE_ELEMENT_H +#define SIMDJSON_INLINE_ELEMENT_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(); +} + +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 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 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 +} +#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); +} + +#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 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 { + +// +// 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 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_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(); } + +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(); +} + +#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 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::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 is invalid on an atom + return INVALID_JSON_POINTER; + } + // an empty string means that we return the current node + dom::element copy(*this); + return simdjson_result(std::move(copy)); + } + } +} +#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); +} +#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::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 + } +} + +} // namespace dom + +} // namespace simdjson + +#endif // SIMDJSON_INLINE_ELEMENT_H +/* end file include/simdjson/dom/element-inl.h */ + +#if defined(__cpp_lib_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_INLINE_ARRAY_H +/* end file include/simdjson/dom/array-inl.h */ +/* begin file include/simdjson/dom/document_stream-inl.h */ +#ifndef SIMDJSON_INLINE_DOCUMENT_STREAM_H +#define SIMDJSON_INLINE_DOCUMENT_STREAM_H + +#include +#include +#include +namespace simdjson { +namespace dom { + +#ifdef SIMDJSON_THREADS_ENABLED +inline void stage1_worker::finish() { + // After calling "run" someone would call finish() to wait + // for the end of the processing. + // This function will wait until either the thread has done + // the processing or, else, the destructor has been called. + std::unique_lock 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, 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} +#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}, + error{UNINITIALIZED} +#ifdef SIMDJSON_THREADS_ENABLED + , use_thread(false) +#endif +{ +} + +simdjson_inline document_stream::~document_stream() noexcept { +#ifdef SIMDJSON_THREADS_ENABLED + worker.reset(); +#endif +} + +simdjson_inline document_stream::iterator::iterator() noexcept + : stream{nullptr}, finished{true} { +} + +simdjson_inline document_stream::iterator document_stream::begin() noexcept { + start(); + // If there are no documents, we're finished. + return iterator(this, error == EMPTY); +} + +simdjson_inline document_stream::iterator document_stream::end() noexcept { + return iterator(this, true); +} + +simdjson_inline document_stream::iterator::iterator(document_stream* _stream, bool is_end) noexcept + : stream{_stream}, finished{is_end} { +} + +simdjson_inline document_stream::iterator::reference document_stream::iterator::operator*() noexcept { + // Note that in case of error, we do not yet mark + // the iterator as "finished": this detection is done + // in the operator++ function since it is possible + // to call operator++ repeatedly while omitting + // calls to operator*. + if (stream->error) { return stream->error; } + return stream->parser->doc.root(); +} + +simdjson_inline document_stream::iterator& document_stream::iterator::operator++() noexcept { + // If there is an error, then we want the iterator + // to be finished, no matter what. (E.g., we do not + // keep generating documents with errors, or go beyond + // a document with errors.) + // + // Users do not have to call "operator*()" when they use operator++, + // so we need to end the stream in the operator++ function. + // + // Note that setting finished = true is essential otherwise + // we would enter an infinite loop. + if (stream->error) { finished = true; } + // Note that stream->error() is guarded against error conditions + // (it will immediately return if stream->error casts to false). + // In effect, this next function does nothing when (stream->error) + // is true (hence the risk of an infinite loop). + stream->next(); + // If that was the last document, we're finished. + // It is the only type of error we do not want to appear + // in operator*. + if (stream->error == EMPTY) { finished = true; } + // If we had any other kind of error (not EMPTY) then we want + // to pass it along to the operator* and we cannot mark the result + // as "finished" just yet. + return *this; +} + +simdjson_inline bool document_stream::iterator::operator!=(const document_stream::iterator &other) const noexcept { + return finished != other.finished; +} + +inline void document_stream::start() noexcept { + if (error) { return; } + error = parser->ensure_capacity(batch_size); + if (error) { return; } + // Always run the first stage 1 parse immediately + batch_start = 0; + error = run_stage1(*parser, batch_start); + while(error == EMPTY) { + // In exceptional cases, we may start with an empty block + batch_start = next_batch_start(); + if (batch_start >= len) { return; } + error = run_stage1(*parser, batch_start); + } + if (error) { return; } +#ifdef SIMDJSON_THREADS_ENABLED + if (use_thread && next_batch_start() < len) { + // Kick off the first thread if needed + error = stage1_thread_parser.ensure_capacity(batch_size); + if (error) { return; } + worker->start_thread(); + start_stage1_thread(); + if (error) { return; } + } +#endif // SIMDJSON_THREADS_ENABLED + next(); +} + +simdjson_inline size_t document_stream::iterator::current_index() const noexcept { + return stream->doc_index; +} + +simdjson_inline std::string_view document_stream::iterator::source() const noexcept { + const char* start = reinterpret_cast(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]; + return std::string_view(reinterpret_cast(stream->buf) + current_index(), next_doc_index - current_index() - 1); + } +} + + +inline void document_stream::next() noexcept { + // We always exit at once, once in an error condition. + if (error) { return; } + + // Load the next document from the batch + doc_index = batch_start + parser->implementation->structural_indexes[parser->implementation->next_structural_index]; + error = parser->implementation->stage2_next(parser->doc); + // If that was the last document in the batch, load another batch (if available) + while (error == EMPTY) { + batch_start = next_batch_start(); + if (batch_start >= len) { break; } + +#ifdef SIMDJSON_THREADS_ENABLED + if(use_thread) { + load_from_stage1_thread(); + } else { + error = run_stage1(*parser, batch_start); + } +#else + error = run_stage1(*parser, batch_start); +#endif + if (error) { continue; } // If the error was EMPTY, we may want to load another batch. + // Run stage 2 on the first document in the batch + doc_index = batch_start + parser->implementation->structural_indexes[parser->implementation->next_structural_index]; + error = parser->implementation->stage2_next(parser->doc); + } +} +inline size_t document_stream::size_in_bytes() const noexcept { + return len; +} + +inline size_t document_stream::truncated_bytes() const noexcept { + if(error == CAPACITY) { return len - batch_start; } + return parser->implementation->structural_indexes[parser->implementation->n_structural_indexes] - parser->implementation->structural_indexes[parser->implementation->n_structural_indexes + 1]; +} + +inline size_t document_stream::next_batch_start() const noexcept { + return batch_start + parser->implementation->structural_indexes[parser->implementation->n_structural_indexes]; +} + +inline error_code document_stream::run_stage1(dom::parser &p, size_t _batch_start) noexcept { + size_t remaining = len - _batch_start; + 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; } + + // If there's anything left, start the stage 1 thread! + if (next_batch_start() < len) { + start_stage1_thread(); + } +} + +inline void document_stream::start_stage1_thread() noexcept { + // we call the thread on a lambda that will update + // this->stage1_thread_error + // there is only one thread that may write to this value + // TODO this is NOT exception-safe. + this->stage1_thread_error = UNINITIALIZED; // In case something goes wrong, make sure it's an error + size_t _next_batch_start = this->next_batch_start(); + + worker->run(this, & this->stage1_thread_parser, _next_batch_start); +} + +#endif // SIMDJSON_THREADS_ENABLED + +} // namespace dom + +simdjson_inline simdjson_result::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(); +} +#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(); +} +#endif // SIMDJSON_DISABLE_DEPRECATED_API +#endif // SIMDJSON_EXCEPTIONS + +} // namespace simdjson +#endif // SIMDJSON_INLINE_DOCUMENT_STREAM_H +/* end file include/simdjson/dom/document_stream-inl.h */ +/* begin file include/simdjson/dom/document-inl.h */ +#ifndef SIMDJSON_INLINE_DOCUMENT_H +#define SIMDJSON_INLINE_DOCUMENT_H + +// Inline implementations go in here. + +#include +#include + +namespace simdjson { +namespace dom { + +// +// document inline implementation +// +inline element document::root() const noexcept { + return element(internal::tape_ref(this, 1)); +} +simdjson_warn_unused +inline size_t document::capacity() const noexcept { + return allocated_capacity; +} + +simdjson_warn_unused +inline error_code document::allocate(size_t capacity) noexcept { + if (capacity == 0) { + string_buf.reset(); + tape.reset(); + allocated_capacity = 0; + return SUCCESS; + } + + // 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; +} + +} // namespace dom +} // namespace simdjson + +#endif // SIMDJSON_INLINE_DOCUMENT_H +/* end file include/simdjson/dom/document-inl.h */ +/* begin file include/simdjson/dom/object-inl.h */ +#ifndef SIMDJSON_INLINE_OBJECT_H +#define SIMDJSON_INLINE_OBJECT_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::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); +} + +#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(); +} + +#endif // SIMDJSON_EXCEPTIONS + +namespace dom { + +// +// object inline implementation +// +simdjson_inline object::object() noexcept : tape{} {} +simdjson_inline object::object(const internal::tape_ref &_tape) noexcept : tape{_tape} { } +inline object::iterator object::begin() const noexcept { + SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914 + return internal::tape_ref(tape.doc, tape.json_index + 1); +} +inline object::iterator object::end() const noexcept { + SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914 + return internal::tape_ref(tape.doc, tape.after_element() - 1); +} +inline size_t object::size() const noexcept { + SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914 + return tape.scope_count(); +} + +inline simdjson_result 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; +} + +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; +} + +// +// 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) +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_INLINE_OBJECT_H +/* end file include/simdjson/dom/object-inl.h */ +/* begin file include/simdjson/dom/parsedjson_iterator-inl.h */ +#ifndef SIMDJSON_INLINE_PARSEDJSON_ITERATOR_H +#define SIMDJSON_INLINE_PARSEDJSON_ITERATOR_H + +#include + +#ifndef SIMDJSON_DISABLE_DEPRECATED_API + +namespace simdjson { + +// VS2017 reports deprecated warnings when you define a deprecated class's methods. +SIMDJSON_PUSH_DISABLE_WARNINGS +SIMDJSON_DISABLE_DEPRECATED_WARNING + +// Because of template weirdness, the actual class definition is inline in the document class +simdjson_warn_unused bool dom::parser::Iterator::is_ok() const { + return location < tape_length; +} + +// useful for debugging purposes +size_t dom::parser::Iterator::get_tape_location() const { + return location; +} + +// useful for debugging purposes +size_t dom::parser::Iterator::get_tape_length() const { + return tape_length; +} + +// returns the current depth (start at 1 with 0 reserved for the fictitious root +// node) +size_t dom::parser::Iterator::get_depth() const { + return depth; +} + +// A scope is a series of nodes at the same depth, typically it is either an +// object ({) or an array ([). The root node has type 'r'. +uint8_t dom::parser::Iterator::get_scope_type() const { + return depth_index[depth].scope_type; +} + +bool dom::parser::Iterator::move_forward() { + if (location + 1 >= tape_length) { + return false; // we are at the end! + } + + if ((current_type == '[') || (current_type == '{')) { + // We are entering a new scope + depth++; + assert(depth < max_depth); + depth_index[depth].start_of_scope = location; + depth_index[depth].scope_type = current_type; + } else if ((current_type == ']') || (current_type == '}')) { + // Leaving a scope. + depth--; + } else if (is_number()) { + // these types use 2 locations on the tape, not just one. + location += 1; + } + + location += 1; + current_val = doc.tape[location]; + current_type = uint8_t(current_val >> 56); + return true; +} + +void dom::parser::Iterator::move_to_value() { + // assume that we are on a key, so move by 1. + location += 1; + current_val = doc.tape[location]; + current_type = uint8_t(current_val >> 56); +} + +bool dom::parser::Iterator::move_to_key(const char *key) { + if (down()) { + do { + const bool right_key = (strcmp(get_string(), key) == 0); + move_to_value(); + if (right_key) { + return true; + } + } while (next()); + up(); + } + return false; +} + +bool dom::parser::Iterator::move_to_key_insensitive( + const char *key) { + if (down()) { + do { + const bool right_key = (simdjson_strcasecmp(get_string(), key) == 0); + move_to_value(); + if (right_key) { + return true; + } + } while (next()); + up(); + } + return false; +} + +bool dom::parser::Iterator::move_to_key(const char *key, + uint32_t length) { + if (down()) { + do { + bool right_key = ((get_string_length() == length) && + (memcmp(get_string(), key, length) == 0)); + move_to_value(); + if (right_key) { + return true; + } + } while (next()); + up(); + } + return false; +} + +bool dom::parser::Iterator::move_to_index(uint32_t index) { + if (down()) { + uint32_t i = 0; + for (; i < index; i++) { + if (!next()) { + break; + } + } + if (i == index) { + return true; + } + up(); + } + return false; +} + +bool dom::parser::Iterator::prev() { + size_t target_location = location; + to_start_scope(); + size_t npos = location; + if (target_location == npos) { + return false; // we were already at the start + } + size_t oldnpos; + // we have that npos < target_location here + do { + oldnpos = npos; + if ((current_type == '[') || (current_type == '{')) { + // we need to jump + npos = uint32_t(current_val); + } else { + npos = npos + ((current_type == 'd' || current_type == 'l') ? 2 : 1); + } + } while (npos < target_location); + location = oldnpos; + current_val = doc.tape[location]; + current_type = uint8_t(current_val >> 56); + return true; +} + +bool dom::parser::Iterator::up() { + if (depth == 1) { + return false; // don't allow moving back to root + } + to_start_scope(); + // next we just move to the previous value + depth--; + location -= 1; + current_val = doc.tape[location]; + current_type = uint8_t(current_val >> 56); + return true; +} + +bool dom::parser::Iterator::down() { + if (location + 1 >= tape_length) { + return false; + } + if ((current_type == '[') || (current_type == '{')) { + size_t npos = uint32_t(current_val); + if (npos == location + 2) { + return false; // we have an empty scope + } + depth++; + assert(depth < max_depth); + location = location + 1; + depth_index[depth].start_of_scope = location; + depth_index[depth].scope_type = current_type; + current_val = doc.tape[location]; + current_type = uint8_t(current_val >> 56); + return true; + } + return false; +} + +void dom::parser::Iterator::to_start_scope() { + location = depth_index[depth].start_of_scope; + current_val = doc.tape[location]; + current_type = uint8_t(current_val >> 56); +} + +bool dom::parser::Iterator::next() { + size_t npos; + if ((current_type == '[') || (current_type == '{')) { + // we need to jump + npos = uint32_t(current_val); + } else { + npos = location + (is_number() ? 2 : 1); + } + uint64_t next_val = doc.tape[npos]; + uint8_t next_type = uint8_t(next_val >> 56); + if ((next_type == ']') || (next_type == '}')) { + return false; // we reached the end of the scope + } + location = npos; + current_val = next_val; + current_type = next_type; + return true; +} +dom::parser::Iterator::Iterator(const dom::parser &pj) noexcept(false) + : doc(pj.doc) +{ +#if SIMDJSON_EXCEPTIONS + if (!pj.valid) { throw simdjson_error(pj.error); } +#else + if (!pj.valid) { return; } // abort() usage is forbidden in the library +#endif + + max_depth = pj.max_depth(); + depth_index = new scopeindex_t[max_depth + 1]; + depth_index[0].start_of_scope = location; + current_val = doc.tape[location++]; + current_type = uint8_t(current_val >> 56); + depth_index[0].scope_type = current_type; + tape_length = size_t(current_val & internal::JSON_VALUE_MASK); + if (location < tape_length) { + // If we make it here, then depth_capacity must >=2, but the compiler + // may not know this. + current_val = doc.tape[location]; + current_type = uint8_t(current_val >> 56); + depth++; + assert(depth < max_depth); + depth_index[depth].start_of_scope = location; + depth_index[depth].scope_type = current_type; + } +} +dom::parser::Iterator::Iterator( + const dom::parser::Iterator &o) noexcept + : doc(o.doc), + max_depth(o.depth), + depth(o.depth), + location(o.location), + tape_length(o.tape_length), + current_type(o.current_type), + current_val(o.current_val) +{ + depth_index = new scopeindex_t[max_depth+1]; + std::memcpy(depth_index, o.depth_index, (depth + 1) * sizeof(depth_index[0])); +} + +dom::parser::Iterator::~Iterator() noexcept { + if (depth_index) { delete[] depth_index; } +} + +bool dom::parser::Iterator::print(std::ostream &os, bool escape_strings) const { + if (!is_ok()) { + return false; + } + switch (current_type) { + case '"': // we have a string + os << '"'; + if (escape_strings) { + os << internal::escape_json_string(std::string_view(get_string(), get_string_length())); + } else { + // was: os << get_string();, but given that we can include null chars, we + // have to do something crazier: + std::copy(get_string(), get_string() + get_string_length(), std::ostream_iterator(os)); + } + os << '"'; + break; + case 'l': // we have a long int + os << get_integer(); + break; + case 'u': + os << get_unsigned_integer(); + break; + case 'd': + os << get_double(); + break; + case 'n': // we have a null + os << "null"; + break; + case 't': // we have a true + os << "true"; + break; + case 'f': // we have a false + os << "false"; + break; + case '{': // we have an object + case '}': // we end an object + case '[': // we start an array + case ']': // we end an array + os << char(current_type); + break; + default: + return false; + } + return true; +} + +bool dom::parser::Iterator::move_to(const char *pointer, + uint32_t length) { + char *new_pointer = nullptr; + if (pointer[0] == '#') { + // Converting fragment representation to string representation + new_pointer = new char[length]; + uint32_t new_length = 0; + for (uint32_t i = 1; i < length; i++) { + if (pointer[i] == '%' && pointer[i + 1] == 'x') { +#if __cpp_exceptions + try { +#endif + int fragment = + std::stoi(std::string(&pointer[i + 2], 2), nullptr, 16); + if (fragment == '\\' || fragment == '"' || (fragment <= 0x1F)) { + // escaping the character + new_pointer[new_length] = '\\'; + new_length++; + } + new_pointer[new_length] = char(fragment); + i += 3; +#if __cpp_exceptions + } catch (std::invalid_argument &) { + delete[] new_pointer; + return false; // the fragment is invalid + } +#endif + } else { + new_pointer[new_length] = pointer[i]; + } + new_length++; + } + length = new_length; + pointer = new_pointer; + } + + // saving the current state + size_t depth_s = depth; + size_t location_s = location; + uint8_t current_type_s = current_type; + uint64_t current_val_s = current_val; + + rewind(); // The json pointer is used from the root of the document. + + bool found = relative_move_to(pointer, length); + delete[] new_pointer; + + if (!found) { + // since the pointer has found nothing, we get back to the original + // position. + depth = depth_s; + location = location_s; + current_type = current_type_s; + current_val = current_val_s; + } + + return found; +} + +bool dom::parser::Iterator::relative_move_to(const char *pointer, + uint32_t length) { + if (length == 0) { + // returns the whole document + return true; + } + + if (pointer[0] != '/') { + // '/' must be the first character + return false; + } + + // finding the key in an object or the index in an array + std::string key_or_index; + uint32_t offset = 1; + + // checking for the "-" case + if (is_array() && pointer[1] == '-') { + if (length != 2) { + // the pointer must be exactly "/-" + // there can't be anything more after '-' as an index + return false; + } + key_or_index = '-'; + offset = length; // will skip the loop coming right after + } + + // We either transform the first reference token to a valid json key + // or we make sure it is a valid index in an array. + for (; offset < length; offset++) { + if (pointer[offset] == '/') { + // beginning of the next key or index + break; + } + if (is_array() && (pointer[offset] < '0' || pointer[offset] > '9')) { + // the index of an array must be an integer + // we also make sure std::stoi won't discard whitespaces later + return false; + } + if (pointer[offset] == '~') { + // "~1" represents "/" + if (pointer[offset + 1] == '1') { + key_or_index += '/'; + offset++; + continue; + } + // "~0" represents "~" + if (pointer[offset + 1] == '0') { + key_or_index += '~'; + offset++; + continue; + } + } + if (pointer[offset] == '\\') { + if (pointer[offset + 1] == '\\' || pointer[offset + 1] == '"' || + (pointer[offset + 1] <= 0x1F)) { + key_or_index += pointer[offset + 1]; + offset++; + continue; + } + return false; // invalid escaped character + } + if (pointer[offset] == '\"') { + // unescaped quote character. this is an invalid case. + // lets do nothing and assume most pointers will be valid. + // it won't find any corresponding json key anyway. + // return false; + } + key_or_index += pointer[offset]; + } + + bool found = false; + if (is_object()) { + if (move_to_key(key_or_index.c_str(), uint32_t(key_or_index.length()))) { + found = relative_move_to(pointer + offset, length - offset); + } + } else if (is_array()) { + if (key_or_index == "-") { // handling "-" case first + if (down()) { + while (next()) + ; // moving to the end of the array + // moving to the nonexistent value right after... + size_t npos; + if ((current_type == '[') || (current_type == '{')) { + // we need to jump + npos = uint32_t(current_val); + } else { + npos = + location + ((current_type == 'd' || current_type == 'l') ? 2 : 1); + } + location = npos; + current_val = doc.tape[npos]; + current_type = uint8_t(current_val >> 56); + return true; // how could it fail ? + } + } else { // regular numeric index + // The index can't have a leading '0' + if (key_or_index[0] == '0' && key_or_index.length() > 1) { + return false; + } + // it cannot be empty + if (key_or_index.length() == 0) { + return false; + } + // we already checked the index contains only valid digits + uint32_t index = std::stoi(key_or_index); + if (move_to_index(index)) { + found = relative_move_to(pointer + offset, length - offset); + } + } + } + + return found; +} + +SIMDJSON_POP_DISABLE_WARNINGS +} // namespace simdjson + +#endif // SIMDJSON_DISABLE_DEPRECATED_API + + +#endif // SIMDJSON_INLINE_PARSEDJSON_ITERATOR_H +/* end file include/simdjson/dom/parsedjson_iterator-inl.h */ +/* begin file include/simdjson/dom/parser-inl.h */ +#ifndef SIMDJSON_INLINE_PARSER_H +#define SIMDJSON_INLINE_PARSER_H + +#include +#include + +namespace simdjson { +namespace dom { + +// +// parser inline implementation +// +simdjson_inline parser::parser(size_t max_capacity) noexcept + : _max_capacity{max_capacity}, + loaded_bytes(nullptr) { +} +simdjson_inline parser::parser(parser &&other) noexcept = default; +simdjson_inline parser &parser::operator=(parser &&other) noexcept = default; + +inline bool parser::is_valid() const noexcept { return valid; } +inline int parser::get_error_code() const noexcept { return error; } +inline std::string parser::get_error_message() const noexcept { return error_message(error); } + +inline bool parser::dump_raw_tape(std::ostream &os) const noexcept { + return valid ? doc.dump_raw_tape(os) : false; +} + +inline simdjson_result 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 + + if (fp == nullptr) { + return IO_ERROR; + } + + // Get the file size + int ret; +#if SIMDJSON_VISUAL_STUDIO && !SIMDJSON_IS_32BITS + ret = _fseeki64(fp, 0, SEEK_END); +#else + ret = std::fseek(fp, 0, SEEK_END); +#endif // _WIN64 + if(ret < 0) { + std::fclose(fp); + return IO_ERROR; + } +#if SIMDJSON_VISUAL_STUDIO && !SIMDJSON_IS_32BITS + __int64 len = _ftelli64(fp); + if(len == -1L) { + std::fclose(fp); + return IO_ERROR; + } +#else + long len = std::ftell(fp); + if((len < 0) || (len == LONG_MAX)) { + std::fclose(fp); + return IO_ERROR; + } +#endif + + // Make sure we have enough capacity to load the file + if (_loaded_bytes_capacity < size_t(len)) { + loaded_bytes.reset( internal::allocate_padded_buffer(len) ); + if (!loaded_bytes) { + std::fclose(fp); + return MEMALLOC; + } + _loaded_bytes_capacity = len; + } + + // Read the string + std::rewind(fp); + size_t bytes_read = std::fread(loaded_bytes.get(), 1, len, fp); + if (std::fclose(fp) != 0 || bytes_read != size_t(len)) { + return IO_ERROR; + } + + return bytes_read; +} + +inline simdjson_result parser::load(const std::string &path) & noexcept { + size_t len; + auto _error = read_file(path).get(len); + if (_error) { return _error; } + return parse(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::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); + } + _error = implementation->parse(realloc_if_needed ? reinterpret_cast(loaded_bytes.get()): buf, len, provided_doc); + + if (_error) { return _error; } + + 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); +} + + +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); +} + +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; } + 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_inline size_t parser::max_depth() const noexcept { + return implementation ? implementation->max_depth() : DEFAULT_MAX_DEPTH; +} + +simdjson_warn_unused +inline error_code parser::allocate(size_t capacity, size_t max_depth) noexcept { + // + // Reallocate implementation if needed + // + error_code err; + if (implementation) { + err = implementation->allocate(capacity, max_depth); + } else { + err = simdjson::get_active_implementation()->create_dom_parser_implementation(capacity, max_depth, implementation); + } + if (err) { return err; } + return SUCCESS; +} + +#ifndef SIMDJSON_DISABLE_DEPRECATED_API +simdjson_warn_unused +inline bool parser::allocate_capacity(size_t capacity, size_t max_depth) noexcept { + return !allocate(capacity, max_depth); +} +#endif // SIMDJSON_DISABLE_DEPRECATED_API + +inline error_code parser::ensure_capacity(size_t desired_capacity) noexcept { + return ensure_capacity(doc, desired_capacity); +} + + +inline error_code parser::ensure_capacity(document& target_document, size_t desired_capacity) noexcept { + // 1. It is wasteful to allocate a document and a parser for documents spanning less than MINIMAL_DOCUMENT_CAPACITY bytes. + // 2. If we allow desired_capacity = 0 then it is possible to exit this function with implementation == nullptr. + if(desired_capacity < MINIMAL_DOCUMENT_CAPACITY) { desired_capacity = MINIMAL_DOCUMENT_CAPACITY; } + // If we don't have enough capacity, (try to) automatically bump it. + // If the document needs allocation, do it too. + // Both in one if statement to minimize unlikely branching. + // + // Note: we must make sure that this function is called if capacity() == 0. We do so because we + // ensure that desired_capacity > 0. + if (simdjson_unlikely(capacity() < desired_capacity || target_document.capacity() < desired_capacity)) { + if (desired_capacity > max_capacity()) { + return error = CAPACITY; + } + error_code err1 = target_document.capacity() < desired_capacity ? target_document.allocate(desired_capacity) : SUCCESS; + error_code err2 = capacity() < desired_capacity ? allocate(desired_capacity, max_depth()) : SUCCESS; + if(err1 != SUCCESS) { return error = err1; } + if(err2 != SUCCESS) { return error = err2; } + } + return SUCCESS; +} + +simdjson_inline void parser::set_max_capacity(size_t max_capacity) noexcept { + if(max_capacity > MINIMAL_DOCUMENT_CAPACITY) { + _max_capacity = max_capacity; + } else { + _max_capacity = MINIMAL_DOCUMENT_CAPACITY; + } +} + +} // namespace dom +} // namespace simdjson + +#endif // SIMDJSON_INLINE_PARSER_H +/* end file include/simdjson/dom/parser-inl.h */ +/* begin file include/simdjson/internal/tape_ref-inl.h */ +#ifndef SIMDJSON_INLINE_TAPE_REF_H +#define SIMDJSON_INLINE_TAPE_REF_H + +#include + +namespace simdjson { +namespace internal { + +// +// tape_ref inline implementation +// +simdjson_inline tape_ref::tape_ref() noexcept : doc{nullptr}, json_index{0} {} +simdjson_inline tape_ref::tape_ref(const dom::document *_doc, size_t _json_index) noexcept : doc{_doc}, json_index{_json_index} {} + + +simdjson_inline bool tape_ref::is_document_root() const noexcept { + return json_index == 1; // should we ever change the structure of the tape, this should get updated. +} +simdjson_inline bool tape_ref::usable() const noexcept { + return doc != nullptr; // when the document pointer is null, this tape_ref is uninitialized (should not be accessed). +} +// Some value types have a specific on-tape word value. It can be faster +// to check the type by doing a word-to-word comparison instead of extracting the +// most significant 8 bits. + +simdjson_inline bool tape_ref::is_double() const noexcept { + constexpr uint64_t tape_double = uint64_t(tape_type::DOUBLE)<<56; + return doc->tape[json_index] == tape_double; +} +simdjson_inline bool tape_ref::is_int64() const noexcept { + constexpr uint64_t tape_int64 = uint64_t(tape_type::INT64)<<56; + return doc->tape[json_index] == tape_int64; +} +simdjson_inline bool tape_ref::is_uint64() const noexcept { + constexpr uint64_t tape_uint64 = uint64_t(tape_type::UINT64)<<56; + return doc->tape[json_index] == tape_uint64; +} +simdjson_inline bool tape_ref::is_false() const noexcept { + constexpr uint64_t tape_false = uint64_t(tape_type::FALSE_VALUE)<<56; + return doc->tape[json_index] == tape_false; +} +simdjson_inline bool tape_ref::is_true() const noexcept { + constexpr uint64_t tape_true = uint64_t(tape_type::TRUE_VALUE)<<56; + return doc->tape[json_index] == tape_true; +} +simdjson_inline bool tape_ref::is_null_on_tape() const noexcept { + constexpr uint64_t tape_null = uint64_t(tape_type::NULL_VALUE)<<56; + return doc->tape[json_index] == tape_null; +} + +inline size_t tape_ref::after_element() const noexcept { + switch (tape_ref_type()) { + case tape_type::START_ARRAY: + case tape_type::START_OBJECT: + return matching_brace_index(); + case tape_type::UINT64: + case tape_type::INT64: + case tape_type::DOUBLE: + return json_index + 2; + default: + return json_index + 1; + } +} +simdjson_inline tape_type tape_ref::tape_ref_type() const noexcept { + return static_cast(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; +} + +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)]); +} + +inline std::string_view internal::tape_ref::get_string_view() const noexcept { + return std::string_view( + get_c_str(), + get_string_length() + ); +} + +} // namespace internal +} // namespace simdjson + +#endif // SIMDJSON_INLINE_TAPE_REF_H +/* end file include/simdjson/internal/tape_ref-inl.h */ +/* begin file include/simdjson/dom/serialization-inl.h */ + +#ifndef SIMDJSON_SERIALIZATION_INL_H +#define SIMDJSON_SERIALIZATION_INL_H + + +#include +#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; +} +} +/*** + * 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. + */ +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. + */ +char *fast_itoa(char *output, uint64_t value) noexcept { + // This is a standard implementation of itoa. + char buffer[20]; + const char *const end_buffer = buffer + 20; + char *write_pointer = buffer + 19; + // A faster approach is possible if we expect large integers: + // unroll the loop (work in 100s, 1000s) and use some kind of + // memoization. + while(value >= 10) { + *write_pointer-- = char('0' + (value % 10)); + value /= 10; + }; + *write_pointer = char('0' + value); + size_t len = end_buffer - write_pointer; + std::memcpy(output, write_pointer, len); + return output + len; +} +} // anonymous namespace +namespace internal { + +/*** + * Minifier/formatter code. + **/ + +simdjson_inline void mini_formatter::number(uint64_t x) { + char number_buffer[24]; + char *newp = fast_itoa(number_buffer, x); + buffer.insert(buffer.end(), number_buffer, newp); +} + +simdjson_inline void mini_formatter::number(int64_t x) { + char number_buffer[24]; + char *newp = fast_itoa(number_buffer, x); + buffer.insert(buffer.end(), number_buffer, newp); +} + +simdjson_inline void mini_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); +} + +simdjson_inline void mini_formatter::start_array() { one_char('['); } +simdjson_inline void mini_formatter::end_array() { one_char(']'); } +simdjson_inline void mini_formatter::start_object() { one_char('{'); } +simdjson_inline void mini_formatter::end_object() { one_char('}'); } +simdjson_inline void mini_formatter::comma() { one_char(','); } + + +simdjson_inline void mini_formatter::true_atom() { + const char * s = "true"; + buffer.insert(buffer.end(), s, s + 4); +} +simdjson_inline void mini_formatter::false_atom() { + const char * s = "false"; + buffer.insert(buffer.end(), s, s + 5); +} +simdjson_inline void mini_formatter::null_atom() { + const char * s = "null"; + buffer.insert(buffer.end(), s, s + 4); +} +simdjson_inline void mini_formatter::one_char(char c) { buffer.push_back(c); } +simdjson_inline void mini_formatter::key(std::string_view unescaped) { + string(unescaped); + one_char(':'); +} +simdjson_inline void mini_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. + 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('\"'); +} + +inline void mini_formatter::clear() { + buffer.clear(); +} + +simdjson_inline std::string_view mini_formatter::str() const { + return std::string_view(buffer.data(), buffer.size()); +} + + +/*** + * String building code. + **/ + +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; + + internal::tape_ref iter(value.tape); + do { + // print commas after each value + if (after_value) { + format.comma(); + } + // 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()); + iter.json_index++; + } + switch (iter.tape_ref_type()) { + + // Arrays + case tape_type::START_ARRAY: { + // If we're too deep, we need to recurse to go deeper. + depth++; + if (simdjson_unlikely(depth >= MAX_DEPTH)) { + append(simdjson::dom::array(iter)); + iter.json_index = iter.matching_brace_index() - 1; // Jump to the ] + depth--; + break; + } + + // Output start [ + format.start_array(); + iter.json_index++; + + // Handle empty [] (we don't want to come back around and print commas) + if (iter.tape_ref_type() == tape_type::END_ARRAY) { + format.end_array(); + depth--; + break; + } + + is_object[depth] = false; + after_value = false; + continue; + } + + // Objects + case tape_type::START_OBJECT: { + // If we're too deep, we need to recurse to go deeper. + depth++; + if (simdjson_unlikely(depth >= MAX_DEPTH)) { + append(simdjson::dom::object(iter)); + iter.json_index = iter.matching_brace_index() - 1; // Jump to the } + depth--; + break; + } + + // Output start { + format.start_object(); + iter.json_index++; + + // Handle empty {} (we don't want to come back around and print commas) + if (iter.tape_ref_type() == tape_type::END_OBJECT) { + format.end_object(); + depth--; + break; + } + + is_object[depth] = true; + after_value = false; + continue; + } + + // 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; + + // These are impossible + case tape_type::END_ARRAY: + case tape_type::END_OBJECT: + case tape_type::ROOT: + SIMDJSON_UNREACHABLE(); + } + iter.json_index++; + after_value = true; + + // Handle multiple ends in a row + while (depth != 0 && (iter.tape_ref_type() == tape_type::END_ARRAY || + iter.tape_ref_type() == tape_type::END_OBJECT)) { + if (iter.tape_ref_type() == tape_type::END_ARRAY) { + format.end_array(); + } else { + format.end_object(); + } + depth--; + iter.json_index++; + } + + // 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(); +} + +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::append(simdjson::dom::key_value_pair kv) { + format.key(kv.key); + append(kv.value); +} + +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 simdjson + +#endif +/* end file include/simdjson/dom/serialization-inl.h */ + +SIMDJSON_POP_DISABLE_WARNINGS + +#endif // SIMDJSON_DOM_H +/* end file include/simdjson/dom.h */ +/* begin file include/simdjson/builtin.h */ +#ifndef SIMDJSON_BUILTIN_H +#define SIMDJSON_BUILTIN_H + +/* begin file include/simdjson/implementations.h */ +#ifndef SIMDJSON_IMPLEMENTATIONS_H +#define SIMDJSON_IMPLEMENTATIONS_H + +/* begin file include/simdjson/implementation-base.h */ +#ifndef SIMDJSON_IMPLEMENTATION_BASE_H +#define SIMDJSON_IMPLEMENTATION_BASE_H + +/** + * @file + * + * Includes common stuff needed for implementations. + */ + + +// Implementation-internal files (must be included before the implementations themselves, to keep +// amalgamation working--otherwise, the first time a file is included, it might be put inside the +// #ifdef SIMDJSON_IMPLEMENTATION_ARM64/FALLBACK/etc., which means the other implementations can't +// compile unless that implementation is turned on). +/* begin file include/simdjson/internal/jsoncharutils_tables.h */ +#ifndef SIMDJSON_INTERNAL_JSONCHARUTILS_TABLES_H +#define SIMDJSON_INTERNAL_JSONCHARUTILS_TABLES_H + + +#ifdef JSON_TEST_STRINGS +void found_string(const uint8_t *buf, const uint8_t *parsed_begin, + const uint8_t *parsed_end); +void found_bad_string(const uint8_t *buf); +#endif + +namespace simdjson { +namespace internal { +// structural chars here are +// they are { 0x7b } 0x7d : 0x3a [ 0x5b ] 0x5d , 0x2c (and NULL) +// we are also interested in the four whitespace characters +// space 0x20, linefeed 0x0a, horizontal tab 0x09 and carriage return 0x0d + +extern SIMDJSON_DLLIMPORTEXPORT const bool structural_or_whitespace_negated[256]; +extern SIMDJSON_DLLIMPORTEXPORT const bool structural_or_whitespace[256]; +extern SIMDJSON_DLLIMPORTEXPORT const uint32_t digit_to_val32[886]; + +} // namespace internal +} // namespace simdjson + +#endif // SIMDJSON_INTERNAL_JSONCHARUTILS_TABLES_H +/* end file include/simdjson/internal/jsoncharutils_tables.h */ +/* begin file include/simdjson/internal/numberparsing_tables.h */ +#ifndef SIMDJSON_INTERNAL_NUMBERPARSING_TABLES_H +#define SIMDJSON_INTERNAL_NUMBERPARSING_TABLES_H + + +namespace simdjson { +namespace internal { +/** + * The smallest non-zero float (binary64) is 2^-1074. + * We take as input numbers of the form w x 10^q where w < 2^64. + * We have that w * 10^-343 < 2^(64-344) 5^-343 < 2^-1076. + * However, we have that + * (2^64-1) * 10^-342 = (2^64-1) * 2^-342 * 5^-342 > 2^-1074. + * Thus it is possible for a number of the form w * 10^-342 where + * w is a 64-bit value to be a non-zero floating-point number. + ********* + * Any number of form w * 10^309 where w>= 1 is going to be + * infinite in binary64 so we never need to worry about powers + * of 5 greater than 308. + */ +constexpr int smallest_power = -342; +constexpr int largest_power = 308; + +/** + * Represents a 128-bit value. + * low: least significant 64 bits. + * high: most significant 64 bits. + */ +struct value128 { + uint64_t low; + uint64_t high; +}; + + +// Precomputed powers of ten from 10^0 to 10^22. These +// can be represented exactly using the double type. +extern SIMDJSON_DLLIMPORTEXPORT const double power_of_ten[]; + + +/** + * When mapping numbers from decimal to binary, + * we go from w * 10^q to m * 2^p but we have + * 10^q = 5^q * 2^q, so effectively + * we are trying to match + * w * 2^q * 5^q to m * 2^p. Thus the powers of two + * are not a concern since they can be represented + * exactly using the binary notation, only the powers of five + * affect the binary significand. + */ + + +// The truncated powers of five from 5^-342 all the way to 5^308 +// The mantissa is truncated to 128 bits, and +// never rounded up. Uses about 10KB. +extern SIMDJSON_DLLIMPORTEXPORT const uint64_t power_of_five_128[]; +} // namespace internal +} // namespace simdjson + +#endif // SIMDJSON_INTERNAL_NUMBERPARSING_TABLES_H +/* end file include/simdjson/internal/numberparsing_tables.h */ +/* begin file include/simdjson/internal/simdprune_tables.h */ +#ifndef SIMDJSON_INTERNAL_SIMDPRUNE_TABLES_H +#define SIMDJSON_INTERNAL_SIMDPRUNE_TABLES_H + +#include + +namespace simdjson { // table modified and copied from +namespace internal { // http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetTable + +extern SIMDJSON_DLLIMPORTEXPORT const unsigned char BitsSetTable256mul2[256]; + +extern SIMDJSON_DLLIMPORTEXPORT const uint8_t pshufb_combine_table[272]; + +// 256 * 8 bytes = 2kB, easily fits in cache. +extern SIMDJSON_DLLIMPORTEXPORT const uint64_t thintable_epi8[256]; + +} // namespace internal +} // namespace simdjson + +#endif // SIMDJSON_INTERNAL_SIMDPRUNE_TABLES_H +/* end file include/simdjson/internal/simdprune_tables.h */ + +#endif // SIMDJSON_IMPLEMENTATION_BASE_H +/* end file include/simdjson/implementation-base.h */ + +// +// First, figure out which implementations can be run. Doing it here makes it so we don't have to worry about the order +// in which we include them. +// + +#ifndef SIMDJSON_IMPLEMENTATION_ARM64 +#define SIMDJSON_IMPLEMENTATION_ARM64 (SIMDJSON_IS_ARM64) +#endif +#define SIMDJSON_CAN_ALWAYS_RUN_ARM64 SIMDJSON_IMPLEMENTATION_ARM64 && SIMDJSON_IS_ARM64 + +#ifdef __has_include +// How do we detect that a compiler supports vbmi2? +// For sure if the following header is found, we are ok? +#if __has_include() +#define SIMDJSON_COMPILER_SUPPORTS_VBMI2 1 +#endif +#endif + +#ifdef _MSC_VER +#if _MSC_VER >= 1920 +// Visual Studio 2019 and up support VBMI2 under x64 even if the header +// avx512vbmi2intrin.h is not found. +#define SIMDJSON_COMPILER_SUPPORTS_VBMI2 1 +#endif +#endif + +// By default, we allow AVX512. +#ifndef SIMDJSON_AVX512_ALLOWED +#define SIMDJSON_AVX512_ALLOWED 1 +#endif + +// Default Icelake to on if this is x86-64. Even if we're not compiled for it, it could be selected +// at runtime. +#ifndef SIMDJSON_IMPLEMENTATION_ICELAKE +#define SIMDJSON_IMPLEMENTATION_ICELAKE ((SIMDJSON_IS_X86_64) && (SIMDJSON_AVX512_ALLOWED) && (SIMDJSON_COMPILER_SUPPORTS_VBMI2)) +#endif + +#ifdef _MSC_VER +// To see why (__BMI__) && (__PCLMUL__) && (__LZCNT__) are not part of this next line, see +// https://github.com/simdjson/simdjson/issues/1247 +#define SIMDJSON_CAN_ALWAYS_RUN_ICELAKE ((SIMDJSON_IMPLEMENTATION_ICELAKE) && (__AVX2__) && (__AVX512F__) && (__AVX512DQ__) && (__AVX512CD__) && (__AVX512BW__) && (__AVX512VL__) && (__AVX512VBMI2__)) +#else +#define SIMDJSON_CAN_ALWAYS_RUN_ICELAKE ((SIMDJSON_IMPLEMENTATION_ICELAKE) && (__AVX2__) && (__BMI__) && (__PCLMUL__) && (__LZCNT__) && (__AVX512F__) && (__AVX512DQ__) && (__AVX512CD__) && (__AVX512BW__) && (__AVX512VL__) && (__AVX512VBMI2__)) +#endif + +// Default Haswell to on if this is x86-64. Even if we're not compiled for it, it could be selected +// at runtime. +#ifndef SIMDJSON_IMPLEMENTATION_HASWELL +#if SIMDJSON_CAN_ALWAYS_RUN_ICELAKE +// if icelake is always available, never enable haswell. +#define SIMDJSON_IMPLEMENTATION_HASWELL 0 +#else +#define SIMDJSON_IMPLEMENTATION_HASWELL SIMDJSON_IS_X86_64 +#endif +#endif +#ifdef _MSC_VER +// To see why (__BMI__) && (__PCLMUL__) && (__LZCNT__) are not part of this next line, see +// https://github.com/simdjson/simdjson/issues/1247 +#define SIMDJSON_CAN_ALWAYS_RUN_HASWELL ((SIMDJSON_IMPLEMENTATION_HASWELL) && (SIMDJSON_IS_X86_64) && (__AVX2__)) +#else +#define SIMDJSON_CAN_ALWAYS_RUN_HASWELL ((SIMDJSON_IMPLEMENTATION_HASWELL) && (SIMDJSON_IS_X86_64) && (__AVX2__) && (__BMI__) && (__PCLMUL__) && (__LZCNT__)) +#endif + +// Default Westmere to on if this is x86-64. +#ifndef SIMDJSON_IMPLEMENTATION_WESTMERE +#if SIMDJSON_CAN_ALWAYS_RUN_ICELAKE || SIMDJSON_CAN_ALWAYS_RUN_HASWELL +// if icelake or haswell are always available, never enable westmere. +#define SIMDJSON_IMPLEMENTATION_WESTMERE 0 +#else +#define SIMDJSON_IMPLEMENTATION_WESTMERE SIMDJSON_IS_X86_64 +#endif +#endif +#define SIMDJSON_CAN_ALWAYS_RUN_WESTMERE (SIMDJSON_IMPLEMENTATION_WESTMERE && SIMDJSON_IS_X86_64 && __SSE4_2__ && __PCLMUL__) + +#ifndef SIMDJSON_IMPLEMENTATION_PPC64 +#define SIMDJSON_IMPLEMENTATION_PPC64 (SIMDJSON_IS_PPC64 && SIMDJSON_IS_PPC64_VMX) +#endif +#define SIMDJSON_CAN_ALWAYS_RUN_PPC64 SIMDJSON_IMPLEMENTATION_PPC64 && SIMDJSON_IS_PPC64 && SIMDJSON_IS_PPC64_VMX + +// 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 +// if anything at all except fallback can always run, then disable fallback. +#define SIMDJSON_IMPLEMENTATION_FALLBACK 0 +#else +#define SIMDJSON_IMPLEMENTATION_FALLBACK 1 +#endif +#endif +#define SIMDJSON_CAN_ALWAYS_RUN_FALLBACK SIMDJSON_IMPLEMENTATION_FALLBACK + +SIMDJSON_PUSH_DISABLE_WARNINGS +SIMDJSON_DISABLE_UNDESIRED_WARNINGS + +// Implementations +/* begin file include/simdjson/arm64.h */ +#ifndef SIMDJSON_ARM64_H +#define SIMDJSON_ARM64_H + + +#if SIMDJSON_IMPLEMENTATION_ARM64 + +namespace simdjson { +/** + * Implementation for NEON (ARMv8). + */ +namespace arm64 { +} // namespace arm64 +} // namespace simdjson + +/* begin file include/simdjson/arm64/implementation.h */ +#ifndef SIMDJSON_ARM64_IMPLEMENTATION_H +#define SIMDJSON_ARM64_IMPLEMENTATION_H + + +namespace simdjson { +namespace arm64 { + +namespace { +using namespace simdjson; +using namespace simdjson::dom; +} + +/** + * @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 simdjson + +#endif // SIMDJSON_ARM64_IMPLEMENTATION_H +/* end file include/simdjson/arm64/implementation.h */ + +/* begin file include/simdjson/arm64/begin.h */ +// redefining SIMDJSON_IMPLEMENTATION to "arm64" +// #define SIMDJSON_IMPLEMENTATION arm64 +/* end file include/simdjson/arm64/begin.h */ + +// Declarations +/* begin file include/simdjson/generic/dom_parser_implementation.h */ + +namespace simdjson { +namespace arm64 { + +// expectation: sizeof(open_container) = 64/8. +struct open_container { + uint32_t tape_index; // where, on the tape, does the scope ([,{) begins + uint32_t count; // how many elements in the scope +}; // struct open_container + +static_assert(sizeof(open_container) == 64/8, "Open container must be 64 bits"); + +class dom_parser_implementation final : public internal::dom_parser_implementation { +public: + /** Tape location of each open { or [ */ + std::unique_ptr open_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 arm64 +} // namespace simdjson + +namespace simdjson { +namespace arm64 { + +inline dom_parser_implementation::dom_parser_implementation() noexcept = default; +inline dom_parser_implementation::dom_parser_implementation(dom_parser_implementation &&other) noexcept = default; +inline dom_parser_implementation &dom_parser_implementation::operator=(dom_parser_implementation &&other) noexcept = default; + +// Leaving these here so they can be inlined if so desired +inline simdjson_warn_unused error_code dom_parser_implementation::set_capacity(size_t capacity) noexcept { + if(capacity > SIMDJSON_MAXSIZE_BYTES) { 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 arm64 +} // namespace simdjson +/* end file include/simdjson/generic/dom_parser_implementation.h */ +/* begin file include/simdjson/arm64/intrinsics.h */ +#ifndef SIMDJSON_ARM64_INTRINSICS_H +#define SIMDJSON_ARM64_INTRINSICS_H + +// 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 include/simdjson/arm64/intrinsics.h */ +/* begin file include/simdjson/arm64/bitmanipulation.h */ +#ifndef SIMDJSON_ARM64_BITMANIPULATION_H +#define SIMDJSON_ARM64_BITMANIPULATION_H + +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) { +#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; +#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) { +#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; +#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) { +#ifdef 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 include/simdjson/arm64/bitmanipulation.h */ +/* begin file include/simdjson/arm64/bitmask.h */ +#ifndef SIMDJSON_ARM64_BITMASK_H +#define SIMDJSON_ARM64_BITMASK_H + +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 include/simdjson/arm64/bitmask.h */ +/* begin file include/simdjson/arm64/simd.h */ +#ifndef SIMDJSON_ARM64_SIMD_H +#define SIMDJSON_ARM64_SIMD_H + +#include + + +namespace simdjson { +namespace arm64 { +namespace { +namespace simd { + +#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO +namespace { +// Start of private section with Visual Studio workaround + + +/** + * make_uint8x16_t initializes a SIMD register (uint8x16_t). + * This is needed because, incredibly, the syntax uint8x16_t x = {1,2,3...} + * is not recognized under Visual Studio! This is a workaround. + * Using a std::initializer_list as a parameter resulted in + * inefficient code. With the current approach, if the parameters are + * compile-time constants, + * GNU GCC compiles it to ldr, the same as uint8x16_t x = {1,2,3...}. + * You should not use this function except for compile-time constants: + * it is not efficient. + */ +simdjson_inline uint8x16_t make_uint8x16_t(uint8_t x1, uint8_t x2, uint8_t x3, uint8_t x4, + uint8_t x5, uint8_t x6, uint8_t x7, uint8_t x8, + uint8_t x9, uint8_t x10, uint8_t x11, uint8_t x12, + uint8_t x13, uint8_t x14, uint8_t x15, uint8_t x16) { + // Doing a load like so end ups generating worse code. + // uint8_t array[16] = {x1, x2, x3, x4, x5, x6, x7, x8, + // x9, x10,x11,x12,x13,x14,x15,x16}; + // return vld1q_u8(array); + uint8x16_t x{}; + // incredibly, Visual Studio does not allow x[0] = x1 + x = vsetq_lane_u8(x1, x, 0); + x = vsetq_lane_u8(x2, x, 1); + x = vsetq_lane_u8(x3, x, 2); + x = vsetq_lane_u8(x4, x, 3); + x = vsetq_lane_u8(x5, x, 4); + x = vsetq_lane_u8(x6, x, 5); + x = vsetq_lane_u8(x7, x, 6); + x = vsetq_lane_u8(x8, x, 7); + x = vsetq_lane_u8(x9, x, 8); + x = vsetq_lane_u8(x10, x, 9); + x = vsetq_lane_u8(x11, x, 10); + x = vsetq_lane_u8(x12, x, 11); + x = vsetq_lane_u8(x13, x, 12); + x = vsetq_lane_u8(x14, x, 13); + x = vsetq_lane_u8(x15, x, 14); + x = vsetq_lane_u8(x16, x, 15); + return x; +} + +simdjson_inline uint8x8_t make_uint8x8_t(uint8_t x1, uint8_t x2, uint8_t x3, uint8_t x4, + uint8_t x5, uint8_t x6, uint8_t x7, uint8_t x8) { + uint8x8_t x{}; + x = vset_lane_u8(x1, x, 0); + x = vset_lane_u8(x2, x, 1); + x = vset_lane_u8(x3, x, 2); + x = vset_lane_u8(x4, x, 3); + x = vset_lane_u8(x5, x, 4); + x = vset_lane_u8(x6, x, 5); + x = vset_lane_u8(x7, x, 6); + x = vset_lane_u8(x8, x, 7); + return x; +} + +// We have to do the same work for make_int8x16_t +simdjson_inline int8x16_t make_int8x16_t(int8_t x1, int8_t x2, int8_t x3, int8_t x4, + int8_t x5, int8_t x6, int8_t x7, int8_t x8, + int8_t x9, int8_t x10, int8_t x11, int8_t x12, + int8_t x13, int8_t x14, int8_t x15, int8_t x16) { + // Doing a load like so end ups generating worse code. + // int8_t array[16] = {x1, x2, x3, x4, x5, x6, x7, x8, + // x9, x10,x11,x12,x13,x14,x15,x16}; + // return vld1q_s8(array); + int8x16_t x{}; + // incredibly, Visual Studio does not allow x[0] = x1 + x = vsetq_lane_s8(x1, x, 0); + x = vsetq_lane_s8(x2, x, 1); + x = vsetq_lane_s8(x3, x, 2); + x = vsetq_lane_s8(x4, x, 3); + x = vsetq_lane_s8(x5, x, 4); + x = vsetq_lane_s8(x6, x, 5); + x = vsetq_lane_s8(x7, x, 6); + x = vsetq_lane_s8(x8, x, 7); + x = vsetq_lane_s8(x9, x, 8); + x = vsetq_lane_s8(x10, x, 9); + x = vsetq_lane_s8(x11, x, 10); + x = vsetq_lane_s8(x12, x, 11); + x = vsetq_lane_s8(x13, x, 12); + x = vsetq_lane_s8(x14, x, 13); + x = vsetq_lane_s8(x15, x, 14); + x = vsetq_lane_s8(x16, x, 15); + return x; +} + +// 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 { +#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO + const uint8x16_t bit_mask = 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); + } + simdjson_inline bool any() const { return vmaxvq_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 +#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(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 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); } + + // 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 = 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 +#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO + uint8x8_t inc = 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 +#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(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]; + + 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 { +#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO + const uint8x16_t bit_mask = 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 include/simdjson/arm64/simd.h */ +/* begin file include/simdjson/generic/jsoncharutils.h */ + +namespace simdjson { +namespace arm64 { +namespace { +namespace jsoncharutils { + +// return non-zero if not a structural or whitespace char +// zero otherwise +simdjson_inline uint32_t is_not_structural_or_whitespace(uint8_t c) { + return internal::structural_or_whitespace_negated[c]; +} + +simdjson_inline uint32_t is_structural_or_whitespace(uint8_t c) { + return internal::structural_or_whitespace[c]; +} + +// returns a value with the high 16 bits set if not valid +// otherwise returns the conversion of the 4 hex digits at src into the bottom +// 16 bits of the 32-bit return register +// +// see +// https://lemire.me/blog/2019/04/17/parsing-short-hexadecimal-strings-efficiently/ +static inline uint32_t hex_to_u32_nocheck( + const uint8_t *src) { // strictly speaking, static inline is a C-ism + uint32_t v1 = internal::digit_to_val32[630 + src[0]]; + uint32_t v2 = internal::digit_to_val32[420 + src[1]]; + uint32_t v3 = internal::digit_to_val32[210 + src[2]]; + uint32_t v4 = internal::digit_to_val32[0 + src[3]]; + return v1 | v2 | v3 | v4; +} + +// given a code point cp, writes to c +// the utf-8 code, outputting the length in +// bytes, if the length is zero, the code point +// is invalid +// +// This can possibly be made faster using pdep +// and clz and table lookups, but JSON documents +// have few escaped code points, and the following +// function looks cheap. +// +// Note: we assume that surrogates are treated separately +// +simdjson_inline size_t codepoint_to_utf8(uint32_t cp, uint8_t *c) { + if (cp <= 0x7F) { + c[0] = uint8_t(cp); + return 1; // ascii + } + if (cp <= 0x7FF) { + c[0] = uint8_t((cp >> 6) + 192); + c[1] = uint8_t((cp & 63) + 128); + return 2; // universal plane + // Surrogates are treated elsewhere... + //} //else if (0xd800 <= cp && cp <= 0xdfff) { + // return 0; // surrogates // could put assert here + } else if (cp <= 0xFFFF) { + c[0] = uint8_t((cp >> 12) + 224); + c[1] = uint8_t(((cp >> 6) & 63) + 128); + c[2] = uint8_t((cp & 63) + 128); + return 3; + } else if (cp <= 0x10FFFF) { // if you know you have a valid code point, this + // is not needed + c[0] = uint8_t((cp >> 18) + 240); + c[1] = uint8_t(((cp >> 12) & 63) + 128); + c[2] = uint8_t(((cp >> 6) & 63) + 128); + c[3] = uint8_t((cp & 63) + 128); + return 4; + } + // will return 0 when the code point was too large. + return 0; // bad r +} + +#if SIMDJSON_IS_32BITS // _umul128 for x86, arm +// this is a slow emulation routine for 32-bit +// +static simdjson_inline uint64_t __emulu(uint32_t x, uint32_t y) { + return x * (uint64_t)y; +} +static simdjson_inline uint64_t _umul128(uint64_t ab, uint64_t cd, uint64_t *hi) { + uint64_t ad = __emulu((uint32_t)(ab >> 32), (uint32_t)cd); + uint64_t bd = __emulu((uint32_t)ab, (uint32_t)cd); + uint64_t adbc = ad + __emulu((uint32_t)ab, (uint32_t)(cd >> 32)); + uint64_t adbc_carry = !!(adbc < ad); + uint64_t lo = bd + (adbc << 32); + *hi = __emulu((uint32_t)(ab >> 32), (uint32_t)(cd >> 32)) + (adbc >> 32) + + (adbc_carry << 32) + !!(lo < bd); + return lo; +} +#endif + +using internal::value128; + +simdjson_inline value128 full_multiplication(uint64_t value1, uint64_t value2) { + value128 answer; +#if SIMDJSON_REGULAR_VISUAL_STUDIO || SIMDJSON_IS_32BITS +#ifdef _M_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 // _M_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 jsoncharutils +} // unnamed namespace +} // namespace arm64 +} // namespace simdjson +/* end file include/simdjson/generic/jsoncharutils.h */ +/* begin file include/simdjson/generic/atomparsing.h */ +namespace simdjson { +namespace arm64 { +namespace { +/// @private +namespace atomparsing { + +// The string_to_uint32 is exclusively used to map literal strings to 32-bit values. +// We use memcpy instead of a pointer cast to avoid undefined behaviors since we cannot +// be certain that the character pointer will be properly aligned. +// You might think that using memcpy makes this function expensive, but you'd be wrong. +// All decent optimizing compilers (GCC, clang, Visual Studio) will compile string_to_uint32("false"); +// to the compile-time constant 1936482662. +simdjson_inline uint32_t string_to_uint32(const char* str) { uint32_t val; std::memcpy(&val, str, sizeof(uint32_t)); return val; } + + +// 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 arm64 +} // namespace simdjson +/* end file include/simdjson/generic/atomparsing.h */ +/* begin file include/simdjson/arm64/stringparsing.h */ +#ifndef SIMDJSON_ARM64_STRINGPARSING_H +#define SIMDJSON_ARM64_STRINGPARSING_H + + +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 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 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 + }; +} + +} // unnamed namespace +} // namespace arm64 +} // namespace simdjson + +#endif // SIMDJSON_ARM64_STRINGPARSING_H +/* end file include/simdjson/arm64/stringparsing.h */ +/* begin file include/simdjson/arm64/numberparsing.h */ +#ifndef SIMDJSON_ARM64_NUMBERPARSING_H +#define SIMDJSON_ARM64_NUMBERPARSING_H + +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); +} + +} // namespace numberparsing +} // namespace arm64 +} // namespace simdjson + +#define SIMDJSON_SWAR_NUMBER_PARSING 1 + +/* begin file include/simdjson/generic/numberparsing.h */ +#include + +namespace simdjson { +namespace arm64 { +/// @private +namespace numberparsing { + +/** + * 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 +}; + +#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))) +#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)) +#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 + // 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; + + + // 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 = jsoncharutils::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. + + // 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. In very rare cases, even that + // will not suffice, though it is seemingly very hard to find such a scenario. + // + // 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 = jsoncharutils::full_multiplication(i, simdjson::internal::power_of_five_128[index + 1]); + firstproduct.low += secondproduct.high; + if(secondproduct.high > firstproduct.low) { firstproduct.high++; } + // At this point, we might need to add at most one to firstproduct, but this + // can only change the value of firstproduct.high if firstproduct.low is maximal. + if(simdjson_unlikely(firstproduct.low == 0xFFFFFFFFFFFFFFFF)) { + // This is very unlikely, but if so, we need to do much more work! + return false; + } + } + 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 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_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 doesn't 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 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 */ +template +error_code slow_float_parsing(simdjson_unused const uint8_t * src, W writer) { + double d; + if (parse_float_fallback(src, &d)) { + writer.append_double(d); + 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: This makes a *copy* of the writer and passes it to slow_float_parsing. This happens + // because slow_float_parsing is a non-inlined function. 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. + // This is what forces the skip_double, as well. + error_code error = slow_float_parsing(src, writer); + writer.skip_double(); + 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; +} + +// 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 +} + +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_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 INVALID_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 INVALID_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 doesn't 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 = start_digits-src > 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++; } + if ( p == src ) { return NUMBER_ERROR; } + if (jsoncharutils::is_structural_or_whitespace(*p)) { + // We have an integer. + // If the number is negative and valid, it must be a signed integer. + if(negative) { return number_type::signed_integer; } + // We want values larger or equal to 9223372036854775808 to be unsigned + // integers, and the other values to be signed integers. + int digit_count = int(p - src); + if(digit_count >= 19) { + const uint8_t * smaller_big_integer = reinterpret_cast("9223372036854775808"); + if((digit_count >= 20) || (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 = start_digits-src > 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 + +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; + default: SIMDJSON_UNREACHABLE(); + } + return out; +} + +} // namespace numberparsing +} // namespace arm64 +} // namespace simdjson +/* end file include/simdjson/generic/numberparsing.h */ + +#endif // SIMDJSON_ARM64_NUMBERPARSING_H +/* end file include/simdjson/arm64/numberparsing.h */ +/* begin file include/simdjson/arm64/end.h */ +/* end file include/simdjson/arm64/end.h */ + +#endif // SIMDJSON_IMPLEMENTATION_ARM64 + +#endif // SIMDJSON_ARM64_H +/* end file include/simdjson/arm64.h */ +/* begin file include/simdjson/fallback.h */ +#ifndef SIMDJSON_FALLBACK_H +#define SIMDJSON_FALLBACK_H + + +#if SIMDJSON_IMPLEMENTATION_FALLBACK + +namespace simdjson { +/** + * Fallback implementation (runs on any machine). + */ +namespace fallback { +} // namespace fallback +} // namespace simdjson + +/* begin file include/simdjson/fallback/implementation.h */ +#ifndef SIMDJSON_FALLBACK_IMPLEMENTATION_H +#define SIMDJSON_FALLBACK_IMPLEMENTATION_H + + +namespace simdjson { +namespace fallback { + +namespace { +using namespace simdjson; +using namespace simdjson::dom; +} + +/** + * @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 simdjson + +#endif // SIMDJSON_FALLBACK_IMPLEMENTATION_H +/* end file include/simdjson/fallback/implementation.h */ + +/* begin file include/simdjson/fallback/begin.h */ +// redefining SIMDJSON_IMPLEMENTATION to "fallback" +// #define SIMDJSON_IMPLEMENTATION fallback +/* end file include/simdjson/fallback/begin.h */ + +// Declarations +/* begin file include/simdjson/generic/dom_parser_implementation.h */ + +namespace simdjson { +namespace fallback { + +// expectation: sizeof(open_container) = 64/8. +struct open_container { + uint32_t tape_index; // where, on the tape, does the scope ([,{) begins + uint32_t count; // how many elements in the scope +}; // struct open_container + +static_assert(sizeof(open_container) == 64/8, "Open container must be 64 bits"); + +class dom_parser_implementation final : public internal::dom_parser_implementation { +public: + /** Tape location of each open { or [ */ + std::unique_ptr open_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 fallback +} // namespace simdjson + +namespace simdjson { +namespace fallback { + +inline dom_parser_implementation::dom_parser_implementation() noexcept = default; +inline dom_parser_implementation::dom_parser_implementation(dom_parser_implementation &&other) noexcept = default; +inline dom_parser_implementation &dom_parser_implementation::operator=(dom_parser_implementation &&other) noexcept = default; + +// Leaving these here so they can be inlined if so desired +inline simdjson_warn_unused error_code dom_parser_implementation::set_capacity(size_t capacity) noexcept { + if(capacity > SIMDJSON_MAXSIZE_BYTES) { 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 fallback +} // namespace simdjson +/* end file include/simdjson/generic/dom_parser_implementation.h */ +/* begin file include/simdjson/fallback/bitmanipulation.h */ +#ifndef SIMDJSON_FALLBACK_BITMANIPULATION_H +#define SIMDJSON_FALLBACK_BITMANIPULATION_H + +#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 include/simdjson/fallback/bitmanipulation.h */ +/* begin file include/simdjson/generic/jsoncharutils.h */ + +namespace simdjson { +namespace fallback { +namespace { +namespace jsoncharutils { + +// return non-zero if not a structural or whitespace char +// zero otherwise +simdjson_inline uint32_t is_not_structural_or_whitespace(uint8_t c) { + return internal::structural_or_whitespace_negated[c]; +} + +simdjson_inline uint32_t is_structural_or_whitespace(uint8_t c) { + return internal::structural_or_whitespace[c]; +} + +// returns a value with the high 16 bits set if not valid +// otherwise returns the conversion of the 4 hex digits at src into the bottom +// 16 bits of the 32-bit return register +// +// see +// https://lemire.me/blog/2019/04/17/parsing-short-hexadecimal-strings-efficiently/ +static inline uint32_t hex_to_u32_nocheck( + const uint8_t *src) { // strictly speaking, static inline is a C-ism + uint32_t v1 = internal::digit_to_val32[630 + src[0]]; + uint32_t v2 = internal::digit_to_val32[420 + src[1]]; + uint32_t v3 = internal::digit_to_val32[210 + src[2]]; + uint32_t v4 = internal::digit_to_val32[0 + src[3]]; + return v1 | v2 | v3 | v4; +} + +// given a code point cp, writes to c +// the utf-8 code, outputting the length in +// bytes, if the length is zero, the code point +// is invalid +// +// This can possibly be made faster using pdep +// and clz and table lookups, but JSON documents +// have few escaped code points, and the following +// function looks cheap. +// +// Note: we assume that surrogates are treated separately +// +simdjson_inline size_t codepoint_to_utf8(uint32_t cp, uint8_t *c) { + if (cp <= 0x7F) { + c[0] = uint8_t(cp); + return 1; // ascii + } + if (cp <= 0x7FF) { + c[0] = uint8_t((cp >> 6) + 192); + c[1] = uint8_t((cp & 63) + 128); + return 2; // universal plane + // Surrogates are treated elsewhere... + //} //else if (0xd800 <= cp && cp <= 0xdfff) { + // return 0; // surrogates // could put assert here + } else if (cp <= 0xFFFF) { + c[0] = uint8_t((cp >> 12) + 224); + c[1] = uint8_t(((cp >> 6) & 63) + 128); + c[2] = uint8_t((cp & 63) + 128); + return 3; + } else if (cp <= 0x10FFFF) { // if you know you have a valid code point, this + // is not needed + c[0] = uint8_t((cp >> 18) + 240); + c[1] = uint8_t(((cp >> 12) & 63) + 128); + c[2] = uint8_t(((cp >> 6) & 63) + 128); + c[3] = uint8_t((cp & 63) + 128); + return 4; + } + // will return 0 when the code point was too large. + return 0; // bad r +} + +#if SIMDJSON_IS_32BITS // _umul128 for x86, arm +// this is a slow emulation routine for 32-bit +// +static simdjson_inline uint64_t __emulu(uint32_t x, uint32_t y) { + return x * (uint64_t)y; +} +static simdjson_inline uint64_t _umul128(uint64_t ab, uint64_t cd, uint64_t *hi) { + uint64_t ad = __emulu((uint32_t)(ab >> 32), (uint32_t)cd); + uint64_t bd = __emulu((uint32_t)ab, (uint32_t)cd); + uint64_t adbc = ad + __emulu((uint32_t)ab, (uint32_t)(cd >> 32)); + uint64_t adbc_carry = !!(adbc < ad); + uint64_t lo = bd + (adbc << 32); + *hi = __emulu((uint32_t)(ab >> 32), (uint32_t)(cd >> 32)) + (adbc >> 32) + + (adbc_carry << 32) + !!(lo < bd); + return lo; +} +#endif + +using internal::value128; + +simdjson_inline value128 full_multiplication(uint64_t value1, uint64_t value2) { + value128 answer; +#if SIMDJSON_REGULAR_VISUAL_STUDIO || SIMDJSON_IS_32BITS +#ifdef _M_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 // _M_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 jsoncharutils +} // unnamed namespace +} // namespace fallback +} // namespace simdjson +/* end file include/simdjson/generic/jsoncharutils.h */ +/* begin file include/simdjson/generic/atomparsing.h */ +namespace simdjson { +namespace fallback { +namespace { +/// @private +namespace atomparsing { + +// The string_to_uint32 is exclusively used to map literal strings to 32-bit values. +// We use memcpy instead of a pointer cast to avoid undefined behaviors since we cannot +// be certain that the character pointer will be properly aligned. +// You might think that using memcpy makes this function expensive, but you'd be wrong. +// All decent optimizing compilers (GCC, clang, Visual Studio) will compile string_to_uint32("false"); +// to the compile-time constant 1936482662. +simdjson_inline uint32_t string_to_uint32(const char* str) { uint32_t val; std::memcpy(&val, str, sizeof(uint32_t)); return val; } + + +// 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 fallback +} // namespace simdjson +/* end file include/simdjson/generic/atomparsing.h */ +/* begin file include/simdjson/fallback/stringparsing.h */ +#ifndef SIMDJSON_FALLBACK_STRINGPARSING_H +#define SIMDJSON_FALLBACK_STRINGPARSING_H + + +namespace simdjson { +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); + + 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] }; +} + +} // unnamed namespace +} // namespace fallback +} // namespace simdjson + +#endif // SIMDJSON_FALLBACK_STRINGPARSING_H +/* end file include/simdjson/fallback/stringparsing.h */ +/* begin file include/simdjson/fallback/numberparsing.h */ +#ifndef SIMDJSON_FALLBACK_NUMBERPARSING_H +#define SIMDJSON_FALLBACK_NUMBERPARSING_H + +#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)); +} + +} // namespace numberparsing +} // namespace fallback +} // namespace simdjson + +#define SIMDJSON_SWAR_NUMBER_PARSING 1 + +/* begin file include/simdjson/generic/numberparsing.h */ +#include + +namespace simdjson { +namespace fallback { +/// @private +namespace numberparsing { + +/** + * 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 +}; + +#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))) +#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)) +#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 + // 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; + + + // 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 = jsoncharutils::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. + + // 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. In very rare cases, even that + // will not suffice, though it is seemingly very hard to find such a scenario. + // + // 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 = jsoncharutils::full_multiplication(i, simdjson::internal::power_of_five_128[index + 1]); + firstproduct.low += secondproduct.high; + if(secondproduct.high > firstproduct.low) { firstproduct.high++; } + // At this point, we might need to add at most one to firstproduct, but this + // can only change the value of firstproduct.high if firstproduct.low is maximal. + if(simdjson_unlikely(firstproduct.low == 0xFFFFFFFFFFFFFFFF)) { + // This is very unlikely, but if so, we need to do much more work! + return false; + } + } + 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 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_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 doesn't 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 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 */ +template +error_code slow_float_parsing(simdjson_unused const uint8_t * src, W writer) { + double d; + if (parse_float_fallback(src, &d)) { + writer.append_double(d); + 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: This makes a *copy* of the writer and passes it to slow_float_parsing. This happens + // because slow_float_parsing is a non-inlined function. 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. + // This is what forces the skip_double, as well. + error_code error = slow_float_parsing(src, writer); + writer.skip_double(); + 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; +} + +// 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 +} + +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_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 INVALID_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 INVALID_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 doesn't 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 = start_digits-src > 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++; } + if ( p == src ) { return NUMBER_ERROR; } + if (jsoncharutils::is_structural_or_whitespace(*p)) { + // We have an integer. + // If the number is negative and valid, it must be a signed integer. + if(negative) { return number_type::signed_integer; } + // We want values larger or equal to 9223372036854775808 to be unsigned + // integers, and the other values to be signed integers. + int digit_count = int(p - src); + if(digit_count >= 19) { + const uint8_t * smaller_big_integer = reinterpret_cast("9223372036854775808"); + if((digit_count >= 20) || (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 = start_digits-src > 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 + +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; + default: SIMDJSON_UNREACHABLE(); + } + return out; +} + +} // namespace numberparsing +} // namespace fallback +} // namespace simdjson +/* end file include/simdjson/generic/numberparsing.h */ + +#endif // SIMDJSON_FALLBACK_NUMBERPARSING_H +/* end file include/simdjson/fallback/numberparsing.h */ +/* begin file include/simdjson/fallback/end.h */ +/* end file include/simdjson/fallback/end.h */ + +#endif // SIMDJSON_IMPLEMENTATION_FALLBACK +#endif // SIMDJSON_FALLBACK_H +/* end file include/simdjson/fallback.h */ +/* begin file include/simdjson/icelake.h */ +#ifndef SIMDJSON_ICELAKE_H +#define SIMDJSON_ICELAKE_H + + +#if SIMDJSON_IMPLEMENTATION_ICELAKE + +#if SIMDJSON_CAN_ALWAYS_RUN_ICELAKE +#define SIMDJSON_TARGET_ICELAKE +#define SIMDJSON_UNTARGET_ICELAKE +#else +#define SIMDJSON_TARGET_ICELAKE SIMDJSON_TARGET_REGION("avx512f,avx512dq,avx512cd,avx512bw,avx512vbmi,avx512vbmi2,avx512vl,avx2,bmi,pclmul,lzcnt,popcnt") +#define SIMDJSON_UNTARGET_ICELAKE SIMDJSON_UNTARGET_REGION +#endif + +namespace simdjson { +/** + * Implementation for Icelake (Intel AVX512). + */ +namespace icelake { +} // namespace icelake +} // namespace simdjson + +// +// These two need to be included outside SIMDJSON_TARGET_ICELAKE +// +/* begin file include/simdjson/icelake/implementation.h */ +#ifndef SIMDJSON_ICELAKE_IMPLEMENTATION_H +#define SIMDJSON_ICELAKE_IMPLEMENTATION_H + + +// The constructor may be executed on any host, so we take care not to use SIMDJSON_TARGET_ICELAKE +namespace simdjson { +namespace icelake { + +using namespace simdjson; + +/** + * @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 simdjson + +#endif // SIMDJSON_ICELAKE_IMPLEMENTATION_H +/* end file include/simdjson/icelake/implementation.h */ +/* begin file include/simdjson/icelake/intrinsics.h */ +#ifndef SIMDJSON_ICELAKE_INTRINSICS_H +#define SIMDJSON_ICELAKE_INTRINSICS_H + + +#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 include/simdjson/icelake/intrinsics.h */ + +// +// The rest need to be inside the region +// +/* begin file include/simdjson/icelake/begin.h */ +// redefining SIMDJSON_IMPLEMENTATION to "icelake" +// #define SIMDJSON_IMPLEMENTATION icelake +SIMDJSON_TARGET_ICELAKE +/* end file include/simdjson/icelake/begin.h */ + +// Declarations +/* begin file include/simdjson/generic/dom_parser_implementation.h */ + +namespace simdjson { +namespace icelake { + +// expectation: sizeof(open_container) = 64/8. +struct open_container { + uint32_t tape_index; // where, on the tape, does the scope ([,{) begins + uint32_t count; // how many elements in the scope +}; // struct open_container + +static_assert(sizeof(open_container) == 64/8, "Open container must be 64 bits"); + +class dom_parser_implementation final : public internal::dom_parser_implementation { +public: + /** Tape location of each open { or [ */ + std::unique_ptr open_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 icelake +} // namespace simdjson + +namespace simdjson { +namespace icelake { + +inline dom_parser_implementation::dom_parser_implementation() noexcept = default; +inline dom_parser_implementation::dom_parser_implementation(dom_parser_implementation &&other) noexcept = default; +inline dom_parser_implementation &dom_parser_implementation::operator=(dom_parser_implementation &&other) noexcept = default; + +// Leaving these here so they can be inlined if so desired +inline simdjson_warn_unused error_code dom_parser_implementation::set_capacity(size_t capacity) noexcept { + if(capacity > SIMDJSON_MAXSIZE_BYTES) { 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 icelake +} // namespace simdjson +/* end file include/simdjson/generic/dom_parser_implementation.h */ +/* begin file include/simdjson/icelake/bitmanipulation.h */ +#ifndef SIMDJSON_ICELAKE_BITMANIPULATION_H +#define SIMDJSON_ICELAKE_BITMANIPULATION_H + +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 include/simdjson/icelake/bitmanipulation.h */ +/* begin file include/simdjson/icelake/bitmask.h */ +#ifndef SIMDJSON_ICELAKE_BITMASK_H +#define SIMDJSON_ICELAKE_BITMASK_H + +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 include/simdjson/icelake/bitmask.h */ +/* begin file include/simdjson/icelake/simd.h */ +#ifndef SIMDJSON_ICELAKE_SIMD_H +#define SIMDJSON_ICELAKE_SIMD_H + + + + +#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 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 + )); + } + }; + + // 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]; + + 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 include/simdjson/icelake/simd.h */ +/* begin file include/simdjson/generic/jsoncharutils.h */ + +namespace simdjson { +namespace icelake { +namespace { +namespace jsoncharutils { + +// return non-zero if not a structural or whitespace char +// zero otherwise +simdjson_inline uint32_t is_not_structural_or_whitespace(uint8_t c) { + return internal::structural_or_whitespace_negated[c]; +} + +simdjson_inline uint32_t is_structural_or_whitespace(uint8_t c) { + return internal::structural_or_whitespace[c]; +} + +// returns a value with the high 16 bits set if not valid +// otherwise returns the conversion of the 4 hex digits at src into the bottom +// 16 bits of the 32-bit return register +// +// see +// https://lemire.me/blog/2019/04/17/parsing-short-hexadecimal-strings-efficiently/ +static inline uint32_t hex_to_u32_nocheck( + const uint8_t *src) { // strictly speaking, static inline is a C-ism + uint32_t v1 = internal::digit_to_val32[630 + src[0]]; + uint32_t v2 = internal::digit_to_val32[420 + src[1]]; + uint32_t v3 = internal::digit_to_val32[210 + src[2]]; + uint32_t v4 = internal::digit_to_val32[0 + src[3]]; + return v1 | v2 | v3 | v4; +} + +// given a code point cp, writes to c +// the utf-8 code, outputting the length in +// bytes, if the length is zero, the code point +// is invalid +// +// This can possibly be made faster using pdep +// and clz and table lookups, but JSON documents +// have few escaped code points, and the following +// function looks cheap. +// +// Note: we assume that surrogates are treated separately +// +simdjson_inline size_t codepoint_to_utf8(uint32_t cp, uint8_t *c) { + if (cp <= 0x7F) { + c[0] = uint8_t(cp); + return 1; // ascii + } + if (cp <= 0x7FF) { + c[0] = uint8_t((cp >> 6) + 192); + c[1] = uint8_t((cp & 63) + 128); + return 2; // universal plane + // Surrogates are treated elsewhere... + //} //else if (0xd800 <= cp && cp <= 0xdfff) { + // return 0; // surrogates // could put assert here + } else if (cp <= 0xFFFF) { + c[0] = uint8_t((cp >> 12) + 224); + c[1] = uint8_t(((cp >> 6) & 63) + 128); + c[2] = uint8_t((cp & 63) + 128); + return 3; + } else if (cp <= 0x10FFFF) { // if you know you have a valid code point, this + // is not needed + c[0] = uint8_t((cp >> 18) + 240); + c[1] = uint8_t(((cp >> 12) & 63) + 128); + c[2] = uint8_t(((cp >> 6) & 63) + 128); + c[3] = uint8_t((cp & 63) + 128); + return 4; + } + // will return 0 when the code point was too large. + return 0; // bad r +} + +#if SIMDJSON_IS_32BITS // _umul128 for x86, arm +// this is a slow emulation routine for 32-bit +// +static simdjson_inline uint64_t __emulu(uint32_t x, uint32_t y) { + return x * (uint64_t)y; +} +static simdjson_inline uint64_t _umul128(uint64_t ab, uint64_t cd, uint64_t *hi) { + uint64_t ad = __emulu((uint32_t)(ab >> 32), (uint32_t)cd); + uint64_t bd = __emulu((uint32_t)ab, (uint32_t)cd); + uint64_t adbc = ad + __emulu((uint32_t)ab, (uint32_t)(cd >> 32)); + uint64_t adbc_carry = !!(adbc < ad); + uint64_t lo = bd + (adbc << 32); + *hi = __emulu((uint32_t)(ab >> 32), (uint32_t)(cd >> 32)) + (adbc >> 32) + + (adbc_carry << 32) + !!(lo < bd); + return lo; +} +#endif + +using internal::value128; + +simdjson_inline value128 full_multiplication(uint64_t value1, uint64_t value2) { + value128 answer; +#if SIMDJSON_REGULAR_VISUAL_STUDIO || SIMDJSON_IS_32BITS +#ifdef _M_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 // _M_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 jsoncharutils +} // unnamed namespace +} // namespace icelake +} // namespace simdjson +/* end file include/simdjson/generic/jsoncharutils.h */ +/* begin file include/simdjson/generic/atomparsing.h */ +namespace simdjson { +namespace icelake { +namespace { +/// @private +namespace atomparsing { + +// The string_to_uint32 is exclusively used to map literal strings to 32-bit values. +// We use memcpy instead of a pointer cast to avoid undefined behaviors since we cannot +// be certain that the character pointer will be properly aligned. +// You might think that using memcpy makes this function expensive, but you'd be wrong. +// All decent optimizing compilers (GCC, clang, Visual Studio) will compile string_to_uint32("false"); +// to the compile-time constant 1936482662. +simdjson_inline uint32_t string_to_uint32(const char* str) { uint32_t val; std::memcpy(&val, str, sizeof(uint32_t)); return val; } + + +// 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 icelake +} // namespace simdjson +/* end file include/simdjson/generic/atomparsing.h */ +/* begin file include/simdjson/icelake/stringparsing.h */ +#ifndef SIMDJSON_ICELAKE_STRINGPARSING_H +#define SIMDJSON_ICELAKE_STRINGPARSING_H + + +namespace simdjson { +namespace icelake { +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 ((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 + }; +} + +} // unnamed namespace +} // namespace icelake +} // namespace simdjson + +#endif // SIMDJSON_ICELAKE_STRINGPARSING_H +/* end file include/simdjson/icelake/stringparsing.h */ +/* begin file include/simdjson/icelake/numberparsing.h */ +#ifndef SIMDJSON_ICELAKE_NUMBERPARSING_H +#define SIMDJSON_ICELAKE_NUMBERPARSING_H + +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 +} + +} // namespace numberparsing +} // namespace icelake +} // namespace simdjson + +#define SIMDJSON_SWAR_NUMBER_PARSING 1 + +/* begin file include/simdjson/generic/numberparsing.h */ +#include + +namespace simdjson { +namespace icelake { +/// @private +namespace numberparsing { + +/** + * 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 +}; + +#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))) +#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)) +#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 + // 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; + + + // 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 = jsoncharutils::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. + + // 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. In very rare cases, even that + // will not suffice, though it is seemingly very hard to find such a scenario. + // + // 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 = jsoncharutils::full_multiplication(i, simdjson::internal::power_of_five_128[index + 1]); + firstproduct.low += secondproduct.high; + if(secondproduct.high > firstproduct.low) { firstproduct.high++; } + // At this point, we might need to add at most one to firstproduct, but this + // can only change the value of firstproduct.high if firstproduct.low is maximal. + if(simdjson_unlikely(firstproduct.low == 0xFFFFFFFFFFFFFFFF)) { + // This is very unlikely, but if so, we need to do much more work! + return false; + } + } + 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 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_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 doesn't 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 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 */ +template +error_code slow_float_parsing(simdjson_unused const uint8_t * src, W writer) { + double d; + if (parse_float_fallback(src, &d)) { + writer.append_double(d); + 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: This makes a *copy* of the writer and passes it to slow_float_parsing. This happens + // because slow_float_parsing is a non-inlined function. 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. + // This is what forces the skip_double, as well. + error_code error = slow_float_parsing(src, writer); + writer.skip_double(); + 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; +} + +// 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 +} + +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_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 INVALID_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 INVALID_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 doesn't 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 = start_digits-src > 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++; } + if ( p == src ) { return NUMBER_ERROR; } + if (jsoncharutils::is_structural_or_whitespace(*p)) { + // We have an integer. + // If the number is negative and valid, it must be a signed integer. + if(negative) { return number_type::signed_integer; } + // We want values larger or equal to 9223372036854775808 to be unsigned + // integers, and the other values to be signed integers. + int digit_count = int(p - src); + if(digit_count >= 19) { + const uint8_t * smaller_big_integer = reinterpret_cast("9223372036854775808"); + if((digit_count >= 20) || (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 = start_digits-src > 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 + +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; + default: SIMDJSON_UNREACHABLE(); + } + return out; +} + +} // namespace numberparsing +} // namespace icelake +} // namespace simdjson +/* end file include/simdjson/generic/numberparsing.h */ + +#endif // SIMDJSON_ICELAKE_NUMBERPARSING_H +/* end file include/simdjson/icelake/numberparsing.h */ +/* begin file include/simdjson/icelake/end.h */ +SIMDJSON_UNTARGET_ICELAKE +/* end file include/simdjson/icelake/end.h */ + +#endif // SIMDJSON_IMPLEMENTATION_ICELAKE +#endif // SIMDJSON_ICELAKE_H +/* end file include/simdjson/icelake.h */ +/* begin file include/simdjson/haswell.h */ +#ifndef SIMDJSON_HASWELL_H +#define SIMDJSON_HASWELL_H + + +#if SIMDJSON_IMPLEMENTATION_HASWELL + +#if SIMDJSON_CAN_ALWAYS_RUN_HASWELL +#define SIMDJSON_TARGET_HASWELL +#define SIMDJSON_UNTARGET_HASWELL +#else +#define SIMDJSON_TARGET_HASWELL SIMDJSON_TARGET_REGION("avx2,bmi,pclmul,lzcnt,popcnt") +#define SIMDJSON_UNTARGET_HASWELL SIMDJSON_UNTARGET_REGION +#endif + +namespace simdjson { +/** + * Implementation for Haswell (Intel AVX2). + */ +namespace haswell { +} // namespace haswell +} // namespace simdjson + +// +// These two need to be included outside SIMDJSON_TARGET_HASWELL +// +/* begin file include/simdjson/haswell/implementation.h */ +#ifndef SIMDJSON_HASWELL_IMPLEMENTATION_H +#define SIMDJSON_HASWELL_IMPLEMENTATION_H + + +// The constructor may be executed on any host, so we take care not to use SIMDJSON_TARGET_HASWELL +namespace simdjson { +namespace haswell { + +using namespace simdjson; + +/** + * @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 simdjson + +#endif // SIMDJSON_HASWELL_IMPLEMENTATION_H +/* end file include/simdjson/haswell/implementation.h */ +/* begin file include/simdjson/haswell/intrinsics.h */ +#ifndef SIMDJSON_HASWELL_INTRINSICS_H +#define SIMDJSON_HASWELL_INTRINSICS_H + + +#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 include/simdjson/haswell/intrinsics.h */ + +// +// The rest need to be inside the region +// +/* begin file include/simdjson/haswell/begin.h */ +// redefining SIMDJSON_IMPLEMENTATION to "haswell" +// #define SIMDJSON_IMPLEMENTATION haswell +SIMDJSON_TARGET_HASWELL +/* end file include/simdjson/haswell/begin.h */ + +// Declarations +/* begin file include/simdjson/generic/dom_parser_implementation.h */ + +namespace simdjson { +namespace haswell { + +// expectation: sizeof(open_container) = 64/8. +struct open_container { + uint32_t tape_index; // where, on the tape, does the scope ([,{) begins + uint32_t count; // how many elements in the scope +}; // struct open_container + +static_assert(sizeof(open_container) == 64/8, "Open container must be 64 bits"); + +class dom_parser_implementation final : public internal::dom_parser_implementation { +public: + /** Tape location of each open { or [ */ + std::unique_ptr open_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 haswell +} // namespace simdjson + +namespace simdjson { +namespace haswell { + +inline dom_parser_implementation::dom_parser_implementation() noexcept = default; +inline dom_parser_implementation::dom_parser_implementation(dom_parser_implementation &&other) noexcept = default; +inline dom_parser_implementation &dom_parser_implementation::operator=(dom_parser_implementation &&other) noexcept = default; + +// Leaving these here so they can be inlined if so desired +inline simdjson_warn_unused error_code dom_parser_implementation::set_capacity(size_t capacity) noexcept { + if(capacity > SIMDJSON_MAXSIZE_BYTES) { 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 haswell +} // namespace simdjson +/* end file include/simdjson/generic/dom_parser_implementation.h */ +/* begin file include/simdjson/haswell/bitmanipulation.h */ +#ifndef SIMDJSON_HASWELL_BITMANIPULATION_H +#define SIMDJSON_HASWELL_BITMANIPULATION_H + +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 + 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 include/simdjson/haswell/bitmanipulation.h */ +/* begin file include/simdjson/haswell/bitmask.h */ +#ifndef SIMDJSON_HASWELL_BITMASK_H +#define SIMDJSON_HASWELL_BITMASK_H + +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 include/simdjson/haswell/bitmask.h */ +/* begin file include/simdjson/haswell/simd.h */ +#ifndef SIMDJSON_HASWELL_SIMD_H +#define SIMDJSON_HASWELL_SIMD_H + + +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]; + + 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 include/simdjson/haswell/simd.h */ +/* begin file include/simdjson/generic/jsoncharutils.h */ + +namespace simdjson { +namespace haswell { +namespace { +namespace jsoncharutils { + +// return non-zero if not a structural or whitespace char +// zero otherwise +simdjson_inline uint32_t is_not_structural_or_whitespace(uint8_t c) { + return internal::structural_or_whitespace_negated[c]; +} + +simdjson_inline uint32_t is_structural_or_whitespace(uint8_t c) { + return internal::structural_or_whitespace[c]; +} + +// returns a value with the high 16 bits set if not valid +// otherwise returns the conversion of the 4 hex digits at src into the bottom +// 16 bits of the 32-bit return register +// +// see +// https://lemire.me/blog/2019/04/17/parsing-short-hexadecimal-strings-efficiently/ +static inline uint32_t hex_to_u32_nocheck( + const uint8_t *src) { // strictly speaking, static inline is a C-ism + uint32_t v1 = internal::digit_to_val32[630 + src[0]]; + uint32_t v2 = internal::digit_to_val32[420 + src[1]]; + uint32_t v3 = internal::digit_to_val32[210 + src[2]]; + uint32_t v4 = internal::digit_to_val32[0 + src[3]]; + return v1 | v2 | v3 | v4; +} + +// given a code point cp, writes to c +// the utf-8 code, outputting the length in +// bytes, if the length is zero, the code point +// is invalid +// +// This can possibly be made faster using pdep +// and clz and table lookups, but JSON documents +// have few escaped code points, and the following +// function looks cheap. +// +// Note: we assume that surrogates are treated separately +// +simdjson_inline size_t codepoint_to_utf8(uint32_t cp, uint8_t *c) { + if (cp <= 0x7F) { + c[0] = uint8_t(cp); + return 1; // ascii + } + if (cp <= 0x7FF) { + c[0] = uint8_t((cp >> 6) + 192); + c[1] = uint8_t((cp & 63) + 128); + return 2; // universal plane + // Surrogates are treated elsewhere... + //} //else if (0xd800 <= cp && cp <= 0xdfff) { + // return 0; // surrogates // could put assert here + } else if (cp <= 0xFFFF) { + c[0] = uint8_t((cp >> 12) + 224); + c[1] = uint8_t(((cp >> 6) & 63) + 128); + c[2] = uint8_t((cp & 63) + 128); + return 3; + } else if (cp <= 0x10FFFF) { // if you know you have a valid code point, this + // is not needed + c[0] = uint8_t((cp >> 18) + 240); + c[1] = uint8_t(((cp >> 12) & 63) + 128); + c[2] = uint8_t(((cp >> 6) & 63) + 128); + c[3] = uint8_t((cp & 63) + 128); + return 4; + } + // will return 0 when the code point was too large. + return 0; // bad r +} + +#if SIMDJSON_IS_32BITS // _umul128 for x86, arm +// this is a slow emulation routine for 32-bit +// +static simdjson_inline uint64_t __emulu(uint32_t x, uint32_t y) { + return x * (uint64_t)y; +} +static simdjson_inline uint64_t _umul128(uint64_t ab, uint64_t cd, uint64_t *hi) { + uint64_t ad = __emulu((uint32_t)(ab >> 32), (uint32_t)cd); + uint64_t bd = __emulu((uint32_t)ab, (uint32_t)cd); + uint64_t adbc = ad + __emulu((uint32_t)ab, (uint32_t)(cd >> 32)); + uint64_t adbc_carry = !!(adbc < ad); + uint64_t lo = bd + (adbc << 32); + *hi = __emulu((uint32_t)(ab >> 32), (uint32_t)(cd >> 32)) + (adbc >> 32) + + (adbc_carry << 32) + !!(lo < bd); + return lo; +} +#endif + +using internal::value128; + +simdjson_inline value128 full_multiplication(uint64_t value1, uint64_t value2) { + value128 answer; +#if SIMDJSON_REGULAR_VISUAL_STUDIO || SIMDJSON_IS_32BITS +#ifdef _M_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 // _M_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 jsoncharutils +} // unnamed namespace +} // namespace haswell +} // namespace simdjson +/* end file include/simdjson/generic/jsoncharutils.h */ +/* begin file include/simdjson/generic/atomparsing.h */ +namespace simdjson { +namespace haswell { +namespace { +/// @private +namespace atomparsing { + +// The string_to_uint32 is exclusively used to map literal strings to 32-bit values. +// We use memcpy instead of a pointer cast to avoid undefined behaviors since we cannot +// be certain that the character pointer will be properly aligned. +// You might think that using memcpy makes this function expensive, but you'd be wrong. +// All decent optimizing compilers (GCC, clang, Visual Studio) will compile string_to_uint32("false"); +// to the compile-time constant 1936482662. +simdjson_inline uint32_t string_to_uint32(const char* str) { uint32_t val; std::memcpy(&val, str, sizeof(uint32_t)); return val; } + + +// 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 haswell +} // namespace simdjson +/* end file include/simdjson/generic/atomparsing.h */ +/* begin file include/simdjson/haswell/stringparsing.h */ +#ifndef SIMDJSON_HASWELL_STRINGPARSING_H +#define SIMDJSON_HASWELL_STRINGPARSING_H + + +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 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 ((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 + }; +} + +} // unnamed namespace +} // namespace haswell +} // namespace simdjson + +#endif // SIMDJSON_HASWELL_STRINGPARSING_H +/* end file include/simdjson/haswell/stringparsing.h */ +/* begin file include/simdjson/haswell/numberparsing.h */ +#ifndef SIMDJSON_HASWELL_NUMBERPARSING_H +#define SIMDJSON_HASWELL_NUMBERPARSING_H + +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 +} + +} // namespace numberparsing +} // namespace haswell +} // namespace simdjson + +#define SIMDJSON_SWAR_NUMBER_PARSING 1 + +/* begin file include/simdjson/generic/numberparsing.h */ +#include + +namespace simdjson { +namespace haswell { +/// @private +namespace numberparsing { + +/** + * 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 +}; + +#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))) +#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)) +#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 + // 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; + + + // 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 = jsoncharutils::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. + + // 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. In very rare cases, even that + // will not suffice, though it is seemingly very hard to find such a scenario. + // + // 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 = jsoncharutils::full_multiplication(i, simdjson::internal::power_of_five_128[index + 1]); + firstproduct.low += secondproduct.high; + if(secondproduct.high > firstproduct.low) { firstproduct.high++; } + // At this point, we might need to add at most one to firstproduct, but this + // can only change the value of firstproduct.high if firstproduct.low is maximal. + if(simdjson_unlikely(firstproduct.low == 0xFFFFFFFFFFFFFFFF)) { + // This is very unlikely, but if so, we need to do much more work! + return false; + } + } + 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 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_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 doesn't 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 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 */ +template +error_code slow_float_parsing(simdjson_unused const uint8_t * src, W writer) { + double d; + if (parse_float_fallback(src, &d)) { + writer.append_double(d); + 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: This makes a *copy* of the writer and passes it to slow_float_parsing. This happens + // because slow_float_parsing is a non-inlined function. 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. + // This is what forces the skip_double, as well. + error_code error = slow_float_parsing(src, writer); + writer.skip_double(); + 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; +} + +// 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 +} + +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_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 INVALID_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 INVALID_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 doesn't 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 = start_digits-src > 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++; } + if ( p == src ) { return NUMBER_ERROR; } + if (jsoncharutils::is_structural_or_whitespace(*p)) { + // We have an integer. + // If the number is negative and valid, it must be a signed integer. + if(negative) { return number_type::signed_integer; } + // We want values larger or equal to 9223372036854775808 to be unsigned + // integers, and the other values to be signed integers. + int digit_count = int(p - src); + if(digit_count >= 19) { + const uint8_t * smaller_big_integer = reinterpret_cast("9223372036854775808"); + if((digit_count >= 20) || (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 = start_digits-src > 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 + +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; + default: SIMDJSON_UNREACHABLE(); + } + return out; +} + +} // namespace numberparsing +} // namespace haswell +} // namespace simdjson +/* end file include/simdjson/generic/numberparsing.h */ + +#endif // SIMDJSON_HASWELL_NUMBERPARSING_H +/* end file include/simdjson/haswell/numberparsing.h */ +/* begin file include/simdjson/haswell/end.h */ +SIMDJSON_UNTARGET_HASWELL +/* end file include/simdjson/haswell/end.h */ + +#endif // SIMDJSON_IMPLEMENTATION_HASWELL +#endif // SIMDJSON_HASWELL_COMMON_H +/* end file include/simdjson/haswell.h */ +/* begin file include/simdjson/ppc64.h */ +#ifndef SIMDJSON_PPC64_H +#define SIMDJSON_PPC64_H + + +#if SIMDJSON_IMPLEMENTATION_PPC64 + +namespace simdjson { +/** + * Implementation for ALTIVEC (PPC64). + */ +namespace ppc64 { +} // namespace ppc64 +} // namespace simdjson + +/* begin file include/simdjson/ppc64/implementation.h */ +#ifndef SIMDJSON_PPC64_IMPLEMENTATION_H +#define SIMDJSON_PPC64_IMPLEMENTATION_H + + +namespace simdjson { +namespace ppc64 { + +namespace { +using namespace simdjson; +using namespace simdjson::dom; +} // namespace + +/** + * @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; +}; + +} // namespace ppc64 +} // namespace simdjson + +#endif // SIMDJSON_PPC64_IMPLEMENTATION_H +/* end file include/simdjson/ppc64/implementation.h */ + +/* begin file include/simdjson/ppc64/begin.h */ +// redefining SIMDJSON_IMPLEMENTATION to "ppc64" +// #define SIMDJSON_IMPLEMENTATION ppc64 +/* end file include/simdjson/ppc64/begin.h */ + +// Declarations +/* begin file include/simdjson/generic/dom_parser_implementation.h */ + +namespace simdjson { +namespace ppc64 { + +// expectation: sizeof(open_container) = 64/8. +struct open_container { + uint32_t tape_index; // where, on the tape, does the scope ([,{) begins + uint32_t count; // how many elements in the scope +}; // struct open_container + +static_assert(sizeof(open_container) == 64/8, "Open container must be 64 bits"); + +class dom_parser_implementation final : public internal::dom_parser_implementation { +public: + /** Tape location of each open { or [ */ + std::unique_ptr open_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 ppc64 +} // namespace simdjson + +namespace simdjson { +namespace ppc64 { + +inline dom_parser_implementation::dom_parser_implementation() noexcept = default; +inline dom_parser_implementation::dom_parser_implementation(dom_parser_implementation &&other) noexcept = default; +inline dom_parser_implementation &dom_parser_implementation::operator=(dom_parser_implementation &&other) noexcept = default; + +// Leaving these here so they can be inlined if so desired +inline simdjson_warn_unused error_code dom_parser_implementation::set_capacity(size_t capacity) noexcept { + if(capacity > SIMDJSON_MAXSIZE_BYTES) { 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 ppc64 +} // namespace simdjson +/* end file include/simdjson/generic/dom_parser_implementation.h */ +/* begin file include/simdjson/ppc64/intrinsics.h */ +#ifndef SIMDJSON_PPC64_INTRINSICS_H +#define SIMDJSON_PPC64_INTRINSICS_H + + +// 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 include/simdjson/ppc64/intrinsics.h */ +/* begin file include/simdjson/ppc64/bitmanipulation.h */ +#ifndef SIMDJSON_PPC64_BITMANIPULATION_H +#define SIMDJSON_PPC64_BITMANIPULATION_H + +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 + 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 include/simdjson/ppc64/bitmanipulation.h */ +/* begin file include/simdjson/ppc64/bitmask.h */ +#ifndef SIMDJSON_PPC64_BITMASK_H +#define SIMDJSON_PPC64_BITMASK_H + +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 include/simdjson/ppc64/bitmask.h */ +/* begin file include/simdjson/ppc64/simd.h */ +#ifndef SIMDJSON_PPC64_SIMD_H +#define SIMDJSON_PPC64_SIMD_H + +#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; + } +}; + +// Forward-declared so they can be used by splat and friends. +template struct simd8; + +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]; + + 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 include/simdjson/ppc64/simd.h */ +/* begin file include/simdjson/generic/jsoncharutils.h */ + +namespace simdjson { +namespace ppc64 { +namespace { +namespace jsoncharutils { + +// return non-zero if not a structural or whitespace char +// zero otherwise +simdjson_inline uint32_t is_not_structural_or_whitespace(uint8_t c) { + return internal::structural_or_whitespace_negated[c]; +} + +simdjson_inline uint32_t is_structural_or_whitespace(uint8_t c) { + return internal::structural_or_whitespace[c]; +} + +// returns a value with the high 16 bits set if not valid +// otherwise returns the conversion of the 4 hex digits at src into the bottom +// 16 bits of the 32-bit return register +// +// see +// https://lemire.me/blog/2019/04/17/parsing-short-hexadecimal-strings-efficiently/ +static inline uint32_t hex_to_u32_nocheck( + const uint8_t *src) { // strictly speaking, static inline is a C-ism + uint32_t v1 = internal::digit_to_val32[630 + src[0]]; + uint32_t v2 = internal::digit_to_val32[420 + src[1]]; + uint32_t v3 = internal::digit_to_val32[210 + src[2]]; + uint32_t v4 = internal::digit_to_val32[0 + src[3]]; + return v1 | v2 | v3 | v4; +} + +// given a code point cp, writes to c +// the utf-8 code, outputting the length in +// bytes, if the length is zero, the code point +// is invalid +// +// This can possibly be made faster using pdep +// and clz and table lookups, but JSON documents +// have few escaped code points, and the following +// function looks cheap. +// +// Note: we assume that surrogates are treated separately +// +simdjson_inline size_t codepoint_to_utf8(uint32_t cp, uint8_t *c) { + if (cp <= 0x7F) { + c[0] = uint8_t(cp); + return 1; // ascii + } + if (cp <= 0x7FF) { + c[0] = uint8_t((cp >> 6) + 192); + c[1] = uint8_t((cp & 63) + 128); + return 2; // universal plane + // Surrogates are treated elsewhere... + //} //else if (0xd800 <= cp && cp <= 0xdfff) { + // return 0; // surrogates // could put assert here + } else if (cp <= 0xFFFF) { + c[0] = uint8_t((cp >> 12) + 224); + c[1] = uint8_t(((cp >> 6) & 63) + 128); + c[2] = uint8_t((cp & 63) + 128); + return 3; + } else if (cp <= 0x10FFFF) { // if you know you have a valid code point, this + // is not needed + c[0] = uint8_t((cp >> 18) + 240); + c[1] = uint8_t(((cp >> 12) & 63) + 128); + c[2] = uint8_t(((cp >> 6) & 63) + 128); + c[3] = uint8_t((cp & 63) + 128); + return 4; + } + // will return 0 when the code point was too large. + return 0; // bad r +} + +#if SIMDJSON_IS_32BITS // _umul128 for x86, arm +// this is a slow emulation routine for 32-bit +// +static simdjson_inline uint64_t __emulu(uint32_t x, uint32_t y) { + return x * (uint64_t)y; +} +static simdjson_inline uint64_t _umul128(uint64_t ab, uint64_t cd, uint64_t *hi) { + uint64_t ad = __emulu((uint32_t)(ab >> 32), (uint32_t)cd); + uint64_t bd = __emulu((uint32_t)ab, (uint32_t)cd); + uint64_t adbc = ad + __emulu((uint32_t)ab, (uint32_t)(cd >> 32)); + uint64_t adbc_carry = !!(adbc < ad); + uint64_t lo = bd + (adbc << 32); + *hi = __emulu((uint32_t)(ab >> 32), (uint32_t)(cd >> 32)) + (adbc >> 32) + + (adbc_carry << 32) + !!(lo < bd); + return lo; +} +#endif + +using internal::value128; + +simdjson_inline value128 full_multiplication(uint64_t value1, uint64_t value2) { + value128 answer; +#if SIMDJSON_REGULAR_VISUAL_STUDIO || SIMDJSON_IS_32BITS +#ifdef _M_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 // _M_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 jsoncharutils +} // unnamed namespace +} // namespace ppc64 +} // namespace simdjson +/* end file include/simdjson/generic/jsoncharutils.h */ +/* begin file include/simdjson/generic/atomparsing.h */ +namespace simdjson { +namespace ppc64 { +namespace { +/// @private +namespace atomparsing { + +// The string_to_uint32 is exclusively used to map literal strings to 32-bit values. +// We use memcpy instead of a pointer cast to avoid undefined behaviors since we cannot +// be certain that the character pointer will be properly aligned. +// You might think that using memcpy makes this function expensive, but you'd be wrong. +// All decent optimizing compilers (GCC, clang, Visual Studio) will compile string_to_uint32("false"); +// to the compile-time constant 1936482662. +simdjson_inline uint32_t string_to_uint32(const char* str) { uint32_t val; std::memcpy(&val, str, sizeof(uint32_t)); return val; } + + +// 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 ppc64 +} // namespace simdjson +/* end file include/simdjson/generic/atomparsing.h */ +/* begin file include/simdjson/ppc64/stringparsing.h */ +#ifndef SIMDJSON_PPC64_STRINGPARSING_H +#define SIMDJSON_PPC64_STRINGPARSING_H + + +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 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 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 + }; +} + +} // unnamed namespace +} // namespace ppc64 +} // namespace simdjson + +#endif // SIMDJSON_PPC64_STRINGPARSING_H +/* end file include/simdjson/ppc64/stringparsing.h */ +/* begin file include/simdjson/ppc64/numberparsing.h */ +#ifndef SIMDJSON_PPC64_NUMBERPARSING_H +#define SIMDJSON_PPC64_NUMBERPARSING_H + +#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); +} + +} // namespace numberparsing +} // namespace ppc64 +} // namespace simdjson + +#define SIMDJSON_SWAR_NUMBER_PARSING 1 + +/* begin file include/simdjson/generic/numberparsing.h */ +#include + +namespace simdjson { +namespace ppc64 { +/// @private +namespace numberparsing { + +/** + * 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 +}; + +#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))) +#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)) +#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 + // 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; + + + // 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 = jsoncharutils::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. + + // 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. In very rare cases, even that + // will not suffice, though it is seemingly very hard to find such a scenario. + // + // 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 = jsoncharutils::full_multiplication(i, simdjson::internal::power_of_five_128[index + 1]); + firstproduct.low += secondproduct.high; + if(secondproduct.high > firstproduct.low) { firstproduct.high++; } + // At this point, we might need to add at most one to firstproduct, but this + // can only change the value of firstproduct.high if firstproduct.low is maximal. + if(simdjson_unlikely(firstproduct.low == 0xFFFFFFFFFFFFFFFF)) { + // This is very unlikely, but if so, we need to do much more work! + return false; + } + } + 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 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_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 doesn't 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 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 */ +template +error_code slow_float_parsing(simdjson_unused const uint8_t * src, W writer) { + double d; + if (parse_float_fallback(src, &d)) { + writer.append_double(d); + 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: This makes a *copy* of the writer and passes it to slow_float_parsing. This happens + // because slow_float_parsing is a non-inlined function. 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. + // This is what forces the skip_double, as well. + error_code error = slow_float_parsing(src, writer); + writer.skip_double(); + 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; +} + +// 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 +} + +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_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 INVALID_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 INVALID_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 doesn't 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 = start_digits-src > 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++; } + if ( p == src ) { return NUMBER_ERROR; } + if (jsoncharutils::is_structural_or_whitespace(*p)) { + // We have an integer. + // If the number is negative and valid, it must be a signed integer. + if(negative) { return number_type::signed_integer; } + // We want values larger or equal to 9223372036854775808 to be unsigned + // integers, and the other values to be signed integers. + int digit_count = int(p - src); + if(digit_count >= 19) { + const uint8_t * smaller_big_integer = reinterpret_cast("9223372036854775808"); + if((digit_count >= 20) || (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 = start_digits-src > 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 + +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; + default: SIMDJSON_UNREACHABLE(); + } + return out; +} + +} // namespace numberparsing +} // namespace ppc64 +} // namespace simdjson +/* end file include/simdjson/generic/numberparsing.h */ + +#endif // SIMDJSON_PPC64_NUMBERPARSING_H +/* end file include/simdjson/ppc64/numberparsing.h */ +/* begin file include/simdjson/ppc64/end.h */ +/* end file include/simdjson/ppc64/end.h */ + +#endif // SIMDJSON_IMPLEMENTATION_PPC64 + +#endif // SIMDJSON_PPC64_H +/* end file include/simdjson/ppc64.h */ +/* begin file include/simdjson/westmere.h */ +#ifndef SIMDJSON_WESTMERE_H +#define SIMDJSON_WESTMERE_H + + +#if SIMDJSON_IMPLEMENTATION_WESTMERE + +#if SIMDJSON_CAN_ALWAYS_RUN_WESTMERE +#define SIMDJSON_TARGET_WESTMERE +#define SIMDJSON_UNTARGET_WESTMERE +#else +#define SIMDJSON_TARGET_WESTMERE SIMDJSON_TARGET_REGION("sse4.2,pclmul,popcnt") +#define SIMDJSON_UNTARGET_WESTMERE SIMDJSON_UNTARGET_REGION +#endif + +namespace simdjson { +/** + * Implementation for Westmere (Intel SSE4.2). + */ +namespace westmere { +} // namespace westmere +} // namespace simdjson + +// +// These two need to be included outside SIMDJSON_TARGET_WESTMERE +// +/* begin file include/simdjson/westmere/implementation.h */ +#ifndef SIMDJSON_WESTMERE_IMPLEMENTATION_H +#define SIMDJSON_WESTMERE_IMPLEMENTATION_H + + +// The constructor may be executed on any host, so we take care not to use SIMDJSON_TARGET_WESTMERE +namespace simdjson { +namespace westmere { + +namespace { +using namespace simdjson; +using namespace simdjson::dom; +} + +/** + * @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 simdjson + +#endif // SIMDJSON_WESTMERE_IMPLEMENTATION_H +/* end file include/simdjson/westmere/implementation.h */ +/* begin file include/simdjson/westmere/intrinsics.h */ +#ifndef SIMDJSON_WESTMERE_INTRINSICS_H +#define SIMDJSON_WESTMERE_INTRINSICS_H + +#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 include/simdjson/westmere/intrinsics.h */ + +// +// The rest need to be inside the region +// +/* begin file include/simdjson/westmere/begin.h */ +// redefining SIMDJSON_IMPLEMENTATION to "westmere" +// #define SIMDJSON_IMPLEMENTATION westmere +SIMDJSON_TARGET_WESTMERE +/* end file include/simdjson/westmere/begin.h */ + +// Declarations +/* begin file include/simdjson/generic/dom_parser_implementation.h */ + +namespace simdjson { +namespace westmere { + +// expectation: sizeof(open_container) = 64/8. +struct open_container { + uint32_t tape_index; // where, on the tape, does the scope ([,{) begins + uint32_t count; // how many elements in the scope +}; // struct open_container + +static_assert(sizeof(open_container) == 64/8, "Open container must be 64 bits"); + +class dom_parser_implementation final : public internal::dom_parser_implementation { +public: + /** Tape location of each open { or [ */ + std::unique_ptr open_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 westmere +} // namespace simdjson + +namespace simdjson { +namespace westmere { + +inline dom_parser_implementation::dom_parser_implementation() noexcept = default; +inline dom_parser_implementation::dom_parser_implementation(dom_parser_implementation &&other) noexcept = default; +inline dom_parser_implementation &dom_parser_implementation::operator=(dom_parser_implementation &&other) noexcept = default; + +// Leaving these here so they can be inlined if so desired +inline simdjson_warn_unused error_code dom_parser_implementation::set_capacity(size_t capacity) noexcept { + if(capacity > SIMDJSON_MAXSIZE_BYTES) { 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 westmere +} // namespace simdjson +/* end file include/simdjson/generic/dom_parser_implementation.h */ +/* begin file include/simdjson/westmere/bitmanipulation.h */ +#ifndef SIMDJSON_WESTMERE_BITMANIPULATION_H +#define SIMDJSON_WESTMERE_BITMANIPULATION_H + +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 + 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 include/simdjson/westmere/bitmanipulation.h */ +/* begin file include/simdjson/westmere/bitmask.h */ +#ifndef SIMDJSON_WESTMERE_BITMASK_H +#define SIMDJSON_WESTMERE_BITMASK_H + +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 include/simdjson/westmere/bitmask.h */ +/* begin file include/simdjson/westmere/simd.h */ +#ifndef SIMDJSON_WESTMERE_SIMD_H +#define SIMDJSON_WESTMERE_SIMD_H + + +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; } + }; + + // Forward-declared so they can be used by splat and friends. + template + struct simd8; + + 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]; + + 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 include/simdjson/westmere/simd.h */ +/* begin file include/simdjson/generic/jsoncharutils.h */ + +namespace simdjson { +namespace westmere { +namespace { +namespace jsoncharutils { + +// return non-zero if not a structural or whitespace char +// zero otherwise +simdjson_inline uint32_t is_not_structural_or_whitespace(uint8_t c) { + return internal::structural_or_whitespace_negated[c]; +} + +simdjson_inline uint32_t is_structural_or_whitespace(uint8_t c) { + return internal::structural_or_whitespace[c]; +} + +// returns a value with the high 16 bits set if not valid +// otherwise returns the conversion of the 4 hex digits at src into the bottom +// 16 bits of the 32-bit return register +// +// see +// https://lemire.me/blog/2019/04/17/parsing-short-hexadecimal-strings-efficiently/ +static inline uint32_t hex_to_u32_nocheck( + const uint8_t *src) { // strictly speaking, static inline is a C-ism + uint32_t v1 = internal::digit_to_val32[630 + src[0]]; + uint32_t v2 = internal::digit_to_val32[420 + src[1]]; + uint32_t v3 = internal::digit_to_val32[210 + src[2]]; + uint32_t v4 = internal::digit_to_val32[0 + src[3]]; + return v1 | v2 | v3 | v4; +} + +// given a code point cp, writes to c +// the utf-8 code, outputting the length in +// bytes, if the length is zero, the code point +// is invalid +// +// This can possibly be made faster using pdep +// and clz and table lookups, but JSON documents +// have few escaped code points, and the following +// function looks cheap. +// +// Note: we assume that surrogates are treated separately +// +simdjson_inline size_t codepoint_to_utf8(uint32_t cp, uint8_t *c) { + if (cp <= 0x7F) { + c[0] = uint8_t(cp); + return 1; // ascii + } + if (cp <= 0x7FF) { + c[0] = uint8_t((cp >> 6) + 192); + c[1] = uint8_t((cp & 63) + 128); + return 2; // universal plane + // Surrogates are treated elsewhere... + //} //else if (0xd800 <= cp && cp <= 0xdfff) { + // return 0; // surrogates // could put assert here + } else if (cp <= 0xFFFF) { + c[0] = uint8_t((cp >> 12) + 224); + c[1] = uint8_t(((cp >> 6) & 63) + 128); + c[2] = uint8_t((cp & 63) + 128); + return 3; + } else if (cp <= 0x10FFFF) { // if you know you have a valid code point, this + // is not needed + c[0] = uint8_t((cp >> 18) + 240); + c[1] = uint8_t(((cp >> 12) & 63) + 128); + c[2] = uint8_t(((cp >> 6) & 63) + 128); + c[3] = uint8_t((cp & 63) + 128); + return 4; + } + // will return 0 when the code point was too large. + return 0; // bad r +} + +#if SIMDJSON_IS_32BITS // _umul128 for x86, arm +// this is a slow emulation routine for 32-bit +// +static simdjson_inline uint64_t __emulu(uint32_t x, uint32_t y) { + return x * (uint64_t)y; +} +static simdjson_inline uint64_t _umul128(uint64_t ab, uint64_t cd, uint64_t *hi) { + uint64_t ad = __emulu((uint32_t)(ab >> 32), (uint32_t)cd); + uint64_t bd = __emulu((uint32_t)ab, (uint32_t)cd); + uint64_t adbc = ad + __emulu((uint32_t)ab, (uint32_t)(cd >> 32)); + uint64_t adbc_carry = !!(adbc < ad); + uint64_t lo = bd + (adbc << 32); + *hi = __emulu((uint32_t)(ab >> 32), (uint32_t)(cd >> 32)) + (adbc >> 32) + + (adbc_carry << 32) + !!(lo < bd); + return lo; +} +#endif + +using internal::value128; + +simdjson_inline value128 full_multiplication(uint64_t value1, uint64_t value2) { + value128 answer; +#if SIMDJSON_REGULAR_VISUAL_STUDIO || SIMDJSON_IS_32BITS +#ifdef _M_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 // _M_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 jsoncharutils +} // unnamed namespace +} // namespace westmere +} // namespace simdjson +/* end file include/simdjson/generic/jsoncharutils.h */ +/* begin file include/simdjson/generic/atomparsing.h */ +namespace simdjson { +namespace westmere { +namespace { +/// @private +namespace atomparsing { + +// The string_to_uint32 is exclusively used to map literal strings to 32-bit values. +// We use memcpy instead of a pointer cast to avoid undefined behaviors since we cannot +// be certain that the character pointer will be properly aligned. +// You might think that using memcpy makes this function expensive, but you'd be wrong. +// All decent optimizing compilers (GCC, clang, Visual Studio) will compile string_to_uint32("false"); +// to the compile-time constant 1936482662. +simdjson_inline uint32_t string_to_uint32(const char* str) { uint32_t val; std::memcpy(&val, str, sizeof(uint32_t)); return val; } + + +// 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 westmere +} // namespace simdjson +/* end file include/simdjson/generic/atomparsing.h */ +/* begin file include/simdjson/westmere/stringparsing.h */ +#ifndef SIMDJSON_WESTMERE_STRINGPARSING_H +#define SIMDJSON_WESTMERE_STRINGPARSING_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 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 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 +} // namespace simdjson + +#endif // SIMDJSON_WESTMERE_STRINGPARSING_H +/* end file include/simdjson/westmere/stringparsing.h */ +/* begin file include/simdjson/westmere/numberparsing.h */ +#ifndef SIMDJSON_WESTMERE_NUMBERPARSING_H +#define SIMDJSON_WESTMERE_NUMBERPARSING_H + +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 +} + +} // namespace numberparsing +} // namespace westmere +} // namespace simdjson + +#define SIMDJSON_SWAR_NUMBER_PARSING 1 + +/* begin file include/simdjson/generic/numberparsing.h */ +#include + +namespace simdjson { +namespace westmere { +/// @private +namespace numberparsing { + +/** + * 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 +}; + +#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))) +#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)) +#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 + // 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; + + + // 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 = jsoncharutils::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. + + // 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. In very rare cases, even that + // will not suffice, though it is seemingly very hard to find such a scenario. + // + // 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 = jsoncharutils::full_multiplication(i, simdjson::internal::power_of_five_128[index + 1]); + firstproduct.low += secondproduct.high; + if(secondproduct.high > firstproduct.low) { firstproduct.high++; } + // At this point, we might need to add at most one to firstproduct, but this + // can only change the value of firstproduct.high if firstproduct.low is maximal. + if(simdjson_unlikely(firstproduct.low == 0xFFFFFFFFFFFFFFFF)) { + // This is very unlikely, but if so, we need to do much more work! + return false; + } + } + 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 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_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 doesn't 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 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 */ +template +error_code slow_float_parsing(simdjson_unused const uint8_t * src, W writer) { + double d; + if (parse_float_fallback(src, &d)) { + writer.append_double(d); + 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: This makes a *copy* of the writer and passes it to slow_float_parsing. This happens + // because slow_float_parsing is a non-inlined function. 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. + // This is what forces the skip_double, as well. + error_code error = slow_float_parsing(src, writer); + writer.skip_double(); + 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; +} + +// 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 +} + +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_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 INVALID_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 INVALID_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 doesn't 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 = start_digits-src > 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++; } + if ( p == src ) { return NUMBER_ERROR; } + if (jsoncharutils::is_structural_or_whitespace(*p)) { + // We have an integer. + // If the number is negative and valid, it must be a signed integer. + if(negative) { return number_type::signed_integer; } + // We want values larger or equal to 9223372036854775808 to be unsigned + // integers, and the other values to be signed integers. + int digit_count = int(p - src); + if(digit_count >= 19) { + const uint8_t * smaller_big_integer = reinterpret_cast("9223372036854775808"); + if((digit_count >= 20) || (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 = start_digits-src > 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 + +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; + default: SIMDJSON_UNREACHABLE(); + } + return out; +} + +} // namespace numberparsing +} // namespace westmere +} // namespace simdjson +/* end file include/simdjson/generic/numberparsing.h */ + +#endif // SIMDJSON_WESTMERE_NUMBERPARSING_H +/* end file include/simdjson/westmere/numberparsing.h */ +/* begin file include/simdjson/westmere/end.h */ +SIMDJSON_UNTARGET_WESTMERE +/* end file include/simdjson/westmere/end.h */ + +#endif // SIMDJSON_IMPLEMENTATION_WESTMERE +#endif // SIMDJSON_WESTMERE_COMMON_H +/* end file include/simdjson/westmere.h */ + +// Builtin implementation + +SIMDJSON_POP_DISABLE_WARNINGS + +#endif // SIMDJSON_IMPLEMENTATIONS_H +/* end file include/simdjson/implementations.h */ + +// Determine the best builtin implementation +#ifndef SIMDJSON_BUILTIN_IMPLEMENTATION +#if SIMDJSON_CAN_ALWAYS_RUN_ICELAKE +#define SIMDJSON_BUILTIN_IMPLEMENTATION icelake +#elif SIMDJSON_CAN_ALWAYS_RUN_HASWELL +#define SIMDJSON_BUILTIN_IMPLEMENTATION haswell +#elif SIMDJSON_CAN_ALWAYS_RUN_WESTMERE +#define SIMDJSON_BUILTIN_IMPLEMENTATION westmere +#elif SIMDJSON_CAN_ALWAYS_RUN_ARM64 +#define SIMDJSON_BUILTIN_IMPLEMENTATION arm64 +#elif SIMDJSON_CAN_ALWAYS_RUN_PPC64 +#define SIMDJSON_BUILTIN_IMPLEMENTATION ppc64 +#elif SIMDJSON_CAN_ALWAYS_RUN_FALLBACK +#define SIMDJSON_BUILTIN_IMPLEMENTATION fallback +#else +#error "All possible implementations (including fallback) have been disabled! simdjson will not run." +#endif +#endif // SIMDJSON_BUILTIN_IMPLEMENTATION + +// redefining SIMDJSON_IMPLEMENTATION to "SIMDJSON_BUILTIN_IMPLEMENTATION" +// #define SIMDJSON_IMPLEMENTATION SIMDJSON_BUILTIN_IMPLEMENTATION + +// ondemand is only compiled as part of the builtin implementation at present + +// Interface declarations +/* begin file include/simdjson/generic/implementation_simdjson_result_base.h */ +namespace simdjson { +namespace SIMDJSON_BUILTIN_IMPLEMENTATION { + +// 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_inline error_code get(T &value) && noexcept; + + /** + * The error. + */ + simdjson_inline error_code error() const noexcept; + +#if SIMDJSON_EXCEPTIONS + + /** + * Get the result value. + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T& value() & noexcept(false); + + /** + * Take the result value (move it). + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T&& value() && noexcept(false); + + /** + * Take the result value (move it). + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T&& take_value() && noexcept(false); + + /** + * Cast to the value (will throw on error). + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline operator T&&() && noexcept(false); + + +#endif // SIMDJSON_EXCEPTIONS + + /** + * Get the result value. This function is safe if and only + * the error() method returns a value that evaluates to false. + */ + simdjson_inline const T& value_unsafe() const& noexcept; + /** + * 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 + +} // namespace SIMDJSON_BUILTIN_IMPLEMENTATION +} // namespace simdjson +/* end file include/simdjson/generic/implementation_simdjson_result_base.h */ +/* begin file include/simdjson/generic/ondemand.h */ +namespace simdjson { +namespace SIMDJSON_BUILTIN_IMPLEMENTATION { +/** + * 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::SIMDJSON_BUILTIN_IMPLEMENTATION::numberparsing::number_type */ +using number_type = simdjson::SIMDJSON_BUILTIN_IMPLEMENTATION::numberparsing::number_type; + +} // namespace ondemand +} // namespace SIMDJSON_BUILTIN_IMPLEMENTATION +} // namespace simdjson + +/* begin file include/simdjson/generic/ondemand/json_type.h */ +namespace simdjson { +namespace SIMDJSON_BUILTIN_IMPLEMENTATION { +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) +}; + +class value_iterator; + +/** + * 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 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::slow_float_parsing(simdjson_unused const uint8_t * src, W writer); + 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 SIMDJSON_BUILTIN_IMPLEMENTATION +} // namespace simdjson + +namespace simdjson { + +template<> +struct simdjson_result : public SIMDJSON_BUILTIN_IMPLEMENTATION::implementation_simdjson_result_base { +public: + simdjson_inline simdjson_result(SIMDJSON_BUILTIN_IMPLEMENTATION::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 +/* end file include/simdjson/generic/ondemand/json_type.h */ +/* begin file include/simdjson/generic/ondemand/token_position.h */ +namespace simdjson { +namespace SIMDJSON_BUILTIN_IMPLEMENTATION { +namespace ondemand { + +/** @private Position in the JSON buffer indexes */ +using token_position = const uint32_t *; + +} // namespace ondemand +} // namespace SIMDJSON_BUILTIN_IMPLEMENTATION +} // namespace simdjson +/* end file include/simdjson/generic/ondemand/token_position.h */ +/* begin file include/simdjson/generic/ondemand/logger.h */ +namespace simdjson { +namespace SIMDJSON_BUILTIN_IMPLEMENTATION { +namespace ondemand { + +class json_iterator; +class value_iterator; + +// 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 { + +#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; +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) noexcept; +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) 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 SIMDJSON_BUILTIN_IMPLEMENTATION +} // namespace simdjson +/* end file include/simdjson/generic/ondemand/logger.h */ +/* begin file include/simdjson/generic/ondemand/raw_json_string.h */ + +namespace simdjson { +namespace SIMDJSON_BUILTIN_IMPLEMENTATION { +namespace ondemand { + +class object; +class parser; +class json_iterator; + +/** + * 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 SIMDJSON_BUILTIN_IMPLEMENTATION +} // namespace simdjson + +namespace simdjson { + +template<> +struct simdjson_result : public SIMDJSON_BUILTIN_IMPLEMENTATION::implementation_simdjson_result_base { +public: + simdjson_inline simdjson_result(SIMDJSON_BUILTIN_IMPLEMENTATION::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(SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::json_iterator &iter, bool allow_replacement) const noexcept; + simdjson_inline simdjson_warn_unused simdjson_result unescape_wobbly(SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::json_iterator &iter) const noexcept; +}; + +} // namespace simdjson +/* end file include/simdjson/generic/ondemand/raw_json_string.h */ +/* begin file include/simdjson/generic/ondemand/token_iterator.h */ +namespace simdjson { +namespace SIMDJSON_BUILTIN_IMPLEMENTATION { +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 isn't 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; + + /** + * 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; + 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) noexcept; + 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) noexcept; +}; + +} // namespace ondemand +} // namespace SIMDJSON_BUILTIN_IMPLEMENTATION +} // namespace simdjson + +namespace simdjson { + +template<> +struct simdjson_result : public SIMDJSON_BUILTIN_IMPLEMENTATION::implementation_simdjson_result_base { +public: + simdjson_inline simdjson_result(SIMDJSON_BUILTIN_IMPLEMENTATION::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 +/* end file include/simdjson/generic/ondemand/token_iterator.h */ +/* begin file include/simdjson/generic/ondemand/json_iterator.h */ +namespace simdjson { +namespace SIMDJSON_BUILTIN_IMPLEMENTATION { +namespace ondemand { + +class document; +class document_stream; +class object; +class array; +class value; +class raw_json_string; +class parser; + +/** + * 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 isn't 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 isn't 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 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 isn't 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; + /// 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 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) noexcept; + 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) noexcept; +}; // json_iterator + +} // namespace ondemand +} // namespace SIMDJSON_BUILTIN_IMPLEMENTATION +} // namespace simdjson + +namespace simdjson { + +template<> +struct simdjson_result : public SIMDJSON_BUILTIN_IMPLEMENTATION::implementation_simdjson_result_base { +public: + simdjson_inline simdjson_result(SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::json_iterator &&value) noexcept; ///< @private + simdjson_inline simdjson_result(error_code error) noexcept; ///< @private + + simdjson_inline simdjson_result() noexcept = default; +}; + +} // namespace simdjson +/* end file include/simdjson/generic/ondemand/json_iterator.h */ +/* begin file include/simdjson/generic/ondemand/value_iterator.h */ +namespace simdjson { +namespace SIMDJSON_BUILTIN_IMPLEMENTATION { +namespace ondemand { + +class document; +class object; +class array; +class value; +class raw_json_string; +class parser; + +/** + * 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). + */ + 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; + 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; + 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; + + /** + * 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; +}; // value_iterator + +} // namespace ondemand +} // namespace SIMDJSON_BUILTIN_IMPLEMENTATION +} // namespace simdjson + +namespace simdjson { + +template<> +struct simdjson_result : public SIMDJSON_BUILTIN_IMPLEMENTATION::implementation_simdjson_result_base { +public: + simdjson_inline simdjson_result(SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::value_iterator &&value) noexcept; ///< @private + simdjson_inline simdjson_result(error_code error) noexcept; ///< @private + simdjson_inline simdjson_result() noexcept = default; +}; + +} // namespace simdjson +/* end file include/simdjson/generic/ondemand/value_iterator.h */ +/* begin file include/simdjson/generic/ondemand/array_iterator.h */ + +namespace simdjson { +namespace SIMDJSON_BUILTIN_IMPLEMENTATION { +namespace ondemand { + +class array; +class value; +class document; + +/** + * 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 SIMDJSON_BUILTIN_IMPLEMENTATION +} // namespace simdjson + +namespace simdjson { + +template<> +struct simdjson_result : public SIMDJSON_BUILTIN_IMPLEMENTATION::implementation_simdjson_result_base { +public: + simdjson_inline simdjson_result(SIMDJSON_BUILTIN_IMPLEMENTATION::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 +/* end file include/simdjson/generic/ondemand/array_iterator.h */ +/* begin file include/simdjson/generic/ondemand/object_iterator.h */ + +namespace simdjson { +namespace SIMDJSON_BUILTIN_IMPLEMENTATION { +namespace ondemand { + +class field; + +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 SIMDJSON_BUILTIN_IMPLEMENTATION +} // namespace simdjson + +namespace simdjson { + +template<> +struct simdjson_result : public SIMDJSON_BUILTIN_IMPLEMENTATION::implementation_simdjson_result_base { +public: + simdjson_inline simdjson_result(SIMDJSON_BUILTIN_IMPLEMENTATION::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 +/* end file include/simdjson/generic/ondemand/object_iterator.h */ +/* begin file include/simdjson/generic/ondemand/array.h */ + +namespace simdjson { +namespace SIMDJSON_BUILTIN_IMPLEMENTATION { +namespace ondemand { + +class value; +class document; + +/** + * 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; + /** + * 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 SIMDJSON_BUILTIN_IMPLEMENTATION +} // namespace simdjson + +namespace simdjson { + +template<> +struct simdjson_result : public SIMDJSON_BUILTIN_IMPLEMENTATION::implementation_simdjson_result_base { +public: + simdjson_inline simdjson_result(SIMDJSON_BUILTIN_IMPLEMENTATION::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 raw_json() noexcept; + +}; + +} // namespace simdjson +/* end file include/simdjson/generic/ondemand/array.h */ +/* begin file include/simdjson/generic/ondemand/document.h */ + +namespace simdjson { +namespace SIMDJSON_BUILTIN_IMPLEMENTATION { +namespace ondemand { + +class parser; +class array; +class object; +class value; +class raw_json_string; +class array_iterator; +class document_stream; + +/** + * 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; + /** + * 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. + * + * @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 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."); + } + /** @overload template simdjson_result get() & noexcept */ + template simdjson_inline simdjson_result get() && 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."); + } + + /** + * 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_inline error_code get(T &out) && noexcept; + +#if SIMDJSON_EXCEPTIONS + /** + * 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. + * + * @returns A value value. + * @exception if a JSON value cannot be found + */ + 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 wasn't there when they aren't). + * + * 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 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 + * get_number_type() is number_type::signed_integer if we have an + * integer that is less 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 + * + * 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; + /** + * 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; + 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 raw_json() noexcept; + simdjson_inline operator document&() const noexcept; + +#if SIMDJSON_EXCEPTIONS + 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 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; +private: + document *doc{nullptr}; +}; +} // namespace ondemand +} // namespace SIMDJSON_BUILTIN_IMPLEMENTATION +} // namespace simdjson + +namespace simdjson { + +template<> +struct simdjson_result : public SIMDJSON_BUILTIN_IMPLEMENTATION::implementation_simdjson_result_base { +public: + simdjson_inline simdjson_result(SIMDJSON_BUILTIN_IMPLEMENTATION::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; + 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 + simdjson_inline operator SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::array() & noexcept(false); + simdjson_inline operator SIMDJSON_BUILTIN_IMPLEMENTATION::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 SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::raw_json_string() noexcept(false); + simdjson_inline operator bool() noexcept(false); + simdjson_inline operator SIMDJSON_BUILTIN_IMPLEMENTATION::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 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; +}; + + +} // namespace simdjson + + + +namespace simdjson { + +template<> +struct simdjson_result : public SIMDJSON_BUILTIN_IMPLEMENTATION::implementation_simdjson_result_base { +public: + simdjson_inline simdjson_result(SIMDJSON_BUILTIN_IMPLEMENTATION::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; + 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 + simdjson_inline operator SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::array() & noexcept(false); + simdjson_inline operator SIMDJSON_BUILTIN_IMPLEMENTATION::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 SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::raw_json_string() noexcept(false); + simdjson_inline operator bool() noexcept(false); + simdjson_inline operator SIMDJSON_BUILTIN_IMPLEMENTATION::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 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; +}; + + +} // namespace simdjson +/* end file include/simdjson/generic/ondemand/document.h */ +/* begin file include/simdjson/generic/ondemand/value.h */ + +namespace simdjson { +namespace SIMDJSON_BUILTIN_IMPLEMENTATION { +namespace ondemand { + +class array; +class document; +class field; +class object; +class raw_json_string; + +/** + * 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 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."); + } + + /** + * 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; + + + /** + * 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 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 wasn't there when they aren't). + * + * @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 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 + * 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. + * + * 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 + */ + simdjson_inline std::string_view raw_json_token() 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; + +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; +}; + +} // namespace ondemand +} // namespace SIMDJSON_BUILTIN_IMPLEMENTATION +} // namespace simdjson + +namespace simdjson { + +template<> +struct simdjson_result : public SIMDJSON_BUILTIN_IMPLEMENTATION::implementation_simdjson_result_base { +public: + simdjson_inline simdjson_result(SIMDJSON_BUILTIN_IMPLEMENTATION::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; + 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 + simdjson_inline operator SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::array() noexcept(false); + simdjson_inline operator SIMDJSON_BUILTIN_IMPLEMENTATION::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 SIMDJSON_BUILTIN_IMPLEMENTATION::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 wasn't there when they aren't). + * + * @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_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; + + /** @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; +}; + +} // namespace simdjson +/* end file include/simdjson/generic/ondemand/value.h */ +/* begin file include/simdjson/generic/ondemand/field.h */ + +namespace simdjson { +namespace SIMDJSON_BUILTIN_IMPLEMENTATION { +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) noexcept; + /** + * Get the key as a raw_json_string. Can be used for direct comparison with + * an unescaped C string: e.g., key() == "test". + */ + simdjson_inline raw_json_string 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 SIMDJSON_BUILTIN_IMPLEMENTATION +} // namespace simdjson + +namespace simdjson { + +template<> +struct simdjson_result : public SIMDJSON_BUILTIN_IMPLEMENTATION::implementation_simdjson_result_base { +public: + simdjson_inline simdjson_result(SIMDJSON_BUILTIN_IMPLEMENTATION::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; + simdjson_inline simdjson_result key() noexcept; + simdjson_inline simdjson_result value() noexcept; +}; + +} // namespace simdjson +/* end file include/simdjson/generic/ondemand/field.h */ +/* begin file include/simdjson/generic/ondemand/object.h */ + +namespace simdjson { +namespace SIMDJSON_BUILTIN_IMPLEMENTATION { +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. + * + * @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 wasn't there when they aren't). + * + * 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. + * + * @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; + + /** + * 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 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 SIMDJSON_BUILTIN_IMPLEMENTATION +} // namespace simdjson + +namespace simdjson { + +template<> +struct simdjson_result : public SIMDJSON_BUILTIN_IMPLEMENTATION::implementation_simdjson_result_base { +public: + simdjson_inline simdjson_result(SIMDJSON_BUILTIN_IMPLEMENTATION::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; + 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 +/* end file include/simdjson/generic/ondemand/object.h */ +/* begin file include/simdjson/generic/ondemand/parser.h */ + +namespace simdjson { +namespace SIMDJSON_BUILTIN_IMPLEMENTATION { +namespace ondemand { + +class array; +class object; +class value; +class raw_json_string; +class document_stream; + +/** + * 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. + * + * ### 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; + /** @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(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). + * + * 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_inline size_t capacity() const noexcept; + /** The maximum capacity of this parser (the largest document it is allowed to process). */ + 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_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; + +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 SIMDJSON_BUILTIN_IMPLEMENTATION +} // namespace simdjson + +namespace simdjson { + +template<> +struct simdjson_result : public SIMDJSON_BUILTIN_IMPLEMENTATION::implementation_simdjson_result_base { +public: + simdjson_inline simdjson_result(SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::parser &&value) noexcept; ///< @private + simdjson_inline simdjson_result(error_code error) noexcept; ///< @private + simdjson_inline simdjson_result() noexcept = default; +}; + +} // namespace simdjson +/* end file include/simdjson/generic/ondemand/parser.h */ +/* begin file include/simdjson/generic/ondemand/document_stream.h */ +#ifdef SIMDJSON_THREADS_ENABLED +#include +#include +#include +#endif + +namespace simdjson { +namespace SIMDJSON_BUILTIN_IMPLEMENTATION { +namespace ondemand { + +class parser; +class json_iterator; +class document; + +#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 = value_type; + + using difference_type = std::ptrdiff_t; + + using iterator_category = std::input_iterator_tag; + + /** + * Default constructor. + */ + simdjson_inline iterator() noexcept; + /** + * Get the current document (or error). + */ + simdjson_inline simdjson_result 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 caller is responsible for omitting it, UTF-8 BOM are + * discouraged. + * + * 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 internal::simdjson_result_base; +}; // document_stream + +} // namespace ondemand +} // namespace SIMDJSON_BUILTIN_IMPLEMENTATION +} // namespace simdjson + +namespace simdjson { +template<> +struct simdjson_result : public SIMDJSON_BUILTIN_IMPLEMENTATION::implementation_simdjson_result_base { +public: + simdjson_inline simdjson_result(SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::document_stream &&value) noexcept; ///< @private + simdjson_inline simdjson_result(error_code error) noexcept; ///< @private + simdjson_inline simdjson_result() noexcept = default; +}; + +} // namespace simdjson +/* end file include/simdjson/generic/ondemand/document_stream.h */ +/* begin file include/simdjson/generic/ondemand/serialization.h */ + +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(SIMDJSON_BUILTIN_IMPLEMENTATION::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(SIMDJSON_BUILTIN_IMPLEMENTATION::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(SIMDJSON_BUILTIN_IMPLEMENTATION::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(SIMDJSON_BUILTIN_IMPLEMENTATION::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 + +/** + * 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 SIMDJSON_BUILTIN_IMPLEMENTATION { 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::SIMDJSON_BUILTIN_IMPLEMENTATION::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::SIMDJSON_BUILTIN_IMPLEMENTATION::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::SIMDJSON_BUILTIN_IMPLEMENTATION::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::SIMDJSON_BUILTIN_IMPLEMENTATION::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::SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::object value); +#if SIMDJSON_EXCEPTIONS +inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result x); +#endif +}}} // namespace simdjson::SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand +/* end file include/simdjson/generic/ondemand/serialization.h */ +/* end file include/simdjson/generic/ondemand.h */ + +// Inline definitions +/* begin file include/simdjson/generic/implementation_simdjson_result_base-inl.h */ +namespace simdjson { +namespace SIMDJSON_BUILTIN_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_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; +} + +#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::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 SIMDJSON_BUILTIN_IMPLEMENTATION +} // namespace simdjson +/* end file include/simdjson/generic/implementation_simdjson_result_base-inl.h */ +/* begin file include/simdjson/generic/ondemand-inl.h */ +/* begin file include/simdjson/generic/ondemand/json_type-inl.h */ +namespace simdjson { +namespace SIMDJSON_BUILTIN_IMPLEMENTATION { +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 SIMDJSON_BUILTIN_IMPLEMENTATION +} // namespace simdjson + +namespace simdjson { + +simdjson_inline simdjson_result::simdjson_result(SIMDJSON_BUILTIN_IMPLEMENTATION::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 +/* end file include/simdjson/generic/ondemand/json_type-inl.h */ +/* begin file include/simdjson/generic/ondemand/logger-inl.h */ +namespace simdjson { +namespace SIMDJSON_BUILTIN_IMPLEMENTATION { +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 ' '; + } +} + +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); +} + +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); +} +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); +} + +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); + 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); + 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); +} + +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); +} +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); +} + +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) { + // 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); + } +} + +inline void log_line(const json_iterator &iter, const char *title_prefix, const char *title, std::string_view detail, int delta, int depth_delta) noexcept { + log_line(iter, iter.position()+delta, depth_t(iter.depth()+depth_delta), title_prefix, title, detail); +} +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) noexcept { + if (LOG_ENABLED) { + const int indent = depth*2; + const auto buf = iter.token.buf; + printf("| %*s%s%-*s ", + indent, "", + title_prefix, + LOG_EVENT_LEN - indent - int(strlen(title_prefix)), title + ); + { + // 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(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 SIMDJSON_BUILTIN_IMPLEMENTATION +} // namespace simdjson + +namespace simdjson { + +simdjson_inline simdjson_result::simdjson_result(SIMDJSON_BUILTIN_IMPLEMENTATION::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(SIMDJSON_BUILTIN_IMPLEMENTATION::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(SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::json_iterator &iter) const noexcept { + if (error()) { return error(); } + return first.unescape_wobbly(iter); +} +} // namespace simdjson +/* end file include/simdjson/generic/ondemand/raw_json_string-inl.h */ +/* begin file include/simdjson/generic/ondemand/token_iterator-inl.h */ +namespace simdjson { +namespace SIMDJSON_BUILTIN_IMPLEMENTATION { +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 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 SIMDJSON_BUILTIN_IMPLEMENTATION +} // namespace simdjson + +namespace simdjson { + +simdjson_inline simdjson_result::simdjson_result(SIMDJSON_BUILTIN_IMPLEMENTATION::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 +/* end file include/simdjson/generic/ondemand/token_iterator-inl.h */ +/* begin file include/simdjson/generic/ondemand/json_iterator-inl.h */ +namespace simdjson { +namespace SIMDJSON_BUILTIN_IMPLEMENTATION { +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 +} + +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(0); +} + +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 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 { + return parser->unescape(in, _string_buf_loc, allow_replacement); +} + +simdjson_inline simdjson_result json_iterator::unescape_wobbly(raw_json_string in) noexcept { + return parser->unescape_wobbly(in, _string_buf_loc); +} + +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 SIMDJSON_BUILTIN_IMPLEMENTATION +} // namespace simdjson + +namespace simdjson { + +simdjson_inline simdjson_result::simdjson_result(SIMDJSON_BUILTIN_IMPLEMENTATION::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 +/* end file include/simdjson/generic/ondemand/json_iterator-inl.h */ +/* begin file include/simdjson/generic/ondemand/value_iterator-inl.h */ +namespace simdjson { +namespace SIMDJSON_BUILTIN_IMPLEMENTATION { +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 isn't 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 isn't 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); +} +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_start_length(); + auto json = peek_root_scalar("is_root_integer"); + uint8_t tmpbuf[20+1]; // <20 digits> is the longest possible unsigned integer + 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_start_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]; + 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 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_start_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]; + 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; + } + 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); +} +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_start_length(); + auto json = peek_root_scalar("uint64"); + uint8_t tmpbuf[20+1]; // <20 digits> is the longest possible unsigned integer + 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_start_length(); + auto json = peek_root_scalar("uint64"); + uint8_t tmpbuf[20+1]; // <20 digits> is the longest possible unsigned integer + 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_start_length(); + auto json = peek_root_scalar("int64"); + uint8_t tmpbuf[20+1]; // -<19 digits> is the longest possible integer + 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_start_length(); + auto json = peek_root_scalar("int64"); + uint8_t tmpbuf[20+1]; // -<19 digits> is the longest possible integer + 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_start_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]; + 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_start_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]; + 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_start_length(); + auto json = peek_root_scalar("bool"); + uint8_t tmpbuf[5+1]; + 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_start_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 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 SIMDJSON_BUILTIN_IMPLEMENTATION +} // namespace simdjson + +namespace simdjson { + +simdjson_inline simdjson_result::simdjson_result(SIMDJSON_BUILTIN_IMPLEMENTATION::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 +/* end file include/simdjson/generic/ondemand/value_iterator-inl.h */ +/* begin file include/simdjson/generic/ondemand/array_iterator-inl.h */ +namespace simdjson { +namespace SIMDJSON_BUILTIN_IMPLEMENTATION { +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 SIMDJSON_BUILTIN_IMPLEMENTATION +} // namespace simdjson + +namespace simdjson { + +simdjson_inline simdjson_result::simdjson_result( + SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::array_iterator &&value +) noexcept + : SIMDJSON_BUILTIN_IMPLEMENTATION::implementation_simdjson_result_base(std::forward(value)) +{ + first.iter.assert_is_valid(); +} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : SIMDJSON_BUILTIN_IMPLEMENTATION::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 +/* end file include/simdjson/generic/ondemand/array_iterator-inl.h */ +/* begin file include/simdjson/generic/ondemand/object_iterator-inl.h */ +namespace simdjson { +namespace SIMDJSON_BUILTIN_IMPLEMENTATION { +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 SIMDJSON_BUILTIN_IMPLEMENTATION +} // namespace simdjson + +namespace simdjson { + +simdjson_inline simdjson_result::simdjson_result( + SIMDJSON_BUILTIN_IMPLEMENTATION::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 +/* end file include/simdjson/generic/ondemand/object_iterator-inl.h */ +/* begin file include/simdjson/generic/ondemand/array-inl.h */ +namespace simdjson { +namespace SIMDJSON_BUILTIN_IMPLEMENTATION { +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; +} + +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 SIMDJSON_BUILTIN_IMPLEMENTATION +} // namespace simdjson + +namespace simdjson { + +simdjson_inline simdjson_result::simdjson_result( + SIMDJSON_BUILTIN_IMPLEMENTATION::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::raw_json() noexcept { + if (error()) { return error(); } + return first.raw_json(); +} +} // namespace simdjson +/* end file include/simdjson/generic/ondemand/array-inl.h */ +/* begin file include/simdjson/generic/ondemand/document-inl.h */ +namespace simdjson { +namespace SIMDJSON_BUILTIN_IMPLEMENTATION { +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. + iter.assert_at_document_depth(); + 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); +} +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_raw_json_string(); } +template<> simdjson_inline simdjson_result document::get() && noexcept { return get_string(false); } +template<> simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_double(); } +template<> simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_uint64(); } +template<> simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_int64(); } +template<> simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_bool(); } +template<> 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_inline error_code document::get(T &out) && noexcept { + return std::forward(*this).get().get(out); +} + +#if SIMDJSON_EXCEPTIONS +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 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_start_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; + } +} + +} // namespace ondemand +} // namespace SIMDJSON_BUILTIN_IMPLEMENTATION +} // namespace simdjson + +namespace simdjson { + +simdjson_inline simdjson_result::simdjson_result( + SIMDJSON_BUILTIN_IMPLEMENTATION::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); +} +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_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_inline simdjson_result simdjson_result::get() && noexcept { + if (error()) { return error(); } + return std::forward(first); +} +template<> simdjson_inline error_code simdjson_result::get(SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::document &out) & noexcept = delete; +template<> simdjson_inline error_code simdjson_result::get(SIMDJSON_BUILTIN_IMPLEMENTATION::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 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 +simdjson_inline simdjson_result::operator SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::array() & noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator SIMDJSON_BUILTIN_IMPLEMENTATION::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 SIMDJSON_BUILTIN_IMPLEMENTATION::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 SIMDJSON_BUILTIN_IMPLEMENTATION::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); +} + + +} // namespace simdjson + + +namespace simdjson { +namespace SIMDJSON_BUILTIN_IMPLEMENTATION { +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); } +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); } + +#if SIMDJSON_EXCEPTIONS +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 raw_json_string(*doc); } +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::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::raw_json() noexcept { return doc->raw_json();} +simdjson_inline document_reference::operator document&() const noexcept { return *doc; } + +} // namespace ondemand +} // namespace SIMDJSON_BUILTIN_IMPLEMENTATION +} // namespace simdjson + + + +namespace simdjson { +simdjson_inline simdjson_result::simdjson_result(SIMDJSON_BUILTIN_IMPLEMENTATION::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); +} +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_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 +simdjson_inline simdjson_result::operator SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::array() & noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator SIMDJSON_BUILTIN_IMPLEMENTATION::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 SIMDJSON_BUILTIN_IMPLEMENTATION::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 SIMDJSON_BUILTIN_IMPLEMENTATION::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); +} + + +} // namespace simdjson +/* end file include/simdjson/generic/ondemand/document-inl.h */ +/* begin file include/simdjson/generic/ondemand/value-inl.h */ +namespace simdjson { +namespace SIMDJSON_BUILTIN_IMPLEMENTATION { +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); +} +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 +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 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::current_location() noexcept { + return iter.json_iter().current_location(); +} + +simdjson_inline int32_t value::current_depth() const noexcept{ + return iter.json_iter().depth(); +} + +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: + return INVALID_JSON_POINTER; + } +} + +} // namespace ondemand +} // namespace SIMDJSON_BUILTIN_IMPLEMENTATION +} // namespace simdjson + +namespace simdjson { + +simdjson_inline simdjson_result::simdjson_result( + SIMDJSON_BUILTIN_IMPLEMENTATION::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); +} +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(SIMDJSON_BUILTIN_IMPLEMENTATION::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_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 +simdjson_inline simdjson_result::operator SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::array() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator SIMDJSON_BUILTIN_IMPLEMENTATION::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 SIMDJSON_BUILTIN_IMPLEMENTATION::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::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); +} + +} // namespace simdjson +/* end file include/simdjson/generic/ondemand/value-inl.h */ +/* begin file include/simdjson/generic/ondemand/field-inl.h */ +namespace simdjson { +namespace SIMDJSON_BUILTIN_IMPLEMENTATION { +namespace ondemand { + +// clang 6 doesn't 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; +} + +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 value &field::value() & noexcept { + return second; +} + +simdjson_inline value field::value() && noexcept { + return std::forward(*this).second; +} + +} // namespace ondemand +} // namespace SIMDJSON_BUILTIN_IMPLEMENTATION +} // namespace simdjson + +namespace simdjson { + +simdjson_inline simdjson_result::simdjson_result( + SIMDJSON_BUILTIN_IMPLEMENTATION::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::unescaped_key(bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.unescaped_key(allow_replacement); +} +simdjson_inline simdjson_result simdjson_result::value() noexcept { + if (error()) { return error(); } + return std::move(first.value()); +} + +} // namespace simdjson +/* end file include/simdjson/generic/ondemand/field-inl.h */ +/* begin file include/simdjson/generic/ondemand/object-inl.h */ +namespace simdjson { +namespace SIMDJSON_BUILTIN_IMPLEMENTATION { +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) { 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) { 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) { 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) { 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(0)}; + 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; +} + +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 SIMDJSON_BUILTIN_IMPLEMENTATION +} // namespace simdjson + +namespace simdjson { + +simdjson_inline simdjson_result::simdjson_result(SIMDJSON_BUILTIN_IMPLEMENTATION::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); +} + +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 +/* end file include/simdjson/generic/ondemand/object-inl.h */ +/* begin file include/simdjson/generic/ondemand/parser-inl.h */ +namespace simdjson { +namespace SIMDJSON_BUILTIN_IMPLEMENTATION { +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; +} + +simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(padded_string_view json) & noexcept { + if (json.padding() < SIMDJSON_PADDING) { return INSUFFICIENT_PADDING; } + + // 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 }); +} + +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(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; } + + // 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(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_inline size_t parser::capacity() const noexcept { + return _capacity; +} +simdjson_inline size_t parser::max_capacity() const noexcept { + return _max_capacity; +} +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 SIMDJSON_BUILTIN_IMPLEMENTATION +} // namespace simdjson + +namespace simdjson { + +simdjson_inline simdjson_result::simdjson_result(SIMDJSON_BUILTIN_IMPLEMENTATION::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 +/* end file include/simdjson/generic/ondemand/parser-inl.h */ +/* begin file include/simdjson/generic/ondemand/document_stream-inl.h */ +#include +#include +#include +namespace simdjson { +namespace SIMDJSON_BUILTIN_IMPLEMENTATION { +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: Remove any trailing whitespaces + // This returns a string spanning from start of value to the beginning of the next document (excluded) + return std::string_view(reinterpret_cast(stream->buf) + current_index(), stream->parser->implementation->structural_indexes[++cur_struct_index] - current_index() - 1); + } + 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 SIMDJSON_BUILTIN_IMPLEMENTATION +} // 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( + SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::document_stream &&value +) noexcept : + implementation_simdjson_result_base( + std::forward(value) + ) +{ +} + +} +/* end file include/simdjson/generic/ondemand/document_stream-inl.h */ +/* begin file include/simdjson/generic/ondemand/serialization-inl.h */ + + +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(SIMDJSON_BUILTIN_IMPLEMENTATION::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(SIMDJSON_BUILTIN_IMPLEMENTATION::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(SIMDJSON_BUILTIN_IMPLEMENTATION::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 SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand; + SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::json_type t; + auto error = x.type().get(t); + if(error != SUCCESS) { return error; } + switch (t) + { + case json_type::array: + { + SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::array array; + error = x.get_array().get(array); + if(error) { return error; } + return to_json_string(array); + } + case json_type::object: + { + SIMDJSON_BUILTIN_IMPLEMENTATION::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(SIMDJSON_BUILTIN_IMPLEMENTATION::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(SIMDJSON_BUILTIN_IMPLEMENTATION::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 SIMDJSON_BUILTIN_IMPLEMENTATION { namespace ondemand { + +#if SIMDJSON_EXCEPTIONS +inline std::ostream& operator<<(std::ostream& out, simdjson::SIMDJSON_BUILTIN_IMPLEMENTATION::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::SIMDJSON_BUILTIN_IMPLEMENTATION::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::SIMDJSON_BUILTIN_IMPLEMENTATION::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::SIMDJSON_BUILTIN_IMPLEMENTATION::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::SIMDJSON_BUILTIN_IMPLEMENTATION::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::SIMDJSON_BUILTIN_IMPLEMENTATION::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::SIMDJSON_BUILTIN_IMPLEMENTATION::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::SIMDJSON_BUILTIN_IMPLEMENTATION::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::SIMDJSON_BUILTIN_IMPLEMENTATION::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::SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand +/* end file include/simdjson/generic/ondemand/serialization-inl.h */ +/* end file include/simdjson/generic/ondemand-inl.h */ + + +namespace simdjson { + /** + * 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; + /** + * @copydoc simdjson::SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand + */ + namespace ondemand = SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand; + /** + * 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_H +/* end file include/simdjson/builtin.h */ + +#endif // SIMDJSON_H +/* end file include/simdjson.h */ diff --git a/hybridse/src/CMakeLists.txt b/hybridse/src/CMakeLists.txt index f68ffd4333f..643a6dea739 100644 --- a/hybridse/src/CMakeLists.txt +++ b/hybridse/src/CMakeLists.txt @@ -64,7 +64,8 @@ target_link_libraries(hybridse_flags ${GFLAGS_LIBRARY}) # hybridse core library, enable BUILD_SHARED_LIBS to build shared lib add_library(hybridse_core ${SRC_FILE_LIST} $ case/case_data_mock.cc) target_link_libraries(hybridse_core - ${yaml_libs} ${LLVM_LIBS} ${ZETASQL_LIBS} ${OS_LIB} ${COMMON_LIBS} ${g_libs} ${LLVM_EXT_LIB} farmhash hybridse_flags) + ${yaml_libs} ${LLVM_LIBS} ${ZETASQL_LIBS} ${OS_LIB} ${COMMON_LIBS} ${g_libs} + ${LLVM_EXT_LIB} op_contrib::simdjson farmhash hybridse_flags) set(HYBRIDSE_CORE_LIBS hybridse_core) add_subdirectory(testing) diff --git a/hybridse/src/codegen/udf_ir_builder_test.cc b/hybridse/src/codegen/udf_ir_builder_test.cc index fd90a938129..bd70b4ab2ab 100644 --- a/hybridse/src/codegen/udf_ir_builder_test.cc +++ b/hybridse/src/codegen/udf_ir_builder_test.cc @@ -1463,6 +1463,16 @@ TEST_F(UdfIRBuilderTest, AddMonths) { CheckUdf, Date, int64_t>("add_months", Date(2013, 3, 31), Date(2012, 1, 31), 14); } +TEST_F(UdfIRBuilderTest, JsonArrayLength) { + CheckUdf, Nullable>("json_array_length", 0, "[]"); + CheckUdf, Nullable>("json_array_length", 3, "[1,2,3]"); + CheckUdf, Nullable>("json_array_length", 5, R"([1,2,3,{"f1":1,"f2":[5,6]},4])"); + + CheckUdf, Nullable>("json_array_length", nullptr, R"({})"); + CheckUdf, Nullable>("json_array_length", nullptr, "[1,2,3"); + CheckUdf, Nullable>("json_array_length", nullptr, nullptr); +} + } // namespace codegen } // namespace hybridse diff --git a/hybridse/src/udf/default_defs/json_defs.cc b/hybridse/src/udf/default_defs/json_defs.cc new file mode 100644 index 00000000000..d48a64b53cb --- /dev/null +++ b/hybridse/src/udf/default_defs/json_defs.cc @@ -0,0 +1,77 @@ +/** + * Copyright (c) 2023 OpenMLDB authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "simdjson.h" +#include "udf/default_udf_library.h" +#include "udf/udf_library.h" +#include "udf/udf_registry.h" + +namespace hybridse { +namespace udf { + +void json_array_length(openmldb::base::StringRef* in, int32_t* sz, bool* is_null) { + *is_null = true; + + simdjson::ondemand::parser parser; + simdjson::padded_string json(in->data_, in->size_); + simdjson::ondemand::document doc; + auto err = parser.iterate(json).get(doc); + if (err) { + return; + } + simdjson::ondemand::array arr; + err = doc.get_array().get(arr); + if (err) { + return; + } + size_t arr_sz; + arr.count_elements().tie(arr_sz, err); + if (err) { + return; + } + + *is_null = false; + *sz = static_cast(arr_sz); +} + +void DefaultUdfLibrary::InitJsonUdfs() { + RegisterExternal("json_array_length") + .args(json_array_length) + .doc(R"( + @brief Returns the number of elements in the outermost JSON array. + + Null returned if input is not valid JSON array string. + + @param jsonArray JSON arry in string + + Example: + + @code{.sql} + select json_array_length('[1, 2]') + -- 2 + + SELECT json_array_length('[1,2,3,{"f1":1,"f2":[5,6]},4]'); + -- 5 + + select json_array_length('[1, 2') + -- NULL + @endcode + + @since 0.9.0)"); +} + +} // namespace udf +} // namespace hybridse diff --git a/hybridse/src/udf/default_udf_library.cc b/hybridse/src/udf/default_udf_library.cc index dff76da6b20..f2c9bc1afd8 100644 --- a/hybridse/src/udf/default_udf_library.cc +++ b/hybridse/src/udf/default_udf_library.cc @@ -667,6 +667,7 @@ void DefaultUdfLibrary::Init() { InitArrayUdfs(); InitEarthDistanceUdf(); + InitJsonUdfs(); AddExternalFunction("init_udfcontext.opaque", reinterpret_cast(static_cast(udf::v1::init_udfcontext))); diff --git a/hybridse/src/udf/default_udf_library.h b/hybridse/src/udf/default_udf_library.h index 3236c24e6f2..be5ed6c2414 100644 --- a/hybridse/src/udf/default_udf_library.h +++ b/hybridse/src/udf/default_udf_library.h @@ -57,6 +57,8 @@ class DefaultUdfLibrary : public UdfLibrary { // earth distance udfs void InitEarthDistanceUdf(); + + void InitJsonUdfs(); }; } // namespace udf From 73d29377845102d84b6a40e682a22d2a9a11a5f4 Mon Sep 17 00:00:00 2001 From: HuangWei Date: Fri, 4 Aug 2023 11:12:20 +0800 Subject: [PATCH 03/63] fix: udf build and deploy (#3410) --- docs/en/developer/udf_develop_guide.md | 2 +- docs/zh/openmldb_sql/udf_develop_guide.md | 2 +- release/sbin/deploy-all.sh | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/en/developer/udf_develop_guide.md b/docs/en/developer/udf_develop_guide.md index 7153af27be9..63530ae0f1c 100644 --- a/docs/en/developer/udf_develop_guide.md +++ b/docs/en/developer/udf_develop_guide.md @@ -151,7 +151,7 @@ For more UDF implementation, see [here](../../../src/examples/test_udf.cc). - Run the compiling command. `-I` specifies the path of `include` directory. `-o` specifies the name of the dynamic library. ```shell -g++ -shared -o libtest_udf.so examples/test_udf.cc -I /work/OpenMLDB/include -std=c++11 -fPIC +g++ -shared -o libtest_udf.so examples/test_udf.cc -I /work/OpenMLDB/include -std=c++17 -fPIC ``` ### 2.3 Copy the Dynamic Library diff --git a/docs/zh/openmldb_sql/udf_develop_guide.md b/docs/zh/openmldb_sql/udf_develop_guide.md index bfe581068ab..7fe4e81988d 100644 --- a/docs/zh/openmldb_sql/udf_develop_guide.md +++ b/docs/zh/openmldb_sql/udf_develop_guide.md @@ -149,7 +149,7 @@ int64_t special_sum_output(::openmldb::base::UDFContext* ctx) { - 执行编译命令,其中 -I 指定inlcude目录的路径 -o 指定产出动态库的名称 - ```shell -g++ -shared -o libtest_udf.so examples/test_udf.cc -I /work/OpenMLDB/include -std=c++11 -fPIC +g++ -shared -o libtest_udf.so examples/test_udf.cc -I /work/OpenMLDB/include -std=c++17 -fPIC ``` ### 2.3 拷贝动态库 diff --git a/release/sbin/deploy-all.sh b/release/sbin/deploy-all.sh index 932ff52a7fa..f665810f7e4 100755 --- a/release/sbin/deploy-all.sh +++ b/release/sbin/deploy-all.sh @@ -60,7 +60,7 @@ distribute() { rsync -arz "${SPARK_HOME}/" "$host:${SPARK_HOME}/" fi else - dir_list=(bin sbin conf) + dir_list=(bin sbin conf udf) fi for folder in "${dir_list[@]}" do From 3a17aecd9cdb3be3040df1af06eb3386845e1315 Mon Sep 17 00:00:00 2001 From: emo-coder <122784380+emo-coder@users.noreply.github.com> Date: Mon, 7 Aug 2023 10:54:04 +0800 Subject: [PATCH 04/63] feat: support showing jobs sorted by id (#3371) --- src/sdk/job_table_helper.cc | 16 ++++++++++++++ src/sdk/sql_cluster_test.cc | 44 +++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/src/sdk/job_table_helper.cc b/src/sdk/job_table_helper.cc index 91f1c4f63d4..8a486d3b59d 100644 --- a/src/sdk/job_table_helper.cc +++ b/src/sdk/job_table_helper.cc @@ -16,6 +16,7 @@ #include "sdk/job_table_helper.h" +#include #include #include #include "codec/schema_codec.h" @@ -164,6 +165,21 @@ std::shared_ptr JobTableHelper::MakeResultSet( vec.push_back("TaskManager"); records.emplace_back(std::move(vec)); } + + // sort jobs by id(asc) + std::sort(records.begin(), records.end(), + [](const std::vector& vec1, const std::vector& vec2) { + if (vec1.empty()) { + return true; + } + if (vec2.empty()) { + return false; + } + uint64_t id1, id2; + if (!absl::SimpleAtoi(vec1[0], &id1) || !absl::SimpleAtoi(vec2[0], &id2)) { + return vec1[0] < vec2[0]; + } + return id1 < id2;}); *status = {}; return ResultSetSQL::MakeResultSet(schema, records, status); } diff --git a/src/sdk/sql_cluster_test.cc b/src/sdk/sql_cluster_test.cc index b63f26feaf0..0155cf9cd81 100644 --- a/src/sdk/sql_cluster_test.cc +++ b/src/sdk/sql_cluster_test.cc @@ -26,6 +26,7 @@ #include "codec/fe_row_codec.h" #include "gflags/gflags.h" #include "gtest/gtest.h" +#include "sdk/job_table_helper.h" #include "sdk/mini_cluster.h" #include "sdk/sql_cluster_router.h" #include "sdk/sql_router.h" @@ -99,6 +100,49 @@ class SQLClusterDDLTest : public SQLClusterTest { std::string db; }; +TEST_F(SQLClusterDDLTest, TestShowSortedJobs) { + std::string name = "job_info" + GenRand(); + ::hybridse::sdk::Status status; + + std::string sql; + sql = "create table " + name + + "(" + "id int, job_type string, state string, start_time timestamp, end_time timestamp, " + "parameter string, cluster string, application_id string, error string, " + "index(key=id));"; + ASSERT_TRUE(router->ExecuteDDL(db, sql, &status)) << "ddl: " << sql; + ASSERT_TRUE(router->RefreshCatalog()); + + std::vector randint; + for (int i = 1; i < 100; i++) { + randint.push_back(i); + } + std::random_shuffle(randint.begin(), randint.end()); + for (uint64_t i = 0; i < randint.size(); i++) { + std::string id = std::to_string(randint[i]); + sql = "insert into " + name + + " values(" + id + ", \"Type\", \"State\", 0, " + id + ", " + "\"/tmp/sql-000000000000000000" + id + "\", \"local[*]\", \"local-0000000000000" + id + "\", \"\");"; + router->ExecuteSQL(db, sql, &status); + ASSERT_TRUE(status.IsOK()); + } + + auto rs = router->ExecuteSQL(db, "select * from " + name + ";", &status); + ASSERT_TRUE(status.IsOK()); + + rs = JobTableHelper::MakeResultSet(rs, "", &status); + ASSERT_TRUE(status.IsOK()); + + int id_current = 0, id_next; + while (rs->Next()) { + ASSERT_TRUE(rs->GetInt32(0, &id_next)); + ASSERT_LT(id_current, id_next); + id_current = id_next; + } + + ASSERT_TRUE(router->ExecuteDDL(db, "drop table " + name + ";", &status)); +} + TEST_F(SQLClusterDDLTest, TestCreateTableLike) { ::hybridse::sdk::Status status; From 55083c9827499dba0fa5b7e42e913c057ba54692 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 7 Aug 2023 16:19:47 +0800 Subject: [PATCH 05/63] docs(udf): upgrade udf list (#3421) --- .../functions_and_operators/Files/udfs_8h.md | 42 +++++++++++++++++++ .../functions_and_operators/Files/udfs_8h.md | 42 +++++++++++++++++++ 2 files changed, 84 insertions(+) diff --git a/docs/en/reference/sql/functions_and_operators/Files/udfs_8h.md b/docs/en/reference/sql/functions_and_operators/Files/udfs_8h.md index 890da2a2d60..fe8bf35b6b4 100644 --- a/docs/en/reference/sql/functions_and_operators/Files/udfs_8h.md +++ b/docs/en/reference/sql/functions_and_operators/Files/udfs_8h.md @@ -72,6 +72,7 @@ title: udfs/udfs.h | **[is_null](/openmldb_sql/functions_and_operators/Files/udfs_8h.md#function-is-null)**()|
Check if input value is null, return bool. | | **[isnull](/openmldb_sql/functions_and_operators/Files/udfs_8h.md#function-isnull)**()| | | **[join](/openmldb_sql/functions_and_operators/Files/udfs_8h.md#function-join)**()|
For each string value from specified column of window, join by delimeter. Null values are skipped. | +| **[json_array_length](/openmldb_sql/functions_and_operators/Files/udfs_8h.md#function-json-array-length)**()|
Returns the number of elements in the outermost JSON array. | | **[lag](/openmldb_sql/functions_and_operators/Files/udfs_8h.md#function-lag)**()|
Returns value evaluated at the row that is offset rows before the current row within the partition. Offset is evaluated with respect to the current row. | | **[last_day](/openmldb_sql/functions_and_operators/Files/udfs_8h.md#function-last-day)**()|
Return the last day of the month to which the date belongs to. | | **[lcase](/openmldb_sql/functions_and_operators/Files/udfs_8h.md#function-lcase)**()|
Convert all the characters to lowercase. Note that characters with values > 127 are simply returned. | @@ -2139,6 +2140,47 @@ select `join`(split("k1:v1,k2:v2", ","), " "); * [`list`, `string`] +### function json_array_length + +```cpp +json_array_length() +``` + +**Description**: + +Returns the number of elements in the outermost JSON array. + +**Parameters**: + + * **jsonArray** JSON arry in string + + +**Since**: +0.9.0 + + +Null returned if input is not valid JSON array string. + + +Example: + +```sql + +select json_array_length('[1, 2]') +-- 2 + +SELECT json_array_length('[1,2,3,{"f1":1,"f2":[5,6]},4]'); +-- 5 + +select json_array_length('[1, 2') +-- NULL +``` + + +**Supported Types**: + +* [`string`] + ### function lag ```cpp diff --git a/docs/zh/openmldb_sql/functions_and_operators/Files/udfs_8h.md b/docs/zh/openmldb_sql/functions_and_operators/Files/udfs_8h.md index 890da2a2d60..fe8bf35b6b4 100644 --- a/docs/zh/openmldb_sql/functions_and_operators/Files/udfs_8h.md +++ b/docs/zh/openmldb_sql/functions_and_operators/Files/udfs_8h.md @@ -72,6 +72,7 @@ title: udfs/udfs.h | **[is_null](/openmldb_sql/functions_and_operators/Files/udfs_8h.md#function-is-null)**()|
Check if input value is null, return bool. | | **[isnull](/openmldb_sql/functions_and_operators/Files/udfs_8h.md#function-isnull)**()| | | **[join](/openmldb_sql/functions_and_operators/Files/udfs_8h.md#function-join)**()|
For each string value from specified column of window, join by delimeter. Null values are skipped. | +| **[json_array_length](/openmldb_sql/functions_and_operators/Files/udfs_8h.md#function-json-array-length)**()|
Returns the number of elements in the outermost JSON array. | | **[lag](/openmldb_sql/functions_and_operators/Files/udfs_8h.md#function-lag)**()|
Returns value evaluated at the row that is offset rows before the current row within the partition. Offset is evaluated with respect to the current row. | | **[last_day](/openmldb_sql/functions_and_operators/Files/udfs_8h.md#function-last-day)**()|
Return the last day of the month to which the date belongs to. | | **[lcase](/openmldb_sql/functions_and_operators/Files/udfs_8h.md#function-lcase)**()|
Convert all the characters to lowercase. Note that characters with values > 127 are simply returned. | @@ -2139,6 +2140,47 @@ select `join`(split("k1:v1,k2:v2", ","), " "); * [`list`, `string`] +### function json_array_length + +```cpp +json_array_length() +``` + +**Description**: + +Returns the number of elements in the outermost JSON array. + +**Parameters**: + + * **jsonArray** JSON arry in string + + +**Since**: +0.9.0 + + +Null returned if input is not valid JSON array string. + + +Example: + +```sql + +select json_array_length('[1, 2]') +-- 2 + +SELECT json_array_length('[1,2,3,{"f1":1,"f2":[5,6]},4]'); +-- 5 + +select json_array_length('[1, 2') +-- NULL +``` + + +**Supported Types**: + +* [`string`] + ### function lag ```cpp From d09d5e0d5d169ec5582849646ea757e8e87cdf02 Mon Sep 17 00:00:00 2001 From: emo-coder <122784380+emo-coder@users.noreply.github.com> Date: Mon, 7 Aug 2023 16:20:17 +0800 Subject: [PATCH 06/63] feat: support showing and dropping deployment with db (#3353) --- cases/plan/cmd.yaml | 20 +++++++++-- hybridse/src/planv2/ast_node_converter.cc | 14 ++++++-- src/sdk/sql_cluster_router.cc | 15 ++++---- src/sdk/sql_cluster_test.cc | 43 +++++++++++++++++++++++ 4 files changed, 81 insertions(+), 11 deletions(-) diff --git a/cases/plan/cmd.yaml b/cases/plan/cmd.yaml index 1b67d2d8e9c..3ca7d89ba6f 100644 --- a/cases/plan/cmd.yaml +++ b/cases/plan/cmd.yaml @@ -197,7 +197,7 @@ cases: +-node[CMD] +-cmd_type: show deployments +-args: [] - - id: show_deployment + - id: show_deployment1 desc: show deployment foo sql: SHOW DEPLOYMENT foo; expect: @@ -205,6 +205,14 @@ cases: +-node[CMD] +-cmd_type: show deployment +-args: [foo] + - id: show_deployment2 + desc: show deployment foo + sql: SHOW DEPLOYMENT db.foo; + expect: + node_tree_str: | + +-node[CMD] + +-cmd_type: show deployment + +-args: [db, foo] - id: show_functions desc: show functions sql: SHOW FUNCTIONS; @@ -213,7 +221,7 @@ cases: +-node[CMD] +-cmd_type: show functions +-args: [] - - id: drop_deployment + - id: drop_deployment1 desc: drop deployment foo sql: DROP DEPLOYMENT foo; expect: @@ -221,6 +229,14 @@ cases: +-node[CMD] +-cmd_type: drop deployment +-args: [foo] + - id: drop_deployment2 + desc: drop deployment foo + sql: DROP DEPLOYMENT db.foo; + expect: + node_tree_str: | + +-node[CMD] + +-cmd_type: drop deployment + +-args: [db, foo] - id: deploy_stmt_1 desc: deploy query task sql: DEPLOY foo SELECT col1 from t1; diff --git a/hybridse/src/planv2/ast_node_converter.cc b/hybridse/src/planv2/ast_node_converter.cc index 06b13fefd99..19bb0ccfc6c 100644 --- a/hybridse/src/planv2/ast_node_converter.cc +++ b/hybridse/src/planv2/ast_node_converter.cc @@ -2039,10 +2039,18 @@ base::Status ConvertDropStatement(const zetasql::ASTDropStatement* root, node::N return base::Status::OK(); } case zetasql::SchemaObjectKind::kDeployment: { - CHECK_TRUE(1 == names.size(), common::kSqlAstError, "Invalid deployment path expression ", + CHECK_TRUE(2 >= names.size(), common::kSqlAstError, "Invalid deployment path expression ", root->name()->ToIdentifierPathString()) - *output = - dynamic_cast(node_manager->MakeCmdNode(node::CmdType::kCmdDropDeployment, names[0])); + if (names.size() == 1) { + *output = + dynamic_cast(node_manager->MakeCmdNode(node::CmdType::kCmdDropDeployment, + names.back())); + + } else { + *output = + dynamic_cast(node_manager->MakeCmdNode(node::CmdType::kCmdDropDeployment, + names[0], names[1])); + } return base::Status::OK(); } default: { diff --git a/src/sdk/sql_cluster_router.cc b/src/sdk/sql_cluster_router.cc index 3c768151aa2..5d821dbe884 100644 --- a/src/sdk/sql_cluster_router.cc +++ b/src/sdk/sql_cluster_router.cc @@ -1743,8 +1743,9 @@ std::shared_ptr SQLClusterRouter::HandleSQLCmd(const h if (!status->IsOK()) { return {}; } + std::vector sps; - if (!ns_ptr->ShowProcedure(db, deploy_name, &sps, &msg)) { + if (!ns_ptr->ShowProcedure(db_name, deploy_name, &sps, &msg)) { *status = {StatusCode::kCmdError, msg}; return {}; } @@ -1781,13 +1782,15 @@ std::shared_ptr SQLClusterRouter::HandleSQLCmd(const h return ResultSetSQL::MakeResultSet({"DB", "Deployment"}, lines, status); } case hybridse::node::kCmdDropDeployment: { - if (db.empty()) { - *status = {StatusCode::kCmdError, "please enter database first"}; + std::string db_name, deploy_name; + auto& args = cmd_node->GetArgs(); + *status = ParseNamesFromArgs(db, args, &db_name, &deploy_name); + if (!status->IsOK()) { return {}; } - std::string deploy_name = cmd_node->GetArgs()[0]; + // check if deployment, avoid deleting the normal procedure - auto sp = cluster_sdk_->GetProcedureInfo(db, deploy_name, &msg); + auto sp = cluster_sdk_->GetProcedureInfo(db_name, deploy_name, &msg); if (!sp || sp->GetType() != hybridse::sdk::kReqDeployment) { *status = {StatusCode::kCmdError, sp ? "not a deployment" : "deployment not found"}; return {}; @@ -1795,7 +1798,7 @@ std::shared_ptr SQLClusterRouter::HandleSQLCmd(const h if (!CheckAnswerIfInteractive("deployment", deploy_name)) { return {}; } - if (ns_ptr->DropProcedure(db, deploy_name, msg)) { + if (ns_ptr->DropProcedure(db_name, deploy_name, msg)) { RefreshCatalog(); *status = {}; } else { diff --git a/src/sdk/sql_cluster_test.cc b/src/sdk/sql_cluster_test.cc index 0155cf9cd81..321be553905 100644 --- a/src/sdk/sql_cluster_test.cc +++ b/src/sdk/sql_cluster_test.cc @@ -100,6 +100,49 @@ class SQLClusterDDLTest : public SQLClusterTest { std::string db; }; + +TEST_F(SQLClusterDDLTest, TestShowAndDropDeployment) { + std::string db2 = "db" + GenRand(); + std::string table_name = "tb" + GenRand(); + std::string deploy_name = "dp" + GenRand(); + ::hybridse::sdk::Status status; + + std::string ddl; + ddl = "create table " + table_name + + "(" + "col1 int, col2 bigint, col3 string, " + "index(key = col3, ts = col2));"; + + ASSERT_TRUE(router->CreateDB(db2, &status)); + ASSERT_TRUE(router->ExecuteDDL(db, ddl, &status)); + + ASSERT_TRUE(router->RefreshCatalog()); + SetOnlineMode(router); + + router->ExecuteSQL(db, "deploy " + deploy_name + " select col1 from " + table_name + ";", &status); + ASSERT_TRUE(status.IsOK()); + router->ExecuteSQL(db2, "deploy " + deploy_name + " select col1 from " + db + "." + table_name + ";", &status); + ASSERT_TRUE(status.IsOK()); + + router->ExecuteSQL(db, "show deployment " + deploy_name + ";", &status); + ASSERT_TRUE(status.IsOK()); + router->ExecuteSQL(db, "show deployment " + db2 + "." + deploy_name + ";", &status); + ASSERT_TRUE(status.IsOK()); + + router->ExecuteSQL(db, "drop deployment " + deploy_name + ";", &status); + ASSERT_TRUE(status.IsOK()); + router->ExecuteSQL(db, "drop deployment " + db2 + "." + deploy_name + ";", &status); + ASSERT_TRUE(status.IsOK()); + + router->ExecuteSQL(db, "show deployment " + deploy_name + ";", &status); + ASSERT_FALSE(status.IsOK()); + router->ExecuteSQL(db, "show deployment " + db2 + "." + deploy_name + ";", &status); + ASSERT_FALSE(status.IsOK()); + + ASSERT_TRUE(router->ExecuteDDL(db, "drop table " + table_name + ";", &status)); + ASSERT_TRUE(router->DropDB(db2, &status)); +} + TEST_F(SQLClusterDDLTest, TestShowSortedJobs) { std::string name = "job_info" + GenRand(); ::hybridse::sdk::Status status; From 1ceeca13c3c58838c849c6d655c360e6c224e765 Mon Sep 17 00:00:00 2001 From: dl239 Date: Mon, 7 Aug 2023 08:59:00 -0500 Subject: [PATCH 07/63] feat: upgrade docker version (#3388) * feat: upgrade docker version * feat: update java/python version * feat: update script * fix: fix comment * revert setup * refact: rm unused code * refact: revert prepare_release.sh --- CMakeLists.txt | 4 ++-- demo/Dockerfile | 2 +- java/hybridse-native/pom.xml | 2 +- java/hybridse-proto/pom.xml | 2 +- java/hybridse-sdk/pom.xml | 2 +- java/openmldb-batch/pom.xml | 2 +- java/openmldb-batchjob/pom.xml | 2 +- java/openmldb-common/pom.xml | 2 +- java/openmldb-jdbc/pom.xml | 2 +- java/openmldb-native/pom.xml | 2 +- java/openmldb-spark-connector/pom.xml | 2 +- java/openmldb-synctool/pom.xml | 2 +- java/openmldb-taskmanager/pom.xml | 2 +- java/pom.xml | 4 ++-- python/openmldb_sdk/setup.py | 2 +- python/openmldb_tool/setup.py | 2 +- steps/post_release.sh | 2 ++ test/integration-test/openmldb-test-python/install.sh | 2 +- 18 files changed, 21 insertions(+), 19 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 62ff8443380..a9f10095c38 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -35,8 +35,8 @@ endif() message (STATUS "CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}") set(OPENMLDB_VERSION_MAJOR 0) -set(OPENMLDB_VERSION_MINOR 7) -set(OPENMLDB_VERSION_BUG 0) +set(OPENMLDB_VERSION_MINOR 8) +set(OPENMLDB_VERSION_BUG 2) function(get_commitid CODE_DIR COMMIT_ID) find_package(Git REQUIRED) diff --git a/demo/Dockerfile b/demo/Dockerfile index 354fe86bd66..e6495931d35 100644 --- a/demo/Dockerfile +++ b/demo/Dockerfile @@ -25,7 +25,7 @@ COPY *_dist.yml /work/ ENV LANG=en_US.UTF-8 ENV SPARK_HOME=/work/openmldb/spark-3.2.1-bin-openmldbspark -ARG OPENMLDB_VERSION=0.8.1 +ARG OPENMLDB_VERSION=0.8.2 ENV OPENMLDB_VERSION="${OPENMLDB_VERSION}" RUN if [ "${USE_ADD_WHL}" = "true" ] ; then \ diff --git a/java/hybridse-native/pom.xml b/java/hybridse-native/pom.xml index 897adba864d..e347b945b6c 100644 --- a/java/hybridse-native/pom.xml +++ b/java/hybridse-native/pom.xml @@ -5,7 +5,7 @@ openmldb-parent com.4paradigm.openmldb - 0.7.0-SNAPSHOT + 0.8.2-SNAPSHOT ../pom.xml 4.0.0 diff --git a/java/hybridse-proto/pom.xml b/java/hybridse-proto/pom.xml index ded34ae58f4..83b28bdd95f 100644 --- a/java/hybridse-proto/pom.xml +++ b/java/hybridse-proto/pom.xml @@ -4,7 +4,7 @@ openmldb-parent com.4paradigm.openmldb - 0.7.0-SNAPSHOT + 0.8.2-SNAPSHOT ../pom.xml 4.0.0 diff --git a/java/hybridse-sdk/pom.xml b/java/hybridse-sdk/pom.xml index e9b7a2fc9bd..e103c064280 100644 --- a/java/hybridse-sdk/pom.xml +++ b/java/hybridse-sdk/pom.xml @@ -6,7 +6,7 @@ openmldb-parent com.4paradigm.openmldb - 0.7.0-SNAPSHOT + 0.8.2-SNAPSHOT ../pom.xml 4.0.0 diff --git a/java/openmldb-batch/pom.xml b/java/openmldb-batch/pom.xml index 5fe4dfb31aa..a2a93ad4d3a 100644 --- a/java/openmldb-batch/pom.xml +++ b/java/openmldb-batch/pom.xml @@ -7,7 +7,7 @@ openmldb-parent com.4paradigm.openmldb - 0.7.0-SNAPSHOT + 0.8.2-SNAPSHOT openmldb-batch diff --git a/java/openmldb-batchjob/pom.xml b/java/openmldb-batchjob/pom.xml index 9eed0f057da..fddac8ffb82 100644 --- a/java/openmldb-batchjob/pom.xml +++ b/java/openmldb-batchjob/pom.xml @@ -7,7 +7,7 @@ openmldb-parent com.4paradigm.openmldb - 0.7.0-SNAPSHOT + 0.8.2-SNAPSHOT openmldb-batchjob diff --git a/java/openmldb-common/pom.xml b/java/openmldb-common/pom.xml index d24d31c44a6..8bd3e054cce 100644 --- a/java/openmldb-common/pom.xml +++ b/java/openmldb-common/pom.xml @@ -5,7 +5,7 @@ openmldb-parent com.4paradigm.openmldb - 0.7.0-SNAPSHOT + 0.8.2-SNAPSHOT 4.0.0 openmldb-common diff --git a/java/openmldb-jdbc/pom.xml b/java/openmldb-jdbc/pom.xml index fe29c9c4905..0b2b8af9df0 100644 --- a/java/openmldb-jdbc/pom.xml +++ b/java/openmldb-jdbc/pom.xml @@ -5,7 +5,7 @@ openmldb-parent com.4paradigm.openmldb - 0.7.0-SNAPSHOT + 0.8.2-SNAPSHOT ../pom.xml 4.0.0 diff --git a/java/openmldb-native/pom.xml b/java/openmldb-native/pom.xml index 0175a87f401..8bd8a8399f3 100644 --- a/java/openmldb-native/pom.xml +++ b/java/openmldb-native/pom.xml @@ -5,7 +5,7 @@ openmldb-parent com.4paradigm.openmldb - 0.7.0-SNAPSHOT + 0.8.2-SNAPSHOT ../pom.xml 4.0.0 diff --git a/java/openmldb-spark-connector/pom.xml b/java/openmldb-spark-connector/pom.xml index e36ad745b71..f8d2e31d9f2 100644 --- a/java/openmldb-spark-connector/pom.xml +++ b/java/openmldb-spark-connector/pom.xml @@ -6,7 +6,7 @@ openmldb-parent com.4paradigm.openmldb - 0.7.0-SNAPSHOT + 0.8.2-SNAPSHOT openmldb-spark-connector diff --git a/java/openmldb-synctool/pom.xml b/java/openmldb-synctool/pom.xml index 60917c9e905..a877d921eac 100644 --- a/java/openmldb-synctool/pom.xml +++ b/java/openmldb-synctool/pom.xml @@ -6,7 +6,7 @@ openmldb-parent com.4paradigm.openmldb - 0.7.0-SNAPSHOT + 0.8.2-SNAPSHOT openmldb-synctool openmldb-synctool diff --git a/java/openmldb-taskmanager/pom.xml b/java/openmldb-taskmanager/pom.xml index 108725a9acf..56aeef8b7c3 100644 --- a/java/openmldb-taskmanager/pom.xml +++ b/java/openmldb-taskmanager/pom.xml @@ -6,7 +6,7 @@ openmldb-parent com.4paradigm.openmldb - 0.7.0-SNAPSHOT + 0.8.2-SNAPSHOT openmldb-taskmanager openmldb-taskmanager diff --git a/java/pom.xml b/java/pom.xml index 1d168284dbc..3a46ab4d5c1 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -7,7 +7,7 @@ openmldb-parent pom openmldb - 0.7.0-SNAPSHOT + 0.8.2-SNAPSHOT hybridse-sdk hybridse-native @@ -65,7 +65,7 @@ - 0.7.0-SNAPSHOT + 0.8.2-SNAPSHOT error 2.9.0 diff --git a/python/openmldb_sdk/setup.py b/python/openmldb_sdk/setup.py index 196841116ba..528250e1822 100644 --- a/python/openmldb_sdk/setup.py +++ b/python/openmldb_sdk/setup.py @@ -18,7 +18,7 @@ setup( name='openmldb', - version='0.7.0a0', + version='0.8.2a0', author='OpenMLDB Team', author_email=' ', url='https://github.com/4paradigm/OpenMLDB', diff --git a/python/openmldb_tool/setup.py b/python/openmldb_tool/setup.py index f7120cfa256..097b3c229c8 100644 --- a/python/openmldb_tool/setup.py +++ b/python/openmldb_tool/setup.py @@ -18,7 +18,7 @@ setup( name="openmldb-tool", - version="0.7.0a0", + version="0.8.2a0", author="OpenMLDB Team", author_email=" ", url="https://github.com/4paradigm/OpenMLDB", diff --git a/steps/post_release.sh b/steps/post_release.sh index 36b4b656e4a..05c3a25183e 100644 --- a/steps/post_release.sh +++ b/steps/post_release.sh @@ -24,3 +24,5 @@ fi sed -i"" -e "s/OPENMLDB_VERSION=[0-9]\.[0-9]\.[0-9]/OPENMLDB_VERSION=${VERSION}/g" demo/Dockerfile sed -i"" -e "s/SPARK_VERSION=[0-9]\.[0-9]\.[0-9]/SPARK_VERSION=${VERSION}/g" test/integration-test/openmldb-test-python/install.sh + +sh steps/prepare_release.sh "${VERSION}.a0" diff --git a/test/integration-test/openmldb-test-python/install.sh b/test/integration-test/openmldb-test-python/install.sh index c859a44ee5a..9b16e934807 100644 --- a/test/integration-test/openmldb-test-python/install.sh +++ b/test/integration-test/openmldb-test-python/install.sh @@ -17,7 +17,7 @@ set -eE -x CURRENT_DIR=$(dirname "$0") -SPARK_VERSION=0.8.1 +SPARK_VERSION=0.8.2 pushd "${CURRENT_DIR}" cp -r ../../../openmldb ./ sed -i"" -e "s/OPENMLDB_MODE:=standalone/OPENMLDB_MODE:=cluster/g" openmldb/conf/openmldb-env.sh From 9c44acb5fa0a8f7b35112808a8de2399b6e3646f Mon Sep 17 00:00:00 2001 From: HuangWei Date: Fri, 11 Aug 2023 17:16:51 +0800 Subject: [PATCH 08/63] build: add usability testing and use ori tar name (#3411) --- demo/docker-compose.test.yml | 11 +- demo/setup_openmldb.sh | 20 ++- demo/usability_testing/README.md | 119 +++++++++++++ demo/usability_testing/data_mocker.py | 167 ++++++++++++++++++ demo/usability_testing/demo_setup.sh | 9 + demo/usability_testing/demo_test.sh | 26 +++ .../offline_test.sql.template | 39 ++++ demo/usability_testing/simple_test.sql | 28 +++ demo/usability_testing/udf_test.sql | 17 ++ 9 files changed, 426 insertions(+), 10 deletions(-) create mode 100644 demo/usability_testing/README.md create mode 100644 demo/usability_testing/data_mocker.py create mode 100644 demo/usability_testing/demo_setup.sh create mode 100644 demo/usability_testing/demo_test.sh create mode 100644 demo/usability_testing/offline_test.sql.template create mode 100644 demo/usability_testing/simple_test.sql create mode 100644 demo/usability_testing/udf_test.sql diff --git a/demo/docker-compose.test.yml b/demo/docker-compose.test.yml index 9fd12df1b35..72339162894 100644 --- a/demo/docker-compose.test.yml +++ b/demo/docker-compose.test.yml @@ -10,12 +10,16 @@ services: - ./java_quickstart/demo/target:/work/java_quickstart - ./python_quickstart:/work/python_quickstart - ./cxx_quickstart:/work/cxx_quickstart + - ./usability_testing:/work/usability_testing # You can add `cat ` here(e.g. `cat /work/openmldb/taskmanager/bin/logs/job_1_error.log`, cat `predict.log`), to check the log info. # No need to docker-compose build again. But if you modified the Dockerfile, must rebuild it. command: - /bin/bash - -ecx # -e, otherwise, the command may not exit when 'exit' - | + # deploy-all miss udf dir <=0.8.2, mkdir first until new version released + mkdir -p /tmp/openmldb/tablet-1/udf + mkdir -p /tmp/openmldb/tablet-2/udf ./init.sh sleep 5 # quickstart test @@ -61,8 +65,11 @@ services: # check deployment, jobs will be checked by openmldb_tool curl http://127.0.0.1:9080/dbs/JD_db/deployments/demo | grep "ok" || exit -1 - # TODO(hw): udf test - + # usability testing, don't miss interactive flag + cd /work/usability_testing + bash demo_setup.sh + bash demo_test.sh + cd /work openmldb_tool status --diff -f /work/openmldb/conf/hosts openmldb_tool inspect diff --git a/demo/setup_openmldb.sh b/demo/setup_openmldb.sh index 8f5dea44f80..e7a98c51f09 100755 --- a/demo/setup_openmldb.sh +++ b/demo/setup_openmldb.sh @@ -18,29 +18,34 @@ set -eE -x VERSION="$1" if [[ -z ${VERSION} ]]; then - VERSION=0.6.3 + VERSION=0.8.2 fi echo "version: ${VERSION}" +ZK_TAR="zookeeper-3.4.14.tar.gz" +OPENMLDB_TAR="openmldb-${VERSION}-linux.tar.gz" +# TODO(hw): spark release pkg name should add version +SPARK_TAR="spark-3.2.1-bin-openmldbspark.tgz" + if [ $# -gt 1 ] && [ "$2" = "skip_download" ]; then echo "skip download packages, the 3 packages should in current dir" else - curl -SLo zookeeper.tar.gz https://archive.apache.org/dist/zookeeper/zookeeper-3.4.14/zookeeper-3.4.14.tar.gz - curl -SLo openmldb.tar.gz "https://github.com/4paradigm/OpenMLDB/releases/download/v${VERSION}/openmldb-${VERSION}-linux.tar.gz" - curl -SLo spark-3.2.1-bin-openmldbspark.tgz "https://github.com/4paradigm/spark/releases/download/v3.2.1-openmldb${VERSION}/spark-3.2.1-bin-openmldbspark.tgz" + curl -SLO "https://archive.apache.org/dist/zookeeper/zookeeper-3.4.14/${ZK_TAR}" + curl -SLO "https://github.com/4paradigm/OpenMLDB/releases/download/v${VERSION}/${OPENMLDB_TAR}" + curl -SLO "https://github.com/4paradigm/spark/releases/download/v3.2.1-openmldb${VERSION}/${SPARK_TAR}" fi WORKDIR=/work mkdir -p "$WORKDIR" -tar xzf zookeeper.tar.gz -C "$WORKDIR" +tar xzf "${ZK_TAR}" -C "$WORKDIR" pushd $WORKDIR/zookeeper-3.4.14/ cp conf/zoo_sample.cfg conf/zoo.cfg popd mkdir -p "${WORKDIR}/openmldb" -tar xzf openmldb.tar.gz -C "${WORKDIR}/openmldb" --strip-components 1 +tar xzf "${OPENMLDB_TAR}" -C "${WORKDIR}/openmldb" --strip-components 1 # remove symbols and sections strip -s "${WORKDIR}/openmldb/bin/openmldb" # do not install sync tools in demo docker @@ -48,12 +53,11 @@ rm "${WORKDIR}/openmldb/bin/data_collector" rm -rf "${WORKDIR}/openmldb/synctool" mkdir -p "${WORKDIR}/openmldb/spark-3.2.1-bin-openmldbspark" -tar xzf spark-3.2.1-bin-openmldbspark.tgz -C "${WORKDIR}/openmldb/spark-3.2.1-bin-openmldbspark" --strip-components 1 +tar xzf "${SPARK_TAR}" -C "${WORKDIR}/openmldb/spark-3.2.1-bin-openmldbspark" --strip-components 1 pushd "${WORKDIR}/openmldb" ln -s "${WORKDIR}/zookeeper-3.4.14" zookeeper ln -s spark-3.2.1-bin-openmldbspark spark -rm -f spark-3.2.1-bin-openmldbspark/jars/curator-* # curator NoSuchMethodError error popd rm -f ./*.tar.gz diff --git a/demo/usability_testing/README.md b/demo/usability_testing/README.md new file mode 100644 index 00000000000..0b71594a0bf --- /dev/null +++ b/demo/usability_testing/README.md @@ -0,0 +1,119 @@ +# Usability testing + +## Introduction + +This directory contains the black-box testing for the project. The tests are sql scripts that are run against the database. But some of the tests need setup and the setup may can not be done automatically. + +Notice that, we need to check the result manually, grep `ERROR` in sql results and check the output result data, **not only check job status**. We may support run scripts by diag tool, and it will check the result automatically(add mark to let diag tool sleep for some async ops). + +## Setup data + +In some cases, you should setup data before running the tests. The data can be generated by `data_mocker.py` or you can prepare the data manually. +``` +pip3 install faker pandas numpy click pyarrow +python3 data_mocker.py -s "create table t1(c1 int, c2 string)" -o test -nf 10 -n 1000 +python3 data_mocker.py -s "c1 int, c2 string" -o test -nf 10 -n 1000 +``` + +## simple_test.sql + +This directory contains the simple tests. The tests are simple and can be run automatically. No setup needed. + +```shell +/work/openmldb/bin/openmldb --zk_cluster=127.0.0.1:2181 --zk_root_path=/openmldb --role=sql_client --interactive=false < simple_test.sql +# or +cat simple_test.sql | /work/openmldb/bin/openmldb --zk_cluster=127.0.0.1:2181 --zk_root_path=/openmldb --role=sql_client --interactive=false +``` + +If some async jobs still running after drop, you can ignore them, just need to care about sync jobs. If you want check async jobs, you should comment out the `drop table/database` statement in the script. To check the result, you can `SHOW JOBS` or use diag tool to check the result. + +So `RUNNING` is ok, but there should be no `ERROR`/`FAILED` in the result. + +## offline + +This script contains the offline tests. The tests are simple and can be run automatically. But it needs setup, may needs to be done manually. + +Setup: + +- Generate data, you can gen a big data set to check about spark execute config. Here just a small data set. +``` +# generate data, multi files, big file e.g. 1G/512M, to meet executor memory limit +python3 data_mocker.py -s "create table t1(c1 int, c2 string)" -o /tmp/openmldb_test/t1/ -nf 10 -n 1000 -f csv +python3 data_mocker.py -s "create table t1(c1 int, c2 string)" -o /tmp/openmldb_test/t2/ -nf 10 -n 1000 +ls /tmp/openmldb_test/t1/*.csv +ls /tmp/openmldb_test/t2/*.parquet +``` + +- Source place, hdfs/local/hive, choose one. + - Local spark and file, so just use the date generated in `file:///tmp/openmldb_test`. And dst path is also local. + ```{note} + We assume the taskmanager process is running on the same machine as the test, and the spark master is local. Distributed cluster should use hdfs path. + ``` + ``` + sed "s##file:///tmp/openmldb_test#" offline_test.sql.template > offline_test.sql + sed -i'' "s##file:///tmp/openmldb_testout#" offline_test.sql + ``` + - HDFS, you should put the data to hdfs, and give the hdfs path. e.g. + ``` + HDFS_PATH=hdfs://0.0.0.0/tmp # or hdfs:///tmp, hdfs cluster is needless, use the cluster in spark config + sed "s##${HDFS_PATH}/openmldb_test#" offline_test.sql.template > offline_test.sql + sed -i'' "s##${HDFS_PATH}/openmldb_testout#" offline_test.sql + ``` + ```{note} + Data in HDFS is `$HDFS_PATH/openmldb_test/t1` and `$HDFS_PATH/openmldb_test/t2`, and the dst path is `$HDFS_PATH/openmldb_testout`. We can create dst dir when `SELECT INTO`, but it needs write permission of `$HDFS_PATH`. + ``` + - Hive, you should create hive table t1&t2, load data to hive talbes, and give the hive path with database, e.g. + ``` + HIVE_PATH=hive://db1. + sed "s##${HIVE_PATH}#" offline_test.sql.template > offline_test.sql + sed -i'' "s##${HIVE_PATH}#" offline_test.sql + ``` + ```{note} + Table in HIVE is `db1.t1` and `db1.t2`, and the dst path is `db1.t1_deep`, `db1.t2_deep`, etc. We can create dst table when `SELECT INTO`, but it needs write permission of HIVE. + + If you want use custom hive table, feel free to modify the script. + ``` + +Run(no matter source type): +``` +/work/openmldb/bin/openmldb --zk_cluster=127.0.0.1:2181 --zk_root_path=/openmldb --role=sql_client --interactive=false < offline_test.sql +``` + +Check: + +You should check data in dst path, get the right row count, e.g. 10*1000=10000, but csv files have header, so one file has one more line. So you should run: +``` +wc -l /tmp/openmldb_testout/t1_deep/*.csv | grep -v total | awk '{sum+=$1-1}END{print sum}' +wc -l /tmp/openmldb_testout/t1_soft/*.csv | grep -v total | awk '{sum+=$1-1}END{print sum}' +wc -l /tmp/openmldb_testout/t2_deep/*.csv | grep -v total | awk '{sum+=$1-1}END{print sum}' +# _double should be 2*total_row_count +wc -l /tmp/openmldb_testout/t1_double/*.csv | grep -v total | awk '{sum+=$1-1}END{print sum}' +wc -l /tmp/openmldb_testout/t2_double/*.csv | grep -v total | awk '{sum+=$1-1}END{print sum}' +``` + +HDFS could use ```hdfs dfs -cat | wc -l``` to get total rows with header rows, and use `hdfs dfs -ls` to check file count. Or download the files to local and check. + +HIVE could use `select count(*) from .` in HIVE Cli to get total rows. + +We use offline engine(Spark) to load online data, so online data load is tested here. Check the `Row` in the result of script(`show table status`) to see if the row count is right. + +## udf + +Setup: +- download libtest_udf.so, src is `src/examples/test_udf.cc` +``` +curl -SLO https://openmldb.ai/download/testing/libtest_udf.so +``` +- copy libtest_udf.so to the right place + - demo onebox + ```shell + cp libtest_udf.so /tmp/openmldb/tablet-1/udf + cp libtest_udf.so /tmp/openmldb/tablet-2/udf + cp libtest_udf.so /work/openmldb/taskmanager/bin/udf + ``` + - distributed cluster, copy or download to all TabletServer `udf/` and all TaskManager `taskmanager/bin/udf/` manually + +Run: +``` +/work/openmldb/bin/openmldb --zk_cluster=127.0.0.1:2181 --zk_root_path=/openmldb --role=sql_client --interactive=false < udf_test.sql +``` diff --git a/demo/usability_testing/data_mocker.py b/demo/usability_testing/data_mocker.py new file mode 100644 index 00000000000..f873daec9dc --- /dev/null +++ b/demo/usability_testing/data_mocker.py @@ -0,0 +1,167 @@ +from faker import Faker +import re +import os +import click +import csv +from typing import Optional +import numpy as np +import pandas as pd + + +# to support save csv, and faster parquet, we don't use faker-cli directly +# but the design is similar, thanks for the author +fake = Faker() +def fake_write(writer, num_rows, col_types): + for i in range(num_rows): + row = [ fake.format(ctype[0], **ctype[1]) for ctype in col_types ] + writer.write(row) + +class Writer: + def __init__(self, output, headers, filename: Optional[str] = None): + self.output = output + self.headers = headers + self.writer = None + + def write(self, row): + pass + + def close(self): + pass + + +class CSVWriter(Writer): + def __init__(self, output, headers, filename): + super().__init__(output, headers) + self.writer = csv.writer(self.output) + self.write(headers) + + def write(self, row): + self.writer.writerow(row) + +class ParquetWriter(Writer): + def __init__(self, output, headers, filename, types): + super().__init__(output, headers) + self.filename = filename + self.append_dicts = {} + # sql types to dtype + DTYPES = { + 'smallint': "int16", + "int": "int32", + 'string':'str', + 'bigint': "int64", + "date": "datetime64[ns]", + "timestamp": "datetime64[ns]", + "float":"float32", + 'double':"float64", + } + self.append_rows = [] + self.types = types + self.dtype = [(k, v) for k,v in zip(self.headers, [DTYPES[t] if t in DTYPES else t for t in types])] + # print(self.dtype) + + def write(self, row): + """concat_tables is slow, so we store rows in cache""" + self.append_rows.append(tuple(row)) + + def close(self): + print('write data to file') + # print(self.append_rows) + data = np.array(self.append_rows, dtype=self.dtype) + df = pd.DataFrame(data) + # speical for date type + for c, t in zip(self.headers, self.types): + if t == 'date': + df[c] = df[c].dt.date + # print(df) + # engine can't use fastparquet when use_deprecated_int96_timestamps + # df.to_parquet(path, times="int96") + # Which forwards the kwarg **{"times": "int96"} into fastparquet.writer.write(). + df.to_parquet(self.filename, use_deprecated_int96_timestamps=True) + +# TODO faster parquet faker? gen one column(x rows) in one time + +KLAS_MAPPER = { + "csv": CSVWriter, + # "json": JSONWriter, + "parquet": ParquetWriter, + # "deltalake": DeltaLakeWriter +} + +def fake_file(num_rows, fmt, output, columns, fake_types): + print(f'generate {output}') + # columns [[c,t], [c,t,...]] + headers = [ c[0] for c in columns ] + if fmt == 'csv': + with open(output, mode='w', newline='') as file: + writer = KLAS_MAPPER.get(fmt)(file, headers, output) + fake_write(writer, num_rows, fake_types) + elif fmt == 'parquet': + import sys + writer = ParquetWriter(sys.stdout, headers, output, [c[1] for c in columns]) + fake_write(writer, num_rows, fake_types) + else: + assert False, f"{fmt} unsupported" + writer.close() + + +@click.command() +@click.option("--num-files", "-nf", default=1, help="Number of files") +@click.option("--num-rows", "-n", default=1, help="Number of rows per file") +@click.option("--fmt", "-f", type=click.Choice(["csv", "json", "parquet"]), default="parquet", help="Format of the output") +@click.option("--output", "-o", type=click.Path(writable=True), help='output dir') +@click.option("--sql", "-s", help="create table sql/table schema part", default=None) +def main(num_files, num_rows, fmt, output, sql): + if fmt in ['parquet'] and output is None: + raise click.BadArgumentUsage("parquet formats requires --output/-o filename parameter.") + + # openmldb create table may has some problem, cut to simple style + create_table_sql = sql # 'CREATE TABLE users (id INTEGER NOT NULL, name VARCHAR, INDEX(foo=bar)) OPTIONS (type="kv")' + regex = r'CREATE TABLE (\w+) ?\((.*?)\)' # no options + match = re.search(regex, create_table_sql, flags= re.IGNORECASE) + if not match: + cols = sql + else: + # columns, [index] + table = match.group(1) + cols_idx = match.group(2) + cols = re.sub(r',[ *]INDEX\((.*)', '', cols_idx, flags= re.IGNORECASE) + # parse schema from cols is enough, just use item[0]&[1] name&type + cols = [ [c[0], c[1].lower()] for c in (c.strip().split(' ') for c in cols.split(',')) ] + print(cols) + # sql types to faker provider + def type_converter(sql_type): + ranges = { + "int32": {"min_value": -2**31, "max_value": 2**31-1}, + "int64": {"min_value": -2**63, "max_value": 2**63-1}, + "int16": {"min_value": -2**15, "max_value": 2**15-1}, + + "float": {}, + "double": {}, + } + if sql_type.startswith('int') or sql_type in ['bigint', 'smallint']: + if sql_type == 'bigint': sql_type = 'int64' + if sql_type == 'smallint': sql_type = 'int16' + if sql_type == 'int': sql_type = 'int32' + return 'pyint', ranges[sql_type] + if sql_type in ['varchar', 'string']: + # TODO(hw): set max length + return 'pystr', {} + if sql_type in ['date', 'timestamp']: + return 'iso8601', {} + if sql_type in ['float', 'double']: + return 'pyfloat', ranges[sql_type] + return 'py' + sql_type, {} + + types = [ (type_converter(c[1])) for c in cols ] + print(types) + os.makedirs(output, exist_ok=True) + + import time + start = time.time() + for i in range(num_files): + fake_file(num_rows, fmt, f'{output}/{int(start)}-{i}.{fmt}', cols, types) + elap = time.time() - start + print(f"elap {elap/60}min") + +if __name__ == '__main__': + main() diff --git a/demo/usability_testing/demo_setup.sh b/demo/usability_testing/demo_setup.sh new file mode 100644 index 00000000000..f65d322d0a6 --- /dev/null +++ b/demo/usability_testing/demo_setup.sh @@ -0,0 +1,9 @@ +#!/bin/bash +set -ex +pip3 install faker pandas numpy click pyarrow +echo "mock datat t1,t2" +python3 data_mocker.py -s "create table t1(c1 int, c2 string)" -o /tmp/openmldb_test/t1/ -nf 10 -n 1000 -f csv +python3 data_mocker.py -s "create table t1(c1 int, c2 string)" -o /tmp/openmldb_test/t2/ -nf 10 -n 1000 +echo "setup offline test script" +sed "s##file:///tmp/openmldb_test#" offline_test.sql.template > offline_test.sql +sed -i'' "s##file:///tmp/openmldb_testout#" offline_test.sql diff --git a/demo/usability_testing/demo_test.sh b/demo/usability_testing/demo_test.sh new file mode 100644 index 00000000000..f706d7f05bb --- /dev/null +++ b/demo/usability_testing/demo_test.sh @@ -0,0 +1,26 @@ +#!/bin/bash +set -ex + +echo "start demo test" +echo "simple test" +/work/openmldb/bin/openmldb --zk_cluster=127.0.0.1:2181 --zk_root_path=/openmldb --role=sql_client --interactive=false < simple_test.sql + +echo "offline test with mocked data" +/work/openmldb/bin/openmldb --zk_cluster=127.0.0.1:2181 --zk_root_path=/openmldb --role=sql_client --interactive=false < offline_test.sql +OUT1=$(wc -l /tmp/openmldb_testout/t1_deep/*.csv | grep -v total | awk '{sum+=$1-1}END{print sum}') +OUT2=$(wc -l /tmp/openmldb_testout/t1_soft/*.csv | grep -v total | awk '{sum+=$1-1}END{print sum}') +OUT3=$(wc -l /tmp/openmldb_testout/t2_deep/*.csv | grep -v total | awk '{sum+=$1-1}END{print sum}') +# _double should be 2*total_row_count +OUT4=$(wc -l /tmp/openmldb_testout/t1_double/*.csv | grep -v total | awk '{sum+=$1-1}END{print sum}') +OUT5=$(wc -l /tmp/openmldb_testout/t2_double/*.csv | grep -v total | awk '{sum+=$1-1}END{print sum}') +if [ "$OUT1,$OUT2,$OUT3,$OUT4,$OUT5" != "10000,10000,10000,20000,20000" ]; then + echo "offline test failed, $OUT1,$OUT2,$OUT3,$OUT4,$OUT5" + exit 1 +fi + +echo "udf test" +curl -SLO https://openmldb.ai/download/testing/libtest_udf.so +cp libtest_udf.so /tmp/openmldb/tablet-1/udf +cp libtest_udf.so /tmp/openmldb/tablet-2/udf +cp libtest_udf.so /work/openmldb/taskmanager/bin/udf +/work/openmldb/bin/openmldb --zk_cluster=127.0.0.1:2181 --zk_root_path=/openmldb --role=sql_client --interactive=false < udf_test.sql diff --git a/demo/usability_testing/offline_test.sql.template b/demo/usability_testing/offline_test.sql.template new file mode 100644 index 00000000000..c0e70c6baa6 --- /dev/null +++ b/demo/usability_testing/offline_test.sql.template @@ -0,0 +1,39 @@ +create database if not exists offline_test; +use offline_test; +drop table if exists t1; +create table t1(c1 int, c2 string); +/* offline load test, still use getDeepCopy when drop soft, so it'll get ERROR Get unsupported file path, fix later */ +set @@execute_mode='offline'; +set @@sync_job=true; +-- deep copy +load data infile '/t1' into table t1; +set @@sync_job=false; +select * from t1; +set @@sync_job=true; +select * from t1 into outfile '/t1_deep' options(mode='overwrite'); +-- rewrite offline storage, soft csv +load data infile '/t1' into table t1 options(deep_copy=false, mode='overwrite'); +select * from t1 into outfile '/t1_soft' options(mode='overwrite'); +-- add a soft path, csv too +alter table t1 add offline_path '/t1_deep'; +select * from t1 into outfile '/t1_double' options(mode='overwrite'); + +drop table if exists t2; +create table t2 like parquet '/t2'; +set @@sync_job=true; +load data infile '/t2' into table t2 options(format='parquet'); +select * from t2 into outfile '/t2_deep' options(mode='overwrite'); +alter table t2 add offline_path '/t2'; +select * from t2 into outfile '/t2_double' options(mode='overwrite'); + +/* online load test */ +set @@execute_mode='online'; +load data infile '/t1' into table t1 options(mode='append'); +show table status; +load data infile '/t1' into table t1 options(mode='append'); +show table status; + +/*check outfile later, so the table can be droped, but Get unsupported file path in 0.8.2 onebox, will be fixed in new version*/ +drop table t1; +drop table t2; +drop database offline_test; diff --git a/demo/usability_testing/simple_test.sql b/demo/usability_testing/simple_test.sql new file mode 100644 index 00000000000..310482fba86 --- /dev/null +++ b/demo/usability_testing/simple_test.sql @@ -0,0 +1,28 @@ +create database if not exists simple_test; +use simple_test; +drop table if exists t1; +create table t1(c1 int, c2 string); +set @@execute_mode='online'; +insert into t1 values (1, 'a'),(2,'b'); +select * from t1; + +/*test deploy and drop*/ +deploy d1 select * from t1; +drop deployment d1; +/*async op, drop later*/ +create index new_idx on t1(c2) options (ttl_type=absolute, ttl=30d); + +/*empty select in offline, just to test running jobs*/ +use simple_test; +set @@execute_mode='offline'; +select * from t1; +set @@sync_job=true; +select * from t1; + +show jobs; +show components; +show jobs from nameserver; +/*after a little, index should be added, drop is sync*/ +drop index t1.new_idx; +drop table t1; +drop database simple_test; diff --git a/demo/usability_testing/udf_test.sql b/demo/usability_testing/udf_test.sql new file mode 100644 index 00000000000..66ec9db7c9d --- /dev/null +++ b/demo/usability_testing/udf_test.sql @@ -0,0 +1,17 @@ +create database udf_test; +use udf_test; +create table t1(c1 int, c2 string); +create function cut2(x STRING) RETURNS STRING OPTIONS (FILE='libtest_udf.so'); +create aggregate function special_sum(x BIGINT) RETURNS BIGINT OPTIONS (FILE='libtest_udf.so'); +create aggregate function third(x BIGINT) RETURNS BIGINT OPTIONS (FILE='libtest_udf.so', ARG_NULLABLE=true, RETURN_NULLABLE=true); +/* only test cut2 */ +set @@execute_mode='offline'; +select cut2(c2) from t1; +set @@execute_mode='online'; +insert into t1 values (1, 'abcd'),(2,'efgh'); +select cut2(c2) from t1; +drop function cut2; +drop function special_sum; +drop function third; +drop table t1; +drop database udf_test; From e2dd0a581ed211056cb764d10c8dcff0007f50e3 Mon Sep 17 00:00:00 2001 From: aceforeverd Date: Sat, 12 Aug 2023 14:21:49 +0800 Subject: [PATCH 09/63] feat!: rm monitoring source (#3435) move to https://github.com/4paradigm/openmldb-exporter --- .github/workflows/monitoring.yml | 84 - monitoring/.env | 2 - monitoring/.gitignore | 2 - monitoring/README.md | 150 +- monitoring/docker-compose.yml | 132 - monitoring/openmldb_exporter/__init__.py | 8 - .../openmldb_exporter/collector/__init__.py | 45 - .../openmldb_exporter/collector/collectors.py | 176 - .../collector/configstore.py | 73 - .../openmldb_exporter/collector/metrics.py | 118 - monitoring/openmldb_exporter/exporter.py | 73 - monitoring/openmldb_mixin/.gitignore | 3 - monitoring/openmldb_mixin/Makefile | 23 - monitoring/openmldb_mixin/README.md | 29 - monitoring/openmldb_mixin/mixin.libsonnet | 5 - .../openmldb_mixin/openmldb_dashboard.json | 4015 ----------------- .../openmldb_mixin/prometheus_example.yml | 53 - monitoring/poetry.lock | 1231 ----- monitoring/prod.Dockerfile | 10 - monitoring/prod.env | 2 - monitoring/pyproject.toml | 39 - monitoring/res/prometheus.yml | 46 - monitoring/src.Dockerfile | 16 - monitoring/test.Dockerfile | 7 - monitoring/tests/conftest.py | 42 - monitoring/tests/test_exporter.py | 204 - monitoring/tests/test_prometheus.py | 15 - 27 files changed, 2 insertions(+), 6601 deletions(-) delete mode 100644 .github/workflows/monitoring.yml delete mode 100644 monitoring/.env delete mode 100644 monitoring/.gitignore delete mode 100644 monitoring/docker-compose.yml delete mode 100644 monitoring/openmldb_exporter/__init__.py delete mode 100644 monitoring/openmldb_exporter/collector/__init__.py delete mode 100644 monitoring/openmldb_exporter/collector/collectors.py delete mode 100644 monitoring/openmldb_exporter/collector/configstore.py delete mode 100644 monitoring/openmldb_exporter/collector/metrics.py delete mode 100644 monitoring/openmldb_exporter/exporter.py delete mode 100644 monitoring/openmldb_mixin/.gitignore delete mode 100644 monitoring/openmldb_mixin/Makefile delete mode 100644 monitoring/openmldb_mixin/README.md delete mode 100644 monitoring/openmldb_mixin/mixin.libsonnet delete mode 100644 monitoring/openmldb_mixin/openmldb_dashboard.json delete mode 100644 monitoring/openmldb_mixin/prometheus_example.yml delete mode 100644 monitoring/poetry.lock delete mode 100644 monitoring/prod.Dockerfile delete mode 100644 monitoring/prod.env delete mode 100644 monitoring/pyproject.toml delete mode 100644 monitoring/res/prometheus.yml delete mode 100644 monitoring/src.Dockerfile delete mode 100644 monitoring/test.Dockerfile delete mode 100644 monitoring/tests/conftest.py delete mode 100644 monitoring/tests/test_exporter.py delete mode 100644 monitoring/tests/test_prometheus.py diff --git a/.github/workflows/monitoring.yml b/.github/workflows/monitoring.yml deleted file mode 100644 index 1df2e89c0b8..00000000000 --- a/.github/workflows/monitoring.yml +++ /dev/null @@ -1,84 +0,0 @@ -name: monitoring integration test - -on: - push: - branches: - - main - paths: - - '.github/workflows/monitoring.yml' - - 'monitoring/**' - pull_request: - paths: - - '.github/workflows/monitoring.yml' - - 'monitoring/**' - workflow_dispatch: - -permissions: - checks: write - pull-requests: write - -jobs: - src-test: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v3 - - - name: Set up QEMU - uses: docker/setup-qemu-action@v2 - - - name: Setup Docker Buildx - uses: docker/setup-buildx-action@v2 - - - name: service up - working-directory: monitoring - run: | - docker compose --file docker-compose.yml up -d grafana - - - name: verbose services - working-directory: monitoring - # ensure all services is up - run: | - sleep 60 - docker compose --file docker-compose.yml ps -a - - - name: verbose exporter log - working-directory: monitoring - run: | - docker compose --file docker-compose.yml logs openmldb-exporter - - - name: run tests - working-directory: monitoring - run: | - docker compose --file docker-compose.yml up --no-recreate --exit-code-from testing testing - - - name: Publish Test Results - uses: EnricoMi/publish-unit-test-result-action@v2 - if: always() - with: - check_name: Monitoring integration test - comment_title: Monitoring integration test - files: | - ./monitoring/pytest.xml - - prod-test: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v3 - - - name: Set up QEMU - uses: docker/setup-qemu-action@v2 - - - name: Setup Docker Buildx - uses: docker/setup-buildx-action@v2 - - - name: service up - working-directory: monitoring - run: | - docker compose --file docker-compose.yml --env-file prod.env up -d grafana - - - name: run tests - working-directory: monitoring - run: | - docker compose --file docker-compose.yml --env-file prod.env up --no-recreate --exit-code-from testing testing diff --git a/monitoring/.env b/monitoring/.env deleted file mode 100644 index 69a6d7baaec..00000000000 --- a/monitoring/.env +++ /dev/null @@ -1,2 +0,0 @@ -OPENMLDB_VERSION=0.8.0 -ENV_TYPE=src diff --git a/monitoring/.gitignore b/monitoring/.gitignore deleted file mode 100644 index c6abda4d01b..00000000000 --- a/monitoring/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -dist/ -**/__pycache__/ diff --git a/monitoring/README.md b/monitoring/README.md index 88aa0a58751..b0c11e7f2f2 100644 --- a/monitoring/README.md +++ b/monitoring/README.md @@ -1,148 +1,2 @@ -# OpenMLDB Prometheus Exporter - -[![PyPI](https://img.shields.io/pypi/v/openmldb-exporter?label=openmldb-exporter)](https://pypi.org/project/openmldb-exporter/) -![PyPI - Python Version](https://img.shields.io/pypi/pyversions/openmldb-exporter) - -## Intro - -This directory contains - -1. OpenMLDB Exporter exposing prometheus metrics -2. OpenMLDB mixin provides well-configured examples for prometheus server and grafana dashboard - -## Requirements - -- A runnable OpenMLDB instance that is accessible from your network -- OpenMLDB version >= 0.5.0 -- Python >= 3.8 - - -## Setup - -For those want to try, simply install from pip, it will install the latest release: - -```bash -pip install openmldb-exporter -``` - -Then type - -```sh -openmldb-exporter -h -``` - -To see which flags should provided. - -Developers may refer [Development](#development) for how to setup openmldb exporter from source code. - - -## Development - -### Extra Requirements - -- Same in [Requirements](#requirements) -- [poetry](https://github.com/python-poetry/poetry) as build tool -- cmake (optional if want to test latest commit) - -### Build python SDK (Optional) - -openmldb exporter depends on [openmldb python SDK](https://pypi.org/project/openmldb/). For those introducing changes in python SDK or native code as well, this steps is required in order to take the latest Python SDK locally instead of the published one. - -#### 1. Build native code - -`cd` to root directory of OpenMLDB, and run: - -```bash -make SQL_PYSDK_ENABLE=ON -``` - -This will generate compiled shared library in `python` source directory. - -#### 2. Fix dependency list - -Back to openmldb exporter directory, modify `pyproject.toml` and make content like below: - -```toml -[tool.poetry.dependencies] -# ... -# openmldb = "^0.6.0" -# uncomment below to use openmldb sdk built from source -# set develop = true so changes in python will take effect immediately -openmldb = { path = "../python/", develop = true } -``` - -### Run - -1. Setup python dependencies: - - ```bash - poetry install - ``` - -2. Start openmldb exporter - - ```bash - poetry run openmldb-exporter - ``` - - You need pass necessary flags after `openmldb-exporter`. Run `poetry run openmldb-exporter --help` to get the help info - - ```bash - usage: openmldb-exporter [-h] [--log.level LOG.LEVEL] [--web.listen-address WEB.LISTEN_ADDRESS] - [--web.telemetry-path WEB.TELEMETRY_PATH] [--config.zk_root CONFIG.ZK_ROOT] - [--config.zk_path CONFIG.ZK_PATH] [--config.interval CONFIG.INTERVAL] - - OpenMLDB exporter - - optional arguments: - -h, --help show this help message and exit - --log.level LOG.LEVEL - config log level, default WARN - --web.listen-address WEB.LISTEN_ADDRESS - process listen port, default 8000 - --web.telemetry-path WEB.TELEMETRY_PATH - Path under which to expose metrics, default metrics - --config.zk_root CONFIG.ZK_ROOT - endpoint to zookeeper, default 127.0.0.1:6181 - --config.zk_path CONFIG.ZK_PATH - root path in zookeeper for OpenMLDB, default / - --config.interval CONFIG.INTERVAL - interval in seconds to pull metrics periodically, default 30.0 - ``` - -3. View the available metrics, you can pull through `curl` - - ```bash - curl http://127.0.0.1:8000/metrics - ``` - - A example output: - - ```bash - # HELP openmldb_connected_seconds_total duration for a component conncted time in seconds - # TYPE openmldb_connected_seconds_total counter - openmldb_connected_seconds_total{endpoint="172.17.0.15:9520",role="tablet"} 208834.70900011063 - openmldb_connected_seconds_total{endpoint="172.17.0.15:9521",role="tablet"} 208834.70700001717 - openmldb_connected_seconds_total{endpoint="172.17.0.15:9522",role="tablet"} 208834.71399998665 - openmldb_connected_seconds_total{endpoint="172.17.0.15:9622",role="nameserver"} 208833.70000004768 - openmldb_connected_seconds_total{endpoint="172.17.0.15:9623",role="nameserver"} 208831.70900011063 - openmldb_connected_seconds_total{endpoint="172.17.0.15:9624",role="nameserver"} 208829.7230000496 - # HELP openmldb_connected_seconds_created duration for a component conncted time in seconds - # TYPE openmldb_connected_seconds_created gauge - openmldb_connected_seconds_created{endpoint="172.17.0.15:9520",role="tablet"} 1.6501813860467942e+09 - openmldb_connected_seconds_created{endpoint="172.17.0.15:9521",role="tablet"} 1.6501813860495396e+09 - openmldb_connected_seconds_created{endpoint="172.17.0.15:9522",role="tablet"} 1.650181386050323e+09 - openmldb_connected_seconds_created{endpoint="172.17.0.15:9622",role="nameserver"} 1.6501813860512116e+09 - openmldb_connected_seconds_created{endpoint="172.17.0.15:9623",role="nameserver"} 1.650181386051238e+09 - openmldb_connected_seconds_created{endpoint="172.17.0.15:9624",role="nameserver"} 1.6501813860512598e+09 - ``` - -## Release History - -- 0.7.1 - * Features - - Upgrade OpenMLDB SDK to v0.7 - - Upgrade prometheus client to 0.16 -- 0.6.0 - * Features - - Depends on OpenMLDB SDK v0.6 +Moved to [openmldb-exporter](https://github.com/4paradigm/openmldb-exporter) +---- diff --git a/monitoring/docker-compose.yml b/monitoring/docker-compose.yml deleted file mode 100644 index 7159856e998..00000000000 --- a/monitoring/docker-compose.yml +++ /dev/null @@ -1,132 +0,0 @@ -version: "3.8" - -services: - testing: - container_name: openmldb_test_client - build: - context: . - dockerfile: test.Dockerfile - volumes: - - ./:/usr/src/openmldb-exporter - command: - - bash - - -ce - - | - poetry install - poetry run pytest --junit-xml=./pytest.xml - depends_on: - - grafana - - grafana: - container_name: openmldb_grafana - image: grafana/grafana-oss:8.3.11 - ports: - - 3000 - depends_on: - - prometheus - - prometheus: - container_name: openmldb_prometheus - image: prom/prometheus:v2.43.0 - ports: - - 9090 - volumes: - - ./res/prometheus.yml:/etc/prometheus/prometheus.yml - depends_on: - - openmldb-exporter - - openmldb-api - - openmldb-exporter: - container_name: openmldb_exporter - build: - context: . - dockerfile: ${ENV_TYPE}.Dockerfile - ports: - - 8000 - command: - - "--config.zk_root=openmldb-zk:2181" - - "--config.zk_path=/openmldb" - restart: on-failure - depends_on: - - openmldb-ns1 - - openmldb-ns2 - - openmldb-api: - container_name: openmldb_api - image: ghcr.io/aceforeverd/openmldb-server:${OPENMLDB_VERSION} - restart: on-failure - command: - - "--role=apiserver" - - "--endpoint=openmldb-api:9527" - - "--zk_cluster=openmldb-zk:2181" - - "--zk_root_path=/openmldb" - ports: - - 9527 - depends_on: - - openmldb-ns1 - - openmldb-ns2 - - openmldb-zk: - container_name: openmldb_zk - image: zookeeper:3.4.14 - ports: - - 2181 - - openmldb-ns1: - container_name: openmldb_ns1 - image: ghcr.io/aceforeverd/openmldb-server:${OPENMLDB_VERSION} - command: - - "--role=nameserver" - - "--endpoint=openmldb-ns1:9527" - - "--zk_cluster=openmldb-zk:2181" - - "--zk_root_path=/openmldb" - depends_on: - - openmldb-tablet1 - - openmldb-tablet2 - - openmldb-tablet3 - - openmldb-ns2: - container_name: openmldb_ns2 - image: ghcr.io/aceforeverd/openmldb-server:${OPENMLDB_VERSION} - command: - - "--role=nameserver" - - "--endpoint=openmldb-ns2:9527" - - "--zk_cluster=openmldb-zk:2181" - - "--zk_root_path=/openmldb" - depends_on: - - openmldb-tablet1 - - openmldb-tablet2 - - openmldb-tablet3 - - openmldb-tablet1: - container_name: openmldb_tablet1 - image: ghcr.io/aceforeverd/openmldb-server:${OPENMLDB_VERSION} - command: - - "--role=tablet" - - "--endpoint=openmldb-tablet1:9527" - - "--zk_cluster=openmldb-zk:2181" - - "--zk_root_path=/openmldb" - depends_on: - - openmldb-zk - - openmldb-tablet2: - container_name: openmldb_tablet2 - image: ghcr.io/aceforeverd/openmldb-server:${OPENMLDB_VERSION} - command: - - "--role=tablet" - - "--endpoint=openmldb-tablet2:9527" - - "--zk_cluster=openmldb-zk:2181" - - "--zk_root_path=/openmldb" - depends_on: - - openmldb-zk - - openmldb-tablet3: - container_name: openmldb_tablet3 - image: ghcr.io/aceforeverd/openmldb-server:${OPENMLDB_VERSION} - command: - - "--role=tablet" - - "--endpoint=openmldb-tablet3:9527" - - "--zk_cluster=openmldb-zk:2181" - - "--zk_root_path=/openmldb" - depends_on: - - openmldb-zk diff --git a/monitoring/openmldb_exporter/__init__.py b/monitoring/openmldb_exporter/__init__.py deleted file mode 100644 index a8aa5c95c92..00000000000 --- a/monitoring/openmldb_exporter/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -""" -module openmldb_exporter -""" - -from importlib.metadata import version - -__version__ = version(__name__) - diff --git a/monitoring/openmldb_exporter/collector/__init__.py b/monitoring/openmldb_exporter/collector/__init__.py deleted file mode 100644 index 0b689c8796c..00000000000 --- a/monitoring/openmldb_exporter/collector/__init__.py +++ /dev/null @@ -1,45 +0,0 @@ -""" -module collector -""" -from openmldb_exporter.collector.metrics import ( - connected_seconds, - component_status, - table_rows, - table_partitions, - table_partitions_unalive, - table_replica, - table_disk, - table_memory, - deploy_response_time, - tablet_memory_application, - tablet_memory_actual, -) -from openmldb_exporter.collector.configstore import ( - ConfigStore -) - -from openmldb_exporter.collector.collectors import ( - TableStatusCollector, - DeployQueryStatCollector, - ComponentStatusCollector, - Collector, -) - -__all__ = [ - "connected_seconds", - "component_status", - "table_rows", - "table_partitions", - "table_partitions_unalive", - "table_replica", - "table_disk", - "table_memory", - "deploy_response_time", - "tablet_memory_application", - "tablet_memory_actual", - "ConfigStore", - "TableStatusCollector", - "DeployQueryStatCollector", - "ComponentStatusCollector", - "Collector", -] diff --git a/monitoring/openmldb_exporter/collector/collectors.py b/monitoring/openmldb_exporter/collector/collectors.py deleted file mode 100644 index 72e4b471dba..00000000000 --- a/monitoring/openmldb_exporter/collector/collectors.py +++ /dev/null @@ -1,176 +0,0 @@ -# Copyright 2022 4Paradigm -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -""" -Collector definations -""" - -from abc import ABC, abstractmethod -from sqlalchemy import (engine, Table, Column, MetaData, String, Integer) -from openmldb_exporter.collector import (connected_seconds, component_status, table_rows, table_partitions, - table_partitions_unalive, table_memory, table_disk, table_replica, deploy_response_time, - tablet_memory_application, tablet_memory_actual) -from urllib import request -import time - -import logging - - -class Collector(ABC): - ''' - ABC for OpenMLDB prometheus collectors - ''' - - @abstractmethod - def collect(self): - ''' - define how to collect and save metric values - ''' - pass - - -class SDKConnectable(object): - ''' - base class that hold a OpenMLDB connection through python sdk - ''' - _conn: engine.Connection - - def __init__(self, conn: engine.Connection): - self._conn = conn - - -class TableStatusCollector(Collector, SDKConnectable): - ''' - table statistics metric collector - ''' - - def collect(self): - rs = self._conn.execute("SHOW TABLE STATUS") - rows = rs.fetchall() - for row in rows: - logging.debug(row) - - # TODO: use storage_type - tid, tb_name, db_name, storage_type, rows, mem, disk, partition, partition_unalive, replica, *_ = row - tb_path = f"{db_name}_{tb_name}" - tid = int(tid) - table_rows.labels(tb_path, tid, storage_type).set(int(rows)) - table_partitions.labels(tb_path, tid, storage_type).set(int(partition)) - table_partitions_unalive.labels(tb_path, tid, storage_type).set(int(partition_unalive)) - table_replica.labels(tb_path, tid, storage_type).set(int(replica)) - table_memory.labels(tb_path, tid, storage_type).set(int(mem)) - table_disk.labels(tb_path, tid, storage_type).set(int(disk)) - - -class DeployQueryStatCollector(Collector, SDKConnectable): - ''' - deploy query statistics collector - ''' - _metadata: MetaData - _deploy_response_time: Table - - def __init__(self, conn: engine.Connection): - super().__init__(conn) - self._init_table_info() - - def collect(self): - rs = self._conn.execute(self._deploy_response_time.select()) - row = rs.fetchone() - acc_map = {} - while row is not None: - logging.debug(row) - - dp_name, time_second, count, total = row - time_second = float(time_second) - - # update bucket count - for i, bound in enumerate(deploy_response_time._upper_bounds): - if time_second <= bound: - # FIXME: handle Histogram reset correctly - deploy_response_time.labels(dp_name)._buckets[i].set(int(count)) - break - # update sum for each deploy - if dp_name in acc_map: - acc_map[dp_name] += float(total) - else: - acc_map[dp_name] = float(total) - row = rs.fetchone() - - # write sums - for key,value in acc_map.items(): - deploy_response_time.labels(key)._sum.set(value) - - def _init_table_info(self): - # sql parser do not recognize quoted string - self._metadata = MetaData(schema="INFORMATION_SCHEMA", bind=self._conn, quote_schema=False) - self._deploy_response_time = Table( - "DEPLOY_RESPONSE_TIME", - self._metadata, - Column("DEPLOY_NAME", String, quote=False), - Column("TIME", String, quote=False), - Column("COUNT", Integer, quote=False), - Column("TOTAL", String, quote=False), - quote=False, - ) - - -class ComponentStatusCollector(Collector, SDKConnectable): - ''' - component statistics and tablet memory collector - ''' - - def collect(self): - rs = self._conn.execute("SHOW COMPONENTS") - components = rs.fetchall() - for row in components: - logging.debug(row) - - endpoint = row[0] - role = row[1] - # connected time in millisecond - connected_time = int(row[2]) - connect_duration = int(time.time()) - connected_time / 1000 - status = row[3] - # set protected member for Counter is dangerous, though it seems the only way - connected_seconds.labels(endpoint, role)._value.set(connect_duration) - component_status.labels(endpoint, role).state(status) - - # collect tablet application memory - if role == "tablet": - app, actual = self._get_mem(f"http://{endpoint}/TabletServer/ShowMemPool") - tablet_memory_application.labels(endpoint).set(app) - tablet_memory_actual.labels(endpoint).set(actual) - - - def _get_mem(self, url: str): - memory_by_application = 0 - memory_acutal_used = 0 - # http request with 1s timeout - with request.urlopen(url, timeout=1) as resp: - for i in resp: - line = i.decode().strip() - if line.rfind("use by application") > 0: - try: - memory_by_application = int(line.split()[1]) - except ValueError as e: - memory_by_application = 0 - logging.error(e) - elif line.rfind("Actual memory used") > 0: - try: - memory_acutal_used = int(line.split()[2]) - except ValueError as e: - memory_acutal_used = 0 - logging.error(e) - else: - continue - return memory_by_application, memory_acutal_used diff --git a/monitoring/openmldb_exporter/collector/configstore.py b/monitoring/openmldb_exporter/collector/configstore.py deleted file mode 100644 index 00d62e98296..00000000000 --- a/monitoring/openmldb_exporter/collector/configstore.py +++ /dev/null @@ -1,73 +0,0 @@ -# Copyright 2022 4Paradigm -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -""" -exporter configuration parse and management -""" - -import argparse -import logging - - -class ConfigStore(object): - ''' - class to init a ArgumentParser and store all exporter configurations - ''' - - _args: argparse.Namespace - log_level: str - zk_root: str - zk_path: str - listen_port: int - telemetry_path: str - pull_interval: float - - def __init__(self): - parser = argparse.ArgumentParser(description="OpenMLDB exporter") - parser.add_argument("--log.level", type=str, default="WARNING", help="config log level") - parser.add_argument("--web.listen-address", type=int, default=8000, help="process listen port") - parser.add_argument("--web.telemetry-path", - type=str, - default="metrics", - help="Path under which to expose metrics") - parser.add_argument("--config.zk_root", type=str, default="127.0.0.1:6181", help="endpoint to zookeeper") - parser.add_argument("--config.zk_path", type=str, default="/", help="root path in zookeeper for OpenMLDB") - parser.add_argument("--config.interval", - type=float, - default=30.0, - help="interval in seconds to pull metrics periodically") - self._args = parser.parse_args() - self._store_cfgs() - - def _get_cfg(self, key: str): - ''' - key fetching that handles key whose value may contains literal dot('.') - ''' - val = self._args.__dict__.get(key) - if val is None: - raise KeyError(f"value for {key} not exist") - return val - - def _store_cfgs(self): - self.log_level = self._get_cfg("log.level") - self.zk_root = self._get_cfg("config.zk_root") - self.zk_path = self._get_cfg("config.zk_path") - self.listen_port = self._get_cfg("web.listen_address") - self.telemetry_path = self._get_cfg("web.telemetry_path") - self.pull_interval = self._get_cfg("config.interval") - - def get_log_level(self): - numeric_level = getattr(logging, self.log_level.upper(), None) - if not isinstance(numeric_level, int): - raise ValueError(f"Invalid log level: {self.log_level}") - return numeric_level diff --git a/monitoring/openmldb_exporter/collector/metrics.py b/monitoring/openmldb_exporter/collector/metrics.py deleted file mode 100644 index 4b8d788f031..00000000000 --- a/monitoring/openmldb_exporter/collector/metrics.py +++ /dev/null @@ -1,118 +0,0 @@ -# Copyright 2022 4Paradigm -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -""" -metric definatitons of OpenMLDB -""" - -from prometheus_client import Counter, Gauge, Histogram -from prometheus_client.metrics import Enum -from prometheus_client.utils import INF - -NAMESPACE = "openmldb" - -# labels -ENDPOINT = "endpoint" -ROLE = "role" -TABLE_ID = "tid" -TABLE_PATH = "table_path" -DEPLOY_PATH = "deploy_path" -STORAGE_MODE = "storage_mode" - -component_lables = [ENDPOINT, ROLE] -table_lables = [TABLE_PATH, TABLE_ID, STORAGE_MODE] - -connected_seconds = Counter("connected", - "duration for a component conncted time in seconds", - component_lables, - namespace=NAMESPACE, - unit="seconds") - -component_status = Enum( - "status", - "component status", - component_lables, - states=["online", "offline"], - namespace=NAMESPACE, -) - -tablet_memory_application = Gauge( - "tablet_memory_application", - "tablet application memory usage in bytes", - [ENDPOINT], - namespace=NAMESPACE, - unit="bytes", -) - -tablet_memory_actual = Gauge( - "tablet_memory_actual_used", - "actual memory used in bytes for tablet application", - [ENDPOINT], - namespace=NAMESPACE, - unit="bytes", -) - -table_rows = Gauge( - "table_rows", - "table row count", - table_lables, - namespace=NAMESPACE, -) - -table_partitions = Gauge( - "table_partitions", - "table partition count", - table_lables, - namespace=NAMESPACE, -) - -table_partitions_unalive = Gauge( - "table_partitions_unalive", - "table partition count that is unalive", - table_lables, - namespace=NAMESPACE, -) - -table_replica = Gauge( - "table_replica", - "table replica count", - table_lables, - namespace=NAMESPACE, -) - -table_disk = Gauge( - "table_disk", - "table disk usage in bytes", - table_lables, - namespace=NAMESPACE, - unit="bytes", -) - -table_memory = Gauge( - "table_memory", - "table memory usage in bytes", - table_lables, - namespace=NAMESPACE, - unit="bytes", -) - -BUCKETS = (1 / 1000000, 1 / 100000, 1 / 10000, 1 / 1000, 1 / 100, 1 / 10, 1, 10, 100, 1000, 10000, 100000, 1000000, INF) -deploy_response_time = Histogram( - "deploy_response_time", - "Deployment query response time histogram", - [DEPLOY_PATH], - subsystem="info_schema", - namespace=NAMESPACE, - unit="seconds", - buckets=BUCKETS, -) diff --git a/monitoring/openmldb_exporter/exporter.py b/monitoring/openmldb_exporter/exporter.py deleted file mode 100644 index 62d542965e1..00000000000 --- a/monitoring/openmldb_exporter/exporter.py +++ /dev/null @@ -1,73 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- - -# Copyright 2021 4Paradigm -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -""" -main entry of openmldb prometheus exporter -""" - -import os -import sys -import logging -from typing import (Iterable) - -from openmldb_exporter.collector import (ConfigStore, Collector, TableStatusCollector, DeployQueryStatCollector, - ComponentStatusCollector) -from prometheus_client.twisted import MetricsResource -from sqlalchemy import engine -from twisted.internet import reactor, task -from twisted.web.resource import Resource -from twisted.web.server import Site - -dir_name = os.path.dirname(os.path.realpath(sys.argv[0])) - - -def collect_task(collectors: Iterable[Collector]): - for collector in collectors: - try: - logging.info("%s collecting", type(collector).__qualname__) - collector.collect() - except: - logging.exception("error in %s", type(collector).__qualname__) - - -def main(): - cfg_store = ConfigStore() - - # assuming loglevel is bound to the string value obtained from the command line argument. - log_level = cfg_store.get_log_level() - logging.basicConfig(level=log_level) - - eng = engine.create_engine(f"openmldb:///?zk={cfg_store.zk_root}&zkPath={cfg_store.zk_path}") - conn = eng.connect() - - collectors = ( - TableStatusCollector(conn), - DeployQueryStatCollector(conn), - ComponentStatusCollector(conn), - ) - - task.LoopingCall(collect_task, collectors).start(cfg_store.pull_interval) - - root = Resource() - # child path must be bytes - root.putChild(cfg_store.telemetry_path.encode(), MetricsResource()) - factory = Site(root) - reactor.listenTCP(cfg_store.listen_port, factory) - reactor.run() - - -if __name__ == "__main__": - main() diff --git a/monitoring/openmldb_mixin/.gitignore b/monitoring/openmldb_mixin/.gitignore deleted file mode 100644 index 8f1e415fe1c..00000000000 --- a/monitoring/openmldb_mixin/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/alerts.yaml -/rules.yaml -dashboards_out/ diff --git a/monitoring/openmldb_mixin/Makefile b/monitoring/openmldb_mixin/Makefile deleted file mode 100644 index f2643c2b789..00000000000 --- a/monitoring/openmldb_mixin/Makefile +++ /dev/null @@ -1,23 +0,0 @@ -JSONNET_FMT := jsonnetfmt -n 2 --max-blank-lines 2 --string-style s --comment-style s - -default: build - -all: fmt lint build clean - -fmt: - find . -name 'vendor' -prune -o -name '*.libsonnet' -print -o -name '*.jsonnet' -print | \ - xargs -n 1 -- $(JSONNET_FMT) -i - -lint: - find . -name 'vendor' -prune -o -name '*.libsonnet' -print -o -name '*.jsonnet' -print | \ - while read f; do \ - $(JSONNET_FMT) "$$f" | diff -u "$$f" -; \ - done - - mixtool lint mixin.libsonnet - -build: - mixtool generate all mixin.libsonnet - -clean: - rm -rf dashboards_out alerts.yaml rules.yaml diff --git a/monitoring/openmldb_mixin/README.md b/monitoring/openmldb_mixin/README.md deleted file mode 100644 index 840b0d2a276..00000000000 --- a/monitoring/openmldb_mixin/README.md +++ /dev/null @@ -1,29 +0,0 @@ -# OpenMLDB Mixin - -A set of configurable, reusable, and extensible alert and rules for prometheus and grafana dashboard configuration - -## Requirement - -- Golang >= 1.17 -- [mixtool](https://github.com/monitoring-mixins/mixtool) -- [jsonnetfmt](https://github.com/google/go-jsonnet) - -```bash -$ go install github.com/monitoring-mixins/mixtool/cmd/mixtool@latest -$ go install github.com/google/go-jsonnet/cmd/jsonnetfmt@latest -``` - -## Build - -```bash -make -``` - -## Use - -1. A config example for prometheus server: `prometheus_example.yml` -2. Grafana dashboard available in `dashboards_out/openmldb_dashboard.json` - -You can also import grafana dashboard from grafana.com: [OpenMLDB dashboard](https://grafana.com/grafana/dashboards/17843). - -Refer [promtheus get started](https://prometheus.io/docs/prometheus/latest/getting_started/) and [grafana get started](https://grafana.com/docs/grafana/latest/getting-started/getting-started-prometheus/) on how to configure prometheus and grafana diff --git a/monitoring/openmldb_mixin/mixin.libsonnet b/monitoring/openmldb_mixin/mixin.libsonnet deleted file mode 100644 index 96629972164..00000000000 --- a/monitoring/openmldb_mixin/mixin.libsonnet +++ /dev/null @@ -1,5 +0,0 @@ -{ - grafanaDashboards: { - 'openmldb_dashboard.json': (import 'openmldb_dashboard.json'), - }, -} diff --git a/monitoring/openmldb_mixin/openmldb_dashboard.json b/monitoring/openmldb_mixin/openmldb_dashboard.json deleted file mode 100644 index b98530514c2..00000000000 --- a/monitoring/openmldb_mixin/openmldb_dashboard.json +++ /dev/null @@ -1,4015 +0,0 @@ -{ - "__inputs": [ - { - "name": "DS_PROMETHEUS", - "label": "Prometheus", - "description": "", - "type": "datasource", - "pluginId": "prometheus", - "pluginName": "Prometheus" - } - ], - "__elements": [], - "__requires": [ - { - "type": "panel", - "id": "gauge", - "name": "Gauge", - "version": "" - }, - { - "type": "grafana", - "id": "grafana", - "name": "Grafana", - "version": "8.3.11" - }, - { - "type": "datasource", - "id": "prometheus", - "name": "Prometheus", - "version": "1.0.0" - }, - { - "type": "panel", - "id": "stat", - "name": "Stat", - "version": "" - }, - { - "type": "panel", - "id": "timeseries", - "name": "Time series", - "version": "" - } - ], - "annotations": { - "list": [ - { - "builtIn": 1, - "datasource": "${DS_PROMETHEUS}", - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "name": "Annotations & Alerts", - "target": { - "limit": 100, - "matchAny": false, - "tags": [], - "type": "dashboard" - }, - "type": "dashboard" - } - ] - }, - "description": "Grafana dashboard for https://github.com/4paradigm/OpenMLDB", - "editable": true, - "fiscalYearStartMonth": 0, - "gnetId": 17843, - "graphTooltip": 1, - "id": null, - "iteration": 1683194246663, - "links": [], - "liveNow": false, - "panels": [ - { - "collapsed": false, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 0 - }, - "id": 173, - "panels": [], - "title": "OpenMLDB Cluster Overview", - "type": "row" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "whether a component is online or offline", - "fieldConfig": { - "defaults": { - "color": { - "mode": "continuous-GrYlRd" - }, - "decimals": 0, - "mappings": [ - { - "options": { - "0": { - "color": "dark-red", - "index": 0, - "text": "OFFLINE" - }, - "1": { - "color": "dark-green", - "index": 1, - "text": "ONLINE" - } - }, - "type": "value" - } - ], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - } - ] - }, - "unit": "bool" - }, - "overrides": [] - }, - "gridPos": { - "h": 6, - "w": 12, - "x": 0, - "y": 1 - }, - "id": 61, - "links": [], - "options": { - "colorMode": "background", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "text": {}, - "textMode": "value_and_name" - }, - "pluginVersion": "8.3.11", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "openmldb_status{job=~\"$job\", openmldb_status=\"online\", endpoint=~\"$endpoint\"}", - "hide": false, - "interval": "", - "legendFormat": "{{endpoint}}-{{role}}", - "refId": "A" - } - ], - "title": "Component Status", - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "connected time in seconds for each components in the OpenMLDB cluster", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - } - ] - }, - "unit": "s" - }, - "overrides": [] - }, - "gridPos": { - "h": 6, - "w": 12, - "x": 12, - "y": 1 - }, - "id": 62, - "links": [], - "options": { - "colorMode": "value", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "pluginVersion": "8.3.11", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "openmldb_connected_seconds_total{job=~\"$job\", endpoint=~\"$endpoint\"}", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "{{endpoint}}-{{role}}", - "refId": "A" - } - ], - "title": "Component Connected Time", - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "fieldConfig": { - "defaults": { - "mappings": [], - "thresholds": { - "mode": "percentage", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "orange", - "value": 70 - }, - { - "color": "red", - "value": 85 - } - ] - }, - "unit": "bytes" - }, - "overrides": [] - }, - "gridPos": { - "h": 6, - "w": 12, - "x": 0, - "y": 7 - }, - "id": 80, - "options": { - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "showThresholdLabels": false, - "showThresholdMarkers": true - }, - "pluginVersion": "8.3.11", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "process_memory_virtual{job=~\"$component_job\", instance=~\"$endpoint\"}", - "interval": "", - "legendFormat": "{{instance}}", - "refId": "A" - } - ], - "title": "Process Virtual Memory", - "type": "gauge" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - } - ] - }, - "unit": "none" - }, - "overrides": [] - }, - "gridPos": { - "h": 6, - "w": 3, - "x": 12, - "y": 7 - }, - "id": 379, - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "pluginVersion": "8.3.11", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "sum ({__name__=~\"rpc_server_.*_openmldb_api_tablet_server_query_count\", job=~\"$component_job\"})", - "interval": "", - "legendFormat": "", - "refId": "A" - } - ], - "title": "Total Query RPC Request ", - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - } - ] - }, - "unit": "none" - }, - "overrides": [] - }, - "gridPos": { - "h": 6, - "w": 3, - "x": 15, - "y": 7 - }, - "id": 380, - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "pluginVersion": "8.3.11", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "sum ({__name__=~\"rpc_server_.*_openmldb_api_tablet_server_sub_query_count\", job=~\"$component_job\"})", - "interval": "", - "legendFormat": "", - "refId": "A" - } - ], - "title": "Total Sub Query RPC Request ", - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - } - ] - }, - "unit": "none" - }, - "overrides": [] - }, - "gridPos": { - "h": 6, - "w": 3, - "x": 18, - "y": 7 - }, - "id": 381, - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "pluginVersion": "8.3.11", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "sum ({__name__=~\"rpc_server_.*_openmldb_api_tablet_server_sqlbatch_request_query_count\", job=~\"$component_job\"})", - "interval": "", - "legendFormat": "", - "refId": "A" - } - ], - "title": "Total Batch Request Query RPC Request $endpoint", - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - } - ] - }, - "unit": "none" - }, - "overrides": [] - }, - "gridPos": { - "h": 6, - "w": 3, - "x": 21, - "y": 7 - }, - "id": 470, - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "pluginVersion": "8.3.11", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "sum ({__name__=~\"rpc_server_.*_openmldb_api_tablet_server_sub_batch_request_query_count\", job=~\"$component_job\"})", - "interval": "", - "legendFormat": "", - "refId": "A" - } - ], - "title": "Total Sub Batch Request Query RPC Request ", - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "Table Rows Count for all tables", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 5, - "w": 4, - "x": 0, - "y": 13 - }, - "id": 214, - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "pluginVersion": "8.3.11", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "sum(openmldb_table_rows{job=~\"$job\"})", - "interval": "", - "legendFormat": "", - "refId": "A" - } - ], - "title": "Table Row Count Total", - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "Table Memory Used for all tables", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - } - ] - }, - "unit": "bytes" - }, - "overrides": [] - }, - "gridPos": { - "h": 5, - "w": 4, - "x": 4, - "y": 13 - }, - "id": 252, - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "pluginVersion": "8.3.11", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "sum(openmldb_table_memory_bytes{job=~\"$job\"})", - "interval": "", - "legendFormat": "", - "refId": "A" - } - ], - "title": "Table Memory Used Total", - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "Table Disk Used for all tables", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - } - ] - }, - "unit": "bytes" - }, - "overrides": [] - }, - "gridPos": { - "h": 5, - "w": 4, - "x": 8, - "y": 13 - }, - "id": 251, - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "pluginVersion": "8.3.11", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "sum (openmldb_table_disk_bytes{job=~\"$job\"})", - "interval": "", - "legendFormat": "", - "refId": "A" - } - ], - "title": "Table Disk Used Total", - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "Deploy Query Count for all deployments", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - } - ] - }, - "unit": "none" - }, - "overrides": [] - }, - "gridPos": { - "h": 5, - "w": 3, - "x": 12, - "y": 13 - }, - "id": 250, - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "pluginVersion": "8.3.11", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "sum (openmldb_info_schema_deploy_response_time_seconds_count{job=~\"$job\"})", - "interval": "", - "legendFormat": "", - "refId": "A" - } - ], - "title": "Deploy Query Count Total", - "type": "stat" - }, - { - "collapsed": true, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 18 - }, - "id": 74, - "panels": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 3, - "w": 7, - "x": 0, - "y": 2 - }, - "id": 106, - "links": [], - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "pluginVersion": "8.3.3", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": false, - "expr": "openmldb_table_partitions{job=~\"$job\", table_path=~\"$table_path\"}", - "format": "time_series", - "instant": false, - "interval": "", - "intervalFactor": 1, - "legendFormat": "{{table_path}}-{{tid}}-{{storage_mode}}", - "refId": "A" - } - ], - "title": "Table Partition Number", - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 6, - "w": 5, - "x": 7, - "y": 2 - }, - "id": 16, - "links": [], - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "pluginVersion": "8.3.3", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "openmldb_table_replica{job=~\"$job\", table_path=~\"$table_path\"}", - "hide": false, - "interval": "", - "legendFormat": "{{table_path}}-{{tid}}-{{storage_mode}}", - "refId": "B" - } - ], - "title": "Table Replica Number", - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 6, - "w": 4, - "x": 12, - "y": 2 - }, - "id": 107, - "links": [], - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "pluginVersion": "8.3.3", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "openmldb_table_rows{job=~\"$job\", table_path=~\"$table_path\"}", - "hide": false, - "interval": "", - "legendFormat": "{{table_path}}-{{tid}}-{{storage_mode}}", - "refId": "B" - } - ], - "title": "Table Rows Count", - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - } - ] - }, - "unit": "bytes" - }, - "overrides": [] - }, - "gridPos": { - "h": 6, - "w": 4, - "x": 16, - "y": 2 - }, - "id": 108, - "links": [], - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "pluginVersion": "8.3.3", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "openmldb_table_memory_bytes{job=~\"$job\", table_path=~\"$table_path\"}", - "hide": false, - "interval": "", - "legendFormat": "{{table_path}}-{{tid}}-{{storage_mode}}", - "refId": "B" - } - ], - "title": "Table Memory Used", - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - } - ] - }, - "unit": "bytes" - }, - "overrides": [] - }, - "gridPos": { - "h": 6, - "w": 4, - "x": 20, - "y": 2 - }, - "id": 109, - "links": [], - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "pluginVersion": "8.3.3", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "openmldb_table_disk_bytes{job=~\"$job\", table_path=~\"$table_path\"}", - "hide": false, - "interval": "", - "legendFormat": "{{table_path}}-{{tid}}-{{storage_mode}}", - "refId": "B" - } - ], - "title": "Table Disk Used", - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 1 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 3, - "w": 7, - "x": 0, - "y": 5 - }, - "id": 105, - "links": [], - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "pluginVersion": "8.3.3", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "openmldb_table_partitions_unalive{job=~\"$job\", table_path=~\"$table_path\"}", - "format": "time_series", - "instant": false, - "interval": "", - "intervalFactor": 1, - "legendFormat": "{{table_path}}-{{tid}}-{{storage_mode}}", - "refId": "A" - } - ], - "title": "Table Partition Unalive Number", - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "none" - }, - "overrides": [] - }, - "gridPos": { - "h": 10, - "w": 8, - "x": 0, - "y": 8 - }, - "id": 32, - "links": [], - "options": { - "legend": { - "calcs": [ - "lastNotNull" - ], - "displayMode": "table", - "placement": "bottom" - }, - "tooltip": { - "mode": "single" - } - }, - "pluginVersion": "8.3.3", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "openmldb_table_rows{job=~\"$job\", table_path=~\"$table_path\"}", - "format": "time_series", - "hide": false, - "instant": false, - "interval": "", - "intervalFactor": 1, - "legendFormat": "{{table_path}}-{{tid}}-{{storage_mode}}", - "refId": "A" - } - ], - "title": "Table Rows", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "smooth", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "bytes" - }, - "overrides": [] - }, - "gridPos": { - "h": 10, - "w": 8, - "x": 8, - "y": 8 - }, - "id": 2, - "links": [], - "options": { - "legend": { - "calcs": [ - "lastNotNull" - ], - "displayMode": "table", - "placement": "bottom" - }, - "tooltip": { - "mode": "single" - } - }, - "pluginVersion": "8.3.3", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "openmldb_table_memory_bytes{job=~\"$job\", table_path=~\"$table_path\"}", - "format": "time_series", - "hide": false, - "instant": false, - "interval": "", - "intervalFactor": 1, - "legendFormat": "{{table_path}}-{{tid}}-{{storage_mode}}", - "refId": "A" - } - ], - "title": "Table Memory Usage", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "smooth", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "bytes" - }, - "overrides": [] - }, - "gridPos": { - "h": 10, - "w": 8, - "x": 16, - "y": 8 - }, - "id": 65, - "options": { - "legend": { - "calcs": [ - "lastNotNull" - ], - "displayMode": "table", - "placement": "bottom" - }, - "tooltip": { - "mode": "single" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "openmldb_table_disk_bytes{job=~\"$job\", table_path=~\"$table_path\"}", - "interval": "", - "legendFormat": "{{table_path}}-{{tid}}-{{storage_mode}}", - "refId": "A" - } - ], - "title": "Table Disk Usage", - "type": "timeseries" - } - ], - "repeat": "table_path", - "title": "Table Status-$table_path", - "type": "row" - }, - { - "collapsed": true, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 19 - }, - "id": 84, - "panels": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 21 - }, - "id": 93, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom" - }, - "tooltip": { - "mode": "single" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "openmldb_info_schema_deploy_response_time_seconds_count{job=~\"$job\", deploy_path=~\"$deploy_path\"}", - "interval": "", - "legendFormat": "{{deploy_path}}", - "refId": "A" - } - ], - "title": "Deploy Query Count $deploy_path", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "reqps" - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 21 - }, - "id": 620, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom" - }, - "tooltip": { - "mode": "single" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "rate(openmldb_info_schema_deploy_response_time_seconds_count{job=~\"$job\", deploy_path=~\"$deploy_path\"}[5m])", - "interval": "", - "legendFormat": "{{deploy_path}}", - "refId": "A" - } - ], - "title": "Deploy Query QPS in 5m duration $deploy_path", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "s" - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 29 - }, - "id": 692, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom" - }, - "tooltip": { - "mode": "single" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "rate(openmldb_info_schema_deploy_response_time_seconds_sum{job=~\"$job\", deploy_path=~\"$deploy_path\"}[5m]) / rate(openmldb_info_schema_deploy_response_time_seconds_count{job=~\"$job\", deploy_path=~\"$deploy_path\"}[5m])", - "interval": "", - "legendFormat": "{{deploy_path}}", - "refId": "A" - } - ], - "title": "Deploy Query AVG duration in past 5m $deploy_path", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - } - ] - }, - "unit": "s" - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "0.001" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "blue", - "mode": "fixed" - } - } - ] - } - ] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 29 - }, - "id": 676, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom" - }, - "tooltip": { - "mode": "single" - } - }, - "pluginVersion": "8.3.3", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "histogram_quantile(0.5, rate(openmldb_info_schema_deploy_response_time_seconds_bucket{job=~\"$job\", deploy_path=~\"$deploy_path\"}[5m]))", - "hide": false, - "interval": "", - "legendFormat": "0.5", - "refId": "C" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "histogram_quantile(0.9, rate(openmldb_info_schema_deploy_response_time_seconds_bucket{job=~\"$job\", deploy_path=~\"$deploy_path\"}[5m]))", - "interval": "", - "legendFormat": "0.9", - "refId": "A" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "histogram_quantile(0.95, rate(openmldb_info_schema_deploy_response_time_seconds_bucket{job=~\"$job\", deploy_path=~\"$deploy_path\"}[5m]))", - "hide": false, - "interval": "", - "legendFormat": "0.95", - "refId": "B" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "histogram_quantile(0.99, rate(openmldb_info_schema_deploy_response_time_seconds_bucket{job=~\"$job\", deploy_path=~\"$deploy_path\"}[5m]))", - "hide": false, - "interval": "", - "legendFormat": "0.99", - "refId": "D" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "histogram_quantile(0.999, rate(openmldb_info_schema_deploy_response_time_seconds_bucket{job=~\"$job\", deploy_path=~\"$deploy_path\"}[5m]))", - "hide": false, - "interval": "", - "legendFormat": "0.999", - "refId": "E" - } - ], - "title": "0.5/0.9/0.95/0.99/0.999 quantile for deploy query response time $deploy_path", - "type": "timeseries" - } - ], - "repeat": "deploy_path", - "title": "Deploy Query Response Time: $deploy_path", - "type": "row" - }, - { - "collapsed": true, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 20 - }, - "id": 76, - "panels": [ - { - "description": "Uptime for a single component, whether connected to the cluster is not considered", - "fieldConfig": { - "defaults": { - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - } - ] - }, - "unit": "s" - }, - "overrides": [] - }, - "gridPos": { - "h": 5, - "w": 6, - "x": 0, - "y": 4 - }, - "id": 78, - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "center", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "text": {}, - "textMode": "auto" - }, - "pluginVersion": "8.3.11", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "process_uptime{job=~\"$component_job\", instance=~\"$endpoint\"}", - "format": "time_series", - "instant": false, - "interval": "", - "legendFormat": "{{instance}}", - "refId": "A" - } - ], - "title": "Component Uptime $endpoint", - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "Uptime for a single component, whether connected to the cluster is not considered", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "percentage", - "steps": [ - { - "color": "green" - }, - { - "color": "#EAB839", - "value": 80 - }, - { - "color": "red", - "value": 90 - } - ] - }, - "unit": "percentunit" - }, - "overrides": [] - }, - "gridPos": { - "h": 5, - "w": 4, - "x": 6, - "y": 4 - }, - "id": 266, - "options": { - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "showThresholdLabels": false, - "showThresholdMarkers": true - }, - "pluginVersion": "8.3.11", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "process_cpu_usage{job=\"$component_job\", instance=~\"$endpoint\"}", - "format": "time_series", - "instant": false, - "interval": "", - "legendFormat": "{{instance}}", - "refId": "A" - } - ], - "title": "Process Cpu Usage $endpoint", - "type": "gauge" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - } - ] - }, - "unit": "none" - }, - "overrides": [] - }, - "gridPos": { - "h": 5, - "w": 3, - "x": 10, - "y": 4 - }, - "id": 265, - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "pluginVersion": "8.3.11", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "rpc_channel_connection_count{job=~\"$component_job\", instance=~\"$endpoint\"}", - "interval": "", - "legendFormat": "{{instance}}", - "refId": "A" - } - ], - "title": "RPC Connection Count $endpoint", - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - } - ] - }, - "unit": "none" - }, - "overrides": [] - }, - "gridPos": { - "h": 5, - "w": 3, - "x": 13, - "y": 4 - }, - "id": 679, - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "pluginVersion": "8.3.11", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": " process_thread_count{job=~\"$component_job\", instance=~\"$endpoint\"}", - "interval": "", - "legendFormat": "{{instance}}", - "refId": "A" - } - ], - "title": "Thread Count $endpoint", - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - } - ] - }, - "unit": "none" - }, - "overrides": [] - }, - "gridPos": { - "h": 5, - "w": 3, - "x": 16, - "y": 4 - }, - "id": 681, - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "pluginVersion": "8.3.11", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "process_fd_count{job=~\"$component_job\", instance=~\"$endpoint\"}", - "interval": "", - "legendFormat": "{{instance}}", - "refId": "A" - } - ], - "title": "FD Count $endpoint", - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - } - ] - }, - "unit": "none" - }, - "overrides": [] - }, - "gridPos": { - "h": 5, - "w": 3, - "x": 19, - "y": 4 - }, - "id": 680, - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "pluginVersion": "8.3.11", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "bthread_count{job=~\"$component_job\", instance=~\"$endpoint\"}", - "interval": "", - "legendFormat": "{{instance}}", - "refId": "A" - } - ], - "title": "BThread Count $endpoint", - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - } - ] - }, - "unit": "percentunit" - }, - "overrides": [] - }, - "gridPos": { - "h": 12, - "w": 6, - "x": 0, - "y": 9 - }, - "id": 267, - "links": [], - "options": { - "legend": { - "calcs": [ - "lastNotNull" - ], - "displayMode": "table", - "placement": "bottom" - }, - "tooltip": { - "mode": "single" - } - }, - "pluginVersion": "8.3.3", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "process_cpu_usage{job=~\"$component_job\", instance=~\"$endpoint\"}", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "{{instance}}-all", - "refId": "A" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "process_cpu_usage_user{job=~\"$component_job\", instance=~\"$endpoint\"}", - "hide": false, - "interval": "", - "legendFormat": "{{instance}}-user", - "refId": "B" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "process_cpu_usage_system{job=~\"$component_job\", instance=~\"$endpoint\"}", - "hide": false, - "interval": "", - "legendFormat": "{{instance}}-system", - "refId": "C" - } - ], - "title": "Cpu Usage $endpoint", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "bytes" - }, - "overrides": [] - }, - "gridPos": { - "h": 12, - "w": 6, - "x": 6, - "y": 9 - }, - "id": 97, - "options": { - "legend": { - "calcs": [ - "lastNotNull" - ], - "displayMode": "table", - "placement": "bottom" - }, - "tooltip": { - "mode": "single" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "process_memory_virtual{job=~\"$component_job\", instance=~\"$endpoint\"}", - "interval": "", - "legendFormat": "{{instance}}", - "refId": "A" - } - ], - "title": "Virtual Memory $endpoint", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "bytes read/write per second", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - } - ] - }, - "unit": "bytes" - }, - "overrides": [] - }, - "gridPos": { - "h": 12, - "w": 6, - "x": 12, - "y": 9 - }, - "id": 677, - "links": [], - "options": { - "legend": { - "calcs": [ - "lastNotNull" - ], - "displayMode": "table", - "placement": "bottom" - }, - "tooltip": { - "mode": "single" - } - }, - "pluginVersion": "8.3.3", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": " process_io_read_bytes_second{job=~\"$component_job\", instance=~\"$endpoint\"}", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "{{instance}}-read-per-second", - "refId": "A" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": " process_io_write_bytes_second{job=~\"$component_job\", instance=~\"$endpoint\"}", - "format": "time_series", - "hide": false, - "interval": "", - "intervalFactor": 1, - "legendFormat": "{{instance}}-read-per-second", - "refId": "B" - } - ], - "title": "IO $endpoint", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "bytes read/write per second", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - } - ] - }, - "unit": "bytes" - }, - "overrides": [] - }, - "gridPos": { - "h": 12, - "w": 6, - "x": 18, - "y": 9 - }, - "id": 268, - "links": [], - "options": { - "legend": { - "calcs": [ - "lastNotNull" - ], - "displayMode": "table", - "placement": "bottom" - }, - "tooltip": { - "mode": "single" - } - }, - "pluginVersion": "8.3.3", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "process_disk_read_bytes_second{job=~\"$component_job\", instance=~\"$endpoint\"}", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "{{instance}}-read-per-second", - "refId": "A" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "process_disk_write_bytes_second{job=~\"$component_job\", instance=~\"$endpoint\"}", - "hide": false, - "interval": "", - "legendFormat": "{{instance}}-write-per-second", - "refId": "B" - } - ], - "title": " Disk Usage $endpoint", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "bytes read/write per second", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - } - ] - }, - "unit": "s" - }, - "overrides": [] - }, - "gridPos": { - "h": 10, - "w": 6, - "x": 0, - "y": 21 - }, - "id": 545, - "links": [], - "options": { - "legend": { - "calcs": [ - "lastNotNull" - ], - "displayMode": "table", - "placement": "bottom" - }, - "tooltip": { - "mode": "single" - } - }, - "pluginVersion": "8.3.3", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "process_uptime{job=~\"$component_job\", instance=~\"$endpoint\"}", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "{{instance}}", - "refId": "A" - } - ], - "title": "Uptime Time $endpoint", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "bytes read/write per second", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - } - ] - }, - "unit": "s" - }, - "overrides": [] - }, - "gridPos": { - "h": 10, - "w": 6, - "x": 6, - "y": 21 - }, - "id": 269, - "links": [], - "options": { - "legend": { - "calcs": [ - "lastNotNull" - ], - "displayMode": "table", - "placement": "bottom" - }, - "tooltip": { - "mode": "single" - } - }, - "pluginVersion": "8.3.3", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "openmldb_connected_seconds_total{job=~\"$job\", endpoint=~\"$endpoint\"}", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "{{endpoint}}-{{role}}", - "refId": "A" - } - ], - "title": "Connected Time $endpoint", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - } - ] - }, - "unit": "s" - }, - "overrides": [] - }, - "gridPos": { - "h": 10, - "w": 6, - "x": 12, - "y": 21 - }, - "id": 678, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom" - }, - "tooltip": { - "mode": "single" - } - }, - "pluginVersion": "8.3.3", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": " process_io_write_second{job=~\"$component_job\", instance=~\"$endpoint\"}", - "interval": "", - "legendFormat": "{{instance}}", - "refId": "A" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": " process_io_read_second{job=~\"$component_job\", instance=~\"$endpoint\"}", - "hide": false, - "interval": "", - "legendFormat": "{{instance}}", - "refId": "B" - } - ], - "title": "Process IO READ/WRITE Second $endpoint", - "type": "timeseries" - } - ], - "repeat": "endpoint", - "title": "Component Status: $endpoint", - "type": "row" - }, - { - "collapsed": true, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 145 - }, - "id": 283, - "panels": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - } - ] - }, - "unit": "none" - }, - "overrides": [] - }, - "gridPos": { - "h": 5, - "w": 12, - "x": 0, - "y": 18 - }, - "id": 423, - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "pluginVersion": "8.3.3", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "{__name__=~\"rpc_server_.*_openmldb_api_tablet_server_query_count\", job=~\"$component_job\", instance=~\"$tablet_endpoint\"}", - "interval": "", - "legendFormat": "{{instance}}", - "refId": "A" - } - ], - "title": "Query RPC Count $tablet_endpoint", - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - } - ] - }, - "unit": "none" - }, - "overrides": [] - }, - "gridPos": { - "h": 5, - "w": 12, - "x": 12, - "y": 18 - }, - "id": 382, - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "pluginVersion": "8.3.3", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": " ({__name__=~\"rpc_server_.*_openmldb_api_tablet_server_sqlbatch_request_query_count\", job=~\"$component_job\", instance=~\"$tablet_endpoint\"})", - "interval": "", - "legendFormat": "{{instance}}", - "refId": "A" - } - ], - "title": "Batch Request Query RPC Count $tablet_endpoint", - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - } - ] - }, - "unit": "none" - }, - "overrides": [] - }, - "gridPos": { - "h": 5, - "w": 12, - "x": 0, - "y": 23 - }, - "id": 471, - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "pluginVersion": "8.3.3", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "{__name__=~\"rpc_server_.*_openmldb_api_tablet_server_sub_query_count\", job=~\"$component_job\", instance=~\"$tablet_endpoint\"}", - "interval": "", - "legendFormat": "{{instance}}", - "refId": "A" - } - ], - "title": "Sub Query RPC Count $tablet_endpoint", - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - } - ] - }, - "unit": "none" - }, - "overrides": [] - }, - "gridPos": { - "h": 5, - "w": 12, - "x": 12, - "y": 23 - }, - "id": 469, - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "pluginVersion": "8.3.3", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": " ({__name__=~\"rpc_server_.*_openmldb_api_tablet_server_sub_batch_request_query_count\", job=~\"$component_job\", instance=~\"$tablet_endpoint\"})", - "interval": "", - "legendFormat": "{{instance}}", - "refId": "A" - } - ], - "title": "Sub Batch Request Query RPC Count $tablet_endpoint", - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - } - ] - }, - "unit": "none" - }, - "overrides": [] - }, - "gridPos": { - "h": 14, - "w": 8, - "x": 0, - "y": 28 - }, - "id": 484, - "links": [], - "maxPerRow": 4, - "options": { - "legend": { - "calcs": [ - "lastNotNull" - ], - "displayMode": "table", - "placement": "bottom" - }, - "tooltip": { - "mode": "single" - } - }, - "pluginVersion": "8.3.3", - "repeat": "tablet_endpoint", - "repeatDirection": "h", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "{__name__ =~ \"rpc_server_.*_openmldb_api_tablet_server_query_count\", job=~\"$component_job\", instance=~\"$tablet_endpoint\"}", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "{{instance}}-count", - "refId": "A" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "{__name__ =~ \"rpc_server_.*_openmldb_api_tablet_server_query_error\", job=~\"$component_job\", instance=~\"$tablet_endpoint\"}", - "format": "time_series", - "hide": false, - "interval": "", - "intervalFactor": 1, - "legendFormat": "{{instance}}-error", - "refId": "B" - } - ], - "title": "RPC Query Count & Error Count $tablet_endpoint ", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - } - ] - }, - "unit": "none" - }, - "overrides": [] - }, - "gridPos": { - "h": 14, - "w": 8, - "x": 0, - "y": 42 - }, - "id": 87, - "links": [], - "maxPerRow": 4, - "options": { - "legend": { - "calcs": [ - "lastNotNull" - ], - "displayMode": "table", - "placement": "bottom" - }, - "tooltip": { - "mode": "single" - } - }, - "pluginVersion": "8.3.3", - "repeat": "tablet_endpoint", - "repeatDirection": "h", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "{__name__=~\"rpc_server_.*_openmldb_api_tablet_server_sub_query_count\", job=~\"$component_job\", instance=~\"$tablet_endpoint\"}", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "{{instance}}-count", - "refId": "A" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "{__name__=~\"rpc_server_.*_openmldb_api_tablet_server_sub_query_error\", job=~\"$component_job\", instance=~\"$tablet_endpoint\"}", - "format": "time_series", - "hide": false, - "interval": "", - "intervalFactor": 1, - "legendFormat": "{{instance}}-error", - "refId": "B" - } - ], - "title": "RPC Sub Query Count & Error Count $tablet_endpoint ", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - } - ] - }, - "unit": "none" - }, - "overrides": [] - }, - "gridPos": { - "h": 14, - "w": 8, - "x": 0, - "y": 56 - }, - "id": 336, - "links": [], - "maxPerRow": 4, - "options": { - "legend": { - "calcs": [ - "lastNotNull" - ], - "displayMode": "table", - "placement": "bottom" - }, - "tooltip": { - "mode": "single" - } - }, - "pluginVersion": "8.3.3", - "repeat": "tablet_endpoint", - "repeatDirection": "h", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "{__name__ =~ \"rpc_server_.*_openmldb_api_tablet_server_sqlbatch_request_query_count\", job=~\"$component_job\", instance=~\"$tablet_endpoint\"}", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "{{instance}}-count", - "refId": "A" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "{__name__ =~ \"rpc_server_.*_openmldb_api_tablet_server_sqlbatch_request_query_error\", job=~\"$component_job\", instance=~\"$tablet_endpoint\"}", - "format": "time_series", - "hide": false, - "interval": "", - "intervalFactor": 1, - "legendFormat": "{{instance}}-error", - "refId": "B" - } - ], - "title": "RPC Batch Request Query Count & Error Count $tablet_endpoint ", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - } - ] - }, - "unit": "none" - }, - "overrides": [] - }, - "gridPos": { - "h": 14, - "w": 8, - "x": 0, - "y": 70 - }, - "id": 378, - "links": [], - "maxPerRow": 4, - "options": { - "legend": { - "calcs": [ - "lastNotNull" - ], - "displayMode": "table", - "placement": "bottom" - }, - "tooltip": { - "mode": "single" - } - }, - "pluginVersion": "8.3.3", - "repeat": "tablet_endpoint", - "repeatDirection": "h", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "{__name__ =~ \"rpc_server_.*_openmldb_api_tablet_server_sub_batch_request_query_count\", job=~\"$component_job\", instance=~\"$tablet_endpoint\"}", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "{{instance}}-count", - "refId": "A" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "{__name__ =~ \"rpc_server_.*_openmldb_api_tablet_server_sub_batch_request_query_error\", job=~\"$component_job\", instance=~\"$tablet_endpoint\"}", - "format": "time_series", - "hide": false, - "interval": "", - "intervalFactor": 1, - "legendFormat": "{{instance}}-error", - "refId": "B" - } - ], - "title": "RPC Sub Batch Request Query Count & Error Count $tablet_endpoint ", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 10, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "smooth", - "lineStyle": { - "fill": "solid" - }, - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": true, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "bytes" - }, - "overrides": [] - }, - "gridPos": { - "h": 10, - "w": 8, - "x": 0, - "y": 84 - }, - "id": 66, - "links": [], - "options": { - "legend": { - "calcs": [ - "lastNotNull" - ], - "displayMode": "table", - "placement": "bottom" - }, - "tooltip": { - "mode": "single" - } - }, - "pluginVersion": "8.3.3", - "repeat": "tablet_endpoint", - "repeatDirection": "h", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "openmldb_tablet_memory_actual_used_bytes{endpoint=~\"$tablet_endpoint\", job=~\"$job\"}", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "tablet-actual-{{endpoint}}", - "refId": "A" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": true, - "expr": "openmldb_tablet_memory_application_bytes{endpoint=~\"$tablet_endpoint\", job=~\"$job\"}", - "hide": false, - "interval": "", - "legendFormat": "tablet-application-{{endpoint}}", - "refId": "B" - } - ], - "title": "Tablet $tablet_endpoint Memory (Application & Actual Memory)", - "type": "timeseries" - } - ], - "title": "Tablet Specific status $tablet_endpoint", - "type": "row" - } - ], - "refresh": false, - "schemaVersion": 34, - "style": "dark", - "tags": [ - "openmldb" - ], - "templating": { - "list": [ - { - "current": { - "selected": false, - "text": "default", - "value": "default" - }, - "hide": 0, - "includeAll": false, - "label": "Data Source", - "multi": false, - "name": "datasource", - "options": [], - "query": "prometheus", - "queryValue": "", - "refresh": 1, - "regex": "", - "skipUrlSync": false, - "type": "datasource" - }, - { - "current": {}, - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "definition": "label_values(openmldb_status, job)", - "description": "Name of the prometheus job that pulls OpenMLDB DB-Level Metrics", - "hide": 0, - "includeAll": true, - "label": "Job", - "multi": true, - "name": "job", - "options": [], - "query": { - "query": "label_values(openmldb_status, job)", - "refId": "StandardVariableQuery" - }, - "refresh": 1, - "regex": "", - "skipUrlSync": false, - "sort": 0, - "type": "query" - }, - { - "current": {}, - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "definition": "label_values(process_uptime, job)", - "description": "The name of prometheus job that pulls Component-Level metrics ", - "hide": 0, - "includeAll": true, - "label": "Component Job", - "multi": true, - "name": "component_job", - "options": [], - "query": { - "query": "label_values(process_uptime, job)", - "refId": "StandardVariableQuery" - }, - "refresh": 1, - "regex": "", - "skipUrlSync": false, - "sort": 0, - "type": "query" - }, - { - "current": {}, - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "definition": "label_values(process_uptime, instance)", - "description": "The 'IP:PORT' endpoint of a component", - "hide": 0, - "includeAll": true, - "label": "Endpoint", - "multi": true, - "name": "endpoint", - "options": [], - "query": { - "query": "label_values(process_uptime, instance)", - "refId": "StandardVariableQuery" - }, - "refresh": 1, - "regex": "", - "skipUrlSync": false, - "sort": 0, - "type": "query" - }, - { - "current": {}, - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "definition": "label_values(openmldb_table_rows, table_path)", - "description": "${db_name}_${table_name} to a table", - "hide": 0, - "includeAll": true, - "label": "Table Path", - "multi": true, - "name": "table_path", - "options": [], - "query": { - "query": "label_values(openmldb_table_rows, table_path)", - "refId": "StandardVariableQuery" - }, - "refresh": 1, - "regex": "", - "skipUrlSync": false, - "sort": 0, - "type": "query" - }, - { - "current": {}, - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "definition": "label_values(openmldb_tablet_memory_application_bytes, endpoint)", - "description": "Endpoint to a Tablet", - "hide": 0, - "includeAll": true, - "label": "Tablet Endpoint", - "multi": true, - "name": "tablet_endpoint", - "options": [], - "query": { - "query": "label_values(openmldb_tablet_memory_application_bytes, endpoint)", - "refId": "StandardVariableQuery" - }, - "refresh": 1, - "regex": "", - "skipUrlSync": false, - "sort": 0, - "type": "query" - }, - { - "current": {}, - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "definition": "label_values(openmldb_info_schema_deploy_response_time_seconds_count, deploy_path)", - "description": "{db_name}}_{{deploy_name}} for a deployment name", - "hide": 0, - "includeAll": true, - "label": "Deploy Path", - "multi": true, - "name": "deploy_path", - "options": [], - "query": { - "query": "label_values(openmldb_info_schema_deploy_response_time_seconds_count, deploy_path)", - "refId": "StandardVariableQuery" - }, - "refresh": 1, - "regex": "", - "skipUrlSync": false, - "sort": 0, - "type": "query" - } - ] - }, - "time": { - "from": "now-6h", - "to": "now" - }, - "timepicker": { - "refresh_intervals": [ - "5s", - "10s", - "30s", - "1m", - "5m", - "15m", - "30m", - "1h", - "2h", - "1d" - ], - "time_options": [ - "5m", - "15m", - "1h", - "6h", - "12h", - "24h", - "2d", - "7d", - "30d" - ] - }, - "timezone": "", - "title": "OpenMLDB dashboard", - "uid": "SXUXvsbMk", - "version": 2, - "weekStart": "" -} \ No newline at end of file diff --git a/monitoring/openmldb_mixin/prometheus_example.yml b/monitoring/openmldb_mixin/prometheus_example.yml deleted file mode 100644 index f89b447637f..00000000000 --- a/monitoring/openmldb_mixin/prometheus_example.yml +++ /dev/null @@ -1,53 +0,0 @@ -# my global config -global: - scrape_interval: 1m # Set the scrape interval, default is every 1 minute. - evaluation_interval: 1m # Evaluate rules, default is every 1 minute. - # scrape_timeout is set to the global default (10s). - -# Alertmanager configuration -alerting: - alertmanagers: - - static_configs: - - targets: - # - alertmanager:9093 - -# Load rules once and periodically evaluate them according to the global 'evaluation_interval'. -rule_files: - # - "first_rules.yml" - # - "second_rules.yml" - -# A scrape configuration containing exactly one endpoint to scrape: -# Here it's Prometheus itself. -scrape_configs: - # The job name is added as a label `job=` to any timeseries scraped from this config. - - job_name: prometheus - static_configs: - - targets: - - localhost:9090 - - - job_name: node - # the job pull machine & OS related metrics, which exported by prometheus node_exporter - # refer https://github.com/prometheus/node_exporter for instructions and setup - - # metrics_path defaults to '/metrics' - # scheme defaults to 'http'. - - static_configs: - - targets: - - 172.17.0.15:9100 - - - job_name: openmldb_components - # job to pull component metrics from OpenMLDB like tablet/nameserver - # tweak the 'targets' list in 'static_configs' on your need - # every nameserver/tablet component endpoint should be added into targets - metrics_path: /brpc_metrics - static_configs: - - targets: - - 172.17.0.15:9622 - - - job_name: openmldb_exporter - # pull OpenMLDB DB-Level specific metric - # change the 'targets' value to your deployed OpenMLDB exporter endpoint - static_configs: - - targets: - - 172.17.0.15:8000 diff --git a/monitoring/poetry.lock b/monitoring/poetry.lock deleted file mode 100644 index 58cbbee4aab..00000000000 --- a/monitoring/poetry.lock +++ /dev/null @@ -1,1231 +0,0 @@ -# This file is automatically @generated by Poetry and should not be changed by hand. - -[[package]] -name = "appnope" -version = "0.1.3" -description = "Disable App Nap on macOS >= 10.9" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "appnope-0.1.3-py2.py3-none-any.whl", hash = "sha256:265a455292d0bd8a72453494fa24df5a11eb18373a60c7c0430889f22548605e"}, - {file = "appnope-0.1.3.tar.gz", hash = "sha256:02bd91c4de869fbb1e1c50aafc4098827a7a54ab2f39d9dcba6c9547ed920e24"}, -] - -[[package]] -name = "astroid" -version = "2.15.4" -description = "An abstract syntax tree for Python with inference support." -category = "dev" -optional = false -python-versions = ">=3.7.2" -files = [ - {file = "astroid-2.15.4-py3-none-any.whl", hash = "sha256:a1b8543ef9d36ea777194bc9b17f5f8678d2c56ee6a45b2c2f17eec96f242347"}, - {file = "astroid-2.15.4.tar.gz", hash = "sha256:c81e1c7fbac615037744d067a9bb5f9aeb655edf59b63ee8b59585475d6f80d8"}, -] - -[package.dependencies] -lazy-object-proxy = ">=1.4.0" -typing-extensions = {version = ">=4.0.0", markers = "python_version < \"3.11\""} -wrapt = [ - {version = ">=1.11,<2", markers = "python_version < \"3.11\""}, - {version = ">=1.14,<2", markers = "python_version >= \"3.11\""}, -] - -[[package]] -name = "asttokens" -version = "2.2.1" -description = "Annotate AST trees with source code positions" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "asttokens-2.2.1-py2.py3-none-any.whl", hash = "sha256:6b0ac9e93fb0335014d382b8fa9b3afa7df546984258005da0b9e7095b3deb1c"}, - {file = "asttokens-2.2.1.tar.gz", hash = "sha256:4622110b2a6f30b77e1473affaa97e711bc2f07d3f10848420ff1898edbe94f3"}, -] - -[package.dependencies] -six = "*" - -[package.extras] -test = ["astroid", "pytest"] - -[[package]] -name = "attrs" -version = "23.1.0" -description = "Classes Without Boilerplate" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "attrs-23.1.0-py3-none-any.whl", hash = "sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04"}, - {file = "attrs-23.1.0.tar.gz", hash = "sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015"}, -] - -[package.extras] -cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] -dev = ["attrs[docs,tests]", "pre-commit"] -docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] -tests = ["attrs[tests-no-zope]", "zope-interface"] -tests-no-zope = ["cloudpickle", "hypothesis", "mypy (>=1.1.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] - -[[package]] -name = "automat" -version = "22.10.0" -description = "Self-service finite-state machines for the programmer on the go." -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "Automat-22.10.0-py2.py3-none-any.whl", hash = "sha256:c3164f8742b9dc440f3682482d32aaff7bb53f71740dd018533f9de286b64180"}, - {file = "Automat-22.10.0.tar.gz", hash = "sha256:e56beb84edad19dcc11d30e8d9b895f75deeb5ef5e96b84a467066b3b84bb04e"}, -] - -[package.dependencies] -attrs = ">=19.2.0" -six = "*" - -[package.extras] -visualize = ["Twisted (>=16.1.1)", "graphviz (>0.5.1)"] - -[[package]] -name = "backcall" -version = "0.2.0" -description = "Specifications for callback functions passed in to an API" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "backcall-0.2.0-py2.py3-none-any.whl", hash = "sha256:fbbce6a29f263178a1f7915c1940bde0ec2b2a967566fe1c65c1dfb7422bd255"}, - {file = "backcall-0.2.0.tar.gz", hash = "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e"}, -] - -[[package]] -name = "certifi" -version = "2022.12.7" -description = "Python package for providing Mozilla's CA Bundle." -category = "dev" -optional = false -python-versions = ">=3.6" -files = [ - {file = "certifi-2022.12.7-py3-none-any.whl", hash = "sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18"}, - {file = "certifi-2022.12.7.tar.gz", hash = "sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3"}, -] - -[[package]] -name = "charset-normalizer" -version = "3.1.0" -description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." -category = "dev" -optional = false -python-versions = ">=3.7.0" -files = [ - {file = "charset-normalizer-3.1.0.tar.gz", hash = "sha256:34e0a2f9c370eb95597aae63bf85eb5e96826d81e3dcf88b8886012906f509b5"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e0ac8959c929593fee38da1c2b64ee9778733cdf03c482c9ff1d508b6b593b2b"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d7fc3fca01da18fbabe4625d64bb612b533533ed10045a2ac3dd194bfa656b60"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:04eefcee095f58eaabe6dc3cc2262f3bcd776d2c67005880894f447b3f2cb9c1"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20064ead0717cf9a73a6d1e779b23d149b53daf971169289ed2ed43a71e8d3b0"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1435ae15108b1cb6fffbcea2af3d468683b7afed0169ad718451f8db5d1aff6f"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c84132a54c750fda57729d1e2599bb598f5fa0344085dbde5003ba429a4798c0"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75f2568b4189dda1c567339b48cba4ac7384accb9c2a7ed655cd86b04055c795"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:11d3bcb7be35e7b1bba2c23beedac81ee893ac9871d0ba79effc7fc01167db6c"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:891cf9b48776b5c61c700b55a598621fdb7b1e301a550365571e9624f270c203"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:5f008525e02908b20e04707a4f704cd286d94718f48bb33edddc7d7b584dddc1"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:b06f0d3bf045158d2fb8837c5785fe9ff9b8c93358be64461a1089f5da983137"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:49919f8400b5e49e961f320c735388ee686a62327e773fa5b3ce6721f7e785ce"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:22908891a380d50738e1f978667536f6c6b526a2064156203d418f4856d6e86a"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-win32.whl", hash = "sha256:12d1a39aa6b8c6f6248bb54550efcc1c38ce0d8096a146638fd4738e42284448"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:65ed923f84a6844de5fd29726b888e58c62820e0769b76565480e1fdc3d062f8"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9a3267620866c9d17b959a84dd0bd2d45719b817245e49371ead79ed4f710d19"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6734e606355834f13445b6adc38b53c0fd45f1a56a9ba06c2058f86893ae8017"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f8303414c7b03f794347ad062c0516cee0e15f7a612abd0ce1e25caf6ceb47df"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aaf53a6cebad0eae578f062c7d462155eada9c172bd8c4d250b8c1d8eb7f916a"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3dc5b6a8ecfdc5748a7e429782598e4f17ef378e3e272eeb1340ea57c9109f41"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e1b25e3ad6c909f398df8921780d6a3d120d8c09466720226fc621605b6f92b1"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ca564606d2caafb0abe6d1b5311c2649e8071eb241b2d64e75a0d0065107e62"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b82fab78e0b1329e183a65260581de4375f619167478dddab510c6c6fb04d9b6"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:bd7163182133c0c7701b25e604cf1611c0d87712e56e88e7ee5d72deab3e76b5"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:11d117e6c63e8f495412d37e7dc2e2fff09c34b2d09dbe2bee3c6229577818be"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:cf6511efa4801b9b38dc5546d7547d5b5c6ef4b081c60b23e4d941d0eba9cbeb"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:abc1185d79f47c0a7aaf7e2412a0eb2c03b724581139193d2d82b3ad8cbb00ac"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cb7b2ab0188829593b9de646545175547a70d9a6e2b63bf2cd87a0a391599324"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-win32.whl", hash = "sha256:c36bcbc0d5174a80d6cccf43a0ecaca44e81d25be4b7f90f0ed7bcfbb5a00909"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:cca4def576f47a09a943666b8f829606bcb17e2bc2d5911a46c8f8da45f56755"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0c95f12b74681e9ae127728f7e5409cbbef9cd914d5896ef238cc779b8152373"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fca62a8301b605b954ad2e9c3666f9d97f63872aa4efcae5492baca2056b74ab"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac0aa6cd53ab9a31d397f8303f92c42f534693528fafbdb997c82bae6e477ad9"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c3af8e0f07399d3176b179f2e2634c3ce9c1301379a6b8c9c9aeecd481da494f"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a5fc78f9e3f501a1614a98f7c54d3969f3ad9bba8ba3d9b438c3bc5d047dd28"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:628c985afb2c7d27a4800bfb609e03985aaecb42f955049957814e0491d4006d"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:74db0052d985cf37fa111828d0dd230776ac99c740e1a758ad99094be4f1803d"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:1e8fcdd8f672a1c4fc8d0bd3a2b576b152d2a349782d1eb0f6b8e52e9954731d"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:04afa6387e2b282cf78ff3dbce20f0cc071c12dc8f685bd40960cc68644cfea6"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:dd5653e67b149503c68c4018bf07e42eeed6b4e956b24c00ccdf93ac79cdff84"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d2686f91611f9e17f4548dbf050e75b079bbc2a82be565832bc8ea9047b61c8c"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-win32.whl", hash = "sha256:4155b51ae05ed47199dc5b2a4e62abccb274cee6b01da5b895099b61b1982974"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:322102cdf1ab682ecc7d9b1c5eed4ec59657a65e1c146a0da342b78f4112db23"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e633940f28c1e913615fd624fcdd72fdba807bf53ea6925d6a588e84e1151531"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3a06f32c9634a8705f4ca9946d667609f52cf130d5548881401f1eb2c39b1e2c"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7381c66e0561c5757ffe616af869b916c8b4e42b367ab29fedc98481d1e74e14"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3573d376454d956553c356df45bb824262c397c6e26ce43e8203c4c540ee0acb"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e89df2958e5159b811af9ff0f92614dabf4ff617c03a4c1c6ff53bf1c399e0e1"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:78cacd03e79d009d95635e7d6ff12c21eb89b894c354bd2b2ed0b4763373693b"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de5695a6f1d8340b12a5d6d4484290ee74d61e467c39ff03b39e30df62cf83a0"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1c60b9c202d00052183c9be85e5eaf18a4ada0a47d188a83c8f5c5b23252f649"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f645caaf0008bacf349875a974220f1f1da349c5dbe7c4ec93048cdc785a3326"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ea9f9c6034ea2d93d9147818f17c2a0860d41b71c38b9ce4d55f21b6f9165a11"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:80d1543d58bd3d6c271b66abf454d437a438dff01c3e62fdbcd68f2a11310d4b"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:73dc03a6a7e30b7edc5b01b601e53e7fc924b04e1835e8e407c12c037e81adbd"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6f5c2e7bc8a4bf7c426599765b1bd33217ec84023033672c1e9a8b35eaeaaaf8"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-win32.whl", hash = "sha256:12a2b561af122e3d94cdb97fe6fb2bb2b82cef0cdca131646fdb940a1eda04f0"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:3160a0fd9754aab7d47f95a6b63ab355388d890163eb03b2d2b87ab0a30cfa59"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:38e812a197bf8e71a59fe55b757a84c1f946d0ac114acafaafaf21667a7e169e"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6baf0baf0d5d265fa7944feb9f7451cc316bfe30e8df1a61b1bb08577c554f31"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8f25e17ab3039b05f762b0a55ae0b3632b2e073d9c8fc88e89aca31a6198e88f"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3747443b6a904001473370d7810aa19c3a180ccd52a7157aacc264a5ac79265e"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b116502087ce8a6b7a5f1814568ccbd0e9f6cfd99948aa59b0e241dc57cf739f"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d16fd5252f883eb074ca55cb622bc0bee49b979ae4e8639fff6ca3ff44f9f854"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21fa558996782fc226b529fdd2ed7866c2c6ec91cee82735c98a197fae39f706"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f6c7a8a57e9405cad7485f4c9d3172ae486cfef1344b5ddd8e5239582d7355e"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ac3775e3311661d4adace3697a52ac0bab17edd166087d493b52d4f4f553f9f0"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:10c93628d7497c81686e8e5e557aafa78f230cd9e77dd0c40032ef90c18f2230"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:6f4f4668e1831850ebcc2fd0b1cd11721947b6dc7c00bf1c6bd3c929ae14f2c7"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:0be65ccf618c1e7ac9b849c315cc2e8a8751d9cfdaa43027d4f6624bd587ab7e"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:53d0a3fa5f8af98a1e261de6a3943ca631c526635eb5817a87a59d9a57ebf48f"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-win32.whl", hash = "sha256:a04f86f41a8916fe45ac5024ec477f41f886b3c435da2d4e3d2709b22ab02af1"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:830d2948a5ec37c386d3170c483063798d7879037492540f10a475e3fd6f244b"}, - {file = "charset_normalizer-3.1.0-py3-none-any.whl", hash = "sha256:3d9098b479e78c85080c98e1e35ff40b4a31d8953102bb0fd7d1b6f8a2111a3d"}, -] - -[[package]] -name = "colorama" -version = "0.4.6" -description = "Cross-platform colored terminal text." -category = "main" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -files = [ - {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, - {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, -] - -[[package]] -name = "constantly" -version = "15.1.0" -description = "Symbolic constants in Python" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "constantly-15.1.0-py2.py3-none-any.whl", hash = "sha256:dd2fa9d6b1a51a83f0d7dd76293d734046aa176e384bf6e33b7e44880eb37c5d"}, - {file = "constantly-15.1.0.tar.gz", hash = "sha256:586372eb92059873e29eba4f9dec8381541b4d3834660707faf8ba59146dfc35"}, -] - -[[package]] -name = "decorator" -version = "5.1.1" -description = "Decorators for Humans" -category = "main" -optional = false -python-versions = ">=3.5" -files = [ - {file = "decorator-5.1.1-py3-none-any.whl", hash = "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186"}, - {file = "decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"}, -] - -[[package]] -name = "dill" -version = "0.3.6" -description = "serialize all of python" -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ - {file = "dill-0.3.6-py3-none-any.whl", hash = "sha256:a07ffd2351b8c678dfc4a856a3005f8067aea51d6ba6c700796a4d9e280f39f0"}, - {file = "dill-0.3.6.tar.gz", hash = "sha256:e5db55f3687856d8fbdab002ed78544e1c4559a130302693d839dfe8f93f2373"}, -] - -[package.extras] -graph = ["objgraph (>=1.7.2)"] - -[[package]] -name = "exceptiongroup" -version = "1.1.1" -description = "Backport of PEP 654 (exception groups)" -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ - {file = "exceptiongroup-1.1.1-py3-none-any.whl", hash = "sha256:232c37c63e4f682982c8b6459f33a8981039e5fb8756b2074364e5055c498c9e"}, - {file = "exceptiongroup-1.1.1.tar.gz", hash = "sha256:d484c3090ba2889ae2928419117447a14daf3c1231d5e30d0aae34f354f01785"}, -] - -[package.extras] -test = ["pytest (>=6)"] - -[[package]] -name = "executing" -version = "1.2.0" -description = "Get the currently executing AST node of a frame, and other information" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "executing-1.2.0-py2.py3-none-any.whl", hash = "sha256:0314a69e37426e3608aada02473b4161d4caf5a4b244d1d0c48072b8fee7bacc"}, - {file = "executing-1.2.0.tar.gz", hash = "sha256:19da64c18d2d851112f09c287f8d3dbbdf725ab0e569077efb6cdcbd3497c107"}, -] - -[package.extras] -tests = ["asttokens", "littleutils", "pytest", "rich"] - -[[package]] -name = "greenlet" -version = "2.0.2" -description = "Lightweight in-process concurrent programming" -category = "main" -optional = false -python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*" -files = [ - {file = "greenlet-2.0.2-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:bdfea8c661e80d3c1c99ad7c3ff74e6e87184895bbaca6ee8cc61209f8b9b85d"}, - {file = "greenlet-2.0.2-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:9d14b83fab60d5e8abe587d51c75b252bcc21683f24699ada8fb275d7712f5a9"}, - {file = "greenlet-2.0.2-cp27-cp27m-win32.whl", hash = "sha256:6c3acb79b0bfd4fe733dff8bc62695283b57949ebcca05ae5c129eb606ff2d74"}, - {file = "greenlet-2.0.2-cp27-cp27m-win_amd64.whl", hash = "sha256:283737e0da3f08bd637b5ad058507e578dd462db259f7f6e4c5c365ba4ee9343"}, - {file = "greenlet-2.0.2-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:d27ec7509b9c18b6d73f2f5ede2622441de812e7b1a80bbd446cb0633bd3d5ae"}, - {file = "greenlet-2.0.2-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:30bcf80dda7f15ac77ba5af2b961bdd9dbc77fd4ac6105cee85b0d0a5fcf74df"}, - {file = "greenlet-2.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:26fbfce90728d82bc9e6c38ea4d038cba20b7faf8a0ca53a9c07b67318d46088"}, - {file = "greenlet-2.0.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9190f09060ea4debddd24665d6804b995a9c122ef5917ab26e1566dcc712ceeb"}, - {file = "greenlet-2.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d75209eed723105f9596807495d58d10b3470fa6732dd6756595e89925ce2470"}, - {file = "greenlet-2.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:3a51c9751078733d88e013587b108f1b7a1fb106d402fb390740f002b6f6551a"}, - {file = "greenlet-2.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:76ae285c8104046b3a7f06b42f29c7b73f77683df18c49ab5af7983994c2dd91"}, - {file = "greenlet-2.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:2d4686f195e32d36b4d7cf2d166857dbd0ee9f3d20ae349b6bf8afc8485b3645"}, - {file = "greenlet-2.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c4302695ad8027363e96311df24ee28978162cdcdd2006476c43970b384a244c"}, - {file = "greenlet-2.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c48f54ef8e05f04d6eff74b8233f6063cb1ed960243eacc474ee73a2ea8573ca"}, - {file = "greenlet-2.0.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a1846f1b999e78e13837c93c778dcfc3365902cfb8d1bdb7dd73ead37059f0d0"}, - {file = "greenlet-2.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a06ad5312349fec0ab944664b01d26f8d1f05009566339ac6f63f56589bc1a2"}, - {file = "greenlet-2.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:eff4eb9b7eb3e4d0cae3d28c283dc16d9bed6b193c2e1ace3ed86ce48ea8df19"}, - {file = "greenlet-2.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5454276c07d27a740c5892f4907c86327b632127dd9abec42ee62e12427ff7e3"}, - {file = "greenlet-2.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:7cafd1208fdbe93b67c7086876f061f660cfddc44f404279c1585bbf3cdc64c5"}, - {file = "greenlet-2.0.2-cp35-cp35m-macosx_10_14_x86_64.whl", hash = "sha256:910841381caba4f744a44bf81bfd573c94e10b3045ee00de0cbf436fe50673a6"}, - {file = "greenlet-2.0.2-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:18a7f18b82b52ee85322d7a7874e676f34ab319b9f8cce5de06067384aa8ff43"}, - {file = "greenlet-2.0.2-cp35-cp35m-win32.whl", hash = "sha256:03a8f4f3430c3b3ff8d10a2a86028c660355ab637cee9333d63d66b56f09d52a"}, - {file = "greenlet-2.0.2-cp35-cp35m-win_amd64.whl", hash = "sha256:4b58adb399c4d61d912c4c331984d60eb66565175cdf4a34792cd9600f21b394"}, - {file = "greenlet-2.0.2-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:703f18f3fda276b9a916f0934d2fb6d989bf0b4fb5a64825260eb9bfd52d78f0"}, - {file = "greenlet-2.0.2-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:32e5b64b148966d9cccc2c8d35a671409e45f195864560829f395a54226408d3"}, - {file = "greenlet-2.0.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2dd11f291565a81d71dab10b7033395b7a3a5456e637cf997a6f33ebdf06f8db"}, - {file = "greenlet-2.0.2-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e0f72c9ddb8cd28532185f54cc1453f2c16fb417a08b53a855c4e6a418edd099"}, - {file = "greenlet-2.0.2-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cd021c754b162c0fb55ad5d6b9d960db667faad0fa2ff25bb6e1301b0b6e6a75"}, - {file = "greenlet-2.0.2-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:3c9b12575734155d0c09d6c3e10dbd81665d5c18e1a7c6597df72fd05990c8cf"}, - {file = "greenlet-2.0.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:b9ec052b06a0524f0e35bd8790686a1da006bd911dd1ef7d50b77bfbad74e292"}, - {file = "greenlet-2.0.2-cp36-cp36m-win32.whl", hash = "sha256:dbfcfc0218093a19c252ca8eb9aee3d29cfdcb586df21049b9d777fd32c14fd9"}, - {file = "greenlet-2.0.2-cp36-cp36m-win_amd64.whl", hash = "sha256:9f35ec95538f50292f6d8f2c9c9f8a3c6540bbfec21c9e5b4b751e0a7c20864f"}, - {file = "greenlet-2.0.2-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:d5508f0b173e6aa47273bdc0a0b5ba055b59662ba7c7ee5119528f466585526b"}, - {file = "greenlet-2.0.2-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:f82d4d717d8ef19188687aa32b8363e96062911e63ba22a0cff7802a8e58e5f1"}, - {file = "greenlet-2.0.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c9c59a2120b55788e800d82dfa99b9e156ff8f2227f07c5e3012a45a399620b7"}, - {file = "greenlet-2.0.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2780572ec463d44c1d3ae850239508dbeb9fed38e294c68d19a24d925d9223ca"}, - {file = "greenlet-2.0.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:937e9020b514ceedb9c830c55d5c9872abc90f4b5862f89c0887033ae33c6f73"}, - {file = "greenlet-2.0.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:36abbf031e1c0f79dd5d596bfaf8e921c41df2bdf54ee1eed921ce1f52999a86"}, - {file = "greenlet-2.0.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:18e98fb3de7dba1c0a852731c3070cf022d14f0d68b4c87a19cc1016f3bb8b33"}, - {file = "greenlet-2.0.2-cp37-cp37m-win32.whl", hash = "sha256:3f6ea9bd35eb450837a3d80e77b517ea5bc56b4647f5502cd28de13675ee12f7"}, - {file = "greenlet-2.0.2-cp37-cp37m-win_amd64.whl", hash = "sha256:7492e2b7bd7c9b9916388d9df23fa49d9b88ac0640db0a5b4ecc2b653bf451e3"}, - {file = "greenlet-2.0.2-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:b864ba53912b6c3ab6bcb2beb19f19edd01a6bfcbdfe1f37ddd1778abfe75a30"}, - {file = "greenlet-2.0.2-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:ba2956617f1c42598a308a84c6cf021a90ff3862eddafd20c3333d50f0edb45b"}, - {file = "greenlet-2.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc3a569657468b6f3fb60587e48356fe512c1754ca05a564f11366ac9e306526"}, - {file = "greenlet-2.0.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8eab883b3b2a38cc1e050819ef06a7e6344d4a990d24d45bc6f2cf959045a45b"}, - {file = "greenlet-2.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:acd2162a36d3de67ee896c43effcd5ee3de247eb00354db411feb025aa319857"}, - {file = "greenlet-2.0.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:0bf60faf0bc2468089bdc5edd10555bab6e85152191df713e2ab1fcc86382b5a"}, - {file = "greenlet-2.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b0ef99cdbe2b682b9ccbb964743a6aca37905fda5e0452e5ee239b1654d37f2a"}, - {file = "greenlet-2.0.2-cp38-cp38-win32.whl", hash = "sha256:b80f600eddddce72320dbbc8e3784d16bd3fb7b517e82476d8da921f27d4b249"}, - {file = "greenlet-2.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:4d2e11331fc0c02b6e84b0d28ece3a36e0548ee1a1ce9ddde03752d9b79bba40"}, - {file = "greenlet-2.0.2-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:88d9ab96491d38a5ab7c56dd7a3cc37d83336ecc564e4e8816dbed12e5aaefc8"}, - {file = "greenlet-2.0.2-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:561091a7be172ab497a3527602d467e2b3fbe75f9e783d8b8ce403fa414f71a6"}, - {file = "greenlet-2.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:971ce5e14dc5e73715755d0ca2975ac88cfdaefcaab078a284fea6cfabf866df"}, - {file = "greenlet-2.0.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:be4ed120b52ae4d974aa40215fcdfde9194d63541c7ded40ee12eb4dda57b76b"}, - {file = "greenlet-2.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94c817e84245513926588caf1152e3b559ff794d505555211ca041f032abbb6b"}, - {file = "greenlet-2.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:1a819eef4b0e0b96bb0d98d797bef17dc1b4a10e8d7446be32d1da33e095dbb8"}, - {file = "greenlet-2.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:7efde645ca1cc441d6dc4b48c0f7101e8d86b54c8530141b09fd31cef5149ec9"}, - {file = "greenlet-2.0.2-cp39-cp39-win32.whl", hash = "sha256:ea9872c80c132f4663822dd2a08d404073a5a9b5ba6155bea72fb2a79d1093b5"}, - {file = "greenlet-2.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:db1a39669102a1d8d12b57de2bb7e2ec9066a6f2b3da35ae511ff93b01b5d564"}, - {file = "greenlet-2.0.2.tar.gz", hash = "sha256:e7c8dc13af7db097bed64a051d2dd49e9f0af495c26995c00a9ee842690d34c0"}, -] - -[package.extras] -docs = ["Sphinx", "docutils (<0.18)"] -test = ["objgraph", "psutil"] - -[[package]] -name = "hyperlink" -version = "21.0.0" -description = "A featureful, immutable, and correct URL for Python." -category = "main" -optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -files = [ - {file = "hyperlink-21.0.0-py2.py3-none-any.whl", hash = "sha256:e6b14c37ecb73e89c77d78cdb4c2cc8f3fb59a885c5b3f819ff4ed80f25af1b4"}, - {file = "hyperlink-21.0.0.tar.gz", hash = "sha256:427af957daa58bc909471c6c40f74c5450fa123dd093fc53efd2e91d2705a56b"}, -] - -[package.dependencies] -idna = ">=2.5" - -[[package]] -name = "idna" -version = "3.4" -description = "Internationalized Domain Names in Applications (IDNA)" -category = "main" -optional = false -python-versions = ">=3.5" -files = [ - {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, - {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, -] - -[[package]] -name = "incremental" -version = "22.10.0" -description = "\"A small library that versions your Python projects.\"" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "incremental-22.10.0-py2.py3-none-any.whl", hash = "sha256:b864a1f30885ee72c5ac2835a761b8fe8aa9c28b9395cacf27286602688d3e51"}, - {file = "incremental-22.10.0.tar.gz", hash = "sha256:912feeb5e0f7e0188e6f42241d2f450002e11bbc0937c65865045854c24c0bd0"}, -] - -[package.extras] -mypy = ["click (>=6.0)", "mypy (==0.812)", "twisted (>=16.4.0)"] -scripts = ["click (>=6.0)", "twisted (>=16.4.0)"] - -[[package]] -name = "iniconfig" -version = "2.0.0" -description = "brain-dead simple config-ini parsing" -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ - {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, - {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, -] - -[[package]] -name = "ipython" -version = "8.12.0" -description = "IPython: Productive Interactive Computing" -category = "main" -optional = false -python-versions = ">=3.8" -files = [ - {file = "ipython-8.12.0-py3-none-any.whl", hash = "sha256:1c183bf61b148b00bcebfa5d9b39312733ae97f6dad90d7e9b4d86c8647f498c"}, - {file = "ipython-8.12.0.tar.gz", hash = "sha256:a950236df04ad75b5bc7f816f9af3d74dc118fd42f2ff7e80e8e60ca1f182e2d"}, -] - -[package.dependencies] -appnope = {version = "*", markers = "sys_platform == \"darwin\""} -backcall = "*" -colorama = {version = "*", markers = "sys_platform == \"win32\""} -decorator = "*" -jedi = ">=0.16" -matplotlib-inline = "*" -pexpect = {version = ">4.3", markers = "sys_platform != \"win32\""} -pickleshare = "*" -prompt-toolkit = ">=3.0.30,<3.0.37 || >3.0.37,<3.1.0" -pygments = ">=2.4.0" -stack-data = "*" -traitlets = ">=5" -typing-extensions = {version = "*", markers = "python_version < \"3.10\""} - -[package.extras] -all = ["black", "curio", "docrepr", "ipykernel", "ipyparallel", "ipywidgets", "matplotlib", "matplotlib (!=3.2.0)", "nbconvert", "nbformat", "notebook", "numpy (>=1.21)", "pandas", "pytest (<7)", "pytest (<7.1)", "pytest-asyncio", "qtconsole", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "stack-data", "testpath", "trio", "typing-extensions"] -black = ["black"] -doc = ["docrepr", "ipykernel", "matplotlib", "pytest (<7)", "pytest (<7.1)", "pytest-asyncio", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "stack-data", "testpath", "typing-extensions"] -kernel = ["ipykernel"] -nbconvert = ["nbconvert"] -nbformat = ["nbformat"] -notebook = ["ipywidgets", "notebook"] -parallel = ["ipyparallel"] -qtconsole = ["qtconsole"] -test = ["pytest (<7.1)", "pytest-asyncio", "testpath"] -test-extra = ["curio", "matplotlib (!=3.2.0)", "nbformat", "numpy (>=1.21)", "pandas", "pytest (<7.1)", "pytest-asyncio", "testpath", "trio"] - -[[package]] -name = "isort" -version = "5.12.0" -description = "A Python utility / library to sort Python imports." -category = "dev" -optional = false -python-versions = ">=3.8.0" -files = [ - {file = "isort-5.12.0-py3-none-any.whl", hash = "sha256:f84c2818376e66cf843d497486ea8fed8700b340f308f076c6fb1229dff318b6"}, - {file = "isort-5.12.0.tar.gz", hash = "sha256:8bef7dde241278824a6d83f44a544709b065191b95b6e50894bdc722fcba0504"}, -] - -[package.extras] -colors = ["colorama (>=0.4.3)"] -pipfile-deprecated-finder = ["pip-shims (>=0.5.2)", "pipreqs", "requirementslib"] -plugins = ["setuptools"] -requirements-deprecated-finder = ["pip-api", "pipreqs"] - -[[package]] -name = "jedi" -version = "0.18.2" -description = "An autocompletion tool for Python that can be used for text editors." -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "jedi-0.18.2-py2.py3-none-any.whl", hash = "sha256:203c1fd9d969ab8f2119ec0a3342e0b49910045abe6af0a3ae83a5764d54639e"}, - {file = "jedi-0.18.2.tar.gz", hash = "sha256:bae794c30d07f6d910d32a7048af09b5a39ed740918da923c6b780790ebac612"}, -] - -[package.dependencies] -parso = ">=0.8.0,<0.9.0" - -[package.extras] -docs = ["Jinja2 (==2.11.3)", "MarkupSafe (==1.1.1)", "Pygments (==2.8.1)", "alabaster (==0.7.12)", "babel (==2.9.1)", "chardet (==4.0.0)", "commonmark (==0.8.1)", "docutils (==0.17.1)", "future (==0.18.2)", "idna (==2.10)", "imagesize (==1.2.0)", "mock (==1.0.1)", "packaging (==20.9)", "pyparsing (==2.4.7)", "pytz (==2021.1)", "readthedocs-sphinx-ext (==2.1.4)", "recommonmark (==0.5.0)", "requests (==2.25.1)", "six (==1.15.0)", "snowballstemmer (==2.1.0)", "sphinx (==1.8.5)", "sphinx-rtd-theme (==0.4.3)", "sphinxcontrib-serializinghtml (==1.1.4)", "sphinxcontrib-websupport (==1.2.4)", "urllib3 (==1.26.4)"] -qa = ["flake8 (==3.8.3)", "mypy (==0.782)"] -testing = ["Django (<3.1)", "attrs", "colorama", "docopt", "pytest (<7.0.0)"] - -[[package]] -name = "lazy-object-proxy" -version = "1.9.0" -description = "A fast and thorough lazy object proxy." -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ - {file = "lazy-object-proxy-1.9.0.tar.gz", hash = "sha256:659fb5809fa4629b8a1ac5106f669cfc7bef26fbb389dda53b3e010d1ac4ebae"}, - {file = "lazy_object_proxy-1.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b40387277b0ed2d0602b8293b94d7257e17d1479e257b4de114ea11a8cb7f2d7"}, - {file = "lazy_object_proxy-1.9.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8c6cfb338b133fbdbc5cfaa10fe3c6aeea827db80c978dbd13bc9dd8526b7d4"}, - {file = "lazy_object_proxy-1.9.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:721532711daa7db0d8b779b0bb0318fa87af1c10d7fe5e52ef30f8eff254d0cd"}, - {file = "lazy_object_proxy-1.9.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:66a3de4a3ec06cd8af3f61b8e1ec67614fbb7c995d02fa224813cb7afefee701"}, - {file = "lazy_object_proxy-1.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1aa3de4088c89a1b69f8ec0dcc169aa725b0ff017899ac568fe44ddc1396df46"}, - {file = "lazy_object_proxy-1.9.0-cp310-cp310-win32.whl", hash = "sha256:f0705c376533ed2a9e5e97aacdbfe04cecd71e0aa84c7c0595d02ef93b6e4455"}, - {file = "lazy_object_proxy-1.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:ea806fd4c37bf7e7ad82537b0757999264d5f70c45468447bb2b91afdbe73a6e"}, - {file = "lazy_object_proxy-1.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:946d27deaff6cf8452ed0dba83ba38839a87f4f7a9732e8f9fd4107b21e6ff07"}, - {file = "lazy_object_proxy-1.9.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79a31b086e7e68b24b99b23d57723ef7e2c6d81ed21007b6281ebcd1688acb0a"}, - {file = "lazy_object_proxy-1.9.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f699ac1c768270c9e384e4cbd268d6e67aebcfae6cd623b4d7c3bfde5a35db59"}, - {file = "lazy_object_proxy-1.9.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:bfb38f9ffb53b942f2b5954e0f610f1e721ccebe9cce9025a38c8ccf4a5183a4"}, - {file = "lazy_object_proxy-1.9.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:189bbd5d41ae7a498397287c408617fe5c48633e7755287b21d741f7db2706a9"}, - {file = "lazy_object_proxy-1.9.0-cp311-cp311-win32.whl", hash = "sha256:81fc4d08b062b535d95c9ea70dbe8a335c45c04029878e62d744bdced5141586"}, - {file = "lazy_object_proxy-1.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:f2457189d8257dd41ae9b434ba33298aec198e30adf2dcdaaa3a28b9994f6adb"}, - {file = "lazy_object_proxy-1.9.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d9e25ef10a39e8afe59a5c348a4dbf29b4868ab76269f81ce1674494e2565a6e"}, - {file = "lazy_object_proxy-1.9.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cbf9b082426036e19c6924a9ce90c740a9861e2bdc27a4834fd0a910742ac1e8"}, - {file = "lazy_object_proxy-1.9.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f5fa4a61ce2438267163891961cfd5e32ec97a2c444e5b842d574251ade27d2"}, - {file = "lazy_object_proxy-1.9.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:8fa02eaab317b1e9e03f69aab1f91e120e7899b392c4fc19807a8278a07a97e8"}, - {file = "lazy_object_proxy-1.9.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e7c21c95cae3c05c14aafffe2865bbd5e377cfc1348c4f7751d9dc9a48ca4bda"}, - {file = "lazy_object_proxy-1.9.0-cp37-cp37m-win32.whl", hash = "sha256:f12ad7126ae0c98d601a7ee504c1122bcef553d1d5e0c3bfa77b16b3968d2734"}, - {file = "lazy_object_proxy-1.9.0-cp37-cp37m-win_amd64.whl", hash = "sha256:edd20c5a55acb67c7ed471fa2b5fb66cb17f61430b7a6b9c3b4a1e40293b1671"}, - {file = "lazy_object_proxy-1.9.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2d0daa332786cf3bb49e10dc6a17a52f6a8f9601b4cf5c295a4f85854d61de63"}, - {file = "lazy_object_proxy-1.9.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cd077f3d04a58e83d04b20e334f678c2b0ff9879b9375ed107d5d07ff160171"}, - {file = "lazy_object_proxy-1.9.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:660c94ea760b3ce47d1855a30984c78327500493d396eac4dfd8bd82041b22be"}, - {file = "lazy_object_proxy-1.9.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:212774e4dfa851e74d393a2370871e174d7ff0ebc980907723bb67d25c8a7c30"}, - {file = "lazy_object_proxy-1.9.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:f0117049dd1d5635bbff65444496c90e0baa48ea405125c088e93d9cf4525b11"}, - {file = "lazy_object_proxy-1.9.0-cp38-cp38-win32.whl", hash = "sha256:0a891e4e41b54fd5b8313b96399f8b0e173bbbfc03c7631f01efbe29bb0bcf82"}, - {file = "lazy_object_proxy-1.9.0-cp38-cp38-win_amd64.whl", hash = "sha256:9990d8e71b9f6488e91ad25f322898c136b008d87bf852ff65391b004da5e17b"}, - {file = "lazy_object_proxy-1.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9e7551208b2aded9c1447453ee366f1c4070602b3d932ace044715d89666899b"}, - {file = "lazy_object_proxy-1.9.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f83ac4d83ef0ab017683d715ed356e30dd48a93746309c8f3517e1287523ef4"}, - {file = "lazy_object_proxy-1.9.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7322c3d6f1766d4ef1e51a465f47955f1e8123caee67dd641e67d539a534d006"}, - {file = "lazy_object_proxy-1.9.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:18b78ec83edbbeb69efdc0e9c1cb41a3b1b1ed11ddd8ded602464c3fc6020494"}, - {file = "lazy_object_proxy-1.9.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:09763491ce220c0299688940f8dc2c5d05fd1f45af1e42e636b2e8b2303e4382"}, - {file = "lazy_object_proxy-1.9.0-cp39-cp39-win32.whl", hash = "sha256:9090d8e53235aa280fc9239a86ae3ea8ac58eff66a705fa6aa2ec4968b95c821"}, - {file = "lazy_object_proxy-1.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:db1c1722726f47e10e0b5fdbf15ac3b8adb58c091d12b3ab713965795036985f"}, -] - -[[package]] -name = "matplotlib-inline" -version = "0.1.6" -description = "Inline Matplotlib backend for Jupyter" -category = "main" -optional = false -python-versions = ">=3.5" -files = [ - {file = "matplotlib-inline-0.1.6.tar.gz", hash = "sha256:f887e5f10ba98e8d2b150ddcf4702c1e5f8b3a20005eb0f74bfdbd360ee6f304"}, - {file = "matplotlib_inline-0.1.6-py3-none-any.whl", hash = "sha256:f1f41aab5328aa5aaea9b16d083b128102f8712542f819fe7e6a420ff581b311"}, -] - -[package.dependencies] -traitlets = "*" - -[[package]] -name = "mccabe" -version = "0.7.0" -description = "McCabe checker, plugin for flake8" -category = "dev" -optional = false -python-versions = ">=3.6" -files = [ - {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, - {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, -] - -[[package]] -name = "openmldb" -version = "0.7.3" -description = "OpenMLDB Python SDK" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "openmldb-0.7.3-py3-none-macosx_10_15_x86_64.whl", hash = "sha256:1b2ee03731a2d8ace198dcf1240800d47fcc0f7c72edf02bd312694ce1045264"}, - {file = "openmldb-0.7.3-py3-none-manylinux1_x86_64.whl", hash = "sha256:ee3e4a5346eafb7defa740db12bba50a40bdf348f60d43ee421be579ee47c744"}, -] - -[package.dependencies] -IPython = "*" -prettytable = "*" -sqlalchemy = "<=1.4.9" - -[package.extras] -test = ["pytest", "tox"] - -[[package]] -name = "packaging" -version = "23.1" -description = "Core utilities for Python packages" -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ - {file = "packaging-23.1-py3-none-any.whl", hash = "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61"}, - {file = "packaging-23.1.tar.gz", hash = "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"}, -] - -[[package]] -name = "parso" -version = "0.8.3" -description = "A Python Parser" -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "parso-0.8.3-py2.py3-none-any.whl", hash = "sha256:c001d4636cd3aecdaf33cbb40aebb59b094be2a74c556778ef5576c175e19e75"}, - {file = "parso-0.8.3.tar.gz", hash = "sha256:8c07be290bb59f03588915921e29e8a50002acaf2cdc5fa0e0114f91709fafa0"}, -] - -[package.extras] -qa = ["flake8 (==3.8.3)", "mypy (==0.782)"] -testing = ["docopt", "pytest (<6.0.0)"] - -[[package]] -name = "pexpect" -version = "4.8.0" -description = "Pexpect allows easy control of interactive console applications." -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "pexpect-4.8.0-py2.py3-none-any.whl", hash = "sha256:0b48a55dcb3c05f3329815901ea4fc1537514d6ba867a152b581d69ae3710937"}, - {file = "pexpect-4.8.0.tar.gz", hash = "sha256:fc65a43959d153d0114afe13997d439c22823a27cefceb5ff35c2178c6784c0c"}, -] - -[package.dependencies] -ptyprocess = ">=0.5" - -[[package]] -name = "pickleshare" -version = "0.7.5" -description = "Tiny 'shelve'-like database with concurrency support" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "pickleshare-0.7.5-py2.py3-none-any.whl", hash = "sha256:9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56"}, - {file = "pickleshare-0.7.5.tar.gz", hash = "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca"}, -] - -[[package]] -name = "platformdirs" -version = "3.4.0" -description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ - {file = "platformdirs-3.4.0-py3-none-any.whl", hash = "sha256:01437886022decaf285d8972f9526397bfae2ac55480ed372ed6d9eca048870a"}, - {file = "platformdirs-3.4.0.tar.gz", hash = "sha256:a5e1536e5ea4b1c238a1364da17ff2993d5bd28e15600c2c8224008aff6bbcad"}, -] - -[package.extras] -docs = ["furo (>=2023.3.27)", "proselint (>=0.13)", "sphinx (>=6.1.3)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.3.1)", "pytest-cov (>=4)", "pytest-mock (>=3.10)"] - -[[package]] -name = "pluggy" -version = "1.0.0" -description = "plugin and hook calling mechanisms for python" -category = "dev" -optional = false -python-versions = ">=3.6" -files = [ - {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, - {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, -] - -[package.extras] -dev = ["pre-commit", "tox"] -testing = ["pytest", "pytest-benchmark"] - -[[package]] -name = "prettytable" -version = "3.7.0" -description = "A simple Python library for easily displaying tabular data in a visually appealing ASCII table format" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "prettytable-3.7.0-py3-none-any.whl", hash = "sha256:f4aaf2ed6e6062a82fd2e6e5289bbbe705ec2788fe401a3a1f62a1cea55526d2"}, - {file = "prettytable-3.7.0.tar.gz", hash = "sha256:ef8334ee40b7ec721651fc4d37ecc7bb2ef55fde5098d994438f0dfdaa385c0c"}, -] - -[package.dependencies] -wcwidth = "*" - -[package.extras] -tests = ["pytest", "pytest-cov", "pytest-lazy-fixture"] - -[[package]] -name = "prometheus-client" -version = "0.16.0" -description = "Python client for the Prometheus monitoring system." -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "prometheus_client-0.16.0-py3-none-any.whl", hash = "sha256:0836af6eb2c8f4fed712b2f279f6c0a8bbab29f9f4aa15276b91c7cb0d1616ab"}, - {file = "prometheus_client-0.16.0.tar.gz", hash = "sha256:a03e35b359f14dd1630898543e2120addfdeacd1a6069c1367ae90fd93ad3f48"}, -] - -[package.extras] -twisted = ["twisted"] - -[[package]] -name = "prompt-toolkit" -version = "3.0.38" -description = "Library for building powerful interactive command lines in Python" -category = "main" -optional = false -python-versions = ">=3.7.0" -files = [ - {file = "prompt_toolkit-3.0.38-py3-none-any.whl", hash = "sha256:45ea77a2f7c60418850331366c81cf6b5b9cf4c7fd34616f733c5427e6abbb1f"}, - {file = "prompt_toolkit-3.0.38.tar.gz", hash = "sha256:23ac5d50538a9a38c8bde05fecb47d0b403ecd0662857a86f886f798563d5b9b"}, -] - -[package.dependencies] -wcwidth = "*" - -[[package]] -name = "ptyprocess" -version = "0.7.0" -description = "Run a subprocess in a pseudo terminal" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"}, - {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"}, -] - -[[package]] -name = "pure-eval" -version = "0.2.2" -description = "Safely evaluate AST nodes without side effects" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "pure_eval-0.2.2-py3-none-any.whl", hash = "sha256:01eaab343580944bc56080ebe0a674b39ec44a945e6d09ba7db3cb8cec289350"}, - {file = "pure_eval-0.2.2.tar.gz", hash = "sha256:2b45320af6dfaa1750f543d714b6d1c520a1688dec6fd24d339063ce0aaa9ac3"}, -] - -[package.extras] -tests = ["pytest"] - -[[package]] -name = "pygments" -version = "2.15.1" -description = "Pygments is a syntax highlighting package written in Python." -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "Pygments-2.15.1-py3-none-any.whl", hash = "sha256:db2db3deb4b4179f399a09054b023b6a586b76499d36965813c71aa8ed7b5fd1"}, - {file = "Pygments-2.15.1.tar.gz", hash = "sha256:8ace4d3c1dd481894b2005f560ead0f9f19ee64fe983366be1a21e171d12775c"}, -] - -[package.extras] -plugins = ["importlib-metadata"] - -[[package]] -name = "pylint" -version = "2.17.3" -description = "python code static checker" -category = "dev" -optional = false -python-versions = ">=3.7.2" -files = [ - {file = "pylint-2.17.3-py3-none-any.whl", hash = "sha256:a6cbb4c6e96eab4a3c7de7c6383c512478f58f88d95764507d84c899d656a89a"}, - {file = "pylint-2.17.3.tar.gz", hash = "sha256:761907349e699f8afdcd56c4fe02f3021ab5b3a0fc26d19a9bfdc66c7d0d5cd5"}, -] - -[package.dependencies] -astroid = ">=2.15.4,<=2.17.0-dev0" -colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} -dill = [ - {version = ">=0.2", markers = "python_version < \"3.11\""}, - {version = ">=0.3.6", markers = "python_version >= \"3.11\""}, -] -isort = ">=4.2.5,<6" -mccabe = ">=0.6,<0.8" -platformdirs = ">=2.2.0" -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} -tomlkit = ">=0.10.1" -typing-extensions = {version = ">=3.10.0", markers = "python_version < \"3.10\""} - -[package.extras] -spelling = ["pyenchant (>=3.2,<4.0)"] -testutils = ["gitpython (>3)"] - -[[package]] -name = "pytest" -version = "7.3.1" -description = "pytest: simple powerful testing with Python" -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ - {file = "pytest-7.3.1-py3-none-any.whl", hash = "sha256:3799fa815351fea3a5e96ac7e503a96fa51cc9942c3753cda7651b93c1cfa362"}, - {file = "pytest-7.3.1.tar.gz", hash = "sha256:434afafd78b1d78ed0addf160ad2b77a30d35d4bdf8af234fe621919d9ed15e3"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "sys_platform == \"win32\""} -exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} -iniconfig = "*" -packaging = "*" -pluggy = ">=0.12,<2.0" -tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} - -[package.extras] -testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"] - -[[package]] -name = "requests" -version = "2.31.0" -description = "Python HTTP for Humans." -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ - {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, - {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, -] - -[package.dependencies] -certifi = ">=2017.4.17" -charset-normalizer = ">=2,<4" -idna = ">=2.5,<4" -urllib3 = ">=1.21.1,<3" - -[package.extras] -socks = ["PySocks (>=1.5.6,!=1.5.7)"] -use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] - -[[package]] -name = "setuptools" -version = "67.7.2" -description = "Easily download, build, install, upgrade, and uninstall Python packages" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "setuptools-67.7.2-py3-none-any.whl", hash = "sha256:23aaf86b85ca52ceb801d32703f12d77517b2556af839621c641fca11287952b"}, - {file = "setuptools-67.7.2.tar.gz", hash = "sha256:f104fa03692a2602fa0fec6c6a9e63b6c8a968de13e17c026957dd1f53d80990"}, -] - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] -testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] - -[[package]] -name = "six" -version = "1.16.0" -description = "Python 2 and 3 compatibility utilities" -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" -files = [ - {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, - {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, -] - -[[package]] -name = "sqlalchemy" -version = "1.4.9" -description = "Database Abstraction Library" -category = "main" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" -files = [ - {file = "SQLAlchemy-1.4.9-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:e26791ac43806dec1f18d328596db87f1b37f9d8271997dd1233054b4c377f51"}, - {file = "SQLAlchemy-1.4.9-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:c4485040d86d4b3d9aa509fd3c492de3687d9bf52fb85d66b33912ad068a088c"}, - {file = "SQLAlchemy-1.4.9-cp27-cp27m-win32.whl", hash = "sha256:a8763fe4de02f746666161b130cc3e5d1494a6f5475f5622f05251739fc22e55"}, - {file = "SQLAlchemy-1.4.9-cp27-cp27m-win_amd64.whl", hash = "sha256:e7d262415e4adf148441bd9f10ae4e5498d6649962fabc62a64ec7b4891d56c5"}, - {file = "SQLAlchemy-1.4.9-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:c6f228b79fd757d9ca539c9958190b3a44308f743dc7d83575aa0891033f6c86"}, - {file = "SQLAlchemy-1.4.9-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:cfbf2cf8e8ef0a1d23bfd0fa387057e6e522d55e43821f1d115941d913ee7762"}, - {file = "SQLAlchemy-1.4.9-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:815a8cdf9c0fa504d0bfbe83fb3e596b7663fc828b73259a20299c01330467aa"}, - {file = "SQLAlchemy-1.4.9-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:cfa4a336de7d32ae30b54f7b8ec888fb5c6313a1b7419a9d7b3f49cdd83012a3"}, - {file = "SQLAlchemy-1.4.9-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:065ac7331b87494a86bf3dc4430c1ee7779d6dc532213c528394ddd00804e518"}, - {file = "SQLAlchemy-1.4.9-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:690fbca2a208314504a2ab46d3e7dae320247fcb1967863b9782a70bf49fc600"}, - {file = "SQLAlchemy-1.4.9-cp36-cp36m-win32.whl", hash = "sha256:4edff2b4101a1c442fb1b17d594a5fdf99145f27c5eaffae12c26aef2bb2bf65"}, - {file = "SQLAlchemy-1.4.9-cp36-cp36m-win_amd64.whl", hash = "sha256:6c6090d73820dcf04549f0b6e80f67b46c8191f0e40bf09c6d6f8ece2464e8b6"}, - {file = "SQLAlchemy-1.4.9-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:fc82688695eacf77befc3d839df2bc7ff314cd1d547f120835acdcbac1a480b8"}, - {file = "SQLAlchemy-1.4.9-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:4e554872766d2783abf0a11704536596e8794229fb0fa63d311a74caae58c6c5"}, - {file = "SQLAlchemy-1.4.9-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:bce6eaf7b9a3a445911e225570b8fd26b7e98654ac9f308a8a52addb64a2a488"}, - {file = "SQLAlchemy-1.4.9-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:25aaf0bec9eadde9789e3c0178c718ae6923b57485fdeae85999bc3089d9b871"}, - {file = "SQLAlchemy-1.4.9-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:f239778cf03cd46da4962636501f6dea55af9b4684cd7ceee104ad4f0290e878"}, - {file = "SQLAlchemy-1.4.9-cp37-cp37m-win32.whl", hash = "sha256:b0266e133d819d33b555798822606e876187a96798e2d8c9b7f85e419d73ef94"}, - {file = "SQLAlchemy-1.4.9-cp37-cp37m-win_amd64.whl", hash = "sha256:230b210fc6d1af5d555d1d04ff9bd4259d6ab82b020369724ab4a1c805a32dd3"}, - {file = "SQLAlchemy-1.4.9-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:a28c7b96bc5beef585172ca9d79068ae7fa2527feaa26bd63371851d7894c66f"}, - {file = "SQLAlchemy-1.4.9-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:457a1652bc1c5f832165ff341380b3742bfb98b9ceca24576350992713ad700f"}, - {file = "SQLAlchemy-1.4.9-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:e9e95568eafae18ac40d00694b82dc3febe653f81eee83204ef248563f39696d"}, - {file = "SQLAlchemy-1.4.9-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:0d8aab144cf8d31c1ac834802c7df4430248f74bd8b3ed3149f9c9eec0eafe50"}, - {file = "SQLAlchemy-1.4.9-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:cde2cf3ee76e8c538f2f43f5cf9252ad53404fc350801191128bab68f335a8b2"}, - {file = "SQLAlchemy-1.4.9-cp38-cp38-win32.whl", hash = "sha256:bb97aeaa699c43da62e35856ab56e5154d062c09a3593a2c12c67d6a21059920"}, - {file = "SQLAlchemy-1.4.9-cp38-cp38-win_amd64.whl", hash = "sha256:fbdcf9019e92253fc6aa0bcd5937302664c3a4d53884c425c0caa994e56c4421"}, - {file = "SQLAlchemy-1.4.9-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:2e1b8d31c97a2b91aea8ed8299ad360a32d60728a89f2aac9c98eef07a633a0e"}, - {file = "SQLAlchemy-1.4.9-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:7bdb0f972bc35054c05088e91cec8fa810c3aa565b690bae75c005ee430e12e8"}, - {file = "SQLAlchemy-1.4.9-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:ec7c33e22beac16b4c5348c41cd94cfee056152e55a0efc62843deebfc53fcb4"}, - {file = "SQLAlchemy-1.4.9-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:29816a338982c30dd7ee76c4e79f17d5991abb1b6561e9f1d72703d030a79c86"}, - {file = "SQLAlchemy-1.4.9-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:099e63ffad329989080c533896267c40f9cb38ed5704168f7dae3afdda121e10"}, - {file = "SQLAlchemy-1.4.9-cp39-cp39-win32.whl", hash = "sha256:343c679899afdc4952ac659dc46f2075a2bd4fba87ca0df264be838eecd02096"}, - {file = "SQLAlchemy-1.4.9-cp39-cp39-win_amd64.whl", hash = "sha256:386f215248c3fb2fab9bb77f631bc3c6cd38354ca2363d241784f8297d16b80a"}, - {file = "SQLAlchemy-1.4.9.tar.gz", hash = "sha256:f31757972677fbe9132932a69a4f23db59187a072cc26427f56a3082b46b6dac"}, -] - -[package.dependencies] -greenlet = {version = "!=0.4.17", markers = "python_version >= \"3\""} - -[package.extras] -aiomysql = ["aiomysql", "greenlet (!=0.4.17)"] -aiosqlite = ["aiosqlite", "greenlet (!=0.4.17)"] -asyncio = ["greenlet (!=0.4.17)"] -mariadb-connector = ["mariadb (>=1.0.1)"] -mssql = ["pyodbc"] -mssql-pymssql = ["pymssql"] -mssql-pyodbc = ["pyodbc"] -mypy = ["mypy (>=0.800)", "sqlalchemy2-stubs"] -mysql = ["mysqlclient (>=1.4.0)", "mysqlclient (>=1.4.0,<2)"] -mysql-connector = ["mysqlconnector"] -oracle = ["cx-oracle (>=7)", "cx-oracle (>=7,<8)"] -postgresql = ["psycopg2 (>=2.7)"] -postgresql-asyncpg = ["asyncpg", "greenlet (!=0.4.17)"] -postgresql-pg8000 = ["pg8000 (>=1.16.6)"] -postgresql-psycopg2binary = ["psycopg2-binary"] -postgresql-psycopg2cffi = ["psycopg2cffi"] -pymysql = ["pymysql", "pymysql (<1)"] -sqlcipher = ["sqlcipher3-binary"] - -[[package]] -name = "stack-data" -version = "0.6.2" -description = "Extract data from python stack frames and tracebacks for informative displays" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "stack_data-0.6.2-py3-none-any.whl", hash = "sha256:cbb2a53eb64e5785878201a97ed7c7b94883f48b87bfb0bbe8b623c74679e4a8"}, - {file = "stack_data-0.6.2.tar.gz", hash = "sha256:32d2dd0376772d01b6cb9fc996f3c8b57a357089dec328ed4b6553d037eaf815"}, -] - -[package.dependencies] -asttokens = ">=2.1.0" -executing = ">=1.2.0" -pure-eval = "*" - -[package.extras] -tests = ["cython", "littleutils", "pygments", "pytest", "typeguard"] - -[[package]] -name = "tomli" -version = "2.0.1" -description = "A lil' TOML parser" -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ - {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, - {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, -] - -[[package]] -name = "tomlkit" -version = "0.11.7" -description = "Style preserving TOML library" -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ - {file = "tomlkit-0.11.7-py3-none-any.whl", hash = "sha256:5325463a7da2ef0c6bbfefb62a3dc883aebe679984709aee32a317907d0a8d3c"}, - {file = "tomlkit-0.11.7.tar.gz", hash = "sha256:f392ef70ad87a672f02519f99967d28a4d3047133e2d1df936511465fbb3791d"}, -] - -[[package]] -name = "traitlets" -version = "5.9.0" -description = "Traitlets Python configuration system" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "traitlets-5.9.0-py3-none-any.whl", hash = "sha256:9e6ec080259b9a5940c797d58b613b5e31441c2257b87c2e795c5228ae80d2d8"}, - {file = "traitlets-5.9.0.tar.gz", hash = "sha256:f6cde21a9c68cf756af02035f72d5a723bf607e862e7be33ece505abf4a3bad9"}, -] - -[package.extras] -docs = ["myst-parser", "pydata-sphinx-theme", "sphinx"] -test = ["argcomplete (>=2.0)", "pre-commit", "pytest", "pytest-mock"] - -[[package]] -name = "twisted" -version = "22.10.0" -description = "An asynchronous networking framework written in Python" -category = "main" -optional = false -python-versions = ">=3.7.1" -files = [ - {file = "Twisted-22.10.0-py3-none-any.whl", hash = "sha256:86c55f712cc5ab6f6d64e02503352464f0400f66d4f079096d744080afcccbd0"}, - {file = "Twisted-22.10.0.tar.gz", hash = "sha256:32acbd40a94f5f46e7b42c109bfae2b302250945561783a8b7a059048f2d4d31"}, -] - -[package.dependencies] -attrs = ">=19.2.0" -Automat = ">=0.8.0" -constantly = ">=15.1" -hyperlink = ">=17.1.1" -incremental = ">=21.3.0" -twisted-iocpsupport = {version = ">=1.0.2,<2", markers = "platform_system == \"Windows\""} -typing-extensions = ">=3.6.5" -"zope.interface" = ">=4.4.2" - -[package.extras] -all-non-platform = ["PyHamcrest (>=1.9.0)", "appdirs (>=1.4.0)", "bcrypt (>=3.0.0)", "contextvars (>=2.4,<3)", "cryptography (>=2.6)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.0,<5.0)", "hypothesis (>=6.0,<7.0)", "idna (>=2.4)", "priority (>=1.1.0,<2.0)", "pyasn1", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "pywin32 (!=226)", "service-identity (>=18.1.0)"] -conch = ["appdirs (>=1.4.0)", "bcrypt (>=3.0.0)", "cryptography (>=2.6)", "pyasn1"] -conch-nacl = ["PyNaCl", "appdirs (>=1.4.0)", "bcrypt (>=3.0.0)", "cryptography (>=2.6)", "pyasn1"] -contextvars = ["contextvars (>=2.4,<3)"] -dev = ["coverage (>=6b1,<7)", "pydoctor (>=22.9.0,<22.10.0)", "pyflakes (>=2.2,<3.0)", "python-subunit (>=1.4,<2.0)", "readthedocs-sphinx-ext (>=2.1,<3.0)", "sphinx (>=5.0,<6)", "sphinx-rtd-theme (>=1.0,<2.0)", "towncrier (>=22.8,<23.0)", "twistedchecker (>=0.7,<1.0)"] -dev-release = ["pydoctor (>=22.9.0,<22.10.0)", "readthedocs-sphinx-ext (>=2.1,<3.0)", "sphinx (>=5.0,<6)", "sphinx-rtd-theme (>=1.0,<2.0)", "towncrier (>=22.8,<23.0)"] -gtk-platform = ["PyHamcrest (>=1.9.0)", "appdirs (>=1.4.0)", "bcrypt (>=3.0.0)", "contextvars (>=2.4,<3)", "cryptography (>=2.6)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.0,<5.0)", "hypothesis (>=6.0,<7.0)", "idna (>=2.4)", "priority (>=1.1.0,<2.0)", "pyasn1", "pygobject", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "pywin32 (!=226)", "service-identity (>=18.1.0)"] -http2 = ["h2 (>=3.0,<5.0)", "priority (>=1.1.0,<2.0)"] -macos-platform = ["PyHamcrest (>=1.9.0)", "appdirs (>=1.4.0)", "bcrypt (>=3.0.0)", "contextvars (>=2.4,<3)", "cryptography (>=2.6)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.0,<5.0)", "hypothesis (>=6.0,<7.0)", "idna (>=2.4)", "priority (>=1.1.0,<2.0)", "pyasn1", "pyobjc-core", "pyobjc-framework-CFNetwork", "pyobjc-framework-Cocoa", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "pywin32 (!=226)", "service-identity (>=18.1.0)"] -mypy = ["PyHamcrest (>=1.9.0)", "PyNaCl", "appdirs (>=1.4.0)", "bcrypt (>=3.0.0)", "contextvars (>=2.4,<3)", "coverage (>=6b1,<7)", "cryptography (>=2.6)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.0,<5.0)", "hypothesis (>=6.0,<7.0)", "idna (>=2.4)", "mypy (==0.930)", "mypy-zope (==0.3.4)", "priority (>=1.1.0,<2.0)", "pyasn1", "pydoctor (>=22.9.0,<22.10.0)", "pyflakes (>=2.2,<3.0)", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "python-subunit (>=1.4,<2.0)", "pywin32 (!=226)", "readthedocs-sphinx-ext (>=2.1,<3.0)", "service-identity (>=18.1.0)", "sphinx (>=5.0,<6)", "sphinx-rtd-theme (>=1.0,<2.0)", "towncrier (>=22.8,<23.0)", "twistedchecker (>=0.7,<1.0)", "types-pyOpenSSL", "types-setuptools"] -osx-platform = ["PyHamcrest (>=1.9.0)", "appdirs (>=1.4.0)", "bcrypt (>=3.0.0)", "contextvars (>=2.4,<3)", "cryptography (>=2.6)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.0,<5.0)", "hypothesis (>=6.0,<7.0)", "idna (>=2.4)", "priority (>=1.1.0,<2.0)", "pyasn1", "pyobjc-core", "pyobjc-framework-CFNetwork", "pyobjc-framework-Cocoa", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "pywin32 (!=226)", "service-identity (>=18.1.0)"] -serial = ["pyserial (>=3.0)", "pywin32 (!=226)"] -test = ["PyHamcrest (>=1.9.0)", "cython-test-exception-raiser (>=1.0.2,<2)", "hypothesis (>=6.0,<7.0)"] -tls = ["idna (>=2.4)", "pyopenssl (>=21.0.0)", "service-identity (>=18.1.0)"] -windows-platform = ["PyHamcrest (>=1.9.0)", "appdirs (>=1.4.0)", "bcrypt (>=3.0.0)", "contextvars (>=2.4,<3)", "cryptography (>=2.6)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.0,<5.0)", "hypothesis (>=6.0,<7.0)", "idna (>=2.4)", "priority (>=1.1.0,<2.0)", "pyasn1", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "pywin32 (!=226)", "pywin32 (!=226)", "service-identity (>=18.1.0)"] - -[[package]] -name = "twisted-iocpsupport" -version = "1.0.3" -description = "An extension for use in the twisted I/O Completion Ports reactor." -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "twisted-iocpsupport-1.0.3.tar.gz", hash = "sha256:afb00801fdfbaccf0d0173a722626500023d4a19719ac9f129d1347a32e2fc66"}, - {file = "twisted_iocpsupport-1.0.3-cp310-cp310-win32.whl", hash = "sha256:a379ef56a576c8090889f74441bc3822ca31ac82253cc61e8d50631bcb0c26d0"}, - {file = "twisted_iocpsupport-1.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:1ea2c3fbdb739c95cc8b3355305cd593d2c9ec56d709207aa1a05d4d98671e85"}, - {file = "twisted_iocpsupport-1.0.3-cp311-cp311-win32.whl", hash = "sha256:7efcdfafb377f32db90f42bd5fc5bb32cd1e3637ee936cdaf3aff4f4786ab3bf"}, - {file = "twisted_iocpsupport-1.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:1dbfac706972bf9ec5ce1ddbc735d2ebba406ad363345df8751ffd5252aa1618"}, - {file = "twisted_iocpsupport-1.0.3-cp36-cp36m-win32.whl", hash = "sha256:1ddfc5fa22ec6f913464b736b3f46e642237f17ac41be47eed6fa9bd52f5d0e0"}, - {file = "twisted_iocpsupport-1.0.3-cp36-cp36m-win_amd64.whl", hash = "sha256:1bdccbb22199fc69fd7744d6d2dfd22d073c028c8611d994b41d2d2ad0e0f40d"}, - {file = "twisted_iocpsupport-1.0.3-cp37-cp37m-win32.whl", hash = "sha256:db11c80054b52dbdea44d63d5474a44c9a6531882f0e2960268b15123088641a"}, - {file = "twisted_iocpsupport-1.0.3-cp37-cp37m-win_amd64.whl", hash = "sha256:67bec1716eb8f466ef366bbf262e1467ecc9e20940111207663ac24049785bad"}, - {file = "twisted_iocpsupport-1.0.3-cp38-cp38-win32.whl", hash = "sha256:98a6f16ab215f8c1446e9fc60aaed0ab7c746d566aa2f3492a23cea334e6bebb"}, - {file = "twisted_iocpsupport-1.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:4f249d0baac836bb431d6fa0178be063a310136bc489465a831e3abd2d7acafd"}, - {file = "twisted_iocpsupport-1.0.3-cp39-cp39-win32.whl", hash = "sha256:aaca8f30c3b7c80d27a33fe9fe0d0bac42b1b012ddc60f677175c30e1becc1f3"}, - {file = "twisted_iocpsupport-1.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:dff43136c33665c2d117a73706aef6f7d6433e5c4560332a118fe066b16b8695"}, - {file = "twisted_iocpsupport-1.0.3-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:8faceae553cfadc42ad791b1790e7cdecb7751102608c405217f6a26e877e0c5"}, - {file = "twisted_iocpsupport-1.0.3-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:6f8c433faaad5d53d30d1da6968d5a3730df415e2efb6864847267a9b51290cd"}, - {file = "twisted_iocpsupport-1.0.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:3f39c41c0213a81a9ce0961e30d0d7650f371ad80f8d261007d15a2deb6d5be3"}, -] - -[[package]] -name = "typing-extensions" -version = "4.5.0" -description = "Backported and Experimental Type Hints for Python 3.7+" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "typing_extensions-4.5.0-py3-none-any.whl", hash = "sha256:fb33085c39dd998ac16d1431ebc293a8b3eedd00fd4a32de0ff79002c19511b4"}, - {file = "typing_extensions-4.5.0.tar.gz", hash = "sha256:5cb5f4a79139d699607b3ef622a1dedafa84e115ab0024e0d9c044a9479ca7cb"}, -] - -[[package]] -name = "urllib3" -version = "1.26.15" -description = "HTTP library with thread-safe connection pooling, file post, and more." -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" -files = [ - {file = "urllib3-1.26.15-py2.py3-none-any.whl", hash = "sha256:aa751d169e23c7479ce47a0cb0da579e3ede798f994f5816a74e4f4500dcea42"}, - {file = "urllib3-1.26.15.tar.gz", hash = "sha256:8a388717b9476f934a21484e8c8e61875ab60644d29b9b39e11e4b9dc1c6b305"}, -] - -[package.extras] -brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"] -secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] -socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] - -[[package]] -name = "wcwidth" -version = "0.2.6" -description = "Measures the displayed width of unicode strings in a terminal" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "wcwidth-0.2.6-py2.py3-none-any.whl", hash = "sha256:795b138f6875577cd91bba52baf9e445cd5118fd32723b460e30a0af30ea230e"}, - {file = "wcwidth-0.2.6.tar.gz", hash = "sha256:a5220780a404dbe3353789870978e472cfe477761f06ee55077256e509b156d0"}, -] - -[[package]] -name = "wrapt" -version = "1.15.0" -description = "Module for decorators, wrappers and monkey patching." -category = "dev" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" -files = [ - {file = "wrapt-1.15.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:ca1cccf838cd28d5a0883b342474c630ac48cac5df0ee6eacc9c7290f76b11c1"}, - {file = "wrapt-1.15.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:e826aadda3cae59295b95343db8f3d965fb31059da7de01ee8d1c40a60398b29"}, - {file = "wrapt-1.15.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:5fc8e02f5984a55d2c653f5fea93531e9836abbd84342c1d1e17abc4a15084c2"}, - {file = "wrapt-1.15.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:96e25c8603a155559231c19c0349245eeb4ac0096fe3c1d0be5c47e075bd4f46"}, - {file = "wrapt-1.15.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:40737a081d7497efea35ab9304b829b857f21558acfc7b3272f908d33b0d9d4c"}, - {file = "wrapt-1.15.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:f87ec75864c37c4c6cb908d282e1969e79763e0d9becdfe9fe5473b7bb1e5f09"}, - {file = "wrapt-1.15.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:1286eb30261894e4c70d124d44b7fd07825340869945c79d05bda53a40caa079"}, - {file = "wrapt-1.15.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:493d389a2b63c88ad56cdc35d0fa5752daac56ca755805b1b0c530f785767d5e"}, - {file = "wrapt-1.15.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:58d7a75d731e8c63614222bcb21dd992b4ab01a399f1f09dd82af17bbfc2368a"}, - {file = "wrapt-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:21f6d9a0d5b3a207cdf7acf8e58d7d13d463e639f0c7e01d82cdb671e6cb7923"}, - {file = "wrapt-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ce42618f67741d4697684e501ef02f29e758a123aa2d669e2d964ff734ee00ee"}, - {file = "wrapt-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41d07d029dd4157ae27beab04d22b8e261eddfc6ecd64ff7000b10dc8b3a5727"}, - {file = "wrapt-1.15.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54accd4b8bc202966bafafd16e69da9d5640ff92389d33d28555c5fd4f25ccb7"}, - {file = "wrapt-1.15.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fbfbca668dd15b744418265a9607baa970c347eefd0db6a518aaf0cfbd153c0"}, - {file = "wrapt-1.15.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:76e9c727a874b4856d11a32fb0b389afc61ce8aaf281ada613713ddeadd1cfec"}, - {file = "wrapt-1.15.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e20076a211cd6f9b44a6be58f7eeafa7ab5720eb796975d0c03f05b47d89eb90"}, - {file = "wrapt-1.15.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a74d56552ddbde46c246b5b89199cb3fd182f9c346c784e1a93e4dc3f5ec9975"}, - {file = "wrapt-1.15.0-cp310-cp310-win32.whl", hash = "sha256:26458da5653aa5b3d8dc8b24192f574a58984c749401f98fff994d41d3f08da1"}, - {file = "wrapt-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:75760a47c06b5974aa5e01949bf7e66d2af4d08cb8c1d6516af5e39595397f5e"}, - {file = "wrapt-1.15.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ba1711cda2d30634a7e452fc79eabcadaffedf241ff206db2ee93dd2c89a60e7"}, - {file = "wrapt-1.15.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:56374914b132c702aa9aa9959c550004b8847148f95e1b824772d453ac204a72"}, - {file = "wrapt-1.15.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a89ce3fd220ff144bd9d54da333ec0de0399b52c9ac3d2ce34b569cf1a5748fb"}, - {file = "wrapt-1.15.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3bbe623731d03b186b3d6b0d6f51865bf598587c38d6f7b0be2e27414f7f214e"}, - {file = "wrapt-1.15.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3abbe948c3cbde2689370a262a8d04e32ec2dd4f27103669a45c6929bcdbfe7c"}, - {file = "wrapt-1.15.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b67b819628e3b748fd3c2192c15fb951f549d0f47c0449af0764d7647302fda3"}, - {file = "wrapt-1.15.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:7eebcdbe3677e58dd4c0e03b4f2cfa346ed4049687d839adad68cc38bb559c92"}, - {file = "wrapt-1.15.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:74934ebd71950e3db69960a7da29204f89624dde411afbfb3b4858c1409b1e98"}, - {file = "wrapt-1.15.0-cp311-cp311-win32.whl", hash = "sha256:bd84395aab8e4d36263cd1b9308cd504f6cf713b7d6d3ce25ea55670baec5416"}, - {file = "wrapt-1.15.0-cp311-cp311-win_amd64.whl", hash = "sha256:a487f72a25904e2b4bbc0817ce7a8de94363bd7e79890510174da9d901c38705"}, - {file = "wrapt-1.15.0-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:4ff0d20f2e670800d3ed2b220d40984162089a6e2c9646fdb09b85e6f9a8fc29"}, - {file = "wrapt-1.15.0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:9ed6aa0726b9b60911f4aed8ec5b8dd7bf3491476015819f56473ffaef8959bd"}, - {file = "wrapt-1.15.0-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:896689fddba4f23ef7c718279e42f8834041a21342d95e56922e1c10c0cc7afb"}, - {file = "wrapt-1.15.0-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:75669d77bb2c071333417617a235324a1618dba66f82a750362eccbe5b61d248"}, - {file = "wrapt-1.15.0-cp35-cp35m-win32.whl", hash = "sha256:fbec11614dba0424ca72f4e8ba3c420dba07b4a7c206c8c8e4e73f2e98f4c559"}, - {file = "wrapt-1.15.0-cp35-cp35m-win_amd64.whl", hash = "sha256:fd69666217b62fa5d7c6aa88e507493a34dec4fa20c5bd925e4bc12fce586639"}, - {file = "wrapt-1.15.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:b0724f05c396b0a4c36a3226c31648385deb6a65d8992644c12a4963c70326ba"}, - {file = "wrapt-1.15.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bbeccb1aa40ab88cd29e6c7d8585582c99548f55f9b2581dfc5ba68c59a85752"}, - {file = "wrapt-1.15.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:38adf7198f8f154502883242f9fe7333ab05a5b02de7d83aa2d88ea621f13364"}, - {file = "wrapt-1.15.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:578383d740457fa790fdf85e6d346fda1416a40549fe8db08e5e9bd281c6a475"}, - {file = "wrapt-1.15.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:a4cbb9ff5795cd66f0066bdf5947f170f5d63a9274f99bdbca02fd973adcf2a8"}, - {file = "wrapt-1.15.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:af5bd9ccb188f6a5fdda9f1f09d9f4c86cc8a539bd48a0bfdc97723970348418"}, - {file = "wrapt-1.15.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:b56d5519e470d3f2fe4aa7585f0632b060d532d0696c5bdfb5e8319e1d0f69a2"}, - {file = "wrapt-1.15.0-cp36-cp36m-win32.whl", hash = "sha256:77d4c1b881076c3ba173484dfa53d3582c1c8ff1f914c6461ab70c8428b796c1"}, - {file = "wrapt-1.15.0-cp36-cp36m-win_amd64.whl", hash = "sha256:077ff0d1f9d9e4ce6476c1a924a3332452c1406e59d90a2cf24aeb29eeac9420"}, - {file = "wrapt-1.15.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5c5aa28df055697d7c37d2099a7bc09f559d5053c3349b1ad0c39000e611d317"}, - {file = "wrapt-1.15.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3a8564f283394634a7a7054b7983e47dbf39c07712d7b177b37e03f2467a024e"}, - {file = "wrapt-1.15.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:780c82a41dc493b62fc5884fb1d3a3b81106642c5c5c78d6a0d4cbe96d62ba7e"}, - {file = "wrapt-1.15.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e169e957c33576f47e21864cf3fc9ff47c223a4ebca8960079b8bd36cb014fd0"}, - {file = "wrapt-1.15.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:b02f21c1e2074943312d03d243ac4388319f2456576b2c6023041c4d57cd7019"}, - {file = "wrapt-1.15.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f2e69b3ed24544b0d3dbe2c5c0ba5153ce50dcebb576fdc4696d52aa22db6034"}, - {file = "wrapt-1.15.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d787272ed958a05b2c86311d3a4135d3c2aeea4fc655705f074130aa57d71653"}, - {file = "wrapt-1.15.0-cp37-cp37m-win32.whl", hash = "sha256:02fce1852f755f44f95af51f69d22e45080102e9d00258053b79367d07af39c0"}, - {file = "wrapt-1.15.0-cp37-cp37m-win_amd64.whl", hash = "sha256:abd52a09d03adf9c763d706df707c343293d5d106aea53483e0ec8d9e310ad5e"}, - {file = "wrapt-1.15.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cdb4f085756c96a3af04e6eca7f08b1345e94b53af8921b25c72f096e704e145"}, - {file = "wrapt-1.15.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:230ae493696a371f1dbffaad3dafbb742a4d27a0afd2b1aecebe52b740167e7f"}, - {file = "wrapt-1.15.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63424c681923b9f3bfbc5e3205aafe790904053d42ddcc08542181a30a7a51bd"}, - {file = "wrapt-1.15.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d6bcbfc99f55655c3d93feb7ef3800bd5bbe963a755687cbf1f490a71fb7794b"}, - {file = "wrapt-1.15.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c99f4309f5145b93eca6e35ac1a988f0dc0a7ccf9ccdcd78d3c0adf57224e62f"}, - {file = "wrapt-1.15.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b130fe77361d6771ecf5a219d8e0817d61b236b7d8b37cc045172e574ed219e6"}, - {file = "wrapt-1.15.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:96177eb5645b1c6985f5c11d03fc2dbda9ad24ec0f3a46dcce91445747e15094"}, - {file = "wrapt-1.15.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d5fe3e099cf07d0fb5a1e23d399e5d4d1ca3e6dfcbe5c8570ccff3e9208274f7"}, - {file = "wrapt-1.15.0-cp38-cp38-win32.whl", hash = "sha256:abd8f36c99512755b8456047b7be10372fca271bf1467a1caa88db991e7c421b"}, - {file = "wrapt-1.15.0-cp38-cp38-win_amd64.whl", hash = "sha256:b06fa97478a5f478fb05e1980980a7cdf2712015493b44d0c87606c1513ed5b1"}, - {file = "wrapt-1.15.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2e51de54d4fb8fb50d6ee8327f9828306a959ae394d3e01a1ba8b2f937747d86"}, - {file = "wrapt-1.15.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0970ddb69bba00670e58955f8019bec4a42d1785db3faa043c33d81de2bf843c"}, - {file = "wrapt-1.15.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76407ab327158c510f44ded207e2f76b657303e17cb7a572ffe2f5a8a48aa04d"}, - {file = "wrapt-1.15.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cd525e0e52a5ff16653a3fc9e3dd827981917d34996600bbc34c05d048ca35cc"}, - {file = "wrapt-1.15.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d37ac69edc5614b90516807de32d08cb8e7b12260a285ee330955604ed9dd29"}, - {file = "wrapt-1.15.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:078e2a1a86544e644a68422f881c48b84fef6d18f8c7a957ffd3f2e0a74a0d4a"}, - {file = "wrapt-1.15.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:2cf56d0e237280baed46f0b5316661da892565ff58309d4d2ed7dba763d984b8"}, - {file = "wrapt-1.15.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:7dc0713bf81287a00516ef43137273b23ee414fe41a3c14be10dd95ed98a2df9"}, - {file = "wrapt-1.15.0-cp39-cp39-win32.whl", hash = "sha256:46ed616d5fb42f98630ed70c3529541408166c22cdfd4540b88d5f21006b0eff"}, - {file = "wrapt-1.15.0-cp39-cp39-win_amd64.whl", hash = "sha256:eef4d64c650f33347c1f9266fa5ae001440b232ad9b98f1f43dfe7a79435c0a6"}, - {file = "wrapt-1.15.0-py3-none-any.whl", hash = "sha256:64b1df0f83706b4ef4cfb4fb0e4c2669100fd7ecacfb59e091fad300d4e04640"}, - {file = "wrapt-1.15.0.tar.gz", hash = "sha256:d06730c6aed78cee4126234cf2d071e01b44b915e725a6cb439a879ec9754a3a"}, -] - -[[package]] -name = "zope-interface" -version = "6.0" -description = "Interfaces for Python" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "zope.interface-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f299c020c6679cb389814a3b81200fe55d428012c5e76da7e722491f5d205990"}, - {file = "zope.interface-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ee4b43f35f5dc15e1fec55ccb53c130adb1d11e8ad8263d68b1284b66a04190d"}, - {file = "zope.interface-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5a158846d0fca0a908c1afb281ddba88744d403f2550dc34405c3691769cdd85"}, - {file = "zope.interface-6.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f72f23bab1848edb7472309e9898603141644faec9fd57a823ea6b4d1c4c8995"}, - {file = "zope.interface-6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:48f4d38cf4b462e75fac78b6f11ad47b06b1c568eb59896db5b6ec1094eb467f"}, - {file = "zope.interface-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:87b690bbee9876163210fd3f500ee59f5803e4a6607d1b1238833b8885ebd410"}, - {file = "zope.interface-6.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f2363e5fd81afb650085c6686f2ee3706975c54f331b426800b53531191fdf28"}, - {file = "zope.interface-6.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:af169ba897692e9cd984a81cb0f02e46dacdc07d6cf9fd5c91e81f8efaf93d52"}, - {file = "zope.interface-6.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fa90bac61c9dc3e1a563e5babb3fd2c0c1c80567e815442ddbe561eadc803b30"}, - {file = "zope.interface-6.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:89086c9d3490a0f265a3c4b794037a84541ff5ffa28bb9c24cc9f66566968464"}, - {file = "zope.interface-6.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:809fe3bf1a91393abc7e92d607976bbb8586512913a79f2bf7d7ec15bd8ea518"}, - {file = "zope.interface-6.0-cp311-cp311-win_amd64.whl", hash = "sha256:0ec9653825f837fbddc4e4b603d90269b501486c11800d7c761eee7ce46d1bbb"}, - {file = "zope.interface-6.0-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:790c1d9d8f9c92819c31ea660cd43c3d5451df1df61e2e814a6f99cebb292788"}, - {file = "zope.interface-6.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b39b8711578dcfd45fc0140993403b8a81e879ec25d53189f3faa1f006087dca"}, - {file = "zope.interface-6.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eba51599370c87088d8882ab74f637de0c4f04a6d08a312dce49368ba9ed5c2a"}, - {file = "zope.interface-6.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ee934f023f875ec2cfd2b05a937bd817efcc6c4c3f55c5778cbf78e58362ddc"}, - {file = "zope.interface-6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:042f2381118b093714081fd82c98e3b189b68db38ee7d35b63c327c470ef8373"}, - {file = "zope.interface-6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:dfbbbf0809a3606046a41f8561c3eada9db811be94138f42d9135a5c47e75f6f"}, - {file = "zope.interface-6.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:424d23b97fa1542d7be882eae0c0fc3d6827784105264a8169a26ce16db260d8"}, - {file = "zope.interface-6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e538f2d4a6ffb6edfb303ce70ae7e88629ac6e5581870e66c306d9ad7b564a58"}, - {file = "zope.interface-6.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:12175ca6b4db7621aedd7c30aa7cfa0a2d65ea3a0105393e05482d7a2d367446"}, - {file = "zope.interface-6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c3d7dfd897a588ec27e391edbe3dd320a03684457470415870254e714126b1f"}, - {file = "zope.interface-6.0-cp38-cp38-win_amd64.whl", hash = "sha256:b3f543ae9d3408549a9900720f18c0194ac0fe810cecda2a584fd4dca2eb3bb8"}, - {file = "zope.interface-6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d0583b75f2e70ec93f100931660328965bb9ff65ae54695fb3fa0a1255daa6f2"}, - {file = "zope.interface-6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:23ac41d52fd15dd8be77e3257bc51bbb82469cf7f5e9a30b75e903e21439d16c"}, - {file = "zope.interface-6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99856d6c98a326abbcc2363827e16bd6044f70f2ef42f453c0bd5440c4ce24e5"}, - {file = "zope.interface-6.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1592f68ae11e557b9ff2bc96ac8fc30b187e77c45a3c9cd876e3368c53dc5ba8"}, - {file = "zope.interface-6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4407b1435572e3e1610797c9203ad2753666c62883b921318c5403fb7139dec2"}, - {file = "zope.interface-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:5171eb073474a5038321409a630904fd61f12dd1856dd7e9d19cd6fe092cbbc5"}, - {file = "zope.interface-6.0.tar.gz", hash = "sha256:aab584725afd10c710b8f1e6e208dbee2d0ad009f57d674cb9d1b3964037275d"}, -] - -[package.dependencies] -setuptools = "*" - -[package.extras] -docs = ["Sphinx", "repoze.sphinx.autointerface"] -test = ["coverage (>=5.0.3)", "zope.event", "zope.testing"] -testing = ["coverage (>=5.0.3)", "zope.event", "zope.testing"] - -[metadata] -lock-version = "2.0" -python-versions = "^3.8" -content-hash = "2a8e318900443c54a471676a2c0494d759953932a018953d69bf4102ec539634" diff --git a/monitoring/prod.Dockerfile b/monitoring/prod.Dockerfile deleted file mode 100644 index e335ee55f31..00000000000 --- a/monitoring/prod.Dockerfile +++ /dev/null @@ -1,10 +0,0 @@ -FROM python:3.11-slim-bullseye - -ENV OPENMLDB_EXPORTER_VERSION=0.7.1 - -RUN pip install --no-cache-dir openmldb-exporter==${OPENMLDB_EXPORTER_VERSION} - -EXPOSE 8000 - -# --config.zk_root and --config.zk_path must provided by user -ENTRYPOINT [ "/usr/local/bin/openmldb-exporter", "--log.level=INFO" ] diff --git a/monitoring/prod.env b/monitoring/prod.env deleted file mode 100644 index 5240755bece..00000000000 --- a/monitoring/prod.env +++ /dev/null @@ -1,2 +0,0 @@ -OPENMLDB_VERSION=0.8.0 -ENV_TYPE=prod diff --git a/monitoring/pyproject.toml b/monitoring/pyproject.toml deleted file mode 100644 index a1da7a748aa..00000000000 --- a/monitoring/pyproject.toml +++ /dev/null @@ -1,39 +0,0 @@ -[tool.poetry] -name = "openmldb-exporter" -version = "0.7.1" -description = "prometheus exporter for OpenMLDB" -authors = ["aceforeverd "] -license = "Apache-2.0" -readme = "README.md" -homepage = "https://openmldb.ai" -repository = "https://github.com/4paradigm/OpenMLDB" -documentation = "https://openmldb.ai/docs/zh/main/maintain/monitoring.html" -keywords = ["openmldb", "prometheus"] -classifiers = [ - "Programming Language :: Python :: 3.8", - "Topic :: System :: Monitoring", - "Topic :: Database", -] - -[tool.poetry.scripts] -openmldb-exporter = "openmldb_exporter.exporter:main" - -[tool.poetry.dependencies] -python = "^3.8" -prometheus-client = "^0.16.0" -openmldb = "^0.7.0" -# uncomment below to use openmldb sdk built from source -# set develop = true so changes in python will take effect immediately -# openmldb = { path = "../python/", develop = true } -Twisted = "^22.2.0" - -[tool.poetry.dev-dependencies] -pylint = "^2.17.3" - -[tool.poetry.group.dev.dependencies] -pytest = "^7.3.1" -requests = "^2.29.0" - -[build-system] -requires = ["poetry-core>=1.0.0"] -build-backend = "poetry.core.masonry.api" diff --git a/monitoring/res/prometheus.yml b/monitoring/res/prometheus.yml deleted file mode 100644 index fa6b1cc9e45..00000000000 --- a/monitoring/res/prometheus.yml +++ /dev/null @@ -1,46 +0,0 @@ -# my global config -global: - scrape_interval: 1m # Set the scrape interval, default is every 1 minute. - evaluation_interval: 1m # Evaluate rules, default is every 1 minute. - # scrape_timeout is set to the global default (10s). - -# Alertmanager configuration -alerting: - alertmanagers: - - static_configs: - - targets: - # - alertmanager:9093 - -# Load rules once and periodically evaluate them according to the global 'evaluation_interval'. -rule_files: - # - "first_rules.yml" - # - "second_rules.yml" - -# A scrape configuration containing exactly one endpoint to scrape: -# Here it's Prometheus itself. -scrape_configs: - # The job name is added as a label `job=` to any timeseries scraped from this config. - - job_name: prometheus - static_configs: - - targets: - - localhost:9090 - - - job_name: openmldb_components - # job to pull component metrics from OpenMLDB like tablet/nameserver - # tweak the 'targets' list in 'static_configs' on your need - # every nameserver/tablet component endpoint should be added into targets - metrics_path: /brpc_metrics - static_configs: - - targets: - - openmldb-ns1:9527 - - openmldb-ns2:9527 - - openmldb-tablet1:9527 - - openmldb-tablet2:9527 - - openmldb-tablet3:9527 - - - job_name: openmldb_exporter - # pull OpenMLDB DB-Level specific metric - # change the 'targets' value to your deployed OpenMLDB exporter endpoint - static_configs: - - targets: - - openmldb-exporter:8000 diff --git a/monitoring/src.Dockerfile b/monitoring/src.Dockerfile deleted file mode 100644 index 20fb04a76a4..00000000000 --- a/monitoring/src.Dockerfile +++ /dev/null @@ -1,16 +0,0 @@ -# openmldb exporter image from source code - -FROM python:3.11-slim-bullseye - -RUN pip install --no-cache-dir poetry - -WORKDIR /usr/src/openmldb-exporter - -COPY ./ /usr/src/openmldb-exporter - -RUN poetry install - -EXPOSE 8000 - -# --config.zk_root and --config.zk_path must provided by user -ENTRYPOINT [ "poetry", "run", "openmldb-exporter" ] diff --git a/monitoring/test.Dockerfile b/monitoring/test.Dockerfile deleted file mode 100644 index 4790b11108b..00000000000 --- a/monitoring/test.Dockerfile +++ /dev/null @@ -1,7 +0,0 @@ -FROM python:3.11-slim-bullseye - -RUN pip install --no-cache-dir poetry - -WORKDIR /usr/src/openmldb-exporter - -CMD [ "/bin/bash" ] diff --git a/monitoring/tests/conftest.py b/monitoring/tests/conftest.py deleted file mode 100644 index ca794d5adfe..00000000000 --- a/monitoring/tests/conftest.py +++ /dev/null @@ -1,42 +0,0 @@ -import pytest -from sqlalchemy import engine - - -def pytest_addoption(parser): - parser.addoption("--zk_root", action="store", default="openmldb-zk:2181", help="endpoint to zookeeper") - parser.addoption("--zk_path", action="store", default="/openmldb", help="root path in zookeeper for OpenMLDB") - parser.addoption("--url", - action="store", - default="http://openmldb-exporter:8000/metrics", - help="openmldb exporter pull url") - parser.addoption("--api", action="store", default="http://openmldb-api:9527", help="openmldb apiserver url") - parser.addoption("--prom", action="store", default="http://prometheus:9090", help="prometheus url") - - -@pytest.fixture(scope="session") -def conn(request): - zk_root = request.config.getoption("--zk_root") - zk_path = request.config.getoption("--zk_path") - - eng = engine.create_engine(f"openmldb:///?zk={zk_root}&zkPath={zk_path}") - conn = eng.connect() - # default online mode - conn.execute("set session execute_mode = 'online'") - # enable deploy response time - conn.execute("set global deploy_stats = 'on'") - return conn - - -@pytest.fixture(scope="session") -def global_url(request): - return request.config.getoption("--url") - - -@pytest.fixture(scope="session") -def api_url(request): - return request.config.getoption("--api") - - -@pytest.fixture(scope="session") -def prom_url(request): - return request.config.getoption("--prom") diff --git a/monitoring/tests/test_exporter.py b/monitoring/tests/test_exporter.py deleted file mode 100644 index b7d35c19185..00000000000 --- a/monitoring/tests/test_exporter.py +++ /dev/null @@ -1,204 +0,0 @@ -import requests - -from prometheus_client.parser import text_string_to_metric_families -import time - -import random - - -def test_components_online(global_url): - # Make a request to your application to get the Prometheus metrics - response = requests.get(global_url) - - # Parse the metrics from the response - metrics = text_string_to_metric_families(response.text) - - ns_cnt = 0 - tb_cnt = 0 - # Assert that the metrics are as expected - for metric in metrics: - # all components online - if metric.name == "openmldb_status": - for sample in metric.samples: - if sample.value == 1.0: - if sample.labels["role"] == "nameserver": - ns_cnt += 1 - elif sample.labels["role"] == "tablet": - tb_cnt += 1 - - assert sample.labels["openmldb_status"] == "online" - - assert ns_cnt == 2 - assert tb_cnt == 3 - - -def test_tablet_mem(global_url): - response = requests.get(global_url) - - metrics = text_string_to_metric_families(response.text) - - app_cnt = 0 - actual_cnt = 0 - for metric in metrics: - if metric.name == "openmldb_tablet_memory_application_bytes": - for sample in metric.samples: - assert sample.value > 0 - app_cnt += 1 - elif metric.name == "openmldb_tablet_memory_actual_used_bytes": - for sample in metric.samples: - assert sample.value > 0 - actual_cnt += 1 - - assert app_cnt == 3 - assert actual_cnt == 3 - - -def test_table_status(global_url, conn): - response = requests.get(global_url) - - metrics = text_string_to_metric_families(response.text) - - metric_name_to_len_dict = { - "openmldb_table_rows": 0, - "openmldb_table_partitions": 0, - "openmldb_table_replica": 0, - "openmldb_table_disk_bytes": 0, - "openmldb_table_memory_bytes": 0 - } - - metric_expect_value_dict = { - "openmldb_table_rows": 3, - "openmldb_table_partitions": 8, # default value - "openmldb_table_replica": 3, - } - - # before: no tables - for metric in metrics: - if metric.name in list(metric_name_to_len_dict.keys()): - metric_name_to_len_dict[metric.name] = len(metric.samples) - - # new table - db = "db" + str(int(time.time())) - tb = "tb" + str(int(time.time())) - conn.execute("create database " + db) - conn.execute("use " + db) - conn.execute("create table " + tb + " (id int, val string)") - conn.execute("insert into " + tb + " values (1, '100')") - conn.execute("insert into " + tb + " values (2, '200')") - conn.execute("insert into " + tb + " values (3, '300')") - - # wait for metric pull - time.sleep(60) - - response = requests.get(global_url) - metrics = text_string_to_metric_families(response.text) - - for metric in metrics: - if metric.name in list(metric_name_to_len_dict.keys()): - # one more series - assert len(metric.samples) == metric_name_to_len_dict[metric.name] + 1 - - for sample in metric.samples: - if sample.labels["table_path"] == db + "_" + tb: - if metric.name in list(metric_expect_value_dict.keys()): - # rows, partition, replica - assert sample.value == metric_expect_value_dict[metric.name], f"{sample}" - elif metric.name == "openmldb_table_memory_bytes": - # memory bytes - assert sample.value > 0, f"{sample}" - elif metric.name == "openmldb_table_disk_bytes": - assert sample.value == 0, f"{sample}" - - assert sample.labels["storage_mode"] == "memory", f"{sample}" - - -def test_connected_seconds(global_url): - response = requests.get(global_url) - metrics = text_string_to_metric_families(response.text) - - cnt = 0 - ns_cnt = 0 - tablet_cnt = 0 - for metric in metrics: - if metric.name == "openmldb_connected_seconds": - cnt += 1 - for sample in metric.samples: - if sample.labels["role"] == "tablet": - tablet_cnt += 1 - elif sample.labels["role"] == "nameserver": - ns_cnt += 1 - assert sample.value > 0.0 - - assert cnt == 1 - assert ns_cnt == 2 - assert tablet_cnt == 3 - - -BUCKET_CNT_EACH_DEPLOY = 14 -DEPLOY_SAMPLE_CNT_EACH_DEPLOY = BUCKET_CNT_EACH_DEPLOY + 1 + 1 -DEPLOY_METRIC_NAME = "openmldb_info_schema_deploy_response_time_seconds" -DEPLOY_METRIC_NAME_BUCKET = DEPLOY_METRIC_NAME + "_bucket" -DEPLOY_METRIC_NAME_COUNT = DEPLOY_METRIC_NAME + "_count" -DEPLOY_METRIC_NAME_SUM = DEPLOY_METRIC_NAME + "_sum" - - -def test_deploy_response_time(global_url, conn, api_url): - response = requests.get(global_url) - metrics = text_string_to_metric_families(response.text) - - old_deploy_sample_cnt = 0 - for metric in metrics: - if metric.name == DEPLOY_METRIC_NAME: - old_deploy_sample_cnt = len(metric.samples) - - db = "db" + str(int(time.time())) - tb = "tb" + str(int(time.time())) - dp = "dp" + str(int(time.time())) - conn.execute("create database " + db) - conn.execute("use " + db) - conn.execute(f"create table {tb} (id int, val string, ts timestamp)") - conn.execute( - f"deploy {dp} select id, count(val) over w as cnt from {tb} window w as (partition by id order by ts rows_range between 2s preceding and current row)" - ) - - post_ep = f"{api_url}/dbs/{db}/deployments/{dp}" - post_data = {"input": [[1, "12", 1000]]} - - deploy_cnt = random.randint(5, 100) - - for _ in range(deploy_cnt): - try: - res = requests.post(post_ep, json=post_data, timeout=5) - assert res.status_code == 200, f"{res}" - except Exception as e: - assert False, f"apiserver is down: {e}" - - time.sleep(60) - - response = requests.get(global_url) - metrics = text_string_to_metric_families(response.text) - - new_bucket = 0 - new_cnt = 0 - new_sum = 0 - new_cnt_value = 0 - for metric in metrics: - print(metric) - - if metric.name == DEPLOY_METRIC_NAME: - assert len(metric.samples) == old_deploy_sample_cnt + DEPLOY_SAMPLE_CNT_EACH_DEPLOY - - for sample in metric.samples: - if sample.labels["deploy_path"] == db + "." + dp: - if sample.name == DEPLOY_METRIC_NAME_BUCKET: - new_bucket += 1 - elif sample.name == DEPLOY_METRIC_NAME_COUNT: - new_cnt += 1 - new_cnt_value = sample.value - elif sample.name == DEPLOY_METRIC_NAME_SUM: - new_sum += 1 - - assert new_cnt_value == deploy_cnt - assert new_cnt == 1 - assert new_sum == 1 - assert new_bucket == BUCKET_CNT_EACH_DEPLOY diff --git a/monitoring/tests/test_prometheus.py b/monitoring/tests/test_prometheus.py deleted file mode 100644 index fe28fee7a95..00000000000 --- a/monitoring/tests/test_prometheus.py +++ /dev/null @@ -1,15 +0,0 @@ -import requests - - -# server is healthy itself -def test_prometheus_healthy(prom_url): - response = requests.get(f"{prom_url}/-/healthy") - assert response.status_code == 200 - - -def test_prometheus_targets(prom_url): - response = requests.get(f"{prom_url}/api/v1/targets") - response_json = response.json() - - for target in response_json["data"]["activeTargets"]: - assert target["health"] == "up", f"Target is {target}" From e48cac9d788ba677a4267a26cece984b82fc15de Mon Sep 17 00:00:00 2001 From: HuangWei Date: Tue, 15 Aug 2023 13:52:47 +0800 Subject: [PATCH 10/63] fix: kafka connector dependency (#3408) openmldb-jdbc needs commons-io, so pack it --- .../online_datasources/kafka_connector_demo.md | 6 ++++++ extensions/kafka-connect-jdbc/DEVELOP.md | 17 +++++++++++++++++ extensions/kafka-connect-jdbc/pom.xml | 2 +- 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/docs/zh/integration/online_datasources/kafka_connector_demo.md b/docs/zh/integration/online_datasources/kafka_connector_demo.md index 0d6ef42d266..e0cbdba9d6e 100644 --- a/docs/zh/integration/online_datasources/kafka_connector_demo.md +++ b/docs/zh/integration/online_datasources/kafka_connector_demo.md @@ -24,6 +24,12 @@ OpenMLDB Kafka Connector实现见[extensions/kafka-connect-jdbc](https://github. docker run -it -v `pwd`:/work/kafka 4pdosc/openmldb:0.8.2 bash ``` +### 注意事项 + +Timestamp列精度为ms,value配置为JsonConvertor,仅支持整型。根据消息的不同,可选配其他Convertor。 + +Connector可用于较早版本的Kafka Server,例如1.1.1,但注意旧版本的Kafka Broker可能并未默认开启“自动创建topic”,需要开启[此选项](https://kafka.apache.org/documentation/#brokerconfigs_auto.create.topics.enable)。 + ### 流程 使用connector的简要流程,如下图所示。我们接下来将详细介绍每一步。 diff --git a/extensions/kafka-connect-jdbc/DEVELOP.md b/extensions/kafka-connect-jdbc/DEVELOP.md index a3d93949549..5b809607dbc 100644 --- a/extensions/kafka-connect-jdbc/DEVELOP.md +++ b/extensions/kafka-connect-jdbc/DEVELOP.md @@ -28,6 +28,23 @@ auto.create=false ``` 请确保已经建好了OpenMLDB表。 +### Message样式 + +仅支持Json格式的Message,不用写schema,因此只需要`:`的Map,例如: +``` +{ + "c1_int16": 1, + "c2_int32": 2, + "c3_int64": 3, + "c4_float": 4.4, + "c5_double": 5.555, + "c6_boolean": true, + "c7_string": "c77777", + "c8_date": 19109, + "c9_timestamp": 1651051906000 +} +``` + ### message convert for auto schema auto schema开启后,主要逻辑在[BufferedRecords](src/main/java/io/confluent/connect/jdbc/sink/BufferedRecords.java) diff --git a/extensions/kafka-connect-jdbc/pom.xml b/extensions/kafka-connect-jdbc/pom.xml index d7a391b7c72..9633118e73b 100644 --- a/extensions/kafka-connect-jdbc/pom.xml +++ b/extensions/kafka-connect-jdbc/pom.xml @@ -172,7 +172,7 @@ commons-io commons-io ${commons-io.version} - test + org.mockito From f6dc23edc2bc9ad50f2f1098ff9b2f7f46edf61a Mon Sep 17 00:00:00 2001 From: aceforeverd Date: Tue, 15 Aug 2023 15:48:21 +0800 Subject: [PATCH 11/63] feat(udf): add get_json_object (#3429) --- CPPLINT.cfg | 1 + contrib/simdjson/README | 2 +- contrib/simdjson/simdjson.cpp | 49569 +++++++--- contrib/simdjson/simdjson.h | 89838 ++++++++++++++---- hybridse/src/codegen/udf_ir_builder_test.cc | 70 + hybridse/src/udf/default_defs/json_defs.cc | 93 +- hybridse/src/udf/literal_traits.h | 7 +- 7 files changed, 111101 insertions(+), 28479 deletions(-) diff --git a/CPPLINT.cfg b/CPPLINT.cfg index be7f74a1e2d..331781c0fee 100644 --- a/CPPLINT.cfg +++ b/CPPLINT.cfg @@ -1,2 +1,3 @@ linelength=120 filter=-build/c++11,-build/include_subdir +exclude_files=contrib/* diff --git a/contrib/simdjson/README b/contrib/simdjson/README index 16007e4600b..528eeda2cfe 100644 --- a/contrib/simdjson/README +++ b/contrib/simdjson/README @@ -1 +1 @@ -smidjson v3.2.1: https://github.com/simdjson/simdjson/releases/tag/v3.2.1 +smidjson v3.2.2: https://github.com/simdjson/simdjson/releases/tag/v3.2.2 diff --git a/contrib/simdjson/simdjson.cpp b/contrib/simdjson/simdjson.cpp index bb265297e21..891ccb47eb2 100644 --- a/contrib/simdjson/simdjson.cpp +++ b/contrib/simdjson/simdjson.cpp @@ -1,1962 +1,4923 @@ -/* auto-generated on 2023-07-06 21:34:14 -0400. Do not edit! */ -/* begin file src/simdjson.cpp */ -#include "simdjson.h" +/* auto-generated on 2023-08-02 16:00:45 -0400. Do not edit! */ +/* including simdjson.cpp: */ +/* begin file simdjson.cpp */ +#define SIMDJSON_SRC_SIMDJSON_CPP + +/* including base.h: #include */ +/* begin file base.h */ +#ifndef SIMDJSON_SRC_BASE_H +#define SIMDJSON_SRC_BASE_H + +/* including simdjson/base.h: #include */ +/* begin file simdjson/base.h */ +/** + * @file Base declarations for all simdjson headers + * @private + */ +#ifndef SIMDJSON_BASE_H +#define SIMDJSON_BASE_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 + +#include +/* including simdjson/compiler_check.h: #include "simdjson/compiler_check.h" */ +/* begin file simdjson/compiler_check.h */ +#ifndef SIMDJSON_COMPILER_CHECK_H +#define SIMDJSON_COMPILER_CHECK_H + +#ifndef __cplusplus +#error simdjson requires a C++ compiler +#endif -SIMDJSON_PUSH_DISABLE_WARNINGS -SIMDJSON_DISABLE_UNDESIRED_WARNINGS +#ifndef SIMDJSON_CPLUSPLUS +#if defined(_MSVC_LANG) && !defined(__clang__) +#define SIMDJSON_CPLUSPLUS (_MSC_VER == 1900 ? 201103L : _MSVC_LANG) +#else +#define SIMDJSON_CPLUSPLUS __cplusplus +#endif +#endif -/* begin file src/to_chars.cpp */ -#include -#include -#include -#include +// C++ 17 +#if !defined(SIMDJSON_CPLUSPLUS17) && (SIMDJSON_CPLUSPLUS >= 201703L) +#define SIMDJSON_CPLUSPLUS17 1 +#endif -namespace simdjson { -namespace internal { -/*! -implements the Grisu2 algorithm for binary to decimal floating-point -conversion. -Adapted from JSON for Modern C++ +// C++ 14 +#if !defined(SIMDJSON_CPLUSPLUS14) && (SIMDJSON_CPLUSPLUS >= 201402L) +#define SIMDJSON_CPLUSPLUS14 1 +#endif -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 { +// C++ 11 +#if !defined(SIMDJSON_CPLUSPLUS11) && (SIMDJSON_CPLUSPLUS >= 201103L) +#define SIMDJSON_CPLUSPLUS11 1 +#endif -template -Target reinterpret_bits(const Source source) { - static_assert(sizeof(Target) == sizeof(Source), "size mismatch"); +#ifndef SIMDJSON_CPLUSPLUS11 +#error simdjson requires a compiler compliant with the C++11 standard +#endif - Target target; - std::memcpy(&target, &source, sizeof(Source)); - return target; -} +#endif // SIMDJSON_COMPILER_CHECK_H +/* end file simdjson/compiler_check.h */ +/* including simdjson/portability.h: #include "simdjson/portability.h" */ +/* begin file simdjson/portability.h */ +#ifndef SIMDJSON_PORTABILITY_H +#define SIMDJSON_PORTABILITY_H -struct diyfp // f * 2^e -{ - static constexpr int kPrecision = 64; // = q +#include +#include +#include +#include +#include +#ifndef _WIN32 +// strcasecmp, strncasecmp +#include +#endif - std::uint64_t f = 0; - int e = 0; +#ifdef _MSC_VER +#define SIMDJSON_VISUAL_STUDIO 1 +/** + * We want to differentiate carefully between + * clang under visual studio and regular visual + * studio. + * + * Under clang for Windows, we enable: + * * target pragmas so that part and only part of the + * code gets compiled for advanced instructions. + * + */ +#ifdef __clang__ +// clang under visual studio +#define SIMDJSON_CLANG_VISUAL_STUDIO 1 +#else +// just regular visual studio (best guess) +#define SIMDJSON_REGULAR_VISUAL_STUDIO 1 +#endif // __clang__ +#endif // _MSC_VER + +#if defined(__x86_64__) || defined(_M_AMD64) +#define SIMDJSON_IS_X86_64 1 +#elif defined(__aarch64__) || defined(_M_ARM64) +#define SIMDJSON_IS_ARM64 1 +#elif defined(__PPC64__) || defined(_M_PPC64) +#if defined(__ALTIVEC__) +#define SIMDJSON_IS_PPC64_VMX 1 +#endif // defined(__ALTIVEC__) +#else +#define SIMDJSON_IS_32BITS 1 + +#if defined(_M_IX86) || defined(__i386__) +#define SIMDJSON_IS_X86_32BITS 1 +#elif defined(__arm__) || defined(_M_ARM) +#define SIMDJSON_IS_ARM_32BITS 1 +#elif defined(__PPC__) || defined(_M_PPC) +#define SIMDJSON_IS_PPC_32BITS 1 +#endif - constexpr diyfp(std::uint64_t f_, int e_) noexcept : f(f_), e(e_) {} +#endif // defined(__x86_64__) || defined(_M_AMD64) +#ifndef SIMDJSON_IS_32BITS +#define SIMDJSON_IS_32BITS 0 +#endif - /*! - @brief returns x - y - @pre x.e == y.e and x.f >= y.f - */ - static diyfp sub(const diyfp &x, const diyfp &y) noexcept { +#if SIMDJSON_IS_32BITS +#ifndef SIMDJSON_NO_PORTABILITY_WARNING +#pragma message("The simdjson library is designed \ +for 64-bit processors and it seems that you are not \ +compiling for a known 64-bit platform. All fast kernels \ +will be disabled and performance may be poor. Please \ +use a 64-bit target such as x64, 64-bit ARM or 64-bit PPC.") +#endif // SIMDJSON_NO_PORTABILITY_WARNING +#endif // SIMDJSON_IS_32BITS + +#define SIMDJSON_CAT_IMPLEMENTATION_(a,...) a ## __VA_ARGS__ +#define SIMDJSON_CAT(a,...) SIMDJSON_CAT_IMPLEMENTATION_(a, __VA_ARGS__) + +#define SIMDJSON_STRINGIFY_IMPLEMENTATION_(a,...) #a SIMDJSON_STRINGIFY(__VA_ARGS__) +#define SIMDJSON_STRINGIFY(a,...) SIMDJSON_CAT_IMPLEMENTATION_(a, __VA_ARGS__) + +// this is almost standard? +#undef SIMDJSON_STRINGIFY_IMPLEMENTATION_ +#undef SIMDJSON_STRINGIFY +#define SIMDJSON_STRINGIFY_IMPLEMENTATION_(a) #a +#define SIMDJSON_STRINGIFY(a) SIMDJSON_STRINGIFY_IMPLEMENTATION_(a) + +// Our fast kernels require 64-bit systems. +// +// On 32-bit x86, we lack 64-bit popcnt, lzcnt, blsr instructions. +// Furthermore, the number of SIMD registers is reduced. +// +// On 32-bit ARM, we would have smaller registers. +// +// The simdjson users should still have the fallback kernel. It is +// slower, but it should run everywhere. - return {x.f - y.f, x.e}; - } +// +// Enable valid runtime implementations, and select SIMDJSON_BUILTIN_IMPLEMENTATION +// - /*! - @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"); +// We are going to use runtime dispatch. +#if SIMDJSON_IS_X86_64 +#ifdef __clang__ +// clang does not have GCC push pop +// warning: clang attribute push can't be used within a namespace in clang up +// til 8.0 so SIMDJSON_TARGET_REGION and SIMDJSON_UNTARGET_REGION must be *outside* of a +// namespace. +#define SIMDJSON_TARGET_REGION(T) \ + _Pragma(SIMDJSON_STRINGIFY( \ + clang attribute push(__attribute__((target(T))), apply_to = function))) +#define SIMDJSON_UNTARGET_REGION _Pragma("clang attribute pop") +#elif defined(__GNUC__) +// GCC is easier +#define SIMDJSON_TARGET_REGION(T) \ + _Pragma("GCC push_options") _Pragma(SIMDJSON_STRINGIFY(GCC target(T))) +#define SIMDJSON_UNTARGET_REGION _Pragma("GCC pop_options") +#endif // clang then gcc + +#endif // x86 + +// Default target region macros don't do anything. +#ifndef SIMDJSON_TARGET_REGION +#define SIMDJSON_TARGET_REGION(T) +#define SIMDJSON_UNTARGET_REGION +#endif - // Computes: - // f = round((x.f * y.f) / 2^q) - // e = x.e + y.e + q +// Is threading enabled? +#if defined(_REENTRANT) || defined(_MT) +#ifndef SIMDJSON_THREADS_ENABLED +#define SIMDJSON_THREADS_ENABLED +#endif +#endif - // 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 +// workaround for large stack sizes under -O0. +// https://github.com/simdjson/simdjson/issues/691 +#ifdef __APPLE__ +#ifndef __OPTIMIZE__ +// Apple systems have small stack sizes in secondary threads. +// Lack of compiler optimization may generate high stack usage. +// Users may want to disable threads for safety, but only when +// in debug mode which we detect by the fact that the __OPTIMIZE__ +// macro is not defined. +#undef SIMDJSON_THREADS_ENABLED +#endif +#endif - 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; +#if defined(__clang__) +#define SIMDJSON_NO_SANITIZE_UNDEFINED __attribute__((no_sanitize("undefined"))) +#elif defined(__GNUC__) +#define SIMDJSON_NO_SANITIZE_UNDEFINED __attribute__((no_sanitize_undefined)) +#else +#define SIMDJSON_NO_SANITIZE_UNDEFINED +#endif - 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; +#if defined(__clang__) || defined(__GNUC__) +#if defined(__has_feature) +# if __has_feature(memory_sanitizer) +#define SIMDJSON_NO_SANITIZE_MEMORY __attribute__((no_sanitize("memory"))) +# endif // if __has_feature(memory_sanitizer) +#endif // defined(__has_feature) +#endif +// make sure it is defined as 'nothing' if it is unapplicable. +#ifndef SIMDJSON_NO_SANITIZE_MEMORY +#define SIMDJSON_NO_SANITIZE_MEMORY +#endif - // 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). +#if SIMDJSON_VISUAL_STUDIO +// This is one case where we do not distinguish between +// regular visual studio and clang under visual studio. +// clang under Windows has _stricmp (like visual studio) but not strcasecmp (as clang normally has) +#define simdjson_strcasecmp _stricmp +#define simdjson_strncasecmp _strnicmp +#else +// The strcasecmp, strncasecmp, and strcasestr functions do not work with multibyte strings (e.g. UTF-8). +// So they are only useful for ASCII in our context. +// https://www.gnu.org/software/libunistring/manual/libunistring.html#char-_002a-strings +#define simdjson_strcasecmp strcasecmp +#define simdjson_strncasecmp strncasecmp +#endif - Q += std::uint64_t{1} << (64u - 32u - 1u); // round, ties up +#if defined(NDEBUG) || defined(__OPTIMIZE__) || (defined(_MSC_VER) && !defined(_DEBUG)) +// If NDEBUG is set, or __OPTIMIZE__ is set, or we are under MSVC in release mode, +// then do away with asserts and use __assume. +#if SIMDJSON_VISUAL_STUDIO +#define SIMDJSON_UNREACHABLE() __assume(0) +#define SIMDJSON_ASSUME(COND) __assume(COND) +#else +#define SIMDJSON_UNREACHABLE() __builtin_unreachable(); +#define SIMDJSON_ASSUME(COND) do { if (!(COND)) __builtin_unreachable(); } while (0) +#endif - const std::uint64_t h = p3 + p2_hi + p1_hi + (Q >> 32u); +#else // defined(NDEBUG) || defined(__OPTIMIZE__) || (defined(_MSC_VER) && !defined(_DEBUG)) +// This should only ever be enabled in debug mode. +#define SIMDJSON_UNREACHABLE() assert(0); +#define SIMDJSON_ASSUME(COND) assert(COND) - return {h, x.e + y.e + 64}; - } +#endif - /*! - @brief normalize x such that the significand is >= 2^(q-1) - @pre x.f != 0 - */ - static diyfp normalize(diyfp x) noexcept { +#endif // SIMDJSON_PORTABILITY_H +/* end file simdjson/portability.h */ - while ((x.f >> 63u) == 0) { - x.f <<= 1u; - x.e--; - } +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; +} - return x; - } +#ifndef SIMDJSON_EXCEPTIONS +#if __cpp_exceptions +#define SIMDJSON_EXCEPTIONS 1 +#else +#define SIMDJSON_EXCEPTIONS 0 +#endif +#endif - /*! - @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; +} // namespace simdjson - return {x.f << delta, target_exponent}; - } -}; +#if defined(__GNUC__) + // Marks a block with a name so that MCA analysis can see it. + #define SIMDJSON_BEGIN_DEBUG_BLOCK(name) __asm volatile("# LLVM-MCA-BEGIN " #name); + #define SIMDJSON_END_DEBUG_BLOCK(name) __asm volatile("# LLVM-MCA-END " #name); + #define SIMDJSON_DEBUG_BLOCK(name, block) BEGIN_DEBUG_BLOCK(name); block; END_DEBUG_BLOCK(name); +#else + #define SIMDJSON_BEGIN_DEBUG_BLOCK(name) + #define SIMDJSON_END_DEBUG_BLOCK(name) + #define SIMDJSON_DEBUG_BLOCK(name, block) +#endif -struct boundaries { - diyfp w; - diyfp minus; - diyfp plus; -}; +// Align to N-byte boundary +#define SIMDJSON_ROUNDUP_N(a, n) (((a) + ((n)-1)) & ~((n)-1)) +#define SIMDJSON_ROUNDDOWN_N(a, n) ((a) & ~((n)-1)) + +#define SIMDJSON_ISALIGNED_N(ptr, n) (((uintptr_t)(ptr) & ((n)-1)) == 0) + +#if SIMDJSON_REGULAR_VISUAL_STUDIO + + #define simdjson_really_inline __forceinline + #define simdjson_never_inline __declspec(noinline) + + #define simdjson_unused + #define simdjson_warn_unused + + #ifndef simdjson_likely + #define simdjson_likely(x) x + #endif + #ifndef simdjson_unlikely + #define simdjson_unlikely(x) x + #endif + + #define SIMDJSON_PUSH_DISABLE_WARNINGS __pragma(warning( push )) + #define SIMDJSON_PUSH_DISABLE_ALL_WARNINGS __pragma(warning( push, 0 )) + #define SIMDJSON_DISABLE_VS_WARNING(WARNING_NUMBER) __pragma(warning( disable : WARNING_NUMBER )) + // Get rid of Intellisense-only warnings (Code Analysis) + // Though __has_include is C++17, it is supported in Visual Studio 2017 or better (_MSC_VER>=1910). + #ifdef __has_include + #if __has_include() + #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 + + #define SIMDJSON_DISABLE_DEPRECATED_WARNING SIMDJSON_DISABLE_VS_WARNING(4996) + #define SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING + #define SIMDJSON_POP_DISABLE_WARNINGS __pragma(warning( pop )) + + #define SIMDJSON_PUSH_DISABLE_UNUSED_WARNINGS + #define SIMDJSON_POP_DISABLE_UNUSED_WARNINGS + +#else // SIMDJSON_REGULAR_VISUAL_STUDIO + + #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)) + + #ifndef simdjson_likely + #define simdjson_likely(x) __builtin_expect(!!(x), 1) + #endif + #ifndef simdjson_unlikely + #define simdjson_unlikely(x) __builtin_expect(!!(x), 0) + #endif + + #define SIMDJSON_PUSH_DISABLE_WARNINGS _Pragma("GCC diagnostic push") + // gcc doesn't seem to disable all warnings with all and extra, add warnings here as necessary + // We do it separately for clang since it has different warnings. + #ifdef __clang__ + // clang is missing -Wmaybe-uninitialized. + #define SIMDJSON_PUSH_DISABLE_ALL_WARNINGS SIMDJSON_PUSH_DISABLE_WARNINGS \ + SIMDJSON_DISABLE_GCC_WARNING(-Weffc++) \ + SIMDJSON_DISABLE_GCC_WARNING(-Wall) \ + SIMDJSON_DISABLE_GCC_WARNING(-Wconversion) \ + SIMDJSON_DISABLE_GCC_WARNING(-Wextra) \ + SIMDJSON_DISABLE_GCC_WARNING(-Wattributes) \ + SIMDJSON_DISABLE_GCC_WARNING(-Wimplicit-fallthrough) \ + SIMDJSON_DISABLE_GCC_WARNING(-Wnon-virtual-dtor) \ + SIMDJSON_DISABLE_GCC_WARNING(-Wreturn-type) \ + SIMDJSON_DISABLE_GCC_WARNING(-Wshadow) \ + SIMDJSON_DISABLE_GCC_WARNING(-Wunused-parameter) \ + SIMDJSON_DISABLE_GCC_WARNING(-Wunused-variable) + #else // __clang__ + #define SIMDJSON_PUSH_DISABLE_ALL_WARNINGS SIMDJSON_PUSH_DISABLE_WARNINGS \ + SIMDJSON_DISABLE_GCC_WARNING(-Weffc++) \ + SIMDJSON_DISABLE_GCC_WARNING(-Wall) \ + SIMDJSON_DISABLE_GCC_WARNING(-Wconversion) \ + SIMDJSON_DISABLE_GCC_WARNING(-Wextra) \ + SIMDJSON_DISABLE_GCC_WARNING(-Wattributes) \ + SIMDJSON_DISABLE_GCC_WARNING(-Wimplicit-fallthrough) \ + SIMDJSON_DISABLE_GCC_WARNING(-Wnon-virtual-dtor) \ + SIMDJSON_DISABLE_GCC_WARNING(-Wreturn-type) \ + SIMDJSON_DISABLE_GCC_WARNING(-Wshadow) \ + SIMDJSON_DISABLE_GCC_WARNING(-Wunused-parameter) \ + SIMDJSON_DISABLE_GCC_WARNING(-Wunused-variable) \ + SIMDJSON_DISABLE_GCC_WARNING(-Wmaybe-uninitialized) \ + SIMDJSON_DISABLE_GCC_WARNING(-Wformat-security) + #endif // __clang__ + + #define SIMDJSON_PRAGMA(P) _Pragma(#P) + #define SIMDJSON_DISABLE_GCC_WARNING(WARNING) SIMDJSON_PRAGMA(GCC diagnostic ignored #WARNING) + #if SIMDJSON_CLANG_VISUAL_STUDIO + #define SIMDJSON_DISABLE_UNDESIRED_WARNINGS SIMDJSON_DISABLE_GCC_WARNING(-Wmicrosoft-include) + #else + #define SIMDJSON_DISABLE_UNDESIRED_WARNINGS + #endif + #define SIMDJSON_DISABLE_DEPRECATED_WARNING SIMDJSON_DISABLE_GCC_WARNING(-Wdeprecated-declarations) + #define SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING SIMDJSON_DISABLE_GCC_WARNING(-Wstrict-overflow) + #define SIMDJSON_POP_DISABLE_WARNINGS _Pragma("GCC diagnostic pop") + + #define SIMDJSON_PUSH_DISABLE_UNUSED_WARNINGS SIMDJSON_PUSH_DISABLE_WARNINGS \ + SIMDJSON_DISABLE_GCC_WARNING(-Wunused) + #define SIMDJSON_POP_DISABLE_UNUSED_WARNINGS SIMDJSON_POP_DISABLE_WARNINGS + + + +#endif // MSC_VER + +#if defined(simdjson_inline) + // Prefer the user's definition of simdjson_inline; don't define it ourselves. +#elif defined(__GNUC__) && !defined(__OPTIMIZE__) + // If optimizations are disabled, forcing inlining can lead to significant + // code bloat and high compile times. Don't use simdjson_really_inline for + // unoptimized builds. + #define simdjson_inline inline +#else + // Force inlining for most simdjson functions. + #define simdjson_inline simdjson_really_inline +#endif -/*! -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) { +#if SIMDJSON_VISUAL_STUDIO + /** + * Windows users need to do some extra work when building + * or using a dynamic library (DLL). When building, we need + * to set SIMDJSON_DLLIMPORTEXPORT to __declspec(dllexport). + * When *using* the DLL, the user needs to set + * SIMDJSON_DLLIMPORTEXPORT __declspec(dllimport). + * + * Static libraries not need require such work. + * + * It does not matter here whether you are using + * the regular visual studio or clang under visual + * studio, you still need to handle these issues. + * + * Non-Windows systems do not have this complexity. + */ + #if SIMDJSON_BUILDING_WINDOWS_DYNAMIC_LIBRARY + // We set SIMDJSON_BUILDING_WINDOWS_DYNAMIC_LIBRARY when we build a DLL under Windows. + // It should never happen that both SIMDJSON_BUILDING_WINDOWS_DYNAMIC_LIBRARY and + // SIMDJSON_USING_WINDOWS_DYNAMIC_LIBRARY are set. + #define SIMDJSON_DLLIMPORTEXPORT __declspec(dllexport) + #elif SIMDJSON_USING_WINDOWS_DYNAMIC_LIBRARY + // Windows user who call a dynamic library should set SIMDJSON_USING_WINDOWS_DYNAMIC_LIBRARY to 1. + #define SIMDJSON_DLLIMPORTEXPORT __declspec(dllimport) + #else + // We assume by default static linkage + #define SIMDJSON_DLLIMPORTEXPORT + #endif - // 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)) +/** + * 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 +#endif - static_assert(std::numeric_limits::is_iec559, - "internal error: dtoa_short requires an IEEE-754 " - "floating-point implementation"); +// C++17 requires string_view. +#if SIMDJSON_CPLUSPLUS17 +#define SIMDJSON_HAS_STRING_VIEW +#include // by the standard, this has to be safe. +#endif - 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) +// This macro (__cpp_lib_string_view) has to be defined +// for C++17 and better, but if it is otherwise defined, +// we are going to assume that string_view is available +// even if we do not have C++17 support. +#ifdef __cpp_lib_string_view +#define SIMDJSON_HAS_STRING_VIEW +#endif - using bits_type = typename std::conditional::type; +// 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 SIMDJSON_HAS_STRING_VIEW +#if defined __has_include +// do not combine the next #if with the previous one (unsafe) +#if __has_include () +// now it is safe to trigger the include +#include // though the file is there, it does not follow that we got the implementation +#if defined(_LIBCPP_STRING_VIEW) +// Ah! So we under libc++ which under its Library Fundamentals Technical Specification, which preceded C++17, +// included string_view. +// This means that we have string_view *even though* we may not have C++17. +#define SIMDJSON_HAS_STRING_VIEW +#endif // _LIBCPP_STRING_VIEW +#endif // __has_include () +#endif // defined __has_include +#endif // def SIMDJSON_HAS_STRING_VIEW +// end of complicated but important routine to try to detect string_view. - const std::uint64_t bits = reinterpret_bits(value); - const std::uint64_t E = bits >> (kPrecision - 1); - const std::uint64_t F = bits & (kHiddenBit - 1); +// +// Backfill std::string_view using nonstd::string_view on systems where +// we expect that string_view is missing. Important: if we get this wrong, +// we will end up with two string_view definitions and potential trouble. +// That is why we work so hard above to avoid it. +// +#ifndef SIMDJSON_HAS_STRING_VIEW +SIMDJSON_PUSH_DISABLE_ALL_WARNINGS +/* including simdjson/nonstd/string_view.hpp: #include "simdjson/nonstd/string_view.hpp" */ +/* begin file simdjson/nonstd/string_view.hpp */ +// Copyright 2017-2020 by Martin Moene +// +// string-view lite, a C++17-like string_view for C++98 and later. +// For more information see https://github.com/martinmoene/string-view-lite +// +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - const bool is_denormal = E == 0; - const diyfp v = is_denormal - ? diyfp(F, kMinExp) - : diyfp(F + kHiddenBit, static_cast(E) - kBias); +#pragma once - // 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+ +#ifndef NONSTD_SV_LITE_H_INCLUDED +#define NONSTD_SV_LITE_H_INCLUDED - 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) +#define string_view_lite_MAJOR 1 +#define string_view_lite_MINOR 7 +#define string_view_lite_PATCH 0 - // Determine the normalized w+ = m+. - const diyfp w_plus = diyfp::normalize(m_plus); +#define string_view_lite_VERSION nssv_STRINGIFY(string_view_lite_MAJOR) "." nssv_STRINGIFY(string_view_lite_MINOR) "." nssv_STRINGIFY(string_view_lite_PATCH) - // Determine w- = m- such that e_(w-) = e_(w+). - const diyfp w_minus = diyfp::normalize_to(m_minus, w_plus.e); +#define nssv_STRINGIFY( x ) nssv_STRINGIFY_( x ) +#define nssv_STRINGIFY_( x ) #x - return {diyfp::normalize(v), w_minus, w_plus}; -} +// string-view lite configuration: + +#define nssv_STRING_VIEW_DEFAULT 0 +#define nssv_STRING_VIEW_NONSTD 1 +#define nssv_STRING_VIEW_STD 2 + +// tweak header support: + +#ifdef __has_include +# if __has_include() +# include +# endif +#define nssv_HAVE_TWEAK_HEADER 1 +#else +#define nssv_HAVE_TWEAK_HEADER 0 +//# pragma message("string_view.hpp: Note: Tweak header not supported.") +#endif + +// string_view selection and configuration: + +#if !defined( nssv_CONFIG_SELECT_STRING_VIEW ) +# define nssv_CONFIG_SELECT_STRING_VIEW ( nssv_HAVE_STD_STRING_VIEW ? nssv_STRING_VIEW_STD : nssv_STRING_VIEW_NONSTD ) +#endif + +#ifndef nssv_CONFIG_STD_SV_OPERATOR +# define nssv_CONFIG_STD_SV_OPERATOR 0 +#endif + +#ifndef nssv_CONFIG_USR_SV_OPERATOR +# define nssv_CONFIG_USR_SV_OPERATOR 1 +#endif + +#ifdef nssv_CONFIG_CONVERSION_STD_STRING +# define nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS nssv_CONFIG_CONVERSION_STD_STRING +# define nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS nssv_CONFIG_CONVERSION_STD_STRING +#endif + +#ifndef nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS +# define nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS 1 +#endif + +#ifndef nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS +# define nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS 1 +#endif + +#ifndef nssv_CONFIG_NO_STREAM_INSERTION +# define nssv_CONFIG_NO_STREAM_INSERTION 0 +#endif + +// Control presence of exception handling (try and auto discover): + +#ifndef nssv_CONFIG_NO_EXCEPTIONS +# if defined(_MSC_VER) +# include // for _HAS_EXCEPTIONS +# endif +# if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || (_HAS_EXCEPTIONS) +# define nssv_CONFIG_NO_EXCEPTIONS 0 +# else +# define nssv_CONFIG_NO_EXCEPTIONS 1 +# endif +#endif + +// C++ language version detection (C++23 is speculative): +// Note: VC14.0/1900 (VS2015) lacks too much from C++14. + +#ifndef nssv_CPLUSPLUS +# if defined(_MSVC_LANG ) && !defined(__clang__) +# define nssv_CPLUSPLUS (_MSC_VER == 1900 ? 201103L : _MSVC_LANG ) +# else +# define nssv_CPLUSPLUS __cplusplus +# endif +#endif + +#define nssv_CPP98_OR_GREATER ( nssv_CPLUSPLUS >= 199711L ) +#define nssv_CPP11_OR_GREATER ( nssv_CPLUSPLUS >= 201103L ) +#define nssv_CPP11_OR_GREATER_ ( nssv_CPLUSPLUS >= 201103L ) +#define nssv_CPP14_OR_GREATER ( nssv_CPLUSPLUS >= 201402L ) +#define nssv_CPP17_OR_GREATER ( nssv_CPLUSPLUS >= 201703L ) +#define nssv_CPP20_OR_GREATER ( nssv_CPLUSPLUS >= 202002L ) +#define nssv_CPP23_OR_GREATER ( nssv_CPLUSPLUS >= 202300L ) + +// use C++17 std::string_view if available and requested: + +#if nssv_CPP17_OR_GREATER && defined(__has_include ) +# if __has_include( ) +# define nssv_HAVE_STD_STRING_VIEW 1 +# else +# define nssv_HAVE_STD_STRING_VIEW 0 +# endif +#else +# define nssv_HAVE_STD_STRING_VIEW 0 +#endif + +#define nssv_USES_STD_STRING_VIEW ( (nssv_CONFIG_SELECT_STRING_VIEW == nssv_STRING_VIEW_STD) || ((nssv_CONFIG_SELECT_STRING_VIEW == nssv_STRING_VIEW_DEFAULT) && nssv_HAVE_STD_STRING_VIEW) ) + +#define nssv_HAVE_STARTS_WITH ( nssv_CPP20_OR_GREATER || !nssv_USES_STD_STRING_VIEW ) +#define nssv_HAVE_ENDS_WITH nssv_HAVE_STARTS_WITH -// 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, +// Use C++17 std::string_view: // -// -e <= 60 or e >= -60 := alpha -constexpr int kAlpha = -60; -constexpr int kGamma = -32; +#if nssv_USES_STD_STRING_VIEW -struct cached_power // c = f * 2^e ~= 10^k +#include + +// Extensions for std::string: + +#if nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS + +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() ) { - std::uint64_t f; - int e; - int k; -}; + return std::basic_string( v.begin(), v.end(), a ); +} -/*! -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.) +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() ); +} - // NB: - // Actually this function returns c, such that -60 <= e_c + e + 64 <= -34. +// Literal operators sv and _sv: - constexpr int kCachedPowersMinDecExp = -300; - constexpr int kCachedPowersDecStep = 8; +#if nssv_CONFIG_STD_SV_OPERATOR - 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}, - }}; +using namespace std::literals::string_view_literals; - // 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); +#endif - const int index = (-kCachedPowersMinDecExp + k + (kCachedPowersDecStep - 1)) / - kCachedPowersDecStep; +#if nssv_CONFIG_USR_SV_OPERATOR - const cached_power cached = kCachedPowers[static_cast(index)]; +inline namespace literals { +inline namespace string_view_literals { - return cached; + +constexpr std::string_view operator "" _sv( const char* str, size_t len ) noexcept // (1) +{ + return std::string_view{ str, len }; } -/*! -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; - } +constexpr std::u16string_view operator "" _sv( const char16_t* str, size_t len ) noexcept // (2) +{ + return std::u16string_view{ str, len }; } -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) { +constexpr std::u32string_view operator "" _sv( const char32_t* str, size_t len ) noexcept // (3) +{ + return std::u32string_view{ str, len }; +} - // <--------------------------- 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. +constexpr std::wstring_view operator "" _sv( const wchar_t* str, size_t len ) noexcept // (4) +{ + return std::wstring_view{ str, len }; +} - // The tests are written in this order to avoid overflow in unsigned - // integer arithmetic. +}} // namespace literals::string_view_literals - while (rest < dist && delta - rest >= ten_k && - (rest + ten_k < dist || dist - rest > rest + ten_k - dist)) { - buf[len - 1]--; - rest += ten_k; - } -} +#endif // nssv_CONFIG_USR_SV_OPERATOR -/*! -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"); +} // namespace nonstd - // 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+]. +#endif // nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS - 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) +namespace nonstd { - // 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 +using std::string_view; +using std::wstring_view; +using std::u16string_view; +using std::u32string_view; +using std::basic_string_view; - const diyfp one(std::uint64_t{1} << -M_plus.e, M_plus.e); +// literal "sv" and "_sv", see above - 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 +using std::operator==; +using std::operator!=; +using std::operator<; +using std::operator<=; +using std::operator>; +using std::operator>=; - // 1) - // - // Generate the digits of the integral part p1 = d[n-1]...d[1]d[0] +using std::operator<<; - std::uint32_t pow10; - const int k = find_largest_pow10(p1, pow10); +} // namespace nonstd - // 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 +#else // nssv_HAVE_STD_STRING_VIEW - 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 - // +// +// Before C++17: use string_view lite: +// - // 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+. +// Compiler versions: +// +// MSVC++ 6.0 _MSC_VER == 1200 nssv_COMPILER_MSVC_VERSION == 60 (Visual Studio 6.0) +// MSVC++ 7.0 _MSC_VER == 1300 nssv_COMPILER_MSVC_VERSION == 70 (Visual Studio .NET 2002) +// MSVC++ 7.1 _MSC_VER == 1310 nssv_COMPILER_MSVC_VERSION == 71 (Visual Studio .NET 2003) +// MSVC++ 8.0 _MSC_VER == 1400 nssv_COMPILER_MSVC_VERSION == 80 (Visual Studio 2005) +// MSVC++ 9.0 _MSC_VER == 1500 nssv_COMPILER_MSVC_VERSION == 90 (Visual Studio 2008) +// MSVC++ 10.0 _MSC_VER == 1600 nssv_COMPILER_MSVC_VERSION == 100 (Visual Studio 2010) +// MSVC++ 11.0 _MSC_VER == 1700 nssv_COMPILER_MSVC_VERSION == 110 (Visual Studio 2012) +// MSVC++ 12.0 _MSC_VER == 1800 nssv_COMPILER_MSVC_VERSION == 120 (Visual Studio 2013) +// MSVC++ 14.0 _MSC_VER == 1900 nssv_COMPILER_MSVC_VERSION == 140 (Visual Studio 2015) +// MSVC++ 14.1 _MSC_VER >= 1910 nssv_COMPILER_MSVC_VERSION == 141 (Visual Studio 2017) +// MSVC++ 14.2 _MSC_VER >= 1920 nssv_COMPILER_MSVC_VERSION == 142 (Visual Studio 2019) + +#if defined(_MSC_VER ) && !defined(__clang__) +# define nssv_COMPILER_MSVC_VER (_MSC_VER ) +# define nssv_COMPILER_MSVC_VERSION (_MSC_VER / 10 - 10 * ( 5 + (_MSC_VER < 1900 ) ) ) +#else +# define nssv_COMPILER_MSVC_VER 0 +# define nssv_COMPILER_MSVC_VERSION 0 +#endif - decimal_exponent += n; +#define nssv_COMPILER_VERSION( major, minor, patch ) ( 10 * ( 10 * (major) + (minor) ) + (patch) ) - // 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); +#if defined( __apple_build_version__ ) +# define nssv_COMPILER_APPLECLANG_VERSION nssv_COMPILER_VERSION(__clang_major__, __clang_minor__, __clang_patchlevel__) +# define nssv_COMPILER_CLANG_VERSION 0 +#elif defined( __clang__ ) +# define nssv_COMPILER_APPLECLANG_VERSION 0 +# define nssv_COMPILER_CLANG_VERSION nssv_COMPILER_VERSION(__clang_major__, __clang_minor__, __clang_patchlevel__) +#else +# define nssv_COMPILER_APPLECLANG_VERSION 0 +# define nssv_COMPILER_CLANG_VERSION 0 +#endif - return; - } +#if defined(__GNUC__) && !defined(__clang__) +# define nssv_COMPILER_GNUC_VERSION nssv_COMPILER_VERSION(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) +#else +# define nssv_COMPILER_GNUC_VERSION 0 +#endif - pow10 /= 10; - // - // pow10 = 10^(n-1) <= p1 < 10^n - // Invariants restored. - } +// half-open range [lo..hi): +#define nssv_BETWEEN( v, lo, hi ) ( (lo) <= (v) && (v) < (hi) ) - // 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 +// Presence of language and library features: - 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. +#ifdef _HAS_CPP0X +# define nssv_HAS_CPP0X _HAS_CPP0X +#else +# define nssv_HAS_CPP0X 0 +#endif - // 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; - } - } +// Unless defined otherwise below, consider VC14 as C++11 for variant-lite: - // V = buffer * 10^-m, with M- <= V <= M+. +#if nssv_COMPILER_MSVC_VER >= 1900 +# undef nssv_CPP11_OR_GREATER +# define nssv_CPP11_OR_GREATER 1 +#endif - decimal_exponent -= m; +#define nssv_CPP11_90 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1500) +#define nssv_CPP11_100 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1600) +#define nssv_CPP11_110 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1700) +#define nssv_CPP11_120 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1800) +#define nssv_CPP11_140 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1900) +#define nssv_CPP11_141 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1910) + +#define nssv_CPP14_000 (nssv_CPP14_OR_GREATER) +#define nssv_CPP17_000 (nssv_CPP17_OR_GREATER) + +// Presence of C++11 language features: + +#define nssv_HAVE_CONSTEXPR_11 nssv_CPP11_140 +#define nssv_HAVE_EXPLICIT_CONVERSION nssv_CPP11_140 +#define nssv_HAVE_INLINE_NAMESPACE nssv_CPP11_140 +#define nssv_HAVE_IS_DEFAULT nssv_CPP11_140 +#define nssv_HAVE_IS_DELETE nssv_CPP11_140 +#define nssv_HAVE_NOEXCEPT nssv_CPP11_140 +#define nssv_HAVE_NULLPTR nssv_CPP11_100 +#define nssv_HAVE_REF_QUALIFIER nssv_CPP11_140 +#define nssv_HAVE_UNICODE_LITERALS nssv_CPP11_140 +#define nssv_HAVE_USER_DEFINED_LITERALS nssv_CPP11_140 +#define nssv_HAVE_WCHAR16_T nssv_CPP11_100 +#define nssv_HAVE_WCHAR32_T nssv_CPP11_100 + +#if ! ( ( nssv_CPP11_OR_GREATER && nssv_COMPILER_CLANG_VERSION ) || nssv_BETWEEN( nssv_COMPILER_CLANG_VERSION, 300, 400 ) ) +# define nssv_HAVE_STD_DEFINED_LITERALS nssv_CPP11_140 +#else +# define nssv_HAVE_STD_DEFINED_LITERALS 0 +#endif - // 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); +// Presence of C++14 language features: - // 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) -} +#define nssv_HAVE_CONSTEXPR_14 nssv_CPP14_000 -/*! -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) { +// Presence of C++17 language features: - // --------(-----------------------+-----------------------)-------- (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]. +#define nssv_HAVE_NODISCARD nssv_CPP17_000 - const cached_power cached = get_cached_power_for_binary_exponent(m_plus.e); +// Presence of C++ library features: - const diyfp c_minus_k(cached.f, cached.e); // = c ~= 10^-k +#define nssv_HAVE_STD_HASH nssv_CPP11_120 - // 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); +// Presence of compiler intrinsics: - // ----(---+---)---------------(---+---)---------------(---+---)---- - // 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); +// Providing char-type specializations for compare() and length() that +// use compiler intrinsics can improve compile- and run-time performance. +// +// The challenge is in using the right combinations of builtin availability +// and its constexpr-ness. +// +// | compiler | __builtin_memcmp (constexpr) | memcmp (constexpr) | +// |----------|------------------------------|---------------------| +// | clang | 4.0 (>= 4.0 ) | any (? ) | +// | clang-a | 9.0 (>= 9.0 ) | any (? ) | +// | gcc | any (constexpr) | any (? ) | +// | msvc | >= 14.2 C++17 (>= 14.2 ) | any (? ) | - decimal_exponent = -cached.k; // = -(-k) = k +#define nssv_HAVE_BUILTIN_VER ( (nssv_CPP17_000 && nssv_COMPILER_MSVC_VERSION >= 142) || nssv_COMPILER_GNUC_VERSION > 0 || nssv_COMPILER_CLANG_VERSION >= 400 || nssv_COMPILER_APPLECLANG_VERSION >= 900 ) +#define nssv_HAVE_BUILTIN_CE ( nssv_HAVE_BUILTIN_VER ) - grisu2_digit_gen(buf, len, decimal_exponent, M_minus, w, M_plus); -} +#define nssv_HAVE_BUILTIN_MEMCMP ( (nssv_HAVE_CONSTEXPR_14 && nssv_HAVE_BUILTIN_CE) || !nssv_HAVE_CONSTEXPR_14 ) +#define nssv_HAVE_BUILTIN_STRLEN ( (nssv_HAVE_CONSTEXPR_11 && nssv_HAVE_BUILTIN_CE) || !nssv_HAVE_CONSTEXPR_11 ) -/*! -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"); +#ifdef __has_builtin +# define nssv_HAVE_BUILTIN( x ) __has_builtin( x ) +#else +# define nssv_HAVE_BUILTIN( x ) 0 +#endif - // 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)); +#if nssv_HAVE_BUILTIN(__builtin_memcmp) || nssv_HAVE_BUILTIN_VER +# define nssv_BUILTIN_MEMCMP __builtin_memcmp #else - const boundaries w = compute_boundaries(value); +# define nssv_BUILTIN_MEMCMP memcmp #endif - grisu2(buf, len, decimal_exponent, w.minus, w.w, w.plus); -} +#if nssv_HAVE_BUILTIN(__builtin_strlen) || nssv_HAVE_BUILTIN_VER +# define nssv_BUILTIN_STRLEN __builtin_strlen +#else +# define nssv_BUILTIN_STRLEN strlen +#endif -/*! -@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) { +// C++ feature usage: - if (e < 0) { - e = -e; - *buf++ = '-'; - } else { - *buf++ = '+'; - } +#if nssv_HAVE_CONSTEXPR_11 +# define nssv_constexpr constexpr +#else +# define nssv_constexpr /*constexpr*/ +#endif - 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); - } +#if nssv_HAVE_CONSTEXPR_14 +# define nssv_constexpr14 constexpr +#else +# define nssv_constexpr14 /*constexpr*/ +#endif - return buf; -} +#if nssv_HAVE_EXPLICIT_CONVERSION +# define nssv_explicit explicit +#else +# define nssv_explicit /*explicit*/ +#endif -/*! -@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) { +#if nssv_HAVE_INLINE_NAMESPACE +# define nssv_inline_ns inline +#else +# define nssv_inline_ns /*inline*/ +#endif - const int k = len; - const int n = len + decimal_exponent; +#if nssv_HAVE_NOEXCEPT +# define nssv_noexcept noexcept +#else +# define nssv_noexcept /*noexcept*/ +#endif - // 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 nssv_HAVE_REF_QUALIFIER +//# define nssv_ref_qual & +//# define nssv_refref_qual && +//#else +//# define nssv_ref_qual /*&*/ +//# define nssv_refref_qual /*&&*/ +//#endif - if (k <= n && n <= max_exp) { - // digits[000] - // len <= max_exp + 2 +#if nssv_HAVE_NULLPTR +# define nssv_nullptr nullptr +#else +# define nssv_nullptr NULL +#endif - 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 nssv_HAVE_NODISCARD +# define nssv_nodiscard [[nodiscard]] +#else +# define nssv_nodiscard /*[[nodiscard]]*/ +#endif - 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); - } +// Additional includes: - if (min_exp < n && n <= 0) { - // 0.[000]digits - // len <= 2 + (-min_exp - 1) + max_digits10 +#include +#include +#include +#include +#include // std::char_traits<> - 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 ! nssv_CONFIG_NO_STREAM_INSERTION +# include +#endif - if (k == 1) { - // dE+123 - // len <= 1 + 5 +#if ! nssv_CONFIG_NO_EXCEPTIONS +# include +#endif - buf += 1; - } else { - // d.igitsE+123 - // len <= max_digits10 + 1 + 5 +#if nssv_CPP11_OR_GREATER +# include +#endif - std::memmove(buf + 2, buf + 1, static_cast(k) - 1); - buf[1] = '.'; - buf += 1 + static_cast(k); - } +// Clang, GNUC, MSVC warning suppression macros: + +#if defined(__clang__) +# pragma clang diagnostic ignored "-Wreserved-user-defined-literal" +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wuser-defined-literals" +#elif defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wliteral-suffix" +#endif // __clang__ + +#if nssv_COMPILER_MSVC_VERSION >= 140 +# define nssv_SUPPRESS_MSGSL_WARNING(expr) [[gsl::suppress(expr)]] +# define nssv_SUPPRESS_MSVC_WARNING(code, descr) __pragma(warning(suppress: code) ) +# define nssv_DISABLE_MSVC_WARNINGS(codes) __pragma(warning(push)) __pragma(warning(disable: codes)) +#else +# define nssv_SUPPRESS_MSGSL_WARNING(expr) +# define nssv_SUPPRESS_MSVC_WARNING(code, descr) +# define nssv_DISABLE_MSVC_WARNINGS(codes) +#endif - *buf++ = 'e'; - return append_exponent(buf, n - 1); -} +#if defined(__clang__) +# define nssv_RESTORE_WARNINGS() _Pragma("clang diagnostic pop") +#elif defined(__GNUC__) +# define nssv_RESTORE_WARNINGS() _Pragma("GCC diagnostic pop") +#elif nssv_COMPILER_MSVC_VERSION >= 140 +# define nssv_RESTORE_WARNINGS() __pragma(warning(pop )) +#else +# define nssv_RESTORE_WARNINGS() +#endif -} // namespace dtoa_impl +// Suppress the following MSVC (GSL) warnings: +// - C4455, non-gsl : 'operator ""sv': literal suffix identifiers that do not +// start with an underscore are reserved +// - C26472, gsl::t.1 : don't use a static_cast for arithmetic conversions; +// use brace initialization, gsl::narrow_cast or gsl::narow +// - C26481: gsl::b.1 : don't use pointer arithmetic. Use span instead -/*! -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++ = '-'; - } +nssv_DISABLE_MSVC_WARNINGS( 4455 26481 26472 ) +//nssv_DISABLE_CLANG_WARNINGS( "-Wuser-defined-literals" ) +//nssv_DISABLE_GNUC_WARNINGS( -Wliteral-suffix ) - 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; +namespace nonstd { namespace sv_lite { - return dtoa_impl::format_buffer(first, len, decimal_exponent, kMinExp, - kMaxExp); -} -} // namespace internal -} // namespace simdjson -/* end file src/to_chars.cpp */ -/* begin file src/from_chars.cpp */ -#include -namespace simdjson { -namespace internal { +// +// basic_string_view declaration: +// -/** - * 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. - **/ +template +< + class CharT, + class Traits = std::char_traits +> +class basic_string_view; -namespace { -constexpr uint32_t max_digits = 768; -constexpr int32_t decimal_point_range = 2047; -} // namespace +namespace detail { -struct adjusted_mantissa { - uint64_t mantissa; - int power2; - adjusted_mantissa() : mantissa(0), power2(0) {} -}; +// support constexpr comparison in C++14; +// for C++17 and later, use provided traits: -struct decimal { - uint32_t num_digits; - int32_t decimal_point; - bool negative; - bool truncated; - uint8_t digits[max_digits]; -}; +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 struct binary_format { - static constexpr int mantissa_explicit_bits(); - static constexpr int minimum_exponent(); - static constexpr int infinite_power(); - static constexpr int sign_index(); -}; +#if nssv_HAVE_BUILTIN_MEMCMP -template <> constexpr int binary_format::mantissa_explicit_bits() { - return 52; +// 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 ); } -template <> constexpr int binary_format::minimum_exponent() { - return -1023; +#endif + +#if nssv_HAVE_BUILTIN_STRLEN + +// specialization of length() for char, see also generic length() further below: + +inline nssv_constexpr std::size_t length( char const * s ) +{ + return nssv_BUILTIN_STRLEN( s ); } -template <> constexpr int binary_format::infinite_power() { - return 0x7FF; + +#endif + +#if defined(__OPTIMIZE__) + +// gcc, clang provide __OPTIMIZE__ +// Expect tail call optimization to make length() non-recursive: + +template< typename CharT > +inline nssv_constexpr std::size_t length( CharT * s, std::size_t result = 0 ) +{ + return *s == '\0' ? result : length( s + 1, result + 1 ); } -template <> constexpr int binary_format::sign_index() { return 63; } +#else // OPTIMIZE -bool is_integer(char c) noexcept { return (c >= '0' && c <= '9'); } +// non-recursive: -// 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; - } +template< typename CharT > +inline nssv_constexpr14 std::size_t length( CharT * s ) +{ + std::size_t result = 0; + while ( *s++ != '\0' ) + { + ++result; + } + return result; +} - while (*p == '0') { - ++p; - } - while (is_integer(*p)) { - if (answer.num_digits < max_digits) { - answer.digits[answer.num_digits] = uint8_t(*p - '0'); +#endif // OPTIMIZE + +#if nssv_CPP11_OR_GREATER && ! nssv_CPP17_OR_GREATER +#if defined(__OPTIMIZE__) + +// gcc, clang provide __OPTIMIZE__ +// Expect tail call optimization to make search() non-recursive: + +template< class CharT, class Traits = std::char_traits > +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: + +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() ); +} + +#endif // OPTIMIZE +#endif // nssv_CPP11_OR_GREATER && ! nssv_CPP17_OR_GREATER + +} // namespace detail + +// +// basic_string_view: +// + +template +< + class CharT, + class Traits /* = std::char_traits */ +> +class basic_string_view +{ +public: + // Member types: + + typedef Traits traits_type; + typedef CharT value_type; + + typedef CharT * pointer; + typedef CharT const * const_pointer; + typedef CharT & reference; + typedef CharT const & const_reference; + + typedef const_pointer iterator; + typedef const_pointer const_iterator; + typedef std::reverse_iterator< const_iterator > reverse_iterator; + typedef std::reverse_iterator< const_iterator > const_reverse_iterator; + + typedef std::size_t size_type; + typedef std::ptrdiff_t difference_type; + + // 24.4.2.1 Construction and assignment: + + nssv_constexpr basic_string_view() nssv_noexcept + : data_( nssv_nullptr ) + , size_( 0 ) + {} + +#if nssv_CPP11_OR_GREATER + nssv_constexpr basic_string_view( basic_string_view const & other ) nssv_noexcept = default; +#else + nssv_constexpr basic_string_view( basic_string_view const & other ) nssv_noexcept + : data_( other.data_) + , size_( other.size_) + {} +#endif + + nssv_constexpr basic_string_view( CharT const * s, size_type count ) nssv_noexcept // non-standard noexcept + : data_( s ) + , size_( count ) + {} + + nssv_constexpr basic_string_view( CharT const * s) nssv_noexcept // non-standard noexcept + : data_( s ) +#if nssv_CPP17_OR_GREATER + , size_( Traits::length(s) ) +#elif nssv_CPP11_OR_GREATER + , size_( detail::length(s) ) +#else + , size_( Traits::length(s) ) +#endif + {} + +#if nssv_HAVE_NULLPTR +# if nssv_HAVE_IS_DELETE + nssv_constexpr basic_string_view( std::nullptr_t ) nssv_noexcept = delete; +# else + private: nssv_constexpr basic_string_view( std::nullptr_t ) nssv_noexcept; public: +# endif +#endif + + // Assignment: + +#if nssv_CPP11_OR_GREATER + nssv_constexpr14 basic_string_view & operator=( basic_string_view const & other ) nssv_noexcept = default; +#else + nssv_constexpr14 basic_string_view & operator=( basic_string_view const & other ) nssv_noexcept + { + data_ = other.data_; + size_ = other.size_; + return *this; } - 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; - } +#endif + + // 24.4.2.2 Iterator support: + + nssv_constexpr const_iterator begin() const nssv_noexcept { return data_; } + nssv_constexpr const_iterator end() const nssv_noexcept { return data_ + size_; } + + nssv_constexpr const_iterator cbegin() const nssv_noexcept { return begin(); } + nssv_constexpr const_iterator cend() const nssv_noexcept { return end(); } + + nssv_constexpr const_reverse_iterator rbegin() const nssv_noexcept { return const_reverse_iterator( end() ); } + nssv_constexpr const_reverse_iterator rend() const nssv_noexcept { return const_reverse_iterator( begin() ); } + + nssv_constexpr const_reverse_iterator crbegin() const nssv_noexcept { return rbegin(); } + nssv_constexpr const_reverse_iterator crend() const nssv_noexcept { return rend(); } + + // 24.4.2.3 Capacity: + + nssv_constexpr size_type size() const nssv_noexcept { return size_; } + nssv_constexpr size_type length() const nssv_noexcept { return size_; } + nssv_constexpr size_type max_size() const nssv_noexcept { return (std::numeric_limits< size_type >::max)(); } + + // since C++20 + nssv_nodiscard nssv_constexpr bool empty() const nssv_noexcept + { + return 0 == size_; } - while (is_integer(*p)) { - if (answer.num_digits < max_digits) { - answer.digits[answer.num_digits] = uint8_t(*p - '0'); - } - answer.num_digits++; - ++p; + + // 24.4.2.4 Element access: + + nssv_constexpr const_reference operator[]( size_type pos ) const + { + return data_at( pos ); } - 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; + + nssv_constexpr14 const_reference at( size_type pos ) const + { +#if nssv_CONFIG_NO_EXCEPTIONS + assert( pos < size() ); +#else + if ( pos >= size() ) + { + throw std::out_of_range("nonstd::string_view::at()"); + } +#endif + return data_at( pos ); } - 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; + + nssv_constexpr const_reference front() const { return data_at( 0 ); } + nssv_constexpr const_reference back() const { return data_at( size() - 1 ); } + + nssv_constexpr const_pointer data() const nssv_noexcept { return data_; } + + // 24.4.2.5 Modifiers: + + nssv_constexpr14 void remove_prefix( size_type n ) + { + assert( n <= size() ); + data_ += n; + size_ -= n; } - 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; + + nssv_constexpr14 void remove_suffix( size_type n ) + { + assert( n <= size() ); + size_ -= n; } - 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; - } + nssv_constexpr14 void swap( basic_string_view & other ) nssv_noexcept + { + const basic_string_view tmp(other); + other = *this; + *this = tmp; + } - 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'); + // 24.4.2.6 String operations: + + size_type copy( CharT * dest, size_type n, size_type pos = 0 ) const + { +#if nssv_CONFIG_NO_EXCEPTIONS + assert( pos <= size() ); +#else + if ( pos > size() ) + { + throw std::out_of_range("nonstd::string_view::copy()"); + } +#endif + const size_type rlen = (std::min)( n, size() - pos ); + + (void) Traits::copy( dest, data() + pos, rlen ); + + return rlen; } - 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; - } + + nssv_constexpr14 basic_string_view substr( size_type pos = 0, size_type n = npos ) const + { +#if nssv_CONFIG_NO_EXCEPTIONS + assert( pos <= size() ); +#else + if ( pos > size() ) + { + throw std::out_of_range("nonstd::string_view::substr()"); + } +#endif + return basic_string_view( data() + pos, (std::min)( n, size() - pos ) ); } - 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; + + // compare(), 6x: + + nssv_constexpr14 int compare( basic_string_view other ) const nssv_noexcept // (1) + { +#if nssv_CPP17_OR_GREATER + if ( const int result = Traits::compare( data(), other.data(), (std::min)( size(), other.size() ) ) ) +#else + if ( const int result = detail::compare( data(), other.data(), (std::min)( size(), other.size() ) ) ) +#endif + { + return result; + } + + return size() == other.size() ? 0 : size() < other.size() ? -1 : 1; } - 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; + + nssv_constexpr int compare( size_type pos1, size_type n1, basic_string_view other ) const // (2) + { + return substr( pos1, n1 ).compare( other ); } - 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; + + 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 ) ); } - 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; + + nssv_constexpr int compare( CharT const * s ) const // (4) + { + return compare( basic_string_view( s ) ); } - answer.decimal_point += (neg_exp ? -exp_number : exp_number); - } - return answer; -} -namespace { + nssv_constexpr int compare( size_type pos1, size_type n1, CharT const * s ) const // (5) + { + return substr( pos1, n1 ).compare( basic_string_view( s ) ); + } -// remove all final zeroes -inline void trim(decimal &h) { - while ((h.num_digits > 0) && (h.digits[h.num_digits - 1] == 0)) { - h.num_digits--; - } -} + 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 ) ); + } -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; + // 24.4.2.7 Searching: + + // starts_with(), 3x, since C++20: + + nssv_constexpr bool starts_with( basic_string_view v ) const nssv_noexcept // (1) + { + return size() >= v.size() && compare( 0, v.size(), v ) == 0; } - } - return num_new_digits; -} -} // end of anonymous namespace + nssv_constexpr bool starts_with( CharT c ) const nssv_noexcept // (2) + { + return starts_with( basic_string_view( &c, 1 ) ); + } -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])); + nssv_constexpr bool starts_with( CharT const * s ) const // (3) + { + return starts_with( basic_string_view( s ) ); } - } - 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; + // ends_with(), 3x, since C++20: - 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; + nssv_constexpr bool ends_with( basic_string_view v ) const nssv_noexcept // (1) + { + return size() >= v.size() && compare( size() - v.size(), npos, v ) == 0; } - 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; + + nssv_constexpr bool ends_with( CharT c ) const nssv_noexcept // (2) + { + return ends_with( basic_string_view( &c, 1 ) ); } - 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; + nssv_constexpr bool ends_with( CharT const * s ) const // (3) + { + return ends_with( basic_string_view( s ) ); + } - uint64_t n = 0; + // find(), 4x: - 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; + nssv_constexpr size_type find( basic_string_view v, size_type pos = 0 ) const nssv_noexcept // (1) + { + return assert( v.size() == 0 || v.data() != nssv_nullptr ) + , pos >= size() + ? npos : to_pos( +#if nssv_CPP11_OR_GREATER && ! nssv_CPP17_OR_GREATER + detail::search( substr(pos), v ) +#else + std::search( cbegin() + pos, cend(), v.cbegin(), v.cend(), Traits::eq ) +#endif + ); } - } - 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; + + nssv_constexpr size_type find( CharT c, size_type pos = 0 ) const nssv_noexcept // (2) + { + return find( basic_string_view( &c, 1 ), pos ); } - } - 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; - } + nssv_constexpr size_type find( CharT const * s, size_type pos, size_type n ) const // (3) + { + return find( basic_string_view( s, n ), pos ); + } - 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; + nssv_constexpr size_type find( CharT const * s, size_type pos = 0 ) const // (4) + { + return find( basic_string_view( s ), pos ); } - 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; + + // rfind(), 4x: + + nssv_constexpr14 size_type rfind( basic_string_view v, size_type pos = npos ) const nssv_noexcept // (1) + { + if ( size() < v.size() ) + { + return npos; + } + + if ( v.empty() ) + { + return (std::min)( size(), pos ); + } + + const_iterator last = cbegin() + (std::min)( size() - v.size(), pos ) + v.size(); + const_iterator result = std::find_end( cbegin(), last, v.cbegin(), v.cend(), Traits::eq ); + + return result != last ? size_type( result - cbegin() ) : npos; } - 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; + + nssv_constexpr14 size_type rfind( CharT c, size_type pos = npos ) const nssv_noexcept // (2) + { + return rfind( basic_string_view( &c, 1 ), pos ); } - 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; + + nssv_constexpr14 size_type rfind( CharT const * s, size_type pos, size_type n ) const // (3) + { + return rfind( basic_string_view( s, n ), pos ); } - 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); + nssv_constexpr14 size_type rfind( CharT const * s, size_type pos = npos ) const // (4) + { + return rfind( basic_string_view( s ), pos ); + } - 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; + // 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 ) ); } - } - 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); -} + 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 ); + } -template -adjusted_mantissa parse_long_mantissa(const char *first, const char *end) { - decimal d = parse_decimal(first, end); - return compute_float(d); -} + 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 ); + } -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; -} + 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: -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; -} + 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 ) ); + } -} // internal -} // simdjson -/* end file src/from_chars.cpp */ -/* begin file src/internal/error_tables.cpp */ + 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 ); + } -namespace simdjson { -namespace internal { + 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 ); + } - 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" }, - { 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[] + 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 ); + } -} // namespace internal -} // namespace simdjson -/* end file src/internal/error_tables.cpp */ -/* begin file src/internal/jsoncharutils_tables.cpp */ + // find_first_not_of(), 4x: -namespace simdjson { -namespace internal { + 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 ) ) ); + } -// 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 + 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 ); + } -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, + 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 ); + } - 1, 1, 1, 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, + 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 ); + } - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + // find_last_not_of(), 4x: - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; + 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 ) ) ); + } -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}; + 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 ); + } -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}; + 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 ); + } -} // namespace internal -} // namespace simdjson -/* end file src/internal/jsoncharutils_tables.cpp */ -/* begin file src/internal/numberparsing_tables.cpp */ + 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 ); + } -namespace simdjson { -namespace internal { + // Constants: -// Precomputed powers of ten from 10^0 to 10^22. These -// can be represented exactly using the double type. -SIMDJSON_DLLIMPORTEXPORT const double 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}; +#if nssv_CPP17_OR_GREATER + static nssv_constexpr size_type npos = size_type(-1); +#elif nssv_CPP11_OR_GREATER + enum : size_type { npos = size_type(-1) }; +#else + enum { npos = size_type(-1) }; +#endif -/** - * 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. - */ +private: + struct not_in_view + { + const basic_string_view v; + nssv_constexpr explicit not_in_view( basic_string_view v_ ) : v( v_ ) {} -// 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 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, + nssv_constexpr bool operator()( CharT c ) const + { + return npos == v.find_first_of( c ); + } + }; + + nssv_constexpr size_type to_pos( const_iterator it ) const + { + return it == cend() ? npos : size_type( it - cbegin() ); + } + + nssv_constexpr size_type to_pos( const_reverse_iterator it ) const + { + return it == crend() ? npos : size_type( crend() - it - 1 ); + } + + nssv_constexpr const_reference data_at( size_type pos ) const + { +#if nssv_BETWEEN( nssv_COMPILER_GNUC_VERSION, 1, 500 ) + return data_[pos]; +#else + return assert( pos < size() ), data_[pos]; +#endif + } + +private: + const_pointer data_; + size_type size_; + +public: +#if nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS + + template< class Allocator > + basic_string_view( std::basic_string 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() ); + } + +#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 ); + } + +#else + + 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 ); + } + +#endif // nssv_CPP11_OR_GREATER + +#endif // nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS +}; + +// +// Non-member functions: +// + +// 24.4.3 Non-member comparison functions: +// lexicographically compare two string views (function template): + +template< class CharT, class Traits > +nssv_constexpr bool operator== ( + basic_string_view 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.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. + +#if ! nssv_CPP11_OR_GREATER || nssv_BETWEEN( nssv_COMPILER_MSVC_VERSION, 100, 141 ) + +// accommodate for older compilers: + +// == + +template< class CharT, class Traits> +nssv_constexpr bool operator==( + basic_string_view 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==( + 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!=( + 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!=( + 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<( + 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<( + 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<=( + 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<=( + 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>( + 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>( + 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>=( + 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>=( + 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; } + +#else // newer compilers: + +#define nssv_BASIC_STRING_VIEW_I(T,U) typename std::decay< basic_string_view >::type + +#if defined(_MSC_VER) // issue 40 +# define nssv_MSVC_ORDER(x) , int=x +#else +# define nssv_MSVC_ORDER(x) /*, int=x*/ +#endif + +// == + +template< class CharT, class Traits nssv_MSVC_ORDER(1) > +nssv_constexpr bool operator==( + basic_string_view 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(1) > +nssv_constexpr bool operator!= ( + basic_string_view < CharT, Traits > lhs, + nssv_BASIC_STRING_VIEW_I( CharT, Traits ) rhs ) nssv_noexcept +{ return !( lhs == rhs ); } + +template< class CharT, class Traits nssv_MSVC_ORDER(2) > +nssv_constexpr bool operator!= ( + nssv_BASIC_STRING_VIEW_I( CharT, Traits ) lhs, + basic_string_view < CharT, Traits > rhs ) nssv_noexcept +{ return !( lhs == rhs ); } + +// < + +template< class CharT, class Traits nssv_MSVC_ORDER(1) > +nssv_constexpr bool operator< ( + basic_string_view < CharT, Traits > lhs, + nssv_BASIC_STRING_VIEW_I( CharT, Traits ) rhs ) nssv_noexcept +{ return lhs.compare( rhs ) < 0; } + +template< class CharT, class Traits nssv_MSVC_ORDER(2) > +nssv_constexpr bool operator< ( + nssv_BASIC_STRING_VIEW_I( CharT, Traits ) lhs, + basic_string_view < CharT, Traits > rhs ) nssv_noexcept +{ return lhs.compare( rhs ) < 0; } + +// <= + +template< class CharT, class Traits nssv_MSVC_ORDER(1) > +nssv_constexpr bool operator<= ( + basic_string_view < CharT, Traits > lhs, + nssv_BASIC_STRING_VIEW_I( CharT, Traits ) rhs ) nssv_noexcept +{ return lhs.compare( rhs ) <= 0; } + +template< class CharT, class Traits nssv_MSVC_ORDER(2) > +nssv_constexpr bool operator<= ( + nssv_BASIC_STRING_VIEW_I( CharT, Traits ) lhs, + basic_string_view < CharT, Traits > rhs ) nssv_noexcept +{ return lhs.compare( rhs ) <= 0; } + +// > + +template< class CharT, class Traits nssv_MSVC_ORDER(1) > +nssv_constexpr bool operator> ( + basic_string_view < CharT, Traits > lhs, + nssv_BASIC_STRING_VIEW_I( CharT, Traits ) rhs ) nssv_noexcept +{ return lhs.compare( rhs ) > 0; } + +template< class CharT, class Traits nssv_MSVC_ORDER(2) > +nssv_constexpr bool operator> ( + nssv_BASIC_STRING_VIEW_I( CharT, Traits ) lhs, + basic_string_view < CharT, Traits > rhs ) nssv_noexcept +{ return lhs.compare( rhs ) > 0; } + +// >= + +template< class CharT, class Traits nssv_MSVC_ORDER(1) > +nssv_constexpr bool operator>= ( + basic_string_view < CharT, Traits > lhs, + nssv_BASIC_STRING_VIEW_I( CharT, Traits ) rhs ) nssv_noexcept +{ return lhs.compare( rhs ) >= 0; } + +template< class CharT, class Traits nssv_MSVC_ORDER(2) > +nssv_constexpr bool operator>= ( + nssv_BASIC_STRING_VIEW_I( CharT, Traits ) lhs, + basic_string_view < CharT, Traits > rhs ) nssv_noexcept +{ return lhs.compare( rhs ) >= 0; } + +#undef nssv_MSVC_ORDER +#undef nssv_BASIC_STRING_VIEW_I + +#endif // compiler-dependent approach to comparisons + +// 24.4.4 Inserters and extractors: + +#if ! nssv_CONFIG_NO_STREAM_INSERTION + +namespace detail { + +template< class Stream > +void write_padding( Stream & os, std::streamsize n ) +{ + for ( std::streamsize i = 0; i < n; ++i ) + os.rdbuf()->sputc( os.fill() ); +} + +template< class Stream, class View > +Stream & write_to_stream( Stream & os, View const & sv ) +{ + typename Stream::sentry sentry( os ); + + if ( !sentry ) + return os; + + const std::streamsize length = static_cast( sv.length() ); + + // Whether, and how, to pad: + const bool pad = ( length < os.width() ); + const bool left_pad = pad && ( os.flags() & std::ios_base::adjustfield ) == std::ios_base::right; + + if ( left_pad ) + write_padding( os, os.width() - length ); + + // Write span characters: + os.rdbuf()->sputn( sv.begin(), length ); + + if ( pad && !left_pad ) + write_padding( os, os.width() - length ); + + // Reset output stream width: + os.width( 0 ); + + return os; +} + +} // namespace detail + +template< class CharT, class Traits > +std::basic_ostream & +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: + +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; +#endif + +}} // namespace nonstd::sv_lite + +// +// 24.4.6 Suffix for basic_string_view literals: +// + +#if nssv_HAVE_USER_DEFINED_LITERALS + +namespace nonstd { +nssv_inline_ns namespace literals { +nssv_inline_ns namespace string_view_literals { + +#if nssv_CONFIG_STD_SV_OPERATOR && nssv_HAVE_STD_DEFINED_LITERALS + +nssv_constexpr nonstd::sv_lite::string_view operator "" sv( const char* str, size_t len ) nssv_noexcept // (1) +{ + return nonstd::sv_lite::string_view{ str, len }; +} + +nssv_constexpr nonstd::sv_lite::u16string_view operator "" sv( const char16_t* str, size_t len ) nssv_noexcept // (2) +{ + return nonstd::sv_lite::u16string_view{ str, len }; +} + +nssv_constexpr nonstd::sv_lite::u32string_view operator "" sv( const char32_t* str, size_t len ) nssv_noexcept // (3) +{ + return nonstd::sv_lite::u32string_view{ str, len }; +} + +nssv_constexpr nonstd::sv_lite::wstring_view operator "" sv( const wchar_t* str, size_t len ) nssv_noexcept // (4) +{ + return nonstd::sv_lite::wstring_view{ str, len }; +} + +#endif // nssv_CONFIG_STD_SV_OPERATOR && nssv_HAVE_STD_DEFINED_LITERALS + +#if nssv_CONFIG_USR_SV_OPERATOR + +nssv_constexpr nonstd::sv_lite::string_view operator "" _sv( const char* str, size_t len ) nssv_noexcept // (1) +{ + return nonstd::sv_lite::string_view{ str, len }; +} + +nssv_constexpr nonstd::sv_lite::u16string_view operator "" _sv( const char16_t* str, size_t len ) nssv_noexcept // (2) +{ + return nonstd::sv_lite::u16string_view{ str, len }; +} + +nssv_constexpr nonstd::sv_lite::u32string_view operator "" _sv( const char32_t* str, size_t len ) nssv_noexcept // (3) +{ + return nonstd::sv_lite::u32string_view{ str, len }; +} + +nssv_constexpr nonstd::sv_lite::wstring_view operator "" _sv( const wchar_t* str, size_t len ) nssv_noexcept // (4) +{ + return nonstd::sv_lite::wstring_view{ str, len }; +} + +#endif // nssv_CONFIG_USR_SV_OPERATOR + +}}} // namespace nonstd::literals::string_view_literals + +#endif + +// +// Extensions for std::string: +// + +#if nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS + +namespace nonstd { +namespace sv_lite { + +// Exclude MSVC 14 (19.00): it yields ambiguous to_string(): + +#if nssv_CPP11_OR_GREATER && nssv_COMPILER_MSVC_VERSION != 140 + +template< class CharT, class Traits, class Allocator = std::allocator > +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, 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() ); +} + +}} // namespace nonstd::sv_lite + +#endif // nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS + +// +// make types and algorithms available in namespace nonstd: +// + +namespace nonstd { + +using sv_lite::basic_string_view; +using sv_lite::string_view; +using sv_lite::wstring_view; + +#if nssv_HAVE_WCHAR16_T +using sv_lite::u16string_view; +#endif +#if nssv_HAVE_WCHAR32_T +using sv_lite::u32string_view; +#endif + +// literal "sv" + +using sv_lite::operator==; +using sv_lite::operator!=; +using sv_lite::operator<; +using sv_lite::operator<=; +using sv_lite::operator>; +using sv_lite::operator>=; + +#if ! nssv_CONFIG_NO_STREAM_INSERTION +using sv_lite::operator<<; +#endif + +#if nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS +using sv_lite::to_string; +using sv_lite::to_string_view; +#endif + +} // namespace nonstd + +// 24.4.5 Hash support (C++11): + +// Note: The hash value of a string view object is equal to the hash value of +// the corresponding string object. + +#if nssv_HAVE_STD_HASH + +#include + +namespace std { + +template<> +struct hash< nonstd::string_view > +{ +public: + std::size_t operator()( nonstd::string_view v ) const nssv_noexcept + { + return std::hash()( std::string( v.data(), v.size() ) ); + } +}; + +template<> +struct hash< nonstd::wstring_view > +{ +public: + std::size_t operator()( nonstd::wstring_view v ) const nssv_noexcept + { + return std::hash()( std::wstring( v.data(), v.size() ) ); + } +}; + +template<> +struct hash< nonstd::u16string_view > +{ +public: + std::size_t operator()( nonstd::u16string_view v ) const nssv_noexcept + { + return std::hash()( std::u16string( v.data(), v.size() ) ); + } +}; + +template<> +struct hash< nonstd::u32string_view > +{ +public: + std::size_t operator()( nonstd::u32string_view v ) const nssv_noexcept + { + return std::hash()( std::u32string( v.data(), v.size() ) ); + } +}; + +} // namespace std + +#endif // nssv_HAVE_STD_HASH + +nssv_RESTORE_WARNINGS() + +#endif // nssv_HAVE_STD_STRING_VIEW +#endif // NONSTD_SV_LITE_H_INCLUDED +/* end file simdjson/nonstd/string_view.hpp */ +SIMDJSON_POP_DISABLE_WARNINGS + +namespace std { + using string_view = nonstd::string_view; +} +#endif // SIMDJSON_HAS_STRING_VIEW +#undef SIMDJSON_HAS_STRING_VIEW // We are not going to need this macro anymore. + +/// If EXPR is an error, returns it. +#define SIMDJSON_TRY(EXPR) { auto _err = (EXPR); if (_err) { return _err; } } + +// Unless the programmer has already set SIMDJSON_DEVELOPMENT_CHECKS, +// we want to set it under debug builds. We detect a debug build +// under Visual Studio when the _DEBUG macro is set. Under the other +// compilers, we use the fact that they define __OPTIMIZE__ whenever +// they allow optimizations. +// It is possible that this could miss some cases where SIMDJSON_DEVELOPMENT_CHECKS +// is helpful, but the programmer can set the macro SIMDJSON_DEVELOPMENT_CHECKS. +// It could also wrongly set SIMDJSON_DEVELOPMENT_CHECKS (e.g., if the programmer +// sets _DEBUG in a release build under Visual Studio, or if some compiler fails to +// set the __OPTIMIZE__ macro). +#ifndef SIMDJSON_DEVELOPMENT_CHECKS +#ifdef _MSC_VER +// Visual Studio seems to set _DEBUG for debug builds. +#ifdef _DEBUG +#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__ +#define SIMDJSON_DEVELOPMENT_CHECKS 1 +#endif // __OPTIMIZE__ +#endif // _MSC_VER +#endif // SIMDJSON_DEVELOPMENT_CHECKS + +// The SIMDJSON_CHECK_EOF macro is a feature flag for the "don't require padding" +// feature. + +#if SIMDJSON_CPLUSPLUS17 +// if we have C++, then fallthrough is a default attribute +# define simdjson_fallthrough [[fallthrough]] +// check if we have __attribute__ support +#elif defined(__has_attribute) +// check if we have the __fallthrough__ attribute +#if __has_attribute(__fallthrough__) +// we are good to go: +# define simdjson_fallthrough __attribute__((__fallthrough__)) +#endif // __has_attribute(__fallthrough__) +#endif // SIMDJSON_CPLUSPLUS17 +// on some systems, we simply do not have support for fallthrough, so use a default: +#ifndef simdjson_fallthrough +# define simdjson_fallthrough do {} while (0) /* fallthrough */ +#endif // simdjson_fallthrough + +#if SIMDJSON_DEVELOPMENT_CHECKS +#define SIMDJSON_DEVELOPMENT_ASSERT(expr) do { assert ((expr)); } while (0) +#else +#define SIMDJSON_DEVELOPMENT_ASSERT(expr) do { } while (0) +#endif + +#ifndef SIMDJSON_UTF8VALIDATION +#define SIMDJSON_UTF8VALIDATION 1 +#endif + +#ifdef __has_include +// How do we detect that a compiler supports vbmi2? +// For sure if the following header is found, we are ok? +#if __has_include() +#define SIMDJSON_COMPILER_SUPPORTS_VBMI2 1 +#endif +#endif + +#ifdef _MSC_VER +#if _MSC_VER >= 1920 +// Visual Studio 2019 and up support VBMI2 under x64 even if the header +// avx512vbmi2intrin.h is not found. +#define SIMDJSON_COMPILER_SUPPORTS_VBMI2 1 +#endif +#endif + +// By default, we allow AVX512. +#ifndef SIMDJSON_AVX512_ALLOWED +#define SIMDJSON_AVX512_ALLOWED 1 +#endif + +#endif // SIMDJSON_COMMON_DEFS_H +/* end file simdjson/common_defs.h */ +/* skipped duplicate #include "simdjson/compiler_check.h" */ +/* including simdjson/error.h: #include "simdjson/error.h" */ +/* begin file simdjson/error.h */ +#ifndef SIMDJSON_ERROR_H +#define SIMDJSON_ERROR_H + +/* skipped duplicate #include "simdjson/base.h" */ + +#include +#include + +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 + 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 reference + 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 + 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 +}; + +/** + * Get the error message for the given error code. + * + * dom::parser parser; + * dom::element doc; + * auto error = parser.parse("foo",3).get(doc); + * if (error) { printf("Error: %s\n", error_message(error)); } + * + * @return The error message. + */ +inline const char *error_message(error_code error) noexcept; + +/** + * Write the error message to the output stream + */ +inline std::ostream& operator<<(std::ostream& out, error_code error) noexcept; + +/** + * Exception thrown when an exception-supporting simdjson method is called + */ +struct simdjson_error : public std::exception { + /** + * Create an exception from a simdjson error code. + * @param error The error code + */ + simdjson_error(error_code error) noexcept : _error{error} { } + /** The error message */ + const char *what() const noexcept { return error_message(error()); } + /** The error code */ + error_code error() const noexcept { return _error; } +private: + /** The error code that was used */ + error_code _error; +}; + +namespace internal { + +/** + * The result of a simdjson operation that could fail. + * + * 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 empty result with error = UNINITIALIZED. + */ + simdjson_inline simdjson_result_base() noexcept; + + /** + * Create a new error result. + */ + simdjson_inline simdjson_result_base(error_code error) noexcept; + + /** + * Create a new successful result. + */ + simdjson_inline simdjson_result_base(T &&value) noexcept; + + /** + * Create a new result with both things (use if you don't want to branch when creating the result). + */ + simdjson_inline simdjson_result_base(T &&value, error_code error) noexcept; + + /** + * Move the value and the error to the provided variables. + * + * @param value The variable to assign the value to. May not be set if there is an error. + * @param error The variable to assign the error to. Set to SUCCESS if there is no error. + */ + simdjson_inline void tie(T &value, error_code &error) && noexcept; + + /** + * Move the value to the provided variable. + * + * @param value The variable to assign the value to. May not be set if there is an error. + */ + simdjson_inline error_code get(T &value) && noexcept; + + /** + * The error. + */ + simdjson_inline error_code error() const noexcept; + +#if SIMDJSON_EXCEPTIONS + + /** + * Get the result value. + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T& value() & noexcept(false); + + /** + * Take the result value (move it). + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T&& value() && noexcept(false); + + /** + * Take the result value (move it). + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T&& take_value() && noexcept(false); + + /** + * Cast to the value (will throw on error). + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline operator T&&() && noexcept(false); +#endif // SIMDJSON_EXCEPTIONS + + /** + * Get the result value. This function is safe if and only + * the error() method returns a value that evaluates to false. + */ + simdjson_inline const T& value_unsafe() const& noexcept; + + /** + * Take the result value (move it). This function is safe if and only + * the error() method returns a value that evaluates to false. + */ + simdjson_inline T&& value_unsafe() && noexcept; + +}; // struct simdjson_result_base + +} // namespace internal + +/** + * The result of a simdjson operation that could fail. + * + * Gives the option of reading error codes, or throwing an exception by casting to the desired result. + */ +template +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 error result. + */ + simdjson_inline simdjson_result(T &&value) noexcept; + /** + * @private Create a new successful 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; + + /** + * The error. + */ + simdjson_inline error_code error() const noexcept; + +#if SIMDJSON_EXCEPTIONS + + /** + * Get the result value. + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T& value() & noexcept(false); + + /** + * Take the result value (move it). + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T&& value() && noexcept(false); + + /** + * Take the result value (move it). + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T&& take_value() && noexcept(false); + + /** + * Cast to the value (will throw on error). + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline operator T&&() && noexcept(false); +#endif // SIMDJSON_EXCEPTIONS + + /** + * Get the result value. This function is safe if and only + * the error() method returns a value that evaluates to false. + */ + simdjson_inline const T& value_unsafe() const& noexcept; + + /** + * Take the result value (move it). This function is safe if and only + * the error() method returns a value that evaluates to false. + */ + simdjson_inline T&& value_unsafe() && noexcept; + +}; // struct simdjson_result + +#if SIMDJSON_EXCEPTIONS + +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 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" */ + +/** + * @brief The top level simdjson namespace, containing everything the library provides. + */ +namespace simdjson { + +SIMDJSON_PUSH_DISABLE_UNUSED_WARNINGS + +/** The maximum document size supported by simdjson. */ +constexpr size_t SIMDJSON_MAXSIZE_BYTES = 0xFFFFFFFF; + +/** + * The amount of padding needed in a buffer to parse JSON. + * + * The input buf should be readable up to buf + SIMDJSON_PADDING + * this is a stopgap; there should be a better description of the + * main loop and its behavior that abstracts over this + * See https://github.com/simdjson/simdjson/issues/174 + */ +constexpr size_t SIMDJSON_PADDING = 64; + +/** + * By default, simdjson supports this many nested objects and arrays. + * + * This is the default for parser::max_depth(). + */ +constexpr size_t DEFAULT_MAX_DEPTH = 1024; + +SIMDJSON_POP_DISABLE_UNUSED_WARNINGS + +class implementation; +struct padded_string; +class padded_string_view; +enum class stage1_mode; + +namespace internal { + +template +class atomic_ptr; +class dom_parser_implementation; +class escape_json_string; +class tape_ref; +struct value128; +enum class tape_type; + +} // namespace internal +} // namespace simdjson + +#endif // SIMDJSON_BASE_H +/* end file simdjson/base.h */ + +#endif // SIMDJSON_SRC_BASE_H +/* end file base.h */ + +SIMDJSON_PUSH_DISABLE_UNUSED_WARNINGS + +/* including to_chars.cpp: #include */ +/* begin file to_chars.cpp */ +#ifndef SIMDJSON_SRC_TO_CHARS_CPP +#define SIMDJSON_SRC_TO_CHARS_CPP + +/* skipped duplicate #include */ + +#include +#include +#include +#include + +namespace simdjson { +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 { + +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; +} + +struct diyfp // f * 2^e +{ + static constexpr int kPrecision = 64; // = q + + std::uint64_t f = 0; + int e = 0; + + 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 { + + 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"); + + // 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 + + 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_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; + + // 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 + + const std::uint64_t h = p3 + p2_hi + p1_hi + (Q >> 32u); + + 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 { + + while ((x.f >> 63u) == 0) { + x.f <<= 1u; + x.e--; + } + + 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; + + return {x.f << delta, target_exponent}; + } +}; + +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) { + + // 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"); + + 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; + + 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); + + // 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) + + // 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); + + 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 + +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; +}; + +/*! +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. + + 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}, + }}; + + // 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)) / + kCachedPowersDecStep; + + const cached_power cached = kCachedPowers[static_cast(index)]; + + 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; + } +} + +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. + + // 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; + } +} + +/*! +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+]. + + 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 + + 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 + + // 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); + + // 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 + // + + // 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; + + // 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; + } + + 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 + + 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; + } + } + + // V = buffer * 10^-m, with M- <= V <= 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); + + // 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) { + + // --------(-----------------------+-----------------------)-------- (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 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); + + // ----(---+---)---------------(---+---)---------------(---+---)---- + // 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 + + 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"); + + // 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)); +#else + const boundaries w = compute_boundaries(value); +#endif + + 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) { + + if (e < 0) { + e = -e; + *buf++ = '-'; + } else { + *buf++ = '+'; + } + + 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); + } + + 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) { + + 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. + + 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; + } + + 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 + + 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 + + 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); + } + + *buf++ = 'e'; + return append_exponent(buf, n - 1); +} + +} // 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++ = '-'; + } + + 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 +} // namespace simdjson + +#endif // SIMDJSON_SRC_TO_CHARS_CPP +/* end file to_chars.cpp */ +/* including from_chars.cpp: #include */ +/* begin file from_chars.cpp */ +#ifndef SIMDJSON_SRC_FROM_CHARS_CPP +#define SIMDJSON_SRC_FROM_CHARS_CPP + +/* skipped duplicate #include */ + +#include +#include +#include + +namespace simdjson { +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. + **/ + +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 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 <> 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::sign_index() { return 63; } + +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; + } + + 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; + } + + 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 { + +// 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; +} + +} // 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; +} + +// 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); +} + +// 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; + + 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; + } + + 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); + + 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, 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, 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 +} // simdjson + +#endif // SIMDJSON_SRC_FROM_CHARS_CPP +/* end file from_chars.cpp */ +/* including internal/error_tables.cpp: #include */ +/* begin file internal/error_tables.cpp */ +#ifndef SIMDJSON_SRC_ERROR_TABLES_CPP +#define SIMDJSON_SRC_ERROR_TABLES_CPP + +/* including simdjson/internal/jsoncharutils_tables.h: #include */ +/* begin file simdjson/internal/jsoncharutils_tables.h */ +#ifndef SIMDJSON_INTERNAL_JSONCHARUTILS_TABLES_H +#define SIMDJSON_INTERNAL_JSONCHARUTILS_TABLES_H + +/* skipped duplicate #include "simdjson/base.h" */ + +#ifdef JSON_TEST_STRINGS +void found_string(const uint8_t *buf, const uint8_t *parsed_begin, + const uint8_t *parsed_end); +void found_bad_string(const uint8_t *buf); +#endif + +namespace simdjson { +namespace internal { +// structural chars here are +// they are { 0x7b } 0x7d : 0x3a [ 0x5b ] 0x5d , 0x2c (and NULL) +// we are also interested in the four whitespace characters +// space 0x20, linefeed 0x0a, horizontal tab 0x09 and carriage return 0x0d + +extern SIMDJSON_DLLIMPORTEXPORT const bool structural_or_whitespace_negated[256]; +extern SIMDJSON_DLLIMPORTEXPORT const bool structural_or_whitespace[256]; +extern SIMDJSON_DLLIMPORTEXPORT const uint32_t digit_to_val32[886]; + +} // namespace internal +} // namespace simdjson + +#endif // SIMDJSON_INTERNAL_JSONCHARUTILS_TABLES_H +/* end file simdjson/internal/jsoncharutils_tables.h */ +/* including simdjson/error-inl.h: #include */ +/* begin file simdjson/error-inl.h */ +#ifndef SIMDJSON_ERROR_INL_H +#define SIMDJSON_ERROR_INL_H + +/* skipped duplicate #include "simdjson/error.h" */ + +#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 const char *error_message(error_code error) noexcept { + // If you're using error_code, we're trusting you got it from the enum. + return internal::error_codes[int(error)].message; +} + +// deprecated function +#ifndef SIMDJSON_DISABLE_DEPRECATED_API +inline const std::string error_message(int error) noexcept { + if (error < 0 || error >= error_code::NUM_ERROR_CODES) { + return internal::error_codes[UNEXPECTED_ERROR].message; + } + return internal::error_codes[error].message; +} +#endif // SIMDJSON_DISABLE_DEPRECATED_API + +inline std::ostream& operator<<(std::ostream& out, error_code error) noexcept { + return out << error_message(error); +} + +namespace internal { + +// +// internal::simdjson_result_base 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_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; +} + +#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::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 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) {} + +} // namespace internal + +/// +/// simdjson_result inline implementation +/// + +template +simdjson_inline void simdjson_result::tie(T &value, error_code &error) && noexcept { + std::forward>(*this).tie(value, 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 std::forward>(*this).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(); +} + +#endif // SIMDJSON_EXCEPTIONS + +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 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 + +#endif // SIMDJSON_ERROR_INL_H +/* end file simdjson/error-inl.h */ + +namespace simdjson { +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" }, + { 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[] + +} // namespace internal +} // namespace simdjson + +#endif // SIMDJSON_SRC_ERROR_TABLES_CPP +/* end file internal/error_tables.cpp */ +/* including internal/jsoncharutils_tables.cpp: #include */ +/* begin file internal/jsoncharutils_tables.cpp */ +#ifndef SIMDJSON_SRC_JSONCHARUTILS_TABLES_CPP +#define SIMDJSON_SRC_JSONCHARUTILS_TABLES_CPP + +/* skipped duplicate #include */ + +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 + +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, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 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 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 simdjson + +#endif // SIMDJSON_SRC_JSONCHARUTILS_TABLES_CPP +/* end file internal/jsoncharutils_tables.cpp */ +/* including internal/numberparsing_tables.cpp: #include */ +/* begin file internal/numberparsing_tables.cpp */ +#ifndef SIMDJSON_SRC_NUMBERPARSING_TABLES_CPP +#define SIMDJSON_SRC_NUMBERPARSING_TABLES_CPP + +/* skipped duplicate #include */ +/* including simdjson/internal/numberparsing_tables.h: #include */ +/* begin file simdjson/internal/numberparsing_tables.h */ +#ifndef SIMDJSON_INTERNAL_NUMBERPARSING_TABLES_H +#define SIMDJSON_INTERNAL_NUMBERPARSING_TABLES_H + +/* skipped duplicate #include "simdjson/base.h" */ + +namespace simdjson { +namespace internal { +/** + * The smallest non-zero float (binary64) is 2^-1074. + * We take as input numbers of the form w x 10^q where w < 2^64. + * We have that w * 10^-343 < 2^(64-344) 5^-343 < 2^-1076. + * However, we have that + * (2^64-1) * 10^-342 = (2^64-1) * 2^-342 * 5^-342 > 2^-1074. + * Thus it is possible for a number of the form w * 10^-342 where + * w is a 64-bit value to be a non-zero floating-point number. + ********* + * Any number of form w * 10^309 where w>= 1 is going to be + * infinite in binary64 so we never need to worry about powers + * of 5 greater than 308. + */ +constexpr int smallest_power = -342; +constexpr int largest_power = 308; + +/** + * Represents a 128-bit value. + * low: least significant 64 bits. + * high: most significant 64 bits. + */ +struct value128 { + uint64_t low; + uint64_t high; +}; + + +// Precomputed powers of ten from 10^0 to 10^22. These +// can be represented exactly using the double type. +extern SIMDJSON_DLLIMPORTEXPORT const double power_of_ten[]; + + +/** + * When mapping numbers from decimal to binary, + * we go from w * 10^q to m * 2^p but we have + * 10^q = 5^q * 2^q, so effectively + * we are trying to match + * w * 2^q * 5^q to m * 2^p. Thus the powers of two + * are not a concern since they can be represented + * exactly using the binary notation, only the powers of five + * affect the binary significand. + */ + + +// The truncated powers of five from 5^-342 all the way to 5^308 +// The mantissa is truncated to 128 bits, and +// never rounded up. Uses about 10KB. +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}; + +/** + * 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, @@ -2466,351 +5427,18294 @@ SIMDJSON_DLLIMPORTEXPORT const uint64_t power_of_five_128[]= { 0xe3d8f9e563a198e5,0x58180fddd97723a6, 0x8e679c2f5e44ff8f,0x570f09eaa7ea7648,}; -} // namespace internal -} // namespace simdjson -/* end file src/internal/numberparsing_tables.cpp */ -/* begin file src/internal/simdprune_tables.cpp */ -#if SIMDJSON_IMPLEMENTATION_ARM64 || SIMDJSON_IMPLEMENTATION_ICELAKE || SIMDJSON_IMPLEMENTATION_HASWELL || SIMDJSON_IMPLEMENTATION_WESTMERE || SIMDJSON_IMPLEMENTATION_PPC64 +#endif // SIMDJSON_SRC_NUMBERPARSING_TABLES_CPP +/* end file internal/numberparsing_tables.cpp */ +/* including internal/simdprune_tables.cpp: #include */ +/* begin file internal/simdprune_tables.cpp */ +#ifndef SIMDJSON_SRC_SIMDPRUNE_TABLES_CPP +#define SIMDJSON_SRC_SIMDPRUNE_TABLES_CPP + +/* including simdjson/implementation_detection.h: #include */ +/* begin file simdjson/implementation_detection.h */ +#ifndef SIMDJSON_IMPLEMENTATION_DETECTION_H +#define SIMDJSON_IMPLEMENTATION_DETECTION_H + +/* skipped duplicate #include "simdjson/base.h" */ + +// 0 is reserved, because undefined SIMDJSON_IMPLEMENTATION equals 0 in preprocessor macros. +#define SIMDJSON_IMPLEMENTATION_ID_arm64 1 +#define SIMDJSON_IMPLEMENTATION_ID_fallback 2 +#define SIMDJSON_IMPLEMENTATION_ID_haswell 3 +#define SIMDJSON_IMPLEMENTATION_ID_icelake 4 +#define SIMDJSON_IMPLEMENTATION_ID_ppc64 5 +#define SIMDJSON_IMPLEMENTATION_ID_westmere 6 + +#define SIMDJSON_IMPLEMENTATION_ID_FOR(IMPL) SIMDJSON_CAT(SIMDJSON_IMPLEMENTATION_ID_, IMPL) +#define SIMDJSON_IMPLEMENTATION_ID SIMDJSON_IMPLEMENTATION_ID_FOR(SIMDJSON_IMPLEMENTATION) + +#define SIMDJSON_IMPLEMENTATION_IS(IMPL) SIMDJSON_IMPLEMENTATION_ID == SIMDJSON_IMPLEMENTATION_ID_FOR(IMPL) + +// +// First, figure out which implementations can be run. Doing it here makes it so we don't have to worry about the order +// in which we include them. +// + +#ifndef SIMDJSON_IMPLEMENTATION_ARM64 +#define SIMDJSON_IMPLEMENTATION_ARM64 (SIMDJSON_IS_ARM64) +#endif +#define SIMDJSON_CAN_ALWAYS_RUN_ARM64 SIMDJSON_IMPLEMENTATION_ARM64 && SIMDJSON_IS_ARM64 + +// Default Icelake to on if this is x86-64. Even if we're not compiled for it, it could be selected +// at runtime. +#ifndef SIMDJSON_IMPLEMENTATION_ICELAKE +#define SIMDJSON_IMPLEMENTATION_ICELAKE ((SIMDJSON_IS_X86_64) && (SIMDJSON_AVX512_ALLOWED) && (SIMDJSON_COMPILER_SUPPORTS_VBMI2)) +#endif + +#ifdef _MSC_VER +// To see why (__BMI__) && (__PCLMUL__) && (__LZCNT__) are not part of this next line, see +// https://github.com/simdjson/simdjson/issues/1247 +#define SIMDJSON_CAN_ALWAYS_RUN_ICELAKE ((SIMDJSON_IMPLEMENTATION_ICELAKE) && (__AVX2__) && (__AVX512F__) && (__AVX512DQ__) && (__AVX512CD__) && (__AVX512BW__) && (__AVX512VL__) && (__AVX512VBMI2__)) +#else +#define SIMDJSON_CAN_ALWAYS_RUN_ICELAKE ((SIMDJSON_IMPLEMENTATION_ICELAKE) && (__AVX2__) && (__BMI__) && (__PCLMUL__) && (__LZCNT__) && (__AVX512F__) && (__AVX512DQ__) && (__AVX512CD__) && (__AVX512BW__) && (__AVX512VL__) && (__AVX512VBMI2__)) +#endif + +// Default Haswell to on if this is x86-64. Even if we're not compiled for it, it could be selected +// at runtime. +#ifndef SIMDJSON_IMPLEMENTATION_HASWELL +#if SIMDJSON_CAN_ALWAYS_RUN_ICELAKE +// if icelake is always available, never enable haswell. +#define SIMDJSON_IMPLEMENTATION_HASWELL 0 +#else +#define SIMDJSON_IMPLEMENTATION_HASWELL SIMDJSON_IS_X86_64 +#endif +#endif +#ifdef _MSC_VER +// To see why (__BMI__) && (__PCLMUL__) && (__LZCNT__) are not part of this next line, see +// https://github.com/simdjson/simdjson/issues/1247 +#define SIMDJSON_CAN_ALWAYS_RUN_HASWELL ((SIMDJSON_IMPLEMENTATION_HASWELL) && (SIMDJSON_IS_X86_64) && (__AVX2__)) +#else +#define SIMDJSON_CAN_ALWAYS_RUN_HASWELL ((SIMDJSON_IMPLEMENTATION_HASWELL) && (SIMDJSON_IS_X86_64) && (__AVX2__) && (__BMI__) && (__PCLMUL__) && (__LZCNT__)) +#endif + +// Default Westmere to on if this is x86-64. +#ifndef SIMDJSON_IMPLEMENTATION_WESTMERE +#if SIMDJSON_CAN_ALWAYS_RUN_ICELAKE || SIMDJSON_CAN_ALWAYS_RUN_HASWELL +// if icelake or haswell are always available, never enable westmere. +#define SIMDJSON_IMPLEMENTATION_WESTMERE 0 +#else +#define SIMDJSON_IMPLEMENTATION_WESTMERE SIMDJSON_IS_X86_64 +#endif +#endif +#define SIMDJSON_CAN_ALWAYS_RUN_WESTMERE (SIMDJSON_IMPLEMENTATION_WESTMERE && SIMDJSON_IS_X86_64 && __SSE4_2__ && __PCLMUL__) + +#ifndef SIMDJSON_IMPLEMENTATION_PPC64 +#define SIMDJSON_IMPLEMENTATION_PPC64 (SIMDJSON_IS_PPC64 && SIMDJSON_IS_PPC64_VMX) +#endif +#define SIMDJSON_CAN_ALWAYS_RUN_PPC64 SIMDJSON_IMPLEMENTATION_PPC64 && SIMDJSON_IS_PPC64 && SIMDJSON_IS_PPC64_VMX + +// 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 +// if anything at all except fallback can always run, then disable fallback. +#define SIMDJSON_IMPLEMENTATION_FALLBACK 0 +#else +#define SIMDJSON_IMPLEMENTATION_FALLBACK 1 +#endif +#endif +#define SIMDJSON_CAN_ALWAYS_RUN_FALLBACK SIMDJSON_IMPLEMENTATION_FALLBACK + +// Determine the best builtin implementation +#ifndef SIMDJSON_BUILTIN_IMPLEMENTATION + +#if SIMDJSON_CAN_ALWAYS_RUN_ICELAKE +#define SIMDJSON_BUILTIN_IMPLEMENTATION icelake +#elif SIMDJSON_CAN_ALWAYS_RUN_HASWELL +#define SIMDJSON_BUILTIN_IMPLEMENTATION haswell +#elif SIMDJSON_CAN_ALWAYS_RUN_WESTMERE +#define SIMDJSON_BUILTIN_IMPLEMENTATION westmere +#elif SIMDJSON_CAN_ALWAYS_RUN_ARM64 +#define SIMDJSON_BUILTIN_IMPLEMENTATION arm64 +#elif SIMDJSON_CAN_ALWAYS_RUN_PPC64 +#define SIMDJSON_BUILTIN_IMPLEMENTATION ppc64 +#elif SIMDJSON_CAN_ALWAYS_RUN_FALLBACK +#define SIMDJSON_BUILTIN_IMPLEMENTATION fallback +#else +#error "All possible implementations (including fallback) have been disabled! simdjson will not run." +#endif + +#endif // SIMDJSON_BUILTIN_IMPLEMENTATION + +#define SIMDJSON_BUILTIN_IMPLEMENTATION_ID SIMDJSON_IMPLEMENTATION_ID_FOR(SIMDJSON_BUILTIN_IMPLEMENTATION) +#define SIMDJSON_BUILTIN_IMPLEMENTATION_IS(IMPL) SIMDJSON_BUILTIN_IMPLEMENTATION_ID == SIMDJSON_IMPLEMENTATION_ID_FOR(IMPL) + +#endif // SIMDJSON_IMPLEMENTATION_DETECTION_H +/* end file simdjson/implementation_detection.h */ + +#if SIMDJSON_IMPLEMENTATION_ARM64 || SIMDJSON_IMPLEMENTATION_ICELAKE || SIMDJSON_IMPLEMENTATION_HASWELL || SIMDJSON_IMPLEMENTATION_WESTMERE || SIMDJSON_IMPLEMENTATION_PPC64 + +#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}; + +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] + +} // namespace internal +} // namespace simdjson + +#endif // SIMDJSON_IMPLEMENTATION_ARM64 || SIMDJSON_IMPLEMENTATION_ICELAKE || SIMDJSON_IMPLEMENTATION_HASWELL || SIMDJSON_IMPLEMENTATION_WESTMERE || SIMDJSON_IMPLEMENTATION_PPC64 + +#endif // SIMDJSON_SRC_SIMDPRUNE_TABLES_CPP +/* end file internal/simdprune_tables.cpp */ + +/* including simdjson/generic/dependencies.h: #include */ +/* begin file simdjson/generic/dependencies.h */ +#ifdef SIMDJSON_CONDITIONAL_INCLUDE +#error simdjson/generic/dependencies.h must be included before defining SIMDJSON_CONDITIONAL_INCLUDE! +#endif + +#ifndef SIMDJSON_GENERIC_DEPENDENCIES_H +#define SIMDJSON_GENERIC_DEPENDENCIES_H + +// Internal headers needed for generics. +// All includes referencing simdjson headers *not* under simdjson/generic must be here! +// Otherwise, amalgamation will fail. +/* skipped duplicate #include "simdjson/base.h" */ +/* including simdjson/implementation.h: #include "simdjson/implementation.h" */ +/* begin file simdjson/implementation.h */ +#ifndef SIMDJSON_IMPLEMENTATION_H +#define SIMDJSON_IMPLEMENTATION_H + +/* including simdjson/internal/atomic_ptr.h: #include "simdjson/internal/atomic_ptr.h" */ +/* begin file simdjson/internal/atomic_ptr.h */ +#ifndef SIMDJSON_INTERNAL_ATOMIC_PTR_H +#define SIMDJSON_INTERNAL_ATOMIC_PTR_H + +/* skipped duplicate #include "simdjson/base.h" */ +#include + +namespace simdjson { +namespace internal { + +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 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; +}; + +} // namespace internal +} // namespace simdjson + +#endif // SIMDJSON_INTERNAL_ATOMIC_PTR_H +/* end file simdjson/internal/atomic_ptr.h */ +/* including simdjson/internal/dom_parser_implementation.h: #include "simdjson/internal/dom_parser_implementation.h" */ +/* begin file simdjson/internal/dom_parser_implementation.h */ +#ifndef SIMDJSON_INTERNAL_DOM_PARSER_IMPLEMENTATION_H +#define SIMDJSON_INTERNAL_DOM_PARSER_IMPLEMENTATION_H + +/* skipped duplicate #include "simdjson/base.h" */ +/* skipped duplicate #include "simdjson/error.h" */ +#include + +namespace simdjson { + +namespace dom { +class document; +} // namespace dom + +/** +* This enum is used with the dom_parser_implementation::stage1 function. +* 1) The regular mode expects a fully formed JSON document. +* 2) The streaming_partial mode expects a possibly truncated +* input within a stream on JSON documents. +* 3) The stream_final mode allows us to truncate final +* unterminated strings. It is useful in conjunction with streaming_partial. +*/ +enum class stage1_mode { regular, streaming_partial, streaming_final}; + +/** + * Returns true if mode == streaming_partial or mode == streaming_final + */ +inline bool is_streaming(stage1_mode mode) { + // performance note: it is probably faster to check that mode is different + // from regular than checking that it is either streaming_partial or streaming_final. + return (mode != stage1_mode::regular); + // return (mode == stage1_mode::streaming_partial || mode == stage1_mode::streaming_final); +} + + +namespace internal { + + +/** + * An implementation of simdjson's DOM parser for a particular CPU architecture. + * + * This class is expected to be accessed only by pointer, and never move in memory (though the + * pointer can move). + */ +class dom_parser_implementation { +public: + + /** + * @private For internal implementation use + * + * Run a full JSON parse on a single document (stage1 + stage2). + * + * Guaranteed only to be called when capacity > document length. + * + * Overridden by each implementation. + * + * @param buf The json document to parse. *MUST* be allocated up to len + SIMDJSON_PADDING bytes. + * @param len The length of the json document. + * @return The error code, or SUCCESS if there was no error. + */ + simdjson_warn_unused virtual error_code parse(const uint8_t *buf, size_t len, dom::document &doc) noexcept = 0; + + /** + * @private For internal implementation use + * + * Stage 1 of the document parser. + * + * Guaranteed only to be called when capacity > document length. + * + * Overridden by each implementation. + * + * @param buf The json document to parse. + * @param len The length of the json document. + * @param streaming Whether this is being called by parser::parse_many. + * @return The error code, or SUCCESS if there was no error. + */ + simdjson_warn_unused virtual error_code stage1(const uint8_t *buf, size_t len, stage1_mode streaming) noexcept = 0; + + /** + * @private For internal implementation use + * + * Stage 2 of the document parser. + * + * Called after stage1(). + * + * Overridden by each implementation. + * + * @param doc The document to output to. + * @return The error code, or SUCCESS if there was no error. + */ + simdjson_warn_unused virtual error_code stage2(dom::document &doc) noexcept = 0; + + /** + * @private For internal implementation use + * + * Stage 2 of the document parser for parser::parse_many. + * + * Guaranteed only to be called after stage1(). + * Overridden by each implementation. + * + * @param doc The document to output to. + * @return The error code, SUCCESS if there was no error, or EMPTY if all documents have been parsed. + */ + simdjson_warn_unused virtual error_code stage2_next(dom::document &doc) noexcept = 0; + + /** + * Unescape a valid UTF-8 string from src to dst, stopping at a final unescaped quote. There + * must be an unescaped quote terminating the string. It returns the final output + * position as pointer. In case of error (e.g., the string has bad escaped codes), + * then null_nullptrptr 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_nullptrptr is returned. It is assumed that the output buffer is large + * enough. E.g., if src points at 'joe"', then dst needs to have four free bytes + + * SIMDJSON_PADDING bytes. + * + * Overridden by each implementation. + * + * @param str pointer to the beginning of a possibly invalid UTF-8 JSON string, must end with an unescaped quote. + * @param dst pointer to a destination buffer, it must point a region in memory of sufficient size. + * @return end of the of the written region (exclusive) or nullptr in case of error. + */ + simdjson_warn_unused virtual uint8_t *parse_wobbly_string(const uint8_t *src, uint8_t *dst) const noexcept = 0; + + /** + * Change the capacity of this parser. + * + * The capacity can never exceed SIMDJSON_MAXSIZE_BYTES (e.g., 4 GB) + * and an CAPACITY error is returned if it is attempted. + * + * Generally used for reallocation. + * + * @param capacity The new capacity. + * @param max_depth The new max_depth. + * @return The error code, or SUCCESS if there was no error. + */ + virtual error_code set_capacity(size_t capacity) noexcept = 0; + + /** + * Change the max depth of this parser. + * + * Generally used for reallocation. + * + * @param capacity The new capacity. + * @param max_depth The new max_depth. + * @return The error code, or SUCCESS if there was no error. + */ + virtual error_code set_max_depth(size_t max_depth) noexcept = 0; + + /** + * Deallocate this parser. + */ + virtual ~dom_parser_implementation() = default; + + /** Number of structural indices passed from stage 1 to stage 2 */ + uint32_t n_structural_indexes{0}; + /** Structural indices passed from stage 1 to stage 2 */ + std::unique_ptr 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_inline size_t capacity() const noexcept; + + /** + * The maximum level of nested object and arrays supported by this parser. + * + * @return Maximum depth, in bytes. + */ + simdjson_inline size_t max_depth() const noexcept; + + /** + * Ensure this parser has enough memory to process JSON documents up to `capacity` bytes in length + * and `max_depth` depth. + * + * @param capacity The new capacity. + * @param max_depth The new max_depth. Defaults to DEFAULT_MAX_DEPTH. + * @return The error, if there is one. + */ + simdjson_warn_unused inline error_code allocate(size_t capacity, size_t max_depth) noexcept; + + +protected: + /** + * The maximum document length this parser supports. + * + * Buffers are large enough to handle any document up to this length. + */ + size_t _capacity{0}; + + /** + * The maximum depth (number of nested objects and arrays) supported by this parser. + * + * Defaults to DEFAULT_MAX_DEPTH. + */ + size_t _max_depth{0}; + + // Declaring these so that subclasses can use them to implement their constructors. + simdjson_inline dom_parser_implementation() noexcept; + simdjson_inline dom_parser_implementation(dom_parser_implementation &&other) noexcept; + simdjson_inline dom_parser_implementation &operator=(dom_parser_implementation &&other) noexcept; + + simdjson_inline dom_parser_implementation(const dom_parser_implementation &) noexcept = delete; + simdjson_inline dom_parser_implementation &operator=(const dom_parser_implementation &other) noexcept = delete; +}; // class dom_parser_implementation + +simdjson_inline dom_parser_implementation::dom_parser_implementation() noexcept = default; +simdjson_inline dom_parser_implementation::dom_parser_implementation(dom_parser_implementation &&other) noexcept = default; +simdjson_inline dom_parser_implementation &dom_parser_implementation::operator=(dom_parser_implementation &&other) noexcept = default; + +simdjson_inline size_t dom_parser_implementation::capacity() const noexcept { + return _capacity; +} + +simdjson_inline size_t dom_parser_implementation::max_depth() const noexcept { + return _max_depth; +} + +simdjson_warn_unused +inline error_code dom_parser_implementation::allocate(size_t capacity, size_t max_depth) noexcept { + if (this->max_depth() != max_depth) { + error_code err = set_max_depth(max_depth); + if (err) { return err; } + } + if (_capacity != capacity) { + error_code err = set_capacity(capacity); + if (err) { return err; } + } + return SUCCESS; +} + +} // namespace internal +} // namespace simdjson + +#endif // SIMDJSON_INTERNAL_DOM_PARSER_IMPLEMENTATION_H +/* end file simdjson/internal/dom_parser_implementation.h */ + +#include + +namespace simdjson { + +/** + * Validate the UTF-8 string. + * + * @param buf the string to validate. + * @param len the length of the string in bytes. + * @return true if the string is valid UTF-8. + */ +simdjson_warn_unused bool validate_utf8(const char * buf, size_t len) noexcept; +/** + * Validate the UTF-8 string. + * + * @param sv the string_view to validate. + * @return true if the string is valid UTF-8. + */ +simdjson_inline simdjson_warn_unused bool validate_utf8(const std::string_view sv) noexcept { + return validate_utf8(sv.data(), sv.size()); +} + +/** + * Validate the UTF-8 string. + * + * @param p the string to validate. + * @return true if the string is valid UTF-8. + */ +simdjson_inline simdjson_warn_unused bool validate_utf8(const std::string& s) noexcept { + return validate_utf8(s.data(), s.size()); +} + +/** + * An implementation of simdjson for a particular CPU architecture. + * + * Also used to maintain the currently active implementation. The active implementation is + * automatically initialized on first use to the most advanced implementation supported by the host. + */ +class implementation { +public: + + /** + * The name of this implementation. + * + * const implementation *impl = simdjson::get_active_implementation(); + * cout << "simdjson is optimized for " << impl->name() << "(" << impl->description() << ")" << endl; + * + * @return the name of the implementation, e.g. "haswell", "westmere", "arm64". + */ + virtual const std::string &name() const { return _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 const std::string &description() const { return _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) + { + } + virtual ~implementation()=default; + +private: + /** + * The name of this implementation. + */ + const std::string _name; + + /** + * The description of this implementation. + */ + const std::string _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(); + +} // namespace simdjson + +#endif // SIMDJSON_IMPLEMENTATION_H +/* end file simdjson/implementation.h */ +/* skipped duplicate #include "simdjson/implementation_detection.h" */ +/* including simdjson/internal/instruction_set.h: #include "simdjson/internal/instruction_set.h" */ +/* begin file simdjson/internal/instruction_set.h */ +/* From +https://github.com/endorno/pytorch/blob/master/torch/lib/TH/generic/simd/simd.h +Highly modified. + +Copyright (c) 2016- Facebook, Inc (Adam Paszke) +Copyright (c) 2014- Facebook, Inc (Soumith Chintala) +Copyright (c) 2011-2014 Idiap Research Institute (Ronan Collobert) +Copyright (c) 2012-2014 Deepmind Technologies (Koray Kavukcuoglu) +Copyright (c) 2011-2012 NEC Laboratories America (Koray Kavukcuoglu) +Copyright (c) 2011-2013 NYU (Clement Farabet) +Copyright (c) 2006-2010 NEC Laboratories America (Ronan Collobert, Leon Bottou, +Iain Melvin, Jason Weston) Copyright (c) 2006 Idiap Research Institute +(Samy Bengio) Copyright (c) 2001-2004 Idiap Research Institute (Ronan Collobert, +Samy Bengio, Johnny Mariethoz) + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the names of Facebook, Deepmind Technologies, NYU, NEC Laboratories +America and IDIAP Research Institute nor the names of its contributors may be + used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef SIMDJSON_INTERNAL_INSTRUCTION_SET_H +#define SIMDJSON_INTERNAL_INSTRUCTION_SET_H + +namespace simdjson { +namespace internal { + +enum instruction_set { + DEFAULT = 0x0, + NEON = 0x1, + AVX2 = 0x4, + SSE42 = 0x8, + PCLMULQDQ = 0x10, + BMI1 = 0x20, + BMI2 = 0x40, + ALTIVEC = 0x80, + AVX512F = 0x100, + AVX512DQ = 0x200, + AVX512IFMA = 0x400, + AVX512PF = 0x800, + AVX512ER = 0x1000, + AVX512CD = 0x2000, + AVX512BW = 0x4000, + AVX512VL = 0x8000, + AVX512VBMI2 = 0x10000 +}; + +} // namespace internal +} // namespace simdjson + +#endif // SIMDJSON_INTERNAL_INSTRUCTION_SET_H +/* end file simdjson/internal/instruction_set.h */ +/* skipped duplicate #include "simdjson/internal/dom_parser_implementation.h" */ +/* skipped duplicate #include "simdjson/internal/jsoncharutils_tables.h" */ +/* skipped duplicate #include "simdjson/internal/numberparsing_tables.h" */ +/* including simdjson/internal/simdprune_tables.h: #include "simdjson/internal/simdprune_tables.h" */ +/* begin file simdjson/internal/simdprune_tables.h */ +#ifndef SIMDJSON_INTERNAL_SIMDPRUNE_TABLES_H +#define SIMDJSON_INTERNAL_SIMDPRUNE_TABLES_H + +/* skipped duplicate #include "simdjson/base.h" */ + +#include + +namespace simdjson { // table modified and copied from +namespace internal { // http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetTable + +extern SIMDJSON_DLLIMPORTEXPORT const unsigned char BitsSetTable256mul2[256]; + +extern SIMDJSON_DLLIMPORTEXPORT const uint8_t pshufb_combine_table[272]; + +// 256 * 8 bytes = 2kB, easily fits in cache. +extern SIMDJSON_DLLIMPORTEXPORT const uint64_t thintable_epi8[256]; + +} // namespace internal +} // namespace simdjson + +#endif // SIMDJSON_INTERNAL_SIMDPRUNE_TABLES_H +/* end file simdjson/internal/simdprune_tables.h */ + +#endif // SIMDJSON_GENERIC_DEPENDENCIES_H +/* end file simdjson/generic/dependencies.h */ +/* including generic/dependencies.h: #include */ +/* begin file generic/dependencies.h */ +#ifdef SIMDJSON_CONDITIONAL_INCLUDE +#error generic/dependencies.h must be included before defining SIMDJSON_CONDITIONAL_INCLUDE! +#endif + +#ifndef SIMDJSON_SRC_GENERIC_DEPENDENCIES_H +#define SIMDJSON_SRC_GENERIC_DEPENDENCIES_H + +/* skipped duplicate #include */ + +#endif // SIMDJSON_SRC_GENERIC_DEPENDENCIES_H +/* end file generic/dependencies.h */ +/* including generic/stage1/dependencies.h: #include */ +/* begin file generic/stage1/dependencies.h */ +#ifndef SIMDJSON_SRC_GENERIC_STAGE1_DEPENDENCIES_H +#define SIMDJSON_SRC_GENERIC_STAGE1_DEPENDENCIES_H + +#endif // SIMDJSON_SRC_GENERIC_STAGE1_DEPENDENCIES_H +/* end file generic/stage1/dependencies.h */ +/* including generic/stage2/dependencies.h: #include */ +/* begin file generic/stage2/dependencies.h */ +#ifndef SIMDJSON_SRC_GENERIC_STAGE2_DEPENDENCIES_H +#define SIMDJSON_SRC_GENERIC_STAGE2_DEPENDENCIES_H + +/* including simdjson/dom/document.h: #include */ +/* begin file simdjson/dom/document.h */ +#ifndef SIMDJSON_DOM_DOCUMENT_H +#define SIMDJSON_DOM_DOCUMENT_H + +/* including simdjson/dom/base.h: #include "simdjson/dom/base.h" */ +/* begin file simdjson/dom/base.h */ +#ifndef SIMDJSON_DOM_BASE_H +#define SIMDJSON_DOM_BASE_H + +/* skipped duplicate #include "simdjson/base.h" */ + +namespace simdjson { + +/** + * @brief A DOM API on top of the simdjson parser. + */ +namespace dom { + +/** The default batch size for parser.parse_many() and parser.load_many() */ +static constexpr size_t DEFAULT_BATCH_SIZE = 1000000; +/** + * Some adversary might try to set the batch size to 0 or 1, which might cause problems. + * We set a minimum of 32B since anything else is highly likely to be an error. In practice, + * most users will want a much larger batch size. + * + * All non-negative MINIMAL_BATCH_SIZE values should be 'safe' except that, obviously, no JSON + * document can ever span 0 or 1 byte and that very large values would create memory allocation issues. + */ +static constexpr size_t MINIMAL_BATCH_SIZE = 32; + +/** + * It is wasteful to allocate memory for tiny documents (e.g., 4 bytes). + */ +static constexpr size_t MINIMAL_DOCUMENT_CAPACITY = 32; + +class array; +class document; +class document_stream; +class element; +class key_value_pair; +class object; +class parser; + +#ifdef SIMDJSON_THREADS_ENABLED +struct stage1_worker; +#endif // SIMDJSON_THREADS_ENABLED + +} // namespace dom + +namespace internal { + +template +class string_builder; +class tape_ref; + +} // namespace internal + +} // namespace simdjson + +#endif // SIMDJSON_DOM_BASE_H +/* end file simdjson/dom/base.h */ + +#include + +namespace simdjson { +namespace dom { + +/** + * A parsed JSON document. + * + * This class cannot be copied, only moved, to avoid unintended allocations. + */ +class document { +public: + /** + * Create a document container with zero capacity. + * + * The parser will allocate capacity as needed. + */ + document() noexcept = default; + ~document() noexcept = default; + + /** + * Take another document's buffers. + * + * @param other The document to take. Its capacity is zeroed and it is invalidated. + */ + document(document &&other) noexcept = default; + /** @private */ + document(const document &) = delete; // Disallow copying + /** + * Take another document's buffers. + * + * @param other The document to take. Its capacity is zeroed. + */ + document &operator=(document &&other) noexcept = default; + /** @private */ + document &operator=(const document &) = delete; // Disallow copying + + /** + * Get the root element of this document as a JSON array. + */ + element root() const noexcept; + + /** + * @private Dump the raw tape for debugging. + * + * @param os the stream to output to. + * @return false if the tape is likely wrong (e.g., you did not parse a valid JSON). + */ + bool dump_raw_tape(std::ostream &os) const noexcept; + + /** @private Structural values. */ + std::unique_ptr 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: + size_t allocated_capacity{0}; + friend class parser; +}; // class document + +} // namespace dom +} // namespace simdjson + +#endif // SIMDJSON_DOM_DOCUMENT_H +/* end file simdjson/dom/document.h */ +/* including simdjson/internal/tape_type.h: #include */ +/* begin file simdjson/internal/tape_type.h */ +#ifndef SIMDJSON_INTERNAL_TAPE_TYPE_H +#define SIMDJSON_INTERNAL_TAPE_TYPE_H + +namespace simdjson { +namespace internal { + +/** + * The possible types in the tape. + */ +enum class tape_type { + ROOT = 'r', + START_ARRAY = '[', + START_OBJECT = '{', + END_ARRAY = ']', + END_OBJECT = '}', + STRING = '"', + INT64 = 'l', + UINT64 = 'u', + DOUBLE = 'd', + TRUE_VALUE = 't', + FALSE_VALUE = 'f', + NULL_VALUE = 'n' +}; // enum class tape_type + +} // namespace internal +} // namespace simdjson + +#endif // SIMDJSON_INTERNAL_TAPE_TYPE_H +/* end file simdjson/internal/tape_type.h */ + +#endif // SIMDJSON_SRC_GENERIC_STAGE2_DEPENDENCIES_H +/* end file generic/stage2/dependencies.h */ + +/* including implementation.cpp: #include */ +/* begin file implementation.cpp */ +#ifndef SIMDJSON_SRC_IMPLEMENTATION_CPP +#define SIMDJSON_SRC_IMPLEMENTATION_CPP + +/* skipped duplicate #include */ +/* skipped duplicate #include */ +/* skipped duplicate #include */ +/* including internal/isadetection.h: #include */ +/* begin file internal/isadetection.h */ +/* From +https://github.com/endorno/pytorch/blob/master/torch/lib/TH/generic/simd/simd.h +Highly modified. + +Copyright (c) 2016- Facebook, Inc (Adam Paszke) +Copyright (c) 2014- Facebook, Inc (Soumith Chintala) +Copyright (c) 2011-2014 Idiap Research Institute (Ronan Collobert) +Copyright (c) 2012-2014 Deepmind Technologies (Koray Kavukcuoglu) +Copyright (c) 2011-2012 NEC Laboratories America (Koray Kavukcuoglu) +Copyright (c) 2011-2013 NYU (Clement Farabet) +Copyright (c) 2006-2010 NEC Laboratories America (Ronan Collobert, Leon Bottou, +Iain Melvin, Jason Weston) Copyright (c) 2006 Idiap Research Institute +(Samy Bengio) Copyright (c) 2001-2004 Idiap Research Institute (Ronan Collobert, +Samy Bengio, Johnny Mariethoz) + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the names of Facebook, Deepmind Technologies, NYU, NEC Laboratories +America and IDIAP Research Institute nor the names of its contributors may be + used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef SIMDJSON_INTERNAL_ISADETECTION_H +#define SIMDJSON_INTERNAL_ISADETECTION_H + +/* skipped duplicate #include "simdjson/internal/instruction_set.h" */ + +#include +#include +#if defined(_MSC_VER) +#include +#elif defined(HAVE_GCC_GET_CPUID) && defined(USE_GCC_GET_CPUID) +#include +#endif + +namespace simdjson { +namespace internal { + +#if defined(__PPC64__) + +static inline uint32_t detect_supported_architectures() { + return instruction_set::ALTIVEC; +} + +#elif defined(__aarch64__) || defined(_M_ARM64) + +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 +} + + + +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]; +#elif defined(HAVE_GCC_GET_CPUID) && defined(USE_GCC_GET_CPUID) + 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; +#endif +} + + +static inline uint64_t xgetbv() { +#if defined(_MSC_VER) + 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); +#endif +} + +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); + + 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_osxsave) != cpuid_osxsave) { + return host_isa; + } + + // xgetbv for checking if the OS saves registers + uint64_t xcr0 = xgetbv(); + + 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; + } + + if (ebx & cpuid_bmi2_bit) { + host_isa |= instruction_set::BMI2; + } + + if (!((xcr0 & cpuid_avx512_saved) == cpuid_avx512_saved)) { + return host_isa; + } + + if (ebx & cpuid_avx512f_bit) { + host_isa |= instruction_set::AVX512F; + } + + if (ebx & cpuid_avx512dq_bit) { + host_isa |= instruction_set::AVX512DQ; + } + + if (ebx & cpuid_avx512ifma_bit) { + host_isa |= instruction_set::AVX512IFMA; + } + + if (ebx & cpuid_avx512pf_bit) { + host_isa |= instruction_set::AVX512PF; + } + + if (ebx & cpuid_avx512er_bit) { + host_isa |= instruction_set::AVX512ER; + } + + if (ebx & cpuid_avx512cd_bit) { + host_isa |= instruction_set::AVX512CD; + } + + if (ebx & cpuid_avx512bw_bit) { + host_isa |= instruction_set::AVX512BW; + } + + if (ebx & cpuid_avx512vl_bit) { + host_isa |= instruction_set::AVX512VL; + } + + if (ecx & cpuid_avx512vbmi2_bit) { + host_isa |= instruction_set::AVX512VBMI2; + } + + return host_isa; +} +#else // fallback + + +static inline uint32_t detect_supported_architectures() { + return instruction_set::DEFAULT; +} + + +#endif // end SIMD extension detection code + +} // namespace internal +} // namespace simdjson + +#endif // SIMDJSON_INTERNAL_ISADETECTION_H +/* end file internal/isadetection.h */ + +#include + +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); +} + +} // namespace simdjson + +/* defining SIMDJSON_CONDITIONAL_INCLUDE */ +#define SIMDJSON_CONDITIONAL_INCLUDE + +#if SIMDJSON_IMPLEMENTATION_ARM64 +/* including simdjson/arm64/implementation.h: #include */ +/* begin file simdjson/arm64/implementation.h */ +#ifndef SIMDJSON_ARM64_IMPLEMENTATION_H +#define SIMDJSON_ARM64_IMPLEMENTATION_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #include "simdjson/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/implementation.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/internal/instruction_set.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { +namespace arm64 { + +/** + * @private + */ +class implementation final : public simdjson::implementation { +public: + simdjson_inline implementation() : simdjson::implementation("arm64", "ARM NEON", internal::instruction_set::NEON) {} + simdjson_warn_unused error_code create_dom_parser_implementation( + size_t capacity, + size_t max_length, + std::unique_ptr& dst + ) const noexcept final; + simdjson_warn_unused error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept final; + simdjson_warn_unused bool validate_utf8(const char *buf, size_t len) const noexcept final; +}; + +} // namespace arm64 +} // namespace simdjson + +#endif // SIMDJSON_ARM64_IMPLEMENTATION_H +/* end file simdjson/arm64/implementation.h */ +namespace simdjson { +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 + +#if SIMDJSON_IMPLEMENTATION_FALLBACK +/* including simdjson/fallback/implementation.h: #include */ +/* begin file simdjson/fallback/implementation.h */ +#ifndef SIMDJSON_FALLBACK_IMPLEMENTATION_H +#define SIMDJSON_FALLBACK_IMPLEMENTATION_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #include "simdjson/fallback/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/implementation.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { +namespace fallback { + +/** + * @private + */ +class implementation final : public simdjson::implementation { +public: + simdjson_inline implementation() : simdjson::implementation( + "fallback", + "Generic fallback implementation", + 0 + ) {} + simdjson_warn_unused error_code create_dom_parser_implementation( + size_t capacity, + size_t max_length, + std::unique_ptr& dst + ) const noexcept final; + simdjson_warn_unused error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept final; + simdjson_warn_unused bool validate_utf8(const char *buf, size_t len) const noexcept final; +}; + +} // namespace fallback +} // namespace simdjson + +#endif // SIMDJSON_FALLBACK_IMPLEMENTATION_H +/* end file simdjson/fallback/implementation.h */ +namespace simdjson { +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 + + +#if SIMDJSON_IMPLEMENTATION_HASWELL +/* including simdjson/haswell/implementation.h: #include */ +/* begin file simdjson/haswell/implementation.h */ +#ifndef SIMDJSON_HASWELL_IMPLEMENTATION_H +#define SIMDJSON_HASWELL_IMPLEMENTATION_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #include "simdjson/haswell/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/implementation.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/internal/instruction_set.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +// The constructor may be executed on any host, so we take care not to use SIMDJSON_TARGET_HASWELL +namespace simdjson { +namespace haswell { + +/** + * @private + */ +class implementation final : public simdjson::implementation { +public: + simdjson_inline implementation() : simdjson::implementation( + "haswell", + "Intel/AMD AVX2", + internal::instruction_set::AVX2 | internal::instruction_set::PCLMULQDQ | internal::instruction_set::BMI1 | internal::instruction_set::BMI2 + ) {} + simdjson_warn_unused error_code create_dom_parser_implementation( + size_t capacity, + size_t max_length, + std::unique_ptr& dst + ) const noexcept final; + simdjson_warn_unused error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept final; + simdjson_warn_unused bool validate_utf8(const char *buf, size_t len) const noexcept final; +}; + +} // namespace haswell +} // namespace simdjson + +#endif // SIMDJSON_HASWELL_IMPLEMENTATION_H +/* end file simdjson/haswell/implementation.h */ +namespace simdjson { +namespace internal { +static const haswell::implementation* get_haswell_singleton() { + static const haswell::implementation haswell_singleton{}; + return &haswell_singleton; +} +} // namespace internal +} // namespace simdjson +#endif + +#if SIMDJSON_IMPLEMENTATION_ICELAKE +/* including simdjson/icelake/implementation.h: #include */ +/* begin file simdjson/icelake/implementation.h */ +#ifndef SIMDJSON_ICELAKE_IMPLEMENTATION_H +#define SIMDJSON_ICELAKE_IMPLEMENTATION_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #include "simdjson/icelake/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/implementation.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/internal/instruction_set.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +// The constructor may be executed on any host, so we take care not to use SIMDJSON_TARGET_ICELAKE +namespace simdjson { +namespace icelake { + +/** + * @private + */ +class implementation final : public simdjson::implementation { +public: + simdjson_inline implementation() : simdjson::implementation( + "icelake", + "Intel/AMD AVX512", + internal::instruction_set::AVX2 | internal::instruction_set::PCLMULQDQ | internal::instruction_set::BMI1 | internal::instruction_set::BMI2 | internal::instruction_set::AVX512F | internal::instruction_set::AVX512DQ | internal::instruction_set::AVX512CD | internal::instruction_set::AVX512BW | internal::instruction_set::AVX512VL | internal::instruction_set::AVX512VBMI2 + ) {} + simdjson_warn_unused error_code create_dom_parser_implementation( + size_t capacity, + size_t max_length, + std::unique_ptr& dst + ) const noexcept final; + simdjson_warn_unused error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept final; + simdjson_warn_unused bool validate_utf8(const char *buf, size_t len) const noexcept final; +}; + +} // namespace icelake +} // namespace simdjson + +#endif // SIMDJSON_ICELAKE_IMPLEMENTATION_H +/* end file simdjson/icelake/implementation.h */ +namespace simdjson { +namespace internal { +static const icelake::implementation* get_icelake_singleton() { + static const icelake::implementation icelake_singleton{}; + return &icelake_singleton; +} +} // namespace internal +} // namespace simdjson +#endif + +#if SIMDJSON_IMPLEMENTATION_PPC64 +/* including simdjson/ppc64/implementation.h: #include */ +/* begin file simdjson/ppc64/implementation.h */ +#ifndef SIMDJSON_PPC64_IMPLEMENTATION_H +#define SIMDJSON_PPC64_IMPLEMENTATION_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #include "simdjson/ppc64/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/implementation.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/internal/instruction_set.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { + +/** + * Implementation for ALTIVEC (PPC64). + */ +namespace ppc64 { + +/** + * @private + */ +class implementation final : public simdjson::implementation { +public: + simdjson_inline implementation() + : simdjson::implementation("ppc64", "PPC64 ALTIVEC", + internal::instruction_set::ALTIVEC) {} + + simdjson_warn_unused error_code create_dom_parser_implementation( + size_t capacity, size_t max_length, + std::unique_ptr &dst) + const noexcept final; + simdjson_warn_unused error_code minify(const uint8_t *buf, size_t len, + uint8_t *dst, + size_t &dst_len) const noexcept final; + simdjson_warn_unused bool validate_utf8(const char *buf, + size_t len) const noexcept final; +}; + +} // namespace ppc64 +} // namespace simdjson + +#endif // SIMDJSON_PPC64_IMPLEMENTATION_H +/* end file simdjson/ppc64/implementation.h */ +namespace simdjson { +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 + +#if SIMDJSON_IMPLEMENTATION_WESTMERE +/* including simdjson/westmere/implementation.h: #include */ +/* begin file simdjson/westmere/implementation.h */ +#ifndef SIMDJSON_WESTMERE_IMPLEMENTATION_H +#define SIMDJSON_WESTMERE_IMPLEMENTATION_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #include "simdjson/westmere/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/implementation.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/internal/instruction_set.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +// The constructor may be executed on any host, so we take care not to use SIMDJSON_TARGET_WESTMERE +namespace simdjson { +namespace westmere { + +/** + * @private + */ +class implementation final : public simdjson::implementation { +public: + simdjson_inline implementation() : simdjson::implementation("westmere", "Intel/AMD SSE4.2", internal::instruction_set::SSE42 | internal::instruction_set::PCLMULQDQ) {} + simdjson_warn_unused error_code create_dom_parser_implementation( + size_t capacity, + size_t max_length, + std::unique_ptr& dst + ) const noexcept final; + simdjson_warn_unused error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept final; + simdjson_warn_unused bool validate_utf8(const char *buf, size_t len) const noexcept final; +}; + +} // namespace westmere +} // namespace simdjson + +#endif // SIMDJSON_WESTMERE_IMPLEMENTATION_H +/* end file simdjson/westmere/implementation.h */ +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 simdjson +#endif // SIMDJSON_IMPLEMENTATION_WESTMERE + +/* undefining SIMDJSON_CONDITIONAL_INCLUDE */ +#undef SIMDJSON_CONDITIONAL_INCLUDE + +namespace simdjson { +namespace internal { + +// 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: + const std::string &name() const noexcept final { return set_best()->name(); } + const 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 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_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) {} +}; + +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? +} + +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; +} + +SIMDJSON_DLLIMPORTEXPORT internal::atomic_ptr& get_active_implementation() { + static const internal::detect_best_supported_implementation_on_first_use detect_best_supported_implementation_on_first_use_singleton; + static internal::atomic_ptr active_implementation{&detect_best_supported_implementation_on_first_use_singleton}; + return active_implementation; +} + +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 + +#endif // SIMDJSON_SRC_IMPLEMENTATION_CPP +/* end file implementation.cpp */ + +/* defining SIMDJSON_CONDITIONAL_INCLUDE */ +#define SIMDJSON_CONDITIONAL_INCLUDE + +#if SIMDJSON_IMPLEMENTATION_ARM64 +/* including arm64.cpp: #include */ +/* begin file arm64.cpp */ +#ifndef SIMDJSON_SRC_ARM64_CPP +#define SIMDJSON_SRC_ARM64_CPP + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #include */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +/* including simdjson/arm64.h: #include */ +/* begin file simdjson/arm64.h */ +#ifndef SIMDJSON_ARM64_H +#define SIMDJSON_ARM64_H + +/* including simdjson/arm64/begin.h: #include "simdjson/arm64/begin.h" */ +/* begin file simdjson/arm64/begin.h */ +/* defining SIMDJSON_IMPLEMENTATION to "arm64" */ +#define SIMDJSON_IMPLEMENTATION arm64 +/* including simdjson/arm64/base.h: #include "simdjson/arm64/base.h" */ +/* begin file simdjson/arm64/base.h */ +#ifndef SIMDJSON_ARM64_BASE_H +#define SIMDJSON_ARM64_BASE_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #include "simdjson/base.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { +/** + * Implementation for NEON (ARMv8). + */ +namespace arm64 { + +class implementation; + +namespace { +namespace simd { +template 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) { +#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; +#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) { +#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; +#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) { +#ifdef 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 _M_ARM64 +// __umulh requires intrin.h +#include +#endif // _M_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 +#ifdef _M_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 // _M_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 + +#define SIMDJSON_SWAR_NUMBER_PARSING 1 + +#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 { + +#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO +namespace { +// Start of private section with Visual Studio workaround + + +/** + * make_uint8x16_t initializes a SIMD register (uint8x16_t). + * This is needed because, incredibly, the syntax uint8x16_t x = {1,2,3...} + * is not recognized under Visual Studio! This is a workaround. + * Using a std::initializer_list as a parameter resulted in + * inefficient code. With the current approach, if the parameters are + * compile-time constants, + * GNU GCC compiles it to ldr, the same as uint8x16_t x = {1,2,3...}. + * You should not use this function except for compile-time constants: + * it is not efficient. + */ +simdjson_inline uint8x16_t make_uint8x16_t(uint8_t x1, uint8_t x2, uint8_t x3, uint8_t x4, + uint8_t x5, uint8_t x6, uint8_t x7, uint8_t x8, + uint8_t x9, uint8_t x10, uint8_t x11, uint8_t x12, + uint8_t x13, uint8_t x14, uint8_t x15, uint8_t x16) { + // Doing a load like so end ups generating worse code. + // uint8_t array[16] = {x1, x2, x3, x4, x5, x6, x7, x8, + // x9, x10,x11,x12,x13,x14,x15,x16}; + // return vld1q_u8(array); + uint8x16_t x{}; + // incredibly, Visual Studio does not allow x[0] = x1 + x = vsetq_lane_u8(x1, x, 0); + x = vsetq_lane_u8(x2, x, 1); + x = vsetq_lane_u8(x3, x, 2); + x = vsetq_lane_u8(x4, x, 3); + x = vsetq_lane_u8(x5, x, 4); + x = vsetq_lane_u8(x6, x, 5); + x = vsetq_lane_u8(x7, x, 6); + x = vsetq_lane_u8(x8, x, 7); + x = vsetq_lane_u8(x9, x, 8); + x = vsetq_lane_u8(x10, x, 9); + x = vsetq_lane_u8(x11, x, 10); + x = vsetq_lane_u8(x12, x, 11); + x = vsetq_lane_u8(x13, x, 12); + x = vsetq_lane_u8(x14, x, 13); + x = vsetq_lane_u8(x15, x, 14); + x = vsetq_lane_u8(x16, x, 15); + return x; +} + +simdjson_inline uint8x8_t make_uint8x8_t(uint8_t x1, uint8_t x2, uint8_t x3, uint8_t x4, + uint8_t x5, uint8_t x6, uint8_t x7, uint8_t x8) { + uint8x8_t x{}; + x = vset_lane_u8(x1, x, 0); + x = vset_lane_u8(x2, x, 1); + x = vset_lane_u8(x3, x, 2); + x = vset_lane_u8(x4, x, 3); + x = vset_lane_u8(x5, x, 4); + x = vset_lane_u8(x6, x, 5); + x = vset_lane_u8(x7, x, 6); + x = vset_lane_u8(x8, x, 7); + return x; +} + +// We have to do the same work for make_int8x16_t +simdjson_inline int8x16_t make_int8x16_t(int8_t x1, int8_t x2, int8_t x3, int8_t x4, + int8_t x5, int8_t x6, int8_t x7, int8_t x8, + int8_t x9, int8_t x10, int8_t x11, int8_t x12, + int8_t x13, int8_t x14, int8_t x15, int8_t x16) { + // Doing a load like so end ups generating worse code. + // int8_t array[16] = {x1, x2, x3, x4, x5, x6, x7, x8, + // x9, x10,x11,x12,x13,x14,x15,x16}; + // return vld1q_s8(array); + int8x16_t x{}; + // incredibly, Visual Studio does not allow x[0] = x1 + x = vsetq_lane_s8(x1, x, 0); + x = vsetq_lane_s8(x2, x, 1); + x = vsetq_lane_s8(x3, x, 2); + x = vsetq_lane_s8(x4, x, 3); + x = vsetq_lane_s8(x5, x, 4); + x = vsetq_lane_s8(x6, x, 5); + x = vsetq_lane_s8(x7, x, 6); + x = vsetq_lane_s8(x8, x, 7); + x = vsetq_lane_s8(x9, x, 8); + x = vsetq_lane_s8(x10, x, 9); + x = vsetq_lane_s8(x11, x, 10); + x = vsetq_lane_s8(x12, x, 11); + x = vsetq_lane_s8(x13, x, 12); + x = vsetq_lane_s8(x14, x, 13); + x = vsetq_lane_s8(x15, x, 14); + x = vsetq_lane_s8(x16, x, 15); + return x; +} + +// 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 { +#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO + const uint8x16_t bit_mask = 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); + } + simdjson_inline bool any() const { return vmaxvq_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 +#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(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 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); } + + // 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 = 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 +#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO + uint8x8_t inc = 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 +#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(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]; + + 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 { +#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO + const uint8x16_t bit_mask = 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 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 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 + }; +} + +} // unnamed namespace +} // namespace arm64 +} // namespace simdjson + +#endif // SIMDJSON_ARM64_STRINGPARSING_DEFS_H +/* end file simdjson/arm64/stringparsing_defs.h */ + +#define SIMDJSON_SKIP_BACKSLASH_SHORT_CIRCUIT 1 +/* end file simdjson/arm64/begin.h */ +/* including simdjson/generic/amalgamated.h for arm64: #include "simdjson/generic/amalgamated.h" */ +/* begin file simdjson/generic/amalgamated.h for arm64 */ +#if defined(SIMDJSON_CONDITIONAL_INCLUDE) && !defined(SIMDJSON_GENERIC_DEPENDENCIES_H) +#error simdjson/generic/dependencies.h must be included before simdjson/generic/amalgamated.h! +#endif + +/* including simdjson/generic/base.h for arm64: #include "simdjson/generic/base.h" */ +/* begin file simdjson/generic/base.h for arm64 */ +#ifndef SIMDJSON_GENERIC_BASE_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_BASE_H */ +/* amalgamation skipped (editor-only): #include "simdjson/base.h" */ +/* amalgamation skipped (editor-only): // If we haven't got an implementation yet, we're in the editor, editing a generic file! Just */ +/* amalgamation skipped (editor-only): // use the most advanced one we can so the most possible stuff can be tested. */ +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_IMPLEMENTATION */ +/* amalgamation skipped (editor-only): #include "simdjson/implementation_detection.h" */ +/* amalgamation skipped (editor-only): #if SIMDJSON_IMPLEMENTATION_ICELAKE */ +/* amalgamation skipped (editor-only): #include "simdjson/icelake/begin.h" */ +/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_HASWELL */ +/* amalgamation skipped (editor-only): #include "simdjson/haswell/begin.h" */ +/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_WESTMERE */ +/* amalgamation skipped (editor-only): #include "simdjson/westmere/begin.h" */ +/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_ARM64 */ +/* amalgamation skipped (editor-only): #include "simdjson/arm64/begin.h" */ +/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_PPC64 */ +/* amalgamation skipped (editor-only): #include "simdjson/ppc64/begin.h" */ +/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_FALLBACK */ +/* amalgamation skipped (editor-only): #include "simdjson/fallback/begin.h" */ +/* amalgamation skipped (editor-only): #else */ +/* amalgamation skipped (editor-only): #error "All possible implementations (including fallback) have been disabled! simdjson will not run." */ +/* amalgamation skipped (editor-only): #endif */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_IMPLEMENTATION */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { +namespace arm64 { + +struct open_container; +class dom_parser_implementation; + +/** + * The type of a JSON number + */ +enum class number_type { + floating_point_number=1, /// a binary64 number + signed_integer, /// a signed integer that fits in a 64-bit word using two's complement + unsigned_integer /// a positive integer larger or equal to 1<<63 +}; + +} // namespace arm64 +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_BASE_H +/* end file simdjson/generic/base.h for arm64 */ +/* including simdjson/generic/jsoncharutils.h for arm64: #include "simdjson/generic/jsoncharutils.h" */ +/* begin file simdjson/generic/jsoncharutils.h for arm64 */ +#ifndef SIMDJSON_GENERIC_JSONCHARUTILS_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_JSONCHARUTILS_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/internal/jsoncharutils_tables.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/internal/numberparsing_tables.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { +namespace arm64 { +namespace { +namespace jsoncharutils { + +// return non-zero if not a structural or whitespace char +// zero otherwise +simdjson_inline uint32_t is_not_structural_or_whitespace(uint8_t c) { + return internal::structural_or_whitespace_negated[c]; +} + +simdjson_inline uint32_t is_structural_or_whitespace(uint8_t c) { + return internal::structural_or_whitespace[c]; +} + +// returns a value with the high 16 bits set if not valid +// otherwise returns the conversion of the 4 hex digits at src into the bottom +// 16 bits of the 32-bit return register +// +// see +// https://lemire.me/blog/2019/04/17/parsing-short-hexadecimal-strings-efficiently/ +static inline uint32_t hex_to_u32_nocheck( + const uint8_t *src) { // strictly speaking, static inline is a C-ism + uint32_t v1 = internal::digit_to_val32[630 + src[0]]; + uint32_t v2 = internal::digit_to_val32[420 + src[1]]; + uint32_t v3 = internal::digit_to_val32[210 + src[2]]; + uint32_t v4 = internal::digit_to_val32[0 + src[3]]; + return v1 | v2 | v3 | v4; +} + +// given a code point cp, writes to c +// the utf-8 code, outputting the length in +// bytes, if the length is zero, the code point +// is invalid +// +// This can possibly be made faster using pdep +// and clz and table lookups, but JSON documents +// have few escaped code points, and the following +// function looks cheap. +// +// Note: we assume that surrogates are treated separately +// +simdjson_inline size_t codepoint_to_utf8(uint32_t cp, uint8_t *c) { + if (cp <= 0x7F) { + c[0] = uint8_t(cp); + return 1; // ascii + } + if (cp <= 0x7FF) { + c[0] = uint8_t((cp >> 6) + 192); + c[1] = uint8_t((cp & 63) + 128); + return 2; // universal plane + // Surrogates are treated elsewhere... + //} //else if (0xd800 <= cp && cp <= 0xdfff) { + // return 0; // surrogates // could put assert here + } else if (cp <= 0xFFFF) { + c[0] = uint8_t((cp >> 12) + 224); + c[1] = uint8_t(((cp >> 6) & 63) + 128); + c[2] = uint8_t((cp & 63) + 128); + return 3; + } else if (cp <= 0x10FFFF) { // if you know you have a valid code point, this + // is not needed + c[0] = uint8_t((cp >> 18) + 240); + c[1] = uint8_t(((cp >> 12) & 63) + 128); + c[2] = uint8_t(((cp >> 6) & 63) + 128); + c[3] = uint8_t((cp & 63) + 128); + return 4; + } + // will return 0 when the code point was too large. + return 0; // bad r +} + +#if SIMDJSON_IS_32BITS // _umul128 for x86, arm +// this is a slow emulation routine for 32-bit +// +static simdjson_inline uint64_t __emulu(uint32_t x, uint32_t y) { + return x * (uint64_t)y; +} +static simdjson_inline uint64_t _umul128(uint64_t ab, uint64_t cd, uint64_t *hi) { + uint64_t ad = __emulu((uint32_t)(ab >> 32), (uint32_t)cd); + uint64_t bd = __emulu((uint32_t)ab, (uint32_t)cd); + uint64_t adbc = ad + __emulu((uint32_t)ab, (uint32_t)(cd >> 32)); + uint64_t adbc_carry = !!(adbc < ad); + uint64_t lo = bd + (adbc << 32); + *hi = __emulu((uint32_t)(ab >> 32), (uint32_t)(cd >> 32)) + (adbc >> 32) + + (adbc_carry << 32) + !!(lo < bd); + return lo; +} +#endif + +} // namespace jsoncharutils +} // unnamed namespace +} // namespace arm64 +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_JSONCHARUTILS_H +/* end file simdjson/generic/jsoncharutils.h for arm64 */ +/* including simdjson/generic/atomparsing.h for arm64: #include "simdjson/generic/atomparsing.h" */ +/* begin file simdjson/generic/atomparsing.h for arm64 */ +#ifndef SIMDJSON_GENERIC_ATOMPARSING_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ATOMPARSING_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/jsoncharutils.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +#include + +namespace simdjson { +namespace arm64 { +namespace { +/// @private +namespace atomparsing { + +// The string_to_uint32 is exclusively used to map literal strings to 32-bit values. +// We use memcpy instead of a pointer cast to avoid undefined behaviors since we cannot +// be certain that the character pointer will be properly aligned. +// You might think that using memcpy makes this function expensive, but you'd be wrong. +// All decent optimizing compilers (GCC, clang, Visual Studio) will compile string_to_uint32("false"); +// to the compile-time constant 1936482662. +simdjson_inline uint32_t string_to_uint32(const char* str) { uint32_t val; std::memcpy(&val, str, sizeof(uint32_t)); return val; } + + +// 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 arm64 +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_ATOMPARSING_H +/* end file simdjson/generic/atomparsing.h for arm64 */ +/* including simdjson/generic/dom_parser_implementation.h for arm64: #include "simdjson/generic/dom_parser_implementation.h" */ +/* begin file simdjson/generic/dom_parser_implementation.h for arm64 */ +#ifndef SIMDJSON_GENERIC_DOM_PARSER_IMPLEMENTATION_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_DOM_PARSER_IMPLEMENTATION_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/internal/dom_parser_implementation.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { +namespace arm64 { + +// expectation: sizeof(open_container) = 64/8. +struct open_container { + uint32_t tape_index; // where, on the tape, does the scope ([,{) begins + uint32_t count; // how many elements in the scope +}; // struct open_container + +static_assert(sizeof(open_container) == 64/8, "Open container must be 64 bits"); + +class dom_parser_implementation final : public internal::dom_parser_implementation { +public: + /** Tape location of each open { or [ */ + std::unique_ptr open_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 arm64 +} // namespace simdjson + +namespace simdjson { +namespace arm64 { + +inline dom_parser_implementation::dom_parser_implementation() noexcept = default; +inline dom_parser_implementation::dom_parser_implementation(dom_parser_implementation &&other) noexcept = default; +inline dom_parser_implementation &dom_parser_implementation::operator=(dom_parser_implementation &&other) noexcept = default; + +// Leaving these here so they can be inlined if so desired +inline simdjson_warn_unused error_code dom_parser_implementation::set_capacity(size_t capacity) noexcept { + if(capacity > SIMDJSON_MAXSIZE_BYTES) { 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 arm64 +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_DOM_PARSER_IMPLEMENTATION_H +/* end file simdjson/generic/dom_parser_implementation.h for arm64 */ +/* including simdjson/generic/implementation_simdjson_result_base.h for arm64: #include "simdjson/generic/implementation_simdjson_result_base.h" */ +/* begin file simdjson/generic/implementation_simdjson_result_base.h for arm64 */ +#ifndef SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { +namespace arm64 { + +// This is a near copy of include/error.h's implementation_simdjson_result_base, except it doesn't use std::pair +// so we can avoid inlining errors +// TODO reconcile these! +/** + * The result of a simdjson operation that could fail. + * + * Gives the option of reading error codes, or throwing an exception by casting to the desired result. + * + * This is a base class for implementations that want to add functions to the result type for + * chaining. + * + * Override like: + * + * struct simdjson_result : 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_inline error_code get(T &value) && noexcept; + + /** + * The error. + */ + simdjson_inline error_code error() const noexcept; + +#if SIMDJSON_EXCEPTIONS + + /** + * Get the result value. + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T& value() & noexcept(false); + + /** + * Take the result value (move it). + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T&& value() && noexcept(false); + + /** + * Take the result value (move it). + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T&& take_value() && noexcept(false); + + /** + * Cast to the value (will throw on error). + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline operator T&&() && noexcept(false); + + +#endif // SIMDJSON_EXCEPTIONS + + /** + * Get the result value. This function is safe if and only + * the error() method returns a value that evaluates to false. + */ + simdjson_inline const T& value_unsafe() const& noexcept; + /** + * 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 + +} // namespace arm64 +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_H +/* end file simdjson/generic/implementation_simdjson_result_base.h for arm64 */ +/* including simdjson/generic/numberparsing.h for arm64: #include "simdjson/generic/numberparsing.h" */ +/* begin file simdjson/generic/numberparsing.h for arm64 */ +#ifndef SIMDJSON_GENERIC_NUMBERPARSING_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_NUMBERPARSING_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/jsoncharutils.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/internal/numberparsing_tables.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +#include +#include +#include + +namespace simdjson { +namespace arm64 { +namespace numberparsing { + +#ifdef JSON_TEST_NUMBERS +#define INVALID_NUMBER(SRC) (found_invalid_number((SRC)), NUMBER_ERROR) +#define WRITE_INTEGER(VALUE, SRC, WRITER) (found_integer((VALUE), (SRC)), (WRITER).append_s64((VALUE))) +#define WRITE_UNSIGNED(VALUE, SRC, WRITER) (found_unsigned_integer((VALUE), (SRC)), (WRITER).append_u64((VALUE))) +#define WRITE_DOUBLE(VALUE, SRC, WRITER) (found_float((VALUE), (SRC)), (WRITER).append_double((VALUE))) +#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)) +#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 + // 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; + + + // 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. + + // 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. In very rare cases, even that + // will not suffice, though it is seemingly very hard to find such a scenario. + // + // 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++; } + // At this point, we might need to add at most one to firstproduct, but this + // can only change the value of firstproduct.high if firstproduct.low is maximal. + if(simdjson_unlikely(firstproduct.low == 0xFFFFFFFFFFFFFFFF)) { + // This is very unlikely, but if so, we need to do much more work! + return false; + } + } + 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 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_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 doesn't 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 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 */ +template +error_code slow_float_parsing(simdjson_unused const uint8_t * src, W writer) { + double d; + if (parse_float_fallback(src, &d)) { + writer.append_double(d); + 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: This makes a *copy* of the writer and passes it to slow_float_parsing. This happens + // because slow_float_parsing is a non-inlined function. 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. + // This is what forces the skip_double, as well. + error_code error = slow_float_parsing(src, writer); + writer.skip_double(); + 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; +} + +// 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 +} + +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_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 INVALID_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 INVALID_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 doesn't 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 = start_digits-src > 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++; } + if ( p == src ) { return NUMBER_ERROR; } + if (jsoncharutils::is_structural_or_whitespace(*p)) { + // We have an integer. + // If the number is negative and valid, it must be a signed integer. + if(negative) { return number_type::signed_integer; } + // We want values larger or equal to 9223372036854775808 to be unsigned + // integers, and the other values to be signed integers. + int digit_count = int(p - src); + if(digit_count >= 19) { + const uint8_t * smaller_big_integer = reinterpret_cast("9223372036854775808"); + if((digit_count >= 20) || (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 = start_digits-src > 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; + default: SIMDJSON_UNREACHABLE(); + } + return out; +} + +} // namespace arm64 +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_NUMBERPARSING_H +/* end file simdjson/generic/numberparsing.h for arm64 */ + +/* including simdjson/generic/implementation_simdjson_result_base-inl.h for arm64: #include "simdjson/generic/implementation_simdjson_result_base-inl.h" */ +/* begin file simdjson/generic/implementation_simdjson_result_base-inl.h for arm64 */ +#ifndef SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_INL_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_INL_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { +namespace arm64 { + +// +// internal::implementation_simdjson_result_base 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_inline error_code implementation_simdjson_result_base::error() const noexcept { + return this->second; +} + +#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::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 arm64 +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_INL_H +/* end file simdjson/generic/implementation_simdjson_result_base-inl.h for arm64 */ +/* end file simdjson/generic/amalgamated.h for arm64 */ +/* including simdjson/arm64/end.h: #include "simdjson/arm64/end.h" */ +/* begin file simdjson/arm64/end.h */ +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #include "simdjson/arm64/base.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +#undef SIMDJSON_SKIP_BACKSLASH_SHORT_CIRCUIT +/* undefining SIMDJSON_IMPLEMENTATION from "arm64" */ +#undef SIMDJSON_IMPLEMENTATION +/* end file simdjson/arm64/end.h */ + +#endif // SIMDJSON_ARM64_H +/* end file simdjson/arm64.h */ +/* including simdjson/arm64/implementation.h: #include */ +/* begin file simdjson/arm64/implementation.h */ +#ifndef SIMDJSON_ARM64_IMPLEMENTATION_H +#define SIMDJSON_ARM64_IMPLEMENTATION_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #include "simdjson/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/implementation.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/internal/instruction_set.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { +namespace arm64 { + +/** + * @private + */ +class implementation final : public simdjson::implementation { +public: + simdjson_inline implementation() : simdjson::implementation("arm64", "ARM NEON", internal::instruction_set::NEON) {} + simdjson_warn_unused error_code create_dom_parser_implementation( + size_t capacity, + size_t max_length, + std::unique_ptr& dst + ) const noexcept final; + simdjson_warn_unused error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept final; + simdjson_warn_unused bool validate_utf8(const char *buf, size_t len) const noexcept final; +}; + +} // namespace arm64 +} // namespace simdjson + +#endif // SIMDJSON_ARM64_IMPLEMENTATION_H +/* end file simdjson/arm64/implementation.h */ + +/* including simdjson/arm64/begin.h: #include */ +/* 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) { +#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; +#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) { +#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; +#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) { +#ifdef 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 _M_ARM64 +// __umulh requires intrin.h +#include +#endif // _M_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 +#ifdef _M_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 // _M_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 + +#define SIMDJSON_SWAR_NUMBER_PARSING 1 + +#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 { + +#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO +namespace { +// Start of private section with Visual Studio workaround + + +/** + * make_uint8x16_t initializes a SIMD register (uint8x16_t). + * This is needed because, incredibly, the syntax uint8x16_t x = {1,2,3...} + * is not recognized under Visual Studio! This is a workaround. + * Using a std::initializer_list as a parameter resulted in + * inefficient code. With the current approach, if the parameters are + * compile-time constants, + * GNU GCC compiles it to ldr, the same as uint8x16_t x = {1,2,3...}. + * You should not use this function except for compile-time constants: + * it is not efficient. + */ +simdjson_inline uint8x16_t make_uint8x16_t(uint8_t x1, uint8_t x2, uint8_t x3, uint8_t x4, + uint8_t x5, uint8_t x6, uint8_t x7, uint8_t x8, + uint8_t x9, uint8_t x10, uint8_t x11, uint8_t x12, + uint8_t x13, uint8_t x14, uint8_t x15, uint8_t x16) { + // Doing a load like so end ups generating worse code. + // uint8_t array[16] = {x1, x2, x3, x4, x5, x6, x7, x8, + // x9, x10,x11,x12,x13,x14,x15,x16}; + // return vld1q_u8(array); + uint8x16_t x{}; + // incredibly, Visual Studio does not allow x[0] = x1 + x = vsetq_lane_u8(x1, x, 0); + x = vsetq_lane_u8(x2, x, 1); + x = vsetq_lane_u8(x3, x, 2); + x = vsetq_lane_u8(x4, x, 3); + x = vsetq_lane_u8(x5, x, 4); + x = vsetq_lane_u8(x6, x, 5); + x = vsetq_lane_u8(x7, x, 6); + x = vsetq_lane_u8(x8, x, 7); + x = vsetq_lane_u8(x9, x, 8); + x = vsetq_lane_u8(x10, x, 9); + x = vsetq_lane_u8(x11, x, 10); + x = vsetq_lane_u8(x12, x, 11); + x = vsetq_lane_u8(x13, x, 12); + x = vsetq_lane_u8(x14, x, 13); + x = vsetq_lane_u8(x15, x, 14); + x = vsetq_lane_u8(x16, x, 15); + return x; +} + +simdjson_inline uint8x8_t make_uint8x8_t(uint8_t x1, uint8_t x2, uint8_t x3, uint8_t x4, + uint8_t x5, uint8_t x6, uint8_t x7, uint8_t x8) { + uint8x8_t x{}; + x = vset_lane_u8(x1, x, 0); + x = vset_lane_u8(x2, x, 1); + x = vset_lane_u8(x3, x, 2); + x = vset_lane_u8(x4, x, 3); + x = vset_lane_u8(x5, x, 4); + x = vset_lane_u8(x6, x, 5); + x = vset_lane_u8(x7, x, 6); + x = vset_lane_u8(x8, x, 7); + return x; +} + +// We have to do the same work for make_int8x16_t +simdjson_inline int8x16_t make_int8x16_t(int8_t x1, int8_t x2, int8_t x3, int8_t x4, + int8_t x5, int8_t x6, int8_t x7, int8_t x8, + int8_t x9, int8_t x10, int8_t x11, int8_t x12, + int8_t x13, int8_t x14, int8_t x15, int8_t x16) { + // Doing a load like so end ups generating worse code. + // int8_t array[16] = {x1, x2, x3, x4, x5, x6, x7, x8, + // x9, x10,x11,x12,x13,x14,x15,x16}; + // return vld1q_s8(array); + int8x16_t x{}; + // incredibly, Visual Studio does not allow x[0] = x1 + x = vsetq_lane_s8(x1, x, 0); + x = vsetq_lane_s8(x2, x, 1); + x = vsetq_lane_s8(x3, x, 2); + x = vsetq_lane_s8(x4, x, 3); + x = vsetq_lane_s8(x5, x, 4); + x = vsetq_lane_s8(x6, x, 5); + x = vsetq_lane_s8(x7, x, 6); + x = vsetq_lane_s8(x8, x, 7); + x = vsetq_lane_s8(x9, x, 8); + x = vsetq_lane_s8(x10, x, 9); + x = vsetq_lane_s8(x11, x, 10); + x = vsetq_lane_s8(x12, x, 11); + x = vsetq_lane_s8(x13, x, 12); + x = vsetq_lane_s8(x14, x, 13); + x = vsetq_lane_s8(x15, x, 14); + x = vsetq_lane_s8(x16, x, 15); + return x; +} + +// 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 { +#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO + const uint8x16_t bit_mask = 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); + } + simdjson_inline bool any() const { return vmaxvq_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 +#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(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 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); } + + // 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 = 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 +#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO + uint8x8_t inc = 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 +#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(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]; + + 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 { +#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO + const uint8x16_t bit_mask = 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 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 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 + }; +} + +} // 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 generic/amalgamated.h for arm64: #include */ +/* begin file generic/amalgamated.h for arm64 */ +#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 arm64: #include */ +/* begin file generic/base.h for arm64 */ +#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 arm64 { +namespace { + +struct json_character_block; + +} // unnamed namespace +} // namespace arm64 +} // namespace simdjson + +#endif // SIMDJSON_SRC_GENERIC_BASE_H +/* end file generic/base.h for arm64 */ +/* including generic/dom_parser_implementation.h for arm64: #include */ +/* begin file generic/dom_parser_implementation.h for arm64 */ +#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 arm64 { +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 arm64 +} // namespace simdjson + +#endif // SIMDJSON_SRC_GENERIC_DOM_PARSER_IMPLEMENTATION_H +/* end file generic/dom_parser_implementation.h for arm64 */ +/* including generic/json_character_block.h for arm64: #include */ +/* begin file generic/json_character_block.h for arm64 */ +#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 arm64 { +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 arm64 +} // namespace simdjson + +#endif // SIMDJSON_SRC_GENERIC_JSON_CHARACTER_BLOCK_H +/* end file generic/json_character_block.h for arm64 */ +/* end file generic/amalgamated.h for arm64 */ +/* including generic/stage1/amalgamated.h for arm64: #include */ +/* begin file generic/stage1/amalgamated.h for arm64 */ +// Stuff other things depend on +/* including generic/stage1/base.h for arm64: #include */ +/* begin file generic/stage1/base.h for arm64 */ +#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 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; + +} // namespace stage1 + +namespace utf8_validation { +struct utf8_checker; +} // namespace utf8_validation + +using utf8_validation::utf8_checker; + +} // unnamed namespace +} // namespace arm64 +} // namespace simdjson + +#endif // SIMDJSON_SRC_GENERIC_STAGE1_BASE_H +/* end file generic/stage1/base.h for arm64 */ +/* including generic/stage1/buf_block_reader.h for arm64: #include */ +/* begin file generic/stage1/buf_block_reader.h for arm64 */ +#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 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; +}; + +// 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 arm64 +} // namespace simdjson + +#endif // SIMDJSON_SRC_GENERIC_STAGE1_BUF_BLOCK_READER_H +/* end file generic/stage1/buf_block_reader.h for arm64 */ +/* including generic/stage1/json_escape_scanner.h for arm64: #include */ +/* begin file generic/stage1/json_escape_scanner.h for arm64 */ +#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 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; + + 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 arm64 +} // namespace simdjson + +#endif // SIMDJSON_SRC_GENERIC_STAGE1_JSON_STRING_SCANNER_H +/* end file generic/stage1/json_escape_scanner.h for arm64 */ +/* including generic/stage1/json_string_scanner.h for arm64: #include */ +/* begin file generic/stage1/json_string_scanner.h for arm64 */ +#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 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) {} + + // 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 arm64 +} // namespace simdjson + +#endif // SIMDJSON_SRC_GENERIC_STAGE1_JSON_STRING_SCANNER_H +/* end file generic/stage1/json_string_scanner.h for arm64 */ +/* including generic/stage1/utf8_lookup4_algorithm.h for arm64: #include */ +/* begin file generic/stage1/utf8_lookup4_algorithm.h for arm64 */ +#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 arm64 { +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 = simd8(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 + }; +#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; + } + +#ifndef SIMDJSON_IF_CONSTEXPR +#if SIMDJSON_CPLUSPLUS17 +#define SIMDJSON_IF_CONSTEXPR if constexpr +#else +#define SIMDJSON_IF_CONSTEXPR if +#endif +#endif + + 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]); + } 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; + } + + }; // struct utf8_checker +} // namespace utf8_validation + +} // unnamed namespace +} // namespace arm64 +} // namespace simdjson + +#endif // SIMDJSON_SRC_GENERIC_STAGE1_UTF8_LOOKUP4_ALGORITHM_H +/* end file generic/stage1/utf8_lookup4_algorithm.h for arm64 */ +/* including generic/stage1/json_scanner.h for arm64: #include */ +/* begin file generic/stage1/json_scanner.h for arm64 */ +#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 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) {} + + /** + * 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_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_inline error_code json_scanner::finish() { + return string_scanner.finish(); +} + +} // namespace stage1 +} // unnamed namespace +} // namespace arm64 +} // namespace simdjson + +#endif // SIMDJSON_SRC_GENERIC_STAGE1_JSON_SCANNER_H +/* end file generic/stage1/json_scanner.h for arm64 */ + +// All other declarations +/* including generic/stage1/find_next_document_index.h for arm64: #include */ +/* begin file generic/stage1/find_next_document_index.h for arm64 */ +#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 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; +} + +} // namespace stage1 +} // unnamed namespace +} // namespace arm64 +} // namespace simdjson + +#endif // SIMDJSON_SRC_GENERIC_STAGE1_FIND_NEXT_DOCUMENT_INDEX_H +/* end file generic/stage1/find_next_document_index.h for arm64 */ +/* including generic/stage1/json_minifier.h for arm64: #include */ +/* begin file generic/stage1/json_minifier.h for arm64 */ +#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 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; + +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; +}; + +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; +} + +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 arm64 +} // namespace simdjson + +#endif // SIMDJSON_SRC_GENERIC_STAGE1_JSON_MINIFIER_H +/* end file generic/stage1/json_minifier.h for arm64 */ +/* including generic/stage1/json_structural_indexer.h for arm64: #include */ +/* begin file generic/stage1/json_structural_indexer.h for arm64 */ +#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 arm64 { +namespace { +namespace stage1 { + +class bit_indexer { +public: + uint32_t *tail; + + simdjson_inline bit_indexer(uint32_t *index_buf) : tail(index_buf) {} + + // 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; +#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. + */ + + uint64_t rev_bits = reverse_bits(bits); + int cnt = static_cast(count_ones(bits)); + int i = 0; + // Do the first 8 all together + for (; i<8; i++) { + int lz = leading_zeroes(rev_bits); + this->tail[i] = static_cast(idx) + lz; + rev_bits = zero_leading_bit(rev_bits, lz); + } + // Do the next 8 all together (we hope in most cases it won't happen at all + // and the branch is easily predicted). + if (simdjson_unlikely(cnt > 8)) { + i = 8; + for (; i<16; i++) { + int lz = leading_zeroes(rev_bits); + this->tail[i] = static_cast(idx) + lz; + rev_bits = zero_leading_bit(rev_bits, lz); + } + + + // Most files don't have 16+ structurals per block, so we take several basically guaranteed + // branch mispredictions here. 16+ structurals per block means either punctuation ({} [] , :) + // or the start of a value ("abc" true 123) every four characters. + if (simdjson_unlikely(cnt > 16)) { + i = 16; + while (rev_bits != 0) { + int lz = leading_zeroes(rev_bits); + this->tail[i++] = static_cast(idx) + lz; + rev_bits = zero_leading_bit(rev_bits, lz); + } + } + } + this->tail += cnt; +#else // SIMDJSON_PREFER_REVERSE_BITS + /** + * 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. + */ + + int cnt = static_cast(count_ones(bits)); + // Do the first 8 all together + for (int i=0; i<8; i++) { + this->tail[i] = idx + trailing_zeroes(bits); + bits = clear_lowest_bit(bits); + } + + // Do the next 8 all together (we hope in most cases it won't happen at all + // and the branch is easily predicted). + if (simdjson_unlikely(cnt > 8)) { + for (int i=8; i<16; i++) { + this->tail[i] = idx + trailing_zeroes(bits); + bits = clear_lowest_bit(bits); + } + + // Most files don't have 16+ structurals per block, so we take several basically guaranteed + // branch mispredictions here. 16+ structurals per block means either punctuation ({} [] , :) + // or the start of a value ("abc" true 123) every four characters. + if (simdjson_unlikely(cnt > 16)) { + int i = 16; + do { + this->tail[i] = idx + trailing_zeroes(bits); + bits = clear_lowest_bit(bits); + i++; + } while (i < cnt); + } + } + + this->tail += cnt; +#endif + } +#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_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 doesn't 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 arm64 +} // 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 arm64 */ +/* including generic/stage1/utf8_validator.h for arm64: #include */ +/* begin file generic/stage1/utf8_validator.h for arm64 */ +#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 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; +} + +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 simdjson + +#endif // SIMDJSON_SRC_GENERIC_STAGE1_UTF8_VALIDATOR_H +/* end file generic/stage1/utf8_validator.h for arm64 */ +/* end file generic/stage1/amalgamated.h for arm64 */ +/* including generic/stage2/amalgamated.h for arm64: #include */ +/* begin file generic/stage2/amalgamated.h for arm64 */ +// Stuff other things depend on +/* including generic/stage2/base.h for arm64: #include */ +/* begin file generic/stage2/base.h for arm64 */ +#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 arm64 { +namespace { +namespace stage2 { + +class json_iterator; +class structural_iterator; +struct tape_builder; +struct tape_writer; + +} // namespace stage2 +} // unnamed namespace +} // namespace arm64 +} // namespace simdjson + +#endif // SIMDJSON_SRC_GENERIC_STAGE2_BASE_H +/* end file generic/stage2/base.h for arm64 */ +/* including generic/stage2/tape_writer.h for arm64: #include */ +/* begin file generic/stage2/tape_writer.h for arm64 */ +#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 arm64 { +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 arm64 +} // namespace simdjson + +#endif // SIMDJSON_SRC_GENERIC_STAGE2_TAPE_WRITER_H +/* end file generic/stage2/tape_writer.h for arm64 */ +/* including generic/stage2/logger.h for arm64: #include */ +/* begin file generic/stage2/logger.h for arm64 */ +#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 arm64 { +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 arm64 */ +#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 arm64 { +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 { + switch (*value) { + case '"': return visitor.visit_string(*this, 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); + case '-': + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + return visitor.visit_number(*this, value); + default: + log_error("Non-value found when value was expected!"); + return TAPE_ERROR; + } +} + +} // 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 */ +#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 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, + + 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 isn't 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 isn't 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_nullptrptr 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; + } + } + /* can't be reached */ + return nullptr; +} + +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; + } + } + /* can't be reached */ + return nullptr; +} + +} // namespace stringparsing +} // unnamed namespace +} // namespace arm64 +} // namespace simdjson + +#endif // SIMDJSON_SRC_GENERIC_STAGE2_STRINGPARSING_H +/* end file generic/stage2/stringparsing.h for arm64 */ +/* including generic/stage2/structural_iterator.h for arm64: #include */ +/* begin file generic/stage2/structural_iterator.h for arm64 */ +#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 arm64 { +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 arm64 +} // namespace simdjson + +#endif // SIMDJSON_SRC_GENERIC_STAGE2_STRUCTURAL_ITERATOR_H +/* end file generic/stage2/structural_iterator.h for arm64 */ +/* including generic/stage2/tape_builder.h for arm64: #include */ +/* begin file generic/stage2/tape_builder.h for arm64 */ +#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 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; + + /** 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 arm64 +} // namespace simdjson + +#endif // SIMDJSON_SRC_GENERIC_STAGE2_TAPE_BUILDER_H +/* end file generic/stage2/tape_builder.h for arm64 */ +/* end file generic/stage2/amalgamated.h for arm64 */ + +// +// Stage 1 +// +namespace simdjson { +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; +} + +namespace { + +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); + + 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. + + 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(); + + return { whitespace, op }; +} + +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_inline simd8 must_be_2_3_continuation(const simd8 prev2, const simd8 prev3) { + simd8 is_third_byte = prev2 >= uint8_t(0xe0u); + simd8 is_fourth_byte = prev3 >= uint8_t(0xf0u); + return is_third_byte ^ is_fourth_byte; +} + +} // unnamed namespace +} // namespace arm64 +} // namespace simdjson + +// +// Stage 2 +// + +// +// Implementation-specific overrides +// +namespace simdjson { +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 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 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 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 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 simdjson + +/* including simdjson/arm64/end.h: #include */ +/* 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_SRC_ARM64_CPP +/* end file arm64.cpp */ +#endif +#if SIMDJSON_IMPLEMENTATION_FALLBACK +/* including fallback.cpp: #include */ +/* begin file fallback.cpp */ +#ifndef SIMDJSON_SRC_FALLBACK_CPP +#define SIMDJSON_SRC_FALLBACK_CPP + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #include */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +/* including simdjson/fallback.h: #include */ +/* begin file simdjson/fallback.h */ +#ifndef SIMDJSON_FALLBACK_H +#define SIMDJSON_FALLBACK_H + +/* including simdjson/fallback/begin.h: #include "simdjson/fallback/begin.h" */ +/* begin file simdjson/fallback/begin.h */ +/* defining SIMDJSON_IMPLEMENTATION to "fallback" */ +#define SIMDJSON_IMPLEMENTATION fallback +/* including simdjson/fallback/base.h: #include "simdjson/fallback/base.h" */ +/* begin file simdjson/fallback/base.h */ +#ifndef SIMDJSON_FALLBACK_BASE_H +#define SIMDJSON_FALLBACK_BASE_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #include "simdjson/base.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { +/** + * Fallback implementation (runs on any machine). + */ +namespace fallback { + +class implementation; + +} // namespace fallback +} // namespace simdjson + +#endif // SIMDJSON_FALLBACK_BASE_H +/* end file simdjson/fallback/base.h */ +/* including simdjson/fallback/bitmanipulation.h: #include "simdjson/fallback/bitmanipulation.h" */ +/* begin file simdjson/fallback/bitmanipulation.h */ +#ifndef SIMDJSON_FALLBACK_BITMANIPULATION_H +#define SIMDJSON_FALLBACK_BITMANIPULATION_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #include "simdjson/fallback/base.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { +namespace fallback { +namespace { + +#if defined(_MSC_VER) && !defined(_M_ARM64) && !defined(_M_X64) +static inline unsigned char _BitScanForward64(unsigned long* ret, uint64_t x) { + unsigned long x0 = (unsigned long)x, top, bottom; + _BitScanForward(&top, (unsigned long)(x >> 32)); + _BitScanForward(&bottom, x0); + *ret = x0 ? bottom : 32 + top; + return x != 0; +} +static unsigned char _BitScanReverse64(unsigned long* ret, uint64_t x) { + unsigned long x1 = (unsigned long)(x >> 32), top, bottom; + _BitScanReverse(&top, x1); + _BitScanReverse(&bottom, (unsigned long)x); + *ret = x1 ? top + 32 : bottom; + return x != 0; +} +#endif + +/* result might be undefined when input_num is zero */ +simdjson_inline int leading_zeroes(uint64_t input_num) { +#ifdef _MSC_VER + unsigned long leading_zero = 0; + // Search the mask data from most significant bit (MSB) + // to least significant bit (LSB) for a set bit (1). + if (_BitScanReverse64(&leading_zero, input_num)) + return (int)(63 - leading_zero); + else + return 64; +#else + return __builtin_clzll(input_num); +#endif// _MSC_VER +} + +} // 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 static 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] }; +} + +} // 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 +#ifdef _M_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 // _M_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 + +#define SIMDJSON_SWAR_NUMBER_PARSING 1 + +#endif // SIMDJSON_FALLBACK_NUMBERPARSING_DEFS_H +/* end file simdjson/fallback/numberparsing_defs.h */ +/* end file simdjson/fallback/begin.h */ +/* including simdjson/generic/amalgamated.h for fallback: #include "simdjson/generic/amalgamated.h" */ +/* begin file simdjson/generic/amalgamated.h for fallback */ +#if defined(SIMDJSON_CONDITIONAL_INCLUDE) && !defined(SIMDJSON_GENERIC_DEPENDENCIES_H) +#error simdjson/generic/dependencies.h must be included before simdjson/generic/amalgamated.h! +#endif + +/* including simdjson/generic/base.h for fallback: #include "simdjson/generic/base.h" */ +/* begin file simdjson/generic/base.h for fallback */ +#ifndef SIMDJSON_GENERIC_BASE_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_BASE_H */ +/* amalgamation skipped (editor-only): #include "simdjson/base.h" */ +/* amalgamation skipped (editor-only): // If we haven't got an implementation yet, we're in the editor, editing a generic file! Just */ +/* amalgamation skipped (editor-only): // use the most advanced one we can so the most possible stuff can be tested. */ +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_IMPLEMENTATION */ +/* amalgamation skipped (editor-only): #include "simdjson/implementation_detection.h" */ +/* amalgamation skipped (editor-only): #if SIMDJSON_IMPLEMENTATION_ICELAKE */ +/* amalgamation skipped (editor-only): #include "simdjson/icelake/begin.h" */ +/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_HASWELL */ +/* amalgamation skipped (editor-only): #include "simdjson/haswell/begin.h" */ +/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_WESTMERE */ +/* amalgamation skipped (editor-only): #include "simdjson/westmere/begin.h" */ +/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_ARM64 */ +/* amalgamation skipped (editor-only): #include "simdjson/arm64/begin.h" */ +/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_PPC64 */ +/* amalgamation skipped (editor-only): #include "simdjson/ppc64/begin.h" */ +/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_FALLBACK */ +/* amalgamation skipped (editor-only): #include "simdjson/fallback/begin.h" */ +/* amalgamation skipped (editor-only): #else */ +/* amalgamation skipped (editor-only): #error "All possible implementations (including fallback) have been disabled! simdjson will not run." */ +/* amalgamation skipped (editor-only): #endif */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_IMPLEMENTATION */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { +namespace fallback { + +struct open_container; +class dom_parser_implementation; + +/** + * The type of a JSON number + */ +enum class number_type { + floating_point_number=1, /// a binary64 number + signed_integer, /// a signed integer that fits in a 64-bit word using two's complement + unsigned_integer /// a positive integer larger or equal to 1<<63 +}; + +} // namespace fallback +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_BASE_H +/* end file simdjson/generic/base.h for fallback */ +/* including simdjson/generic/jsoncharutils.h for fallback: #include "simdjson/generic/jsoncharutils.h" */ +/* begin file simdjson/generic/jsoncharutils.h for fallback */ +#ifndef SIMDJSON_GENERIC_JSONCHARUTILS_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_JSONCHARUTILS_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/internal/jsoncharutils_tables.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/internal/numberparsing_tables.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { +namespace fallback { +namespace { +namespace jsoncharutils { + +// return non-zero if not a structural or whitespace char +// zero otherwise +simdjson_inline uint32_t is_not_structural_or_whitespace(uint8_t c) { + return internal::structural_or_whitespace_negated[c]; +} + +simdjson_inline uint32_t is_structural_or_whitespace(uint8_t c) { + return internal::structural_or_whitespace[c]; +} + +// returns a value with the high 16 bits set if not valid +// otherwise returns the conversion of the 4 hex digits at src into the bottom +// 16 bits of the 32-bit return register +// +// see +// https://lemire.me/blog/2019/04/17/parsing-short-hexadecimal-strings-efficiently/ +static inline uint32_t hex_to_u32_nocheck( + const uint8_t *src) { // strictly speaking, static inline is a C-ism + uint32_t v1 = internal::digit_to_val32[630 + src[0]]; + uint32_t v2 = internal::digit_to_val32[420 + src[1]]; + uint32_t v3 = internal::digit_to_val32[210 + src[2]]; + uint32_t v4 = internal::digit_to_val32[0 + src[3]]; + return v1 | v2 | v3 | v4; +} + +// given a code point cp, writes to c +// the utf-8 code, outputting the length in +// bytes, if the length is zero, the code point +// is invalid +// +// This can possibly be made faster using pdep +// and clz and table lookups, but JSON documents +// have few escaped code points, and the following +// function looks cheap. +// +// Note: we assume that surrogates are treated separately +// +simdjson_inline size_t codepoint_to_utf8(uint32_t cp, uint8_t *c) { + if (cp <= 0x7F) { + c[0] = uint8_t(cp); + return 1; // ascii + } + if (cp <= 0x7FF) { + c[0] = uint8_t((cp >> 6) + 192); + c[1] = uint8_t((cp & 63) + 128); + return 2; // universal plane + // Surrogates are treated elsewhere... + //} //else if (0xd800 <= cp && cp <= 0xdfff) { + // return 0; // surrogates // could put assert here + } else if (cp <= 0xFFFF) { + c[0] = uint8_t((cp >> 12) + 224); + c[1] = uint8_t(((cp >> 6) & 63) + 128); + c[2] = uint8_t((cp & 63) + 128); + return 3; + } else if (cp <= 0x10FFFF) { // if you know you have a valid code point, this + // is not needed + c[0] = uint8_t((cp >> 18) + 240); + c[1] = uint8_t(((cp >> 12) & 63) + 128); + c[2] = uint8_t(((cp >> 6) & 63) + 128); + c[3] = uint8_t((cp & 63) + 128); + return 4; + } + // will return 0 when the code point was too large. + return 0; // bad r +} + +#if SIMDJSON_IS_32BITS // _umul128 for x86, arm +// this is a slow emulation routine for 32-bit +// +static simdjson_inline uint64_t __emulu(uint32_t x, uint32_t y) { + return x * (uint64_t)y; +} +static simdjson_inline uint64_t _umul128(uint64_t ab, uint64_t cd, uint64_t *hi) { + uint64_t ad = __emulu((uint32_t)(ab >> 32), (uint32_t)cd); + uint64_t bd = __emulu((uint32_t)ab, (uint32_t)cd); + uint64_t adbc = ad + __emulu((uint32_t)ab, (uint32_t)(cd >> 32)); + uint64_t adbc_carry = !!(adbc < ad); + uint64_t lo = bd + (adbc << 32); + *hi = __emulu((uint32_t)(ab >> 32), (uint32_t)(cd >> 32)) + (adbc >> 32) + + (adbc_carry << 32) + !!(lo < bd); + return lo; +} +#endif + +} // namespace jsoncharutils +} // unnamed namespace +} // namespace fallback +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_JSONCHARUTILS_H +/* end file simdjson/generic/jsoncharutils.h for fallback */ +/* including simdjson/generic/atomparsing.h for fallback: #include "simdjson/generic/atomparsing.h" */ +/* begin file simdjson/generic/atomparsing.h for fallback */ +#ifndef SIMDJSON_GENERIC_ATOMPARSING_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ATOMPARSING_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/jsoncharutils.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +#include + +namespace simdjson { +namespace fallback { +namespace { +/// @private +namespace atomparsing { + +// The string_to_uint32 is exclusively used to map literal strings to 32-bit values. +// We use memcpy instead of a pointer cast to avoid undefined behaviors since we cannot +// be certain that the character pointer will be properly aligned. +// You might think that using memcpy makes this function expensive, but you'd be wrong. +// All decent optimizing compilers (GCC, clang, Visual Studio) will compile string_to_uint32("false"); +// to the compile-time constant 1936482662. +simdjson_inline uint32_t string_to_uint32(const char* str) { uint32_t val; std::memcpy(&val, str, sizeof(uint32_t)); return val; } + + +// 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 fallback +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_ATOMPARSING_H +/* end file simdjson/generic/atomparsing.h for fallback */ +/* including simdjson/generic/dom_parser_implementation.h for fallback: #include "simdjson/generic/dom_parser_implementation.h" */ +/* begin file simdjson/generic/dom_parser_implementation.h for fallback */ +#ifndef SIMDJSON_GENERIC_DOM_PARSER_IMPLEMENTATION_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_DOM_PARSER_IMPLEMENTATION_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/internal/dom_parser_implementation.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { +namespace fallback { + +// expectation: sizeof(open_container) = 64/8. +struct open_container { + uint32_t tape_index; // where, on the tape, does the scope ([,{) begins + uint32_t count; // how many elements in the scope +}; // struct open_container + +static_assert(sizeof(open_container) == 64/8, "Open container must be 64 bits"); + +class dom_parser_implementation final : public internal::dom_parser_implementation { +public: + /** Tape location of each open { or [ */ + std::unique_ptr open_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 fallback +} // namespace simdjson + +namespace simdjson { +namespace fallback { + +inline dom_parser_implementation::dom_parser_implementation() noexcept = default; +inline dom_parser_implementation::dom_parser_implementation(dom_parser_implementation &&other) noexcept = default; +inline dom_parser_implementation &dom_parser_implementation::operator=(dom_parser_implementation &&other) noexcept = default; + +// Leaving these here so they can be inlined if so desired +inline simdjson_warn_unused error_code dom_parser_implementation::set_capacity(size_t capacity) noexcept { + if(capacity > SIMDJSON_MAXSIZE_BYTES) { 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 fallback +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_DOM_PARSER_IMPLEMENTATION_H +/* end file simdjson/generic/dom_parser_implementation.h for fallback */ +/* including simdjson/generic/implementation_simdjson_result_base.h for fallback: #include "simdjson/generic/implementation_simdjson_result_base.h" */ +/* begin file simdjson/generic/implementation_simdjson_result_base.h for fallback */ +#ifndef SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { +namespace fallback { + +// This is a near copy of include/error.h's implementation_simdjson_result_base, except it doesn't use std::pair +// so we can avoid inlining errors +// TODO reconcile these! +/** + * The result of a simdjson operation that could fail. + * + * Gives the option of reading error codes, or throwing an exception by casting to the desired result. + * + * This is a base class for implementations that want to add functions to the result type for + * chaining. + * + * Override like: + * + * struct simdjson_result : 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_inline error_code get(T &value) && noexcept; + + /** + * The error. + */ + simdjson_inline error_code error() const noexcept; + +#if SIMDJSON_EXCEPTIONS + + /** + * Get the result value. + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T& value() & noexcept(false); + + /** + * Take the result value (move it). + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T&& value() && noexcept(false); + + /** + * Take the result value (move it). + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T&& take_value() && noexcept(false); + + /** + * Cast to the value (will throw on error). + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline operator T&&() && noexcept(false); + + +#endif // SIMDJSON_EXCEPTIONS + + /** + * Get the result value. This function is safe if and only + * the error() method returns a value that evaluates to false. + */ + simdjson_inline const T& value_unsafe() const& noexcept; + /** + * 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 + +} // namespace fallback +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_H +/* end file simdjson/generic/implementation_simdjson_result_base.h for fallback */ +/* including simdjson/generic/numberparsing.h for fallback: #include "simdjson/generic/numberparsing.h" */ +/* begin file simdjson/generic/numberparsing.h for fallback */ +#ifndef SIMDJSON_GENERIC_NUMBERPARSING_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_NUMBERPARSING_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/jsoncharutils.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/internal/numberparsing_tables.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +#include +#include +#include + +namespace simdjson { +namespace fallback { +namespace numberparsing { + +#ifdef JSON_TEST_NUMBERS +#define INVALID_NUMBER(SRC) (found_invalid_number((SRC)), NUMBER_ERROR) +#define WRITE_INTEGER(VALUE, SRC, WRITER) (found_integer((VALUE), (SRC)), (WRITER).append_s64((VALUE))) +#define WRITE_UNSIGNED(VALUE, SRC, WRITER) (found_unsigned_integer((VALUE), (SRC)), (WRITER).append_u64((VALUE))) +#define WRITE_DOUBLE(VALUE, SRC, WRITER) (found_float((VALUE), (SRC)), (WRITER).append_double((VALUE))) +#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)) +#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 + // 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; + + + // 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. + + // 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. In very rare cases, even that + // will not suffice, though it is seemingly very hard to find such a scenario. + // + // 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++; } + // At this point, we might need to add at most one to firstproduct, but this + // can only change the value of firstproduct.high if firstproduct.low is maximal. + if(simdjson_unlikely(firstproduct.low == 0xFFFFFFFFFFFFFFFF)) { + // This is very unlikely, but if so, we need to do much more work! + return false; + } + } + 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 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_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 doesn't 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 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 */ +template +error_code slow_float_parsing(simdjson_unused const uint8_t * src, W writer) { + double d; + if (parse_float_fallback(src, &d)) { + writer.append_double(d); + 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: This makes a *copy* of the writer and passes it to slow_float_parsing. This happens + // because slow_float_parsing is a non-inlined function. 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. + // This is what forces the skip_double, as well. + error_code error = slow_float_parsing(src, writer); + writer.skip_double(); + 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; +} + +// 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 +} + +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_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 INVALID_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 INVALID_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 doesn't 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 = start_digits-src > 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++; } + if ( p == src ) { return NUMBER_ERROR; } + if (jsoncharutils::is_structural_or_whitespace(*p)) { + // We have an integer. + // If the number is negative and valid, it must be a signed integer. + if(negative) { return number_type::signed_integer; } + // We want values larger or equal to 9223372036854775808 to be unsigned + // integers, and the other values to be signed integers. + int digit_count = int(p - src); + if(digit_count >= 19) { + const uint8_t * smaller_big_integer = reinterpret_cast("9223372036854775808"); + if((digit_count >= 20) || (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 = start_digits-src > 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; + default: SIMDJSON_UNREACHABLE(); + } + return out; +} + +} // namespace fallback +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_NUMBERPARSING_H +/* end file simdjson/generic/numberparsing.h for fallback */ + +/* including simdjson/generic/implementation_simdjson_result_base-inl.h for fallback: #include "simdjson/generic/implementation_simdjson_result_base-inl.h" */ +/* begin file simdjson/generic/implementation_simdjson_result_base-inl.h for fallback */ +#ifndef SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_INL_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_INL_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { +namespace fallback { + +// +// internal::implementation_simdjson_result_base 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_inline error_code implementation_simdjson_result_base::error() const noexcept { + return this->second; +} + +#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::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 fallback +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_INL_H +/* end file simdjson/generic/implementation_simdjson_result_base-inl.h for fallback */ +/* end file simdjson/generic/amalgamated.h for fallback */ +/* including simdjson/fallback/end.h: #include "simdjson/fallback/end.h" */ +/* begin file simdjson/fallback/end.h */ +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #include "simdjson/fallback/base.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +/* undefining SIMDJSON_IMPLEMENTATION from "fallback" */ +#undef SIMDJSON_IMPLEMENTATION +/* end file simdjson/fallback/end.h */ + +#endif // SIMDJSON_FALLBACK_H +/* end file simdjson/fallback.h */ +/* including simdjson/fallback/implementation.h: #include */ +/* begin file simdjson/fallback/implementation.h */ +#ifndef SIMDJSON_FALLBACK_IMPLEMENTATION_H +#define SIMDJSON_FALLBACK_IMPLEMENTATION_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #include "simdjson/fallback/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/implementation.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { +namespace fallback { + +/** + * @private + */ +class implementation final : public simdjson::implementation { +public: + simdjson_inline implementation() : simdjson::implementation( + "fallback", + "Generic fallback implementation", + 0 + ) {} + simdjson_warn_unused error_code create_dom_parser_implementation( + size_t capacity, + size_t max_length, + std::unique_ptr& dst + ) const noexcept final; + simdjson_warn_unused error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept final; + simdjson_warn_unused bool validate_utf8(const char *buf, size_t len) const noexcept final; +}; + +} // namespace fallback +} // namespace simdjson + +#endif // SIMDJSON_FALLBACK_IMPLEMENTATION_H +/* end file simdjson/fallback/implementation.h */ + +/* including simdjson/fallback/begin.h: #include */ +/* 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 static 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] }; +} + +} // 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 +#ifdef _M_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 // _M_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 + +#define SIMDJSON_SWAR_NUMBER_PARSING 1 + +#endif // SIMDJSON_FALLBACK_NUMBERPARSING_DEFS_H +/* end file simdjson/fallback/numberparsing_defs.h */ +/* end file simdjson/fallback/begin.h */ +/* including generic/stage1/find_next_document_index.h for fallback: #include */ +/* begin file generic/stage1/find_next_document_index.h for fallback */ +#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 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; +} + +} // 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 */ +#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 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, + + 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 isn't 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 isn't 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_nullptrptr 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; + } + } + /* can't be reached */ + return nullptr; +} + +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; + } + } + /* can't be reached */ + return nullptr; +} + +} // namespace stringparsing +} // unnamed namespace +} // namespace fallback +} // namespace simdjson + +#endif // SIMDJSON_SRC_GENERIC_STAGE2_STRINGPARSING_H +/* end file generic/stage2/stringparsing.h for fallback */ +/* including generic/stage2/logger.h for fallback: #include */ +/* begin file generic/stage2/logger.h for fallback */ +#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 fallback { +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 fallback */ +#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 fallback { +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 { + switch (*value) { + case '"': return visitor.visit_string(*this, 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); + case '-': + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + return visitor.visit_number(*this, value); + default: + log_error("Non-value found when value was expected!"); + return TAPE_ERROR; + } +} + +} // namespace stage2 +} // unnamed namespace +} // namespace fallback +} // namespace simdjson + +#endif // SIMDJSON_SRC_GENERIC_STAGE2_JSON_ITERATOR_H +/* end file generic/stage2/json_iterator.h for fallback */ +/* including generic/stage2/tape_writer.h for fallback: #include */ +/* begin file generic/stage2/tape_writer.h for fallback */ +#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 fallback { +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 fallback +} // namespace simdjson + +#endif // SIMDJSON_SRC_GENERIC_STAGE2_TAPE_WRITER_H +/* end file generic/stage2/tape_writer.h for fallback */ +/* including generic/stage2/tape_builder.h for fallback: #include */ +/* begin file generic/stage2/tape_builder.h for fallback */ +#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 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; + + /** 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 fallback +} // namespace simdjson + +#endif // SIMDJSON_SRC_GENERIC_STAGE2_TAPE_BUILDER_H +/* end file generic/stage2/tape_builder.h for fallback */ + +// +// Stage 1 +// + +namespace simdjson { +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; +} + +namespace { +namespace stage1 { + +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 void add_structural() { + *next_structural_index = idx; + next_structural_index++; +} + +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; + } + + // 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; + } + + // 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; +} + +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; + } +} + +// +// Parse the entire input in STEP_SIZE-byte chunks. +// +simdjson_inline error_code scan() { + bool unclosed_string = false; + for (;idx 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; +} + +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 + +} // namespace stage1 +} // unnamed namespace + +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(); +} + +// 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 + +// +// Stage 2 +// + +namespace simdjson { +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_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_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); +} + +} // namespace fallback +} // namespace simdjson + +/* including simdjson/fallback/end.h: #include */ +/* 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_SRC_FALLBACK_CPP +/* end file fallback.cpp */ +#endif +#if SIMDJSON_IMPLEMENTATION_HASWELL +/* including haswell.cpp: #include */ +/* begin file haswell.cpp */ +#ifndef SIMDJSON_SRC_HASWELL_CPP +#define SIMDJSON_SRC_HASWELL_CPP + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #include */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +/* including simdjson/haswell.h: #include */ +/* begin file simdjson/haswell.h */ +#ifndef SIMDJSON_HASWELL_H +#define SIMDJSON_HASWELL_H + +/* including simdjson/haswell/begin.h: #include "simdjson/haswell/begin.h" */ +/* begin file simdjson/haswell/begin.h */ +/* defining SIMDJSON_IMPLEMENTATION to "haswell" */ +#define SIMDJSON_IMPLEMENTATION haswell + +/* including simdjson/haswell/base.h: #include "simdjson/haswell/base.h" */ +/* begin file simdjson/haswell/base.h */ +#ifndef SIMDJSON_HASWELL_BASE_H +#define SIMDJSON_HASWELL_BASE_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #include "simdjson/base.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +// The constructor may be executed on any host, so we take care not to use SIMDJSON_TARGET_HASWELL +namespace simdjson { +/** + * Implementation for Haswell (Intel AVX2). + */ +namespace haswell { + +class implementation; + +namespace { +namespace simd { +template 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 +SIMDJSON_TARGET_REGION("avx2,bmi,pclmul,lzcnt,popcnt") +#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 +#ifdef _M_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 // _M_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]; + + 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 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 ((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 + }; +} + +} // unnamed namespace +} // namespace haswell +} // namespace simdjson + +#endif // SIMDJSON_HASWELL_STRINGPARSING_DEFS_H +/* end file simdjson/haswell/stringparsing_defs.h */ +/* end file simdjson/haswell/begin.h */ +/* including simdjson/generic/amalgamated.h for haswell: #include "simdjson/generic/amalgamated.h" */ +/* begin file simdjson/generic/amalgamated.h for haswell */ +#if defined(SIMDJSON_CONDITIONAL_INCLUDE) && !defined(SIMDJSON_GENERIC_DEPENDENCIES_H) +#error simdjson/generic/dependencies.h must be included before simdjson/generic/amalgamated.h! +#endif + +/* including simdjson/generic/base.h for haswell: #include "simdjson/generic/base.h" */ +/* begin file simdjson/generic/base.h for haswell */ +#ifndef SIMDJSON_GENERIC_BASE_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_BASE_H */ +/* amalgamation skipped (editor-only): #include "simdjson/base.h" */ +/* amalgamation skipped (editor-only): // If we haven't got an implementation yet, we're in the editor, editing a generic file! Just */ +/* amalgamation skipped (editor-only): // use the most advanced one we can so the most possible stuff can be tested. */ +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_IMPLEMENTATION */ +/* amalgamation skipped (editor-only): #include "simdjson/implementation_detection.h" */ +/* amalgamation skipped (editor-only): #if SIMDJSON_IMPLEMENTATION_ICELAKE */ +/* amalgamation skipped (editor-only): #include "simdjson/icelake/begin.h" */ +/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_HASWELL */ +/* amalgamation skipped (editor-only): #include "simdjson/haswell/begin.h" */ +/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_WESTMERE */ +/* amalgamation skipped (editor-only): #include "simdjson/westmere/begin.h" */ +/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_ARM64 */ +/* amalgamation skipped (editor-only): #include "simdjson/arm64/begin.h" */ +/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_PPC64 */ +/* amalgamation skipped (editor-only): #include "simdjson/ppc64/begin.h" */ +/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_FALLBACK */ +/* amalgamation skipped (editor-only): #include "simdjson/fallback/begin.h" */ +/* amalgamation skipped (editor-only): #else */ +/* amalgamation skipped (editor-only): #error "All possible implementations (including fallback) have been disabled! simdjson will not run." */ +/* amalgamation skipped (editor-only): #endif */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_IMPLEMENTATION */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { +namespace haswell { + +struct open_container; +class dom_parser_implementation; + +/** + * The type of a JSON number + */ +enum class number_type { + floating_point_number=1, /// a binary64 number + signed_integer, /// a signed integer that fits in a 64-bit word using two's complement + unsigned_integer /// a positive integer larger or equal to 1<<63 +}; + +} // namespace haswell +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_BASE_H +/* end file simdjson/generic/base.h for haswell */ +/* including simdjson/generic/jsoncharutils.h for haswell: #include "simdjson/generic/jsoncharutils.h" */ +/* begin file simdjson/generic/jsoncharutils.h for haswell */ +#ifndef SIMDJSON_GENERIC_JSONCHARUTILS_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_JSONCHARUTILS_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/internal/jsoncharutils_tables.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/internal/numberparsing_tables.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { +namespace haswell { +namespace { +namespace jsoncharutils { + +// return non-zero if not a structural or whitespace char +// zero otherwise +simdjson_inline uint32_t is_not_structural_or_whitespace(uint8_t c) { + return internal::structural_or_whitespace_negated[c]; +} + +simdjson_inline uint32_t is_structural_or_whitespace(uint8_t c) { + return internal::structural_or_whitespace[c]; +} + +// returns a value with the high 16 bits set if not valid +// otherwise returns the conversion of the 4 hex digits at src into the bottom +// 16 bits of the 32-bit return register +// +// see +// https://lemire.me/blog/2019/04/17/parsing-short-hexadecimal-strings-efficiently/ +static inline uint32_t hex_to_u32_nocheck( + const uint8_t *src) { // strictly speaking, static inline is a C-ism + uint32_t v1 = internal::digit_to_val32[630 + src[0]]; + uint32_t v2 = internal::digit_to_val32[420 + src[1]]; + uint32_t v3 = internal::digit_to_val32[210 + src[2]]; + uint32_t v4 = internal::digit_to_val32[0 + src[3]]; + return v1 | v2 | v3 | v4; +} + +// given a code point cp, writes to c +// the utf-8 code, outputting the length in +// bytes, if the length is zero, the code point +// is invalid +// +// This can possibly be made faster using pdep +// and clz and table lookups, but JSON documents +// have few escaped code points, and the following +// function looks cheap. +// +// Note: we assume that surrogates are treated separately +// +simdjson_inline size_t codepoint_to_utf8(uint32_t cp, uint8_t *c) { + if (cp <= 0x7F) { + c[0] = uint8_t(cp); + return 1; // ascii + } + if (cp <= 0x7FF) { + c[0] = uint8_t((cp >> 6) + 192); + c[1] = uint8_t((cp & 63) + 128); + return 2; // universal plane + // Surrogates are treated elsewhere... + //} //else if (0xd800 <= cp && cp <= 0xdfff) { + // return 0; // surrogates // could put assert here + } else if (cp <= 0xFFFF) { + c[0] = uint8_t((cp >> 12) + 224); + c[1] = uint8_t(((cp >> 6) & 63) + 128); + c[2] = uint8_t((cp & 63) + 128); + return 3; + } else if (cp <= 0x10FFFF) { // if you know you have a valid code point, this + // is not needed + c[0] = uint8_t((cp >> 18) + 240); + c[1] = uint8_t(((cp >> 12) & 63) + 128); + c[2] = uint8_t(((cp >> 6) & 63) + 128); + c[3] = uint8_t((cp & 63) + 128); + return 4; + } + // will return 0 when the code point was too large. + return 0; // bad r +} + +#if SIMDJSON_IS_32BITS // _umul128 for x86, arm +// this is a slow emulation routine for 32-bit +// +static simdjson_inline uint64_t __emulu(uint32_t x, uint32_t y) { + return x * (uint64_t)y; +} +static simdjson_inline uint64_t _umul128(uint64_t ab, uint64_t cd, uint64_t *hi) { + uint64_t ad = __emulu((uint32_t)(ab >> 32), (uint32_t)cd); + uint64_t bd = __emulu((uint32_t)ab, (uint32_t)cd); + uint64_t adbc = ad + __emulu((uint32_t)ab, (uint32_t)(cd >> 32)); + uint64_t adbc_carry = !!(adbc < ad); + uint64_t lo = bd + (adbc << 32); + *hi = __emulu((uint32_t)(ab >> 32), (uint32_t)(cd >> 32)) + (adbc >> 32) + + (adbc_carry << 32) + !!(lo < bd); + return lo; +} +#endif + +} // namespace jsoncharutils +} // unnamed namespace +} // namespace haswell +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_JSONCHARUTILS_H +/* end file simdjson/generic/jsoncharutils.h for haswell */ +/* including simdjson/generic/atomparsing.h for haswell: #include "simdjson/generic/atomparsing.h" */ +/* begin file simdjson/generic/atomparsing.h for haswell */ +#ifndef SIMDJSON_GENERIC_ATOMPARSING_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ATOMPARSING_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/jsoncharutils.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +#include + +namespace simdjson { +namespace haswell { +namespace { +/// @private +namespace atomparsing { + +// The string_to_uint32 is exclusively used to map literal strings to 32-bit values. +// We use memcpy instead of a pointer cast to avoid undefined behaviors since we cannot +// be certain that the character pointer will be properly aligned. +// You might think that using memcpy makes this function expensive, but you'd be wrong. +// All decent optimizing compilers (GCC, clang, Visual Studio) will compile string_to_uint32("false"); +// to the compile-time constant 1936482662. +simdjson_inline uint32_t string_to_uint32(const char* str) { uint32_t val; std::memcpy(&val, str, sizeof(uint32_t)); return val; } + + +// 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 haswell +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_ATOMPARSING_H +/* end file simdjson/generic/atomparsing.h for haswell */ +/* including simdjson/generic/dom_parser_implementation.h for haswell: #include "simdjson/generic/dom_parser_implementation.h" */ +/* begin file simdjson/generic/dom_parser_implementation.h for haswell */ +#ifndef SIMDJSON_GENERIC_DOM_PARSER_IMPLEMENTATION_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_DOM_PARSER_IMPLEMENTATION_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/internal/dom_parser_implementation.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { +namespace haswell { + +// expectation: sizeof(open_container) = 64/8. +struct open_container { + uint32_t tape_index; // where, on the tape, does the scope ([,{) begins + uint32_t count; // how many elements in the scope +}; // struct open_container + +static_assert(sizeof(open_container) == 64/8, "Open container must be 64 bits"); + +class dom_parser_implementation final : public internal::dom_parser_implementation { +public: + /** Tape location of each open { or [ */ + std::unique_ptr open_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 haswell +} // namespace simdjson + +namespace simdjson { +namespace haswell { + +inline dom_parser_implementation::dom_parser_implementation() noexcept = default; +inline dom_parser_implementation::dom_parser_implementation(dom_parser_implementation &&other) noexcept = default; +inline dom_parser_implementation &dom_parser_implementation::operator=(dom_parser_implementation &&other) noexcept = default; + +// Leaving these here so they can be inlined if so desired +inline simdjson_warn_unused error_code dom_parser_implementation::set_capacity(size_t capacity) noexcept { + if(capacity > SIMDJSON_MAXSIZE_BYTES) { 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 haswell +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_DOM_PARSER_IMPLEMENTATION_H +/* end file simdjson/generic/dom_parser_implementation.h for haswell */ +/* including simdjson/generic/implementation_simdjson_result_base.h for haswell: #include "simdjson/generic/implementation_simdjson_result_base.h" */ +/* begin file simdjson/generic/implementation_simdjson_result_base.h for haswell */ +#ifndef SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { +namespace haswell { + +// This is a near copy of include/error.h's implementation_simdjson_result_base, except it doesn't use std::pair +// so we can avoid inlining errors +// TODO reconcile these! +/** + * The result of a simdjson operation that could fail. + * + * Gives the option of reading error codes, or throwing an exception by casting to the desired result. + * + * This is a base class for implementations that want to add functions to the result type for + * chaining. + * + * Override like: + * + * struct simdjson_result : 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_inline error_code get(T &value) && noexcept; + + /** + * The error. + */ + simdjson_inline error_code error() const noexcept; + +#if SIMDJSON_EXCEPTIONS + + /** + * Get the result value. + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T& value() & noexcept(false); + + /** + * Take the result value (move it). + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T&& value() && noexcept(false); + + /** + * Take the result value (move it). + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T&& take_value() && noexcept(false); + + /** + * Cast to the value (will throw on error). + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline operator T&&() && noexcept(false); + + +#endif // SIMDJSON_EXCEPTIONS + + /** + * Get the result value. This function is safe if and only + * the error() method returns a value that evaluates to false. + */ + simdjson_inline const T& value_unsafe() const& noexcept; + /** + * 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 + +} // namespace haswell +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_H +/* end file simdjson/generic/implementation_simdjson_result_base.h for haswell */ +/* including simdjson/generic/numberparsing.h for haswell: #include "simdjson/generic/numberparsing.h" */ +/* begin file simdjson/generic/numberparsing.h for haswell */ +#ifndef SIMDJSON_GENERIC_NUMBERPARSING_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_NUMBERPARSING_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/jsoncharutils.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/internal/numberparsing_tables.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +#include +#include +#include + +namespace simdjson { +namespace haswell { +namespace numberparsing { + +#ifdef JSON_TEST_NUMBERS +#define INVALID_NUMBER(SRC) (found_invalid_number((SRC)), NUMBER_ERROR) +#define WRITE_INTEGER(VALUE, SRC, WRITER) (found_integer((VALUE), (SRC)), (WRITER).append_s64((VALUE))) +#define WRITE_UNSIGNED(VALUE, SRC, WRITER) (found_unsigned_integer((VALUE), (SRC)), (WRITER).append_u64((VALUE))) +#define WRITE_DOUBLE(VALUE, SRC, WRITER) (found_float((VALUE), (SRC)), (WRITER).append_double((VALUE))) +#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)) +#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 + // 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; + + + // 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. + + // 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. In very rare cases, even that + // will not suffice, though it is seemingly very hard to find such a scenario. + // + // 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++; } + // At this point, we might need to add at most one to firstproduct, but this + // can only change the value of firstproduct.high if firstproduct.low is maximal. + if(simdjson_unlikely(firstproduct.low == 0xFFFFFFFFFFFFFFFF)) { + // This is very unlikely, but if so, we need to do much more work! + return false; + } + } + 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 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_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 doesn't 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 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 */ +template +error_code slow_float_parsing(simdjson_unused const uint8_t * src, W writer) { + double d; + if (parse_float_fallback(src, &d)) { + writer.append_double(d); + 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: This makes a *copy* of the writer and passes it to slow_float_parsing. This happens + // because slow_float_parsing is a non-inlined function. 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. + // This is what forces the skip_double, as well. + error_code error = slow_float_parsing(src, writer); + writer.skip_double(); + 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; +} + +// 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 +} + +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_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 INVALID_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 INVALID_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 doesn't 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 = start_digits-src > 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++; } + if ( p == src ) { return NUMBER_ERROR; } + if (jsoncharutils::is_structural_or_whitespace(*p)) { + // We have an integer. + // If the number is negative and valid, it must be a signed integer. + if(negative) { return number_type::signed_integer; } + // We want values larger or equal to 9223372036854775808 to be unsigned + // integers, and the other values to be signed integers. + int digit_count = int(p - src); + if(digit_count >= 19) { + const uint8_t * smaller_big_integer = reinterpret_cast("9223372036854775808"); + if((digit_count >= 20) || (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 = start_digits-src > 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; + default: SIMDJSON_UNREACHABLE(); + } + return out; +} + +} // namespace haswell +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_NUMBERPARSING_H +/* end file simdjson/generic/numberparsing.h for haswell */ + +/* including simdjson/generic/implementation_simdjson_result_base-inl.h for haswell: #include "simdjson/generic/implementation_simdjson_result_base-inl.h" */ +/* begin file simdjson/generic/implementation_simdjson_result_base-inl.h for haswell */ +#ifndef SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_INL_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_INL_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { +namespace haswell { + +// +// internal::implementation_simdjson_result_base 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_inline error_code implementation_simdjson_result_base::error() const noexcept { + return this->second; +} + +#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::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 haswell +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_INL_H +/* end file simdjson/generic/implementation_simdjson_result_base-inl.h for haswell */ +/* end file simdjson/generic/amalgamated.h for haswell */ +/* including simdjson/haswell/end.h: #include "simdjson/haswell/end.h" */ +/* begin file simdjson/haswell/end.h */ +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #include "simdjson/haswell/base.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +#if !SIMDJSON_CAN_ALWAYS_RUN_HASWELL +SIMDJSON_UNTARGET_REGION +#endif + +/* undefining SIMDJSON_IMPLEMENTATION from "haswell" */ +#undef SIMDJSON_IMPLEMENTATION +/* end file simdjson/haswell/end.h */ + +#endif // SIMDJSON_HASWELL_H +/* end file simdjson/haswell.h */ +/* including simdjson/haswell/implementation.h: #include */ +/* begin file simdjson/haswell/implementation.h */ +#ifndef SIMDJSON_HASWELL_IMPLEMENTATION_H +#define SIMDJSON_HASWELL_IMPLEMENTATION_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #include "simdjson/haswell/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/implementation.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/internal/instruction_set.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +// The constructor may be executed on any host, so we take care not to use SIMDJSON_TARGET_HASWELL +namespace simdjson { +namespace haswell { + +/** + * @private + */ +class implementation final : public simdjson::implementation { +public: + simdjson_inline implementation() : simdjson::implementation( + "haswell", + "Intel/AMD AVX2", + internal::instruction_set::AVX2 | internal::instruction_set::PCLMULQDQ | internal::instruction_set::BMI1 | internal::instruction_set::BMI2 + ) {} + simdjson_warn_unused error_code create_dom_parser_implementation( + size_t capacity, + size_t max_length, + std::unique_ptr& dst + ) const noexcept final; + simdjson_warn_unused error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept final; + simdjson_warn_unused bool validate_utf8(const char *buf, size_t len) const noexcept final; +}; + +} // namespace haswell +} // namespace simdjson + +#endif // SIMDJSON_HASWELL_IMPLEMENTATION_H +/* end file simdjson/haswell/implementation.h */ + +/* including simdjson/haswell/begin.h: #include */ +/* 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 +SIMDJSON_TARGET_REGION("avx2,bmi,pclmul,lzcnt,popcnt") +#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 +#ifdef _M_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 // _M_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]; + + 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 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 ((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 + }; +} + +} // 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 generic/amalgamated.h for haswell: #include */ +/* begin file generic/amalgamated.h for haswell */ +#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 haswell: #include */ +/* begin file generic/base.h for haswell */ +#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 haswell { +namespace { + +struct json_character_block; + +} // unnamed namespace +} // namespace haswell +} // namespace simdjson + +#endif // SIMDJSON_SRC_GENERIC_BASE_H +/* end file generic/base.h for haswell */ +/* including generic/dom_parser_implementation.h for haswell: #include */ +/* begin file generic/dom_parser_implementation.h for haswell */ +#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 haswell { +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 haswell +} // namespace simdjson + +#endif // SIMDJSON_SRC_GENERIC_DOM_PARSER_IMPLEMENTATION_H +/* end file generic/dom_parser_implementation.h for haswell */ +/* including generic/json_character_block.h for haswell: #include */ +/* begin file generic/json_character_block.h for haswell */ +#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 haswell { +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 haswell +} // namespace simdjson + +#endif // SIMDJSON_SRC_GENERIC_JSON_CHARACTER_BLOCK_H +/* end file generic/json_character_block.h for haswell */ +/* end file generic/amalgamated.h for haswell */ +/* including generic/stage1/amalgamated.h for haswell: #include */ +/* begin file generic/stage1/amalgamated.h for haswell */ +// Stuff other things depend on +/* including generic/stage1/base.h for haswell: #include */ +/* begin file generic/stage1/base.h for haswell */ +#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 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; + +} // namespace stage1 + +namespace utf8_validation { +struct utf8_checker; +} // namespace utf8_validation + +using utf8_validation::utf8_checker; + +} // unnamed namespace +} // namespace haswell +} // namespace simdjson + +#endif // SIMDJSON_SRC_GENERIC_STAGE1_BASE_H +/* end file generic/stage1/base.h for haswell */ +/* including generic/stage1/buf_block_reader.h for haswell: #include */ +/* begin file generic/stage1/buf_block_reader.h for haswell */ +#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 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; +}; + +// 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 haswell +} // namespace simdjson + +#endif // SIMDJSON_SRC_GENERIC_STAGE1_BUF_BLOCK_READER_H +/* end file generic/stage1/buf_block_reader.h for haswell */ +/* including generic/stage1/json_escape_scanner.h for haswell: #include */ +/* begin file generic/stage1/json_escape_scanner.h for haswell */ +#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 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; + + 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 haswell +} // namespace simdjson + +#endif // SIMDJSON_SRC_GENERIC_STAGE1_JSON_STRING_SCANNER_H +/* end file generic/stage1/json_escape_scanner.h for haswell */ +/* including generic/stage1/json_string_scanner.h for haswell: #include */ +/* begin file generic/stage1/json_string_scanner.h for haswell */ +#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 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) {} + + // 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 haswell +} // namespace simdjson + +#endif // SIMDJSON_SRC_GENERIC_STAGE1_JSON_STRING_SCANNER_H +/* end file generic/stage1/json_string_scanner.h for haswell */ +/* including generic/stage1/utf8_lookup4_algorithm.h for haswell: #include */ +/* begin file generic/stage1/utf8_lookup4_algorithm.h for haswell */ +#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 haswell { +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 = simd8(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 + }; +#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; + } + +#ifndef SIMDJSON_IF_CONSTEXPR +#if SIMDJSON_CPLUSPLUS17 +#define SIMDJSON_IF_CONSTEXPR if constexpr +#else +#define SIMDJSON_IF_CONSTEXPR if +#endif +#endif + + 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]); + } 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; + } + + }; // struct utf8_checker +} // namespace utf8_validation + +} // unnamed namespace +} // namespace haswell +} // namespace simdjson + +#endif // SIMDJSON_SRC_GENERIC_STAGE1_UTF8_LOOKUP4_ALGORITHM_H +/* end file generic/stage1/utf8_lookup4_algorithm.h for haswell */ +/* including generic/stage1/json_scanner.h for haswell: #include */ +/* begin file generic/stage1/json_scanner.h for haswell */ +#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 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) {} + + /** + * 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_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_inline error_code json_scanner::finish() { + return string_scanner.finish(); +} + +} // namespace stage1 +} // unnamed namespace +} // namespace haswell +} // namespace simdjson + +#endif // SIMDJSON_SRC_GENERIC_STAGE1_JSON_SCANNER_H +/* end file generic/stage1/json_scanner.h for haswell */ + +// All other declarations +/* including generic/stage1/find_next_document_index.h for haswell: #include */ +/* begin file generic/stage1/find_next_document_index.h for haswell */ +#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 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; +} + +} // namespace stage1 +} // unnamed namespace +} // namespace haswell +} // namespace simdjson + +#endif // SIMDJSON_SRC_GENERIC_STAGE1_FIND_NEXT_DOCUMENT_INDEX_H +/* end file generic/stage1/find_next_document_index.h for haswell */ +/* including generic/stage1/json_minifier.h for haswell: #include */ +/* begin file generic/stage1/json_minifier.h for haswell */ +#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 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; + +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; +}; + +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; +} + +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 haswell +} // namespace simdjson + +#endif // SIMDJSON_SRC_GENERIC_STAGE1_JSON_MINIFIER_H +/* end file generic/stage1/json_minifier.h for haswell */ +/* including generic/stage1/json_structural_indexer.h for haswell: #include */ +/* begin file generic/stage1/json_structural_indexer.h for haswell */ +#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 haswell { +namespace { +namespace stage1 { + +class bit_indexer { +public: + uint32_t *tail; + + simdjson_inline bit_indexer(uint32_t *index_buf) : tail(index_buf) {} + + // 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; +#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. + */ + + uint64_t rev_bits = reverse_bits(bits); + int cnt = static_cast(count_ones(bits)); + int i = 0; + // Do the first 8 all together + for (; i<8; i++) { + int lz = leading_zeroes(rev_bits); + this->tail[i] = static_cast(idx) + lz; + rev_bits = zero_leading_bit(rev_bits, lz); + } + // Do the next 8 all together (we hope in most cases it won't happen at all + // and the branch is easily predicted). + if (simdjson_unlikely(cnt > 8)) { + i = 8; + for (; i<16; i++) { + int lz = leading_zeroes(rev_bits); + this->tail[i] = static_cast(idx) + lz; + rev_bits = zero_leading_bit(rev_bits, lz); + } + + + // Most files don't have 16+ structurals per block, so we take several basically guaranteed + // branch mispredictions here. 16+ structurals per block means either punctuation ({} [] , :) + // or the start of a value ("abc" true 123) every four characters. + if (simdjson_unlikely(cnt > 16)) { + i = 16; + while (rev_bits != 0) { + int lz = leading_zeroes(rev_bits); + this->tail[i++] = static_cast(idx) + lz; + rev_bits = zero_leading_bit(rev_bits, lz); + } + } + } + this->tail += cnt; +#else // SIMDJSON_PREFER_REVERSE_BITS + /** + * 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. + */ + + int cnt = static_cast(count_ones(bits)); + // Do the first 8 all together + for (int i=0; i<8; i++) { + this->tail[i] = idx + trailing_zeroes(bits); + bits = clear_lowest_bit(bits); + } + + // Do the next 8 all together (we hope in most cases it won't happen at all + // and the branch is easily predicted). + if (simdjson_unlikely(cnt > 8)) { + for (int i=8; i<16; i++) { + this->tail[i] = idx + trailing_zeroes(bits); + bits = clear_lowest_bit(bits); + } + + // Most files don't have 16+ structurals per block, so we take several basically guaranteed + // branch mispredictions here. 16+ structurals per block means either punctuation ({} [] , :) + // or the start of a value ("abc" true 123) every four characters. + if (simdjson_unlikely(cnt > 16)) { + int i = 16; + do { + this->tail[i] = idx + trailing_zeroes(bits); + bits = clear_lowest_bit(bits); + i++; + } while (i < cnt); + } + } + + this->tail += cnt; +#endif + } +#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_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 doesn't 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 haswell +} // 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 haswell */ +/* including generic/stage1/utf8_validator.h for haswell: #include */ +/* begin file generic/stage1/utf8_validator.h for haswell */ +#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 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; +} + +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 simdjson + +#endif // SIMDJSON_SRC_GENERIC_STAGE1_UTF8_VALIDATOR_H +/* end file generic/stage1/utf8_validator.h for haswell */ +/* end file generic/stage1/amalgamated.h for haswell */ +/* including generic/stage2/amalgamated.h for haswell: #include */ +/* begin file generic/stage2/amalgamated.h for haswell */ +// Stuff other things depend on +/* including generic/stage2/base.h for haswell: #include */ +/* begin file generic/stage2/base.h for haswell */ +#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 haswell { +namespace { +namespace stage2 { + +class json_iterator; +class structural_iterator; +struct tape_builder; +struct tape_writer; + +} // namespace stage2 +} // unnamed namespace +} // namespace haswell +} // namespace simdjson + +#endif // SIMDJSON_SRC_GENERIC_STAGE2_BASE_H +/* end file generic/stage2/base.h for haswell */ +/* including generic/stage2/tape_writer.h for haswell: #include */ +/* begin file generic/stage2/tape_writer.h for haswell */ +#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 haswell { +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 haswell +} // namespace simdjson + +#endif // SIMDJSON_SRC_GENERIC_STAGE2_TAPE_WRITER_H +/* end file generic/stage2/tape_writer.h for haswell */ +/* including generic/stage2/logger.h for haswell: #include */ +/* begin file generic/stage2/logger.h for haswell */ +#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 haswell { +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 haswell */ +#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 haswell { +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 { + switch (*value) { + case '"': return visitor.visit_string(*this, 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); + case '-': + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + return visitor.visit_number(*this, value); + default: + log_error("Non-value found when value was expected!"); + return TAPE_ERROR; + } +} + +} // 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 */ +#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 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, + + 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 isn't 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 isn't 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_nullptrptr 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; + } + } + /* can't be reached */ + return nullptr; +} + +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; + } + } + /* can't be reached */ + return nullptr; +} + +} // namespace stringparsing +} // unnamed namespace +} // namespace haswell +} // namespace simdjson + +#endif // SIMDJSON_SRC_GENERIC_STAGE2_STRINGPARSING_H +/* end file generic/stage2/stringparsing.h for haswell */ +/* including generic/stage2/structural_iterator.h for haswell: #include */ +/* begin file generic/stage2/structural_iterator.h for haswell */ +#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 haswell { +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 haswell +} // namespace simdjson + +#endif // SIMDJSON_SRC_GENERIC_STAGE2_STRUCTURAL_ITERATOR_H +/* end file generic/stage2/structural_iterator.h for haswell */ +/* including generic/stage2/tape_builder.h for haswell: #include */ +/* begin file generic/stage2/tape_builder.h for haswell */ +#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 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; -#include + /** 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; -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}; + /** 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; -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, -}; + /** 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; -// 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] + /** + * 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; -} // namespace internal -} // namespace simdjson + 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; -#endif // SIMDJSON_IMPLEMENTATION_ARM64 || SIMDJSON_IMPLEMENTATION_ICELAKE || SIMDJSON_IMPLEMENTATION_HASWELL || SIMDJSON_IMPLEMENTATION_WESTMERE || SIMDJSON_IMPLEMENTATION_PPC64 -/* end file src/internal/simdprune_tables.cpp */ -/* begin file src/implementation.cpp */ -#include + 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; -namespace simdjson { + /** 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; -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); -} + /** 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; -namespace internal { + simdjson_inline tape_builder(dom::document &doc) noexcept; -// Static array of known implementations. We're hoping these get baked into the executable -// without requiring a static initializer. + 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 -#if SIMDJSON_IMPLEMENTATION_ICELAKE -static const icelake::implementation* get_icelake_singleton() { - static const icelake::implementation icelake_singleton{}; - return &icelake_singleton; +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); } -#endif -#if SIMDJSON_IMPLEMENTATION_HASWELL -static const haswell::implementation* get_haswell_singleton() { - static const haswell::implementation haswell_singleton{}; - return &haswell_singleton; + +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); } -#endif -#if SIMDJSON_IMPLEMENTATION_WESTMERE -static const westmere::implementation* get_westmere_singleton() { - static const westmere::implementation westmere_singleton{}; - return &westmere_singleton; +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); } -#endif // SIMDJSON_IMPLEMENTATION_WESTMERE -#if SIMDJSON_IMPLEMENTATION_ARM64 -static const arm64::implementation* get_arm64_singleton() { - static const arm64::implementation arm64_singleton{}; - return &arm64_singleton; +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); } -#endif // SIMDJSON_IMPLEMENTATION_ARM64 -#if SIMDJSON_IMPLEMENTATION_PPC64 -static const ppc64::implementation* get_ppc64_singleton() { - static const ppc64::implementation ppc64_singleton{}; - return &ppc64_singleton; +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); } -#endif // SIMDJSON_IMPLEMENTATION_PPC64 -#if SIMDJSON_IMPLEMENTATION_FALLBACK -static const fallback::implementation* get_fallback_singleton() { - static const fallback::implementation fallback_singleton{}; - return &fallback_singleton; +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); } -#endif // SIMDJSON_IMPLEMENTATION_FALLBACK -/** - * @private Detects best supported implementation on first use, and sets it - */ -class detect_best_supported_implementation_on_first_use final : public implementation { -public: - const std::string &name() const noexcept final { return set_best()->name(); } - const 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_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; } - 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; -}; + on_end_string(dst); + return SUCCESS; +} -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_FALLBACK - get_fallback_singleton(), -#endif - }; // available_implementation_pointers - return available_implementation_pointers; +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); } -// 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) {} -}; +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); +} -const unsupported_implementation* get_unsupported_singleton() { - static const unsupported_implementation unsupported_singleton{}; - return &unsupported_singleton; +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; } -size_t available_implementation_list::size() const noexcept { - return internal::get_available_implementation_pointers().size(); +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; } -const implementation * const *available_implementation_list::begin() const noexcept { - return internal::get_available_implementation_pointers().begin(); + +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; } -const implementation * const *available_implementation_list::end() const noexcept { - return internal::get_available_implementation_pointers().end(); + +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; } -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? + +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; } -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 +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; +} - 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_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; } -} // namespace internal +// private: -SIMDJSON_DLLIMPORTEXPORT const internal::available_implementation_list& get_available_implementations() { - static const internal::available_implementation_list available_implementations{}; - return available_implementations; +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_DLLIMPORTEXPORT internal::atomic_ptr& get_active_implementation() { - static const internal::detect_best_supported_implementation_on_first_use detect_best_supported_implementation_on_first_use_singleton; - static internal::atomic_ptr active_implementation{&detect_best_supported_implementation_on_first_use_singleton}; - return active_implementation; +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 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_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 bool validate_utf8(const char *buf, size_t len) noexcept { - return get_active_implementation()->validate_utf8(buf, len); + +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; } -const implementation * builtin_implementation() { - static const implementation * builtin_impl = get_available_implementations()[SIMDJSON_STRINGIFY(SIMDJSON_BUILTIN_IMPLEMENTATION)]; - assert(builtin_impl); - return builtin_impl; + +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 haswell } // namespace simdjson -/* end file src/implementation.cpp */ -#if SIMDJSON_IMPLEMENTATION_ARM64 -/* begin file src/arm64/implementation.cpp */ -/* begin file include/simdjson/arm64/begin.h */ -// redefining SIMDJSON_IMPLEMENTATION to "arm64" -// #define SIMDJSON_IMPLEMENTATION arm64 -/* end file include/simdjson/arm64/begin.h */ +#endif // SIMDJSON_SRC_GENERIC_STAGE2_TAPE_BUILDER_H +/* end file generic/stage2/tape_builder.h for haswell */ +/* end file generic/stage2/amalgamated.h for haswell */ + +// +// Stage 1 +// namespace simdjson { -namespace arm64 { +namespace haswell { simdjson_warn_unused error_code implementation::create_dom_parser_implementation( size_t capacity, @@ -2826,3549 +23730,5011 @@ simdjson_warn_unused error_code implementation::create_dom_parser_implementation return SUCCESS; } -} // namespace arm64 -} // namespace simdjson - -/* begin file include/simdjson/arm64/end.h */ -/* end file include/simdjson/arm64/end.h */ -/* end file src/arm64/implementation.cpp */ -/* begin file src/arm64/dom_parser_implementation.cpp */ -/* begin file include/simdjson/arm64/begin.h */ -// redefining SIMDJSON_IMPLEMENTATION to "arm64" -// #define SIMDJSON_IMPLEMENTATION arm64 -/* end file include/simdjson/arm64/begin.h */ - -// -// Stage 1 -// -namespace simdjson { -namespace arm64 { namespace { using namespace simd; -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; -}; - +// 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) { - // 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); + // 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); - 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) + // 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 + // 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). *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(); + // minifying (we only need whitespace). - 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(); + 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 }; } simdjson_inline bool is_ascii(const simd8x64& input) { - simd8 bits = input.reduce_or(); - return bits.max_val() < 0x80u; + 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 >= 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; + 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 >= uint8_t(0xe0u); - simd8 is_fourth_byte = prev3 >= uint8_t(0xf0u); - return is_third_byte ^ is_fourth_byte; + 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_third_byte | is_fourth_byte) > int8_t(0); } } // unnamed namespace -} // namespace arm64 +} // namespace haswell } // namespace simdjson -/* begin file src/generic/stage1/utf8_lookup4_algorithm.h */ +// +// Stage 2 +// + +// +// Implementation-specific overrides +// namespace simdjson { -namespace arm64 { -namespace { -namespace utf8_validation { +namespace haswell { -using namespace simd; +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_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_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); +} - 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, +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_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_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); +} + +} // namespace haswell +} // namespace simdjson + +/* including simdjson/haswell/end.h: #include */ +/* 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_SRC_HASWELL_CPP +/* end file haswell.cpp */ +#endif +#if SIMDJSON_IMPLEMENTATION_ICELAKE +/* including icelake.cpp: #include */ +/* begin file icelake.cpp */ +#ifndef SIMDJSON_SRC_ICELAKE_CPP +#define SIMDJSON_SRC_ICELAKE_CPP + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #include */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +/* including simdjson/icelake.h: #include */ +/* begin file simdjson/icelake.h */ +#ifndef SIMDJSON_ICELAKE_H +#define SIMDJSON_ICELAKE_H + +/* including simdjson/icelake/begin.h: #include "simdjson/icelake/begin.h" */ +/* begin file simdjson/icelake/begin.h */ +/* defining SIMDJSON_IMPLEMENTATION to "icelake" */ +#define SIMDJSON_IMPLEMENTATION icelake +/* including simdjson/icelake/base.h: #include "simdjson/icelake/base.h" */ +/* begin file simdjson/icelake/base.h */ +#ifndef SIMDJSON_ICELAKE_BASE_H +#define SIMDJSON_ICELAKE_BASE_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #include "simdjson/base.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +// The constructor may be executed on any host, so we take care not to use SIMDJSON_TARGET_ICELAKE +namespace simdjson { +/** + * Implementation for Icelake (Intel AVX512). + */ +namespace icelake { - // ____0100 ________ - CARRY | TOO_LARGE, - // ____0101 ________ - CARRY | TOO_LARGE | TOO_LARGE_1000, - // ____011_ ________ - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, +class implementation; - // ____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, +} // namespace icelake +} // namespace simdjson - // ________ 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, +#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 - // ________ 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 = simd8(must_be_2_3_continuation(prev2, prev3)); - simd8 must23_80 = must23 & uint8_t(0x80); - return must23_80 ^ sc; - } +/* 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 */ - // - // 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 - }; +#if SIMDJSON_VISUAL_STUDIO +// under clang within visual studio, this will include +#include // visual studio or clang #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 - }; +#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 - 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; +/* 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 - // - // 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); - } +/* 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 */ - // 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; - } +namespace simdjson { +namespace icelake { +namespace { -#ifndef SIMDJSON_IF_CONSTEXPR -#if SIMDJSON_CPLUSPLUS17 -#define SIMDJSON_IF_CONSTEXPR if constexpr +// 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 -#define SIMDJSON_IF_CONSTEXPR if +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 +} - 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]); - } 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; - } +} // unnamed namespace +} // namespace icelake +} // namespace simdjson - }; // struct utf8_checker -} // namespace utf8_validation +#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 -using utf8_validation::utf8_checker; +/* 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 arm64 +} // namespace icelake } // namespace simdjson -/* end file src/generic/stage1/utf8_lookup4_algorithm.h */ -/* begin file src/generic/stage1/json_structural_indexer.h */ -// 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) -/* begin file src/generic/stage1/buf_block_reader.h */ +#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 arm64 { +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; } + }; -// 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; -}; + // Forward-declared so they can be used by splat and friends. + template + struct simd8; -// 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; -} + template> + struct base8: base> { + typedef uint32_t bitmask_t; + typedef uint64_t bitmask2_t; -// 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_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 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 + )); + } + }; + + // 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]; + + 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_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_inline uint64_t eq(const T m) const { + const simd8 mask = simd8::splat(m); + return this->chunks[0] == mask; + } -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} {} + simdjson_inline uint64_t eq(const simd8x64 &other) const { + return this->chunks[0] == other.chunks[0]; + } -template -simdjson_inline size_t buf_block_reader::block_index() { return idx; } + simdjson_inline uint64_t lteq(const T m) const { + const simd8 mask = simd8::splat(m); + return this->chunks[0] <= mask; + } + }; // struct simd8x64 -template -simdjson_inline bool buf_block_reader::has_full_block() const { - return idx < lenminusstep; -} +} // namespace simd -template -simdjson_inline const uint8_t *buf_block_reader::full_block() const { - return &buf[idx]; -} +} // unnamed namespace +} // namespace icelake +} // namespace simdjson -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; -} +#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 -template -simdjson_inline void buf_block_reader::advance() { - idx += STEP_SIZE; +/* 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 = 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 ((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 + }; } } // unnamed namespace -} // namespace arm64 +} // namespace icelake } // namespace simdjson -/* end file src/generic/stage1/buf_block_reader.h */ -/* begin file src/generic/stage1/json_string_scanner.h */ + +#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 arm64 { -namespace { -namespace stage1 { +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 +#ifdef _M_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 // _M_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; +} -struct json_string_block { - // We spell out the constructors in the hope of resolving inlining issues with Visual Studio 2017 - simdjson_inline json_string_block(uint64_t backslash, uint64_t escaped, uint64_t quote, uint64_t in_string) : - _backslash(backslash), _escaped(escaped), _quote(quote), _in_string(in_string) {} +} // namespace numberparsing +} // namespace icelake +} // namespace simdjson - // Escaped characters (characters following an escape() character) - simdjson_inline uint64_t escaped() const { return _escaped; } - // Escape characters (backslashes that are not escaped--i.e. in \\, includes only the first \) - simdjson_inline uint64_t escape() const { return _backslash & ~_escaped; } - // Real (non-backslashed) quotes - simdjson_inline uint64_t quote() const { return _quote; } - // Start quotes of strings - simdjson_inline uint64_t string_start() const { return _quote & _in_string; } - // End quotes of strings - simdjson_inline uint64_t string_end() const { return _quote & ~_in_string; } - // Only characters inside the string (not including the quotes) - simdjson_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_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_inline uint64_t non_quote_outside_string(uint64_t mask) const { return mask & ~_in_string; } - // Tail of string (everything except the start quote) - simdjson_inline uint64_t string_tail() const { return _in_string ^ _quote; } +#define SIMDJSON_SWAR_NUMBER_PARSING 1 - // backslash characters - uint64_t _backslash; - // escaped characters (backslashed--does not include the hex characters after \u) - uint64_t _escaped; - // real quotes (non-backslashed ones) - uint64_t _quote; - // string characters (includes start quote but not end quote) - uint64_t _in_string; -}; +#endif // SIMDJSON_ICELAKE_NUMBERPARSING_DEFS_H +/* end file simdjson/icelake/numberparsing_defs.h */ +/* end file simdjson/icelake/begin.h */ +/* including simdjson/generic/amalgamated.h for icelake: #include "simdjson/generic/amalgamated.h" */ +/* begin file simdjson/generic/amalgamated.h for icelake */ +#if defined(SIMDJSON_CONDITIONAL_INCLUDE) && !defined(SIMDJSON_GENERIC_DEPENDENCIES_H) +#error simdjson/generic/dependencies.h must be included before simdjson/generic/amalgamated.h! +#endif -// Scans blocks for string characters, storing the state necessary to do so -class json_string_scanner { -public: - simdjson_inline json_string_block next(const simd::simd8x64& in); - // Returns either UNCLOSED_STRING or SUCCESS - simdjson_inline error_code finish(); +/* including simdjson/generic/base.h for icelake: #include "simdjson/generic/base.h" */ +/* begin file simdjson/generic/base.h for icelake */ +#ifndef SIMDJSON_GENERIC_BASE_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_BASE_H */ +/* amalgamation skipped (editor-only): #include "simdjson/base.h" */ +/* amalgamation skipped (editor-only): // If we haven't got an implementation yet, we're in the editor, editing a generic file! Just */ +/* amalgamation skipped (editor-only): // use the most advanced one we can so the most possible stuff can be tested. */ +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_IMPLEMENTATION */ +/* amalgamation skipped (editor-only): #include "simdjson/implementation_detection.h" */ +/* amalgamation skipped (editor-only): #if SIMDJSON_IMPLEMENTATION_ICELAKE */ +/* amalgamation skipped (editor-only): #include "simdjson/icelake/begin.h" */ +/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_HASWELL */ +/* amalgamation skipped (editor-only): #include "simdjson/haswell/begin.h" */ +/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_WESTMERE */ +/* amalgamation skipped (editor-only): #include "simdjson/westmere/begin.h" */ +/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_ARM64 */ +/* amalgamation skipped (editor-only): #include "simdjson/arm64/begin.h" */ +/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_PPC64 */ +/* amalgamation skipped (editor-only): #include "simdjson/ppc64/begin.h" */ +/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_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 */ -private: - // Intended to be defined by the implementation - simdjson_inline uint64_t find_escaped(uint64_t escape); - simdjson_inline uint64_t find_escaped_branchless(uint64_t escape); +namespace simdjson { +namespace icelake { - // Whether the last iteration was still inside a string (all 1's = true, all 0's = false). - uint64_t prev_in_string = 0ULL; - // Whether the first character of the next iteration is escaped. - uint64_t prev_escaped = 0ULL; +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 }; -// -// Finds escaped characters (characters following \). -// -// Handles runs of backslashes like \\\" and \\\\" correctly (yielding 0101 and 01010, respectively). -// -// Does this by: -// - Shift the escape mask to get potentially escaped characters (characters after backslashes). -// - Mask escaped sequences that start on *even* bits with 1010101010 (odd bits are escaped, even bits are not) -// - Mask escaped sequences that start on *odd* bits with 0101010101 (even bits are escaped, odd bits are not) -// -// To distinguish between escaped sequences starting on even/odd bits, it finds the start of all -// escape sequences, filters out the ones that start on even bits, and adds that to the mask of -// escape sequences. This causes the addition to clear out the sequences starting on odd bits (since -// the start bit causes a carry), and leaves even-bit sequences alone. -// -// Example: -// -// text | \\\ | \\\"\\\" \\\" \\"\\" | -// escape | xxx | xx xxx xxx xx xx | Removed overflow backslash; will | it into follows_escape -// odd_starts | x | x x x | escape & ~even_bits & ~follows_escape -// even_seq | c| cxxx c xx c | c = carry bit -- will be masked out later -// invert_mask | | cxxx c xx c| even_seq << 1 -// follows_escape | xx | x xx xxx xxx xx xx | Includes overflow bit -// escaped | x | x x x x x x x x | -// desired | x | x x x x x x x x | -// text | \\\ | \\\"\\\" \\\" \\"\\" | -// -simdjson_inline uint64_t json_string_scanner::find_escaped_branchless(uint64_t backslash) { - // If there was overflow, pretend the first character isn't a backslash - backslash &= ~prev_escaped; - uint64_t follows_escape = backslash << 1 | prev_escaped; +} // namespace icelake +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_BASE_H +/* end file simdjson/generic/base.h for icelake */ +/* including simdjson/generic/jsoncharutils.h for icelake: #include "simdjson/generic/jsoncharutils.h" */ +/* begin file simdjson/generic/jsoncharutils.h for icelake */ +#ifndef SIMDJSON_GENERIC_JSONCHARUTILS_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_JSONCHARUTILS_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/internal/jsoncharutils_tables.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/internal/numberparsing_tables.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { +namespace icelake { +namespace { +namespace jsoncharutils { - // Get sequences starting on even bits by clearing out the odd series using + - const uint64_t even_bits = 0x5555555555555555ULL; - uint64_t odd_sequence_starts = backslash & ~even_bits & ~follows_escape; - uint64_t sequences_starting_on_even_bits; - prev_escaped = add_overflow(odd_sequence_starts, backslash, &sequences_starting_on_even_bits); - uint64_t invert_mask = sequences_starting_on_even_bits << 1; // The mask we want to return is the *escaped* bits, not escapes. +// 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]; +} - // Mask every other backslashed character as an escaped character - // Flip the mask for sequences that start on even bits, to correct them - return (even_bits ^ invert_mask) & follows_escape; +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 // -// Return a mask of all string characters plus end quotes. +// 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 // -// prev_escaped is overflow saying whether the next character is escaped. -// prev_in_string is overflow saying whether we're still in a string. +// 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. // -// Backslash sequences outside of quotes will be detected in stage 2. +// Note: we assume that surrogates are treated separately // -simdjson_inline json_string_block json_string_scanner::next(const simd::simd8x64& in) { - const uint64_t backslash = in.eq('\\'); - const uint64_t escaped = find_escaped(backslash); - 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 - // - // right shift of a signed value expected to be well-defined and standard - // compliant as of C++20, John Regher from Utah U. says this is fine code - // - 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( - backslash, - escaped, - quote, - in_string - ); +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 } -simdjson_inline error_code json_string_scanner::finish() { - if (prev_in_string) { - return UNCLOSED_STRING; - } - return SUCCESS; +#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 stage1 +} // namespace jsoncharutils } // unnamed namespace -} // namespace arm64 +} // namespace icelake } // namespace simdjson -/* end file src/generic/stage1/json_string_scanner.h */ -/* begin file src/generic/stage1/json_scanner.h */ -namespace simdjson { -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) {} +#endif // SIMDJSON_GENERIC_JSONCHARUTILS_H +/* end file simdjson/generic/jsoncharutils.h for icelake */ +/* including simdjson/generic/atomparsing.h for icelake: #include "simdjson/generic/atomparsing.h" */ +/* begin file simdjson/generic/atomparsing.h for icelake */ +#ifndef SIMDJSON_GENERIC_ATOMPARSING_H - /** - * 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()); } +/* 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 */ - // Helpers +#include - /** 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); } +namespace simdjson { +namespace icelake { +namespace { +/// @private +namespace atomparsing { - // 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) +// 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; } - /** - * 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(); +// 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); +} -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{}; -}; +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; +} -// -// 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_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_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 bool is_valid_null_atom(const uint8_t *src) { + return (str4ncmp(src, "null") | jsoncharutils::is_not_structural_or_whitespace(src[4])) == 0; } -simdjson_inline error_code json_scanner::finish() { - return string_scanner.finish(); +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 stage1 +} // namespace atomparsing } // unnamed namespace -} // namespace arm64 +} // namespace icelake } // namespace simdjson -/* end file src/generic/stage1/json_scanner.h */ -/* begin file src/generic/stage1/json_minifier.h */ -// 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) + +#endif // SIMDJSON_GENERIC_ATOMPARSING_H +/* end file simdjson/generic/atomparsing.h for icelake */ +/* including simdjson/generic/dom_parser_implementation.h for icelake: #include "simdjson/generic/dom_parser_implementation.h" */ +/* begin file simdjson/generic/dom_parser_implementation.h for icelake */ +#ifndef SIMDJSON_GENERIC_DOM_PARSER_IMPLEMENTATION_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_DOM_PARSER_IMPLEMENTATION_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/internal/dom_parser_implementation.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { -namespace arm64 { -namespace { -namespace stage1 { +namespace icelake { -class json_minifier { -public: - template - static error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) noexcept; +// 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 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; + simdjson_inline simdjson_warn_unused error_code set_capacity_stage1(size_t capacity); + }; -simdjson_inline void json_minifier::next(const simd::simd8x64& in, const json_block& block) { - uint64_t mask = block.whitespace(); - dst += in.compress(mask, dst); -} +} // namespace icelake +} // namespace simdjson -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; +namespace simdjson { +namespace icelake { + +inline dom_parser_implementation::dom_parser_implementation() noexcept = default; +inline dom_parser_implementation::dom_parser_implementation(dom_parser_implementation &&other) noexcept = default; +inline dom_parser_implementation &dom_parser_implementation::operator=(dom_parser_implementation &&other) noexcept = default; + +// Leaving these here so they can be inlined if so desired +inline simdjson_warn_unused error_code dom_parser_implementation::set_capacity(size_t capacity) noexcept { + if(capacity > SIMDJSON_MAXSIZE_BYTES) { 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; } -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(); -} +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; } -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(); + _max_depth = max_depth; + return SUCCESS; } -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); +} // namespace icelake +} // namespace simdjson - // Index the first n-1 blocks - while (reader.has_full_block()) { - minifier.step(reader.full_block(), reader); - } +#endif // SIMDJSON_GENERIC_DOM_PARSER_IMPLEMENTATION_H +/* end file simdjson/generic/dom_parser_implementation.h for icelake */ +/* including simdjson/generic/implementation_simdjson_result_base.h for icelake: #include "simdjson/generic/implementation_simdjson_result_base.h" */ +/* begin file simdjson/generic/implementation_simdjson_result_base.h for icelake */ +#ifndef SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_H - // 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); -} +/* 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 stage1 -} // unnamed namespace -} // namespace arm64 +namespace simdjson { +namespace icelake { + +// This is a near copy of include/error.h's implementation_simdjson_result_base, except it doesn't use std::pair +// so we can avoid inlining errors +// TODO reconcile these! +/** + * The result of a simdjson operation that could fail. + * + * Gives the option of reading error codes, or throwing an exception by casting to the desired result. + * + * This is a base class for implementations that want to add functions to the result type for + * chaining. + * + * Override like: + * + * struct simdjson_result : 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_inline error_code get(T &value) && noexcept; + + /** + * The error. + */ + simdjson_inline error_code error() const noexcept; + +#if SIMDJSON_EXCEPTIONS + + /** + * Get the result value. + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T& value() & noexcept(false); + + /** + * Take the result value (move it). + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T&& value() && noexcept(false); + + /** + * Take the result value (move it). + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T&& take_value() && noexcept(false); + + /** + * Cast to the value (will throw on error). + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline operator T&&() && noexcept(false); + + +#endif // SIMDJSON_EXCEPTIONS + + /** + * Get the result value. This function is safe if and only + * the error() method returns a value that evaluates to false. + */ + simdjson_inline const T& value_unsafe() const& noexcept; + /** + * 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 + +} // namespace icelake } // namespace simdjson -/* end file src/generic/stage1/json_minifier.h */ -/* begin file src/generic/stage1/find_next_document_index.h */ + +#endif // SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_H +/* end file simdjson/generic/implementation_simdjson_result_base.h for icelake */ +/* including simdjson/generic/numberparsing.h for icelake: #include "simdjson/generic/numberparsing.h" */ +/* begin file simdjson/generic/numberparsing.h for icelake */ +#ifndef SIMDJSON_GENERIC_NUMBERPARSING_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_NUMBERPARSING_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/jsoncharutils.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/internal/numberparsing_tables.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +#include +#include +#include + namespace simdjson { -namespace arm64 { +namespace icelake { +namespace numberparsing { + +#ifdef JSON_TEST_NUMBERS +#define INVALID_NUMBER(SRC) (found_invalid_number((SRC)), NUMBER_ERROR) +#define WRITE_INTEGER(VALUE, SRC, WRITER) (found_integer((VALUE), (SRC)), (WRITER).append_s64((VALUE))) +#define WRITE_UNSIGNED(VALUE, SRC, WRITER) (found_unsigned_integer((VALUE), (SRC)), (WRITER).append_u64((VALUE))) +#define WRITE_DOUBLE(VALUE, SRC, WRITER) (found_float((VALUE), (SRC)), (WRITER).append_double((VALUE))) +#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)) +#endif + namespace { -/** - * 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; +// 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]; } - auto idxa = parser.structural_indexes[i - 1]; - switch (parser.buf[idxa]) { - case '{': - case '[': - case ':': - case ',': - continue; + if (negative) { + d = -d; } - // Last document is complete, so the next document will appear after! - if (!arr_cnt && !obj_cnt) { - return parser.n_structural_indexes; + 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 + // 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; + + + // 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. + + // 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. In very rare cases, even that + // will not suffice, though it is seemingly very hard to find such a scenario. + // + // 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++; } + // At this point, we might need to add at most one to firstproduct, but this + // can only change the value of firstproduct.high if firstproduct.low is maximal. + if(simdjson_unlikely(firstproduct.low == 0xFFFFFFFFFFFFFFFF)) { + // This is very unlikely, but if so, we need to do much more work! + return false; } - // 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; + 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 + } } - if (!arr_cnt && !obj_cnt) { - // We have a complete document. - return parser.n_structural_indexes; + + 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++; } - return 0; + 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; } -} // unnamed namespace -} // namespace arm64 -} // namespace simdjson -/* end file src/generic/stage1/find_next_document_index.h */ +// 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()); +} -namespace simdjson { -namespace arm64 { -namespace { -namespace stage1 { +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. -class bit_indexer { -public: - uint32_t *tail; + // 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 bit_indexer(uint32_t *index_buf) : tail(index_buf) {} +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; +} - // 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 +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 doesn't 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 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 */ +template +error_code slow_float_parsing(simdjson_unused const uint8_t * src, W writer) { + double d; + if (parse_float_fallback(src, &d)) { + writer.append_double(d); + 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. // - // If the kernel sets SIMDJSON_CUSTOM_BIT_INDEXER, then it will provide its own - // version of the code. -#ifdef SIMDJSON_CUSTOM_BIT_INDEXER - simdjson_inline void write(uint32_t idx, uint64_t bits); + // 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: This makes a *copy* of the writer and passes it to slow_float_parsing. This happens + // because slow_float_parsing is a non-inlined function. 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. + // This is what forces the skip_double, as well. + error_code error = slow_float_parsing(src, writer); + writer.skip_double(); + 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; +} + +// 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 +} + +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 - 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; -#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. - */ - uint64_t rev_bits = reverse_bits(bits); - int cnt = static_cast(count_ones(bits)); - int i = 0; - // Do the first 8 all together - for (; i<8; i++) { - int lz = leading_zeroes(rev_bits); - this->tail[i] = static_cast(idx) + lz; - rev_bits = zero_leading_bit(rev_bits, lz); - } - // Do the next 8 all together (we hope in most cases it won't happen at all - // and the branch is easily predicted). - if (simdjson_unlikely(cnt > 8)) { - i = 8; - for (; i<16; i++) { - int lz = leading_zeroes(rev_bits); - this->tail[i] = static_cast(idx) + lz; - rev_bits = zero_leading_bit(rev_bits, lz); - } +// 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) { + // + // Check for minus sign + // + bool negative = (*src == '-'); + const uint8_t *p = src + uint8_t(negative); - // Most files don't have 16+ structurals per block, so we take several basically guaranteed - // branch mispredictions here. 16+ structurals per block means either punctuation ({} [] , :) - // or the start of a value ("abc" true 123) every four characters. - if (simdjson_unlikely(cnt > 16)) { - i = 16; - while (rev_bits != 0) { - int lz = leading_zeroes(rev_bits); - this->tail[i++] = static_cast(idx) + lz; - rev_bits = zero_leading_bit(rev_bits, lz); - } - } - } - this->tail += cnt; -#else // SIMDJSON_PREFER_REVERSE_BITS - /** - * 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. - */ + // + // Parse the integer part. + // + // PERF NOTE: we don't use 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++; } - int cnt = static_cast(count_ones(bits)); - // Do the first 8 all together - for (int i=0; i<8; i++) { - this->tail[i] = idx + trailing_zeroes(bits); - bits = clear_lowest_bit(bits); - } + // If there were no digits, or if the integer starts with 0 and has more than 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); } - // Do the next 8 all together (we hope in most cases it won't happen at all - // and the branch is easily predicted). - if (simdjson_unlikely(cnt > 8)) { - for (int i=8; i<16; i++) { - this->tail[i] = idx + trailing_zeroes(bits); - bits = clear_lowest_bit(bits); - } + // + // 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 INVALID_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 INVALID_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); } + } - // Most files don't have 16+ structurals per block, so we take several basically guaranteed - // branch mispredictions here. 16+ structurals per block means either punctuation ({} [] , :) - // or the start of a value ("abc" true 123) every four characters. - if (simdjson_unlikely(cnt > 16)) { - int i = 16; - do { - this->tail[i] = idx + trailing_zeroes(bits); - bits = clear_lowest_bit(bits); - i++; - } while (i < cnt); - } - } + // Write unsigned if it doesn't 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; +} - this->tail += cnt; -#endif +// 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; } } -#endif // SIMDJSON_CUSTOM_BIT_INDEXER -}; + return i; +} -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); +// 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; - json_scanner scanner{}; - utf8_checker checker{}; - bit_indexer indexer; - uint64_t prev_structurals = 0; - uint64_t unescaped_chars_error = 0; -}; + // + // Parse the integer part. + // + // PERF 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); -simdjson_inline json_structural_indexer::json_structural_indexer(uint32_t *structural_indexes) : indexer{structural_indexes} {} + // + // 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; } -// 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; + // + // 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 = start_digits-src > 19; } + } else { + overflow = p-src > 19; } - 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 doesn't 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; } + // + // 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; } - 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); + 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; } } - // 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); + if (!parse_float_fallback(src - uint8_t(negative), &d)) { + return NUMBER_ERROR; + } + return d; } -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(); +simdjson_unused simdjson_inline bool is_negative(const uint8_t * src) noexcept { + return (*src == '-'); } -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_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_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_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++; } + if ( p == src ) { return NUMBER_ERROR; } + if (jsoncharutils::is_structural_or_whitespace(*p)) { + // We have an integer. + // If the number is negative and valid, it must be a signed integer. + if(negative) { return number_type::signed_integer; } + // We want values larger or equal to 9223372036854775808 to be unsigned + // integers, and the other values to be signed integers. + int digit_count = int(p - src); + if(digit_count >= 19) { + const uint8_t * smaller_big_integer = reinterpret_cast("9223372036854775808"); + if((digit_count >= 20) || (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; } -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; } +// 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); - 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; - } - } + // + // 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; } - 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; + // + // 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; } - checker.check_eof(); - return checker.errors(); -} -} // namespace stage1 -} // unnamed namespace -} // namespace arm64 -} // namespace simdjson -/* end file src/generic/stage1/json_structural_indexer.h */ -/* begin file src/generic/stage1/utf8_validator.h */ -namespace simdjson { -namespace arm64 { -namespace { -namespace stage1 { + // + // 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 == '+'; -/** - * 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; -} + 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; } -bool generic_validate_utf8(const char * input, size_t length) { - return generic_validate_utf8(reinterpret_cast(input),length); + 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; } -} // namespace stage1 -} // unnamed namespace -} // namespace arm64 -} // namespace simdjson -/* end file src/generic/stage1/utf8_validator.h */ +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; -// -// Stage 2 -// + // + // 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; } -/* begin file src/generic/stage2/stringparsing.h */ -// This file contains the common code every implementation uses -// It is intended to be included multiple times and compiled multiple times + // + // 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 = start_digits-src > 19; + } + } else { + overflow = p-src > 19; + } -namespace simdjson { -namespace arm64 { -namespace { -/// @private -namespace stringparsing { + // + // Parse the exponent + // + if (*p == 'e' || *p == 'E') { + p++; + bool exp_neg = *p == '-'; + p += exp_neg || *p == '+'; -// 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, + 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; } - 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. + exponent += exp_neg ? 0-exp : exp; + } - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + if (*p != '"') { return NUMBER_ERROR; } - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -}; + overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power; -// 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 isn't 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; + // + // 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; +} - // 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); +} // unnamed namespace +#endif // SIMDJSON_SKIPNUMBERPARSING - // 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; - } +} // 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; + default: SIMDJSON_UNREACHABLE(); } - } 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; + return out; } +} // namespace icelake +} // namespace simdjson -// 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 isn't 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; - } - } - } +#endif // SIMDJSON_GENERIC_NUMBERPARSING_H +/* end file simdjson/generic/numberparsing.h for icelake */ - size_t offset = jsoncharutils::codepoint_to_utf8(code_point, *dst_ptr); - *dst_ptr += offset; - return offset > 0; -} +/* including simdjson/generic/implementation_simdjson_result_base-inl.h for icelake: #include "simdjson/generic/implementation_simdjson_result_base-inl.h" */ +/* begin file simdjson/generic/implementation_simdjson_result_base-inl.h for icelake */ +#ifndef SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_INL_H +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_INL_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ -/** - * 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_nullptrptr 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; - } +namespace simdjson { +namespace icelake { + +// +// 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; } - /* can't be reached */ - return nullptr; } -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; - } - } - /* can't be reached */ - return nullptr; +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; +} + +#if SIMDJSON_EXCEPTIONS + +template +simdjson_inline T& implementation_simdjson_result_base::value() & noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return this->first; } -} // namespace stringparsing -} // unnamed namespace -} // namespace arm64 +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 icelake } // namespace simdjson -/* end file src/generic/stage2/stringparsing.h */ -/* begin file src/generic/stage2/tape_builder.h */ -/* begin file src/generic/stage2/json_iterator.h */ -/* begin file src/generic/stage2/logger.h */ -// This is for an internal-only stage 2 specific logger. -// Set LOG_ENABLED = true to log what stage 2 is doing! + +#endif // SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_INL_H +/* end file simdjson/generic/implementation_simdjson_result_base-inl.h for icelake */ +/* end file simdjson/generic/amalgamated.h for icelake */ +/* including simdjson/icelake/end.h: #include "simdjson/icelake/end.h" */ +/* begin file simdjson/icelake/end.h */ +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #include "simdjson/icelake/base.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +#if !SIMDJSON_CAN_ALWAYS_RUN_ICELAKE +SIMDJSON_UNTARGET_REGION +#endif + +/* undefining SIMDJSON_IMPLEMENTATION from "icelake" */ +#undef SIMDJSON_IMPLEMENTATION +/* end file simdjson/icelake/end.h */ + +#endif // SIMDJSON_ICELAKE_H +/* end file simdjson/icelake.h */ +/* including simdjson/icelake/implementation.h: #include */ +/* begin file simdjson/icelake/implementation.h */ +#ifndef SIMDJSON_ICELAKE_IMPLEMENTATION_H +#define SIMDJSON_ICELAKE_IMPLEMENTATION_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #include "simdjson/icelake/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/implementation.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/internal/instruction_set.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +// The constructor may be executed on any host, so we take care not to use SIMDJSON_TARGET_ICELAKE namespace simdjson { -namespace arm64 { -namespace { -namespace logger { +namespace icelake { - static constexpr const char * DASHES = "----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"; +/** + * @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; +}; -#if SIMDJSON_VERBOSE_LOGGING - static constexpr const bool LOG_ENABLED = true; +} // namespace icelake +} // namespace simdjson + +#endif // SIMDJSON_ICELAKE_IMPLEMENTATION_H +/* end file simdjson/icelake/implementation.h */ + +// defining SIMDJSON_GENERIC_JSON_STRUCTURAL_INDEXER_CUSTOM_BIT_INDEXER allows us to provide our own bit_indexer::write +#define SIMDJSON_GENERIC_JSON_STRUCTURAL_INDEXER_CUSTOM_BIT_INDEXER + +/* including simdjson/icelake/begin.h: #include */ +/* 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 - static constexpr const bool LOG_ENABLED = false; +#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 - 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. +/* 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 - // Helper to turn unprintable or newline characters into spaces - static simdjson_inline char printable_char(char c) { - if (c >= 0x20) { - return c; - } else { - return ' '; - } - } +/* 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 */ - // 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); - } - } +namespace simdjson { +namespace icelake { +namespace { - simdjson_unused static simdjson_inline void log_string(const char *message) { - if (LOG_ENABLED) { - printf("%s\n", message); - } - } +// 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 - // 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(result)); +#else + return __builtin_uaddll_overflow(value1, value2, + reinterpret_cast(result)); +#endif +} -} // namespace logger } // unnamed namespace -} // namespace arm64 +} // namespace icelake } // namespace simdjson -/* end file src/generic/stage2/logger.h */ -namespace simdjson { -namespace arm64 { -namespace { -namespace stage2 { +#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 -class json_iterator { -public: - const uint8_t* const buf; - uint32_t *next_structural; - dom_parser_implementation &dom_parser; - uint32_t depth{0}; +/* 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 */ - /** - * 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; +namespace simdjson { +namespace icelake { +namespace { - /** - * 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); +// +// 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); +} - /** - * 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; +} // unnamed namespace +} // namespace icelake +} // namespace simdjson - /** - * 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; +#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 - 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) ); +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; } + }; - // - // Read first value - // - { - auto value = advance(); + // Forward-declared so they can be used by splat and friends. + template + struct simd8; - // 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; - } - } + template> + struct base8: base> { + typedef uint32_t bitmask_t; + typedef uint64_t bitmask2_t; - 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; + 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); } - } - 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) ); + static const int SIZE = sizeof(base::value); - { - 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) ); - } + 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); + } + }; -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; + // 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 + ); } - } -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; - } + simdjson_inline base8_numeric() : base8() {} + simdjson_inline base8_numeric(const __m512i _value) : base8(_value) {} -scope_end: - depth--; - if (depth == 0) { goto document_end; } - if (dom_parser.is_array[depth]) { goto array_continue; } - goto object_continue; + // Store to array + simdjson_inline void store(T dst[64]) const { return _mm512_storeu_si512(reinterpret_cast<__m512i *>(dst), *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) ); + // 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); } -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; + // 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); } - } -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; - } + // 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); + } -document_end: - log_end_value("document"); - SIMDJSON_TRY( visitor.visit_document_end(*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 + )); + } + }; - dom_parser.next_structural_index = uint32_t(next_structural - &dom_parser.structural_indexes[0]); + // 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 + ); + } - // 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; - } + // 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); } - return SUCCESS; + 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))); } + }; -} // walk_document() + // 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 + ); + } -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} { -} + // 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)); } + }; -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); -} + 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 + + 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 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 store(T ptr[64]) const { + this->chunks[0].store(ptr+sizeof(simd8)*0); + } -simdjson_inline void json_iterator::log_value(const char *type) const noexcept { - logger::log_line(*this, "", type, ""); -} + simdjson_inline simd8 reduce_or() const { + return this->chunks[0]; + } -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 simd8x64 bit_or(const T m) const { + const simd8 mask = simd8::splat(m); + return simd8x64( + this->chunks[0] | mask + ); + } -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 uint64_t eq(const T m) const { + const simd8 mask = simd8::splat(m); + return this->chunks[0] == mask; + } -simdjson_inline void json_iterator::log_error(const char *error) const noexcept { - logger::log_line(*this, "", "ERROR", error); -} + simdjson_inline uint64_t eq(const simd8x64 &other) const { + return this->chunks[0] == other.chunks[0]; + } -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 { - switch (*value) { - case '"': return visitor.visit_string(*this, 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); - case '-': - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - return visitor.visit_number(*this, value); - default: - log_error("Non-value found when value was expected!"); - return TAPE_ERROR; - } -} + 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 stage2 } // unnamed namespace -} // namespace arm64 +} // namespace icelake } // namespace simdjson -/* end file src/generic/stage2/json_iterator.h */ -/* begin file src/generic/stage2/tape_writer.h */ + +#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 arm64 { +namespace icelake { 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; +using namespace simd; - /** Write an unsigned 64-bit value to tape. */ - simdjson_inline void append_u64(uint64_t value) noexcept; +// 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 ((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 + }; +} - /** Write a double value to tape. */ - simdjson_inline void append_double(double value) noexcept; +} // unnamed namespace +} // namespace icelake +} // namespace simdjson - /** - * 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; +#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 - /** - * 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; +/* 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 */ - /** - * Skip the number of tape entries necessary to write a large u64 or i64. - */ - simdjson_inline void skip_large_integer() noexcept; +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 +#ifdef _M_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 // _M_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; +} - /** - * Skip the number of tape entries necessary to write a double. - */ - simdjson_inline void skip_double() noexcept; +} // namespace numberparsing +} // namespace icelake +} // namespace simdjson - /** - * 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; +#define SIMDJSON_SWAR_NUMBER_PARSING 1 -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 number_writer +#endif // SIMDJSON_ICELAKE_NUMBERPARSING_DEFS_H +/* end file simdjson/icelake/numberparsing_defs.h */ +/* end file simdjson/icelake/begin.h */ +/* including generic/amalgamated.h for icelake: #include */ +/* begin file generic/amalgamated.h for icelake */ +#if defined(SIMDJSON_CONDITIONAL_INCLUDE) && !defined(SIMDJSON_SRC_GENERIC_DEPENDENCIES_H) +#error generic/dependencies.h must be included before generic/amalgamated.h! +#endif -simdjson_inline void tape_writer::append_s64(int64_t value) noexcept { - append2(0, value, internal::tape_type::INT64); -} +/* including generic/base.h for icelake: #include */ +/* begin file generic/base.h for icelake */ +#ifndef SIMDJSON_SRC_GENERIC_BASE_H -simdjson_inline void tape_writer::append_u64(uint64_t value) noexcept { - append(0, internal::tape_type::UINT64); - *next_tape_loc = value; - next_tape_loc++; -} +/* 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 */ -/** Write a double value to tape. */ -simdjson_inline void tape_writer::append_double(double value) noexcept { - append2(0, value, internal::tape_type::DOUBLE); -} +namespace simdjson { +namespace icelake { +namespace { -simdjson_inline void tape_writer::skip() noexcept { - next_tape_loc++; -} +struct json_character_block; -simdjson_inline void tape_writer::skip_large_integer() noexcept { - next_tape_loc += 2; -} +} // unnamed namespace +} // namespace icelake +} // namespace simdjson -simdjson_inline void tape_writer::skip_double() noexcept { - next_tape_loc += 2; -} +#endif // SIMDJSON_SRC_GENERIC_BASE_H +/* end file generic/base.h for icelake */ +/* including generic/dom_parser_implementation.h for icelake: #include */ +/* begin file generic/dom_parser_implementation.h for icelake */ +#ifndef SIMDJSON_SRC_GENERIC_DOM_PARSER_IMPLEMENTATION_H -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++; -} +/* 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 */ -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++; -} +// Interface a dom parser implementation must fulfill +namespace simdjson { +namespace icelake { +namespace { -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 simd8 must_be_2_3_continuation(const simd8 prev2, const simd8 prev3); +simdjson_inline bool is_ascii(const simd8x64& input); -} // namespace stage2 } // unnamed namespace -} // namespace arm64 +} // namespace icelake } // namespace simdjson -/* end file src/generic/stage2/tape_writer.h */ + +#endif // SIMDJSON_SRC_GENERIC_DOM_PARSER_IMPLEMENTATION_H +/* end file generic/dom_parser_implementation.h for icelake */ +/* including generic/json_character_block.h for icelake: #include */ +/* begin file generic/json_character_block.h for icelake */ +#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 arm64 { +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 json_character_block { + static simdjson_inline json_character_block classify(const simd::simd8x64& in); - /** 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; + 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()); } - /** 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; + uint64_t _whitespace; + uint64_t _op; +}; - /** 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; +} // unnamed namespace +} // namespace icelake +} // namespace simdjson - /** - * 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; +#endif // SIMDJSON_SRC_GENERIC_JSON_CHARACTER_BLOCK_H +/* end file generic/json_character_block.h for icelake */ +/* end file generic/amalgamated.h for icelake */ +/* including generic/stage1/amalgamated.h for icelake: #include */ +/* begin file generic/stage1/amalgamated.h for icelake */ +// Stuff other things depend on +/* including generic/stage1/base.h for icelake: #include */ +/* begin file generic/stage1/base.h for icelake */ +#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 */ - 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; +namespace simdjson { +namespace icelake { +namespace { +namespace stage1 { - 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; +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; - /** 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; +} // namespace stage1 - /** 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; +namespace utf8_validation { +struct utf8_checker; +} // namespace utf8_validation - simdjson_inline tape_builder(dom::document &doc) noexcept; +using utf8_validation::utf8_checker; - 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; -}; // class tape_builder +} // unnamed namespace +} // namespace icelake +} // namespace simdjson -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); -} +#endif // SIMDJSON_SRC_GENERIC_STAGE1_BASE_H +/* end file generic/stage1/base.h for icelake */ +/* including generic/stage1/buf_block_reader.h for icelake: #include */ +/* begin file generic/stage1/buf_block_reader.h for icelake */ +#ifndef SIMDJSON_SRC_GENERIC_STAGE1_BUF_BLOCK_READER_H -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); -} +/* 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 */ -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; -} +#include -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; +namespace simdjson { +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; +}; + +// 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; } -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); + +// 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_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_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_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; +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' : ' '; } - on_end_string(dst); - return SUCCESS; + buf[64] = '\0'; + return buf; } -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); -} +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} {} -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); -} +template +simdjson_inline size_t buf_block_reader::block_index() { return idx; } -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; +template +simdjson_inline bool buf_block_reader::has_full_block() const { + return idx < lenminusstep; } -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; +template +simdjson_inline const uint8_t *buf_block_reader::full_block() const { + return &buf[idx]; } -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; +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; } -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; +template +simdjson_inline void buf_block_reader::advance() { + idx += STEP_SIZE; } -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; -} +} // namespace stage1 +} // unnamed namespace +} // namespace icelake +} // namespace simdjson -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; -} +#endif // SIMDJSON_SRC_GENERIC_STAGE1_BUF_BLOCK_READER_H +/* end file generic/stage1/buf_block_reader.h for icelake */ +/* including generic/stage1/json_escape_scanner.h for icelake: #include */ +/* begin file generic/stage1/json_escape_scanner.h for icelake */ +#ifndef SIMDJSON_SRC_GENERIC_STAGE1_JSON_ESCAPE_SCANNER_H -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; -} +/* 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 */ -// private: +namespace simdjson { +namespace icelake { +namespace { +namespace stage1 { -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()); -} +/** + * 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; -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; -} + 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; + }; -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. -} + /** + * 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 { -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; -} +#if !SIMDJSON_SKIP_BACKSLASH_SHORT_CIRCUIT + if (!backslash) { return {next_escaped_without_backslashes(), 0}; } +#endif -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); -} + // | | 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}; + } -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; -} +private: + static constexpr const uint64_t ODD_BITS = 0xAAAAAAAAAAAAAAAAULL; -} // namespace stage2 + 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 arm64 +} // namespace icelake } // namespace simdjson -/* end file src/generic/stage2/tape_builder.h */ -// -// Implementation-specific overrides -// +#endif // SIMDJSON_SRC_GENERIC_STAGE1_JSON_STRING_SCANNER_H +/* end file generic/stage1/json_escape_scanner.h for icelake */ +/* including generic/stage1/json_string_scanner.h for icelake: #include */ +/* begin file generic/stage1/json_string_scanner.h for icelake */ +#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 arm64 { +namespace icelake { namespace { namespace stage1 { -simdjson_inline uint64_t json_string_scanner::find_escaped(uint64_t backslash) { - // On ARM, we don't short-circuit this if there are no backslashes, because the branch gives us no - // benefit and therefore makes things worse. - // if (!backslash) { uint64_t escaped = prev_escaped; prev_escaped = 0; return escaped; } - return find_escaped_branchless(backslash); +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 icelake +} // namespace simdjson -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 bool implementation::validate_utf8(const char *buf, size_t len) const noexcept { - return arm64::stage1::generic_validate_utf8(buf,len); -} +#endif // SIMDJSON_SRC_GENERIC_STAGE1_JSON_STRING_SCANNER_H +/* end file generic/stage1/json_string_scanner.h for icelake */ +/* including generic/stage1/utf8_lookup4_algorithm.h for icelake: #include */ +/* begin file generic/stage1/utf8_lookup4_algorithm.h for icelake */ +#ifndef SIMDJSON_SRC_GENERIC_STAGE1_UTF8_LOOKUP4_ALGORITHM_H -simdjson_warn_unused error_code dom_parser_implementation::stage2(dom::document &_doc) noexcept { - return stage2::tape_builder::parse_document(*this, _doc); -} +/* 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 */ -simdjson_warn_unused error_code dom_parser_implementation::stage2_next(dom::document &_doc) noexcept { - return stage2::tape_builder::parse_document(*this, _doc); -} +namespace simdjson { +namespace icelake { +namespace { +namespace utf8_validation { -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); -} +using namespace simd; -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_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_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); -} + 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, -} // namespace arm64 -} // namespace simdjson + // ____0100 ________ + CARRY | TOO_LARGE, + // ____0101 ________ + CARRY | TOO_LARGE | TOO_LARGE_1000, + // ____011_ ________ + CARRY | TOO_LARGE | TOO_LARGE_1000, + CARRY | TOO_LARGE | TOO_LARGE_1000, -/* begin file include/simdjson/arm64/end.h */ -/* end file include/simdjson/arm64/end.h */ -/* end file src/arm64/dom_parser_implementation.cpp */ -#endif -#if SIMDJSON_IMPLEMENTATION_FALLBACK -/* begin file src/fallback/implementation.cpp */ -/* begin file include/simdjson/fallback/begin.h */ -// redefining SIMDJSON_IMPLEMENTATION to "fallback" -// #define SIMDJSON_IMPLEMENTATION fallback -/* end file include/simdjson/fallback/begin.h */ -namespace simdjson { -namespace fallback { + // ____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, -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; -} + // ________ 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, -} // namespace fallback -} // namespace simdjson + // ________ 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 = simd8(must_be_2_3_continuation(prev2, prev3)); + simd8 must23_80 = must23 & uint8_t(0x80); + return must23_80 ^ sc; + } -/* begin file include/simdjson/fallback/end.h */ -/* end file include/simdjson/fallback/end.h */ -/* end file src/fallback/implementation.cpp */ -/* begin file src/fallback/dom_parser_implementation.cpp */ -/* begin file include/simdjson/fallback/begin.h */ -// redefining SIMDJSON_IMPLEMENTATION to "fallback" -// #define SIMDJSON_IMPLEMENTATION fallback -/* end file include/simdjson/fallback/begin.h */ + // + // 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 + }; +#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); + } -// -// Stage 1 -// -/* begin file src/generic/stage1/find_next_document_index.h */ -namespace simdjson { -namespace fallback { -namespace { + 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; -/** - * 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; + // + // 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); } - auto idxa = parser.structural_indexes[i - 1]; - switch (parser.buf[idxa]) { - case '{': - case '[': - case ':': - case ',': - continue; + + // 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; } - // Last document is complete, so the next document will appear after! - if (!arr_cnt && !obj_cnt) { - return parser.n_structural_indexes; + +#ifndef SIMDJSON_IF_CONSTEXPR +#if SIMDJSON_CPLUSPLUS17 +#define SIMDJSON_IF_CONSTEXPR if constexpr +#else +#define SIMDJSON_IF_CONSTEXPR if +#endif +#endif + + 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]); + } 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]; + } } - // 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; -} + // 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; + } + + }; // struct utf8_checker +} // namespace utf8_validation } // unnamed namespace -} // namespace fallback +} // namespace icelake } // namespace simdjson -/* end file src/generic/stage1/find_next_document_index.h */ + +#endif // SIMDJSON_SRC_GENERIC_STAGE1_UTF8_LOOKUP4_ALGORITHM_H +/* end file generic/stage1/utf8_lookup4_algorithm.h for icelake */ +/* including generic/stage1/json_scanner.h for icelake: #include */ +/* begin file generic/stage1/json_scanner.h for icelake */ +#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 fallback { +namespace icelake { namespace { namespace stage1 { -class structural_scanner { +/** + * 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) {} -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++; -} + /** + * 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()); } -simdjson_inline bool is_continuation(uint8_t c) { - return (c & 0xc0) == 0x80; -} + // Helpers -simdjson_inline void validate_utf8_character() { - // Continuation - if (simdjson_unlikely((buf[idx] & 0x40) == 0)) { - // extra continuation - error = UTF8_ERROR; - idx++; - return; - } + /** 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); } - // 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; - } + // 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) - // 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; + /** + * 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(); } - - // 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; + /** + * 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; } - // 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; -} +/** + * 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(); + +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{}; +}; -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; - } -} // -// Parse the entire input in STEP_SIZE-byte chunks. +// Check if the current character immediately follows a matching character. // -simdjson_inline error_code scan() { - bool unclosed_string = false; - for (;idx 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; +// 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; } -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 - -} // namespace stage1 -} // unnamed namespace +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 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 error_code json_scanner::finish() { + return string_scanner.finish(); } -// 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, -}; +} // namespace stage1 +} // unnamed namespace +} // namespace icelake +} // namespace simdjson -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; +#endif // SIMDJSON_SRC_GENERIC_STAGE1_JSON_SCANNER_H +/* end file generic/stage1/json_scanner.h for icelake */ - while (i < len) { - unsigned char c = buf[i]; - uint8_t *meta = jump_table + 3 * c; +// All other declarations +/* including generic/stage1/find_next_document_index.h for icelake: #include */ +/* begin file generic/stage1/find_next_document_index.h for icelake */ +#ifndef SIMDJSON_SRC_GENERIC_STAGE1_FIND_NEXT_DOCUMENT_INDEX_H - quote = quote ^ (meta[0] & nonescape); - dst[pos] = c; - pos += meta[2] | quote; +/* 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 */ - 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 simdjson { +namespace icelake { +namespace { +namespace stage1 { -// 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; - } +/** + * 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; } - unsigned char byte = data[pos]; - if (byte < 0x80) { - pos++; + auto idxa = parser.structural_indexes[i - 1]; + switch (parser.buf[idxa]) { + case '{': + case '[': + case ':': + case ',': 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; + // 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; } - return true; + // 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 fallback +} // namespace stage1 +} // unnamed namespace +} // namespace icelake } // namespace simdjson -// -// Stage 2 -// -/* begin file src/generic/stage2/stringparsing.h */ -// This file contains the common code every implementation uses +#endif // SIMDJSON_SRC_GENERIC_STAGE1_FIND_NEXT_DOCUMENT_INDEX_H +/* end file generic/stage1/find_next_document_index.h for icelake */ +/* including generic/stage1/json_minifier.h for icelake: #include */ +/* begin file generic/stage1/json_minifier.h for icelake */ +#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 fallback { +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, - - 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. +namespace stage1 { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +class json_minifier { +public: + template + static error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) noexcept; - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +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; }; -// 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 isn't 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; +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; +} -// 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 isn't 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; - } - } - } +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(); +} - size_t offset = jsoncharutils::codepoint_to_utf8(code_point, *dst_ptr); - *dst_ptr += offset; - return offset > 0; +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); -/** - * 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_nullptrptr 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; - } + // Index the first n-1 blocks + while (reader.has_full_block()) { + minifier.step(reader.full_block(), reader); } - /* can't be reached */ - return nullptr; -} -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; - } + // 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; } - /* can't be reached */ - return nullptr; + return minifier.finish(dst, dst_len); } -} // namespace stringparsing +} // namespace stage1 } // unnamed namespace -} // namespace fallback +} // namespace icelake } // namespace simdjson -/* end file src/generic/stage2/stringparsing.h */ -/* begin file src/generic/stage2/tape_builder.h */ -/* begin file src/generic/stage2/json_iterator.h */ -/* begin file src/generic/stage2/logger.h */ -// 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 { - static constexpr const char * DASHES = "----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"; +#endif // SIMDJSON_SRC_GENERIC_STAGE1_JSON_MINIFIER_H +/* end file generic/stage1/json_minifier.h for icelake */ +/* including generic/stage1/json_structural_indexer.h for icelake: #include */ +/* begin file generic/stage1/json_structural_indexer.h for icelake */ +#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 */ -#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; +// 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) - static int log_depth; // Not threadsafe. Log only. +namespace simdjson { +namespace icelake { +namespace { +namespace stage1 { - // Helper to turn unprintable or newline characters into spaces - static simdjson_inline char printable_char(char c) { - if (c >= 0x20) { - return c; - } else { - return ' '; - } - } +class bit_indexer { +public: + uint32_t *tail; - // 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_inline bit_indexer(uint32_t *index_buf) : tail(index_buf) {} - simdjson_unused static simdjson_inline void log_string(const char *message) { - if (LOG_ENABLED) { - printf("%s\n", message); - } - } + // 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; +#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. + */ - // 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(count_ones(bits)); + int i = 0; + // Do the first 8 all together + for (; i<8; i++) { + int lz = leading_zeroes(rev_bits); + this->tail[i] = static_cast(idx) + lz; + rev_bits = zero_leading_bit(rev_bits, lz); + } + // Do the next 8 all together (we hope in most cases it won't happen at all + // and the branch is easily predicted). + if (simdjson_unlikely(cnt > 8)) { + i = 8; + for (; i<16; i++) { + int lz = leading_zeroes(rev_bits); + this->tail[i] = static_cast(idx) + lz; + rev_bits = zero_leading_bit(rev_bits, lz); } - if (current_index) { - printf("| %*u ", LOG_INDEX_LEN, *current_index); - } else { - printf("| %-*s ", LOG_INDEX_LEN, ""); + + + // Most files don't have 16+ structurals per block, so we take several basically guaranteed + // branch mispredictions here. 16+ structurals per block means either punctuation ({} [] , :) + // or the start of a value ("abc" true 123) every four characters. + if (simdjson_unlikely(cnt > 16)) { + i = 16; + while (rev_bits != 0) { + int lz = leading_zeroes(rev_bits); + this->tail[i++] = static_cast(idx) + lz; + rev_bits = zero_leading_bit(rev_bits, lz); + } } - // printf("| %*u ", LOG_INDEX_LEN, structurals.next_tape_index()); - printf("| %-s ", detail); - printf("|\n"); } - } + this->tail += cnt; +#else // SIMDJSON_PREFER_REVERSE_BITS + /** + * 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. + */ -} // namespace logger -} // unnamed namespace -} // namespace fallback -} // namespace simdjson -/* end file src/generic/stage2/logger.h */ + int cnt = static_cast(count_ones(bits)); + // Do the first 8 all together + for (int i=0; i<8; i++) { + this->tail[i] = idx + trailing_zeroes(bits); + bits = clear_lowest_bit(bits); + } -namespace simdjson { -namespace fallback { -namespace { -namespace stage2 { + // Do the next 8 all together (we hope in most cases it won't happen at all + // and the branch is easily predicted). + if (simdjson_unlikely(cnt > 8)) { + for (int i=8; i<16; i++) { + this->tail[i] = idx + trailing_zeroes(bits); + bits = clear_lowest_bit(bits); + } -class json_iterator { -public: - const uint8_t* const buf; - uint32_t *next_structural; - dom_parser_implementation &dom_parser; - uint32_t depth{0}; + // Most files don't have 16+ structurals per block, so we take several basically guaranteed + // branch mispredictions here. 16+ structurals per block means either punctuation ({} [] , :) + // or the start of a value ("abc" true 123) every four characters. + if (simdjson_unlikely(cnt > 16)) { + int i = 16; + do { + this->tail[i] = idx + trailing_zeroes(bits); + bits = clear_lowest_bit(bits); + i++; + } while (i < cnt); + } + } - /** - * 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; + this->tail += cnt; +#endif + } +#endif // SIMDJSON_GENERIC_JSON_STRUCTURAL_INDEXER_CUSTOM_BIT_INDEXER - /** - * 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); +}; +class json_structural_indexer { +public: /** - * 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. + * Find the important bits of JSON in a 128-byte chunk, and add them to structural_indexes. * - * 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. + * @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. */ - simdjson_inline bool at_beginning() const noexcept; - simdjson_inline uint8_t last_structural() const noexcept; + template + static error_code index(const uint8_t *buf, size_t len, dom_parser_implementation &parser, stage1_mode partial) 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; +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); - 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; + json_scanner scanner{}; + utf8_checker checker{}; + bit_indexer indexer; + uint64_t prev_structurals = 0; + uint64_t unescaped_chars_error = 0; }; -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; - } - } +simdjson_inline json_structural_indexer::json_structural_indexer(uint32_t *structural_indexes) : indexer{structural_indexes} {} - 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; +// 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; } } - goto document_end; + 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; +} // -// Object parser states +// 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 doesn't 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. // -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) ); +// 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()); -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; - } + // 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); +} -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; - } +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(); +} -scope_end: - depth--; - if (depth == 0) { goto document_end; } - if (dom_parser.is_array[depth]) { goto array_continue; } - goto object_continue; +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(); +} -// -// 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) ); +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); +} -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; - } - } +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; } -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; + 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; + } + } -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; + 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(); +} - return SUCCESS; +} // namespace stage1 +} // unnamed namespace +} // namespace icelake +} // namespace simdjson -} // walk_document() +// Clear CUSTOM_BIT_INDEXER so other implementations can set it if they need to. +#undef SIMDJSON_GENERIC_JSON_STRUCTURAL_INDEXER_CUSTOM_BIT_INDEXER -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} { -} +#endif // SIMDJSON_SRC_GENERIC_STAGE1_JSON_STRUCTURAL_INDEXER_H +/* end file generic/stage1/json_structural_indexer.h for icelake */ +/* including generic/stage1/utf8_validator.h for icelake: #include */ +/* begin file generic/stage1/utf8_validator.h for icelake */ +#ifndef SIMDJSON_SRC_GENERIC_STAGE1_UTF8_VALIDATOR_H -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); -} +/* 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 */ -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]]; +namespace simdjson { +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; } -simdjson_inline void json_iterator::log_value(const char *type) const noexcept { - logger::log_line(*this, "", type, ""); +bool generic_validate_utf8(const char * input, size_t length) { + return generic_validate_utf8(reinterpret_cast(input),length); } -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++; } -} +} // namespace stage1 +} // unnamed namespace +} // namespace icelake +} // namespace simdjson -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, ""); -} +#endif // SIMDJSON_SRC_GENERIC_STAGE1_UTF8_VALIDATOR_H +/* end file generic/stage1/utf8_validator.h for icelake */ +/* end file generic/stage1/amalgamated.h for icelake */ +/* including generic/stage2/amalgamated.h for icelake: #include */ +/* begin file generic/stage2/amalgamated.h for icelake */ +// Stuff other things depend on +/* including generic/stage2/base.h for icelake: #include */ +/* begin file generic/stage2/base.h for icelake */ +#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 */ -simdjson_inline void json_iterator::log_error(const char *error) const noexcept { - logger::log_line(*this, "", "ERROR", error); -} +namespace simdjson { +namespace icelake { +namespace { +namespace stage2 { -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 { - switch (*value) { - case '"': return visitor.visit_string(*this, 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); - case '-': - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - return visitor.visit_number(*this, value); - default: - log_error("Non-value found when value was expected!"); - return TAPE_ERROR; - } -} +class json_iterator; +class structural_iterator; +struct tape_builder; +struct tape_writer; } // namespace stage2 } // unnamed namespace -} // namespace fallback +} // namespace icelake } // namespace simdjson -/* end file src/generic/stage2/json_iterator.h */ -/* begin file src/generic/stage2/tape_writer.h */ + +#endif // SIMDJSON_SRC_GENERIC_STAGE2_BASE_H +/* end file generic/stage2/base.h for icelake */ +/* including generic/stage2/tape_writer.h for icelake: #include */ +/* begin file generic/stage2/tape_writer.h for icelake */ +#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 fallback { +namespace icelake { namespace { namespace stage2 { @@ -6422,7 +28788,7 @@ struct tape_writer { */ template simdjson_inline void append2(uint64_t val, T val2, internal::tape_type t) noexcept; -}; // struct number_writer +}; // struct tape_writer simdjson_inline void tape_writer::append_s64(int64_t value) noexcept { append2(0, value, internal::tape_type::INT64); @@ -6470,3182 +28836,4931 @@ simdjson_inline void tape_writer::write(uint64_t &tape_loc, uint64_t val, intern } // namespace stage2 } // unnamed namespace -} // namespace fallback +} // namespace icelake } // namespace simdjson -/* end file src/generic/stage2/tape_writer.h */ - -namespace simdjson { -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; - - /** 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; -}; // class 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); -} +#endif // SIMDJSON_SRC_GENERIC_STAGE2_TAPE_WRITER_H +/* end file generic/stage2/tape_writer.h for icelake */ +/* including generic/stage2/logger.h for icelake: #include */ +/* begin file generic/stage2/logger.h for icelake */ +#ifndef SIMDJSON_SRC_GENERIC_STAGE2_LOGGER_H -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; -} +/* 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 */ -simdjson_inline tape_builder::tape_builder(dom::document &doc) noexcept : tape{doc.tape.get()}, current_string_buf_loc{doc.string_buf.get()} {} +#include -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); -} +// 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 { -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); -} + static constexpr const char * DASHES = "----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"; -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; -} +#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; -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; -} + static int log_depth; // Not threadsafe. Log only. -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; -} + // Helper to turn unprintable or newline characters into spaces + static simdjson_inline char printable_char(char c) { + if (c >= 0x20) { + return c; + } else { + return ' '; + } + } -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; -} + // 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_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_unused static simdjson_inline void log_string(const char *message) { + if (LOG_ENABLED) { + printf("%s\n", message); + } + } -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; -} + // 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;itape.get()); -} +// All other declarations +/* including generic/stage2/json_iterator.h for icelake: #include */ +/* begin file generic/stage2/json_iterator.h for icelake */ +#ifndef SIMDJSON_SRC_GENERIC_STAGE2_JSON_ITERATOR_H -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; -} +/* 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 */ -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. -} +namespace simdjson { +namespace icelake { +namespace { +namespace stage2 { -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; -} +class json_iterator { +public: + const uint8_t* const buf; + uint32_t *next_structural; + dom_parser_implementation &dom_parser; + uint32_t depth{0}; -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); -} + /** + * 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; -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; -} + /** + * 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); -} // namespace stage2 -} // unnamed namespace -} // namespace fallback -} // namespace simdjson -/* end file src/generic/stage2/tape_builder.h */ + /** + * 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(); -namespace simdjson { -namespace fallback { + // + // Start the document + // + if (at_eof()) { return EMPTY; } + log_start_value("document"); + SIMDJSON_TRY( visitor.visit_document_start(*this) ); -simdjson_warn_unused error_code dom_parser_implementation::stage2(dom::document &_doc) noexcept { - return stage2::tape_builder::parse_document(*this, _doc); -} + // + // Read first value + // + { + auto value = advance(); -simdjson_warn_unused error_code dom_parser_implementation::stage2_next(dom::document &_doc) noexcept { - return stage2::tape_builder::parse_document(*this, _doc); -} + // 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; + } + } -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); -} + 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; -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); -} +// +// 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) ); -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); -} + { + 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) ); + } -} // namespace fallback -} // namespace simdjson +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; + } + } -/* begin file include/simdjson/fallback/end.h */ -/* end file include/simdjson/fallback/end.h */ -/* end file src/fallback/dom_parser_implementation.cpp */ -#endif -#if SIMDJSON_IMPLEMENTATION_ICELAKE -/* begin file src/icelake/implementation.cpp */ -/* begin file include/simdjson/icelake/begin.h */ -// redefining SIMDJSON_IMPLEMENTATION to "icelake" -// #define SIMDJSON_IMPLEMENTATION icelake -SIMDJSON_TARGET_ICELAKE -/* end file include/simdjson/icelake/begin.h */ +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; + } -namespace simdjson { -namespace icelake { +scope_end: + depth--; + if (depth == 0) { goto document_end; } + if (dom_parser.is_array[depth]) { goto array_continue; } + goto object_continue; -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; -} +// +// 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) ); -} // namespace icelake -} // namespace simdjson +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; + } + } -/* begin file include/simdjson/icelake/end.h */ -SIMDJSON_UNTARGET_ICELAKE -/* end file include/simdjson/icelake/end.h */ +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; + } -/* end file src/icelake/implementation.cpp */ -/* begin file src/icelake/dom_parser_implementation.cpp */ -/* begin file include/simdjson/icelake/begin.h */ -// redefining SIMDJSON_IMPLEMENTATION to "icelake" -// #define SIMDJSON_IMPLEMENTATION icelake -SIMDJSON_TARGET_ICELAKE -/* end file include/simdjson/icelake/begin.h */ +document_end: + log_end_value("document"); + SIMDJSON_TRY( visitor.visit_document_end(*this) ); -// -// Stage 1 -// + dom_parser.next_structural_index = uint32_t(next_structural - &dom_parser.structural_indexes[0]); -namespace simdjson { -namespace icelake { -namespace { + // 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; + } -using namespace simd; + return SUCCESS; -struct json_character_block { - static simdjson_inline json_character_block classify(const simd::simd8x64& in); - // ASCII white-space ('\r','\n','\t',' ') - simdjson_inline uint64_t whitespace() const noexcept; - // non-quote structural characters (comma, colon, braces, brackets) - simdjson_inline uint64_t op() const noexcept; - // neither a structural character nor a white-space, so letters, numbers and quotes - simdjson_inline uint64_t scalar() const noexcept; - - uint64_t _whitespace; // ASCII white-space ('\r','\n','\t',' ') - uint64_t _op; // structural characters (comma, colon, braces, brackets but not quotes) -}; +} // walk_document() -simdjson_inline uint64_t json_character_block::whitespace() const noexcept { return _whitespace; } -simdjson_inline uint64_t json_character_block::op() const noexcept { return _op; } -simdjson_inline uint64_t json_character_block::scalar() const noexcept { return ~(op() | whitespace()); } +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} { +} -// 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); +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); +} - // 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 - ); +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]]; +} - // 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). +simdjson_inline void json_iterator::log_value(const char *type) const noexcept { + logger::log_line(*this, "", type, ""); +} - 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]) - }); +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++; } +} - return { whitespace, op }; +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 bool is_ascii(const simd8x64& input) { - return input.reduce_or().is_ascii(); +simdjson_inline void json_iterator::log_error(const char *error) const noexcept { + logger::log_line(*this, "", "ERROR", error); } -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); +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; + } } - -simdjson_inline simd8 must_be_2_3_continuation(const simd8 prev2, const simd8 prev3) { - 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_third_byte | is_fourth_byte) > int8_t(0); +template +simdjson_warn_unused simdjson_inline error_code json_iterator::visit_primitive(V &visitor, const uint8_t *value) noexcept { + switch (*value) { + case '"': return visitor.visit_string(*this, 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); + case '-': + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + return visitor.visit_number(*this, value); + default: + log_error("Non-value found when value was expected!"); + return TAPE_ERROR; + } } +} // namespace stage2 } // unnamed namespace } // namespace icelake } // namespace simdjson -/* begin file src/generic/stage1/utf8_lookup4_algorithm.h */ +#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 */ +#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 icelake { namespace { -namespace utf8_validation { +/// @private +namespace stringparsing { -using namespace simd; +// 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, - 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____ + 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. - 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, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - // ____0100 ________ - CARRY | TOO_LARGE, - // ____0101 ________ - CARRY | TOO_LARGE | TOO_LARGE_1000, - // ____011_ ________ - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 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___ ________ - 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, +// 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 isn't 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; - // ________ 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, + // 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); - // ________ 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 = simd8(must_be_2_3_continuation(prev2, prev3)); - simd8 must23_80 = must23 & uint8_t(0x80); - return must23_80 ^ sc; + // 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. // - // 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 - }; -#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); + // jsoncharutils::hex_to_u32_nocheck fills high 16 bits of the return value with 1s if the + // conversion isn't 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; + } + } } - 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; + size_t offset = jsoncharutils::codepoint_to_utf8(code_point, *dst_ptr); + *dst_ptr += offset; + return offset > 0; +} - // - // 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; +/** + * 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_nullptrptr 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(); } - -#ifndef SIMDJSON_IF_CONSTEXPR -#if SIMDJSON_CPLUSPLUS17 -#define SIMDJSON_IF_CONSTEXPR if constexpr -#else -#define SIMDJSON_IF_CONSTEXPR if -#endif -#endif - - simdjson_inline void check_next_input(const simd8x64& input) { - if(simdjson_likely(is_ascii(input))) { - this->error |= this->prev_incomplete; + 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 { - // 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]); - } 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]); + /* 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 */ } - this->prev_incomplete = is_incomplete(input.chunks[simd8x64::NUM_CHUNKS-1]); - this->prev_input_block = input.chunks[simd8x64::NUM_CHUNKS-1]; + 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; } - // 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; + } + /* can't be reached */ + return nullptr; +} + +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; } + } + /* can't be reached */ + return nullptr; +} - }; // struct utf8_checker -} // namespace utf8_validation +} // namespace stringparsing +} // unnamed namespace +} // namespace icelake +} // namespace simdjson -using utf8_validation::utf8_checker; +#endif // SIMDJSON_SRC_GENERIC_STAGE2_STRINGPARSING_H +/* end file generic/stage2/stringparsing.h for icelake */ +/* including generic/stage2/structural_iterator.h for icelake: #include */ +/* begin file generic/stage2/structural_iterator.h for icelake */ +#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 icelake { +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 icelake } // namespace simdjson -/* end file src/generic/stage1/utf8_lookup4_algorithm.h */ -// defining SIMDJSON_CUSTOM_BIT_INDEXER allows us to provide our own bit_indexer::write -#define SIMDJSON_CUSTOM_BIT_INDEXER -/* begin file src/generic/stage1/json_structural_indexer.h */ -// 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) -/* begin file src/generic/stage1/buf_block_reader.h */ +#endif // SIMDJSON_SRC_GENERIC_STAGE2_STRUCTURAL_ITERATOR_H +/* end file generic/stage2/structural_iterator.h for icelake */ +/* including generic/stage2/tape_builder.h for icelake: #include */ +/* begin file generic/stage2/tape_builder.h for icelake */ +#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 icelake { namespace { +namespace stage2 { -// 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; +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; /** - * 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. + * Called when a key in a field is encountered. * - * @return the number of effective characters in the last block. + * primitive, visit_object_start, visit_empty_object, visit_array_start, or visit_empty_array + * will be called after this with the field value. */ - 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; -}; + 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; -// 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; -} + 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; -// 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_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_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; -} + /** 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; -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} {} + /** 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; -template -simdjson_inline size_t buf_block_reader::block_index() { return idx; } + simdjson_inline tape_builder(dom::document &doc) noexcept; -template -simdjson_inline bool buf_block_reader::has_full_block() const { - return idx < lenminusstep; + 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_inline const uint8_t *buf_block_reader::full_block() const { - return &buf[idx]; +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); } -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; +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; } -template -simdjson_inline void buf_block_reader::advance() { - idx += STEP_SIZE; +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); } -} // unnamed namespace -} // namespace icelake -} // namespace simdjson -/* end file src/generic/stage1/buf_block_reader.h */ -/* begin file src/generic/stage1/json_string_scanner.h */ -namespace simdjson { -namespace icelake { -namespace { -namespace stage1 { +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; +} -struct json_string_block { - // We spell out the constructors in the hope of resolving inlining issues with Visual Studio 2017 - simdjson_inline json_string_block(uint64_t backslash, uint64_t escaped, uint64_t quote, uint64_t in_string) : - _backslash(backslash), _escaped(escaped), _quote(quote), _in_string(in_string) {} +simdjson_inline tape_builder::tape_builder(dom::document &doc) noexcept : tape{doc.tape.get()}, current_string_buf_loc{doc.string_buf.get()} {} - // Escaped characters (characters following an escape() character) - simdjson_inline uint64_t escaped() const { return _escaped; } - // Escape characters (backslashes that are not escaped--i.e. in \\, includes only the first \) - simdjson_inline uint64_t escape() const { return _backslash & ~_escaped; } - // Real (non-backslashed) quotes - simdjson_inline uint64_t quote() const { return _quote; } - // Start quotes of strings - simdjson_inline uint64_t string_start() const { return _quote & _in_string; } - // End quotes of strings - simdjson_inline uint64_t string_end() const { return _quote & ~_in_string; } - // Only characters inside the string (not including the quotes) - simdjson_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_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_inline uint64_t non_quote_outside_string(uint64_t mask) const { return mask & ~_in_string; } - // Tail of string (everything except the start quote) - simdjson_inline uint64_t string_tail() const { return _in_string ^ _quote; } +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; +} - // backslash characters - uint64_t _backslash; - // escaped characters (backslashed--does not include the hex characters after \u) - uint64_t _escaped; - // real quotes (non-backslashed ones) - uint64_t _quote; - // string characters (includes start quote but not end quote) - uint64_t _in_string; -}; +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); +} -// Scans blocks for string characters, storing the state necessary to do so -class json_string_scanner { -public: - simdjson_inline json_string_block next(const simd::simd8x64& in); - // Returns either UNCLOSED_STRING or SUCCESS - simdjson_inline error_code finish(); +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); +} -private: - // Intended to be defined by the implementation - simdjson_inline uint64_t find_escaped(uint64_t escape); - simdjson_inline uint64_t find_escaped_branchless(uint64_t escape); +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; +} - // Whether the last iteration was still inside a string (all 1's = true, all 0's = false). - uint64_t prev_in_string = 0ULL; - // Whether the first character of the next iteration is escaped. - uint64_t prev_escaped = 0ULL; -}; +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; +} -// -// Finds escaped characters (characters following \). -// -// Handles runs of backslashes like \\\" and \\\\" correctly (yielding 0101 and 01010, respectively). -// -// Does this by: -// - Shift the escape mask to get potentially escaped characters (characters after backslashes). -// - Mask escaped sequences that start on *even* bits with 1010101010 (odd bits are escaped, even bits are not) -// - Mask escaped sequences that start on *odd* bits with 0101010101 (even bits are escaped, odd bits are not) -// -// To distinguish between escaped sequences starting on even/odd bits, it finds the start of all -// escape sequences, filters out the ones that start on even bits, and adds that to the mask of -// escape sequences. This causes the addition to clear out the sequences starting on odd bits (since -// the start bit causes a carry), and leaves even-bit sequences alone. -// -// Example: -// -// text | \\\ | \\\"\\\" \\\" \\"\\" | -// escape | xxx | xx xxx xxx xx xx | Removed overflow backslash; will | it into follows_escape -// odd_starts | x | x x x | escape & ~even_bits & ~follows_escape -// even_seq | c| cxxx c xx c | c = carry bit -- will be masked out later -// invert_mask | | cxxx c xx c| even_seq << 1 -// follows_escape | xx | x xx xxx xxx xx xx | Includes overflow bit -// escaped | x | x x x x x x x x | -// desired | x | x x x x x x x x | -// text | \\\ | \\\"\\\" \\\" \\"\\" | -// -simdjson_inline uint64_t json_string_scanner::find_escaped_branchless(uint64_t backslash) { - // If there was overflow, pretend the first character isn't a backslash - backslash &= ~prev_escaped; - uint64_t follows_escape = backslash << 1 | prev_escaped; +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; +} - // Get sequences starting on even bits by clearing out the odd series using + - const uint64_t even_bits = 0x5555555555555555ULL; - uint64_t odd_sequence_starts = backslash & ~even_bits & ~follows_escape; - uint64_t sequences_starting_on_even_bits; - prev_escaped = add_overflow(odd_sequence_starts, backslash, &sequences_starting_on_even_bits); - uint64_t invert_mask = sequences_starting_on_even_bits << 1; // The mask we want to return is the *escaped* bits, not escapes. +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; +} - // Mask every other backslashed character as an escaped character - // Flip the mask for sequences that start on even bits, to correct them - return (even_bits ^ invert_mask) & follows_escape; +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; } -// -// 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_inline json_string_block json_string_scanner::next(const simd::simd8x64& in) { - const uint64_t backslash = in.eq('\\'); - const uint64_t escaped = find_escaped(backslash); - const uint64_t quote = in.eq('"') & ~escaped; +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; +} - // - // 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; +// private: - // - // Check if we're still in a string at the end of the box so the next block will know - // - // right shift of a signed value expected to be well-defined and standard - // compliant as of C++20, John Regher from Utah U. says this is fine code - // - prev_in_string = uint64_t(static_cast(in_string) >> 63); +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()); +} - // Use ^ to turn the beginning quote off, and the end quote on. +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; +} - // We are returning a function-local object so either we get a move constructor - // or we get copy elision. - return json_string_block( - backslash, - escaped, - quote, - in_string - ); +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 error_code json_string_scanner::finish() { - if (prev_in_string) { - return UNCLOSED_STRING; - } +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; } -} // namespace stage1 +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 icelake } // namespace simdjson -/* end file src/generic/stage1/json_string_scanner.h */ -/* begin file src/generic/stage1/json_scanner.h */ -namespace simdjson { -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) {} - - /** - * 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 +#endif // SIMDJSON_SRC_GENERIC_STAGE2_TAPE_BUILDER_H +/* end file generic/stage2/tape_builder.h for icelake */ +/* end file generic/stage2/amalgamated.h for icelake */ - /** 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); } +#undef SIMDJSON_GENERIC_JSON_STRUCTURAL_INDEXER_CUSTOM_BIT_INDEXER - // 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) +// +// Stage 1 +// - /** - * 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; - } -}; +namespace simdjson { +namespace icelake { -/** - * 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(); +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; +} -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{}; -}; +namespace { +using namespace simd; -// -// 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; -} +// 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); -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). + // The 6 operators (:,[]{}) have these values: // - // 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. + // , 2C + // : 3A + // [ 5B + // { 7B + // ] 5D + // } 7D // - // 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 + // 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). + + 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 }; } -simdjson_inline error_code json_scanner::finish() { - return string_scanner.finish(); +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_inline simd8 must_be_2_3_continuation(const simd8 prev2, const simd8 prev3) { + 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_third_byte | is_fourth_byte) > int8_t(0); } -} // namespace stage1 } // unnamed namespace } // namespace icelake } // namespace simdjson -/* end file src/generic/stage1/json_scanner.h */ -/* begin file src/generic/stage1/json_minifier.h */ -// 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 icelake { -namespace { -namespace stage1 { +/** + * We provide a custom version of bit_indexer::write using + * 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. +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; } -class json_minifier { -public: - template - static error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) noexcept; + 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); -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; -}; + 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)); -simdjson_inline void json_minifier::next(const simd::simd8x64& in, const json_block& block) { - uint64_t mask = block.whitespace(); - dst += in.compress(mask, dst); + 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 -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; +// +// Stage 2 +// + +// +// Implementation-specific overrides +// +namespace simdjson { +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); } -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(); +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); } -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(); +simdjson_warn_unused bool implementation::validate_utf8(const char *buf, size_t len) const noexcept { + return icelake::stage1::generic_validate_utf8(buf,len); } -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); +simdjson_warn_unused error_code dom_parser_implementation::stage2(dom::document &_doc) noexcept { + return stage2::tape_builder::parse_document(*this, _doc); +} - // Index the first n-1 blocks - while (reader.has_full_block()) { - minifier.step(reader.full_block(), reader); - } +simdjson_warn_unused error_code dom_parser_implementation::stage2_next(dom::document &_doc) noexcept { + return stage2::tape_builder::parse_document(*this, _doc); +} - // 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); +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); } -} // namespace stage1 -} // unnamed namespace -} // namespace icelake -} // namespace simdjson -/* end file src/generic/stage1/json_minifier.h */ -/* begin file src/generic/stage1/find_next_document_index.h */ -namespace simdjson { -namespace icelake { -namespace { +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); +} -/** - * 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; +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); } -} // unnamed namespace } // namespace icelake } // namespace simdjson -/* end file src/generic/stage1/find_next_document_index.h */ + +/* including simdjson/icelake/end.h: #include */ +/* 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_SRC_ICELAKE_CPP +/* end file icelake.cpp */ +#endif +#if SIMDJSON_IMPLEMENTATION_PPC64 +/* including ppc64.cpp: #include */ +/* begin file ppc64.cpp */ +#ifndef SIMDJSON_SRC_PPC64_CPP +#define SIMDJSON_SRC_PPC64_CPP + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #include */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +/* including simdjson/ppc64.h: #include */ +/* begin file simdjson/ppc64.h */ +#ifndef SIMDJSON_PPC64_H +#define SIMDJSON_PPC64_H + +/* including simdjson/ppc64/begin.h: #include "simdjson/ppc64/begin.h" */ +/* begin file simdjson/ppc64/begin.h */ +/* defining SIMDJSON_IMPLEMENTATION to "ppc64" */ +#define SIMDJSON_IMPLEMENTATION ppc64 +/* including simdjson/ppc64/base.h: #include "simdjson/ppc64/base.h" */ +/* begin file simdjson/ppc64/base.h */ +#ifndef SIMDJSON_PPC64_BASE_H +#define SIMDJSON_PPC64_BASE_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #include "simdjson/base.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { -namespace icelake { +/** + * Implementation for ALTIVEC (PPC64). + */ +namespace ppc64 { + +class implementation; + namespace { -namespace stage1 { +namespace simd { +template struct simd8; +template struct simd8x64; +} // namespace simd +} // unnamed namespace -class bit_indexer { -public: - uint32_t *tail; +} // namespace ppc64 +} // namespace simdjson - simdjson_inline bit_indexer(uint32_t *index_buf) : tail(index_buf) {} +#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 - // 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_CUSTOM_BIT_INDEXER, then it will provide its own - // version of the code. -#ifdef SIMDJSON_CUSTOM_BIT_INDEXER - simdjson_inline void write(uint32_t idx, uint64_t bits); +/* 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 - 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; -#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. - */ + return __builtin_clzll(input_num); +#endif // SIMDJSON_REGULAR_VISUAL_STUDIO +} - uint64_t rev_bits = reverse_bits(bits); - int cnt = static_cast(count_ones(bits)); - int i = 0; - // Do the first 8 all together - for (; i<8; i++) { - int lz = leading_zeroes(rev_bits); - this->tail[i] = static_cast(idx) + lz; - rev_bits = zero_leading_bit(rev_bits, lz); - } - // Do the next 8 all together (we hope in most cases it won't happen at all - // and the branch is easily predicted). - if (simdjson_unlikely(cnt > 8)) { - i = 8; - for (; i<16; i++) { - int lz = leading_zeroes(rev_bits); - this->tail[i] = static_cast(idx) + lz; - rev_bits = zero_leading_bit(rev_bits, lz); - } +#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 +} - // Most files don't have 16+ structurals per block, so we take several basically guaranteed - // branch mispredictions here. 16+ structurals per block means either punctuation ({} [] , :) - // or the start of a value ("abc" true 123) every four characters. - if (simdjson_unlikely(cnt > 16)) { - i = 16; - while (rev_bits != 0) { - int lz = leading_zeroes(rev_bits); - this->tail[i++] = static_cast(idx) + lz; - rev_bits = zero_leading_bit(rev_bits, lz); - } - } - } - this->tail += cnt; -#else // SIMDJSON_PREFER_REVERSE_BITS - /** - * 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. - */ +} // unnamed namespace +} // namespace ppc64 +} // namespace simdjson - int cnt = static_cast(count_ones(bits)); - // Do the first 8 all together - for (int i=0; i<8; i++) { - this->tail[i] = idx + trailing_zeroes(bits); - bits = clear_lowest_bit(bits); - } +#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 - // Do the next 8 all together (we hope in most cases it won't happen at all - // and the branch is easily predicted). - if (simdjson_unlikely(cnt > 8)) { - for (int i=8; i<16; i++) { - this->tail[i] = idx + trailing_zeroes(bits); - bits = clear_lowest_bit(bits); - } +/* 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 */ - // Most files don't have 16+ structurals per block, so we take several basically guaranteed - // branch mispredictions here. 16+ structurals per block means either punctuation ({} [] , :) - // or the start of a value ("abc" true 123) every four characters. - if (simdjson_unlikely(cnt > 16)) { - int i = 16; - do { - this->tail[i] = idx + trailing_zeroes(bits); - bits = clear_lowest_bit(bits); - i++; - } while (i < cnt); - } - } +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 - this->tail += cnt; #endif - } -#endif // SIMDJSON_CUSTOM_BIT_INDEXER +/* 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 -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; +#if defined(__linux__) +#include +#elif defined(__FreeBSD__) +#include +#endif -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); +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 +#ifdef _M_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 // _M_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; +} - json_scanner scanner{}; - utf8_checker checker{}; - bit_indexer indexer; - uint64_t prev_structurals = 0; - uint64_t unescaped_chars_error = 0; +} // namespace numberparsing +} // namespace ppc64 +} // namespace simdjson + +#define SIMDJSON_SWAR_NUMBER_PARSING 1 + +#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; + } }; -simdjson_inline json_structural_indexer::json_structural_indexer(uint32_t *structural_indexes) : indexer{structural_indexes} {} +template > +struct base8 : base> { + typedef uint16_t bitmask_t; + typedef uint32_t bitmask2_t; -// 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; - } + 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); } - 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 doesn't 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; } + 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; } - 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); +// 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))); } - // 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(); -} + simdjson_inline simd8() : base8() {} + simdjson_inline simd8(const __m128i _value) + : base8(_value) {} + // Splat constructor + simdjson_inline simd8(bool _value) + : base8(splat(_value)) {} -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 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 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); + 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 - 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 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 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; } +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)); + } +}; - if (unescaped_chars_error) { - return UNESCAPED_CHARS; +// 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); } - 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; +}; + +// 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))); } - if (simdjson_unlikely(parser.structural_indexes[parser.n_structural_indexes - 1] > len)) { - return UNEXPECTED_ERROR; +}; + +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]; + + 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); } - 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; - } + simdjson_inline simd8 reduce_or() const { + return (this->chunks[0] | this->chunks[1]) | + (this->chunks[2] | this->chunks[3]); } - checker.check_eof(); - return checker.errors(); + + 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 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 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 + }; } -} // namespace stage1 } // unnamed namespace -} // namespace icelake +} // namespace ppc64 } // namespace simdjson -/* end file src/generic/stage1/json_structural_indexer.h */ -// We must not forget to undefine it now: -#undef SIMDJSON_CUSTOM_BIT_INDEXER + +#endif // SIMDJSON_PPC64_STRINGPARSING_DEFS_H +/* end file simdjson/ppc64/stringparsing_defs.h */ + +#define SIMDJSON_SKIP_BACKSLASH_SHORT_CIRCUIT 1 +/* end file simdjson/ppc64/begin.h */ +/* including simdjson/generic/amalgamated.h for ppc64: #include "simdjson/generic/amalgamated.h" */ +/* begin file simdjson/generic/amalgamated.h for ppc64 */ +#if defined(SIMDJSON_CONDITIONAL_INCLUDE) && !defined(SIMDJSON_GENERIC_DEPENDENCIES_H) +#error simdjson/generic/dependencies.h must be included before simdjson/generic/amalgamated.h! +#endif + +/* including simdjson/generic/base.h for ppc64: #include "simdjson/generic/base.h" */ +/* begin file simdjson/generic/base.h for ppc64 */ +#ifndef SIMDJSON_GENERIC_BASE_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_BASE_H */ +/* amalgamation skipped (editor-only): #include "simdjson/base.h" */ +/* amalgamation skipped (editor-only): // If we haven't got an implementation yet, we're in the editor, editing a generic file! Just */ +/* amalgamation skipped (editor-only): // use the most advanced one we can so the most possible stuff can be tested. */ +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_IMPLEMENTATION */ +/* amalgamation skipped (editor-only): #include "simdjson/implementation_detection.h" */ +/* amalgamation skipped (editor-only): #if SIMDJSON_IMPLEMENTATION_ICELAKE */ +/* amalgamation skipped (editor-only): #include "simdjson/icelake/begin.h" */ +/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_HASWELL */ +/* amalgamation skipped (editor-only): #include "simdjson/haswell/begin.h" */ +/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_WESTMERE */ +/* amalgamation skipped (editor-only): #include "simdjson/westmere/begin.h" */ +/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_ARM64 */ +/* amalgamation skipped (editor-only): #include "simdjson/arm64/begin.h" */ +/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_PPC64 */ +/* amalgamation skipped (editor-only): #include "simdjson/ppc64/begin.h" */ +/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_FALLBACK */ +/* amalgamation skipped (editor-only): #include "simdjson/fallback/begin.h" */ +/* amalgamation skipped (editor-only): #else */ +/* amalgamation skipped (editor-only): #error "All possible implementations (including fallback) have been disabled! simdjson will not run." */ +/* amalgamation skipped (editor-only): #endif */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_IMPLEMENTATION */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { +namespace ppc64 { + +struct open_container; +class dom_parser_implementation; /** - * We provide a custom version of bit_indexer::write using - * naked intrinsics. - * TODO: make this code more elegant. + * The type of a JSON number */ -// 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; } +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 +}; - 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); +} // namespace ppc64 +} // namespace simdjson - 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)); +#endif // SIMDJSON_GENERIC_BASE_H +/* end file simdjson/generic/base.h for ppc64 */ +/* including simdjson/generic/jsoncharutils.h for ppc64: #include "simdjson/generic/jsoncharutils.h" */ +/* begin file simdjson/generic/jsoncharutils.h for ppc64 */ +#ifndef SIMDJSON_GENERIC_JSONCHARUTILS_H - 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 +/* 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 */ -/* begin file src/generic/stage1/utf8_validator.h */ namespace simdjson { -namespace icelake { +namespace ppc64 { namespace { -namespace stage1 { +namespace jsoncharutils { -/** - * 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; +// 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 } -bool generic_validate_utf8(const char * input, size_t length) { - return generic_validate_utf8(reinterpret_cast(input),length); +#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 stage1 +} // namespace jsoncharutils } // unnamed namespace -} // namespace icelake +} // namespace ppc64 } // namespace simdjson -/* end file src/generic/stage1/utf8_validator.h */ -// -// Stage 2 -// -/* begin file src/generic/stage2/stringparsing.h */ -// This file contains the common code every implementation uses -// It is intended to be included multiple times and compiled multiple times +#endif // SIMDJSON_GENERIC_JSONCHARUTILS_H +/* end file simdjson/generic/jsoncharutils.h for ppc64 */ +/* including simdjson/generic/atomparsing.h for ppc64: #include "simdjson/generic/atomparsing.h" */ +/* begin file simdjson/generic/atomparsing.h for ppc64 */ +#ifndef SIMDJSON_GENERIC_ATOMPARSING_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ATOMPARSING_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/jsoncharutils.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +#include namespace simdjson { -namespace icelake { +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, - - 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. +namespace atomparsing { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 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; } - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 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 +// 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 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 isn't 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; +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; +} -// 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 isn't 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; - } - } - } +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; } +} - size_t offset = jsoncharutils::codepoint_to_utf8(code_point, *dst_ptr); - *dst_ptr += offset; - return offset > 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; } +} -/** - * 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_nullptrptr 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; - } - } - /* can't be reached */ - return nullptr; +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 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; - } - } - /* can't be reached */ - return nullptr; +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 stringparsing +} // namespace atomparsing } // unnamed namespace -} // namespace icelake +} // namespace ppc64 } // namespace simdjson -/* end file src/generic/stage2/stringparsing.h */ -/* begin file src/generic/stage2/tape_builder.h */ -/* begin file src/generic/stage2/json_iterator.h */ -/* begin file src/generic/stage2/logger.h */ -// This is for an internal-only stage 2 specific logger. -// Set LOG_ENABLED = true to log what stage 2 is doing! + +#endif // SIMDJSON_GENERIC_ATOMPARSING_H +/* end file simdjson/generic/atomparsing.h for ppc64 */ +/* including simdjson/generic/dom_parser_implementation.h for ppc64: #include "simdjson/generic/dom_parser_implementation.h" */ +/* begin file simdjson/generic/dom_parser_implementation.h for ppc64 */ +#ifndef SIMDJSON_GENERIC_DOM_PARSER_IMPLEMENTATION_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_DOM_PARSER_IMPLEMENTATION_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/internal/dom_parser_implementation.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + namespace simdjson { -namespace icelake { -namespace { -namespace logger { +namespace ppc64 { - static constexpr const char * DASHES = "----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"; +// 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); -#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. +} // namespace ppc64 +} // namespace simdjson - // Helper to turn unprintable or newline characters into spaces - static simdjson_inline char printable_char(char c) { - if (c >= 0x20) { - return c; - } else { - return ' '; - } - } +namespace simdjson { +namespace ppc64 { - // 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); - } - } +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; +} - simdjson_unused static simdjson_inline void log_string(const char *message) { - if (LOG_ENABLED) { - printf("%s\n", message); - } - } +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; } - // 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 : 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 { /** - * 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. + * Create a new empty result with error = UNINITIALIZED. */ - template - simdjson_warn_unused simdjson_inline error_code walk_document(V &visitor) noexcept; + simdjson_inline implementation_simdjson_result_base() noexcept = default; /** - * Create an iterator capable of walking a JSON document. - * - * The document must have already passed through stage 1. + * Create a new error result. */ - simdjson_inline json_iterator(dom_parser_implementation &_dom_parser, size_t start_structural_index); + simdjson_inline implementation_simdjson_result_base(error_code error) 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`). + * Create a new successful result. */ - simdjson_inline const uint8_t *peek() const noexcept; + simdjson_inline implementation_simdjson_result_base(T &&value) 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`). + * Create a new result with both things (use if you don't want to branch when creating the result). */ - simdjson_inline const uint8_t *advance() noexcept; + simdjson_inline implementation_simdjson_result_base(T &&value, error_code error) noexcept; + /** - * Get the remaining length of the document, from the start of the current token. + * 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 size_t remaining_len() const noexcept; + simdjson_inline void tie(T &value, error_code &error) && noexcept; + /** - * Check if we are at the end of the document. + * Move the value to the provided variable. * - * If this is true, there are no more tokens. + * @param value The variable to assign the value to. May not be set if there is an error. */ - simdjson_inline bool at_eof() const noexcept; + simdjson_inline error_code get(T &value) && noexcept; + /** - * Check if we are at the beginning of the document. + * The error. */ - simdjson_inline bool at_beginning() const noexcept; - simdjson_inline uint8_t last_structural() const noexcept; + simdjson_inline error_code error() const noexcept; + +#if SIMDJSON_EXCEPTIONS /** - * Log that a value has been found. + * Get the result value. * - * Set LOG_ENABLED=true in logger.h to see logging. + * @throw simdjson_error if there was an error. */ - simdjson_inline void log_value(const char *type) const noexcept; + simdjson_inline T& value() & noexcept(false); + /** - * Log the start of a multipart value. + * Take the result value (move it). * - * Set LOG_ENABLED=true in logger.h to see logging. + * @throw simdjson_error if there was an error. */ - simdjson_inline void log_start_value(const char *type) const noexcept; + simdjson_inline T&& value() && noexcept(false); + /** - * Log the end of a multipart value. + * Take the result value (move it). * - * Set LOG_ENABLED=true in logger.h to see logging. + * @throw simdjson_error if there was an error. */ - simdjson_inline void log_end_value(const char *type) const noexcept; + simdjson_inline T&& take_value() && noexcept(false); + /** - * Log an error. + * Cast to the value (will throw on error). * - * Set LOG_ENABLED=true in logger.h to see logging. + * @throw simdjson_error if there was an error. */ - 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; + simdjson_inline operator T&&() && noexcept(false); -// -// 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) ); - } +#endif // SIMDJSON_EXCEPTIONS -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; - } - } + /** + * 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 -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; - } +} // namespace ppc64 +} // namespace simdjson -scope_end: - depth--; - if (depth == 0) { goto document_end; } - if (dom_parser.is_array[depth]) { goto array_continue; } - goto object_continue; +#endif // SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_H +/* end file simdjson/generic/implementation_simdjson_result_base.h for ppc64 */ +/* including simdjson/generic/numberparsing.h for ppc64: #include "simdjson/generic/numberparsing.h" */ +/* begin file simdjson/generic/numberparsing.h for ppc64 */ +#ifndef SIMDJSON_GENERIC_NUMBERPARSING_H -// -// 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) ); +/* 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 */ -array_value: +#include +#include +#include + +namespace simdjson { +namespace ppc64 { +namespace numberparsing { + +#ifdef JSON_TEST_NUMBERS +#define INVALID_NUMBER(SRC) (found_invalid_number((SRC)), NUMBER_ERROR) +#define WRITE_INTEGER(VALUE, SRC, WRITER) (found_integer((VALUE), (SRC)), (WRITER).append_s64((VALUE))) +#define WRITE_UNSIGNED(VALUE, SRC, WRITER) (found_unsigned_integer((VALUE), (SRC)), (WRITER).append_u64((VALUE))) +#define WRITE_DOUBLE(VALUE, SRC, WRITER) (found_float((VALUE), (SRC)), (WRITER).append_double((VALUE))) +#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)) +#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 { - 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; + // 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 + // 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; -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) ); + // We want the most significant bit of i to be 1. Shift if needed. + int lz = leading_zeroes(i); + i <<= lz; - 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; + // 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. + + // 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. In very rare cases, even that + // will not suffice, though it is seemingly very hard to find such a scenario. + // + // 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++; } + // At this point, we might need to add at most one to firstproduct, but this + // can only change the value of firstproduct.high if firstproduct.low is maximal. + if(simdjson_unlikely(firstproduct.low == 0xFFFFFFFFFFFFFFFF)) { + // This is very unlikely, but if so, we need to do much more work! + return false; + } + } + 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 + } } - return SUCCESS; - -} // walk_document() + mantissa += mantissa & 1; + mantissa >>= 1; -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} { + // 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; } -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++)]; +// 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()); } -simdjson_inline size_t json_iterator::remaining_len() const noexcept { - return dom_parser.len - *(next_structural-1); + +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 json_iterator::at_eof() const noexcept { - return next_structural == &dom_parser.structural_indexes[dom_parser.n_structural_indexes]; +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_inline bool json_iterator::at_beginning() const noexcept { - return next_structural == dom_parser.structural_indexes.get(); + +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 doesn't 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 uint8_t json_iterator::last_structural() const noexcept { - return buf[dom_parser.structural_indexes[dom_parser.n_structural_indexes - 1]]; + +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 void json_iterator::log_value(const char *type) const noexcept { - logger::log_line(*this, "", type, ""); +} // unnamed namespace + +/** @private */ +template +error_code slow_float_parsing(simdjson_unused const uint8_t * src, W writer) { + double d; + if (parse_float_fallback(src, &d)) { + writer.append_double(d); + return SUCCESS; + } + return INVALID_NUMBER(src); } -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++; } +/** @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: This makes a *copy* of the writer and passes it to slow_float_parsing. This happens + // because slow_float_parsing is a non-inlined function. 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. + // This is what forces the skip_double, as well. + error_code error = slow_float_parsing(src, writer); + writer.skip_double(); + 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; } -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, ""); +// 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 } -simdjson_inline void json_iterator::log_error(const char *error) const noexcept { - logger::log_line(*this, "", "ERROR", error); +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_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 INVALID_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 INVALID_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 doesn't 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; } -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; +// 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; } -template -simdjson_warn_unused simdjson_inline error_code json_iterator::visit_primitive(V &visitor, const uint8_t *value) noexcept { - switch (*value) { - case '"': return visitor.visit_string(*this, 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); - case '-': - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - return visitor.visit_number(*this, value); - default: - log_error("Non-value found when value was expected!"); - return TAPE_ERROR; + + +// 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; } -} // namespace stage2 -} // unnamed namespace -} // namespace icelake -} // namespace simdjson -/* end file src/generic/stage2/json_iterator.h */ -/* begin file src/generic/stage2/tape_writer.h */ -namespace simdjson { -namespace icelake { -namespace { -namespace stage2 { +// 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; } + } -struct tape_writer { - /** The next place to write to tape */ - uint64_t *next_tape_loc; + return i; +} - /** Write a signed 64-bit value to tape. */ - simdjson_inline void append_s64(int64_t value) noexcept; +// 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); - /** Write an unsigned 64-bit value to tape. */ - simdjson_inline void append_u64(uint64_t value) noexcept; + // + // Parse the integer part. + // + // PERF NOTE: we don't use 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); - /** Write a double value to tape. */ - simdjson_inline void append_double(double value) noexcept; + // + // Parse the integer part. + // + // PERF NOTE: we don't use 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; - /** - * 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; + // + // Parse the integer part. + // + // PERF 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); - /** - * 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; + // + // 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; } - /** - * Skip the number of tape entries necessary to write a large u64 or i64. - */ - simdjson_inline void skip_large_integer() noexcept; + // + // 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 = start_digits-src > 19; + } + } else { + overflow = p-src > 19; + } - /** - * Skip the number of tape entries necessary to write a double. - */ - simdjson_inline void skip_double() noexcept; + // + // Parse the exponent + // + if (*p == 'e' || *p == 'E') { + p++; + bool exp_neg = *p == '-'; + p += exp_neg || *p == '+'; - /** - * 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; + 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; } -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 number_writer + exponent += exp_neg ? 0-exp : exp; + } -simdjson_inline void tape_writer::append_s64(int64_t value) noexcept { - append2(0, value, internal::tape_type::INT64); -} + if (jsoncharutils::is_not_structural_or_whitespace(*p)) { return NUMBER_ERROR; } -simdjson_inline void tape_writer::append_u64(uint64_t value) noexcept { - append(0, internal::tape_type::UINT64); - *next_tape_loc = value; - next_tape_loc++; -} + overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power; -/** Write a double value to tape. */ -simdjson_inline void tape_writer::append_double(double value) noexcept { - append2(0, value, internal::tape_type::DOUBLE); + // + // 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_inline void tape_writer::skip() noexcept { - next_tape_loc++; +simdjson_unused simdjson_inline bool is_negative(const uint8_t * src) noexcept { + return (*src == '-'); } -simdjson_inline void tape_writer::skip_large_integer() noexcept { - next_tape_loc += 2; +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_inline void tape_writer::skip_double() noexcept { - next_tape_loc += 2; +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++; } + if ( p == src ) { return NUMBER_ERROR; } + if (jsoncharutils::is_structural_or_whitespace(*p)) { + // We have an integer. + // If the number is negative and valid, it must be a signed integer. + if(negative) { return number_type::signed_integer; } + // We want values larger or equal to 9223372036854775808 to be unsigned + // integers, and the other values to be signed integers. + int digit_count = int(p - src); + if(digit_count >= 19) { + const uint8_t * smaller_big_integer = reinterpret_cast("9223372036854775808"); + if((digit_count >= 20) || (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; } -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++; -} +// 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); -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++; -} + // + // 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; } -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); -} + // + // 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; + } -} // namespace stage2 -} // unnamed namespace -} // namespace icelake -} // namespace simdjson -/* end file src/generic/stage2/tape_writer.h */ + // + // 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 == '+'; -namespace simdjson { -namespace icelake { -namespace { -namespace stage2 { + 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; } -struct tape_builder { - template - simdjson_warn_unused static simdjson_inline error_code parse_document( - dom_parser_implementation &dom_parser, - dom::document &doc) noexcept; + exponent += exp_neg ? 0-exp : exp; + } - /** 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; + if ((p != src_end) && jsoncharutils::is_not_structural_or_whitespace(*p)) { return NUMBER_ERROR; } - /** 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; + overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power; - /** 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; + // + // 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; +} - /** - * 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_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; - 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; + // + // 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; } - 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; + // + // 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 = start_digits-src > 19; + } + } else { + overflow = p-src > 19; + } - /** 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; + // + // Parse the exponent + // + if (*p == 'e' || *p == 'E') { + p++; + bool exp_neg = *p == '-'; + p += exp_neg || *p == '+'; - /** 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; + 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; } - simdjson_inline tape_builder(dom::document &doc) noexcept; + exponent += exp_neg ? 0-exp : exp; + } - 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; -}; // class tape_builder + if (*p != '"') { return NUMBER_ERROR; } -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); -} + overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power; -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); + // + // 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_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; -} +} // unnamed namespace +#endif // SIMDJSON_SKIPNUMBERPARSING -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); -} +} // namespace numberparsing -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; +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; + default: SIMDJSON_UNREACHABLE(); + } + return out; } -simdjson_inline tape_builder::tape_builder(dom::document &doc) noexcept : tape{doc.tape.get()}, current_string_buf_loc{doc.string_buf.get()} {} +} // namespace ppc64 +} // namespace simdjson -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; -} +#endif // SIMDJSON_GENERIC_NUMBERPARSING_H +/* end file simdjson/generic/numberparsing.h for ppc64 */ -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); -} +/* including simdjson/generic/implementation_simdjson_result_base-inl.h for ppc64: #include "simdjson/generic/implementation_simdjson_result_base-inl.h" */ +/* begin file simdjson/generic/implementation_simdjson_result_base-inl.h for ppc64 */ +#ifndef SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_INL_H -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); -} +/* 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 */ -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; +namespace simdjson { +namespace ppc64 { + +// +// 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; + } } -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; +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; } -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; +template +simdjson_inline error_code implementation_simdjson_result_base::error() const noexcept { + return this->second; } -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; +#if SIMDJSON_EXCEPTIONS + +template +simdjson_inline T& implementation_simdjson_result_base::value() & noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return this->first; } -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; +template +simdjson_inline T&& implementation_simdjson_result_base::value() && noexcept(false) { + return std::forward>(*this).take_value(); } -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; +template +simdjson_inline T&& implementation_simdjson_result_base::take_value() && noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return std::forward(this->first); } -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; +template +simdjson_inline implementation_simdjson_result_base::operator T&&() && noexcept(false) { + return std::forward>(*this).take_value(); } -// private: +#endif // SIMDJSON_EXCEPTIONS -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()); +template +simdjson_inline const T& implementation_simdjson_result_base::value_unsafe() const& noexcept { + return this->first; } -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; +template +simdjson_inline T& implementation_simdjson_result_base::value_unsafe() & noexcept { + return this->first; } -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. +template +simdjson_inline T&& implementation_simdjson_result_base::value_unsafe() && noexcept { + return std::forward(this->first); } -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; +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 simdjson + +#endif // SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_INL_H +/* end file simdjson/generic/implementation_simdjson_result_base-inl.h for ppc64 */ +/* end file simdjson/generic/amalgamated.h for ppc64 */ +/* including simdjson/ppc64/end.h: #include "simdjson/ppc64/end.h" */ +/* begin file simdjson/ppc64/end.h */ +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #include "simdjson/ppc64/base.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +#undef SIMDJSON_SKIP_BACKSLASH_SHORT_CIRCUIT +/* undefining SIMDJSON_IMPLEMENTATION from "ppc64" */ +#undef SIMDJSON_IMPLEMENTATION +/* end file simdjson/ppc64/end.h */ + +#endif // SIMDJSON_PPC64_H +/* end file simdjson/ppc64.h */ +/* including simdjson/ppc64/implementation.h: #include */ +/* begin file simdjson/ppc64/implementation.h */ +#ifndef SIMDJSON_PPC64_IMPLEMENTATION_H +#define SIMDJSON_PPC64_IMPLEMENTATION_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #include "simdjson/ppc64/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/implementation.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/internal/instruction_set.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { + +/** + * Implementation for ALTIVEC (PPC64). + */ +namespace ppc64 { + +/** + * @private + */ +class implementation final : public simdjson::implementation { +public: + simdjson_inline implementation() + : simdjson::implementation("ppc64", "PPC64 ALTIVEC", + internal::instruction_set::ALTIVEC) {} + + simdjson_warn_unused error_code create_dom_parser_implementation( + size_t capacity, size_t max_length, + std::unique_ptr &dst) + const noexcept final; + simdjson_warn_unused error_code minify(const uint8_t *buf, size_t len, + uint8_t *dst, + size_t &dst_len) const noexcept final; + simdjson_warn_unused bool validate_utf8(const char *buf, + size_t len) const noexcept final; +}; + +} // namespace ppc64 +} // namespace simdjson + +#endif // SIMDJSON_PPC64_IMPLEMENTATION_H +/* end file simdjson/ppc64/implementation.h */ + +/* including simdjson/ppc64/begin.h: #include */ +/* 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 } -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); +#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 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 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 } -} // namespace stage2 } // unnamed namespace -} // namespace icelake +} // namespace ppc64 } // namespace simdjson -/* end file src/generic/stage2/tape_builder.h */ -// -// Implementation-specific overrides -// +#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 icelake { +namespace ppc64 { namespace { -namespace stage1 { -simdjson_inline uint64_t json_string_scanner::find_escaped(uint64_t backslash) { - if (!backslash) { uint64_t escaped = prev_escaped; prev_escaped = 0; return escaped; } - return find_escaped_branchless(backslash); +// +// 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; } -} // namespace stage1 } // unnamed namespace +} // namespace ppc64 +} // namespace simdjson -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); -} +#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 */ -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); -} +#include -simdjson_warn_unused bool implementation::validate_utf8(const char *buf, size_t len) const noexcept { - return icelake::stage1::generic_validate_utf8(buf,len); -} +#if defined(__linux__) +#include +#elif defined(__FreeBSD__) +#include +#endif -simdjson_warn_unused error_code dom_parser_implementation::stage2(dom::document &_doc) noexcept { - return stage2::tape_builder::parse_document(*this, _doc); +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 +#ifdef _M_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 // _M_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; } -simdjson_warn_unused error_code dom_parser_implementation::stage2_next(dom::document &_doc) noexcept { - return stage2::tape_builder::parse_document(*this, _doc); -} +} // namespace numberparsing +} // namespace ppc64 +} // namespace simdjson -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); -} +#define SIMDJSON_SWAR_NUMBER_PARSING 1 -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); -} +#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 -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); -} +/* 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]; + + 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]); + } -} // namespace icelake -} // namespace simdjson + 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); + } -/* begin file include/simdjson/icelake/end.h */ -SIMDJSON_UNTARGET_ICELAKE -/* end file include/simdjson/icelake/end.h */ -/* end file src/icelake/dom_parser_implementation.cpp */ -#endif -#if SIMDJSON_IMPLEMENTATION_HASWELL -/* begin file src/haswell/implementation.cpp */ -/* begin file include/simdjson/haswell/begin.h */ -// redefining SIMDJSON_IMPLEMENTATION to "haswell" -// #define SIMDJSON_IMPLEMENTATION haswell -SIMDJSON_TARGET_HASWELL -/* end file include/simdjson/haswell/begin.h */ + 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); + } -namespace simdjson { -namespace haswell { + 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_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_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(); + } -} // namespace haswell -} // namespace simdjson + 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 -/* begin file include/simdjson/haswell/end.h */ -SIMDJSON_UNTARGET_HASWELL -/* end file include/simdjson/haswell/end.h */ +} // namespace simd +} // unnamed namespace +} // namespace ppc64 +} // namespace simdjson -/* end file src/haswell/implementation.cpp */ -/* begin file src/haswell/dom_parser_implementation.cpp */ -/* begin file include/simdjson/haswell/begin.h */ -// redefining SIMDJSON_IMPLEMENTATION to "haswell" -// #define SIMDJSON_IMPLEMENTATION haswell -SIMDJSON_TARGET_HASWELL -/* end file include/simdjson/haswell/begin.h */ +#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 -// -// Stage 1 -// +/* 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 haswell { +namespace ppc64 { namespace { using namespace simd; -struct json_character_block { - static simdjson_inline json_character_block classify(const simd::simd8x64& in); - // ASCII white-space ('\r','\n','\t',' ') - simdjson_inline uint64_t whitespace() const noexcept; - // non-quote structural characters (comma, colon, braces, brackets) - simdjson_inline uint64_t op() const noexcept; - // neither a structural character nor a white-space, so letters, numbers and quotes - simdjson_inline uint64_t scalar() const noexcept; - - uint64_t _whitespace; // ASCII white-space ('\r','\n','\t',' ') - uint64_t _op; // structural characters (comma, colon, braces, brackets but not quotes) -}; - -simdjson_inline uint64_t json_character_block::whitespace() const noexcept { return _whitespace; } -simdjson_inline uint64_t json_character_block::op() const noexcept { return _op; } -simdjson_inline uint64_t json_character_block::scalar() const noexcept { return ~(op() | whitespace()); } - -// 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); +// 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 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 + }; +} - // 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 - ); +} // unnamed namespace +} // namespace ppc64 +} // namespace simdjson - // 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). +#endif // SIMDJSON_PPC64_STRINGPARSING_DEFS_H +/* end file simdjson/ppc64/stringparsing_defs.h */ - 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]) - }); +#define SIMDJSON_SKIP_BACKSLASH_SHORT_CIRCUIT 1 +/* end file simdjson/ppc64/begin.h */ +/* including generic/amalgamated.h for ppc64: #include */ +/* begin file generic/amalgamated.h for ppc64 */ +#if defined(SIMDJSON_CONDITIONAL_INCLUDE) && !defined(SIMDJSON_SRC_GENERIC_DEPENDENCIES_H) +#error generic/dependencies.h must be included before generic/amalgamated.h! +#endif - return { whitespace, op }; -} +/* including generic/base.h for ppc64: #include */ +/* begin file generic/base.h for ppc64 */ +#ifndef SIMDJSON_SRC_GENERIC_BASE_H -simdjson_inline bool is_ascii(const simd8x64& input) { - return input.reduce_or().is_ascii(); -} +/* 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 */ -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); -} +namespace simdjson { +namespace ppc64 { +namespace { -simdjson_inline simd8 must_be_2_3_continuation(const simd8 prev2, const simd8 prev3) { - 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_third_byte | is_fourth_byte) > int8_t(0); -} +struct json_character_block; } // unnamed namespace -} // namespace haswell +} // namespace ppc64 } // namespace simdjson -/* begin file src/generic/stage1/utf8_lookup4_algorithm.h */ +#endif // SIMDJSON_SRC_GENERIC_BASE_H +/* end file generic/base.h for ppc64 */ +/* including generic/dom_parser_implementation.h for ppc64: #include */ +/* begin file generic/dom_parser_implementation.h for ppc64 */ +#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 haswell { +namespace ppc64 { 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____ +simdjson_inline simd8 must_be_2_3_continuation(const simd8 prev2, const simd8 prev3); +simdjson_inline bool is_ascii(const simd8x64& input); - 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, +} // unnamed namespace +} // namespace ppc64 +} // namespace simdjson - // ____0100 ________ - CARRY | TOO_LARGE, - // ____0101 ________ - CARRY | TOO_LARGE | TOO_LARGE_1000, - // ____011_ ________ - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, +#endif // SIMDJSON_SRC_GENERIC_DOM_PARSER_IMPLEMENTATION_H +/* end file generic/dom_parser_implementation.h for ppc64 */ +/* including generic/json_character_block.h for ppc64: #include */ +/* begin file generic/json_character_block.h for ppc64 */ +#ifndef SIMDJSON_SRC_GENERIC_JSON_CHARACTER_BLOCK_H - // ____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, +/* 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 */ - // ________ 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, +namespace simdjson { +namespace ppc64 { +namespace { - // ________ 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 = simd8(must_be_2_3_continuation(prev2, prev3)); - simd8 must23_80 = must23 & uint8_t(0x80); - return must23_80 ^ sc; - } +struct json_character_block { + static simdjson_inline json_character_block classify(const simd::simd8x64& in); - // - // 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 - }; -#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); - } + 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()); } - 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; + uint64_t _whitespace; + uint64_t _op; +}; - // - // 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); - } +} // unnamed namespace +} // namespace ppc64 +} // namespace simdjson - // 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; - } +#endif // SIMDJSON_SRC_GENERIC_JSON_CHARACTER_BLOCK_H +/* end file generic/json_character_block.h for ppc64 */ +/* end file generic/amalgamated.h for ppc64 */ +/* including generic/stage1/amalgamated.h for ppc64: #include */ +/* begin file generic/stage1/amalgamated.h for ppc64 */ +// Stuff other things depend on +/* including generic/stage1/base.h for ppc64: #include */ +/* begin file generic/stage1/base.h for ppc64 */ +#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 */ -#ifndef SIMDJSON_IF_CONSTEXPR -#if SIMDJSON_CPLUSPLUS17 -#define SIMDJSON_IF_CONSTEXPR if constexpr -#else -#define SIMDJSON_IF_CONSTEXPR if -#endif -#endif +namespace simdjson { +namespace ppc64 { +namespace { +namespace stage1 { - 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]); - } 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; - } +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; - }; // struct utf8_checker +} // namespace stage1 + +namespace utf8_validation { +struct utf8_checker; } // namespace utf8_validation using utf8_validation::utf8_checker; } // unnamed namespace -} // namespace haswell +} // namespace ppc64 } // namespace simdjson -/* end file src/generic/stage1/utf8_lookup4_algorithm.h */ -/* begin file src/generic/stage1/json_structural_indexer.h */ -// 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) -/* begin file src/generic/stage1/buf_block_reader.h */ +#endif // SIMDJSON_SRC_GENERIC_STAGE1_BASE_H +/* end file generic/stage1/base.h for ppc64 */ +/* including generic/stage1/buf_block_reader.h for ppc64: #include */ +/* begin file generic/stage1/buf_block_reader.h for ppc64 */ +#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 haswell { +namespace ppc64 { namespace { +namespace stage1 { // Walks through a buffer in block-sized increments, loading the last part with spaces template @@ -9694,6 +33809,17 @@ simdjson_unused static char * format_input_text(const simd8x64& in) { 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++) { @@ -9732,45 +33858,203 @@ simdjson_inline void buf_block_reader::advance() { idx += STEP_SIZE; } +} // namespace stage1 } // unnamed namespace -} // namespace haswell +} // namespace ppc64 } // namespace simdjson -/* end file src/generic/stage1/buf_block_reader.h */ -/* begin file src/generic/stage1/json_string_scanner.h */ + +#endif // SIMDJSON_SRC_GENERIC_STAGE1_BUF_BLOCK_READER_H +/* end file generic/stage1/buf_block_reader.h for ppc64 */ +/* including generic/stage1/json_escape_scanner.h for ppc64: #include */ +/* begin file generic/stage1/json_escape_scanner.h for ppc64 */ +#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 haswell { +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; + + 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 ppc64 +} // namespace simdjson + +#endif // SIMDJSON_SRC_GENERIC_STAGE1_JSON_STRING_SCANNER_H +/* end file generic/stage1/json_escape_scanner.h for ppc64 */ +/* including generic/stage1/json_string_scanner.h for ppc64: #include */ +/* begin file generic/stage1/json_string_scanner.h for ppc64 */ +#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 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_inline json_string_block(uint64_t backslash, uint64_t escaped, uint64_t quote, uint64_t in_string) : - _backslash(backslash), _escaped(escaped), _quote(quote), _in_string(in_string) {} + 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_inline uint64_t escaped() const { return _escaped; } - // Escape characters (backslashes that are not escaped--i.e. in \\, includes only the first \) - simdjson_inline uint64_t escape() const { return _backslash & ~_escaped; } + simdjson_really_inline uint64_t escaped() const { return _escaped; } // Real (non-backslashed) quotes - simdjson_inline uint64_t quote() const { return _quote; } - // Start quotes of strings - simdjson_inline uint64_t string_start() const { return _quote & _in_string; } - // End quotes of strings - simdjson_inline uint64_t string_end() const { return _quote & ~_in_string; } + simdjson_really_inline uint64_t quote() const { return _quote; } // Only characters inside the string (not including the quotes) - simdjson_inline uint64_t string_content() const { return _in_string & ~_quote; } + 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_inline uint64_t non_quote_inside_string(uint64_t mask) const { return mask & _in_string; } + 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_inline uint64_t non_quote_outside_string(uint64_t mask) const { return mask & ~_in_string; } + 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_inline uint64_t string_tail() const { return _in_string ^ _quote; } + simdjson_really_inline uint64_t string_tail() const { return _in_string ^ _quote; } - // backslash characters - uint64_t _backslash; // escaped characters (backslashed--does not include the hex characters after \u) uint64_t _escaped; - // real quotes (non-backslashed ones) + // real quotes (non-escaped ones) uint64_t _quote; // string characters (includes start quote but not end quote) uint64_t _in_string; @@ -9779,121 +34063,297 @@ struct json_string_block { // Scans blocks for string characters, storing the state necessary to do so class json_string_scanner { public: - simdjson_inline json_string_block next(const simd::simd8x64& in); + simdjson_really_inline json_string_block next(const simd::simd8x64& in); // Returns either UNCLOSED_STRING or SUCCESS - simdjson_inline error_code finish(); + simdjson_really_inline error_code finish(); private: - // Intended to be defined by the implementation - simdjson_inline uint64_t find_escaped(uint64_t escape); - simdjson_inline uint64_t find_escaped_branchless(uint64_t escape); - + // 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; - // Whether the first character of the next iteration is escaped. - uint64_t prev_escaped = 0ULL; }; -// -// Finds escaped characters (characters following \). -// -// Handles runs of backslashes like \\\" and \\\\" correctly (yielding 0101 and 01010, respectively). -// -// Does this by: -// - Shift the escape mask to get potentially escaped characters (characters after backslashes). -// - Mask escaped sequences that start on *even* bits with 1010101010 (odd bits are escaped, even bits are not) -// - Mask escaped sequences that start on *odd* bits with 0101010101 (even bits are escaped, odd bits are not) -// -// To distinguish between escaped sequences starting on even/odd bits, it finds the start of all -// escape sequences, filters out the ones that start on even bits, and adds that to the mask of -// escape sequences. This causes the addition to clear out the sequences starting on odd bits (since -// the start bit causes a carry), and leaves even-bit sequences alone. -// -// Example: -// -// text | \\\ | \\\"\\\" \\\" \\"\\" | -// escape | xxx | xx xxx xxx xx xx | Removed overflow backslash; will | it into follows_escape -// odd_starts | x | x x x | escape & ~even_bits & ~follows_escape -// even_seq | c| cxxx c xx c | c = carry bit -- will be masked out later -// invert_mask | | cxxx c xx c| even_seq << 1 -// follows_escape | xx | x xx xxx xxx xx xx | Includes overflow bit -// escaped | x | x x x x x x x x | -// desired | x | x x x x x x x x | -// text | \\\ | \\\"\\\" \\\" \\"\\" | -// -simdjson_inline uint64_t json_string_scanner::find_escaped_branchless(uint64_t backslash) { - // If there was overflow, pretend the first character isn't a backslash - backslash &= ~prev_escaped; - uint64_t follows_escape = backslash << 1 | prev_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; + + // + // 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 ppc64 +} // namespace simdjson + +#endif // SIMDJSON_SRC_GENERIC_STAGE1_JSON_STRING_SCANNER_H +/* end file generic/stage1/json_string_scanner.h for ppc64 */ +/* including generic/stage1/utf8_lookup4_algorithm.h for ppc64: #include */ +/* begin file generic/stage1/utf8_lookup4_algorithm.h for ppc64 */ +#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 ppc64 { +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, - // Get sequences starting on even bits by clearing out the odd series using + - const uint64_t even_bits = 0x5555555555555555ULL; - uint64_t odd_sequence_starts = backslash & ~even_bits & ~follows_escape; - uint64_t sequences_starting_on_even_bits; - prev_escaped = add_overflow(odd_sequence_starts, backslash, &sequences_starting_on_even_bits); - uint64_t invert_mask = sequences_starting_on_even_bits << 1; // The mask we want to return is the *escaped* bits, not escapes. + // ____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, - // Mask every other backslashed character as an escaped character - // Flip the mask for sequences that start on even bits, to correct them - return (even_bits ^ invert_mask) & follows_escape; -} + // ________ 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, -// -// 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_inline json_string_block json_string_scanner::next(const simd::simd8x64& in) { - const uint64_t backslash = in.eq('\\'); - const uint64_t escaped = find_escaped(backslash); - const uint64_t quote = in.eq('"') & ~escaped; + // ________ 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 = simd8(must_be_2_3_continuation(prev2, prev3)); + simd8 must23_80 = must23 & uint8_t(0x80); + return must23_80 ^ sc; + } // - // 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). + // 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. // - const uint64_t in_string = prefix_xor(quote) ^ prev_in_string; + 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 + }; +#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); + } - // - // Check if we're still in a string at the end of the box so the next block will know - // - // right shift of a signed value expected to be well-defined and standard - // compliant as of C++20, John Regher from Utah U. says this is fine code - // - prev_in_string = uint64_t(static_cast(in_string) >> 63); + 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; - // Use ^ to turn the beginning quote off, and the end quote on. + // + // 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); + } - // We are returning a function-local object so either we get a move constructor - // or we get copy elision. - return json_string_block( - backslash, - escaped, - quote, - in_string - ); -} + // 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 error_code json_string_scanner::finish() { - if (prev_in_string) { - return UNCLOSED_STRING; - } - return SUCCESS; -} +#ifndef SIMDJSON_IF_CONSTEXPR +#if SIMDJSON_CPLUSPLUS17 +#define SIMDJSON_IF_CONSTEXPR if constexpr +#else +#define SIMDJSON_IF_CONSTEXPR if +#endif +#endif + + 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]); + } 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; + } + + }; // struct utf8_checker +} // namespace utf8_validation -} // namespace stage1 } // unnamed namespace -} // namespace haswell +} // namespace ppc64 } // namespace simdjson -/* end file src/generic/stage1/json_string_scanner.h */ -/* begin file src/generic/stage1/json_scanner.h */ + +#endif // SIMDJSON_SRC_GENERIC_STAGE1_UTF8_LOOKUP4_ALGORITHM_H +/* end file generic/stage1/utf8_lookup4_algorithm.h for ppc64 */ +/* including generic/stage1/json_scanner.h for ppc64: #include */ +/* begin file generic/stage1/json_scanner.h for ppc64 */ +#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 haswell { +namespace ppc64 { namespace { namespace stage1 { @@ -10034,121 +34494,40 @@ simdjson_inline json_block json_scanner::next(const simd::simd8x64& in) 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(); -} - -} // namespace stage1 -} // unnamed namespace -} // namespace haswell -} // namespace simdjson -/* end file src/generic/stage1/json_scanner.h */ -/* begin file src/generic/stage1/json_minifier.h */ -// 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 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; - -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; -}; - -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; -} - -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(); + return json_block( + strings,// strings is a function-local object so either it moves or the copy is elided. + characters, + follows_nonquote_scalar + ); } -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); +simdjson_inline error_code json_scanner::finish() { + return string_scanner.finish(); } } // namespace stage1 } // unnamed namespace -} // namespace haswell +} // namespace ppc64 } // namespace simdjson -/* end file src/generic/stage1/json_minifier.h */ -/* begin file src/generic/stage1/find_next_document_index.h */ + +#endif // SIMDJSON_SRC_GENERIC_STAGE1_JSON_SCANNER_H +/* end file generic/stage1/json_scanner.h for ppc64 */ + +// All other declarations +/* including generic/stage1/find_next_document_index.h for ppc64: #include */ +/* begin file generic/stage1/find_next_document_index.h for ppc64 */ +#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 haswell { +namespace ppc64 { namespace { +namespace stage1 { /** * This algorithm is used to quickly identify the last structural position that @@ -10236,13 +34615,142 @@ simdjson_inline uint32_t find_next_document_index(dom_parser_implementation &par return 0; } +} // namespace stage1 } // unnamed namespace -} // namespace haswell +} // namespace ppc64 } // namespace simdjson -/* end file src/generic/stage1/find_next_document_index.h */ + +#endif // SIMDJSON_SRC_GENERIC_STAGE1_FIND_NEXT_DOCUMENT_INDEX_H +/* end file generic/stage1/find_next_document_index.h for ppc64 */ +/* including generic/stage1/json_minifier.h for ppc64: #include */ +/* begin file generic/stage1/json_minifier.h for ppc64 */ +#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 haswell { +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; + +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; +}; + +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; +} + +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 ppc64 +} // namespace simdjson + +#endif // SIMDJSON_SRC_GENERIC_STAGE1_JSON_MINIFIER_H +/* end file generic/stage1/json_minifier.h for ppc64 */ +/* including generic/stage1/json_structural_indexer.h for ppc64: #include */ +/* begin file generic/stage1/json_structural_indexer.h for ppc64 */ +#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 ppc64 { namespace { namespace stage1 { @@ -10258,9 +34766,9 @@ class bit_indexer { // 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_CUSTOM_BIT_INDEXER, then it will provide its own - // version of the code. -#ifdef SIMDJSON_CUSTOM_BIT_INDEXER + // 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) { @@ -10355,7 +34863,7 @@ class bit_indexer { this->tail += cnt; #endif } -#endif // SIMDJSON_CUSTOM_BIT_INDEXER +#endif // SIMDJSON_GENERIC_JSON_STRUCTURAL_INDEXER_CUSTOM_BIT_INDEXER }; @@ -10584,12 +35092,27 @@ simdjson_inline error_code json_structural_indexer::finish(dom_parser_implementa } // namespace stage1 } // unnamed namespace -} // namespace haswell +} // namespace ppc64 } // namespace simdjson -/* end file src/generic/stage1/json_structural_indexer.h */ -/* begin file src/generic/stage1/utf8_validator.h */ + +// 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 ppc64 */ +/* including generic/stage1/utf8_validator.h for ppc64: #include */ +/* begin file generic/stage1/utf8_validator.h for ppc64 */ +#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 haswell { +namespace ppc64 { namespace { namespace stage1 { @@ -10620,260 +35143,177 @@ bool generic_validate_utf8(const char * input, size_t length) { } // namespace stage1 } // unnamed namespace -} // namespace haswell +} // namespace ppc64 } // namespace simdjson -/* end file src/generic/stage1/utf8_validator.h */ -// -// Stage 2 -// -/* begin file src/generic/stage2/stringparsing.h */ -// This file contains the common code every implementation uses -// It is intended to be included multiple times and compiled multiple times +#endif // SIMDJSON_SRC_GENERIC_STAGE1_UTF8_VALIDATOR_H +/* end file generic/stage1/utf8_validator.h for ppc64 */ +/* end file generic/stage1/amalgamated.h for ppc64 */ +/* including generic/stage2/amalgamated.h for ppc64: #include */ +/* begin file generic/stage2/amalgamated.h for ppc64 */ +// Stuff other things depend on +/* including generic/stage2/base.h for ppc64: #include */ +/* begin file generic/stage2/base.h for ppc64 */ +#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 haswell { +namespace ppc64 { namespace { -/// @private -namespace stringparsing { +namespace stage2 { -// 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, +class json_iterator; +class structural_iterator; +struct tape_builder; +struct tape_writer; - 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. +} // namespace stage2 +} // unnamed namespace +} // namespace ppc64 +} // namespace simdjson - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +#endif // SIMDJSON_SRC_GENERIC_STAGE2_BASE_H +/* end file generic/stage2/base.h for ppc64 */ +/* including generic/stage2/tape_writer.h for ppc64: #include */ +/* begin file generic/stage2/tape_writer.h for ppc64 */ +#ifndef SIMDJSON_SRC_GENERIC_STAGE2_TAPE_WRITER_H - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -}; +/* 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 */ -// 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 isn't 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; +#include + +namespace simdjson { +namespace ppc64 { +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; - // 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); + /** + * 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; - // 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; - } +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 - } - } 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; +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++; +} -// 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 isn't 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; - } - } - } +/** Write a double value to tape. */ +simdjson_inline void tape_writer::append_double(double value) noexcept { + append2(0, value, internal::tape_type::DOUBLE); +} - size_t offset = jsoncharutils::codepoint_to_utf8(code_point, *dst_ptr); - *dst_ptr += offset; - return offset > 0; +simdjson_inline void tape_writer::skip() noexcept { + next_tape_loc++; } +simdjson_inline void tape_writer::skip_large_integer() noexcept { + next_tape_loc += 2; +} -/** - * 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_nullptrptr 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; - } - } - /* can't be reached */ - return nullptr; +simdjson_inline void tape_writer::skip_double() noexcept { + next_tape_loc += 2; } -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; - } - } - /* can't be reached */ - return nullptr; +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++; } -} // namespace stringparsing +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 haswell +} // namespace ppc64 } // namespace simdjson -/* end file src/generic/stage2/stringparsing.h */ -/* begin file src/generic/stage2/tape_builder.h */ -/* begin file src/generic/stage2/json_iterator.h */ -/* begin file src/generic/stage2/logger.h */ + +#endif // SIMDJSON_SRC_GENERIC_STAGE2_TAPE_WRITER_H +/* end file generic/stage2/tape_writer.h for ppc64 */ +/* including generic/stage2/logger.h for ppc64: #include */ +/* begin file generic/stage2/logger.h for ppc64 */ +#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 haswell { +namespace ppc64 { namespace { namespace logger { @@ -10956,12 +35396,26 @@ namespace logger { } // namespace logger } // unnamed namespace -} // namespace haswell +} // namespace ppc64 } // namespace simdjson -/* end file src/generic/stage2/logger.h */ + +#endif // SIMDJSON_SRC_GENERIC_STAGE2_LOGGER_H +/* end file generic/stage2/logger.h for ppc64 */ + +// All other declarations +/* including generic/stage2/json_iterator.h for ppc64: #include */ +/* begin file generic/stage2/json_iterator.h for ppc64 */ +#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 haswell { +namespace ppc64 { namespace { namespace stage2 { @@ -11271,121 +35725,350 @@ simdjson_warn_unused simdjson_inline error_code json_iterator::visit_primitive(V } } -} // namespace stage2 -} // unnamed namespace -} // namespace haswell -} // namespace simdjson -/* end file src/generic/stage2/json_iterator.h */ -/* begin file src/generic/stage2/tape_writer.h */ -namespace simdjson { -namespace haswell { -namespace { -namespace stage2 { +} // 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 */ +#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 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, + + 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. -struct tape_writer { - /** The next place to write to tape */ - uint64_t *next_tape_loc; + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - /** Write a signed 64-bit value to tape. */ - simdjson_inline void append_s64(int64_t value) noexcept; + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; - /** Write an unsigned 64-bit value to tape. */ - simdjson_inline void append_u64(uint64_t value) noexcept; +// 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 isn't 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; - /** Write a double value to tape. */ - simdjson_inline void append_double(double value) noexcept; + // 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); - /** - * 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; + // 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; + } - /** - * 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; + } + } 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; +} - /** - * 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; +// 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 isn't 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; + } + } + } - /** - * 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; + size_t offset = jsoncharutils::codepoint_to_utf8(code_point, *dst_ptr); + *dst_ptr += offset; + return offset > 0; +} -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 number_writer -simdjson_inline void tape_writer::append_s64(int64_t value) noexcept { - append2(0, value, internal::tape_type::INT64); +/** + * 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_nullptrptr 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; + } + } + /* can't be reached */ + return nullptr; } -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_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; + } + } + /* can't be reached */ + return nullptr; } -/** Write a double value to tape. */ -simdjson_inline void tape_writer::append_double(double value) noexcept { - append2(0, value, internal::tape_type::DOUBLE); -} +} // namespace stringparsing +} // unnamed namespace +} // namespace ppc64 +} // namespace simdjson -simdjson_inline void tape_writer::skip() noexcept { - next_tape_loc++; -} +#endif // SIMDJSON_SRC_GENERIC_STAGE2_STRINGPARSING_H +/* end file generic/stage2/stringparsing.h for ppc64 */ +/* including generic/stage2/structural_iterator.h for ppc64: #include */ +/* begin file generic/stage2/structural_iterator.h for ppc64 */ +#ifndef SIMDJSON_SRC_GENERIC_STAGE2_STRUCTURAL_ITERATOR_H -simdjson_inline void tape_writer::skip_large_integer() noexcept { - next_tape_loc += 2; -} +/* 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 */ -simdjson_inline void tape_writer::skip_double() noexcept { - next_tape_loc += 2; -} +namespace simdjson { +namespace ppc64 { +namespace { +namespace stage2 { -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++; -} +class structural_iterator { +public: + const uint8_t* const buf; + uint32_t *next_structural; + dom_parser_implementation &dom_parser; -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++; -} + // 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 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 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 ppc64 } // namespace simdjson -/* end file src/generic/stage2/tape_writer.h */ + +#endif // SIMDJSON_SRC_GENERIC_STAGE2_STRUCTURAL_ITERATOR_H +/* end file generic/stage2/structural_iterator.h for ppc64 */ +/* including generic/stage2/tape_builder.h for ppc64: #include */ +/* begin file generic/stage2/tape_builder.h for ppc64 */ +#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 haswell { +namespace ppc64 { namespace { namespace stage2 { @@ -11464,7 +36147,7 @@ struct tape_builder { 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; -}; // class tape_builder +}; // struct tape_builder template simdjson_warn_unused simdjson_inline error_code tape_builder::parse_document( @@ -11662,77 +36345,16 @@ simdjson_inline void tape_builder::on_end_string(uint8_t *dst) noexcept { } // namespace stage2 } // unnamed namespace -} // namespace haswell +} // namespace ppc64 } // namespace simdjson -/* end file src/generic/stage2/tape_builder.h */ + +#endif // SIMDJSON_SRC_GENERIC_STAGE2_TAPE_BUILDER_H +/* end file generic/stage2/tape_builder.h for ppc64 */ +/* end file generic/stage2/amalgamated.h for ppc64 */ // -// Implementation-specific overrides +// Stage 1 // -namespace simdjson { -namespace haswell { -namespace { -namespace stage1 { - -simdjson_inline uint64_t json_string_scanner::find_escaped(uint64_t backslash) { - if (!backslash) { uint64_t escaped = prev_escaped; prev_escaped = 0; return escaped; } - return find_escaped_branchless(backslash); -} - -} // namespace stage1 -} // unnamed namespace - -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 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_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_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); -} - -} // namespace haswell -} // namespace simdjson - -/* begin file include/simdjson/haswell/end.h */ -SIMDJSON_UNTARGET_HASWELL -/* end file include/simdjson/haswell/end.h */ -/* end file src/haswell/dom_parser_implementation.cpp */ -#endif -#if SIMDJSON_IMPLEMENTATION_PPC64 -/* begin file src/ppc64/implementation.cpp */ -/* begin file include/simdjson/ppc64/begin.h */ -// redefining SIMDJSON_IMPLEMENTATION to "ppc64" -// #define SIMDJSON_IMPLEMENTATION ppc64 -/* end file include/simdjson/ppc64/begin.h */ - namespace simdjson { namespace ppc64 { @@ -11750,38 +36372,10 @@ simdjson_warn_unused error_code implementation::create_dom_parser_implementation return SUCCESS; } -} // namespace ppc64 -} // namespace simdjson - -/* begin file include/simdjson/ppc64/end.h */ -/* end file include/simdjson/ppc64/end.h */ -/* end file src/ppc64/implementation.cpp */ -/* begin file src/ppc64/dom_parser_implementation.cpp */ -/* begin file include/simdjson/ppc64/begin.h */ -// redefining SIMDJSON_IMPLEMENTATION to "ppc64" -// #define SIMDJSON_IMPLEMENTATION ppc64 -/* end file include/simdjson/ppc64/begin.h */ - -// -// Stage 1 -// -namespace simdjson { -namespace ppc64 { namespace { using namespace simd; -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; -}; - 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); @@ -11834,2451 +36428,4740 @@ simdjson_inline simd8 must_be_2_3_continuation(const simd8 prev2, } // namespace ppc64 } // namespace simdjson -/* begin file src/generic/stage1/utf8_lookup4_algorithm.h */ +// +// Stage 2 +// + +// +// Implementation-specific overrides +// namespace simdjson { namespace ppc64 { -namespace { -namespace utf8_validation { -using namespace simd; +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_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_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); +} - 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, +simdjson_warn_unused bool implementation::validate_utf8(const char *buf, size_t len) const noexcept { + return ppc64::stage1::generic_validate_utf8(buf,len); +} - // ____0100 ________ - CARRY | TOO_LARGE, - // ____0101 ________ - CARRY | TOO_LARGE | TOO_LARGE_1000, - // ____011_ ________ - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, +simdjson_warn_unused error_code dom_parser_implementation::stage2(dom::document &_doc) noexcept { + return stage2::tape_builder::parse_document(*this, _doc); +} - // ____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, +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_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); +} + +} // namespace ppc64 +} // namespace simdjson + +/* including simdjson/ppc64/end.h: #include */ +/* 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_SRC_PPC64_CPP +/* end file ppc64.cpp */ +#endif +#if SIMDJSON_IMPLEMENTATION_WESTMERE +/* including westmere.cpp: #include */ +/* begin file westmere.cpp */ +#ifndef SIMDJSON_SRC_WESTMERE_CPP +#define SIMDJSON_SRC_WESTMERE_CPP + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #include */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +/* including simdjson/westmere.h: #include */ +/* begin file simdjson/westmere.h */ +#ifndef SIMDJSON_WESTMERE_H +#define SIMDJSON_WESTMERE_H + +/* including simdjson/westmere/begin.h: #include "simdjson/westmere/begin.h" */ +/* begin file simdjson/westmere/begin.h */ +/* defining SIMDJSON_IMPLEMENTATION to "westmere" */ +#define SIMDJSON_IMPLEMENTATION westmere +/* including simdjson/westmere/base.h: #include "simdjson/westmere/base.h" */ +/* begin file simdjson/westmere/base.h */ +#ifndef SIMDJSON_WESTMERE_BASE_H +#define SIMDJSON_WESTMERE_BASE_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #include "simdjson/base.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +// The constructor may be executed on any host, so we take care not to use SIMDJSON_TARGET_WESTMERE +namespace simdjson { +/** + * Implementation for Westmere (Intel SSE4.2). + */ +namespace westmere { - // ________ 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, +class implementation; - // ________ 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 = simd8(must_be_2_3_continuation(prev2, prev3)); - simd8 must23_80 = must23 & uint8_t(0x80); - return must23_80 ^ sc; - } +namespace { +namespace simd { - // - // 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 - }; +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 - 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 - }; +#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 - 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; +static_assert(sizeof(__m128i) <= simdjson::SIMDJSON_PADDING, "insufficient padding for westmere"); - // - // 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); - } +#endif // SIMDJSON_WESTMERE_INTRINSICS_H +/* end file simdjson/westmere/intrinsics.h */ - // 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; - } +#if !SIMDJSON_CAN_ALWAYS_RUN_WESTMERE +SIMDJSON_TARGET_REGION("sse4.2,pclmul,popcnt") +#endif -#ifndef SIMDJSON_IF_CONSTEXPR -#if SIMDJSON_CPLUSPLUS17 -#define SIMDJSON_IF_CONSTEXPR if constexpr +/* 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 -#define SIMDJSON_IF_CONSTEXPR if + 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 +} - 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]); - } 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; - } +} // unnamed namespace +} // namespace westmere +} // namespace simdjson - }; // struct utf8_checker -} // namespace utf8_validation +#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 -using utf8_validation::utf8_checker; +/* 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 ppc64 +} // namespace westmere } // namespace simdjson -/* end file src/generic/stage1/utf8_lookup4_algorithm.h */ -/* begin file src/generic/stage1/json_structural_indexer.h */ -// 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) -/* begin file src/generic/stage1/buf_block_reader.h */ +#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 { -namespace ppc64 { +/** + * Implementation for Westmere (Intel SSE4.2). + */ +namespace westmere { + +class implementation; + namespace { +namespace simd { -// 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; -}; +template struct simd8; +template struct simd8x64; -// 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; -} +} // namespace simd +} // unnamed namespace -// 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; +} // 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 +#ifdef _M_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 // _M_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; } -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; -} +} // 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) {} -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} {} + // Store to array + simdjson_inline void store(T dst[16]) const { return _mm_storeu_si128(reinterpret_cast<__m128i *>(dst), *this); } -template -simdjson_inline size_t buf_block_reader::block_index() { return idx; } + // Override to distinguish from bool version + simdjson_inline simd8 operator~() const { return *this ^ 0xFFu; } -template -simdjson_inline bool buf_block_reader::has_full_block() const { - return idx < lenminusstep; -} + // 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); } -template -simdjson_inline const uint8_t *buf_block_reader::full_block() const { - return &buf[idx]; -} + // 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); + } -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; -} + // 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 void buf_block_reader::advance() { - idx += STEP_SIZE; -} + 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 + )); + } + }; -} // unnamed namespace -} // namespace ppc64 -} // namespace simdjson -/* end file src/generic/stage1/buf_block_reader.h */ -/* begin file src/generic/stage1/json_string_scanner.h */ -namespace simdjson { -namespace ppc64 { -namespace { -namespace stage1 { + // 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 + ); + } -struct json_string_block { - // We spell out the constructors in the hope of resolving inlining issues with Visual Studio 2017 - simdjson_inline json_string_block(uint64_t backslash, uint64_t escaped, uint64_t quote, uint64_t in_string) : - _backslash(backslash), _escaped(escaped), _quote(quote), _in_string(in_string) {} + // 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); } + }; - // Escaped characters (characters following an escape() character) - simdjson_inline uint64_t escaped() const { return _escaped; } - // Escape characters (backslashes that are not escaped--i.e. in \\, includes only the first \) - simdjson_inline uint64_t escape() const { return _backslash & ~_escaped; } - // Real (non-backslashed) quotes - simdjson_inline uint64_t quote() const { return _quote; } - // Start quotes of strings - simdjson_inline uint64_t string_start() const { return _quote & _in_string; } - // End quotes of strings - simdjson_inline uint64_t string_end() const { return _quote & ~_in_string; } - // Only characters inside the string (not including the quotes) - simdjson_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_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_inline uint64_t non_quote_outside_string(uint64_t mask) const { return mask & ~_in_string; } - // Tail of string (everything except the start quote) - simdjson_inline uint64_t string_tail() const { return _in_string ^ _quote; } + // 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 + ); + } - // backslash characters - uint64_t _backslash; - // escaped characters (backslashed--does not include the hex characters after \u) - uint64_t _escaped; - // real quotes (non-backslashed ones) - uint64_t _quote; - // string characters (includes start quote but not end quote) - uint64_t _in_string; -}; + // 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)); } + }; -// Scans blocks for string characters, storing the state necessary to do so -class json_string_scanner { -public: - simdjson_inline json_string_block next(const simd::simd8x64& in); - // Returns either UNCLOSED_STRING or SUCCESS - simdjson_inline error_code finish(); + 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]; + + 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); + } -private: - // Intended to be defined by the implementation - simdjson_inline uint64_t find_escaped(uint64_t escape); - simdjson_inline uint64_t find_escaped_branchless(uint64_t escape); + simdjson_inline simd8 reduce_or() const { + return (this->chunks[0] | this->chunks[1]) | (this->chunks[2] | this->chunks[3]); + } - // Whether the last iteration was still inside a string (all 1's = true, all 0's = false). - uint64_t prev_in_string = 0ULL; - // Whether the first character of the next iteration is escaped. - uint64_t prev_escaped = 0ULL; -}; + 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); + } -// -// Finds escaped characters (characters following \). -// -// Handles runs of backslashes like \\\" and \\\\" correctly (yielding 0101 and 01010, respectively). -// -// Does this by: -// - Shift the escape mask to get potentially escaped characters (characters after backslashes). -// - Mask escaped sequences that start on *even* bits with 1010101010 (odd bits are escaped, even bits are not) -// - Mask escaped sequences that start on *odd* bits with 0101010101 (even bits are escaped, odd bits are not) -// -// To distinguish between escaped sequences starting on even/odd bits, it finds the start of all -// escape sequences, filters out the ones that start on even bits, and adds that to the mask of -// escape sequences. This causes the addition to clear out the sequences starting on odd bits (since -// the start bit causes a carry), and leaves even-bit sequences alone. -// -// Example: -// -// text | \\\ | \\\"\\\" \\\" \\"\\" | -// escape | xxx | xx xxx xxx xx xx | Removed overflow backslash; will | it into follows_escape -// odd_starts | x | x x x | escape & ~even_bits & ~follows_escape -// even_seq | c| cxxx c xx c | c = carry bit -- will be masked out later -// invert_mask | | cxxx c xx c| even_seq << 1 -// follows_escape | xx | x xx xxx xxx xx xx | Includes overflow bit -// escaped | x | x x x x x x x x | -// desired | x | x x x x x x x x | -// text | \\\ | \\\"\\\" \\\" \\"\\" | -// -simdjson_inline uint64_t json_string_scanner::find_escaped_branchless(uint64_t backslash) { - // If there was overflow, pretend the first character isn't a backslash - backslash &= ~prev_escaped; - uint64_t follows_escape = backslash << 1 | prev_escaped; + 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); + } - // Get sequences starting on even bits by clearing out the odd series using + - const uint64_t even_bits = 0x5555555555555555ULL; - uint64_t odd_sequence_starts = backslash & ~even_bits & ~follows_escape; - uint64_t sequences_starting_on_even_bits; - prev_escaped = add_overflow(odd_sequence_starts, backslash, &sequences_starting_on_even_bits); - uint64_t invert_mask = sequences_starting_on_even_bits << 1; // The mask we want to return is the *escaped* bits, not escapes. + 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(); + } - // Mask every other backslashed character as an escaped character - // Flip the mask for sequences that start on even bits, to correct them - return (even_bits ^ invert_mask) & follows_escape; -} + 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(); + } -// -// 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_inline json_string_block json_string_scanner::next(const simd::simd8x64& in) { - const uint64_t backslash = in.eq('\\'); - const uint64_t escaped = find_escaped(backslash); - const uint64_t quote = in.eq('"') & ~escaped; + 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 - // - // 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; +} // namespace simd +} // unnamed namespace +} // namespace westmere +} // namespace simdjson - // - // Check if we're still in a string at the end of the box so the next block will know - // - // right shift of a signed value expected to be well-defined and standard - // compliant as of C++20, John Regher from Utah U. says this is fine code - // - prev_in_string = uint64_t(static_cast(in_string) >> 63); +#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 - // Use ^ to turn the beginning quote off, and the end quote on. +/* 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 - // We are returning a function-local object so either we get a move constructor - // or we get copy elision. - return json_string_block( - backslash, - escaped, - quote, - in_string - ); +/* 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 } -simdjson_inline error_code json_string_scanner::finish() { - if (prev_in_string) { - return UNCLOSED_STRING; - } - return SUCCESS; +#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 } -} // namespace stage1 } // unnamed namespace -} // namespace ppc64 +} // namespace westmere } // namespace simdjson -/* end file src/generic/stage1/json_string_scanner.h */ -/* begin file src/generic/stage1/json_scanner.h */ + +#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 ppc64 { +namespace westmere { namespace { -namespace stage1 { +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; } + }; -/** - * 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) {} + template> + struct base8: base> { + typedef uint16_t bitmask_t; + typedef uint32_t bitmask2_t; - /** - * 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()); } + simdjson_inline base8() : base>() {} + simdjson_inline base8(const __m128i _value) : base>(_value) {} - // Helpers + friend simdjson_inline Mask operator==(const simd8 lhs, const simd8 rhs) { return _mm_cmpeq_epi8(lhs, rhs); } - /** 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); } + static const int SIZE = sizeof(base>::value); - // 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) + template + simdjson_inline simd8 prev(const simd8 prev_chunk) const { + return _mm_alignr_epi8(*this, prev_chunk, 16 - N); + } + }; - /** - * 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; - } -}; + // 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))); } -/** - * 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(); + 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); } + }; -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{}; -}; + // 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)); } + }; -// -// 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; -} + 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]; + + 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 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 simd8 reduce_or() const { + return (this->chunks[0] | this->chunks[1]) | (this->chunks[2] | this->chunks[3]); + } -simdjson_inline error_code json_scanner::finish() { - return string_scanner.finish(); -} + 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); + } -} // namespace stage1 + 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 westmere } // namespace simdjson -/* end file src/generic/stage1/json_scanner.h */ -/* begin file src/generic/stage1/json_minifier.h */ -// 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) + +#endif // SIMDJSON_WESTMERE_SIMD_INPUT_H +/* end file simdjson/westmere/simd.h */ namespace simdjson { -namespace ppc64 { +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; -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; -}; +using namespace simd; -simdjson_inline void json_minifier::next(const simd::simd8x64& in, const json_block& block) { - uint64_t mask = block.whitespace(); - dst += in.compress(mask, dst); +// 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 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 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; -} +} // unnamed namespace +} // namespace westmere +} // namespace simdjson -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(); -} +#endif // SIMDJSON_WESTMERE_STRINGPARSING_DEFS_H +/* end file simdjson/westmere/stringparsing_defs.h */ +/* end file simdjson/westmere/begin.h */ +/* including simdjson/generic/amalgamated.h for westmere: #include "simdjson/generic/amalgamated.h" */ +/* begin file simdjson/generic/amalgamated.h for westmere */ +#if defined(SIMDJSON_CONDITIONAL_INCLUDE) && !defined(SIMDJSON_GENERIC_DEPENDENCIES_H) +#error simdjson/generic/dependencies.h must be included before simdjson/generic/amalgamated.h! +#endif -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(); -} +/* including simdjson/generic/base.h for westmere: #include "simdjson/generic/base.h" */ +/* begin file simdjson/generic/base.h for westmere */ +#ifndef SIMDJSON_GENERIC_BASE_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_BASE_H */ +/* amalgamation skipped (editor-only): #include "simdjson/base.h" */ +/* amalgamation skipped (editor-only): // If we haven't got an implementation yet, we're in the editor, editing a generic file! Just */ +/* amalgamation skipped (editor-only): // use the most advanced one we can so the most possible stuff can be tested. */ +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_IMPLEMENTATION */ +/* amalgamation skipped (editor-only): #include "simdjson/implementation_detection.h" */ +/* amalgamation skipped (editor-only): #if SIMDJSON_IMPLEMENTATION_ICELAKE */ +/* amalgamation skipped (editor-only): #include "simdjson/icelake/begin.h" */ +/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_HASWELL */ +/* amalgamation skipped (editor-only): #include "simdjson/haswell/begin.h" */ +/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_WESTMERE */ +/* amalgamation skipped (editor-only): #include "simdjson/westmere/begin.h" */ +/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_ARM64 */ +/* amalgamation skipped (editor-only): #include "simdjson/arm64/begin.h" */ +/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_PPC64 */ +/* amalgamation skipped (editor-only): #include "simdjson/ppc64/begin.h" */ +/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_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 */ -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); +namespace simdjson { +namespace westmere { - // Index the first n-1 blocks - while (reader.has_full_block()) { - minifier.step(reader.full_block(), reader); - } +struct open_container; +class dom_parser_implementation; - // 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); -} +/** + * 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 +}; -} // namespace stage1 -} // unnamed namespace -} // namespace ppc64 +} // namespace westmere } // namespace simdjson -/* end file src/generic/stage1/json_minifier.h */ -/* begin file src/generic/stage1/find_next_document_index.h */ + +#endif // SIMDJSON_GENERIC_BASE_H +/* end file simdjson/generic/base.h for westmere */ +/* including simdjson/generic/jsoncharutils.h for westmere: #include "simdjson/generic/jsoncharutils.h" */ +/* begin file simdjson/generic/jsoncharutils.h for westmere */ +#ifndef SIMDJSON_GENERIC_JSONCHARUTILS_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_JSONCHARUTILS_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/internal/jsoncharutils_tables.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/internal/numberparsing_tables.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + namespace simdjson { -namespace ppc64 { +namespace westmere { namespace { +namespace jsoncharutils { -/** - * 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 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; } - return 0; + // will return 0 when the code point was too large. + return 0; // bad r +} + +#if SIMDJSON_IS_32BITS // _umul128 for x86, arm +// this is a slow emulation routine for 32-bit +// +static simdjson_inline uint64_t __emulu(uint32_t x, uint32_t y) { + return x * (uint64_t)y; +} +static simdjson_inline uint64_t _umul128(uint64_t ab, uint64_t cd, uint64_t *hi) { + uint64_t ad = __emulu((uint32_t)(ab >> 32), (uint32_t)cd); + uint64_t bd = __emulu((uint32_t)ab, (uint32_t)cd); + uint64_t adbc = ad + __emulu((uint32_t)ab, (uint32_t)(cd >> 32)); + uint64_t adbc_carry = !!(adbc < ad); + uint64_t lo = bd + (adbc << 32); + *hi = __emulu((uint32_t)(ab >> 32), (uint32_t)(cd >> 32)) + (adbc >> 32) + + (adbc_carry << 32) + !!(lo < bd); + return lo; } +#endif +} // namespace jsoncharutils } // unnamed namespace -} // namespace ppc64 +} // namespace westmere } // namespace simdjson -/* end file src/generic/stage1/find_next_document_index.h */ + +#endif // SIMDJSON_GENERIC_JSONCHARUTILS_H +/* end file simdjson/generic/jsoncharutils.h for westmere */ +/* including simdjson/generic/atomparsing.h for westmere: #include "simdjson/generic/atomparsing.h" */ +/* begin file simdjson/generic/atomparsing.h for westmere */ +#ifndef SIMDJSON_GENERIC_ATOMPARSING_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ATOMPARSING_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/jsoncharutils.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +#include namespace simdjson { -namespace ppc64 { +namespace westmere { namespace { -namespace stage1 { +/// @private +namespace atomparsing { -class bit_indexer { +// 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 westmere +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_ATOMPARSING_H +/* end file simdjson/generic/atomparsing.h for westmere */ +/* including simdjson/generic/dom_parser_implementation.h for westmere: #include "simdjson/generic/dom_parser_implementation.h" */ +/* begin file simdjson/generic/dom_parser_implementation.h for westmere */ +#ifndef SIMDJSON_GENERIC_DOM_PARSER_IMPLEMENTATION_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_DOM_PARSER_IMPLEMENTATION_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/internal/dom_parser_implementation.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { +namespace westmere { + +// expectation: sizeof(open_container) = 64/8. +struct open_container { + uint32_t tape_index; // where, on the tape, does the scope ([,{) begins + uint32_t count; // how many elements in the scope +}; // struct open_container + +static_assert(sizeof(open_container) == 64/8, "Open container must be 64 bits"); + +class dom_parser_implementation final : public internal::dom_parser_implementation { public: - uint32_t *tail; + /** 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); - simdjson_inline bit_indexer(uint32_t *index_buf) : tail(index_buf) {} +}; - // 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_CUSTOM_BIT_INDEXER, then it will provide its own - // version of the code. -#ifdef SIMDJSON_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; -#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. - */ +} // namespace westmere +} // namespace simdjson - uint64_t rev_bits = reverse_bits(bits); - int cnt = static_cast(count_ones(bits)); - int i = 0; - // Do the first 8 all together - for (; i<8; i++) { - int lz = leading_zeroes(rev_bits); - this->tail[i] = static_cast(idx) + lz; - rev_bits = zero_leading_bit(rev_bits, lz); - } - // Do the next 8 all together (we hope in most cases it won't happen at all - // and the branch is easily predicted). - if (simdjson_unlikely(cnt > 8)) { - i = 8; - for (; i<16; i++) { - int lz = leading_zeroes(rev_bits); - this->tail[i] = static_cast(idx) + lz; - rev_bits = zero_leading_bit(rev_bits, lz); - } +namespace simdjson { +namespace westmere { +inline dom_parser_implementation::dom_parser_implementation() noexcept = default; +inline dom_parser_implementation::dom_parser_implementation(dom_parser_implementation &&other) noexcept = default; +inline dom_parser_implementation &dom_parser_implementation::operator=(dom_parser_implementation &&other) noexcept = default; + +// Leaving these here so they can be inlined if so desired +inline simdjson_warn_unused error_code dom_parser_implementation::set_capacity(size_t capacity) noexcept { + if(capacity > SIMDJSON_MAXSIZE_BYTES) { 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; +} - // Most files don't have 16+ structurals per block, so we take several basically guaranteed - // branch mispredictions here. 16+ structurals per block means either punctuation ({} [] , :) - // or the start of a value ("abc" true 123) every four characters. - if (simdjson_unlikely(cnt > 16)) { - i = 16; - while (rev_bits != 0) { - int lz = leading_zeroes(rev_bits); - this->tail[i++] = static_cast(idx) + lz; - rev_bits = zero_leading_bit(rev_bits, lz); - } - } - } - this->tail += cnt; -#else // SIMDJSON_PREFER_REVERSE_BITS - /** - * 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. - */ +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; } - int cnt = static_cast(count_ones(bits)); - // Do the first 8 all together - for (int i=0; i<8; i++) { - this->tail[i] = idx + trailing_zeroes(bits); - bits = clear_lowest_bit(bits); + _max_depth = max_depth; + return SUCCESS; +} + +} // namespace westmere +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_DOM_PARSER_IMPLEMENTATION_H +/* end file simdjson/generic/dom_parser_implementation.h for westmere */ +/* including simdjson/generic/implementation_simdjson_result_base.h for westmere: #include "simdjson/generic/implementation_simdjson_result_base.h" */ +/* begin file simdjson/generic/implementation_simdjson_result_base.h for westmere */ +#ifndef SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { +namespace westmere { + +// This is a near copy of include/error.h's implementation_simdjson_result_base, except it doesn't use std::pair +// so we can avoid inlining errors +// TODO reconcile these! +/** + * The result of a simdjson operation that could fail. + * + * Gives the option of reading error codes, or throwing an exception by casting to the desired result. + * + * This is a base class for implementations that want to add functions to the result type for + * chaining. + * + * Override like: + * + * struct simdjson_result : 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_inline error_code get(T &value) && noexcept; + + /** + * The error. + */ + simdjson_inline error_code error() const noexcept; + +#if SIMDJSON_EXCEPTIONS + + /** + * Get the result value. + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T& value() & noexcept(false); + + /** + * Take the result value (move it). + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T&& value() && noexcept(false); + + /** + * Take the result value (move it). + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T&& take_value() && noexcept(false); + + /** + * Cast to the value (will throw on error). + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline operator T&&() && noexcept(false); + + +#endif // SIMDJSON_EXCEPTIONS + + /** + * Get the result value. This function is safe if and only + * the error() method returns a value that evaluates to false. + */ + simdjson_inline const T& value_unsafe() const& noexcept; + /** + * 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 + +} // namespace westmere +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_H +/* end file simdjson/generic/implementation_simdjson_result_base.h for westmere */ +/* including simdjson/generic/numberparsing.h for westmere: #include "simdjson/generic/numberparsing.h" */ +/* begin file simdjson/generic/numberparsing.h for westmere */ +#ifndef SIMDJSON_GENERIC_NUMBERPARSING_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_NUMBERPARSING_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/jsoncharutils.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/internal/numberparsing_tables.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +#include +#include +#include + +namespace simdjson { +namespace westmere { +namespace numberparsing { + +#ifdef JSON_TEST_NUMBERS +#define INVALID_NUMBER(SRC) (found_invalid_number((SRC)), NUMBER_ERROR) +#define WRITE_INTEGER(VALUE, SRC, WRITER) (found_integer((VALUE), (SRC)), (WRITER).append_s64((VALUE))) +#define WRITE_UNSIGNED(VALUE, SRC, WRITER) (found_unsigned_integer((VALUE), (SRC)), (WRITER).append_u64((VALUE))) +#define WRITE_DOUBLE(VALUE, SRC, WRITER) (found_float((VALUE), (SRC)), (WRITER).append_double((VALUE))) +#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)) +#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 + // 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; - // Do the next 8 all together (we hope in most cases it won't happen at all - // and the branch is easily predicted). - if (simdjson_unlikely(cnt > 8)) { - for (int i=8; i<16; i++) { - this->tail[i] = idx + trailing_zeroes(bits); - bits = clear_lowest_bit(bits); - } - // Most files don't have 16+ structurals per block, so we take several basically guaranteed - // branch mispredictions here. 16+ structurals per block means either punctuation ({} [] , :) - // or the start of a value ("abc" true 123) every four characters. - if (simdjson_unlikely(cnt > 16)) { - int i = 16; - do { - this->tail[i] = idx + trailing_zeroes(bits); - bits = clear_lowest_bit(bits); - i++; - } while (i < cnt); - } + // 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. + + // 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. In very rare cases, even that + // will not suffice, though it is seemingly very hard to find such a scenario. + // + // 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++; } + // At this point, we might need to add at most one to firstproduct, but this + // can only change the value of firstproduct.high if firstproduct.low is maximal. + if(simdjson_unlikely(firstproduct.low == 0xFFFFFFFFFFFFFFFF)) { + // This is very unlikely, but if so, we need to do much more work! + return false; + } + } + 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 + } + } - this->tail += cnt; -#endif + 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++; } -#endif // SIMDJSON_CUSTOM_BIT_INDEXER + 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()); +} -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; +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. -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); + // 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; +} - json_scanner scanner{}; - utf8_checker checker{}; - bit_indexer indexer; - uint64_t prev_structurals = 0; - uint64_t unescaped_chars_error = 0; -}; +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_inline json_structural_indexer::json_structural_indexer(uint32_t *structural_indexes) : indexer{structural_indexes} {} +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 doesn't 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; +} -// 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; +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 */ +template +error_code slow_float_parsing(simdjson_unused const uint8_t * src, W writer) { + double d; + if (parse_float_fallback(src, &d)) { + writer.append_double(d); + 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: This makes a *copy* of the writer and passes it to slow_float_parsing. This happens + // because slow_float_parsing is a non-inlined function. 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. + // This is what forces the skip_double, as well. + error_code error = slow_float_parsing(src, writer); + writer.skip_double(); + 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); } } - 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; + 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; } +// 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 +} + +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 // -// 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 doesn't 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. +// 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. // -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; } +// 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 INVALID_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 INVALID_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); } } - 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); + // Write unsigned if it doesn't 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); } - // 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); + if (jsoncharutils::is_not_structural_or_whitespace(*p)) { return INVALID_NUMBER(src); } + return SUCCESS; } -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(); -} +// Inlineable functions +namespace { -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(); -} +// 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; } + } -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); + return i; } -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; +// 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; } } - if (simdjson_unlikely(parser.structural_indexes[parser.n_structural_indexes - 1] > len)) { - return UNEXPECTED_ERROR; + + 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; } } - 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; + 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 = start_digits-src > 19; } + } else { + overflow = p-src > 19; } - checker.check_eof(); - return checker.errors(); + + // + // 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; } -} // namespace stage1 -} // unnamed namespace -} // namespace ppc64 -} // namespace simdjson -/* end file src/generic/stage1/json_structural_indexer.h */ -/* begin file src/generic/stage1/utf8_validator.h */ -namespace simdjson { -namespace ppc64 { -namespace { -namespace stage1 { +simdjson_unused simdjson_inline bool is_negative(const uint8_t * src) noexcept { + return (*src == '-'); +} -/** - * 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; +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; } -bool generic_validate_utf8(const char * input, size_t length) { - return generic_validate_utf8(reinterpret_cast(input),length); +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++; } + if ( p == src ) { return NUMBER_ERROR; } + if (jsoncharutils::is_structural_or_whitespace(*p)) { + // We have an integer. + // If the number is negative and valid, it must be a signed integer. + if(negative) { return number_type::signed_integer; } + // We want values larger or equal to 9223372036854775808 to be unsigned + // integers, and the other values to be signed integers. + int digit_count = int(p - src); + if(digit_count >= 19) { + const uint8_t * smaller_big_integer = reinterpret_cast("9223372036854775808"); + if((digit_count >= 20) || (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; } -} // namespace stage1 -} // unnamed namespace -} // namespace ppc64 -} // namespace simdjson -/* end file src/generic/stage1/utf8_validator.h */ +// 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); -// -// Stage 2 -// -/* begin file src/generic/stage2/stringparsing.h */ -// This file contains the common code every implementation uses -// It is intended to be included multiple times and compiled multiple times + // + // 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; } -namespace simdjson { -namespace ppc64 { -namespace { -/// @private -namespace stringparsing { + // + // 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; + } -// 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, + // + // 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 == '+'; - 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. + 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; } - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + exponent += exp_neg ? 0-exp : exp; + } - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -}; + if ((p != src_end) && jsoncharutils::is_not_structural_or_whitespace(*p)) { return NUMBER_ERROR; } -// 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 isn't 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; + overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power; - // 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); + // + // 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; +} - // 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; - } +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 = start_digits-src > 19; } - } 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; + } else { + overflow = p-src > 19; } - size_t offset = jsoncharutils::codepoint_to_utf8(code_point, *dst_ptr); - *dst_ptr += offset; - return offset > 0; -} + // + // 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; -// 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 isn't 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; - } + // 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; + default: SIMDJSON_UNREACHABLE(); } + return out; +} + +} // namespace westmere +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_NUMBERPARSING_H +/* end file simdjson/generic/numberparsing.h for westmere */ + +/* including simdjson/generic/implementation_simdjson_result_base-inl.h for westmere: #include "simdjson/generic/implementation_simdjson_result_base-inl.h" */ +/* begin file simdjson/generic/implementation_simdjson_result_base-inl.h for westmere */ +#ifndef SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_INL_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_INL_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { +namespace westmere { + +// +// internal::implementation_simdjson_result_base 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; } +} - size_t offset = jsoncharutils::codepoint_to_utf8(code_point, *dst_ptr); - *dst_ptr += offset; - return offset > 0; +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; +} -/** - * 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_nullptrptr 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; - } - } - /* can't be reached */ - return nullptr; +#if SIMDJSON_EXCEPTIONS + +template +simdjson_inline T& implementation_simdjson_result_base::value() & noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return this->first; } -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; - } - } - /* can't be reached */ - return nullptr; +template +simdjson_inline T&& implementation_simdjson_result_base::value() && noexcept(false) { + return std::forward>(*this).take_value(); } -} // namespace stringparsing -} // unnamed namespace -} // namespace ppc64 +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 westmere } // namespace simdjson -/* end file src/generic/stage2/stringparsing.h */ -/* begin file src/generic/stage2/tape_builder.h */ -/* begin file src/generic/stage2/json_iterator.h */ -/* begin file src/generic/stage2/logger.h */ -// This is for an internal-only stage 2 specific logger. -// Set LOG_ENABLED = true to log what stage 2 is doing! + +#endif // SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_INL_H +/* end file simdjson/generic/implementation_simdjson_result_base-inl.h for westmere */ +/* end file simdjson/generic/amalgamated.h for westmere */ +/* including simdjson/westmere/end.h: #include "simdjson/westmere/end.h" */ +/* begin file simdjson/westmere/end.h */ +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #include "simdjson/westmere/base.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +#if !SIMDJSON_CAN_ALWAYS_RUN_WESTMERE +SIMDJSON_UNTARGET_REGION +#endif + +/* undefining SIMDJSON_IMPLEMENTATION from "westmere" */ +#undef SIMDJSON_IMPLEMENTATION +/* end file simdjson/westmere/end.h */ + +#endif // SIMDJSON_WESTMERE_H +/* end file simdjson/westmere.h */ +/* including simdjson/westmere/implementation.h: #include */ +/* begin file simdjson/westmere/implementation.h */ +#ifndef SIMDJSON_WESTMERE_IMPLEMENTATION_H +#define SIMDJSON_WESTMERE_IMPLEMENTATION_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #include "simdjson/westmere/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/implementation.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/internal/instruction_set.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +// The constructor may be executed on any host, so we take care not to use SIMDJSON_TARGET_WESTMERE namespace simdjson { -namespace ppc64 { +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; +}; + +} // namespace westmere +} // namespace simdjson + +#endif // SIMDJSON_WESTMERE_IMPLEMENTATION_H +/* end file simdjson/westmere/implementation.h */ + +/* including simdjson/westmere/begin.h: #include */ +/* 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 logger { +namespace simd { - static constexpr const char * DASHES = "----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"; +template struct simd8; +template struct simd8x64; -#if SIMDJSON_VERBOSE_LOGGING - static constexpr const bool LOG_ENABLED = true; +} // 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 - static constexpr const bool LOG_ENABLED = false; +#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 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_assert(sizeof(__m128i) <= simdjson::SIMDJSON_PADDING, "insufficient padding for westmere"); - // Helper to turn unprintable or newline characters into spaces - static simdjson_inline char printable_char(char c) { - if (c >= 0x20) { - return c; - } else { - return ' '; - } - } +#endif // SIMDJSON_WESTMERE_INTRINSICS_H +/* end file simdjson/westmere/intrinsics.h */ - // 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); - } - } +#if !SIMDJSON_CAN_ALWAYS_RUN_WESTMERE +SIMDJSON_TARGET_REGION("sse4.2,pclmul,popcnt") +#endif - simdjson_unused static simdjson_inline void log_string(const char *message) { - if (LOG_ENABLED) { - printf("%s\n", message); - } - } +/* 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 - // 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(result)); +#else + return __builtin_uaddll_overflow(value1, value2, + reinterpret_cast(result)); +#endif +} -} // namespace logger } // unnamed namespace -} // namespace ppc64 +} // namespace westmere } // namespace simdjson -/* end file src/generic/stage2/logger.h */ + +#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 ppc64 { +namespace westmere { 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}; +// +// 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); +} - /** - * 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; +} // unnamed namespace +} // namespace westmere +} // namespace simdjson - /** - * 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); +#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 - /** - * 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; +/* 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 - /** - * 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; +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #include "simdjson/base.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ - 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; -}; +// 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 { -template -simdjson_warn_unused simdjson_inline error_code json_iterator::walk_document(V &visitor) noexcept { - logger::log_start(); +class implementation; - // - // Start the document - // - if (at_eof()) { return EMPTY; } - log_start_value("document"); - SIMDJSON_TRY( visitor.visit_document_start(*this) ); +namespace { +namespace simd { - // - // Read first value - // - { - auto value = advance(); +template struct simd8; +template struct simd8x64; - // 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; - } +} // 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 +#ifdef _M_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 // _M_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); } + }; - 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; + // 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 + ); } - } - 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) ); + simdjson_inline base8_numeric() : base8() {} + simdjson_inline base8_numeric(const __m128i _value) : base8(_value) {} - { - 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) ); - } + // Store to array + simdjson_inline void store(T dst[16]) const { return _mm_storeu_si128(reinterpret_cast<__m128i *>(dst), *this); } -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; + // 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); } - } -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; - } + // 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); + } -scope_end: - depth--; - if (depth == 0) { goto document_end; } - if (dom_parser.is_array[depth]) { goto array_continue; } - goto object_continue; + 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 + )); + } + }; -// -// 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) ); + // 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 + ); + } -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; + // 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]; + + 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); } - } -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; - } + simdjson_inline simd8 reduce_or() const { + return (this->chunks[0] | this->chunks[1]) | (this->chunks[2] | this->chunks[3]); + } -document_end: - log_end_value("document"); - SIMDJSON_TRY( visitor.visit_document_end(*this) ); + 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); + } - dom_parser.next_structural_index = uint32_t(next_structural - &dom_parser.structural_indexes[0]); + 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); + } - // 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; - } + 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(); + } - return SUCCESS; + 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(); + } -} // walk_document() + 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 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} { -} +} // namespace simd +} // unnamed namespace +} // namespace westmere +} // namespace simdjson -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); -} +#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 -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]]; -} +/* 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 -simdjson_inline void json_iterator::log_value(const char *type) const noexcept { - logger::log_line(*this, "", type, ""); -} +/* 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 */ -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++; } -} +namespace simdjson { +namespace westmere { +namespace { -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, ""); +// 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 } -simdjson_inline void json_iterator::log_error(const char *error) const noexcept { - logger::log_line(*this, "", "ERROR", error); +#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 } - -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; - } +#else +simdjson_inline long long int count_ones(uint64_t input_num) { + return _popcnt64(input_num); } -template -simdjson_warn_unused simdjson_inline error_code json_iterator::visit_primitive(V &visitor, const uint8_t *value) noexcept { - switch (*value) { - case '"': return visitor.visit_string(*this, 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); - case '-': - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - return visitor.visit_number(*this, value); - default: - log_error("Non-value found when value was expected!"); - return TAPE_ERROR; - } +#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 } -} // namespace stage2 } // unnamed namespace -} // namespace ppc64 +} // namespace westmere } // namespace simdjson -/* end file src/generic/stage2/json_iterator.h */ -/* begin file src/generic/stage2/tape_writer.h */ + +#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 ppc64 { +namespace westmere { namespace { -namespace stage2 { +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; } + }; -struct tape_writer { - /** The next place to write to tape */ - uint64_t *next_tape_loc; + template> + struct base8: base> { + typedef uint16_t bitmask_t; + typedef uint32_t bitmask2_t; - /** Write a signed 64-bit value to tape. */ - simdjson_inline void append_s64(int64_t value) noexcept; + simdjson_inline base8() : base>() {} + simdjson_inline base8(const __m128i _value) : base>(_value) {} - /** Write an unsigned 64-bit value to tape. */ - simdjson_inline void append_u64(uint64_t value) noexcept; + friend simdjson_inline Mask operator==(const simd8 lhs, const simd8 rhs) { return _mm_cmpeq_epi8(lhs, rhs); } - /** Write a double value to tape. */ - simdjson_inline void append_double(double value) noexcept; + static const int SIZE = sizeof(base>::value); - /** - * 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; + template + simdjson_inline simd8 prev(const simd8 prev_chunk) const { + return _mm_alignr_epi8(*this, prev_chunk, 16 - N); + } + }; - /** - * 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; + // 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))); } - /** - * Skip the number of tape entries necessary to write a large u64 or i64. - */ - simdjson_inline void skip_large_integer() noexcept; + simdjson_inline simd8() : base8() {} + simdjson_inline simd8(const __m128i _value) : base8(_value) {} + // Splat constructor + simdjson_inline simd8(bool _value) : base8(splat(_value)) {} - /** - * Skip the number of tape entries necessary to write a double. - */ - simdjson_inline void skip_double() noexcept; + 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; } + }; - /** - * 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; + 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)); } + }; -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 number_writer + 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]; + + 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 void tape_writer::append_s64(int64_t value) noexcept { - append2(0, value, internal::tape_type::INT64); -} + simdjson_inline simd8 reduce_or() const { + return (this->chunks[0] | this->chunks[1]) | (this->chunks[2] | this->chunks[3]); + } -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 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); + } -/** 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 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 void tape_writer::skip() noexcept { - next_tape_loc++; -} + 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 void tape_writer::skip_large_integer() noexcept { - next_tape_loc += 2; -} + 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 void tape_writer::skip_double() noexcept { - next_tape_loc += 2; -} + 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 void tape_writer::append(uint64_t val, internal::tape_type t) noexcept { - *next_tape_loc = val | ((uint64_t(char(t))) << 56); - next_tape_loc++; -} +} // namespace simd +} // unnamed namespace +} // namespace westmere +} // namespace simdjson -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++; -} +#endif // SIMDJSON_WESTMERE_SIMD_INPUT_H +/* end file simdjson/westmere/simd.h */ -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 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 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 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 + }; } -} // namespace stage2 } // unnamed namespace -} // namespace ppc64 +} // namespace westmere } // namespace simdjson -/* end file src/generic/stage2/tape_writer.h */ + +#endif // SIMDJSON_WESTMERE_STRINGPARSING_DEFS_H +/* end file simdjson/westmere/stringparsing_defs.h */ +/* end file simdjson/westmere/begin.h */ +/* including generic/amalgamated.h for westmere: #include */ +/* begin file generic/amalgamated.h for westmere */ +#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 westmere: #include */ +/* begin file generic/base.h for westmere */ +#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 ppc64 { +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 json_character_block; + +} // unnamed namespace +} // namespace westmere +} // namespace simdjson + +#endif // SIMDJSON_SRC_GENERIC_BASE_H +/* end file generic/base.h for westmere */ +/* including generic/dom_parser_implementation.h for westmere: #include */ +/* begin file generic/dom_parser_implementation.h for westmere */ +#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 */ - /** 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; +// Interface a dom parser implementation must fulfill +namespace simdjson { +namespace westmere { +namespace { - /** 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; +simdjson_inline simd8 must_be_2_3_continuation(const simd8 prev2, const simd8 prev3); +simdjson_inline bool is_ascii(const simd8x64& input); - /** 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; +} // unnamed namespace +} // namespace westmere +} // namespace simdjson - /** - * 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; +#endif // SIMDJSON_SRC_GENERIC_DOM_PARSER_IMPLEMENTATION_H +/* end file generic/dom_parser_implementation.h for westmere */ +/* including generic/json_character_block.h for westmere: #include */ +/* begin file generic/json_character_block.h for westmere */ +#ifndef SIMDJSON_SRC_GENERIC_JSON_CHARACTER_BLOCK_H - 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; +/* 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 */ - 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; +namespace simdjson { +namespace westmere { +namespace { - /** 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; +struct json_character_block { + static simdjson_inline json_character_block classify(const simd::simd8x64& in); - /** 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 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 tape_builder(dom::document &doc) noexcept; + uint64_t _whitespace; + uint64_t _op; +}; - 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; -}; // class tape_builder +} // unnamed namespace +} // namespace westmere +} // namespace simdjson -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); -} +#endif // SIMDJSON_SRC_GENERIC_JSON_CHARACTER_BLOCK_H +/* end file generic/json_character_block.h for westmere */ +/* end file generic/amalgamated.h for westmere */ +/* including generic/stage1/amalgamated.h for westmere: #include */ +/* begin file generic/stage1/amalgamated.h for westmere */ +// Stuff other things depend on +/* including generic/stage1/base.h for westmere: #include */ +/* begin file generic/stage1/base.h for westmere */ +#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 */ -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); -} +namespace simdjson { +namespace westmere { +namespace { +namespace stage1 { -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; -} +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; -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); -} +} // namespace stage1 -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; -} +namespace utf8_validation { +struct utf8_checker; +} // namespace utf8_validation -simdjson_inline tape_builder::tape_builder(dom::document &doc) noexcept : tape{doc.tape.get()}, current_string_buf_loc{doc.string_buf.get()} {} +using utf8_validation::utf8_checker; -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; -} +} // unnamed namespace +} // namespace westmere +} // namespace simdjson -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); -} +#endif // SIMDJSON_SRC_GENERIC_STAGE1_BASE_H +/* end file generic/stage1/base.h for westmere */ +/* including generic/stage1/buf_block_reader.h for westmere: #include */ +/* begin file generic/stage1/buf_block_reader.h for westmere */ +#ifndef SIMDJSON_SRC_GENERIC_STAGE1_BUF_BLOCK_READER_H -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); -} +/* 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 */ -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; -} +#include -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; -} +namespace simdjson { +namespace westmere { +namespace { +namespace stage1 { -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; -} +// 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; +}; -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; +// 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; } -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; +// 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_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_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_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_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; } -// 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()); -} +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} {} -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; -} +template +simdjson_inline size_t buf_block_reader::block_index() { return idx; } -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. +template +simdjson_inline bool buf_block_reader::has_full_block() const { + return idx < lenminusstep; } -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; +template +simdjson_inline const uint8_t *buf_block_reader::full_block() const { + return &buf[idx]; } -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); +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; } -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; +template +simdjson_inline void buf_block_reader::advance() { + idx += STEP_SIZE; } -} // namespace stage2 +} // namespace stage1 } // unnamed namespace -} // namespace ppc64 +} // namespace westmere } // namespace simdjson -/* end file src/generic/stage2/tape_builder.h */ -// -// Implementation-specific overrides -// +#endif // SIMDJSON_SRC_GENERIC_STAGE1_BUF_BLOCK_READER_H +/* end file generic/stage1/buf_block_reader.h for westmere */ +/* including generic/stage1/json_escape_scanner.h for westmere: #include */ +/* begin file generic/stage1/json_escape_scanner.h for westmere */ +#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 ppc64 { +namespace westmere { namespace { namespace stage1 { -simdjson_inline uint64_t json_string_scanner::find_escaped(uint64_t backslash) { - // On PPC, we don't short-circuit this if there are no backslashes, because the branch gives us no - // benefit and therefore makes things worse. - // if (!backslash) { uint64_t escaped = prev_escaped; prev_escaped = 0; return escaped; } - return find_escaped_branchless(backslash); -} - -} // namespace stage1 -} // unnamed namespace - -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); -} +/** + * 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; -simdjson_warn_unused bool implementation::validate_utf8(const char *buf, size_t len) const noexcept { - return ppc64::stage1::generic_validate_utf8(buf,len); -} + 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; + }; -simdjson_warn_unused error_code dom_parser_implementation::stage2(dom::document &_doc) noexcept { - return stage2::tape_builder::parse_document(*this, _doc); -} + /** + * 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 { -simdjson_warn_unused error_code dom_parser_implementation::stage2_next(dom::document &_doc) noexcept { - return stage2::tape_builder::parse_document(*this, _doc); -} +#if !SIMDJSON_SKIP_BACKSLASH_SHORT_CIRCUIT + if (!backslash) { return {next_escaped_without_backslashes(), 0}; } +#endif -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); -} + // | | 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}; + } -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); -} +private: + static constexpr const uint64_t ODD_BITS = 0xAAAAAAAAAAAAAAAAULL; -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_really_inline uint64_t next_escaped_without_backslashes() noexcept { + uint64_t escaped = this->next_is_escaped; + this->next_is_escaped = 0; + return escaped; + } -} // namespace ppc64 -} // namespace simdjson + /** + * 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) + // -/* begin file include/simdjson/ppc64/end.h */ -/* end file include/simdjson/ppc64/end.h */ -/* end file src/ppc64/dom_parser_implementation.cpp */ -#endif -#if SIMDJSON_IMPLEMENTATION_WESTMERE -/* begin file src/westmere/implementation.cpp */ -/* begin file include/simdjson/westmere/begin.h */ -// redefining SIMDJSON_IMPLEMENTATION to "westmere" -// #define SIMDJSON_IMPLEMENTATION westmere -SIMDJSON_TARGET_WESTMERE -/* end file include/simdjson/westmere/begin.h */ + // Escaped characters are characters following an escape. + uint64_t maybe_escaped = potential_escape << 1; -namespace simdjson { -namespace westmere { + // 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; -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; -} + // 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 simdjson -/* begin file include/simdjson/westmere/end.h */ -SIMDJSON_UNTARGET_WESTMERE -/* end file include/simdjson/westmere/end.h */ -/* end file src/westmere/implementation.cpp */ -/* begin file src/westmere/dom_parser_implementation.cpp */ -/* begin file include/simdjson/westmere/begin.h */ -// redefining SIMDJSON_IMPLEMENTATION to "westmere" -// #define SIMDJSON_IMPLEMENTATION westmere -SIMDJSON_TARGET_WESTMERE -/* end file include/simdjson/westmere/begin.h */ +#endif // SIMDJSON_SRC_GENERIC_STAGE1_JSON_STRING_SCANNER_H +/* end file generic/stage1/json_escape_scanner.h for westmere */ +/* including generic/stage1/json_string_scanner.h for westmere: #include */ +/* begin file generic/stage1/json_string_scanner.h for westmere */ +#ifndef SIMDJSON_SRC_GENERIC_STAGE1_JSON_STRING_SCANNER_H -// -// Stage 1 -// +/* 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 westmere { namespace { +namespace stage1 { -using namespace simd; +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_character_block { - static simdjson_inline json_character_block classify(const simd::simd8x64& in); + // 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; } - 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()); } + // 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; +}; - uint64_t _whitespace; - uint64_t _op; +// 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; }; -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); +// +// 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; - // 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. + // prefix_xor flips on bits inside the string (and flips off the end quote). // - // 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). - + // 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; - 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 }; -} + // + // 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); -simdjson_inline bool is_ascii(const simd8x64& input) { - return input.reduce_or().is_ascii(); -} + // Use ^ to turn the beginning quote off, and the end quote on. -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); + // 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_inline simd8 must_be_2_3_continuation(const simd8 prev2, const simd8 prev3) { - 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_third_byte | is_fourth_byte) > int8_t(0); +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 simdjson -/* begin file src/generic/stage1/utf8_lookup4_algorithm.h */ +#endif // SIMDJSON_SRC_GENERIC_STAGE1_JSON_STRING_SCANNER_H +/* end file generic/stage1/json_string_scanner.h for westmere */ +/* including generic/stage1/utf8_lookup4_algorithm.h for westmere: #include */ +/* begin file generic/stage1/utf8_lookup4_algorithm.h for westmere */ +#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 westmere { namespace { @@ -14458,293 +41341,48 @@ using namespace simd; // 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]); - } 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; - } - - }; // struct utf8_checker -} // namespace utf8_validation - -using utf8_validation::utf8_checker; - -} // unnamed namespace -} // namespace westmere -} // namespace simdjson -/* end file src/generic/stage1/utf8_lookup4_algorithm.h */ -/* begin file src/generic/stage1/json_structural_indexer.h */ -// 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) - -/* begin file src/generic/stage1/buf_block_reader.h */ -namespace simdjson { -namespace westmere { -namespace { - -// 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_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; -} - -} // unnamed namespace -} // namespace westmere -} // namespace simdjson -/* end file src/generic/stage1/buf_block_reader.h */ -/* begin file src/generic/stage1/json_string_scanner.h */ -namespace simdjson { -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_inline json_string_block(uint64_t backslash, uint64_t escaped, uint64_t quote, uint64_t in_string) : - _backslash(backslash), _escaped(escaped), _quote(quote), _in_string(in_string) {} - - // Escaped characters (characters following an escape() character) - simdjson_inline uint64_t escaped() const { return _escaped; } - // Escape characters (backslashes that are not escaped--i.e. in \\, includes only the first \) - simdjson_inline uint64_t escape() const { return _backslash & ~_escaped; } - // Real (non-backslashed) quotes - simdjson_inline uint64_t quote() const { return _quote; } - // Start quotes of strings - simdjson_inline uint64_t string_start() const { return _quote & _in_string; } - // End quotes of strings - simdjson_inline uint64_t string_end() const { return _quote & ~_in_string; } - // Only characters inside the string (not including the quotes) - simdjson_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_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_inline uint64_t non_quote_outside_string(uint64_t mask) const { return mask & ~_in_string; } - // Tail of string (everything except the start quote) - simdjson_inline uint64_t string_tail() const { return _in_string ^ _quote; } - - // backslash characters - uint64_t _backslash; - // escaped characters (backslashed--does not include the hex characters after \u) - uint64_t _escaped; - // real quotes (non-backslashed 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_inline json_string_block next(const simd::simd8x64& in); - // Returns either UNCLOSED_STRING or SUCCESS - simdjson_inline error_code finish(); - -private: - // Intended to be defined by the implementation - simdjson_inline uint64_t find_escaped(uint64_t escape); - simdjson_inline uint64_t find_escaped_branchless(uint64_t escape); - - // Whether the last iteration was still inside a string (all 1's = true, all 0's = false). - uint64_t prev_in_string = 0ULL; - // Whether the first character of the next iteration is escaped. - uint64_t prev_escaped = 0ULL; -}; - -// -// Finds escaped characters (characters following \). -// -// Handles runs of backslashes like \\\" and \\\\" correctly (yielding 0101 and 01010, respectively). -// -// Does this by: -// - Shift the escape mask to get potentially escaped characters (characters after backslashes). -// - Mask escaped sequences that start on *even* bits with 1010101010 (odd bits are escaped, even bits are not) -// - Mask escaped sequences that start on *odd* bits with 0101010101 (even bits are escaped, odd bits are not) -// -// To distinguish between escaped sequences starting on even/odd bits, it finds the start of all -// escape sequences, filters out the ones that start on even bits, and adds that to the mask of -// escape sequences. This causes the addition to clear out the sequences starting on odd bits (since -// the start bit causes a carry), and leaves even-bit sequences alone. -// -// Example: -// -// text | \\\ | \\\"\\\" \\\" \\"\\" | -// escape | xxx | xx xxx xxx xx xx | Removed overflow backslash; will | it into follows_escape -// odd_starts | x | x x x | escape & ~even_bits & ~follows_escape -// even_seq | c| cxxx c xx c | c = carry bit -- will be masked out later -// invert_mask | | cxxx c xx c| even_seq << 1 -// follows_escape | xx | x xx xxx xxx xx xx | Includes overflow bit -// escaped | x | x x x x x x x x | -// desired | x | x x x x x x x x | -// text | \\\ | \\\"\\\" \\\" \\"\\" | -// -simdjson_inline uint64_t json_string_scanner::find_escaped_branchless(uint64_t backslash) { - // If there was overflow, pretend the first character isn't a backslash - backslash &= ~prev_escaped; - uint64_t follows_escape = backslash << 1 | prev_escaped; - - // Get sequences starting on even bits by clearing out the odd series using + - const uint64_t even_bits = 0x5555555555555555ULL; - uint64_t odd_sequence_starts = backslash & ~even_bits & ~follows_escape; - uint64_t sequences_starting_on_even_bits; - prev_escaped = add_overflow(odd_sequence_starts, backslash, &sequences_starting_on_even_bits); - uint64_t invert_mask = sequences_starting_on_even_bits << 1; // The mask we want to return is the *escaped* bits, not escapes. - - // Mask every other backslashed character as an escaped character - // Flip the mask for sequences that start on even bits, to correct them - return (even_bits ^ invert_mask) & follows_escape; -} - -// -// 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_inline json_string_block json_string_scanner::next(const simd::simd8x64& in) { - const uint64_t backslash = in.eq('\\'); - const uint64_t escaped = find_escaped(backslash); - 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 - // - // right shift of a signed value expected to be well-defined and standard - // compliant as of C++20, John Regher from Utah U. says this is fine code - // - 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( - backslash, - escaped, - quote, - in_string - ); -} + || (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]); + } 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; + } -simdjson_inline error_code json_string_scanner::finish() { - if (prev_in_string) { - return UNCLOSED_STRING; - } - return SUCCESS; -} + }; // struct utf8_checker +} // namespace utf8_validation -} // namespace stage1 } // unnamed namespace } // namespace westmere } // namespace simdjson -/* end file src/generic/stage1/json_string_scanner.h */ -/* begin file src/generic/stage1/json_scanner.h */ + +#endif // SIMDJSON_SRC_GENERIC_STAGE1_UTF8_LOOKUP4_ALGORITHM_H +/* end file generic/stage1/utf8_lookup4_algorithm.h for westmere */ +/* including generic/stage1/json_scanner.h for westmere: #include */ +/* begin file generic/stage1/json_scanner.h for westmere */ +#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 westmere { namespace { @@ -14902,106 +41540,25 @@ simdjson_inline error_code json_scanner::finish() { } // unnamed namespace } // namespace westmere } // namespace simdjson -/* end file src/generic/stage1/json_scanner.h */ -/* begin file src/generic/stage1/json_minifier.h */ -// 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 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; - -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; -}; - -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; -} - -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); +#endif // SIMDJSON_SRC_GENERIC_STAGE1_JSON_SCANNER_H +/* end file generic/stage1/json_scanner.h for westmere */ - // Index the first n-1 blocks - while (reader.has_full_block()) { - minifier.step(reader.full_block(), reader); - } +// All other declarations +/* including generic/stage1/find_next_document_index.h for westmere: #include */ +/* begin file generic/stage1/find_next_document_index.h for westmere */ +#ifndef SIMDJSON_SRC_GENERIC_STAGE1_FIND_NEXT_DOCUMENT_INDEX_H - // 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); -} +/* 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 stage1 -} // unnamed namespace -} // namespace westmere -} // namespace simdjson -/* end file src/generic/stage1/json_minifier.h */ -/* begin file src/generic/stage1/find_next_document_index.h */ namespace simdjson { namespace westmere { namespace { +namespace stage1 { /** * This algorithm is used to quickly identify the last structural position that @@ -15089,10 +41646,139 @@ simdjson_inline uint32_t find_next_document_index(dom_parser_implementation &par return 0; } +} // namespace stage1 +} // unnamed namespace +} // namespace westmere +} // namespace simdjson + +#endif // SIMDJSON_SRC_GENERIC_STAGE1_FIND_NEXT_DOCUMENT_INDEX_H +/* end file generic/stage1/find_next_document_index.h for westmere */ +/* including generic/stage1/json_minifier.h for westmere: #include */ +/* begin file generic/stage1/json_minifier.h for westmere */ +#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 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; + +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; +}; + +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; +} + +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 westmere } // namespace simdjson -/* end file src/generic/stage1/find_next_document_index.h */ + +#endif // SIMDJSON_SRC_GENERIC_STAGE1_JSON_MINIFIER_H +/* end file generic/stage1/json_minifier.h for westmere */ +/* including generic/stage1/json_structural_indexer.h for westmere: #include */ +/* begin file generic/stage1/json_structural_indexer.h for westmere */ +#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 westmere { @@ -15111,9 +41797,9 @@ class bit_indexer { // 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_CUSTOM_BIT_INDEXER, then it will provide its own - // version of the code. -#ifdef SIMDJSON_CUSTOM_BIT_INDEXER + // 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) { @@ -15208,7 +41894,7 @@ class bit_indexer { this->tail += cnt; #endif } -#endif // SIMDJSON_CUSTOM_BIT_INDEXER +#endif // SIMDJSON_GENERIC_JSON_STRUCTURAL_INDEXER_CUSTOM_BIT_INDEXER }; @@ -15439,8 +42125,23 @@ simdjson_inline error_code json_structural_indexer::finish(dom_parser_implementa } // unnamed namespace } // namespace westmere } // namespace simdjson -/* end file src/generic/stage1/json_structural_indexer.h */ -/* begin file src/generic/stage1/utf8_validator.h */ + +// 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 westmere */ +/* including generic/stage1/utf8_validator.h for westmere: #include */ +/* begin file generic/stage1/utf8_validator.h for westmere */ +#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 westmere { namespace { @@ -15475,254 +42176,171 @@ bool generic_validate_utf8(const char * input, size_t length) { } // unnamed namespace } // namespace westmere } // namespace simdjson -/* end file src/generic/stage1/utf8_validator.h */ -// -// Stage 2 -// -/* begin file src/generic/stage2/stringparsing.h */ -// This file contains the common code every implementation uses -// It is intended to be included multiple times and compiled multiple times +#endif // SIMDJSON_SRC_GENERIC_STAGE1_UTF8_VALIDATOR_H +/* end file generic/stage1/utf8_validator.h for westmere */ +/* end file generic/stage1/amalgamated.h for westmere */ +/* including generic/stage2/amalgamated.h for westmere: #include */ +/* begin file generic/stage2/amalgamated.h for westmere */ +// Stuff other things depend on +/* including generic/stage2/base.h for westmere: #include */ +/* begin file generic/stage2/base.h for westmere */ +#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 westmere { namespace { -/// @private -namespace stringparsing { +namespace stage2 { -// 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, +class json_iterator; +class structural_iterator; +struct tape_builder; +struct tape_writer; + +} // namespace stage2 +} // unnamed namespace +} // namespace westmere +} // namespace simdjson + +#endif // SIMDJSON_SRC_GENERIC_STAGE2_BASE_H +/* end file generic/stage2/base.h for westmere */ +/* including generic/stage2/tape_writer.h for westmere: #include */ +/* begin file generic/stage2/tape_writer.h for westmere */ +#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 westmere { +namespace { +namespace stage2 { - 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. +struct tape_writer { + /** The next place to write to tape */ + uint64_t *next_tape_loc; - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /** Write a signed 64-bit value to tape. */ + simdjson_inline void append_s64(int64_t value) noexcept; - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -}; + /** Write an unsigned 64-bit value to tape. */ + simdjson_inline void append_u64(uint64_t value) noexcept; -// 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 isn't 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; + /** Write a double value to tape. */ + simdjson_inline void append_double(double value) noexcept; - // 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); + /** + * 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; - // 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; - } + /** + * 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; - } - } 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; -} + /** + * 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; -// 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 isn't 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; - } - } - } + /** + * 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; - size_t offset = jsoncharutils::codepoint_to_utf8(code_point, *dst_ptr); - *dst_ptr += offset; - return offset > 0; -} +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); +} -/** - * 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_nullptrptr 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; - } - } - /* can't be reached */ - return nullptr; +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_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; - } - } - /* can't be reached */ - return nullptr; +/** 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++; } -} // namespace stringparsing +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 westmere } // namespace simdjson -/* end file src/generic/stage2/stringparsing.h */ -/* begin file src/generic/stage2/tape_builder.h */ -/* begin file src/generic/stage2/json_iterator.h */ -/* begin file src/generic/stage2/logger.h */ + +#endif // SIMDJSON_SRC_GENERIC_STAGE2_TAPE_WRITER_H +/* end file generic/stage2/tape_writer.h for westmere */ +/* including generic/stage2/logger.h for westmere: #include */ +/* begin file generic/stage2/logger.h for westmere */ +#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 { @@ -15811,7 +42429,21 @@ namespace logger { } // unnamed namespace } // namespace westmere } // namespace simdjson -/* end file src/generic/stage2/logger.h */ + +#endif // SIMDJSON_SRC_GENERIC_STAGE2_LOGGER_H +/* end file generic/stage2/logger.h for westmere */ + +// All other declarations +/* including generic/stage2/json_iterator.h for westmere: #include */ +/* begin file generic/stage2/json_iterator.h for westmere */ +#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 westmere { @@ -16124,118 +42756,347 @@ simdjson_warn_unused simdjson_inline error_code json_iterator::visit_primitive(V } } -} // namespace stage2 +} // 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 */ +#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 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, + + 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 isn't 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 isn't 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_nullptrptr 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; + } + } + /* can't be reached */ + return nullptr; +} + +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; + } + } + /* can't be reached */ + return nullptr; +} + +} // namespace stringparsing } // unnamed namespace } // namespace westmere } // namespace simdjson -/* end file src/generic/stage2/json_iterator.h */ -/* begin file src/generic/stage2/tape_writer.h */ + +#endif // SIMDJSON_SRC_GENERIC_STAGE2_STRINGPARSING_H +/* end file generic/stage2/stringparsing.h for westmere */ +/* including generic/stage2/structural_iterator.h for westmere: #include */ +/* begin file generic/stage2/structural_iterator.h for westmere */ +#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 westmere { 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 number_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++; -} +class structural_iterator { +public: + const uint8_t* const buf; + uint32_t *next_structural; + dom_parser_implementation &dom_parser; -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++; -} + // 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 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 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 simdjson -/* end file src/generic/stage2/tape_writer.h */ + +#endif // SIMDJSON_SRC_GENERIC_STAGE2_STRUCTURAL_ITERATOR_H +/* end file generic/stage2/structural_iterator.h for westmere */ +/* including generic/stage2/tape_builder.h for westmere: #include */ +/* begin file generic/stage2/tape_builder.h for westmere */ +#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 westmere { @@ -16317,7 +43178,7 @@ struct tape_builder { 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; -}; // class tape_builder +}; // struct tape_builder template simdjson_warn_unused simdjson_inline error_code tape_builder::parse_document( @@ -16517,24 +43378,128 @@ simdjson_inline void tape_builder::on_end_string(uint8_t *dst) noexcept { } // unnamed namespace } // namespace westmere } // namespace simdjson -/* end file src/generic/stage2/tape_builder.h */ + +#endif // SIMDJSON_SRC_GENERIC_STAGE2_TAPE_BUILDER_H +/* end file generic/stage2/tape_builder.h for westmere */ +/* end file generic/stage2/amalgamated.h for westmere */ // -// Implementation-specific overrides +// Stage 1 // namespace simdjson { 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; +} + namespace { -namespace stage1 { -simdjson_inline uint64_t json_string_scanner::find_escaped(uint64_t backslash) { - if (!backslash) { uint64_t escaped = prev_escaped; prev_escaped = 0; return escaped; } - return find_escaped_branchless(backslash); +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); + + // 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). + + + 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_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-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_third_byte | is_fourth_byte) > int8_t(0); } -} // namespace stage1 } // unnamed namespace +} // namespace westmere +} // namespace simdjson + +// +// Stage 2 +// + +// +// Implementation-specific overrides +// + +namespace simdjson { +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); @@ -16575,11 +43540,27 @@ simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t * } // namespace westmere } // namespace simdjson -/* begin file include/simdjson/westmere/end.h */ -SIMDJSON_UNTARGET_WESTMERE -/* end file include/simdjson/westmere/end.h */ -/* end file src/westmere/dom_parser_implementation.cpp */ +/* including simdjson/westmere/end.h: #include */ +/* 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 -SIMDJSON_POP_DISABLE_WARNINGS -/* end file src/simdjson.cpp */ +/* undefining SIMDJSON_IMPLEMENTATION from "westmere" */ +#undef SIMDJSON_IMPLEMENTATION +/* end file simdjson/westmere/end.h */ + +#endif // SIMDJSON_SRC_WESTMERE_CPP +/* end file westmere.cpp */ +#endif + +/* undefining SIMDJSON_CONDITIONAL_INCLUDE */ +#undef SIMDJSON_CONDITIONAL_INCLUDE + +SIMDJSON_POP_DISABLE_UNUSED_WARNINGS + +/* end file simdjson.cpp */ diff --git a/contrib/simdjson/simdjson.h b/contrib/simdjson/simdjson.h index d57a890d176..bca2f42af18 100644 --- a/contrib/simdjson/simdjson.h +++ b/contrib/simdjson/simdjson.h @@ -1,5 +1,6 @@ -/* auto-generated on 2023-07-06 21:34:14 -0400. Do not edit! */ -/* begin file include/simdjson.h */ +/* auto-generated on 2023-08-02 16:00:45 -0400. Do not edit! */ +/* including simdjson.h: */ +/* begin file simdjson.h */ #ifndef SIMDJSON_H #define SIMDJSON_H @@ -36,43 +37,14 @@ } */ -/* begin file include/simdjson/simdjson_version.h */ -// /include/simdjson/simdjson_version.h automatically generated by release.py, -// do not change by hand -#ifndef SIMDJSON_SIMDJSON_VERSION_H -#define SIMDJSON_SIMDJSON_VERSION_H - -/** The version of simdjson being used (major.minor.revision) */ -#define SIMDJSON_VERSION "3.2.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 = 2, - /** - * The revision (major.minor.REVISION) of simdjson being used. - */ - SIMDJSON_VERSION_REVISION = 1 -}; -} // namespace simdjson - -#endif // SIMDJSON_SIMDJSON_VERSION_H -/* end file include/simdjson/simdjson_version.h */ -/* begin file include/simdjson/dom.h */ -#ifndef SIMDJSON_DOM_H -#define SIMDJSON_DOM_H - -/* begin file include/simdjson/base.h */ -#ifndef SIMDJSON_BASE_H -#define SIMDJSON_BASE_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 -/* begin file include/simdjson/compiler_check.h */ +#include +/* including simdjson/compiler_check.h: #include "simdjson/compiler_check.h" */ +/* begin file simdjson/compiler_check.h */ #ifndef SIMDJSON_COMPILER_CHECK_H #define SIMDJSON_COMPILER_CHECK_H @@ -108,13 +80,9 @@ enum { #endif #endif // SIMDJSON_COMPILER_CHECK_H -/* end file include/simdjson/compiler_check.h */ -/* begin file include/simdjson/common_defs.h */ -#ifndef SIMDJSON_COMMON_DEFS_H -#define SIMDJSON_COMMON_DEFS_H - -#include -/* begin file include/simdjson/portability.h */ +/* end file simdjson/compiler_check.h */ +/* including simdjson/portability.h: #include "simdjson/portability.h" */ +/* begin file simdjson/portability.h */ #ifndef SIMDJSON_PORTABILITY_H #define SIMDJSON_PORTABILITY_H @@ -160,8 +128,6 @@ enum { #else #define SIMDJSON_IS_32BITS 1 -// We do not support 32-bit platforms, but it can be -// handy to identify them. #if defined(_M_IX86) || defined(__i386__) #define SIMDJSON_IS_X86_32BITS 1 #elif defined(__arm__) || defined(_M_ARM) @@ -185,6 +151,12 @@ use a 64-bit target such as x64, 64-bit ARM or 64-bit PPC.") #endif // SIMDJSON_NO_PORTABILITY_WARNING #endif // SIMDJSON_IS_32BITS +#define SIMDJSON_CAT_IMPLEMENTATION_(a,...) a ## __VA_ARGS__ +#define SIMDJSON_CAT(a,...) SIMDJSON_CAT_IMPLEMENTATION_(a, __VA_ARGS__) + +#define SIMDJSON_STRINGIFY_IMPLEMENTATION_(a,...) #a SIMDJSON_STRINGIFY(__VA_ARGS__) +#define SIMDJSON_STRINGIFY(a,...) SIMDJSON_CAT_IMPLEMENTATION_(a, __VA_ARGS__) + // this is almost standard? #undef SIMDJSON_STRINGIFY_IMPLEMENTATION_ #undef SIMDJSON_STRINGIFY @@ -306,10 +278,9 @@ use a 64-bit target such as x64, 64-bit ARM or 64-bit PPC.") #endif #endif // SIMDJSON_PORTABILITY_H -/* end file include/simdjson/portability.h */ +/* end file simdjson/portability.h */ namespace simdjson { - namespace internal { /** * @private @@ -324,7 +295,6 @@ char *to_chars(char *first, const char *last, double value); */ double from_chars(const char *first) noexcept; double from_chars(const char *first, const char* end) noexcept; - } #ifndef SIMDJSON_EXCEPTIONS @@ -335,26 +305,6 @@ double from_chars(const char *first, const char* end) noexcept; #endif #endif -/** 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; - -/** - * 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; - } // namespace simdjson #if defined(__GNUC__) @@ -409,6 +359,9 @@ constexpr size_t DEFAULT_MAX_DEPTH = 1024; #define SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING #define SIMDJSON_POP_DISABLE_WARNINGS __pragma(warning( pop )) + #define SIMDJSON_PUSH_DISABLE_UNUSED_WARNINGS + #define SIMDJSON_POP_DISABLE_UNUSED_WARNINGS + #else // SIMDJSON_REGULAR_VISUAL_STUDIO #define simdjson_really_inline inline __attribute__((always_inline)) @@ -454,7 +407,8 @@ constexpr size_t DEFAULT_MAX_DEPTH = 1024; SIMDJSON_DISABLE_GCC_WARNING(-Wshadow) \ SIMDJSON_DISABLE_GCC_WARNING(-Wunused-parameter) \ SIMDJSON_DISABLE_GCC_WARNING(-Wunused-variable) \ - SIMDJSON_DISABLE_GCC_WARNING(-Wmaybe-uninitialized) + SIMDJSON_DISABLE_GCC_WARNING(-Wmaybe-uninitialized) \ + SIMDJSON_DISABLE_GCC_WARNING(-Wformat-security) #endif // __clang__ #define SIMDJSON_PRAGMA(P) _Pragma(#P) @@ -468,6 +422,10 @@ constexpr size_t DEFAULT_MAX_DEPTH = 1024; #define SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING SIMDJSON_DISABLE_GCC_WARNING(-Wstrict-overflow) #define SIMDJSON_POP_DISABLE_WARNINGS _Pragma("GCC diagnostic pop") + #define SIMDJSON_PUSH_DISABLE_UNUSED_WARNINGS SIMDJSON_PUSH_DISABLE_WARNINGS \ + SIMDJSON_DISABLE_GCC_WARNING(-Wunused) + #define SIMDJSON_POP_DISABLE_UNUSED_WARNINGS SIMDJSON_POP_DISABLE_WARNINGS + #endif // MSC_VER @@ -570,7 +528,8 @@ constexpr size_t DEFAULT_MAX_DEPTH = 1024; // #ifndef SIMDJSON_HAS_STRING_VIEW SIMDJSON_PUSH_DISABLE_ALL_WARNINGS -/* begin file include/simdjson/nonstd/string_view.hpp */ +/* including simdjson/nonstd/string_view.hpp: #include "simdjson/nonstd/string_view.hpp" */ +/* begin file simdjson/nonstd/string_view.hpp */ // Copyright 2017-2020 by Martin Moene // // string-view lite, a C++17-like string_view for C++98 and later. @@ -2255,7 +2214,7 @@ nssv_RESTORE_WARNINGS() #endif // nssv_HAVE_STD_STRING_VIEW #endif // NONSTD_SV_LITE_H_INCLUDED -/* end file include/simdjson/nonstd/string_view.hpp */ +/* end file simdjson/nonstd/string_view.hpp */ SIMDJSON_POP_DISABLE_WARNINGS namespace std { @@ -2311,7 +2270,6 @@ namespace std { # define simdjson_fallthrough do {} while (0) /* fallthrough */ #endif // simdjson_fallthrough - #if SIMDJSON_DEVELOPMENT_CHECKS #define SIMDJSON_DEVELOPMENT_ASSERT(expr) do { assert ((expr)); } while (0) #else @@ -2322,18 +2280,82 @@ namespace std { #define SIMDJSON_UTF8VALIDATION 1 #endif +#ifdef __has_include +// How do we detect that a compiler supports vbmi2? +// For sure if the following header is found, we are ok? +#if __has_include() +#define SIMDJSON_COMPILER_SUPPORTS_VBMI2 1 +#endif +#endif + +#ifdef _MSC_VER +#if _MSC_VER >= 1920 +// Visual Studio 2019 and up support VBMI2 under x64 even if the header +// avx512vbmi2intrin.h is not found. +#define SIMDJSON_COMPILER_SUPPORTS_VBMI2 1 +#endif +#endif + +// By default, we allow AVX512. +#ifndef SIMDJSON_AVX512_ALLOWED +#define SIMDJSON_AVX512_ALLOWED 1 +#endif + #endif // SIMDJSON_COMMON_DEFS_H -/* end file include/simdjson/common_defs.h */ +/* end file simdjson/common_defs.h */ -SIMDJSON_PUSH_DISABLE_WARNINGS -SIMDJSON_DISABLE_UNDESIRED_WARNINGS +// This provides the public API for simdjson. +// DOM and ondemand are amalgamated separately, in simdjson.h +/* including simdjson/simdjson_version.h: #include "simdjson/simdjson_version.h" */ +/* begin file simdjson/simdjson_version.h */ +// /include/simdjson/simdjson_version.h automatically generated by release.py, +// do not change by hand +#ifndef SIMDJSON_SIMDJSON_VERSION_H +#define SIMDJSON_SIMDJSON_VERSION_H + +/** The version of simdjson being used (major.minor.revision) */ +#define SIMDJSON_VERSION "3.2.2" + +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 = 2, + /** + * The revision (major.minor.REVISION) of simdjson being used. + */ + SIMDJSON_VERSION_REVISION = 2 +}; +} // namespace simdjson + +#endif // SIMDJSON_SIMDJSON_VERSION_H +/* end file simdjson/simdjson_version.h */ + +/* including simdjson/base.h: #include "simdjson/base.h" */ +/* begin file simdjson/base.h */ +/** + * @file Base declarations for all simdjson headers + * @private + */ +#ifndef SIMDJSON_BASE_H +#define SIMDJSON_BASE_H -// Public API -/* begin file include/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 +/* skipped duplicate #include "simdjson/base.h" */ + #include +#include namespace simdjson { @@ -2637,295 +2659,298 @@ inline const std::string error_message(int error) noexcept; } // namespace simdjson #endif // SIMDJSON_ERROR_H -/* end file include/simdjson/error.h */ -/* begin file include/simdjson/minify.h */ -#ifndef SIMDJSON_MINIFY_H -#define SIMDJSON_MINIFY_H - -/* begin file include/simdjson/padded_string.h */ -#ifndef SIMDJSON_PADDED_STRING_H -#define SIMDJSON_PADDED_STRING_H - -#include -#include -#include -#include +/* end file simdjson/error.h */ +/* skipped duplicate #include "simdjson/portability.h" */ +/** + * @brief The top level simdjson namespace, containing everything the library provides. + */ namespace simdjson { -class padded_string_view; +SIMDJSON_PUSH_DISABLE_UNUSED_WARNINGS + +/** The maximum document size supported by simdjson. */ +constexpr size_t SIMDJSON_MAXSIZE_BYTES = 0xFFFFFFFF; /** - * String with extra allocation for ease of use with parser::parse() + * The amount of padding needed in a buffer to parse JSON. * - * This is a move-only class, it cannot be copied. + * 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 */ -struct padded_string final { +constexpr size_t SIMDJSON_PADDING = 64; - /** - * 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 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; +/** + * 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; - /** - * The length of the string. - * - * Does not include padding. - */ - size_t size() const noexcept; +SIMDJSON_POP_DISABLE_UNUSED_WARNINGS - /** - * The length of the string. - * - * Does not include padding. - */ - size_t length() const noexcept; +class implementation; +struct padded_string; +class padded_string_view; +enum class stage1_mode; - /** - * The string data. - **/ - const char *data() const noexcept; - const uint8_t *u8data() const noexcept { return static_cast(static_cast(data_ptr));} +namespace internal { - /** - * The string data. - **/ - char *data() noexcept; +template +class atomic_ptr; +class dom_parser_implementation; +class escape_json_string; +class tape_ref; +struct value128; +enum class tape_type; - /** - * Create a std::string_view with the same content. - */ - operator std::string_view() const; +} // namespace internal +} // namespace simdjson - /** - * Create a padded_string_view with the same content. - */ - operator padded_string_view() const noexcept; +#endif // SIMDJSON_BASE_H +/* end file simdjson/base.h */ - /** - * Load this padded string from a file. - * - * @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; +/* skipped duplicate #include "simdjson/error.h" */ +/* including simdjson/error-inl.h: #include "simdjson/error-inl.h" */ +/* begin file simdjson/error-inl.h */ +#ifndef SIMDJSON_ERROR_INL_H +#define SIMDJSON_ERROR_INL_H -private: - padded_string &operator=(const padded_string &o) = delete; - padded_string(const padded_string &o) = delete; +/* skipped duplicate #include "simdjson/error.h" */ - size_t viable_size{0}; - char *data_ptr{nullptr}; +#include -}; // padded_string +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 -/** - * 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(); } -#endif +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; +} -} // namespace simdjson +// deprecated function +#ifndef SIMDJSON_DISABLE_DEPRECATED_API +inline const std::string error_message(int error) noexcept { + if (error < 0 || error >= error_code::NUM_ERROR_CODES) { + return internal::error_codes[UNEXPECTED_ERROR].message; + } + return internal::error_codes[error].message; +} +#endif // SIMDJSON_DISABLE_DEPRECATED_API -// 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) { - return simdjson::padded_string(str, len); +inline std::ostream& operator<<(std::ostream& out, error_code error) noexcept { + return out << error_message(error); } -namespace simdjson { namespace internal { -// The allocate_padded_buffer function is a low-level function to allocate memory -// with padding so we can read past the "length" bytes safely. It is used by -// the padded_string class automatically. It returns nullptr in case -// of error: the caller should check for a null pointer. -// The length parameter is the maximum size in bytes of the string. -// The caller is responsible to free the memory (e.g., delete[] (...)). -inline char *allocate_padded_buffer(size_t length) noexcept; +// +// internal::simdjson_result_base inline implementation +// -} // namespace internal -} // namespace simdjson +template +simdjson_inline void simdjson_result_base::tie(T &value, error_code &error) && noexcept { + error = this->second; + if (!error) { + value = std::forward>(*this).first; + } +} -#endif // SIMDJSON_PADDED_STRING_H -/* end file include/simdjson/padded_string.h */ -#include -#include -#include +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; +} -namespace simdjson { +template +simdjson_inline error_code simdjson_result_base::error() const noexcept { + return this->second; +} +#if SIMDJSON_EXCEPTIONS +template +simdjson_inline T& simdjson_result_base::value() & noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return this->first; +} -/** - * - * 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; +template +simdjson_inline T&& simdjson_result_base::value() && noexcept(false) { + return std::forward>(*this).take_value(); +} -} // namespace simdjson +template +simdjson_inline T&& simdjson_result_base::take_value() && noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return std::forward(this->first); +} -#endif // SIMDJSON_MINIFY_H -/* end file include/simdjson/minify.h */ -/* begin file include/simdjson/padded_string_view.h */ -#ifndef SIMDJSON_PADDED_STRING_VIEW_H -#define SIMDJSON_PADDED_STRING_VIEW_H +template +simdjson_inline simdjson_result_base::operator T&&() && noexcept(false) { + return std::forward>(*this).take_value(); +} +#endif // SIMDJSON_EXCEPTIONS -#include -#include -#include -#include +template +simdjson_inline const T& simdjson_result_base::value_unsafe() const& noexcept { + return this->first; +} -namespace simdjson { +template +simdjson_inline T&& simdjson_result_base::value_unsafe() && noexcept { + return std::forward(this->first); +} -/** - * 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; +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) {} -public: - /** Create an empty padded_string_view. */ - inline padded_string_view() noexcept = default; +} // namespace internal - /** - * 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. - */ - 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; +/// +/// simdjson_result inline implementation +/// - /** - * 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; +template +simdjson_inline void simdjson_result::tie(T &value, error_code &error) && noexcept { + std::forward>(*this).tie(value, error); +} - /** - * 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. - */ - explicit inline padded_string_view(std::string_view s, size_t capacity) noexcept; +template +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(T &value) && noexcept { + return std::forward>(*this).get(value); +} - /** The number of allocated bytes. */ - inline size_t capacity() const noexcept; +template +simdjson_inline error_code simdjson_result::error() const noexcept { + return internal::simdjson_result_base::error(); +} - /** The amount of padding on the string (capacity() - length()) */ - inline size_t padding() const noexcept; +#if SIMDJSON_EXCEPTIONS -}; // padded_string_view +template +simdjson_inline T& simdjson_result::value() & noexcept(false) { + return internal::simdjson_result_base::value(); +} -#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) { return out << s.value(); } -#endif +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 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 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() {} } // namespace simdjson -#endif // SIMDJSON_PADDED_STRING_VIEW_H -/* end file include/simdjson/padded_string_view.h */ -/* begin file include/simdjson/implementation.h */ +#endif // SIMDJSON_ERROR_INL_H +/* end file simdjson/error-inl.h */ +/* including simdjson/implementation.h: #include "simdjson/implementation.h" */ +/* begin file simdjson/implementation.h */ #ifndef SIMDJSON_IMPLEMENTATION_H #define SIMDJSON_IMPLEMENTATION_H -/* begin file include/simdjson/internal/dom_parser_implementation.h */ -#ifndef SIMDJSON_INTERNAL_DOM_PARSER_IMPLEMENTATION_H -#define SIMDJSON_INTERNAL_DOM_PARSER_IMPLEMENTATION_H +/* including simdjson/internal/atomic_ptr.h: #include "simdjson/internal/atomic_ptr.h" */ +/* begin file simdjson/internal/atomic_ptr.h */ +#ifndef SIMDJSON_INTERNAL_ATOMIC_PTR_H +#define SIMDJSON_INTERNAL_ATOMIC_PTR_H -#include +/* skipped duplicate #include "simdjson/base.h" */ +#include namespace simdjson { +namespace internal { -namespace dom { +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 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; +}; + +} // namespace internal +} // namespace simdjson + +#endif // SIMDJSON_INTERNAL_ATOMIC_PTR_H +/* end file simdjson/internal/atomic_ptr.h */ +/* including simdjson/internal/dom_parser_implementation.h: #include "simdjson/internal/dom_parser_implementation.h" */ +/* begin file simdjson/internal/dom_parser_implementation.h */ +#ifndef SIMDJSON_INTERNAL_DOM_PARSER_IMPLEMENTATION_H +#define SIMDJSON_INTERNAL_DOM_PARSER_IMPLEMENTATION_H + +/* skipped duplicate #include "simdjson/base.h" */ +/* skipped duplicate #include "simdjson/error.h" */ +#include + +namespace simdjson { + +namespace dom { class document; } // namespace dom @@ -3168,264 +3193,9 @@ inline error_code dom_parser_implementation::allocate(size_t capacity, size_t ma } // namespace simdjson #endif // SIMDJSON_INTERNAL_DOM_PARSER_IMPLEMENTATION_H -/* end file include/simdjson/internal/dom_parser_implementation.h */ -/* begin file include/simdjson/internal/isadetection.h */ -/* From -https://github.com/endorno/pytorch/blob/master/torch/lib/TH/generic/simd/simd.h -Highly modified. - -Copyright (c) 2016- Facebook, Inc (Adam Paszke) -Copyright (c) 2014- Facebook, Inc (Soumith Chintala) -Copyright (c) 2011-2014 Idiap Research Institute (Ronan Collobert) -Copyright (c) 2012-2014 Deepmind Technologies (Koray Kavukcuoglu) -Copyright (c) 2011-2012 NEC Laboratories America (Koray Kavukcuoglu) -Copyright (c) 2011-2013 NYU (Clement Farabet) -Copyright (c) 2006-2010 NEC Laboratories America (Ronan Collobert, Leon Bottou, -Iain Melvin, Jason Weston) Copyright (c) 2006 Idiap Research Institute -(Samy Bengio) Copyright (c) 2001-2004 Idiap Research Institute (Ronan Collobert, -Samy Bengio, Johnny Mariethoz) - -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - -3. Neither the names of Facebook, Deepmind Technologies, NYU, NEC Laboratories -America and IDIAP Research Institute nor the names of its contributors may be - used to endorse or promote products derived from this software without - specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. -*/ - -#ifndef SIMDJSON_INTERNAL_ISADETECTION_H -#define SIMDJSON_INTERNAL_ISADETECTION_H - -#include -#include -#if defined(_MSC_VER) -#include -#elif defined(HAVE_GCC_GET_CPUID) && defined(USE_GCC_GET_CPUID) -#include -#endif - -namespace simdjson { -namespace internal { - -enum instruction_set { - DEFAULT = 0x0, - NEON = 0x1, - AVX2 = 0x4, - SSE42 = 0x8, - PCLMULQDQ = 0x10, - BMI1 = 0x20, - BMI2 = 0x40, - ALTIVEC = 0x80, - AVX512F = 0x100, - AVX512DQ = 0x200, - AVX512IFMA = 0x400, - AVX512PF = 0x800, - AVX512ER = 0x1000, - AVX512CD = 0x2000, - AVX512BW = 0x4000, - AVX512VL = 0x8000, - AVX512VBMI2 = 0x10000 -}; - -#if defined(__PPC64__) - -static inline uint32_t detect_supported_architectures() { - return instruction_set::ALTIVEC; -} - -#elif defined(__aarch64__) || defined(_M_ARM64) - -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 -} - - - -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]; -#elif defined(HAVE_GCC_GET_CPUID) && defined(USE_GCC_GET_CPUID) - 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; -#endif -} - - -static inline uint64_t xgetbv() { -#if defined(_MSC_VER) - 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); -#endif -} - -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); - - 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; - } +/* end file simdjson/internal/dom_parser_implementation.h */ - - if ((ecx & cpuid_osxsave) != cpuid_osxsave) { - return host_isa; - } - - // xgetbv for checking if the OS saves registers - uint64_t xcr0 = xgetbv(); - - 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; - } - - if (ebx & cpuid_bmi2_bit) { - host_isa |= instruction_set::BMI2; - } - - if (!((xcr0 & cpuid_avx512_saved) == cpuid_avx512_saved)) { - return host_isa; - } - - if (ebx & cpuid_avx512f_bit) { - host_isa |= instruction_set::AVX512F; - } - - if (ebx & cpuid_avx512dq_bit) { - host_isa |= instruction_set::AVX512DQ; - } - - if (ebx & cpuid_avx512ifma_bit) { - host_isa |= instruction_set::AVX512IFMA; - } - - if (ebx & cpuid_avx512pf_bit) { - host_isa |= instruction_set::AVX512PF; - } - - if (ebx & cpuid_avx512er_bit) { - host_isa |= instruction_set::AVX512ER; - } - - if (ebx & cpuid_avx512cd_bit) { - host_isa |= instruction_set::AVX512CD; - } - - if (ebx & cpuid_avx512bw_bit) { - host_isa |= instruction_set::AVX512BW; - } - - if (ebx & cpuid_avx512vl_bit) { - host_isa |= instruction_set::AVX512VL; - } - - if (ecx & cpuid_avx512vbmi2_bit) { - host_isa |= instruction_set::AVX512VBMI2; - } - - return host_isa; -} -#else // fallback - - -static inline uint32_t detect_supported_architectures() { - return instruction_set::DEFAULT; -} - - -#endif // end SIMD extension detection code - -} // namespace internal -} // namespace simdjson - -#endif // SIMDJSON_INTERNAL_ISADETECTION_H -/* end file include/simdjson/internal/isadetection.h */ -#include -#include -#include +#include namespace simdjson { @@ -3457,10 +3227,6 @@ simdjson_inline simdjson_warn_unused bool validate_utf8(const std::string& s) no return validate_utf8(s.data(), s.size()); } -namespace dom { - class document; -} // namespace dom - /** * An implementation of simdjson for a particular CPU architecture. * @@ -3635,24 +3401,6 @@ class available_implementation_list { const implementation *detect_best_supported() const noexcept; }; -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 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; -}; - } // namespace internal /** @@ -3670,212 +3418,353 @@ extern SIMDJSON_DLLIMPORTEXPORT internal::atomic_ptr& get_ } // namespace simdjson #endif // SIMDJSON_IMPLEMENTATION_H -/* end file include/simdjson/implementation.h */ +/* end file simdjson/implementation.h */ +/* including simdjson/minify.h: #include "simdjson/minify.h" */ +/* begin file simdjson/minify.h */ +#ifndef SIMDJSON_MINIFY_H +#define SIMDJSON_MINIFY_H -// Inline functions -/* begin file include/simdjson/error-inl.h */ -#ifndef SIMDJSON_INLINE_ERROR_H -#define SIMDJSON_INLINE_ERROR_H +/* skipped duplicate #include "simdjson/base.h" */ +/* including simdjson/padded_string.h: #include "simdjson/padded_string.h" */ +/* begin file simdjson/padded_string.h */ +#ifndef SIMDJSON_PADDED_STRING_H +#define SIMDJSON_PADDED_STRING_H + +/* skipped duplicate #include "simdjson/base.h" */ +/* skipped duplicate #include "simdjson/error.h" */ + +/* skipped duplicate #include "simdjson/error-inl.h" */ #include +#include #include -#include +#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 const char *error_message(error_code error) noexcept { - // If you're using error_code, we're trusting you got it from the enum. - return internal::error_codes[int(error)].message; -} - -// deprecated function -#ifndef SIMDJSON_DISABLE_DEPRECATED_API -inline const std::string error_message(int error) noexcept { - if (error < 0 || error >= error_code::NUM_ERROR_CODES) { - return internal::error_codes[UNEXPECTED_ERROR].message; - } - return internal::error_codes[error].message; -} -#endif // SIMDJSON_DISABLE_DEPRECATED_API - -inline std::ostream& operator<<(std::ostream& out, error_code error) noexcept { - return out << error_message(error); -} - -namespace internal { - -// -// internal::simdjson_result_base 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_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; -} -#if SIMDJSON_EXCEPTIONS +class padded_string_view; -template -simdjson_inline T& simdjson_result_base::value() & noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return this->first; -} +/** + * 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 { -template -simdjson_inline T&& simdjson_result_base::value() && noexcept(false) { - return std::forward>(*this).take_value(); -} + /** + * 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 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; -template -simdjson_inline T&& simdjson_result_base::take_value() && noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return std::forward(this->first); -} + /** + * The length of the string. + * + * Does not include padding. + */ + size_t size() const noexcept; -template -simdjson_inline simdjson_result_base::operator T&&() && noexcept(false) { - return std::forward>(*this).take_value(); -} + /** + * The length of the string. + * + * Does not include padding. + */ + size_t length() const noexcept; -#endif // SIMDJSON_EXCEPTIONS + /** + * The string data. + **/ + const char *data() const noexcept; + const uint8_t *u8data() const noexcept { return static_cast(static_cast(data_ptr));} -template -simdjson_inline const T& simdjson_result_base::value_unsafe() const& noexcept { - return this->first; -} + /** + * The string data. + **/ + char *data() noexcept; -template -simdjson_inline T&& simdjson_result_base::value_unsafe() && noexcept { - return std::forward(this->first); -} + /** + * Create a std::string_view with the same content. + */ + operator std::string_view() const; -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) {} + /** + * Create a padded_string_view with the same content. + */ + operator padded_string_view() const noexcept; -} // namespace internal + /** + * Load this padded string from a file. + * + * @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; -/// -/// simdjson_result inline implementation -/// +private: + padded_string &operator=(const padded_string &o) = delete; + padded_string(const padded_string &o) = delete; -template -simdjson_inline void simdjson_result::tie(T &value, error_code &error) && noexcept { - std::forward>(*this).tie(value, error); -} + size_t viable_size{0}; + char *data_ptr{nullptr}; -template -simdjson_warn_unused simdjson_inline error_code simdjson_result::get(T &value) && noexcept { - return std::forward>(*this).get(value); -} +}; // padded_string -template -simdjson_inline error_code simdjson_result::error() const noexcept { - return internal::simdjson_result_base::error(); -} +/** + * 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(); } +#endif -template -simdjson_inline T& simdjson_result::value() & noexcept(false) { - return internal::simdjson_result_base::value(); -} +} // namespace simdjson -template -simdjson_inline T&& simdjson_result::value() && noexcept(false) { - return std::forward>(*this).value(); -} +// 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); -template -simdjson_inline T&& simdjson_result::take_value() && noexcept(false) { - return std::forward>(*this).take_value(); -} +namespace simdjson { +namespace internal { -template -simdjson_inline simdjson_result::operator T&&() && noexcept(false) { - return std::forward>(*this).take_value(); -} +// 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; -#endif // SIMDJSON_EXCEPTIONS +} // namespace internal +} // namespace simdjson -template -simdjson_inline const T& simdjson_result::value_unsafe() const& noexcept { - return internal::simdjson_result_base::value_unsafe(); -} +#endif // SIMDJSON_PADDED_STRING_H +/* end file simdjson/padded_string.h */ +#include +#include +#include -template -simdjson_inline T&& simdjson_result::value_unsafe() && noexcept { - return std::forward>(*this).value_unsafe(); -} +namespace simdjson { -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() {} +/** + * + * Minify the input string assuming that it represents a JSON string, does not parse or validate. + * This function is much faster than parsing a JSON string and then writing a minified version of it. + * However, it does not validate the input. It will merely return an error in simple cases (e.g., if + * there is a string that was never terminated). + * + * + * @param buf the json document to minify. + * @param len the length of the json document. + * @param dst the buffer to write the minified document to. *MUST* be allocated up to len bytes. + * @param dst_len the number of bytes written. Output only. + * @return the error code, or SUCCESS if there was no error. + */ +simdjson_warn_unused error_code minify(const char *buf, size_t len, char *dst, size_t &dst_len) noexcept; } // namespace simdjson -#endif // SIMDJSON_INLINE_ERROR_H -/* end file include/simdjson/error-inl.h */ -/* begin file include/simdjson/padded_string-inl.h */ -#ifndef SIMDJSON_INLINE_PADDED_STRING_H -#define SIMDJSON_INLINE_PADDED_STRING_H +#endif // SIMDJSON_MINIFY_H +/* end file simdjson/minify.h */ +/* skipped duplicate #include "simdjson/padded_string.h" */ +/* including simdjson/padded_string-inl.h: #include "simdjson/padded_string-inl.h" */ +/* begin file simdjson/padded_string-inl.h */ +#ifndef SIMDJSON_PADDED_STRING_INL_H +#define SIMDJSON_PADDED_STRING_INL_H + +/* skipped duplicate #include "simdjson/padded_string.h" */ +/* including simdjson/padded_string_view.h: #include "simdjson/padded_string_view.h" */ +/* begin file simdjson/padded_string_view.h */ +#ifndef SIMDJSON_PADDED_STRING_VIEW_H +#define SIMDJSON_PADDED_STRING_VIEW_H +/* skipped duplicate #include "simdjson/portability.h" */ +/* skipped duplicate #include "simdjson/base.h" // for SIMDJSON_PADDING */ +/* skipped duplicate #include "simdjson/error.h" */ -#include #include #include #include +#include namespace simdjson { -namespace internal { -// The allocate_padded_buffer function is a low-level function to allocate memory -// with padding so we can read past the "length" bytes safely. It is used by -// the padded_string class automatically. It returns nullptr in case -// of error: the caller should check for a null pointer. +/** + * 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; + + /** + * 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. + */ + 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 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. + */ + 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 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); +#endif + +} // namespace simdjson + +#endif // SIMDJSON_PADDED_STRING_VIEW_H +/* end file simdjson/padded_string_view.h */ + +/* skipped duplicate #include "simdjson/error-inl.h" */ +/* including simdjson/padded_string_view-inl.h: #include "simdjson/padded_string_view-inl.h" */ +/* begin file simdjson/padded_string_view-inl.h */ +#ifndef SIMDJSON_PADDED_STRING_VIEW_INL_H +#define SIMDJSON_PADDED_STRING_VIEW_INL_H + +/* skipped duplicate #include "simdjson/padded_string_view.h" */ + +/* skipped duplicate #include "simdjson/error-inl.h" */ + +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) +{ +} + +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 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) +{ +} + +inline size_t padded_string_view::capacity() const noexcept { return _capacity; } + +inline size_t padded_string_view::padding() const noexcept { return capacity() - length(); } + +#if SIMDJSON_EXCEPTIONS +inline std::ostream& operator<<(std::ostream& out, simdjson_result &s) noexcept(false) { return out << s.value(); } +#endif + +} // namespace simdjson + + +#endif // SIMDJSON_PADDED_STRING_VIEW_INL_H +/* end file simdjson/padded_string_view-inl.h */ + +#include + +namespace simdjson { +namespace internal { + +// The allocate_padded_buffer function is a low-level function to allocate memory +// with padding so we can read past the "length" bytes safely. It is used by +// the padded_string class automatically. It returns nullptr in case +// of error: the caller should check for a null pointer. // The length parameter is the maximum size in bytes of the string. // The caller is responsible to free the memory (e.g., delete[] (...)). inline char *allocate_padded_buffer(size_t length) noexcept { @@ -4030,107 +3919,97 @@ inline simdjson_result padded_string::load(std::string_view filen } // namespace simdjson -#endif // SIMDJSON_INLINE_PADDED_STRING_H -/* end file include/simdjson/padded_string-inl.h */ -/* begin file include/simdjson/padded_string_view-inl.h */ -#ifndef SIMDJSON_PADDED_STRING_VIEW_INL_H -#define SIMDJSON_PADDED_STRING_VIEW_INL_H +inline simdjson::padded_string operator "" _padded(const char *str, size_t len) { + return simdjson::padded_string(str, len); +} +#endif // SIMDJSON_PADDED_STRING_INL_H +/* end file simdjson/padded_string-inl.h */ +/* skipped duplicate #include "simdjson/padded_string_view.h" */ +/* skipped duplicate #include "simdjson/padded_string_view-inl.h" */ -#include -#include -#include -#include +/* including simdjson/dom.h: #include "simdjson/dom.h" */ +/* begin file simdjson/dom.h */ +#ifndef SIMDJSON_DOM_H +#define SIMDJSON_DOM_H -namespace simdjson { +/* including simdjson/dom/base.h: #include "simdjson/dom/base.h" */ +/* begin file simdjson/dom/base.h */ +#ifndef SIMDJSON_DOM_BASE_H +#define SIMDJSON_DOM_BASE_H -inline padded_string_view::padded_string_view(const char* s, size_t len, size_t capacity) noexcept - : std::string_view(s, len), _capacity(capacity) -{ -} +/* skipped duplicate #include "simdjson/base.h" */ -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) -{ -} +namespace simdjson { -inline padded_string_view::padded_string_view(const std::string &s) noexcept - : std::string_view(s), _capacity(s.capacity()) -{ -} +/** + * @brief A DOM API on top of the simdjson parser. + */ +namespace dom { -inline padded_string_view::padded_string_view(std::string_view s, size_t capacity) noexcept - : std::string_view(s), _capacity(capacity) -{ -} +/** 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; -inline size_t padded_string_view::capacity() const noexcept { return _capacity; } +/** + * It is wasteful to allocate memory for tiny documents (e.g., 4 bytes). + */ +static constexpr size_t MINIMAL_DOCUMENT_CAPACITY = 32; -inline size_t padded_string_view::padding() const noexcept { return capacity() - length(); } +class array; +class document; +class document_stream; +class element; +class key_value_pair; +class object; +class parser; -} // namespace simdjson +#ifdef SIMDJSON_THREADS_ENABLED +struct stage1_worker; +#endif // SIMDJSON_THREADS_ENABLED -#endif // SIMDJSON_PADDED_STRING_VIEW_INL_H -/* end file include/simdjson/padded_string_view-inl.h */ +} // namespace dom -SIMDJSON_POP_DISABLE_WARNINGS +namespace internal { -#endif // SIMDJSON_BASE_H -/* end file include/simdjson/base.h */ +template +class string_builder; +class tape_ref; -SIMDJSON_PUSH_DISABLE_WARNINGS -SIMDJSON_DISABLE_UNDESIRED_WARNINGS +} // namespace internal -/* begin file include/simdjson/dom/array.h */ +} // namespace simdjson + +#endif // SIMDJSON_DOM_BASE_H +/* end file simdjson/dom/base.h */ +/* including simdjson/dom/array.h: #include "simdjson/dom/array.h" */ +/* begin file simdjson/dom/array.h */ #ifndef SIMDJSON_DOM_ARRAY_H #define SIMDJSON_DOM_ARRAY_H -/* begin file include/simdjson/internal/tape_ref.h */ +/* skipped duplicate #include "simdjson/dom/base.h" */ +/* including simdjson/internal/tape_ref.h: #include "simdjson/internal/tape_ref.h" */ +/* begin file simdjson/internal/tape_ref.h */ #ifndef SIMDJSON_INTERNAL_TAPE_REF_H #define SIMDJSON_INTERNAL_TAPE_REF_H -/* begin file include/simdjson/internal/tape_type.h */ -#ifndef SIMDJSON_INTERNAL_TAPE_TYPE_H -#define SIMDJSON_INTERNAL_TAPE_TYPE_H +/* skipped duplicate #include "simdjson/base.h" */ namespace simdjson { -namespace internal { - -/** - * The possible types in the tape. - */ -enum class tape_type { - ROOT = 'r', - START_ARRAY = '[', - START_OBJECT = '{', - END_ARRAY = ']', - END_OBJECT = '}', - STRING = '"', - INT64 = 'l', - UINT64 = 'u', - DOUBLE = 'd', - TRUE_VALUE = 't', - FALSE_VALUE = 'f', - NULL_VALUE = 'n' -}; // enum class tape_type - -} // namespace internal -} // namespace simdjson - -#endif // SIMDJSON_INTERNAL_TAPE_TYPE_H -/* end file include/simdjson/internal/tape_type.h */ - -namespace simdjson { - namespace dom { - class document; -} +class document; +} // namespace dom namespace internal { -constexpr const uint64_t JSON_VALUE_MASK = 0x00FFFFFFFFFFFFFF; -constexpr const uint32_t JSON_COUNT_MASK = 0xFFFFFF; - /** * A reference to an element on the tape. Internal only. */ @@ -4168,19 +4047,11 @@ class tape_ref { } // namespace simdjson #endif // SIMDJSON_INTERNAL_TAPE_REF_H -/* end file include/simdjson/internal/tape_ref.h */ +/* end file simdjson/internal/tape_ref.h */ namespace simdjson { - -namespace internal { -template -class string_builder; -} namespace dom { -class document; -class element; - /** * JSON array. */ @@ -4347,27 +4218,31 @@ inline constexpr bool enable_view -#include namespace simdjson { namespace dom { -class element; - /** * A parsed JSON document. * @@ -4449,35 +4324,12 @@ class document { } // namespace simdjson #endif // SIMDJSON_DOM_DOCUMENT_H -/* end file include/simdjson/dom/document.h */ -#include -#include -#include +/* end file simdjson/dom/document.h */ namespace simdjson { namespace dom { -class document_stream; -class element; - -/** 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; - /** * A persistent document parser. * @@ -5058,7 +4910,8 @@ class parser { } // namespace simdjson #endif // SIMDJSON_DOM_PARSER_H -/* end file include/simdjson/dom/parser.h */ +/* end file simdjson/dom/parser.h */ + #ifdef SIMDJSON_THREADS_ENABLED #include #include @@ -5068,7 +4921,6 @@ class parser { namespace simdjson { namespace dom { - #ifdef SIMDJSON_THREADS_ENABLED /** @private Custom worker class **/ struct stage1_worker { @@ -5377,22 +5229,18 @@ struct simdjson_result : public internal::simdjson_result_ } // namespace simdjson #endif // SIMDJSON_DOCUMENT_STREAM_H -/* end file include/simdjson/dom/document_stream.h */ -/* begin file include/simdjson/dom/element.h */ +/* end file simdjson/dom/document_stream.h */ +/* skipped duplicate #include "simdjson/dom/document.h" */ +/* including simdjson/dom/element.h: #include "simdjson/dom/element.h" */ +/* begin file simdjson/dom/element.h */ #ifndef SIMDJSON_DOM_ELEMENT_H #define SIMDJSON_DOM_ELEMENT_H -#include +/* skipped duplicate #include "simdjson/dom/base.h" */ +/* skipped duplicate #include "simdjson/dom/array.h" */ namespace simdjson { -namespace internal { -template -class string_builder; -} namespace dom { -class array; -class document; -class object; /** * The actual concrete type of a JSON element @@ -5915,27 +5763,22 @@ struct simdjson_result : public internal::simdjson_result_base -class string_builder; -} namespace dom { -class document; -class element; -class key_value_pair; - /** * JSON object. */ @@ -6192,11 +6035,17 @@ inline constexpr bool enable_view namespace simdjson { @@ -6208,50 +6057,9 @@ namespace simdjson { */ namespace internal { -class mini_formatter; - -/** - * @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{}; -}; - -/** - * @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 { +template +class base_formatter { public: - mini_formatter() = default; /** Add a comma **/ simdjson_inline void comma(); /** Start an array, prints [ **/ @@ -6286,14 +6094,88 @@ class mini_formatter { **/ simdjson_inline std::string_view str() const; -private: - // implementation details (subject to change) /** Prints one character **/ simdjson_inline void one_char(char c); + + simdjson_inline void call_print_newline() { + this->print_newline(); + } + + simdjson_inline void call_print_indents(size_t depth) { + this->print_indents(depth); + } + + simdjson_inline void call_print_space() { + this->print_space(); + } + +protected: + // implementation details (subject to change) /** Backing buffer **/ std::vector buffer{}; // not ideal! }; + +/** + * @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. + */ +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 { @@ -6305,16 +6187,9 @@ namespace dom { * @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) { - simdjson::internal::string_builder<> sb; - sb.append(value); - return (out << sb.str()); -} +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) { - if (x.error()) { throw simdjson::simdjson_error(x.error()); } - return (out << x.value()); -} +inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result x); #endif /** * Print JSON to an output stream. @@ -6323,16 +6198,9 @@ inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result sb; - sb.append(value); - return (out << sb.str()); -} +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) { - if (x.error()) { throw simdjson::simdjson_error(x.error()); } - return (out << x.value()); -} +inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result x); #endif /** * Print JSON to an output stream. @@ -6341,16 +6209,9 @@ inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result sb; - sb.append(value); - return (out << sb.str()); -} +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) { - if (x.error()) { throw simdjson::simdjson_error(x.error()); } - return (out << x.value()); -} +inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result x); #endif } // namespace dom @@ -6401,676 +6262,383 @@ std::string minify(simdjson_result x) { } #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()); +} + +#if SIMDJSON_EXCEPTIONS +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 include/simdjson/dom/serialization.h */ +/* end file simdjson/dom/serialization.h */ // Deprecated API -/* begin file include/simdjson/dom/jsonparser.h */ +/* including simdjson/dom/jsonparser.h: #include "simdjson/dom/jsonparser.h" */ +/* begin file simdjson/dom/jsonparser.h */ // TODO Remove this -- deprecated API and files #ifndef SIMDJSON_DOM_JSONPARSER_H #define SIMDJSON_DOM_JSONPARSER_H -/* begin file include/simdjson/dom/parsedjson.h */ -// TODO Remove this -- deprecated API and files - -#ifndef SIMDJSON_DOM_PARSEDJSON_H -#define SIMDJSON_DOM_PARSEDJSON_H - +/* skipped duplicate #include "simdjson/dom/base.h" */ +/* skipped duplicate #include "simdjson/dom/parser.h" */ +/* skipped duplicate #include "simdjson/dom/element.h" */ + +/* including simdjson/dom/parser-inl.h: #include "simdjson/dom/parser-inl.h" */ +/* begin file simdjson/dom/parser-inl.h */ +#ifndef SIMDJSON_PARSER_INL_H +#define SIMDJSON_PARSER_INL_H + +/* skipped duplicate #include "simdjson/dom/base.h" */ +/* skipped duplicate #include "simdjson/dom/document_stream.h" */ +/* skipped duplicate #include "simdjson/implementation.h" */ +/* skipped duplicate #include "simdjson/internal/dom_parser_implementation.h" */ + +/* skipped duplicate #include "simdjson/error-inl.h" */ +/* skipped duplicate #include "simdjson/padded_string-inl.h" */ +/* including simdjson/dom/document_stream-inl.h: #include "simdjson/dom/document_stream-inl.h" */ +/* begin file simdjson/dom/document_stream-inl.h */ +#ifndef SIMDJSON_DOCUMENT_STREAM_INL_H +#define SIMDJSON_DOCUMENT_STREAM_INL_H + +/* skipped duplicate #include "simdjson/dom/base.h" */ +/* skipped duplicate #include "simdjson/dom/document_stream.h" */ +/* including simdjson/dom/element-inl.h: #include "simdjson/dom/element-inl.h" */ +/* begin file simdjson/dom/element-inl.h */ +#ifndef SIMDJSON_ELEMENT_INL_H +#define SIMDJSON_ELEMENT_INL_H + +/* skipped duplicate #include "simdjson/dom/base.h" */ +/* skipped duplicate #include "simdjson/dom/element.h" */ +/* skipped duplicate #include "simdjson/dom/document.h" */ +/* skipped duplicate #include "simdjson/dom/object.h" */ +/* including simdjson/internal/tape_type.h: #include "simdjson/internal/tape_type.h" */ +/* begin file simdjson/internal/tape_type.h */ +#ifndef SIMDJSON_INTERNAL_TAPE_TYPE_H +#define SIMDJSON_INTERNAL_TAPE_TYPE_H namespace simdjson { +namespace internal { /** - * @deprecated Use `dom::parser` instead. + * The possible types in the tape. */ -using ParsedJson [[deprecated("Use dom::parser instead")]] = dom::parser; +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 simdjson -#endif // SIMDJSON_DOM_PARSEDJSON_H -/* end file include/simdjson/dom/parsedjson.h */ -/* begin file include/simdjson/jsonioutil.h */ -#ifndef SIMDJSON_JSONIOUTIL_H -#define SIMDJSON_JSONIOUTIL_H - +#endif // SIMDJSON_INTERNAL_TAPE_TYPE_H +/* end file simdjson/internal/tape_type.h */ -namespace simdjson { +/* including simdjson/dom/object-inl.h: #include "simdjson/dom/object-inl.h" */ +/* begin file simdjson/dom/object-inl.h */ +#ifndef SIMDJSON_OBJECT_INL_H +#define SIMDJSON_OBJECT_INL_H -#if SIMDJSON_EXCEPTIONS -#ifndef SIMDJSON_DISABLE_DEPRECATED_API -[[deprecated("Use padded_string::load() instead")]] -inline padded_string get_corpus(const char *path) { - return padded_string::load(path); -} -#endif // SIMDJSON_DISABLE_DEPRECATED_API -#endif // SIMDJSON_EXCEPTIONS +/* skipped duplicate #include "simdjson/dom/base.h" */ +/* skipped duplicate #include "simdjson/dom/object.h" */ +/* skipped duplicate #include "simdjson/dom/document.h" */ -} // namespace simdjson +/* skipped duplicate #include "simdjson/dom/element-inl.h" */ +/* skipped duplicate #include "simdjson/error-inl.h" */ -#endif // SIMDJSON_JSONIOUTIL_H -/* end file include/simdjson/jsonioutil.h */ +#include namespace simdjson { // -// C API (json_parse and build_parsed_json) declarations +// 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) {} -#ifndef SIMDJSON_DISABLE_DEPRECATED_API -[[deprecated("Use parser.parse() instead")]] -inline int json_parse(const uint8_t *buf, size_t len, dom::parser &parser, bool realloc_if_needed = true) noexcept { - error_code code = parser.parse(buf, len, realloc_if_needed).error(); - // The deprecated json_parse API is a signal that the user plans to *use* the error code / valid - // bits in the parser instead of heeding the result code. The normal parser unsets those in - // anticipation of making the error code ephemeral. - // Here we put the code back into the parser, until we've removed this method. - parser.valid = code == SUCCESS; - parser.error = code; - return code; +inline simdjson_result simdjson_result::operator[](std::string_view key) const noexcept { + if (error()) { return error(); } + return first[key]; } -[[deprecated("Use parser.parse() instead")]] -inline int json_parse(const char *buf, size_t len, dom::parser &parser, bool realloc_if_needed = true) noexcept { - error_code code = parser.parse(buf, len, realloc_if_needed).error(); - // The deprecated json_parse API is a signal that the user plans to *use* the error code / valid - // bits in the parser instead of heeding the result code. The normal parser unsets those in - // anticipation of making the error code ephemeral. - // Here we put the code back into the parser, until we've removed this method. - parser.valid = code == SUCCESS; - parser.error = code; - return code; +inline simdjson_result simdjson_result::operator[](const char *key) const noexcept { + if (error()) { return error(); } + return first[key]; } -[[deprecated("Use parser.parse() instead")]] -inline int json_parse(const std::string &s, dom::parser &parser, bool realloc_if_needed = true) noexcept { - error_code code = parser.parse(s.data(), s.length(), realloc_if_needed).error(); - // The deprecated json_parse API is a signal that the user plans to *use* the error code / valid - // bits in the parser instead of heeding the result code. The normal parser unsets those in - // anticipation of making the error code ephemeral. - // Here we put the code back into the parser, until we've removed this method. - parser.valid = code == SUCCESS; - parser.error = code; - return code; +inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) const noexcept { + if (error()) { return error(); } + return first.at_pointer(json_pointer); } -[[deprecated("Use parser.parse() instead")]] -inline int json_parse(const padded_string &s, dom::parser &parser) noexcept { - error_code code = parser.parse(s).error(); - // The deprecated json_parse API is a signal that the user plans to *use* the error code / valid - // bits in the parser instead of heeding the result code. The normal parser unsets those in - // anticipation of making the error code ephemeral. - // Here we put the code back into the parser, until we've removed this method. - parser.valid = code == SUCCESS; - parser.error = code; - return code; +inline simdjson_result simdjson_result::at_key(std::string_view key) const noexcept { + if (error()) { return error(); } + return first.at_key(key); } - -[[deprecated("Use parser.parse() instead")]] -simdjson_warn_unused inline dom::parser build_parsed_json(const uint8_t *buf, size_t len, bool realloc_if_needed = true) noexcept { - dom::parser parser; - error_code code = parser.parse(buf, len, realloc_if_needed).error(); - // The deprecated json_parse API is a signal that the user plans to *use* the error code / valid - // bits in the parser instead of heeding the result code. The normal parser unsets those in - // anticipation of making the error code ephemeral. - // Here we put the code back into the parser, until we've removed this method. - parser.valid = code == SUCCESS; - parser.error = code; - return parser; +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); } -[[deprecated("Use parser.parse() instead")]] -simdjson_warn_unused inline dom::parser build_parsed_json(const char *buf, size_t len, bool realloc_if_needed = true) noexcept { - dom::parser parser; - error_code code = parser.parse(buf, len, realloc_if_needed).error(); - // The deprecated json_parse API is a signal that the user plans to *use* the error code / valid - // bits in the parser instead of heeding the result code. The normal parser unsets those in - // anticipation of making the error code ephemeral. - // Here we put the code back into the parser, until we've removed this method. - parser.valid = code == SUCCESS; - parser.error = code; - return parser; + +#if SIMDJSON_EXCEPTIONS + +inline dom::object::iterator simdjson_result::begin() const noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first.begin(); } -[[deprecated("Use parser.parse() instead")]] -simdjson_warn_unused inline dom::parser build_parsed_json(const std::string &s, bool realloc_if_needed = true) noexcept { - dom::parser parser; - error_code code = parser.parse(s.data(), s.length(), realloc_if_needed).error(); - // The deprecated json_parse API is a signal that the user plans to *use* the error code / valid - // bits in the parser instead of heeding the result code. The normal parser unsets those in - // anticipation of making the error code ephemeral. - // Here we put the code back into the parser, until we've removed this method. - parser.valid = code == SUCCESS; - parser.error = code; - return parser; +inline dom::object::iterator simdjson_result::end() const noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first.end(); } -[[deprecated("Use parser.parse() instead")]] -simdjson_warn_unused inline dom::parser build_parsed_json(const padded_string &s) noexcept { - dom::parser parser; - error_code code = parser.parse(s).error(); - // The deprecated json_parse API is a signal that the user plans to *use* the error code / valid - // bits in the parser instead of heeding the result code. The normal parser unsets those in - // anticipation of making the error code ephemeral. - // Here we put the code back into the parser, until we've removed this method. - parser.valid = code == SUCCESS; - parser.error = code; - return parser; +inline size_t simdjson_result::size() const noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first.size(); } -#endif // SIMDJSON_DISABLE_DEPRECATED_API - -/** @private We do not want to allow implicit conversion from C string to std::string. */ -int json_parse(const char *buf, dom::parser &parser) noexcept = delete; -/** @private We do not want to allow implicit conversion from C string to std::string. */ -dom::parser build_parsed_json(const char *buf) noexcept = delete; - -} // namespace simdjson - -#endif // SIMDJSON_DOM_JSONPARSER_H -/* end file include/simdjson/dom/jsonparser.h */ -/* begin file include/simdjson/dom/parsedjson_iterator.h */ -// TODO Remove this -- deprecated API and files - -#ifndef SIMDJSON_DOM_PARSEDJSON_ITERATOR_H -#define SIMDJSON_DOM_PARSEDJSON_ITERATOR_H - -#include -#include -#include -#include -#include -#include - -/* begin file include/simdjson/internal/jsonformatutils.h */ -#ifndef SIMDJSON_INTERNAL_JSONFORMATUTILS_H -#define SIMDJSON_INTERNAL_JSONFORMATUTILS_H - -#include -#include -#include - -namespace simdjson { -namespace internal { -class escape_json_string; - -inline std::ostream& operator<<(std::ostream& out, const escape_json_string &str); +#endif // SIMDJSON_EXCEPTIONS -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); -}; +namespace dom { -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; +// +// 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(); } -} // namespace internal -} // namespace simdjson - -#endif // SIMDJSON_INTERNAL_JSONFORMATUTILS_H -/* end file include/simdjson/internal/jsonformatutils.h */ - -#ifndef SIMDJSON_DISABLE_DEPRECATED_API - -namespace simdjson { -/** @private **/ -class [[deprecated("Use the new DOM navigation API instead (see doc/basics.md)")]] dom::parser::Iterator { -public: - inline Iterator(const dom::parser &parser) noexcept(false); - inline Iterator(const Iterator &o) noexcept; - inline ~Iterator() noexcept; - - inline Iterator& operator=(const Iterator&) = delete; - - inline bool is_ok() const; - - // useful for debugging purposes - inline size_t get_tape_location() const; - - // useful for debugging purposes - inline size_t get_tape_length() const; - - // returns the current depth (start at 1 with 0 reserved for the fictitious - // root node) - inline size_t get_depth() const; - - // A scope is a series of nodes at the same depth, typically it is either an - // object ({) or an array ([). The root node has type 'r'. - inline uint8_t get_scope_type() const; - - // move forward in document order - inline bool move_forward(); - - // retrieve the character code of what we're looking at: - // [{"slutfn are the possibilities - inline uint8_t get_type() const { - return current_type; // short functions should be inlined! - } - - // get the int64_t value at this node; valid only if get_type is "l" - inline int64_t get_integer() const { - if (location + 1 >= tape_length) { - return 0; // default value in case of error - } - return static_cast(doc.tape[location + 1]); +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; - // get the value as uint64; valid only if if get_type is "u" - inline uint64_t get_unsigned_integer() const { - if (location + 1 >= tape_length) { - return 0; // default value in case of error + // 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"); } - return doc.tape[location + 1]; + escape = unescaped.find('~', escape+1); + } while (escape != std::string::npos); + child = at_key(unescaped); + } else { + child = at_key(key); } - - // get the string value at this node (NULL ended); valid only if get_type is " - // note that tabs, and line endings are escaped in the returned value (see - // print_with_escapes) return value is valid UTF-8, it may contain NULL chars - // within the string: get_string_length determines the true string length. - inline const char *get_string() const { - return reinterpret_cast( - doc.string_buf.get() + (current_val & internal::JSON_VALUE_MASK) + sizeof(uint32_t)); + if(child.error()) { + return child; // we do not continue if there was an error } - - // return the length of the string in bytes - inline uint32_t get_string_length() const { - uint32_t answer; - std::memcpy(&answer, - reinterpret_cast(doc.string_buf.get() + - (current_val & internal::JSON_VALUE_MASK)), - sizeof(uint32_t)); - return answer; + // 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; +} - // get the double value at this node; valid only if - // get_type() is "d" - inline double get_double() const { - if (location + 1 >= tape_length) { - return std::numeric_limits::quiet_NaN(); // default value in - // case of error - } - double answer; - std::memcpy(&answer, &doc.tape[location + 1], sizeof(answer)); - return answer; - } - - inline bool is_object_or_array() const { return is_object() || is_array(); } - - inline bool is_object() const { return get_type() == '{'; } - - inline bool is_array() const { return get_type() == '['; } - - inline bool is_string() const { return get_type() == '"'; } - - // Returns true if the current type of the node is an signed integer. - // You can get its value with `get_integer()`. - inline bool is_integer() const { return get_type() == 'l'; } - - // Returns true if the current type of the node is an unsigned integer. - // You can get its value with `get_unsigned_integer()`. - // - // NOTE: - // Only a large value, which is out of range of a 64-bit signed integer, is - // represented internally as an unsigned node. On the other hand, a typical - // positive integer, such as 1, 42, or 1000000, is as a signed node. - // Be aware this function returns false for a signed node. - inline bool is_unsigned_integer() const { return get_type() == 'u'; } - // Returns true if the current type of the node is a double floating-point number. - inline bool is_double() const { return get_type() == 'd'; } - // Returns true if the current type of the node is a number (integer or floating-point). - inline bool is_number() const { - return is_integer() || is_unsigned_integer() || is_double(); - } - // Returns true if the current type of the node is a bool with true value. - inline bool is_true() const { return get_type() == 't'; } - // Returns true if the current type of the node is a bool with false value. - inline bool is_false() const { return get_type() == 'f'; } - // Returns true if the current type of the node is null. - inline bool is_null() const { return get_type() == 'n'; } - // Returns true if the type byte represents an object of an array - static bool is_object_or_array(uint8_t type) { - return ((type == '[') || (type == '{')); - } - - // when at {, go one level deep, looking for a given key - // if successful, we are left pointing at the value, - // if not, we are still pointing at the object ({) - // (in case of repeated keys, this only finds the first one). - // We seek the key using C's strcmp so if your JSON strings contain - // NULL chars, this would trigger a false positive: if you expect that - // to be the case, take extra precautions. - // Furthermore, we do the comparison character-by-character - // without taking into account Unicode equivalence. - inline bool move_to_key(const char *key); - - // as above, but case insensitive lookup (strcmpi instead of strcmp) - inline bool move_to_key_insensitive(const char *key); - - // when at {, go one level deep, looking for a given key - // if successful, we are left pointing at the value, - // if not, we are still pointing at the object ({) - // (in case of repeated keys, this only finds the first one). - // The string we search for can contain NULL values. - // Furthermore, we do the comparison character-by-character - // without taking into account Unicode equivalence. - inline bool move_to_key(const char *key, uint32_t length); - - // when at a key location within an object, this moves to the accompanying - // value (located next to it). This is equivalent but much faster than - // calling "next()". - inline void move_to_value(); - - // when at [, go one level deep, and advance to the given index. - // if successful, we are left pointing at the value, - // if not, we are still pointing at the array ([) - inline bool move_to_index(uint32_t index); - - // Moves the iterator to the value corresponding to the json pointer. - // Always search from the root of the document. - // if successful, we are left pointing at the value, - // if not, we are still pointing the same value we were pointing before the - // call. The json pointer follows the rfc6901 standard's syntax: - // https://tools.ietf.org/html/rfc6901 However, the standard says "If a - // referenced member name is not unique in an object, the member that is - // referenced is undefined, and evaluation fails". Here we just return the - // first corresponding value. The length parameter is the length of the - // jsonpointer string ('pointer'). - inline bool move_to(const char *pointer, uint32_t length); - - // Moves the iterator to the value corresponding to the json pointer. - // Always search from the root of the document. - // if successful, we are left pointing at the value, - // if not, we are still pointing the same value we were pointing before the - // call. The json pointer implementation follows the rfc6901 standard's - // syntax: https://tools.ietf.org/html/rfc6901 However, the standard says - // "If a referenced member name is not unique in an object, the member that - // is referenced is undefined, and evaluation fails". Here we just return - // the first corresponding value. - inline bool move_to(const std::string &pointer) { - return move_to(pointer.c_str(), uint32_t(pointer.length())); - } - - private: - // Almost the same as move_to(), except it searches from the current - // position. The pointer's syntax is identical, though that case is not - // handled by the rfc6901 standard. The '/' is still required at the - // beginning. However, contrary to move_to(), the URI Fragment Identifier - // Representation is not supported here. Also, in case of failure, we are - // left pointing at the closest value it could reach. For these reasons it - // is private. It exists because it is used by move_to(). - inline bool relative_move_to(const char *pointer, uint32_t length); - - public: - // throughout return true if we can do the navigation, false - // otherwise - - // Within a given scope (series of nodes at the same depth within either an - // array or an object), we move forward. - // Thus, given [true, null, {"a":1}, [1,2]], we would visit true, null, { - // and [. At the object ({) or at the array ([), you can issue a "down" to - // visit their content. valid if we're not at the end of a scope (returns - // true). - inline bool next(); - - // Within a given scope (series of nodes at the same depth within either an - // array or an object), we move backward. - // Thus, given [true, null, {"a":1}, [1,2]], we would visit ], }, null, true - // when starting at the end of the scope. At the object ({) or at the array - // ([), you can issue a "down" to visit their content. - // Performance warning: This function is implemented by starting again - // from the beginning of the scope and scanning forward. You should expect - // it to be relatively slow. - inline bool prev(); - - // Moves back to either the containing array or object (type { or [) from - // within a contained scope. - // Valid unless we are at the first level of the document - inline bool up(); - - // Valid if we're at a [ or { and it starts a non-empty scope; moves us to - // start of that deeper scope if it not empty. Thus, given [true, null, - // {"a":1}, [1,2]], if we are at the { node, we would move to the "a" node. - inline bool down(); - - // move us to the start of our current scope, - // a scope is a series of nodes at the same level - inline void to_start_scope(); - - inline void rewind() { - while (up()) - ; +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(); + } } - - - - // print the node we are currently pointing at - inline bool print(std::ostream &os, bool escape_strings = true) const; - - private: - const document &doc; - size_t max_depth{}; - size_t depth{}; - size_t location{}; // our current location on a tape - size_t tape_length{}; - uint8_t current_type{}; - uint64_t current_val{}; - typedef struct { - size_t start_of_scope; - uint8_t scope_type; - } scopeindex_t; - - scopeindex_t *depth_index{}; -}; - -} // namespace simdjson -#endif // SIMDJSON_DISABLE_DEPRECATED_API - -#endif // SIMDJSON_DOM_PARSEDJSON_ITERATOR_H -/* end file include/simdjson/dom/parsedjson_iterator.h */ - -// Inline functions -/* begin file include/simdjson/dom/array-inl.h */ -#ifndef SIMDJSON_INLINE_ARRAY_H -#define SIMDJSON_INLINE_ARRAY_H - -// Inline implementations go in here. - -#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::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(); -} - -#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); + return NO_SUCH_FIELD; } -inline simdjson_result simdjson_result::at(size_t index) const noexcept { - if (error()) { return error(); } - return first.at(index); +// 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; } -namespace dom { - // -// array inline implementation +// object::iterator 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); +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 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 bool object::iterator::operator!=(const object::iterator& other) const noexcept { + return tape.json_index != other.tape.json_index; } -inline size_t array::size() const noexcept { - SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914 - return tape.scope_count(); +inline bool object::iterator::operator==(const object::iterator& other) const noexcept { + return tape.json_index == other.tape.json_index; } -inline size_t array::number_of_slots() const noexcept { - SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914 - return tape.matching_brace_index() - tape.json_index; +inline bool object::iterator::operator<(const object::iterator& other) const noexcept { + return tape.json_index < other.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 bool object::iterator::operator<=(const object::iterator& other) const noexcept { + return tape.json_index <= other.tape.json_index; } - -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 bool object::iterator::operator>=(const object::iterator& other) const noexcept { + return tape.json_index >= other.tape.json_index; } - -// -// 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 bool object::iterator::operator>(const object::iterator& other) const noexcept { + return tape.json_index > other.tape.json_index; } -inline array::iterator& array::iterator::operator++() noexcept { +inline object::iterator& object::iterator::operator++() noexcept { + tape.json_index++; tape.json_index = tape.after_element(); return *this; } -inline array::iterator array::iterator::operator++(int) noexcept { - array::iterator out = *this; +inline object::iterator object::iterator::operator++(int) noexcept { + object::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 std::string_view object::iterator::key() const noexcept { + return tape.get_string_view(); } -inline bool array::iterator::operator==(const array::iterator& other) const noexcept { - return tape.json_index == other.tape.json_index; +inline uint32_t object::iterator::key_length() const noexcept { + return tape.get_string_length(); } -inline bool array::iterator::operator<(const array::iterator& other) const noexcept { - return tape.json_index < other.tape.json_index; +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 bool array::iterator::operator<=(const array::iterator& other) const noexcept { - return tape.json_index <= other.tape.json_index; +inline element object::iterator::value() const noexcept { + return element(internal::tape_ref(tape.doc, tape.json_index + 1)); } -inline bool array::iterator::operator>=(const array::iterator& other) const noexcept { - return tape.json_index >= other.tape.json_index; + +/** + * 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 array::iterator::operator>(const array::iterator& other) const noexcept { - return tape.json_index > other.tape.json_index; + +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 -/* begin file include/simdjson/dom/element-inl.h */ -#ifndef SIMDJSON_INLINE_ELEMENT_H -#define SIMDJSON_INLINE_ELEMENT_H +#if defined(__cpp_lib_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) -#include -#include +#endif // SIMDJSON_OBJECT_INL_H +/* end file simdjson/dom/object-inl.h */ +/* skipped duplicate #include "simdjson/error-inl.h" */ + +#include +#include namespace simdjson { @@ -7503,31 +7071,17 @@ inline std::ostream& operator<<(std::ostream& out, element_type type) { } // namespace simdjson -#endif // SIMDJSON_INLINE_ELEMENT_H -/* end file include/simdjson/dom/element-inl.h */ +#endif // SIMDJSON_ELEMENT_INL_H +/* end file simdjson/dom/element-inl.h */ +/* skipped duplicate #include "simdjson/dom/parser-inl.h" */ +/* skipped duplicate #include "simdjson/error-inl.h" */ +/* skipped duplicate #include "simdjson/internal/dom_parser_implementation.h" */ -#if defined(__cpp_lib_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_INLINE_ARRAY_H -/* end file include/simdjson/dom/array-inl.h */ -/* begin file include/simdjson/dom/document_stream-inl.h */ -#ifndef SIMDJSON_INLINE_DOCUMENT_STREAM_H -#define SIMDJSON_INLINE_DOCUMENT_STREAM_H - -#include -#include -#include namespace simdjson { namespace dom { #ifdef SIMDJSON_THREADS_ENABLED + inline void stage1_worker::finish() { // After calling "run" someone would call finish() to wait // for the end of the processing. @@ -7856,1153 +7410,637 @@ simdjson_inline dom::document_stream::iterator simdjson_result -#include +#include namespace simdjson { namespace dom { // -// document inline implementation +// parser inline implementation // -inline element document::root() const noexcept { - return element(internal::tape_ref(this, 1)); +simdjson_inline parser::parser(size_t max_capacity) noexcept + : _max_capacity{max_capacity}, + loaded_bytes(nullptr) { } -simdjson_warn_unused -inline size_t document::capacity() const noexcept { - return allocated_capacity; +simdjson_inline parser::parser(parser &&other) noexcept = default; +simdjson_inline parser &parser::operator=(parser &&other) noexcept = default; + +inline bool parser::is_valid() const noexcept { return valid; } +inline int parser::get_error_code() const noexcept { return error; } +inline std::string parser::get_error_message() const noexcept { return error_message(error); } + +inline bool parser::dump_raw_tape(std::ostream &os) const noexcept { + return valid ? doc.dump_raw_tape(os) : false; } -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; - } +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 - // 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; + if (fp == nullptr) { + return IO_ERROR; } - // 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; + // 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; } - 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; - } +#if SIMDJSON_VISUAL_STUDIO && !SIMDJSON_IS_32BITS + __int64 len = _ftelli64(fp); + if(len == -1L) { + std::fclose(fp); + return IO_ERROR; } - tape_val = tape[tape_idx]; - payload = tape_val & internal::JSON_VALUE_MASK; - type = uint8_t(tape_val >> 56); - os << tape_idx << " : " << type << "\t// pointing to " << payload - << " (start root)\n"; - return true; -} - -} // namespace dom -} // namespace simdjson - -#endif // SIMDJSON_INLINE_DOCUMENT_H -/* end file include/simdjson/dom/document-inl.h */ -/* begin file include/simdjson/dom/object-inl.h */ -#ifndef SIMDJSON_INLINE_OBJECT_H -#define SIMDJSON_INLINE_OBJECT_H - -#include -#include +#else + long len = std::ftell(fp); + if((len < 0) || (len == LONG_MAX)) { + std::fclose(fp); + return IO_ERROR; + } +#endif -namespace simdjson { + // 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; + } -// -// 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) {} + // 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; + } -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); + return bytes_read; } -#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 simdjson_result parser::load(const std::string &path) & noexcept { + size_t len; + auto _error = read_file(path).get(len); + if (_error) { return _error; } + return parse(loaded_bytes.get(), len, false); } -inline size_t simdjson_result::size() const noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first.size(); + +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); } -#endif // SIMDJSON_EXCEPTIONS +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); + } + _error = implementation->parse(realloc_if_needed ? reinterpret_cast(loaded_bytes.get()): buf, len, provided_doc); -namespace dom { + if (_error) { return _error; } -// -// 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(); + return provided_doc.root(); } -inline simdjson_result object::operator[](std::string_view key) const noexcept { - return at_key(key); +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); } -inline simdjson_result object::operator[](const char *key) const noexcept { - return at_key(key); +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); } -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; +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 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 parser::parse(const uint8_t *buf, size_t len, bool realloc_if_needed) & noexcept { + return parse_into_document(doc, buf, len, realloc_if_needed); } -// -// 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()); +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); } -inline bool object::iterator::operator!=(const object::iterator& other) const noexcept { - return tape.json_index != other.tape.json_index; +simdjson_inline simdjson_result parser::parse(const std::string &s) & noexcept { + return parse(s.data(), s.length(), s.capacity() - s.length() < SIMDJSON_PADDING); } -inline bool object::iterator::operator==(const object::iterator& other) const noexcept { - return tape.json_index == other.tape.json_index; +simdjson_inline simdjson_result parser::parse(const padded_string &s) & noexcept { + return parse(s.data(), s.length(), false); } -inline bool object::iterator::operator<(const object::iterator& other) const noexcept { - return tape.json_index < other.tape.json_index; +simdjson_inline simdjson_result parser::parse(const padded_string_view &v) & noexcept { + return parse(v.data(), v.length(), false); } -inline bool object::iterator::operator<=(const object::iterator& other) const noexcept { - return tape.json_index <= other.tape.json_index; + +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; } + return document_stream(*this, buf, len, batch_size); } -inline bool object::iterator::operator>=(const object::iterator& other) const noexcept { - return tape.json_index >= other.tape.json_index; +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 bool object::iterator::operator>(const object::iterator& other) const noexcept { - return tape.json_index > other.tape.json_index; +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 object::iterator& object::iterator::operator++() noexcept { - tape.json_index++; - tape.json_index = tape.after_element(); - return *this; +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 object::iterator object::iterator::operator++(int) noexcept { - object::iterator out = *this; - ++*this; - return out; + +simdjson_inline size_t parser::capacity() const noexcept { + return implementation ? implementation->capacity() : 0; } -inline std::string_view object::iterator::key() const noexcept { - return tape.get_string_view(); +simdjson_inline size_t parser::max_capacity() const noexcept { + return _max_capacity; } -inline uint32_t object::iterator::key_length() const noexcept { - return tape.get_string_length(); +simdjson_inline size_t parser::max_depth() const noexcept { + return implementation ? implementation->max_depth() : DEFAULT_MAX_DEPTH; } -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)]); + +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; } -inline element object::iterator::value() const noexcept { - return element(internal::tape_ref(tape.doc, tape.json_index + 1)); + +#ifndef SIMDJSON_DISABLE_DEPRECATED_API +simdjson_warn_unused +inline bool parser::allocate_capacity(size_t capacity, size_t max_depth) noexcept { + return !allocate(capacity, max_depth); } +#endif // SIMDJSON_DISABLE_DEPRECATED_API -/** - * 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 error_code parser::ensure_capacity(size_t desired_capacity) noexcept { + return ensure_capacity(doc, desired_capacity); +} -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); + +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 false; + return SUCCESS; } -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); +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; } - 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) -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_INLINE_OBJECT_H -/* end file include/simdjson/dom/object-inl.h */ -/* begin file include/simdjson/dom/parsedjson_iterator-inl.h */ -#ifndef SIMDJSON_INLINE_PARSEDJSON_ITERATOR_H -#define SIMDJSON_INLINE_PARSEDJSON_ITERATOR_H - -#include - -#ifndef SIMDJSON_DISABLE_DEPRECATED_API +#endif // SIMDJSON_PARSER_INL_H +/* end file simdjson/dom/parser-inl.h */ namespace simdjson { -// VS2017 reports deprecated warnings when you define a deprecated class's methods. -SIMDJSON_PUSH_DISABLE_WARNINGS -SIMDJSON_DISABLE_DEPRECATED_WARNING +// +// C API (json_parse and build_parsed_json) declarations +// -// Because of template weirdness, the actual class definition is inline in the document class -simdjson_warn_unused bool dom::parser::Iterator::is_ok() const { - return location < tape_length; +#ifndef SIMDJSON_DISABLE_DEPRECATED_API +[[deprecated("Use parser.parse() instead")]] +inline int json_parse(const uint8_t *buf, size_t len, dom::parser &parser, bool realloc_if_needed = true) noexcept { + error_code code = parser.parse(buf, len, realloc_if_needed).error(); + // The deprecated json_parse API is a signal that the user plans to *use* the error code / valid + // bits in the parser instead of heeding the result code. The normal parser unsets those in + // anticipation of making the error code ephemeral. + // Here we put the code back into the parser, until we've removed this method. + parser.valid = code == SUCCESS; + parser.error = code; + return code; } - -// useful for debugging purposes -size_t dom::parser::Iterator::get_tape_location() const { - return location; +[[deprecated("Use parser.parse() instead")]] +inline int json_parse(const char *buf, size_t len, dom::parser &parser, bool realloc_if_needed = true) noexcept { + error_code code = parser.parse(buf, len, realloc_if_needed).error(); + // The deprecated json_parse API is a signal that the user plans to *use* the error code / valid + // bits in the parser instead of heeding the result code. The normal parser unsets those in + // anticipation of making the error code ephemeral. + // Here we put the code back into the parser, until we've removed this method. + parser.valid = code == SUCCESS; + parser.error = code; + return code; } - -// useful for debugging purposes -size_t dom::parser::Iterator::get_tape_length() const { - return tape_length; +[[deprecated("Use parser.parse() instead")]] +inline int json_parse(const std::string &s, dom::parser &parser, bool realloc_if_needed = true) noexcept { + error_code code = parser.parse(s.data(), s.length(), realloc_if_needed).error(); + // The deprecated json_parse API is a signal that the user plans to *use* the error code / valid + // bits in the parser instead of heeding the result code. The normal parser unsets those in + // anticipation of making the error code ephemeral. + // Here we put the code back into the parser, until we've removed this method. + parser.valid = code == SUCCESS; + parser.error = code; + return code; } - -// returns the current depth (start at 1 with 0 reserved for the fictitious root -// node) -size_t dom::parser::Iterator::get_depth() const { - return depth; +[[deprecated("Use parser.parse() instead")]] +inline int json_parse(const padded_string &s, dom::parser &parser) noexcept { + error_code code = parser.parse(s).error(); + // The deprecated json_parse API is a signal that the user plans to *use* the error code / valid + // bits in the parser instead of heeding the result code. The normal parser unsets those in + // anticipation of making the error code ephemeral. + // Here we put the code back into the parser, until we've removed this method. + parser.valid = code == SUCCESS; + parser.error = code; + return code; } -// A scope is a series of nodes at the same depth, typically it is either an -// object ({) or an array ([). The root node has type 'r'. -uint8_t dom::parser::Iterator::get_scope_type() const { - return depth_index[depth].scope_type; +[[deprecated("Use parser.parse() instead")]] +simdjson_warn_unused inline dom::parser build_parsed_json(const uint8_t *buf, size_t len, bool realloc_if_needed = true) noexcept { + dom::parser parser; + error_code code = parser.parse(buf, len, realloc_if_needed).error(); + // The deprecated json_parse API is a signal that the user plans to *use* the error code / valid + // bits in the parser instead of heeding the result code. The normal parser unsets those in + // anticipation of making the error code ephemeral. + // Here we put the code back into the parser, until we've removed this method. + parser.valid = code == SUCCESS; + parser.error = code; + return parser; +} +[[deprecated("Use parser.parse() instead")]] +simdjson_warn_unused inline dom::parser build_parsed_json(const char *buf, size_t len, bool realloc_if_needed = true) noexcept { + dom::parser parser; + error_code code = parser.parse(buf, len, realloc_if_needed).error(); + // The deprecated json_parse API is a signal that the user plans to *use* the error code / valid + // bits in the parser instead of heeding the result code. The normal parser unsets those in + // anticipation of making the error code ephemeral. + // Here we put the code back into the parser, until we've removed this method. + parser.valid = code == SUCCESS; + parser.error = code; + return parser; +} +[[deprecated("Use parser.parse() instead")]] +simdjson_warn_unused inline dom::parser build_parsed_json(const std::string &s, bool realloc_if_needed = true) noexcept { + dom::parser parser; + error_code code = parser.parse(s.data(), s.length(), realloc_if_needed).error(); + // The deprecated json_parse API is a signal that the user plans to *use* the error code / valid + // bits in the parser instead of heeding the result code. The normal parser unsets those in + // anticipation of making the error code ephemeral. + // Here we put the code back into the parser, until we've removed this method. + parser.valid = code == SUCCESS; + parser.error = code; + return parser; +} +[[deprecated("Use parser.parse() instead")]] +simdjson_warn_unused inline dom::parser build_parsed_json(const padded_string &s) noexcept { + dom::parser parser; + error_code code = parser.parse(s).error(); + // The deprecated json_parse API is a signal that the user plans to *use* the error code / valid + // bits in the parser instead of heeding the result code. The normal parser unsets those in + // anticipation of making the error code ephemeral. + // Here we put the code back into the parser, until we've removed this method. + parser.valid = code == SUCCESS; + parser.error = code; + return parser; } +#endif // SIMDJSON_DISABLE_DEPRECATED_API -bool dom::parser::Iterator::move_forward() { - if (location + 1 >= tape_length) { - return false; // we are at the end! - } +/** @private We do not want to allow implicit conversion from C string to std::string. */ +int json_parse(const char *buf, dom::parser &parser) noexcept = delete; +/** @private We do not want to allow implicit conversion from C string to std::string. */ +dom::parser build_parsed_json(const char *buf) noexcept = delete; - if ((current_type == '[') || (current_type == '{')) { - // We are entering a new scope - depth++; - assert(depth < max_depth); - depth_index[depth].start_of_scope = location; - depth_index[depth].scope_type = current_type; - } else if ((current_type == ']') || (current_type == '}')) { - // Leaving a scope. - depth--; - } else if (is_number()) { - // these types use 2 locations on the tape, not just one. - location += 1; - } +} // namespace simdjson - location += 1; - current_val = doc.tape[location]; - current_type = uint8_t(current_val >> 56); - return true; -} +#endif // SIMDJSON_DOM_JSONPARSER_H +/* end file simdjson/dom/jsonparser.h */ +/* including simdjson/dom/parsedjson.h: #include "simdjson/dom/parsedjson.h" */ +/* begin file simdjson/dom/parsedjson.h */ +// TODO Remove this -- deprecated API and files -void dom::parser::Iterator::move_to_value() { - // assume that we are on a key, so move by 1. - location += 1; - current_val = doc.tape[location]; - current_type = uint8_t(current_val >> 56); -} +#ifndef SIMDJSON_DOM_PARSEDJSON_H +#define SIMDJSON_DOM_PARSEDJSON_H -bool dom::parser::Iterator::move_to_key(const char *key) { - if (down()) { - do { - const bool right_key = (strcmp(get_string(), key) == 0); - move_to_value(); - if (right_key) { - return true; - } - } while (next()); - up(); - } - return false; -} +/* skipped duplicate #include "simdjson/dom/base.h" */ -bool dom::parser::Iterator::move_to_key_insensitive( - const char *key) { - if (down()) { - do { - const bool right_key = (simdjson_strcasecmp(get_string(), key) == 0); - move_to_value(); - if (right_key) { - return true; - } - } while (next()); - up(); - } - return false; -} +namespace simdjson { -bool dom::parser::Iterator::move_to_key(const char *key, - uint32_t length) { - if (down()) { - do { - bool right_key = ((get_string_length() == length) && - (memcmp(get_string(), key, length) == 0)); - move_to_value(); - if (right_key) { - return true; - } - } while (next()); - up(); - } - return false; -} +/** + * @deprecated Use `dom::parser` instead. + */ +using ParsedJson [[deprecated("Use dom::parser instead")]] = dom::parser; -bool dom::parser::Iterator::move_to_index(uint32_t index) { - if (down()) { - uint32_t i = 0; - for (; i < index; i++) { - if (!next()) { - break; - } - } - if (i == index) { - return true; - } - up(); - } - return false; -} +} // namespace simdjson -bool dom::parser::Iterator::prev() { - size_t target_location = location; - to_start_scope(); - size_t npos = location; - if (target_location == npos) { - return false; // we were already at the start - } - size_t oldnpos; - // we have that npos < target_location here - do { - oldnpos = npos; - if ((current_type == '[') || (current_type == '{')) { - // we need to jump - npos = uint32_t(current_val); - } else { - npos = npos + ((current_type == 'd' || current_type == 'l') ? 2 : 1); - } - } while (npos < target_location); - location = oldnpos; - current_val = doc.tape[location]; - current_type = uint8_t(current_val >> 56); - return true; -} +#endif // SIMDJSON_DOM_PARSEDJSON_H +/* end file simdjson/dom/parsedjson.h */ +/* including simdjson/dom/parsedjson_iterator.h: #include "simdjson/dom/parsedjson_iterator.h" */ +/* begin file simdjson/dom/parsedjson_iterator.h */ +// TODO Remove this -- deprecated API and files -bool dom::parser::Iterator::up() { - if (depth == 1) { - return false; // don't allow moving back to root - } - to_start_scope(); - // next we just move to the previous value - depth--; - location -= 1; - current_val = doc.tape[location]; - current_type = uint8_t(current_val >> 56); - return true; -} +#ifndef SIMDJSON_DOM_PARSEDJSON_ITERATOR_H +#define SIMDJSON_DOM_PARSEDJSON_ITERATOR_H -bool dom::parser::Iterator::down() { - if (location + 1 >= tape_length) { - return false; - } - if ((current_type == '[') || (current_type == '{')) { - size_t npos = uint32_t(current_val); - if (npos == location + 2) { - return false; // we have an empty scope - } - depth++; - assert(depth < max_depth); - location = location + 1; - depth_index[depth].start_of_scope = location; - depth_index[depth].scope_type = current_type; - current_val = doc.tape[location]; - current_type = uint8_t(current_val >> 56); - return true; - } - return false; -} +/* skipped duplicate #include "simdjson/dom/base.h" */ +/* skipped duplicate #include "simdjson/dom/parser.h" */ -void dom::parser::Iterator::to_start_scope() { - location = depth_index[depth].start_of_scope; - current_val = doc.tape[location]; - current_type = uint8_t(current_val >> 56); -} +#ifndef SIMDJSON_DISABLE_DEPRECATED_API -bool dom::parser::Iterator::next() { - size_t npos; - if ((current_type == '[') || (current_type == '{')) { - // we need to jump - npos = uint32_t(current_val); - } else { - npos = location + (is_number() ? 2 : 1); - } - uint64_t next_val = doc.tape[npos]; - uint8_t next_type = uint8_t(next_val >> 56); - if ((next_type == ']') || (next_type == '}')) { - return false; // we reached the end of the scope - } - location = npos; - current_val = next_val; - current_type = next_type; - return true; -} -dom::parser::Iterator::Iterator(const dom::parser &pj) noexcept(false) - : doc(pj.doc) -{ -#if SIMDJSON_EXCEPTIONS - if (!pj.valid) { throw simdjson_error(pj.error); } -#else - if (!pj.valid) { return; } // abort() usage is forbidden in the library -#endif +namespace simdjson { +/** @private **/ +class [[deprecated("Use the new DOM navigation API instead (see doc/basics.md)")]] dom::parser::Iterator { +public: + inline Iterator(const dom::parser &parser) noexcept(false); + inline Iterator(const Iterator &o) noexcept; + inline ~Iterator() noexcept; - max_depth = pj.max_depth(); - depth_index = new scopeindex_t[max_depth + 1]; - depth_index[0].start_of_scope = location; - current_val = doc.tape[location++]; - current_type = uint8_t(current_val >> 56); - depth_index[0].scope_type = current_type; - tape_length = size_t(current_val & internal::JSON_VALUE_MASK); - if (location < tape_length) { - // If we make it here, then depth_capacity must >=2, but the compiler - // may not know this. - current_val = doc.tape[location]; - current_type = uint8_t(current_val >> 56); - depth++; - assert(depth < max_depth); - depth_index[depth].start_of_scope = location; - depth_index[depth].scope_type = current_type; - } -} -dom::parser::Iterator::Iterator( - const dom::parser::Iterator &o) noexcept - : doc(o.doc), - max_depth(o.depth), - depth(o.depth), - location(o.location), - tape_length(o.tape_length), - current_type(o.current_type), - current_val(o.current_val) -{ - depth_index = new scopeindex_t[max_depth+1]; - std::memcpy(depth_index, o.depth_index, (depth + 1) * sizeof(depth_index[0])); -} + inline Iterator& operator=(const Iterator&) = delete; -dom::parser::Iterator::~Iterator() noexcept { - if (depth_index) { delete[] depth_index; } -} + inline bool is_ok() const; -bool dom::parser::Iterator::print(std::ostream &os, bool escape_strings) const { - if (!is_ok()) { - return false; - } - switch (current_type) { - case '"': // we have a string - os << '"'; - if (escape_strings) { - os << internal::escape_json_string(std::string_view(get_string(), get_string_length())); - } else { - // was: os << get_string();, but given that we can include null chars, we - // have to do something crazier: - std::copy(get_string(), get_string() + get_string_length(), std::ostream_iterator(os)); - } - os << '"'; - break; - case 'l': // we have a long int - os << get_integer(); - break; - case 'u': - os << get_unsigned_integer(); - break; - case 'd': - os << get_double(); - break; - case 'n': // we have a null - os << "null"; - break; - case 't': // we have a true - os << "true"; - break; - case 'f': // we have a false - os << "false"; - break; - case '{': // we have an object - case '}': // we end an object - case '[': // we start an array - case ']': // we end an array - os << char(current_type); - break; - default: - return false; - } - return true; -} + // useful for debugging purposes + inline size_t get_tape_location() const; -bool dom::parser::Iterator::move_to(const char *pointer, - uint32_t length) { - char *new_pointer = nullptr; - if (pointer[0] == '#') { - // Converting fragment representation to string representation - new_pointer = new char[length]; - uint32_t new_length = 0; - for (uint32_t i = 1; i < length; i++) { - if (pointer[i] == '%' && pointer[i + 1] == 'x') { -#if __cpp_exceptions - try { -#endif - int fragment = - std::stoi(std::string(&pointer[i + 2], 2), nullptr, 16); - if (fragment == '\\' || fragment == '"' || (fragment <= 0x1F)) { - // escaping the character - new_pointer[new_length] = '\\'; - new_length++; - } - new_pointer[new_length] = char(fragment); - i += 3; -#if __cpp_exceptions - } catch (std::invalid_argument &) { - delete[] new_pointer; - return false; // the fragment is invalid - } -#endif - } else { - new_pointer[new_length] = pointer[i]; - } - new_length++; - } - length = new_length; - pointer = new_pointer; - } + // useful for debugging purposes + inline size_t get_tape_length() const; - // saving the current state - size_t depth_s = depth; - size_t location_s = location; - uint8_t current_type_s = current_type; - uint64_t current_val_s = current_val; + // returns the current depth (start at 1 with 0 reserved for the fictitious + // root node) + inline size_t get_depth() const; - rewind(); // The json pointer is used from the root of the document. + // A scope is a series of nodes at the same depth, typically it is either an + // object ({) or an array ([). The root node has type 'r'. + inline uint8_t get_scope_type() const; - bool found = relative_move_to(pointer, length); - delete[] new_pointer; + // move forward in document order + inline bool move_forward(); - if (!found) { - // since the pointer has found nothing, we get back to the original - // position. - depth = depth_s; - location = location_s; - current_type = current_type_s; - current_val = current_val_s; + // retrieve the character code of what we're looking at: + // [{"slutfn are the possibilities + inline uint8_t get_type() const { + return current_type; // short functions should be inlined! } - return found; -} + // get the int64_t value at this node; valid only if get_type is "l" + inline int64_t get_integer() const; -bool dom::parser::Iterator::relative_move_to(const char *pointer, - uint32_t length) { - if (length == 0) { - // returns the whole document - return true; - } + // get the value as uint64; valid only if if get_type is "u" + inline uint64_t get_unsigned_integer() const; - if (pointer[0] != '/') { - // '/' must be the first character - return false; - } + // get the string value at this node (NULL ended); valid only if get_type is " + // note that tabs, and line endings are escaped in the returned value (see + // print_with_escapes) return value is valid UTF-8, it may contain NULL chars + // within the string: get_string_length determines the true string length. + inline const char *get_string() const; - // finding the key in an object or the index in an array - std::string key_or_index; - uint32_t offset = 1; + // return the length of the string in bytes + inline uint32_t get_string_length() const; - // checking for the "-" case - if (is_array() && pointer[1] == '-') { - if (length != 2) { - // the pointer must be exactly "/-" - // there can't be anything more after '-' as an index - return false; - } - key_or_index = '-'; - offset = length; // will skip the loop coming right after - } + // get the double value at this node; valid only if + // get_type() is "d" + inline double get_double() const; - // We either transform the first reference token to a valid json key - // or we make sure it is a valid index in an array. - for (; offset < length; offset++) { - if (pointer[offset] == '/') { - // beginning of the next key or index - break; - } - if (is_array() && (pointer[offset] < '0' || pointer[offset] > '9')) { - // the index of an array must be an integer - // we also make sure std::stoi won't discard whitespaces later - return false; - } - if (pointer[offset] == '~') { - // "~1" represents "/" - if (pointer[offset + 1] == '1') { - key_or_index += '/'; - offset++; - continue; - } - // "~0" represents "~" - if (pointer[offset + 1] == '0') { - key_or_index += '~'; - offset++; - continue; - } - } - if (pointer[offset] == '\\') { - if (pointer[offset + 1] == '\\' || pointer[offset + 1] == '"' || - (pointer[offset + 1] <= 0x1F)) { - key_or_index += pointer[offset + 1]; - offset++; - continue; - } - return false; // invalid escaped character - } - if (pointer[offset] == '\"') { - // unescaped quote character. this is an invalid case. - // lets do nothing and assume most pointers will be valid. - // it won't find any corresponding json key anyway. - // return false; - } - key_or_index += pointer[offset]; - } - - bool found = false; - if (is_object()) { - if (move_to_key(key_or_index.c_str(), uint32_t(key_or_index.length()))) { - found = relative_move_to(pointer + offset, length - offset); - } - } else if (is_array()) { - if (key_or_index == "-") { // handling "-" case first - if (down()) { - while (next()) - ; // moving to the end of the array - // moving to the nonexistent value right after... - size_t npos; - if ((current_type == '[') || (current_type == '{')) { - // we need to jump - npos = uint32_t(current_val); - } else { - npos = - location + ((current_type == 'd' || current_type == 'l') ? 2 : 1); - } - location = npos; - current_val = doc.tape[npos]; - current_type = uint8_t(current_val >> 56); - return true; // how could it fail ? - } - } else { // regular numeric index - // The index can't have a leading '0' - if (key_or_index[0] == '0' && key_or_index.length() > 1) { - return false; - } - // it cannot be empty - if (key_or_index.length() == 0) { - return false; - } - // we already checked the index contains only valid digits - uint32_t index = std::stoi(key_or_index); - if (move_to_index(index)) { - found = relative_move_to(pointer + offset, length - offset); - } - } - } - - return found; -} - -SIMDJSON_POP_DISABLE_WARNINGS -} // namespace simdjson - -#endif // SIMDJSON_DISABLE_DEPRECATED_API + inline bool is_object_or_array() const { return is_object() || is_array(); } + inline bool is_object() const { return get_type() == '{'; } -#endif // SIMDJSON_INLINE_PARSEDJSON_ITERATOR_H -/* end file include/simdjson/dom/parsedjson_iterator-inl.h */ -/* begin file include/simdjson/dom/parser-inl.h */ -#ifndef SIMDJSON_INLINE_PARSER_H -#define SIMDJSON_INLINE_PARSER_H + inline bool is_array() const { return get_type() == '['; } -#include -#include + inline bool is_string() const { return get_type() == '"'; } -namespace simdjson { -namespace dom { + // Returns true if the current type of the node is an signed integer. + // You can get its value with `get_integer()`. + inline bool is_integer() const { return get_type() == 'l'; } -// -// 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; + // Returns true if the current type of the node is an unsigned integer. + // You can get its value with `get_unsigned_integer()`. + // + // NOTE: + // Only a large value, which is out of range of a 64-bit signed integer, is + // represented internally as an unsigned node. On the other hand, a typical + // positive integer, such as 1, 42, or 1000000, is as a signed node. + // Be aware this function returns false for a signed node. + inline bool is_unsigned_integer() const { return get_type() == 'u'; } + // Returns true if the current type of the node is a double floating-point number. + inline bool is_double() const { return get_type() == 'd'; } + // Returns true if the current type of the node is a number (integer or floating-point). + inline bool is_number() const { + return is_integer() || is_unsigned_integer() || is_double(); + } + // Returns true if the current type of the node is a bool with true value. + inline bool is_true() const { return get_type() == 't'; } + // Returns true if the current type of the node is a bool with false value. + inline bool is_false() const { return get_type() == 'f'; } + // Returns true if the current type of the node is null. + inline bool is_null() const { return get_type() == 'n'; } + // Returns true if the type byte represents an object of an array + static bool is_object_or_array(uint8_t type) { + return ((type == '[') || (type == '{')); + } -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); } + // when at {, go one level deep, looking for a given key + // if successful, we are left pointing at the value, + // if not, we are still pointing at the object ({) + // (in case of repeated keys, this only finds the first one). + // We seek the key using C's strcmp so if your JSON strings contain + // NULL chars, this would trigger a false positive: if you expect that + // to be the case, take extra precautions. + // Furthermore, we do the comparison character-by-character + // without taking into account Unicode equivalence. + inline bool move_to_key(const char *key); -inline bool parser::dump_raw_tape(std::ostream &os) const noexcept { - return valid ? doc.dump_raw_tape(os) : false; -} + // as above, but case insensitive lookup (strcmpi instead of strcmp) + inline bool move_to_key_insensitive(const char *key); -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 + // when at {, go one level deep, looking for a given key + // if successful, we are left pointing at the value, + // if not, we are still pointing at the object ({) + // (in case of repeated keys, this only finds the first one). + // The string we search for can contain NULL values. + // Furthermore, we do the comparison character-by-character + // without taking into account Unicode equivalence. + inline bool move_to_key(const char *key, uint32_t length); - if (fp == nullptr) { - return IO_ERROR; - } + // when at a key location within an object, this moves to the accompanying + // value (located next to it). This is equivalent but much faster than + // calling "next()". + inline void move_to_value(); - // Get the file size - int ret; -#if SIMDJSON_VISUAL_STUDIO && !SIMDJSON_IS_32BITS - ret = _fseeki64(fp, 0, SEEK_END); -#else - ret = std::fseek(fp, 0, SEEK_END); -#endif // _WIN64 - if(ret < 0) { - std::fclose(fp); - return IO_ERROR; - } -#if SIMDJSON_VISUAL_STUDIO && !SIMDJSON_IS_32BITS - __int64 len = _ftelli64(fp); - if(len == -1L) { - std::fclose(fp); - return IO_ERROR; - } -#else - long len = std::ftell(fp); - if((len < 0) || (len == LONG_MAX)) { - std::fclose(fp); - return IO_ERROR; - } -#endif + // when at [, go one level deep, and advance to the given index. + // if successful, we are left pointing at the value, + // if not, we are still pointing at the array ([) + inline bool move_to_index(uint32_t index); - // 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; - } + // Moves the iterator to the value corresponding to the json pointer. + // Always search from the root of the document. + // if successful, we are left pointing at the value, + // if not, we are still pointing the same value we were pointing before the + // call. The json pointer follows the rfc6901 standard's syntax: + // https://tools.ietf.org/html/rfc6901 However, the standard says "If a + // referenced member name is not unique in an object, the member that is + // referenced is undefined, and evaluation fails". Here we just return the + // first corresponding value. The length parameter is the length of the + // jsonpointer string ('pointer'). + inline bool move_to(const char *pointer, uint32_t length); - // 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; - } + // Moves the iterator to the value corresponding to the json pointer. + // Always search from the root of the document. + // if successful, we are left pointing at the value, + // if not, we are still pointing the same value we were pointing before the + // call. The json pointer implementation follows the rfc6901 standard's + // syntax: https://tools.ietf.org/html/rfc6901 However, the standard says + // "If a referenced member name is not unique in an object, the member that + // is referenced is undefined, and evaluation fails". Here we just return + // the first corresponding value. + inline bool move_to(const std::string &pointer); - return bytes_read; -} + private: + // Almost the same as move_to(), except it searches from the current + // position. The pointer's syntax is identical, though that case is not + // handled by the rfc6901 standard. The '/' is still required at the + // beginning. However, contrary to move_to(), the URI Fragment Identifier + // Representation is not supported here. Also, in case of failure, we are + // left pointing at the closest value it could reach. For these reasons it + // is private. It exists because it is used by move_to(). + inline bool relative_move_to(const char *pointer, uint32_t length); -inline simdjson_result parser::load(const std::string &path) & noexcept { - size_t len; - auto _error = read_file(path).get(len); - if (_error) { return _error; } - return parse(loaded_bytes.get(), len, false); -} + public: + // throughout return true if we can do the navigation, false + // otherwise -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); -} + // Within a given scope (series of nodes at the same depth within either an + // array or an object), we move forward. + // Thus, given [true, null, {"a":1}, [1,2]], we would visit true, null, { + // and [. At the object ({) or at the array ([), you can issue a "down" to + // visit their content. valid if we're not at the end of a scope (returns + // true). + inline bool next(); -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); - } - _error = implementation->parse(realloc_if_needed ? reinterpret_cast(loaded_bytes.get()): buf, len, provided_doc); + // Within a given scope (series of nodes at the same depth within either an + // array or an object), we move backward. + // Thus, given [true, null, {"a":1}, [1,2]], we would visit ], }, null, true + // when starting at the end of the scope. At the object ({) or at the array + // ([), you can issue a "down" to visit their content. + // Performance warning: This function is implemented by starting again + // from the beginning of the scope and scanning forward. You should expect + // it to be relatively slow. + inline bool prev(); - if (_error) { return _error; } + // Moves back to either the containing array or object (type { or [) from + // within a contained scope. + // Valid unless we are at the first level of the document + inline bool up(); - return provided_doc.root(); -} + // Valid if we're at a [ or { and it starts a non-empty scope; moves us to + // start of that deeper scope if it not empty. Thus, given [true, null, + // {"a":1}, [1,2]], if we are at the { node, we would move to the "a" node. + inline bool down(); -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); -} + // move us to the start of our current scope, + // a scope is a series of nodes at the same level + inline void to_start_scope(); + inline void rewind(); -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); -} -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; } - 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); -} + // print the node we are currently pointing at + inline bool print(std::ostream &os, bool escape_strings = true) const; -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_inline size_t parser::max_depth() const noexcept { - return implementation ? implementation->max_depth() : DEFAULT_MAX_DEPTH; -} + private: + const document &doc; + size_t max_depth{}; + size_t depth{}; + size_t location{}; // our current location on a tape + size_t tape_length{}; + uint8_t current_type{}; + uint64_t current_val{}; + typedef struct { + size_t start_of_scope; + uint8_t scope_type; + } scopeindex_t; -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; -} + scopeindex_t *depth_index{}; +}; -#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); -} +} // namespace simdjson #endif // SIMDJSON_DISABLE_DEPRECATED_API -inline error_code parser::ensure_capacity(size_t desired_capacity) noexcept { - return ensure_capacity(doc, desired_capacity); -} - +#endif // SIMDJSON_DOM_PARSEDJSON_ITERATOR_H +/* end file simdjson/dom/parsedjson_iterator.h */ -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 functions +/* including simdjson/dom/array-inl.h: #include "simdjson/dom/array-inl.h" */ +/* begin file simdjson/dom/array-inl.h */ +#ifndef SIMDJSON_ARRAY_INL_H +#define SIMDJSON_ARRAY_INL_H -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; - } -} +#include -} // namespace dom -} // namespace simdjson +/* skipped duplicate #include "simdjson/dom/base.h" */ +/* skipped duplicate #include "simdjson/dom/array.h" */ +/* skipped duplicate #include "simdjson/dom/element.h" */ +/* skipped duplicate #include "simdjson/error-inl.h" */ +/* including simdjson/internal/tape_ref-inl.h: #include "simdjson/internal/tape_ref-inl.h" */ +/* begin file simdjson/internal/tape_ref-inl.h */ +#ifndef SIMDJSON_TAPE_REF_INL_H +#define SIMDJSON_TAPE_REF_INL_H -#endif // SIMDJSON_INLINE_PARSER_H -/* end file include/simdjson/dom/parser-inl.h */ -/* begin file include/simdjson/internal/tape_ref-inl.h */ -#ifndef SIMDJSON_INLINE_TAPE_REF_H -#define SIMDJSON_INLINE_TAPE_REF_H +/* skipped duplicate #include "simdjson/dom/document.h" */ +/* skipped duplicate #include "simdjson/internal/tape_ref.h" */ +/* skipped duplicate #include "simdjson/internal/tape_type.h" */ #include namespace simdjson { namespace internal { +constexpr const uint64_t JSON_VALUE_MASK = 0x00FFFFFFFFFFFFFF; +constexpr const uint32_t JSON_COUNT_MASK = 0xFFFFFF; + // // tape_ref inline implementation // @@ -9105,3224 +8143,3083 @@ inline std::string_view internal::tape_ref::get_string_view() const noexcept { } // namespace internal } // namespace simdjson -#endif // SIMDJSON_INLINE_TAPE_REF_H -/* end file include/simdjson/internal/tape_ref-inl.h */ -/* begin file include/simdjson/dom/serialization-inl.h */ +#endif // SIMDJSON_TAPE_REF_INL_H +/* end file simdjson/internal/tape_ref-inl.h */ -#ifndef SIMDJSON_SERIALIZATION_INL_H -#define SIMDJSON_SERIALIZATION_INL_H +#include +namespace simdjson { -#include -#include +// +// 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) {} -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; +#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(); } -/*** - * Number utility functions - **/ +#endif // SIMDJSON_EXCEPTIONS -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. - */ -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; +inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) const noexcept { + if (error()) { return error(); } + return first.at_pointer(json_pointer); } -/**@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. - */ -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; +inline simdjson_result simdjson_result::at(size_t index) const noexcept { + if (error()) { return error(); } + return first.at(index); } -} // anonymous namespace -namespace internal { -/*** - * Minifier/formatter code. - **/ +namespace dom { -simdjson_inline void mini_formatter::number(uint64_t x) { - char number_buffer[24]; - char *newp = fast_itoa(number_buffer, x); - buffer.insert(buffer.end(), number_buffer, newp); +// +// 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); } - -simdjson_inline void mini_formatter::number(int64_t x) { - char number_buffer[24]; - char *newp = fast_itoa(number_buffer, x); - buffer.insert(buffer.end(), number_buffer, newp); +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); } - -simdjson_inline void mini_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); +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; } -simdjson_inline void mini_formatter::start_array() { one_char('['); } -simdjson_inline void mini_formatter::end_array() { one_char(']'); } -simdjson_inline void mini_formatter::start_object() { one_char('{'); } -simdjson_inline void mini_formatter::end_object() { one_char('}'); } -simdjson_inline void mini_formatter::comma() { one_char(','); } + // 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" -simdjson_inline void mini_formatter::true_atom() { - const char * s = "true"; - buffer.insert(buffer.end(), s, s + 4); -} -simdjson_inline void mini_formatter::false_atom() { - const char * s = "false"; - buffer.insert(buffer.end(), s, s + 5); -} -simdjson_inline void mini_formatter::null_atom() { - const char * s = "null"; - buffer.insert(buffer.end(), s, s + 4); -} -simdjson_inline void mini_formatter::one_char(char c) { buffer.push_back(c); } -simdjson_inline void mini_formatter::key(std::string_view unescaped) { - string(unescaped); - one_char(':'); -} -simdjson_inline void mini_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; } + // 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; } - for(;i < unescaped.length(); i++) { - if(needs_escaping[uint8_t(unescaped[i])]) { break; } + // If there is a /, we're not done yet, call recursively. + if (i < json_pointer.length()) { + child = child.at_pointer(json_pointer.substr(i)); } - // 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. - 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('\"'); + return child; } -inline void mini_formatter::clear() { - buffer.clear(); +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; } -simdjson_inline std::string_view mini_formatter::str() const { - return std::string_view(buffer.data(), buffer.size()); +// +// 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 -/*** - * String building code. - **/ -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; +} // namespace simdjson - internal::tape_ref iter(value.tape); - do { - // print commas after each value - if (after_value) { - format.comma(); - } - // 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()); - iter.json_index++; - } - switch (iter.tape_ref_type()) { +/* skipped duplicate #include "simdjson/dom/element-inl.h" */ - // 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 defined(__cpp_lib_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) - // Output start [ - format.start_array(); - iter.json_index++; +#endif // SIMDJSON_ARRAY_INL_H +/* end file simdjson/dom/array-inl.h */ +/* skipped duplicate #include "simdjson/dom/document_stream-inl.h" */ +/* including simdjson/dom/document-inl.h: #include "simdjson/dom/document-inl.h" */ +/* begin file simdjson/dom/document-inl.h */ +#ifndef SIMDJSON_DOCUMENT_INL_H +#define SIMDJSON_DOCUMENT_INL_H - // 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; - } +// Inline implementations go in here. - is_object[depth] = false; - after_value = false; - continue; - } +/* skipped duplicate #include "simdjson/dom/base.h" */ +/* skipped duplicate #include "simdjson/dom/document.h" */ +/* skipped duplicate #include "simdjson/dom/element-inl.h" */ +/* skipped duplicate #include "simdjson/internal/tape_ref-inl.h" */ +/* including simdjson/internal/jsonformatutils.h: #include "simdjson/internal/jsonformatutils.h" */ +/* begin file simdjson/internal/jsonformatutils.h */ +#ifndef SIMDJSON_INTERNAL_JSONFORMATUTILS_H +#define SIMDJSON_INTERNAL_JSONFORMATUTILS_H - // 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; - } +/* skipped duplicate #include "simdjson/base.h" */ +#include +#include +#include - // Output start { - format.start_object(); - iter.json_index++; +namespace simdjson { +namespace internal { - // 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; - } +inline std::ostream& operator<<(std::ostream& out, const escape_json_string &str); - is_object[depth] = true; - after_value = false; - continue; - } +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); +}; - // Scalars - case tape_type::STRING: - format.string(iter.get_string_view()); +inline std::ostream& operator<<(std::ostream& out, const escape_json_string &unescaped) { + for (size_t i=0; i()); - iter.json_index++; // numbers take up 2 spots, so we need to increment - // extra + case '\f': + out << "\\f"; 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 + case '\n': + out << "\\n"; 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 + case '\r': + out << "\\r"; break; - case tape_type::TRUE_VALUE: - format.true_atom(); + case '\"': + out << "\\\""; break; - case tape_type::FALSE_VALUE: - format.false_atom(); + case '\t': + out << "\\t"; break; - case tape_type::NULL_VALUE: - format.null_atom(); + case '\\': + out << "\\\\"; break; - - // These are impossible - case tape_type::END_ARRAY: - case tape_type::END_OBJECT: - case tape_type::ROOT: - SIMDJSON_UNREACHABLE(); - } - iter.json_index++; - after_value = true; - - // Handle multiple ends in a row - while (depth != 0 && (iter.tape_ref_type() == tape_type::END_ARRAY || - iter.tape_ref_type() == tape_type::END_OBJECT)) { - if (iter.tape_ref_type() == tape_type::END_ARRAY) { - format.end_array(); + 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 { - format.end_object(); + out << unescaped.str[i]; } - depth--; - iter.json_index++; - } - - // 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(); -} - -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::append(simdjson::dom::key_value_pair kv) { - format.key(kv.key); - append(kv.value); -} - -template -simdjson_inline void string_builder::clear() { - format.clear(); -} - -template -simdjson_inline std::string_view string_builder::str() const { - return format.str(); + return out; } - } // namespace internal } // namespace simdjson -#endif -/* end file include/simdjson/dom/serialization-inl.h */ - -SIMDJSON_POP_DISABLE_WARNINGS - -#endif // SIMDJSON_DOM_H -/* end file include/simdjson/dom.h */ -/* begin file include/simdjson/builtin.h */ -#ifndef SIMDJSON_BUILTIN_H -#define SIMDJSON_BUILTIN_H - -/* begin file include/simdjson/implementations.h */ -#ifndef SIMDJSON_IMPLEMENTATIONS_H -#define SIMDJSON_IMPLEMENTATIONS_H - -/* begin file include/simdjson/implementation-base.h */ -#ifndef SIMDJSON_IMPLEMENTATION_BASE_H -#define SIMDJSON_IMPLEMENTATION_BASE_H - -/** - * @file - * - * Includes common stuff needed for implementations. - */ - - -// Implementation-internal files (must be included before the implementations themselves, to keep -// amalgamation working--otherwise, the first time a file is included, it might be put inside the -// #ifdef SIMDJSON_IMPLEMENTATION_ARM64/FALLBACK/etc., which means the other implementations can't -// compile unless that implementation is turned on). -/* begin file include/simdjson/internal/jsoncharutils_tables.h */ -#ifndef SIMDJSON_INTERNAL_JSONCHARUTILS_TABLES_H -#define SIMDJSON_INTERNAL_JSONCHARUTILS_TABLES_H - +#endif // SIMDJSON_INTERNAL_JSONFORMATUTILS_H +/* end file simdjson/internal/jsonformatutils.h */ -#ifdef JSON_TEST_STRINGS -void found_string(const uint8_t *buf, const uint8_t *parsed_begin, - const uint8_t *parsed_end); -void found_bad_string(const uint8_t *buf); -#endif +#include namespace simdjson { -namespace internal { -// structural chars here are -// they are { 0x7b } 0x7d : 0x3a [ 0x5b ] 0x5d , 0x2c (and NULL) -// we are also interested in the four whitespace characters -// space 0x20, linefeed 0x0a, horizontal tab 0x09 and carriage return 0x0d - -extern SIMDJSON_DLLIMPORTEXPORT const bool structural_or_whitespace_negated[256]; -extern SIMDJSON_DLLIMPORTEXPORT const bool structural_or_whitespace[256]; -extern SIMDJSON_DLLIMPORTEXPORT const uint32_t digit_to_val32[886]; - -} // namespace internal -} // namespace simdjson +namespace dom { -#endif // SIMDJSON_INTERNAL_JSONCHARUTILS_TABLES_H -/* end file include/simdjson/internal/jsoncharutils_tables.h */ -/* begin file include/simdjson/internal/numberparsing_tables.h */ -#ifndef SIMDJSON_INTERNAL_NUMBERPARSING_TABLES_H -#define SIMDJSON_INTERNAL_NUMBERPARSING_TABLES_H +// +// 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; + } -namespace simdjson { -namespace internal { -/** - * The smallest non-zero float (binary64) is 2^-1074. - * We take as input numbers of the form w x 10^q where w < 2^64. - * We have that w * 10^-343 < 2^(64-344) 5^-343 < 2^-1076. - * However, we have that - * (2^64-1) * 10^-342 = (2^64-1) * 2^-342 * 5^-342 > 2^-1074. - * Thus it is possible for a number of the form w * 10^-342 where - * w is a 64-bit value to be a non-zero floating-point number. - ********* - * Any number of form w * 10^309 where w>= 1 is going to be - * infinite in binary64 so we never need to worry about powers - * of 5 greater than 308. - */ -constexpr int smallest_power = -342; -constexpr int largest_power = 308; - -/** - * Represents a 128-bit value. - * low: least significant 64 bits. - * high: most significant 64 bits. - */ -struct value128 { - uint64_t low; - uint64_t high; -}; - - -// Precomputed powers of ten from 10^0 to 10^22. These -// can be represented exactly using the double type. -extern SIMDJSON_DLLIMPORTEXPORT const double power_of_ten[]; - - -/** - * When mapping numbers from decimal to binary, - * we go from w * 10^q to m * 2^p but we have - * 10^q = 5^q * 2^q, so effectively - * we are trying to match - * w * 2^q * 5^q to m * 2^p. Thus the powers of two - * are not a concern since they can be represented - * exactly using the binary notation, only the powers of five - * affect the binary significand. - */ + // 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; +} -// 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 dom } // namespace simdjson -#endif // SIMDJSON_INTERNAL_NUMBERPARSING_TABLES_H -/* end file include/simdjson/internal/numberparsing_tables.h */ -/* begin file include/simdjson/internal/simdprune_tables.h */ -#ifndef SIMDJSON_INTERNAL_SIMDPRUNE_TABLES_H -#define SIMDJSON_INTERNAL_SIMDPRUNE_TABLES_H +#endif // SIMDJSON_DOCUMENT_INL_H +/* end file simdjson/dom/document-inl.h */ +/* skipped duplicate #include "simdjson/dom/element-inl.h" */ +/* skipped duplicate #include "simdjson/dom/object-inl.h" */ +/* including simdjson/dom/parsedjson_iterator-inl.h: #include "simdjson/dom/parsedjson_iterator-inl.h" */ +/* begin file simdjson/dom/parsedjson_iterator-inl.h */ +#ifndef SIMDJSON_PARSEDJSON_ITERATOR_INL_H +#define SIMDJSON_PARSEDJSON_ITERATOR_INL_H -#include +/* skipped duplicate #include "simdjson/dom/base.h" */ +/* skipped duplicate #include "simdjson/dom/parsedjson_iterator.h" */ +/* skipped duplicate #include "simdjson/internal/jsonformatutils.h" */ -namespace simdjson { // table modified and copied from -namespace internal { // http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetTable +/* skipped duplicate #include "simdjson/dom/parser-inl.h" */ +/* skipped duplicate #include "simdjson/internal/tape_ref-inl.h" */ -extern SIMDJSON_DLLIMPORTEXPORT const unsigned char BitsSetTable256mul2[256]; +#include +#include +#include +#include -extern SIMDJSON_DLLIMPORTEXPORT const uint8_t pshufb_combine_table[272]; +#ifndef SIMDJSON_DISABLE_DEPRECATED_API -// 256 * 8 bytes = 2kB, easily fits in cache. -extern SIMDJSON_DLLIMPORTEXPORT const uint64_t thintable_epi8[256]; +namespace simdjson { -} // namespace internal -} // namespace simdjson +// VS2017 reports deprecated warnings when you define a deprecated class's methods. +SIMDJSON_PUSH_DISABLE_WARNINGS +SIMDJSON_DISABLE_DEPRECATED_WARNING -#endif // SIMDJSON_INTERNAL_SIMDPRUNE_TABLES_H -/* end file include/simdjson/internal/simdprune_tables.h */ +// Because of template weirdness, the actual class definition is inline in the document class +simdjson_warn_unused bool dom::parser::Iterator::is_ok() const { + return location < tape_length; +} -#endif // SIMDJSON_IMPLEMENTATION_BASE_H -/* end file include/simdjson/implementation-base.h */ +// useful for debugging purposes +size_t dom::parser::Iterator::get_tape_location() const { + return location; +} -// -// First, figure out which implementations can be run. Doing it here makes it so we don't have to worry about the order -// in which we include them. -// +// useful for debugging purposes +size_t dom::parser::Iterator::get_tape_length() const { + return tape_length; +} -#ifndef SIMDJSON_IMPLEMENTATION_ARM64 -#define SIMDJSON_IMPLEMENTATION_ARM64 (SIMDJSON_IS_ARM64) -#endif -#define SIMDJSON_CAN_ALWAYS_RUN_ARM64 SIMDJSON_IMPLEMENTATION_ARM64 && SIMDJSON_IS_ARM64 +// returns the current depth (start at 1 with 0 reserved for the fictitious root +// node) +size_t dom::parser::Iterator::get_depth() const { + return depth; +} -#ifdef __has_include -// How do we detect that a compiler supports vbmi2? -// For sure if the following header is found, we are ok? -#if __has_include() -#define SIMDJSON_COMPILER_SUPPORTS_VBMI2 1 -#endif -#endif +// A scope is a series of nodes at the same depth, typically it is either an +// object ({) or an array ([). The root node has type 'r'. +uint8_t dom::parser::Iterator::get_scope_type() const { + return depth_index[depth].scope_type; +} -#ifdef _MSC_VER -#if _MSC_VER >= 1920 -// Visual Studio 2019 and up support VBMI2 under x64 even if the header -// avx512vbmi2intrin.h is not found. -#define SIMDJSON_COMPILER_SUPPORTS_VBMI2 1 -#endif -#endif +bool dom::parser::Iterator::move_forward() { + if (location + 1 >= tape_length) { + return false; // we are at the end! + } -// By default, we allow AVX512. -#ifndef SIMDJSON_AVX512_ALLOWED -#define SIMDJSON_AVX512_ALLOWED 1 -#endif + if ((current_type == '[') || (current_type == '{')) { + // We are entering a new scope + depth++; + assert(depth < max_depth); + depth_index[depth].start_of_scope = location; + depth_index[depth].scope_type = current_type; + } else if ((current_type == ']') || (current_type == '}')) { + // Leaving a scope. + depth--; + } else if (is_number()) { + // these types use 2 locations on the tape, not just one. + location += 1; + } -// Default Icelake to on if this is x86-64. Even if we're not compiled for it, it could be selected -// at runtime. -#ifndef SIMDJSON_IMPLEMENTATION_ICELAKE -#define SIMDJSON_IMPLEMENTATION_ICELAKE ((SIMDJSON_IS_X86_64) && (SIMDJSON_AVX512_ALLOWED) && (SIMDJSON_COMPILER_SUPPORTS_VBMI2)) -#endif + location += 1; + current_val = doc.tape[location]; + current_type = uint8_t(current_val >> 56); + return true; +} -#ifdef _MSC_VER -// To see why (__BMI__) && (__PCLMUL__) && (__LZCNT__) are not part of this next line, see -// https://github.com/simdjson/simdjson/issues/1247 -#define SIMDJSON_CAN_ALWAYS_RUN_ICELAKE ((SIMDJSON_IMPLEMENTATION_ICELAKE) && (__AVX2__) && (__AVX512F__) && (__AVX512DQ__) && (__AVX512CD__) && (__AVX512BW__) && (__AVX512VL__) && (__AVX512VBMI2__)) -#else -#define SIMDJSON_CAN_ALWAYS_RUN_ICELAKE ((SIMDJSON_IMPLEMENTATION_ICELAKE) && (__AVX2__) && (__BMI__) && (__PCLMUL__) && (__LZCNT__) && (__AVX512F__) && (__AVX512DQ__) && (__AVX512CD__) && (__AVX512BW__) && (__AVX512VL__) && (__AVX512VBMI2__)) -#endif +void dom::parser::Iterator::move_to_value() { + // assume that we are on a key, so move by 1. + location += 1; + current_val = doc.tape[location]; + current_type = uint8_t(current_val >> 56); +} -// Default Haswell to on if this is x86-64. Even if we're not compiled for it, it could be selected -// at runtime. -#ifndef SIMDJSON_IMPLEMENTATION_HASWELL -#if SIMDJSON_CAN_ALWAYS_RUN_ICELAKE -// if icelake is always available, never enable haswell. -#define SIMDJSON_IMPLEMENTATION_HASWELL 0 -#else -#define SIMDJSON_IMPLEMENTATION_HASWELL SIMDJSON_IS_X86_64 -#endif -#endif -#ifdef _MSC_VER -// To see why (__BMI__) && (__PCLMUL__) && (__LZCNT__) are not part of this next line, see -// https://github.com/simdjson/simdjson/issues/1247 -#define SIMDJSON_CAN_ALWAYS_RUN_HASWELL ((SIMDJSON_IMPLEMENTATION_HASWELL) && (SIMDJSON_IS_X86_64) && (__AVX2__)) -#else -#define SIMDJSON_CAN_ALWAYS_RUN_HASWELL ((SIMDJSON_IMPLEMENTATION_HASWELL) && (SIMDJSON_IS_X86_64) && (__AVX2__) && (__BMI__) && (__PCLMUL__) && (__LZCNT__)) -#endif +bool dom::parser::Iterator::move_to_key(const char *key) { + if (down()) { + do { + const bool right_key = (strcmp(get_string(), key) == 0); + move_to_value(); + if (right_key) { + return true; + } + } while (next()); + up(); + } + return false; +} -// Default Westmere to on if this is x86-64. -#ifndef SIMDJSON_IMPLEMENTATION_WESTMERE -#if SIMDJSON_CAN_ALWAYS_RUN_ICELAKE || SIMDJSON_CAN_ALWAYS_RUN_HASWELL -// if icelake or haswell are always available, never enable westmere. -#define SIMDJSON_IMPLEMENTATION_WESTMERE 0 -#else -#define SIMDJSON_IMPLEMENTATION_WESTMERE SIMDJSON_IS_X86_64 -#endif -#endif -#define SIMDJSON_CAN_ALWAYS_RUN_WESTMERE (SIMDJSON_IMPLEMENTATION_WESTMERE && SIMDJSON_IS_X86_64 && __SSE4_2__ && __PCLMUL__) +bool dom::parser::Iterator::move_to_key_insensitive( + const char *key) { + if (down()) { + do { + const bool right_key = (simdjson_strcasecmp(get_string(), key) == 0); + move_to_value(); + if (right_key) { + return true; + } + } while (next()); + up(); + } + return false; +} -#ifndef SIMDJSON_IMPLEMENTATION_PPC64 -#define SIMDJSON_IMPLEMENTATION_PPC64 (SIMDJSON_IS_PPC64 && SIMDJSON_IS_PPC64_VMX) -#endif -#define SIMDJSON_CAN_ALWAYS_RUN_PPC64 SIMDJSON_IMPLEMENTATION_PPC64 && SIMDJSON_IS_PPC64 && SIMDJSON_IS_PPC64_VMX +bool dom::parser::Iterator::move_to_key(const char *key, + uint32_t length) { + if (down()) { + do { + bool right_key = ((get_string_length() == length) && + (memcmp(get_string(), key, length) == 0)); + move_to_value(); + if (right_key) { + return true; + } + } while (next()); + up(); + } + return false; +} -// 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 -// if anything at all except fallback can always run, then disable fallback. -#define SIMDJSON_IMPLEMENTATION_FALLBACK 0 -#else -#define SIMDJSON_IMPLEMENTATION_FALLBACK 1 -#endif -#endif -#define SIMDJSON_CAN_ALWAYS_RUN_FALLBACK SIMDJSON_IMPLEMENTATION_FALLBACK +bool dom::parser::Iterator::move_to_index(uint32_t index) { + if (down()) { + uint32_t i = 0; + for (; i < index; i++) { + if (!next()) { + break; + } + } + if (i == index) { + return true; + } + up(); + } + return false; +} -SIMDJSON_PUSH_DISABLE_WARNINGS -SIMDJSON_DISABLE_UNDESIRED_WARNINGS +bool dom::parser::Iterator::prev() { + size_t target_location = location; + to_start_scope(); + size_t npos = location; + if (target_location == npos) { + return false; // we were already at the start + } + size_t oldnpos; + // we have that npos < target_location here + do { + oldnpos = npos; + if ((current_type == '[') || (current_type == '{')) { + // we need to jump + npos = uint32_t(current_val); + } else { + npos = npos + ((current_type == 'd' || current_type == 'l') ? 2 : 1); + } + } while (npos < target_location); + location = oldnpos; + current_val = doc.tape[location]; + current_type = uint8_t(current_val >> 56); + return true; +} -// Implementations -/* begin file include/simdjson/arm64.h */ -#ifndef SIMDJSON_ARM64_H -#define SIMDJSON_ARM64_H +bool dom::parser::Iterator::up() { + if (depth == 1) { + return false; // don't allow moving back to root + } + to_start_scope(); + // next we just move to the previous value + depth--; + location -= 1; + current_val = doc.tape[location]; + current_type = uint8_t(current_val >> 56); + return true; +} +bool dom::parser::Iterator::down() { + if (location + 1 >= tape_length) { + return false; + } + if ((current_type == '[') || (current_type == '{')) { + size_t npos = uint32_t(current_val); + if (npos == location + 2) { + return false; // we have an empty scope + } + depth++; + assert(depth < max_depth); + location = location + 1; + depth_index[depth].start_of_scope = location; + depth_index[depth].scope_type = current_type; + current_val = doc.tape[location]; + current_type = uint8_t(current_val >> 56); + return true; + } + return false; +} -#if SIMDJSON_IMPLEMENTATION_ARM64 +void dom::parser::Iterator::to_start_scope() { + location = depth_index[depth].start_of_scope; + current_val = doc.tape[location]; + current_type = uint8_t(current_val >> 56); +} -namespace simdjson { -/** - * Implementation for NEON (ARMv8). - */ -namespace arm64 { -} // namespace arm64 -} // namespace simdjson +inline void dom::parser::Iterator::rewind() { + while (up()) + ; +} -/* begin file include/simdjson/arm64/implementation.h */ -#ifndef SIMDJSON_ARM64_IMPLEMENTATION_H -#define SIMDJSON_ARM64_IMPLEMENTATION_H +bool dom::parser::Iterator::next() { + size_t npos; + if ((current_type == '[') || (current_type == '{')) { + // we need to jump + npos = uint32_t(current_val); + } else { + npos = location + (is_number() ? 2 : 1); + } + uint64_t next_val = doc.tape[npos]; + uint8_t next_type = uint8_t(next_val >> 56); + if ((next_type == ']') || (next_type == '}')) { + return false; // we reached the end of the scope + } + location = npos; + current_val = next_val; + current_type = next_type; + return true; +} +dom::parser::Iterator::Iterator(const dom::parser &pj) noexcept(false) + : doc(pj.doc) +{ +#if SIMDJSON_EXCEPTIONS + if (!pj.valid) { throw simdjson_error(pj.error); } +#else + if (!pj.valid) { return; } // abort() usage is forbidden in the library +#endif -namespace simdjson { -namespace arm64 { + max_depth = pj.max_depth(); + depth_index = new scopeindex_t[max_depth + 1]; + depth_index[0].start_of_scope = location; + current_val = doc.tape[location++]; + current_type = uint8_t(current_val >> 56); + depth_index[0].scope_type = current_type; + tape_length = size_t(current_val & internal::JSON_VALUE_MASK); + if (location < tape_length) { + // If we make it here, then depth_capacity must >=2, but the compiler + // may not know this. + current_val = doc.tape[location]; + current_type = uint8_t(current_val >> 56); + depth++; + assert(depth < max_depth); + depth_index[depth].start_of_scope = location; + depth_index[depth].scope_type = current_type; + } +} +dom::parser::Iterator::Iterator( + const dom::parser::Iterator &o) noexcept + : doc(o.doc), + max_depth(o.depth), + depth(o.depth), + location(o.location), + tape_length(o.tape_length), + current_type(o.current_type), + current_val(o.current_val) +{ + depth_index = new scopeindex_t[max_depth+1]; + std::memcpy(depth_index, o.depth_index, (depth + 1) * sizeof(depth_index[0])); +} -namespace { -using namespace simdjson; -using namespace simdjson::dom; +dom::parser::Iterator::~Iterator() noexcept { + if (depth_index) { delete[] depth_index; } } -/** - * @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; -}; +bool dom::parser::Iterator::print(std::ostream &os, bool escape_strings) const { + if (!is_ok()) { + return false; + } + switch (current_type) { + case '"': // we have a string + os << '"'; + if (escape_strings) { + os << internal::escape_json_string(std::string_view(get_string(), get_string_length())); + } else { + // was: os << get_string();, but given that we can include null chars, we + // have to do something crazier: + std::copy(get_string(), get_string() + get_string_length(), std::ostream_iterator(os)); + } + os << '"'; + break; + case 'l': // we have a long int + os << get_integer(); + break; + case 'u': + os << get_unsigned_integer(); + break; + case 'd': + os << get_double(); + break; + case 'n': // we have a null + os << "null"; + break; + case 't': // we have a true + os << "true"; + break; + case 'f': // we have a false + os << "false"; + break; + case '{': // we have an object + case '}': // we end an object + case '[': // we start an array + case ']': // we end an array + os << char(current_type); + break; + default: + return false; + } + return true; +} -} // namespace arm64 -} // namespace simdjson +bool dom::parser::Iterator::move_to(const char *pointer, + uint32_t length) { + char *new_pointer = nullptr; + if (pointer[0] == '#') { + // Converting fragment representation to string representation + new_pointer = new char[length]; + uint32_t new_length = 0; + for (uint32_t i = 1; i < length; i++) { + if (pointer[i] == '%' && pointer[i + 1] == 'x') { +#if __cpp_exceptions + try { +#endif + int fragment = + std::stoi(std::string(&pointer[i + 2], 2), nullptr, 16); + if (fragment == '\\' || fragment == '"' || (fragment <= 0x1F)) { + // escaping the character + new_pointer[new_length] = '\\'; + new_length++; + } + new_pointer[new_length] = char(fragment); + i += 3; +#if __cpp_exceptions + } catch (std::invalid_argument &) { + delete[] new_pointer; + return false; // the fragment is invalid + } +#endif + } else { + new_pointer[new_length] = pointer[i]; + } + new_length++; + } + length = new_length; + pointer = new_pointer; + } -#endif // SIMDJSON_ARM64_IMPLEMENTATION_H -/* end file include/simdjson/arm64/implementation.h */ + // saving the current state + size_t depth_s = depth; + size_t location_s = location; + uint8_t current_type_s = current_type; + uint64_t current_val_s = current_val; -/* begin file include/simdjson/arm64/begin.h */ -// redefining SIMDJSON_IMPLEMENTATION to "arm64" -// #define SIMDJSON_IMPLEMENTATION arm64 -/* end file include/simdjson/arm64/begin.h */ + rewind(); // The json pointer is used from the root of the document. -// Declarations -/* begin file include/simdjson/generic/dom_parser_implementation.h */ + bool found = relative_move_to(pointer, length); + delete[] new_pointer; -namespace simdjson { -namespace arm64 { + if (!found) { + // since the pointer has found nothing, we get back to the original + // position. + depth = depth_s; + location = location_s; + current_type = current_type_s; + current_val = current_val_s; + } -// 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 + return found; +} -static_assert(sizeof(open_container) == 64/8, "Open container must be 64 bits"); +inline bool dom::parser::Iterator::move_to(const std::string &pointer) { + return move_to(pointer.c_str(), uint32_t(pointer.length())); +} -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 int64_t dom::parser::Iterator::get_integer() const { + if (location + 1 >= tape_length) { + return 0; // default value in case of error + } + return static_cast(doc.tape[location + 1]); +} - 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 uint64_t dom::parser::Iterator::get_unsigned_integer() const { + if (location + 1 >= tape_length) { + return 0; // default value in case of error + } + return doc.tape[location + 1]; +} - 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); +inline const char * dom::parser::Iterator::get_string() const { + return reinterpret_cast( + doc.string_buf.get() + (current_val & internal::JSON_VALUE_MASK) + sizeof(uint32_t)); +} -}; +inline uint32_t dom::parser::Iterator::get_string_length() const { + uint32_t answer; + std::memcpy(&answer, + reinterpret_cast(doc.string_buf.get() + + (current_val & internal::JSON_VALUE_MASK)), + sizeof(uint32_t)); + return answer; +} -} // namespace arm64 -} // namespace simdjson +inline double dom::parser::Iterator::get_double() const { + if (location + 1 >= tape_length) { + return std::numeric_limits::quiet_NaN(); // default value in + // case of error + } + double answer; + std::memcpy(&answer, &doc.tape[location + 1], sizeof(answer)); + return answer; +} -namespace simdjson { -namespace arm64 { +bool dom::parser::Iterator::relative_move_to(const char *pointer, + uint32_t length) { + if (length == 0) { + // returns the whole document + return true; + } -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; + if (pointer[0] != '/') { + // '/' must be the first character + return false; + } -// 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; + // finding the key in an object or the index in an array + std::string key_or_index; + uint32_t offset = 1; - _capacity = capacity; - return SUCCESS; -} + // checking for the "-" case + if (is_array() && pointer[1] == '-') { + if (length != 2) { + // the pointer must be exactly "/-" + // there can't be anything more after '-' as an index + return false; + } + key_or_index = '-'; + offset = length; // will skip the loop coming right after + } -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; } + // We either transform the first reference token to a valid json key + // or we make sure it is a valid index in an array. + for (; offset < length; offset++) { + if (pointer[offset] == '/') { + // beginning of the next key or index + break; + } + if (is_array() && (pointer[offset] < '0' || pointer[offset] > '9')) { + // the index of an array must be an integer + // we also make sure std::stoi won't discard whitespaces later + return false; + } + if (pointer[offset] == '~') { + // "~1" represents "/" + if (pointer[offset + 1] == '1') { + key_or_index += '/'; + offset++; + continue; + } + // "~0" represents "~" + if (pointer[offset + 1] == '0') { + key_or_index += '~'; + offset++; + continue; + } + } + if (pointer[offset] == '\\') { + if (pointer[offset + 1] == '\\' || pointer[offset + 1] == '"' || + (pointer[offset + 1] <= 0x1F)) { + key_or_index += pointer[offset + 1]; + offset++; + continue; + } + return false; // invalid escaped character + } + if (pointer[offset] == '\"') { + // unescaped quote character. this is an invalid case. + // lets do nothing and assume most pointers will be valid. + // it won't find any corresponding json key anyway. + // return false; + } + key_or_index += pointer[offset]; + } - _max_depth = max_depth; - return SUCCESS; + bool found = false; + if (is_object()) { + if (move_to_key(key_or_index.c_str(), uint32_t(key_or_index.length()))) { + found = relative_move_to(pointer + offset, length - offset); + } + } else if (is_array()) { + if (key_or_index == "-") { // handling "-" case first + if (down()) { + while (next()) + ; // moving to the end of the array + // moving to the nonexistent value right after... + size_t npos; + if ((current_type == '[') || (current_type == '{')) { + // we need to jump + npos = uint32_t(current_val); + } else { + npos = + location + ((current_type == 'd' || current_type == 'l') ? 2 : 1); + } + location = npos; + current_val = doc.tape[npos]; + current_type = uint8_t(current_val >> 56); + return true; // how could it fail ? + } + } else { // regular numeric index + // The index can't have a leading '0' + if (key_or_index[0] == '0' && key_or_index.length() > 1) { + return false; + } + // it cannot be empty + if (key_or_index.length() == 0) { + return false; + } + // we already checked the index contains only valid digits + uint32_t index = std::stoi(key_or_index); + if (move_to_index(index)) { + found = relative_move_to(pointer + offset, length - offset); + } + } + } + + return found; } -} // namespace arm64 +SIMDJSON_POP_DISABLE_WARNINGS } // namespace simdjson -/* end file include/simdjson/generic/dom_parser_implementation.h */ -/* begin file include/simdjson/arm64/intrinsics.h */ -#ifndef SIMDJSON_ARM64_INTRINSICS_H -#define SIMDJSON_ARM64_INTRINSICS_H -// This should be the correct header whether -// you use visual studio or other compilers. -#include +#endif // SIMDJSON_DISABLE_DEPRECATED_API -static_assert(sizeof(uint8x16_t) <= simdjson::SIMDJSON_PADDING, "insufficient padding for arm64"); -#endif // SIMDJSON_ARM64_INTRINSICS_H -/* end file include/simdjson/arm64/intrinsics.h */ -/* begin file include/simdjson/arm64/bitmanipulation.h */ -#ifndef SIMDJSON_ARM64_BITMANIPULATION_H -#define SIMDJSON_ARM64_BITMANIPULATION_H +#endif // SIMDJSON_PARSEDJSON_ITERATOR_INL_H +/* end file simdjson/dom/parsedjson_iterator-inl.h */ +/* skipped duplicate #include "simdjson/dom/parser-inl.h" */ +/* skipped duplicate #include "simdjson/internal/tape_ref-inl.h" */ +/* including simdjson/dom/serialization-inl.h: #include "simdjson/dom/serialization-inl.h" */ +/* begin file simdjson/dom/serialization-inl.h */ -namespace simdjson { -namespace arm64 { -namespace { +#ifndef SIMDJSON_SERIALIZATION_INL_H +#define SIMDJSON_SERIALIZATION_INL_H -// 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; -#else // SIMDJSON_REGULAR_VISUAL_STUDIO - return __builtin_ctzll(input_num); -#endif // SIMDJSON_REGULAR_VISUAL_STUDIO -} +/* 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/internal/tape_type.h" */ -/* 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); -} +/* skipped duplicate #include "simdjson/dom/array-inl.h" */ +/* skipped duplicate #include "simdjson/dom/object-inl.h" */ +/* skipped duplicate #include "simdjson/internal/tape_ref-inl.h" */ -/* 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; -#else - return __builtin_clzll(input_num); -#endif// SIMDJSON_REGULAR_VISUAL_STUDIO +#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; } -/* 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))); +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()); +} +#endif +inline std::ostream& operator<<(std::ostream& out, simdjson::dom::array value) { + simdjson::internal::string_builder<> sb; + sb.append(value); + return (out << sb.str()); +} +#if SIMDJSON_EXCEPTIONS +inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result x) { + if (x.error()) { throw simdjson::simdjson_error(x.error()); } + return (out << x.value()); +} +#endif +inline std::ostream& operator<<(std::ostream& out, simdjson::dom::object value) { + simdjson::internal::string_builder<> sb; + sb.append(value); + return (out << sb.str()); +} +#if SIMDJSON_EXCEPTIONS +inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result x) { + if (x.error()) { throw simdjson::simdjson_error(x.error()); } + return (out << x.value()); +} +#endif +} // namespace dom -#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. +/*** + * 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. */ -/* - * We use SIMDJSON_PREFER_REVERSE_BITS as a hint that algorithms that - * work well with bit reversal may use it. +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. */ -#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; +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; } -/** - * 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. + +} // anonymous namespace +namespace internal { + +/*** + * Minifier/formatter code. **/ -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); + +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); } -#endif +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); +} -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; -#else - return __builtin_uaddll_overflow(value1, value2, - reinterpret_cast(result)); -#endif +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); } -} // unnamed namespace -} // namespace arm64 -} // namespace simdjson +template +simdjson_inline void base_formatter::start_array() { one_char('['); } -#endif // SIMDJSON_ARM64_BITMANIPULATION_H -/* end file include/simdjson/arm64/bitmanipulation.h */ -/* begin file include/simdjson/arm64/bitmask.h */ -#ifndef SIMDJSON_ARM64_BITMASK_H -#define SIMDJSON_ARM64_BITMASK_H -namespace simdjson { -namespace arm64 { -namespace { +template +simdjson_inline void base_formatter::end_array() { one_char(']'); } -// -// 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; +template +simdjson_inline void base_formatter::start_object() { one_char('{'); } + +template +simdjson_inline void base_formatter::end_object() { one_char('}'); } + +template +simdjson_inline void base_formatter::comma() { one_char(','); } + +template +simdjson_inline void base_formatter::true_atom() { + const char * s = "true"; + buffer.insert(buffer.end(), s, s + 4); } -} // unnamed namespace -} // namespace arm64 -} // namespace simdjson +template +simdjson_inline void base_formatter::false_atom() { + const char * s = "false"; + buffer.insert(buffer.end(), s, s + 5); +} -#endif -/* end file include/simdjson/arm64/bitmask.h */ -/* begin file include/simdjson/arm64/simd.h */ -#ifndef SIMDJSON_ARM64_SIMD_H -#define SIMDJSON_ARM64_SIMD_H +template +simdjson_inline void base_formatter::null_atom() { + const char * s = "null"; + buffer.insert(buffer.end(), s, s + 4); +} -#include +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(':'); +} -namespace simdjson { -namespace arm64 { -namespace { -namespace simd { +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++) {} -#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO -namespace { -// Start of private section with Visual Studio workaround + // 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('\"'); +} -/** - * make_uint8x16_t initializes a SIMD register (uint8x16_t). - * This is needed because, incredibly, the syntax uint8x16_t x = {1,2,3...} - * is not recognized under Visual Studio! This is a workaround. - * Using a std::initializer_list as a parameter resulted in - * inefficient code. With the current approach, if the parameters are - * compile-time constants, - * GNU GCC compiles it to ldr, the same as uint8x16_t x = {1,2,3...}. - * You should not use this function except for compile-time constants: - * it is not efficient. - */ -simdjson_inline uint8x16_t make_uint8x16_t(uint8_t x1, uint8_t x2, uint8_t x3, uint8_t x4, - uint8_t x5, uint8_t x6, uint8_t x7, uint8_t x8, - uint8_t x9, uint8_t x10, uint8_t x11, uint8_t x12, - uint8_t x13, uint8_t x14, uint8_t x15, uint8_t x16) { - // Doing a load like so end ups generating worse code. - // uint8_t array[16] = {x1, x2, x3, x4, x5, x6, x7, x8, - // x9, x10,x11,x12,x13,x14,x15,x16}; - // return vld1q_u8(array); - uint8x16_t x{}; - // incredibly, Visual Studio does not allow x[0] = x1 - x = vsetq_lane_u8(x1, x, 0); - x = vsetq_lane_u8(x2, x, 1); - x = vsetq_lane_u8(x3, x, 2); - x = vsetq_lane_u8(x4, x, 3); - x = vsetq_lane_u8(x5, x, 4); - x = vsetq_lane_u8(x6, x, 5); - x = vsetq_lane_u8(x7, x, 6); - x = vsetq_lane_u8(x8, x, 7); - x = vsetq_lane_u8(x9, x, 8); - x = vsetq_lane_u8(x10, x, 9); - x = vsetq_lane_u8(x11, x, 10); - x = vsetq_lane_u8(x12, x, 11); - x = vsetq_lane_u8(x13, x, 12); - x = vsetq_lane_u8(x14, x, 13); - x = vsetq_lane_u8(x15, x, 14); - x = vsetq_lane_u8(x16, x, 15); - return x; +template +inline void base_formatter::clear() { + buffer.clear(); } -simdjson_inline uint8x8_t make_uint8x8_t(uint8_t x1, uint8_t x2, uint8_t x3, uint8_t x4, - uint8_t x5, uint8_t x6, uint8_t x7, uint8_t x8) { - uint8x8_t x{}; - x = vset_lane_u8(x1, x, 0); - x = vset_lane_u8(x2, x, 1); - x = vset_lane_u8(x3, x, 2); - x = vset_lane_u8(x4, x, 3); - x = vset_lane_u8(x5, x, 4); - x = vset_lane_u8(x6, x, 5); - x = vset_lane_u8(x7, x, 6); - x = vset_lane_u8(x8, x, 7); - return x; +template +simdjson_inline std::string_view base_formatter::str() const { + return std::string_view(buffer.data(), buffer.size()); } -// We have to do the same work for make_int8x16_t -simdjson_inline int8x16_t make_int8x16_t(int8_t x1, int8_t x2, int8_t x3, int8_t x4, - int8_t x5, int8_t x6, int8_t x7, int8_t x8, - int8_t x9, int8_t x10, int8_t x11, int8_t x12, - int8_t x13, int8_t x14, int8_t x15, int8_t x16) { - // Doing a load like so end ups generating worse code. - // int8_t array[16] = {x1, x2, x3, x4, x5, x6, x7, x8, - // x9, x10,x11,x12,x13,x14,x15,x16}; - // return vld1q_s8(array); - int8x16_t x{}; - // incredibly, Visual Studio does not allow x[0] = x1 - x = vsetq_lane_s8(x1, x, 0); - x = vsetq_lane_s8(x2, x, 1); - x = vsetq_lane_s8(x3, x, 2); - x = vsetq_lane_s8(x4, x, 3); - x = vsetq_lane_s8(x5, x, 4); - x = vsetq_lane_s8(x6, x, 5); - x = vsetq_lane_s8(x7, x, 6); - x = vsetq_lane_s8(x8, x, 7); - x = vsetq_lane_s8(x9, x, 8); - x = vsetq_lane_s8(x10, x, 9); - x = vsetq_lane_s8(x11, x, 10); - x = vsetq_lane_s8(x12, x, 11); - x = vsetq_lane_s8(x13, x, 12); - x = vsetq_lane_s8(x14, x, 13); - x = vsetq_lane_s8(x15, x, 14); - x = vsetq_lane_s8(x16, x, 15); - return x; +simdjson_inline void mini_formatter::print_newline() { + return; } -// End of private section with Visual Studio workaround -} // namespace -#endif // SIMDJSON_REGULAR_VISUAL_STUDIO +simdjson_inline void mini_formatter::print_indents(size_t depth) { + (void)depth; + return; +} +simdjson_inline void mini_formatter::print_space() { + return; +} - template - struct simd8; +simdjson_inline void pretty_formatter::print_newline() { + one_char('\n'); +} - // - // 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); +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(' '); + } +} - // 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; } +simdjson_inline void pretty_formatter::print_space() { + one_char(' '); +} - // 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; } +/*** + * String building code. + **/ - friend simdjson_inline Mask operator==(const simd8 lhs, const simd8 rhs) { return vceqq_u8(lhs, rhs); } +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; - template - simdjson_inline simd8 prev(const simd8 prev_chunk) const { - return vextq_u8(prev_chunk, *this, 16 - N); + internal::tape_ref iter(value.tape); + do { + // print commas after each value + if (after_value) { + format.comma(); + format.print_newline(); } - }; - // 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; + format.print_indents(depth); - static simdjson_inline simd8 splat(bool _value) { return vmovq_n_u8(uint8_t(-(!!_value))); } + // 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()) { - 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)) {} + // 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; + } - // 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 = 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); + // Output start [ + format.start_array(); + iter.json_index++; + + // Handle empty [] (we don't want to come back around and print commas) + if (iter.tape_ref_type() == tape_type::END_ARRAY) { + format.end_array(); + depth--; + break; + } + + is_object[depth] = false; + after_value = false; + format.print_newline(); + continue; } - simdjson_inline bool any() const { return vmaxvq_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); } + // 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; + } - 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(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 + // Output start { + format.start_object(); + iter.json_index++; - // 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 - ); + // Handle empty {} (we don't want to come back around and print commas) + if (iter.tape_ref_type() == tape_type::END_OBJECT) { + format.end_object(); + depth--; + break; + } + + is_object[depth] = true; + after_value = false; + format.print_newline(); + continue; } - // Store to array - simdjson_inline void store(uint8_t dst[16]) const { return vst1q_u8(dst, *this); } + // 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; - // 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); } + // 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; - // 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; } + // 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++; + } - // 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); } + // Stop when we're at depth 0 + } while (depth != 0); - // 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); } + format.print_newline(); +} - // 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); +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 +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(); +} - // 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 = 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 -#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO - uint8x8_t inc = 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; +template +simdjson_inline void string_builder::append(simdjson::dom::key_value_pair kv) { + format.key(kv.key); + append(kv.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); } +template +simdjson_inline void string_builder::clear() { + format.clear(); +} - // 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; } +template +simdjson_inline std::string_view string_builder::str() const { + return format.str(); +} - // 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(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); } +} // namespace internal +} // namespace simdjson - // 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); } +/* end file simdjson/dom/serialization-inl.h */ - // 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; } +#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/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.h: #include "simdjson/builtin.h" */ +/* begin file simdjson/builtin.h */ +#ifndef SIMDJSON_BUILTIN_H +#define SIMDJSON_BUILTIN_H - // 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); } +/* including simdjson/builtin/base.h: #include "simdjson/builtin/base.h" */ +/* begin file simdjson/builtin/base.h */ +#ifndef SIMDJSON_BUILTIN_BASE_H +#define SIMDJSON_BUILTIN_BASE_H - template - simdjson_inline simd8 prev(const simd8 prev_chunk) const { - return vextq_s8(prev_chunk, *this, 16 - N); - } +/* skipped duplicate #include "simdjson/base.h" */ +/* including simdjson/implementation_detection.h: #include "simdjson/implementation_detection.h" */ +/* begin file simdjson/implementation_detection.h */ +#ifndef SIMDJSON_IMPLEMENTATION_DETECTION_H +#define SIMDJSON_IMPLEMENTATION_DETECTION_H - // 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 - )); - } +/* skipped duplicate #include "simdjson/base.h" */ - template - simdjson_inline simd8 apply_lookup_16_to(const simd8 original) { - return vqtbl1q_s8(*this, simd8(original)); - } - }; +// 0 is reserved, because undefined SIMDJSON_IMPLEMENTATION equals 0 in preprocessor macros. +#define SIMDJSON_IMPLEMENTATION_ID_arm64 1 +#define SIMDJSON_IMPLEMENTATION_ID_fallback 2 +#define SIMDJSON_IMPLEMENTATION_ID_haswell 3 +#define SIMDJSON_IMPLEMENTATION_ID_icelake 4 +#define SIMDJSON_IMPLEMENTATION_ID_ppc64 5 +#define SIMDJSON_IMPLEMENTATION_ID_westmere 6 - 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]; +#define SIMDJSON_IMPLEMENTATION_ID_FOR(IMPL) SIMDJSON_CAT(SIMDJSON_IMPLEMENTATION_ID_, IMPL) +#define SIMDJSON_IMPLEMENTATION_ID SIMDJSON_IMPLEMENTATION_ID_FOR(SIMDJSON_IMPLEMENTATION) - simd8x64(const simd8x64& o) = delete; // no copy allowed - simd8x64& operator=(const simd8& other) = delete; // no assignment allowed - simd8x64() = delete; // no default constructor allowed +#define SIMDJSON_IMPLEMENTATION_IS(IMPL) SIMDJSON_IMPLEMENTATION_ID == SIMDJSON_IMPLEMENTATION_ID_FOR(IMPL) - 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)} {} +// +// First, figure out which implementations can be run. Doing it here makes it so we don't have to worry about the order +// in which we include them. +// - 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); - } +#ifndef SIMDJSON_IMPLEMENTATION_ARM64 +#define SIMDJSON_IMPLEMENTATION_ARM64 (SIMDJSON_IS_ARM64) +#endif +#define SIMDJSON_CAN_ALWAYS_RUN_ARM64 SIMDJSON_IMPLEMENTATION_ARM64 && SIMDJSON_IS_ARM64 - simdjson_inline simd8 reduce_or() const { - return (this->chunks[0] | this->chunks[1]) | (this->chunks[2] | this->chunks[3]); - } +// Default Icelake to on if this is x86-64. Even if we're not compiled for it, it could be selected +// at runtime. +#ifndef SIMDJSON_IMPLEMENTATION_ICELAKE +#define SIMDJSON_IMPLEMENTATION_ICELAKE ((SIMDJSON_IS_X86_64) && (SIMDJSON_AVX512_ALLOWED) && (SIMDJSON_COMPILER_SUPPORTS_VBMI2)) +#endif +#ifdef _MSC_VER +// To see why (__BMI__) && (__PCLMUL__) && (__LZCNT__) are not part of this next line, see +// https://github.com/simdjson/simdjson/issues/1247 +#define SIMDJSON_CAN_ALWAYS_RUN_ICELAKE ((SIMDJSON_IMPLEMENTATION_ICELAKE) && (__AVX2__) && (__AVX512F__) && (__AVX512DQ__) && (__AVX512CD__) && (__AVX512BW__) && (__AVX512VL__) && (__AVX512VBMI2__)) +#else +#define SIMDJSON_CAN_ALWAYS_RUN_ICELAKE ((SIMDJSON_IMPLEMENTATION_ICELAKE) && (__AVX2__) && (__BMI__) && (__PCLMUL__) && (__LZCNT__) && (__AVX512F__) && (__AVX512DQ__) && (__AVX512CD__) && (__AVX512BW__) && (__AVX512VL__) && (__AVX512VBMI2__)) +#endif - 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; - } +// Default Haswell to on if this is x86-64. Even if we're not compiled for it, it could be selected +// at runtime. +#ifndef SIMDJSON_IMPLEMENTATION_HASWELL +#if SIMDJSON_CAN_ALWAYS_RUN_ICELAKE +// if icelake is always available, never enable haswell. +#define SIMDJSON_IMPLEMENTATION_HASWELL 0 +#else +#define SIMDJSON_IMPLEMENTATION_HASWELL SIMDJSON_IS_X86_64 +#endif +#endif +#ifdef _MSC_VER +// To see why (__BMI__) && (__PCLMUL__) && (__LZCNT__) are not part of this next line, see +// https://github.com/simdjson/simdjson/issues/1247 +#define SIMDJSON_CAN_ALWAYS_RUN_HASWELL ((SIMDJSON_IMPLEMENTATION_HASWELL) && (SIMDJSON_IS_X86_64) && (__AVX2__)) +#else +#define SIMDJSON_CAN_ALWAYS_RUN_HASWELL ((SIMDJSON_IMPLEMENTATION_HASWELL) && (SIMDJSON_IS_X86_64) && (__AVX2__) && (__BMI__) && (__PCLMUL__) && (__LZCNT__)) +#endif - simdjson_inline uint64_t to_bitmask() const { -#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO - const uint8x16_t bit_mask = make_uint8x16_t( - 0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, - 0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80 - ); +// Default Westmere to on if this is x86-64. +#ifndef SIMDJSON_IMPLEMENTATION_WESTMERE +#if SIMDJSON_CAN_ALWAYS_RUN_ICELAKE || SIMDJSON_CAN_ALWAYS_RUN_HASWELL +// if icelake or haswell are always available, never enable westmere. +#define SIMDJSON_IMPLEMENTATION_WESTMERE 0 #else - const uint8x16_t bit_mask = { - 0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, - 0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80 - }; +#define SIMDJSON_IMPLEMENTATION_WESTMERE SIMDJSON_IS_X86_64 #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); - } +#endif +#define SIMDJSON_CAN_ALWAYS_RUN_WESTMERE (SIMDJSON_IMPLEMENTATION_WESTMERE && SIMDJSON_IS_X86_64 && __SSE4_2__ && __PCLMUL__) - 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(); - } +#ifndef SIMDJSON_IMPLEMENTATION_PPC64 +#define SIMDJSON_IMPLEMENTATION_PPC64 (SIMDJSON_IS_PPC64 && SIMDJSON_IS_PPC64_VMX) +#endif +#define SIMDJSON_CAN_ALWAYS_RUN_PPC64 SIMDJSON_IMPLEMENTATION_PPC64 && SIMDJSON_IS_PPC64 && SIMDJSON_IS_PPC64_VMX - 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 +// 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 +// if anything at all except fallback can always run, then disable fallback. +#define SIMDJSON_IMPLEMENTATION_FALLBACK 0 +#else +#define SIMDJSON_IMPLEMENTATION_FALLBACK 1 +#endif +#endif +#define SIMDJSON_CAN_ALWAYS_RUN_FALLBACK SIMDJSON_IMPLEMENTATION_FALLBACK -} // namespace simd -} // unnamed namespace -} // namespace arm64 -} // namespace simdjson +// Determine the best builtin implementation +#ifndef SIMDJSON_BUILTIN_IMPLEMENTATION -#endif // SIMDJSON_ARM64_SIMD_H -/* end file include/simdjson/arm64/simd.h */ -/* begin file include/simdjson/generic/jsoncharutils.h */ +#if SIMDJSON_CAN_ALWAYS_RUN_ICELAKE +#define SIMDJSON_BUILTIN_IMPLEMENTATION icelake +#elif SIMDJSON_CAN_ALWAYS_RUN_HASWELL +#define SIMDJSON_BUILTIN_IMPLEMENTATION haswell +#elif SIMDJSON_CAN_ALWAYS_RUN_WESTMERE +#define SIMDJSON_BUILTIN_IMPLEMENTATION westmere +#elif SIMDJSON_CAN_ALWAYS_RUN_ARM64 +#define SIMDJSON_BUILTIN_IMPLEMENTATION arm64 +#elif SIMDJSON_CAN_ALWAYS_RUN_PPC64 +#define SIMDJSON_BUILTIN_IMPLEMENTATION ppc64 +#elif SIMDJSON_CAN_ALWAYS_RUN_FALLBACK +#define SIMDJSON_BUILTIN_IMPLEMENTATION fallback +#else +#error "All possible implementations (including fallback) have been disabled! simdjson will not run." +#endif -namespace simdjson { -namespace arm64 { -namespace { -namespace jsoncharutils { +#endif // SIMDJSON_BUILTIN_IMPLEMENTATION -// 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]; -} +#define SIMDJSON_BUILTIN_IMPLEMENTATION_ID SIMDJSON_IMPLEMENTATION_ID_FOR(SIMDJSON_BUILTIN_IMPLEMENTATION) +#define SIMDJSON_BUILTIN_IMPLEMENTATION_IS(IMPL) SIMDJSON_BUILTIN_IMPLEMENTATION_ID == SIMDJSON_IMPLEMENTATION_ID_FOR(IMPL) -simdjson_inline uint32_t is_structural_or_whitespace(uint8_t c) { - return internal::structural_or_whitespace[c]; -} +#endif // SIMDJSON_IMPLEMENTATION_DETECTION_H +/* end file simdjson/implementation_detection.h */ -// 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; -} +namespace simdjson { +#if SIMDJSON_BUILTIN_IMPLEMENTATION_IS(arm64) + namespace arm64 {} +#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(fallback) + namespace fallback {} +#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(haswell) + namespace haswell {} +#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(icelake) + namespace icelake {} +#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(ppc64) + namespace ppc64 {} +#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(westmere) + namespace westmere {} +#else +#error Unknown SIMDJSON_BUILTIN_IMPLEMENTATION +#endif -// 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 -} + /** + * 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 -#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 +#endif // SIMDJSON_BUILTIN_BASE_H +/* end file simdjson/builtin/base.h */ +/* including simdjson/builtin/implementation.h: #include "simdjson/builtin/implementation.h" */ +/* begin file simdjson/builtin/implementation.h */ +#ifndef SIMDJSON_BUILTIN_IMPLEMENTATION_H +#define SIMDJSON_BUILTIN_IMPLEMENTATION_H -using internal::value128; +/* skipped duplicate #include "simdjson/builtin/base.h" */ -simdjson_inline value128 full_multiplication(uint64_t value1, uint64_t value2) { - value128 answer; -#if SIMDJSON_REGULAR_VISUAL_STUDIO || SIMDJSON_IS_32BITS -#ifdef _M_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 // _M_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); +/* including simdjson/generic/dependencies.h: #include "simdjson/generic/dependencies.h" */ +/* begin file simdjson/generic/dependencies.h */ +#ifdef SIMDJSON_CONDITIONAL_INCLUDE +#error simdjson/generic/dependencies.h must be included before defining SIMDJSON_CONDITIONAL_INCLUDE! #endif - return answer; -} -} // namespace jsoncharutils -} // unnamed namespace -} // namespace arm64 -} // namespace simdjson -/* end file include/simdjson/generic/jsoncharutils.h */ -/* begin file include/simdjson/generic/atomparsing.h */ -namespace simdjson { -namespace arm64 { -namespace { -/// @private -namespace atomparsing { +#ifndef SIMDJSON_GENERIC_DEPENDENCIES_H +#define SIMDJSON_GENERIC_DEPENDENCIES_H + +// Internal headers needed for generics. +// All includes referencing simdjson headers *not* under simdjson/generic must be here! +// Otherwise, amalgamation will fail. +/* skipped duplicate #include "simdjson/base.h" */ +/* skipped duplicate #include "simdjson/implementation.h" */ +/* skipped duplicate #include "simdjson/implementation_detection.h" */ +/* including simdjson/internal/instruction_set.h: #include "simdjson/internal/instruction_set.h" */ +/* begin file simdjson/internal/instruction_set.h */ +/* From +https://github.com/endorno/pytorch/blob/master/torch/lib/TH/generic/simd/simd.h +Highly modified. -// 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; } +Copyright (c) 2016- Facebook, Inc (Adam Paszke) +Copyright (c) 2014- Facebook, Inc (Soumith Chintala) +Copyright (c) 2011-2014 Idiap Research Institute (Ronan Collobert) +Copyright (c) 2012-2014 Deepmind Technologies (Koray Kavukcuoglu) +Copyright (c) 2011-2012 NEC Laboratories America (Koray Kavukcuoglu) +Copyright (c) 2011-2013 NYU (Clement Farabet) +Copyright (c) 2006-2010 NEC Laboratories America (Ronan Collobert, Leon Bottou, +Iain Melvin, Jason Weston) Copyright (c) 2006 Idiap Research Institute +(Samy Bengio) Copyright (c) 2001-2004 Idiap Research Institute (Ronan Collobert, +Samy Bengio, Johnny Mariethoz) +All rights reserved. -// 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); -} +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: -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; -} +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. -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; } -} +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. -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; -} +3. Neither the names of Facebook, Deepmind Technologies, NYU, NEC Laboratories +America and IDIAP Research Institute nor the names of its contributors may be + used to endorse or promote products derived from this software without + specific prior written permission. -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; } -} +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +*/ -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; -} +#ifndef SIMDJSON_INTERNAL_INSTRUCTION_SET_H +#define SIMDJSON_INTERNAL_INSTRUCTION_SET_H -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 simdjson { +namespace internal { -} // namespace atomparsing -} // unnamed namespace -} // namespace arm64 +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 +}; + +} // namespace internal } // namespace simdjson -/* end file include/simdjson/generic/atomparsing.h */ -/* begin file include/simdjson/arm64/stringparsing.h */ -#ifndef SIMDJSON_ARM64_STRINGPARSING_H -#define SIMDJSON_ARM64_STRINGPARSING_H +#endif // SIMDJSON_INTERNAL_INSTRUCTION_SET_H +/* end file simdjson/internal/instruction_set.h */ +/* skipped duplicate #include "simdjson/internal/dom_parser_implementation.h" */ +/* including simdjson/internal/jsoncharutils_tables.h: #include "simdjson/internal/jsoncharutils_tables.h" */ +/* begin file simdjson/internal/jsoncharutils_tables.h */ +#ifndef SIMDJSON_INTERNAL_JSONCHARUTILS_TABLES_H +#define SIMDJSON_INTERNAL_JSONCHARUTILS_TABLES_H + +/* skipped duplicate #include "simdjson/base.h" */ + +#ifdef JSON_TEST_STRINGS +void found_string(const uint8_t *buf, const uint8_t *parsed_begin, + const uint8_t *parsed_end); +void found_bad_string(const uint8_t *buf); +#endif namespace simdjson { -namespace arm64 { -namespace { +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 -using namespace simd; +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]; -// 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); +} // namespace internal +} // namespace simdjson - 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); } +#endif // SIMDJSON_INTERNAL_JSONCHARUTILS_TABLES_H +/* end file simdjson/internal/jsoncharutils_tables.h */ +/* including simdjson/internal/numberparsing_tables.h: #include "simdjson/internal/numberparsing_tables.h" */ +/* begin file simdjson/internal/numberparsing_tables.h */ +#ifndef SIMDJSON_INTERNAL_NUMBERPARSING_TABLES_H +#define SIMDJSON_INTERNAL_NUMBERPARSING_TABLES_H - uint32_t bs_bits; - uint32_t quote_bits; -}; // struct backslash_and_quote +/* skipped duplicate #include "simdjson/base.h" */ -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)); +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; - // 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 - }; -} +/** + * Represents a 128-bit value. + * low: least significant 64 bits. + * high: most significant 64 bits. + */ +struct value128 { + uint64_t low; + uint64_t high; +}; -} // unnamed namespace -} // namespace arm64 + +// 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 */ +/* including simdjson/internal/simdprune_tables.h: #include "simdjson/internal/simdprune_tables.h" */ +/* begin file simdjson/internal/simdprune_tables.h */ +#ifndef SIMDJSON_INTERNAL_SIMDPRUNE_TABLES_H +#define SIMDJSON_INTERNAL_SIMDPRUNE_TABLES_H + +/* skipped duplicate #include "simdjson/base.h" */ + +#include + +namespace simdjson { // table modified and copied from +namespace internal { // http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetTable + +extern SIMDJSON_DLLIMPORTEXPORT const unsigned char BitsSetTable256mul2[256]; + +extern SIMDJSON_DLLIMPORTEXPORT const uint8_t pshufb_combine_table[272]; + +// 256 * 8 bytes = 2kB, easily fits in cache. +extern SIMDJSON_DLLIMPORTEXPORT const uint64_t thintable_epi8[256]; + +} // namespace internal } // namespace simdjson -#endif // SIMDJSON_ARM64_STRINGPARSING_H -/* end file include/simdjson/arm64/stringparsing.h */ -/* begin file include/simdjson/arm64/numberparsing.h */ -#ifndef SIMDJSON_ARM64_NUMBERPARSING_H -#define SIMDJSON_ARM64_NUMBERPARSING_H +#endif // SIMDJSON_INTERNAL_SIMDPRUNE_TABLES_H +/* end file simdjson/internal/simdprune_tables.h */ + +#endif // SIMDJSON_GENERIC_DEPENDENCIES_H +/* end file simdjson/generic/dependencies.h */ + +/* defining SIMDJSON_CONDITIONAL_INCLUDE */ +#define SIMDJSON_CONDITIONAL_INCLUDE + +#if SIMDJSON_BUILTIN_IMPLEMENTATION_IS(arm64) +/* including simdjson/arm64/implementation.h: #include "simdjson/arm64/implementation.h" */ +/* begin file simdjson/arm64/implementation.h */ +#ifndef SIMDJSON_ARM64_IMPLEMENTATION_H +#define SIMDJSON_ARM64_IMPLEMENTATION_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #include "simdjson/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/implementation.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/internal/instruction_set.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { namespace arm64 { -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); -} +/** + * @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 numberparsing } // namespace arm64 } // namespace simdjson -#define SIMDJSON_SWAR_NUMBER_PARSING 1 +#endif // SIMDJSON_ARM64_IMPLEMENTATION_H +/* end file simdjson/arm64/implementation.h */ +#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(fallback) +/* including simdjson/fallback/implementation.h: #include "simdjson/fallback/implementation.h" */ +/* begin file simdjson/fallback/implementation.h */ +#ifndef SIMDJSON_FALLBACK_IMPLEMENTATION_H +#define SIMDJSON_FALLBACK_IMPLEMENTATION_H -/* begin file include/simdjson/generic/numberparsing.h */ -#include +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #include "simdjson/fallback/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/implementation.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { -namespace arm64 { -/// @private -namespace numberparsing { +namespace fallback { /** - * The type of a JSON number + * @private */ -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 +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; }; -#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))) -#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)) -#endif +} // namespace fallback +} // namespace simdjson -namespace { +#endif // SIMDJSON_FALLBACK_IMPLEMENTATION_H +/* end file simdjson/fallback/implementation.h */ +#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(haswell) +/* including simdjson/haswell/implementation.h: #include "simdjson/haswell/implementation.h" */ +/* begin file simdjson/haswell/implementation.h */ +#ifndef SIMDJSON_HASWELL_IMPLEMENTATION_H +#define SIMDJSON_HASWELL_IMPLEMENTATION_H -// 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; -} +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #include "simdjson/haswell/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/implementation.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/internal/instruction_set.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ -// 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 constructor may be executed on any host, so we take care not to use SIMDJSON_TARGET_HASWELL +namespace simdjson { +namespace haswell { - // The fast path has now failed, so we are failing back on the slower path. +/** + * @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; +}; - // 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; - } +} // namespace haswell +} // namespace simdjson +#endif // SIMDJSON_HASWELL_IMPLEMENTATION_H +/* end file simdjson/haswell/implementation.h */ +#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(icelake) +/* including simdjson/icelake/implementation.h: #include "simdjson/icelake/implementation.h" */ +/* begin file simdjson/icelake/implementation.h */ +#ifndef SIMDJSON_ICELAKE_IMPLEMENTATION_H +#define SIMDJSON_ICELAKE_IMPLEMENTATION_H - // 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; +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #include "simdjson/icelake/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/implementation.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/internal/instruction_set.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ +// The constructor may be executed on any host, so we take care not to use SIMDJSON_TARGET_ICELAKE +namespace simdjson { +namespace icelake { - // We want the most significant bit of i to be 1. Shift if needed. - int lz = leading_zeroes(i); - i <<= lz; +/** + * @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 simdjson - // 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 = jsoncharutils::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. +#endif // SIMDJSON_ICELAKE_IMPLEMENTATION_H +/* end file simdjson/icelake/implementation.h */ +#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(ppc64) +/* including simdjson/ppc64/implementation.h: #include "simdjson/ppc64/implementation.h" */ +/* begin file simdjson/ppc64/implementation.h */ +#ifndef SIMDJSON_PPC64_IMPLEMENTATION_H +#define SIMDJSON_PPC64_IMPLEMENTATION_H - // 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. In very rare cases, even that - // will not suffice, though it is seemingly very hard to find such a scenario. - // - // 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 = jsoncharutils::full_multiplication(i, simdjson::internal::power_of_five_128[index + 1]); - firstproduct.low += secondproduct.high; - if(secondproduct.high > firstproduct.low) { firstproduct.high++; } - // At this point, we might need to add at most one to firstproduct, but this - // can only change the value of firstproduct.high if firstproduct.low is maximal. - if(simdjson_unlikely(firstproduct.low == 0xFFFFFFFFFFFFFFFF)) { - // This is very unlikely, but if so, we need to do much more work! - return false; - } - } - 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); +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #include "simdjson/ppc64/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/implementation.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/internal/instruction_set.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ - // 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 - } - } +namespace simdjson { - mantissa += mantissa & 1; - mantissa >>= 1; +/** + * Implementation for ALTIVEC (PPC64). + */ +namespace ppc64 { - // 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; -} +/** + * @private + */ +class implementation final : public simdjson::implementation { +public: + simdjson_inline implementation() + : simdjson::implementation("ppc64", "PPC64 ALTIVEC", + internal::instruction_set::ALTIVEC) {} -// 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. + 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; +}; - // 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()); -} +} // namespace ppc64 +} // namespace simdjson -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. +#endif // SIMDJSON_PPC64_IMPLEMENTATION_H +/* end file simdjson/ppc64/implementation.h */ +#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(westmere) +/* including simdjson/westmere/implementation.h: #include "simdjson/westmere/implementation.h" */ +/* begin file simdjson/westmere/implementation.h */ +#ifndef SIMDJSON_WESTMERE_IMPLEMENTATION_H +#define SIMDJSON_WESTMERE_IMPLEMENTATION_H - // 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()); -} +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #include "simdjson/westmere/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/implementation.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/internal/instruction_set.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ -// check quickly whether the next 8 chars are made of digits -// at a glance, it looks better than Mula's -// http://0x80.pl/articles/swar-digits-validate.html -simdjson_inline bool is_made_of_eight_digits_fast(const uint8_t *chars) { - uint64_t val; - // this can read up to 7 bytes beyond the buffer size, but we require - // SIMDJSON_PADDING of padding - static_assert(7 <= SIMDJSON_PADDING, "SIMDJSON_PADDING must be bigger than 7"); - std::memcpy(&val, chars, 8); - // a branchy method might be faster: - // return (( val & 0xF0F0F0F0F0F0F0F0 ) == 0x3030303030303030) - // && (( (val + 0x0606060606060606) & 0xF0F0F0F0F0F0F0F0 ) == - // 0x3030303030303030); - return (((val & 0xF0F0F0F0F0F0F0F0) | - (((val + 0x0606060606060606) & 0xF0F0F0F0F0F0F0F0) >> 4)) == - 0x3333333333333333); -} +// The constructor may be executed on any host, so we take care not to use SIMDJSON_TARGET_WESTMERE +namespace simdjson { +namespace westmere { -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; -} +/** + * @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; +}; -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; +} // namespace westmere +} // namespace simdjson -#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; -} +#endif // SIMDJSON_WESTMERE_IMPLEMENTATION_H +/* end file simdjson/westmere/implementation.h */ +#else +#error Unknown SIMDJSON_BUILTIN_IMPLEMENTATION +#endif -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 +/* undefining SIMDJSON_CONDITIONAL_INCLUDE */ +#undef SIMDJSON_CONDITIONAL_INCLUDE - // 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. +namespace simdjson { + /** + * Function which returns a pointer to an implementation matching the "builtin" implementation. + * The builtin implementation is the best statically linked simdjson implementation that can be used by the compiling + * program. If you compile with g++ -march=haswell, this will return the haswell implementation. + * It is handy to be able to check what builtin was used: builtin_implementation()->name(). + */ + const implementation * builtin_implementation(); +} // namespace simdjson - // 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. +#endif // SIMDJSON_BUILTIN_IMPLEMENTATION_H +/* end file simdjson/builtin/implementation.h */ - // 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. +/* skipped duplicate #include "simdjson/generic/dependencies.h" */ - // If there were more than 18 digits, we may have overflowed the integer. We have to do - // something!!!! - if (simdjson_unlikely(p > start_exp+18)) { - // Skip leading zeroes: 1e000000000000000000001 is technically valid and doesn't overflow - 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; -} +/* defining SIMDJSON_CONDITIONAL_INCLUDE */ +#define SIMDJSON_CONDITIONAL_INCLUDE -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); -} +#if SIMDJSON_BUILTIN_IMPLEMENTATION_IS(arm64) +/* including simdjson/arm64.h: #include "simdjson/arm64.h" */ +/* begin file simdjson/arm64.h */ +#ifndef SIMDJSON_ARM64_H +#define SIMDJSON_ARM64_H + +/* including simdjson/arm64/begin.h: #include "simdjson/arm64/begin.h" */ +/* begin file simdjson/arm64/begin.h */ +/* defining SIMDJSON_IMPLEMENTATION to "arm64" */ +#define SIMDJSON_IMPLEMENTATION arm64 +/* including simdjson/arm64/base.h: #include "simdjson/arm64/base.h" */ +/* begin file simdjson/arm64/base.h */ +#ifndef SIMDJSON_ARM64_BASE_H +#define SIMDJSON_ARM64_BASE_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #include "simdjson/base.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { +/** + * Implementation for NEON (ARMv8). + */ +namespace arm64 { +class implementation; + +namespace { +namespace simd { +template struct simd8; +template struct simd8x64; +} // namespace simd } // unnamed namespace -/** @private */ -template -error_code slow_float_parsing(simdjson_unused const uint8_t * src, W writer) { - double d; - if (parse_float_fallback(src, &d)) { - writer.append_double(d); - return SUCCESS; - } - return INVALID_NUMBER(src); -} +} // namespace arm64 +} // namespace simdjson -/** @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: This makes a *copy* of the writer and passes it to slow_float_parsing. This happens - // because slow_float_parsing is a non-inlined function. 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. - // This is what forces the skip_double, as well. - error_code error = slow_float_parsing(src, writer); - writer.skip_double(); - 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; -} +#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 -// for performance analysis, it is sometimes useful to skip parsing -#ifdef SIMDJSON_SKIPNUMBERPARSING +/* 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 */ -template -simdjson_inline error_code parse_number(const uint8_t *const, W &writer) { - writer.append_s64(0); // always write zero - return SUCCESS; // always succeeds -} +// This should be the correct header whether +// you use visual studio or other compilers. +#include -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 +static_assert(sizeof(uint8x16_t) <= simdjson::SIMDJSON_PADDING, "insufficient padding for arm64"); -// 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) { +#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 - // - // Check for minus sign - // - bool negative = (*src == '-'); - const uint8_t *p = src + uint8_t(negative); +/* 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 */ - // - // Parse the integer part. - // - // PERF NOTE: we don't use 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++; } +namespace simdjson { +namespace arm64 { +namespace { - // If there were no digits, or if the integer starts with 0 and has more than 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); } +// 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; +#else // SIMDJSON_REGULAR_VISUAL_STUDIO + return __builtin_ctzll(input_num); +#endif // SIMDJSON_REGULAR_VISUAL_STUDIO +} - // - // 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; - } +/* 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); +} - // 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 INVALID_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 INVALID_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); } - } +/* 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; +#else + return __builtin_clzll(input_num); +#endif// SIMDJSON_REGULAR_VISUAL_STUDIO +} - // Write unsigned if it doesn't 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; +/* 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))); } -// 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"); +#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 -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; +/* 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); +} -// 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; } - } +#endif - return i; +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; +#else + return __builtin_uaddll_overflow(value1, value2, + reinterpret_cast(result)); +#endif } -// 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; } +} // unnamed namespace +} // namespace arm64 +} // namespace simdjson - 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; } - } +#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 - return i; -} +/* 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 */ -// 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); +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. // - // Parse the integer part. - // - // PERF NOTE: we don't use 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; + //#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; } -// 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); +} // unnamed namespace +} // namespace arm64 +} // namespace simdjson - // - // Parse the integer part. - // - // PERF NOTE: we don't use 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++; } +#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 */ - // If there were no digits, or if the integer starts with 0 and has more than 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; -} +#include -// 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; +#if _M_ARM64 +// __umulh requires intrin.h +#include +#endif // _M_ARM64 - // - // Parse the integer part. - // - // PERF 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++; } +namespace simdjson { +namespace arm64 { +namespace numberparsing { - // If there were no digits, or if the integer starts with 0 and 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; +// 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_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); +simdjson_inline internal::value128 full_multiplication(uint64_t value1, uint64_t value2) { + internal::value128 answer; +#if SIMDJSON_REGULAR_VISUAL_STUDIO || SIMDJSON_IS_32BITS +#ifdef _M_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 // _M_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; +} - // 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; - } +} // namespace numberparsing +} // namespace arm64 +} // namespace simdjson - // - // Parse the exponent - // - if (*p == 'e' || *p == 'E') { - p++; - bool exp_neg = *p == '-'; - p += exp_neg || *p == '+'; +#define SIMDJSON_SWAR_NUMBER_PARSING 1 - 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; } +#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 - exponent += exp_neg ? 0-exp : exp; - } +/* 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 */ - if (jsoncharutils::is_not_structural_or_whitespace(*p)) { return NUMBER_ERROR; } +namespace simdjson { +namespace arm64 { +namespace { +namespace simd { - overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power; +#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO +namespace { +// Start of private section with Visual Studio workaround - // - // 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 == '-'); +/** + * make_uint8x16_t initializes a SIMD register (uint8x16_t). + * This is needed because, incredibly, the syntax uint8x16_t x = {1,2,3...} + * is not recognized under Visual Studio! This is a workaround. + * Using a std::initializer_list as a parameter resulted in + * inefficient code. With the current approach, if the parameters are + * compile-time constants, + * GNU GCC compiles it to ldr, the same as uint8x16_t x = {1,2,3...}. + * You should not use this function except for compile-time constants: + * it is not efficient. + */ +simdjson_inline uint8x16_t make_uint8x16_t(uint8_t x1, uint8_t x2, uint8_t x3, uint8_t x4, + uint8_t x5, uint8_t x6, uint8_t x7, uint8_t x8, + uint8_t x9, uint8_t x10, uint8_t x11, uint8_t x12, + uint8_t x13, uint8_t x14, uint8_t x15, uint8_t x16) { + // Doing a load like so end ups generating worse code. + // uint8_t array[16] = {x1, x2, x3, x4, x5, x6, x7, x8, + // x9, x10,x11,x12,x13,x14,x15,x16}; + // return vld1q_u8(array); + uint8x16_t x{}; + // incredibly, Visual Studio does not allow x[0] = x1 + x = vsetq_lane_u8(x1, x, 0); + x = vsetq_lane_u8(x2, x, 1); + x = vsetq_lane_u8(x3, x, 2); + x = vsetq_lane_u8(x4, x, 3); + x = vsetq_lane_u8(x5, x, 4); + x = vsetq_lane_u8(x6, x, 5); + x = vsetq_lane_u8(x7, x, 6); + x = vsetq_lane_u8(x8, x, 7); + x = vsetq_lane_u8(x9, x, 8); + x = vsetq_lane_u8(x10, x, 9); + x = vsetq_lane_u8(x11, x, 10); + x = vsetq_lane_u8(x12, x, 11); + x = vsetq_lane_u8(x13, x, 12); + x = vsetq_lane_u8(x14, x, 13); + x = vsetq_lane_u8(x15, x, 14); + x = vsetq_lane_u8(x16, x, 15); + return x; } -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_inline uint8x8_t make_uint8x8_t(uint8_t x1, uint8_t x2, uint8_t x3, uint8_t x4, + uint8_t x5, uint8_t x6, uint8_t x7, uint8_t x8) { + uint8x8_t x{}; + x = vset_lane_u8(x1, x, 0); + x = vset_lane_u8(x2, x, 1); + x = vset_lane_u8(x3, x, 2); + x = vset_lane_u8(x4, x, 3); + x = vset_lane_u8(x5, x, 4); + x = vset_lane_u8(x6, x, 5); + x = vset_lane_u8(x7, x, 6); + x = vset_lane_u8(x8, x, 7); + return x; } -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++; } - if ( p == src ) { return NUMBER_ERROR; } - if (jsoncharutils::is_structural_or_whitespace(*p)) { - // We have an integer. - // If the number is negative and valid, it must be a signed integer. - if(negative) { return number_type::signed_integer; } - // We want values larger or equal to 9223372036854775808 to be unsigned - // integers, and the other values to be signed integers. - int digit_count = int(p - src); - if(digit_count >= 19) { - const uint8_t * smaller_big_integer = reinterpret_cast("9223372036854775808"); - if((digit_count >= 20) || (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; +// We have to do the same work for make_int8x16_t +simdjson_inline int8x16_t make_int8x16_t(int8_t x1, int8_t x2, int8_t x3, int8_t x4, + int8_t x5, int8_t x6, int8_t x7, int8_t x8, + int8_t x9, int8_t x10, int8_t x11, int8_t x12, + int8_t x13, int8_t x14, int8_t x15, int8_t x16) { + // Doing a load like so end ups generating worse code. + // int8_t array[16] = {x1, x2, x3, x4, x5, x6, x7, x8, + // x9, x10,x11,x12,x13,x14,x15,x16}; + // return vld1q_s8(array); + int8x16_t x{}; + // incredibly, Visual Studio does not allow x[0] = x1 + x = vsetq_lane_s8(x1, x, 0); + x = vsetq_lane_s8(x2, x, 1); + x = vsetq_lane_s8(x3, x, 2); + x = vsetq_lane_s8(x4, x, 3); + x = vsetq_lane_s8(x5, x, 4); + x = vsetq_lane_s8(x6, x, 5); + x = vsetq_lane_s8(x7, x, 6); + x = vsetq_lane_s8(x8, x, 7); + x = vsetq_lane_s8(x9, x, 8); + x = vsetq_lane_s8(x10, x, 9); + x = vsetq_lane_s8(x11, x, 10); + x = vsetq_lane_s8(x12, x, 11); + x = vsetq_lane_s8(x13, x, 12); + x = vsetq_lane_s8(x14, x, 13); + x = vsetq_lane_s8(x15, x, 14); + x = vsetq_lane_s8(x16, x, 15); + return x; } -// 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); +// End of private section with Visual Studio workaround +} // namespace +#endif // SIMDJSON_REGULAR_VISUAL_STUDIO - // - // 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; } + + template + struct simd8; // - // Parse the decimal part. + // Base class of simd8 and simd8, both of which use uint8x16_t internally. // - 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); + template> + struct base_u8 { + uint8x16_t value; + static const int SIZE = sizeof(value); - // 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; - } + // 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; } - // - // 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 == '+'; + // 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; } - 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; } + friend simdjson_inline Mask operator==(const simd8 lhs, const simd8 rhs) { return vceqq_u8(lhs, rhs); } - exponent += exp_neg ? 0-exp : exp; - } + template + simdjson_inline simd8 prev(const simd8 prev_chunk) const { + return vextq_u8(prev_chunk, *this, 16 - N); + } + }; - if ((p != src_end) && jsoncharutils::is_not_structural_or_whitespace(*p)) { return NUMBER_ERROR; } + // 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; - overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power; + static simdjson_inline simd8 splat(bool _value) { return vmovq_n_u8(uint8_t(-(!!_value))); } - // - // 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_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_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; + // 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 = 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); + } + simdjson_inline bool any() const { return vmaxvq_u8(*this) != 0; } + }; - // - // 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; } + // 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); } - // - // 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); + 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(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 - // 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; + // 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 + ); } - } else { - overflow = p-src > 19; - } - // - // Parse the exponent - // - if (*p == 'e' || *p == 'E') { - p++; - bool exp_neg = *p == '-'; - p += exp_neg || *p == '+'; + // Store to array + simdjson_inline void store(uint8_t dst[16]) const { return vst1q_u8(dst, *this); } - 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; } + // 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); } - exponent += exp_neg ? 0-exp : exp; - } + // 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; } - if (*p != '"') { return NUMBER_ERROR; } + // 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); } - overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power; + // 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); } - // - // 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; -} + // 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); + } -} // unnamed namespace -#endif // SIMDJSON_SKIPNUMBERPARSING -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; - default: SIMDJSON_UNREACHABLE(); + // 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 = 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); } - return out; -} - -} // namespace numberparsing -} // namespace arm64 -} // namespace simdjson -/* end file include/simdjson/generic/numberparsing.h */ -#endif // SIMDJSON_ARM64_NUMBERPARSING_H -/* end file include/simdjson/arm64/numberparsing.h */ -/* begin file include/simdjson/arm64/end.h */ -/* end file include/simdjson/arm64/end.h */ + // 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 = 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)); + } -#endif // SIMDJSON_IMPLEMENTATION_ARM64 + 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 + )); + } -#endif // SIMDJSON_ARM64_H -/* end file include/simdjson/arm64.h */ -/* begin file include/simdjson/fallback.h */ -#ifndef SIMDJSON_FALLBACK_H -#define SIMDJSON_FALLBACK_H + 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; -#if SIMDJSON_IMPLEMENTATION_FALLBACK + 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); } -namespace simdjson { -/** - * Fallback implementation (runs on any machine). - */ -namespace fallback { -} // namespace fallback -} // namespace simdjson + // 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; } -/* begin file include/simdjson/fallback/implementation.h */ -#ifndef SIMDJSON_FALLBACK_IMPLEMENTATION_H -#define SIMDJSON_FALLBACK_IMPLEMENTATION_H + // 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(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); } -namespace simdjson { -namespace fallback { + // 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); } -namespace { -using namespace simdjson; -using namespace simdjson::dom; -} + // 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; } -/** - * @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; -}; + // 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); } -} // namespace fallback -} // namespace simdjson + template + simdjson_inline simd8 prev(const simd8 prev_chunk) const { + return vextq_s8(prev_chunk, *this, 16 - N); + } -#endif // SIMDJSON_FALLBACK_IMPLEMENTATION_H -/* end file include/simdjson/fallback/implementation.h */ + // 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 + )); + } -/* begin file include/simdjson/fallback/begin.h */ -// redefining SIMDJSON_IMPLEMENTATION to "fallback" -// #define SIMDJSON_IMPLEMENTATION fallback -/* end file include/simdjson/fallback/begin.h */ + template + simdjson_inline simd8 apply_lookup_16_to(const simd8 original) { + return vqtbl1q_s8(*this, simd8(original)); + } + }; -// Declarations -/* begin file include/simdjson/generic/dom_parser_implementation.h */ + 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]; -namespace simdjson { -namespace fallback { + simd8x64(const simd8x64& o) = delete; // no copy allowed + simd8x64& operator=(const simd8& other) = delete; // no assignment allowed + simd8x64() = delete; // no default constructor allowed -// 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 + 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)} {} -static_assert(sizeof(open_container) == 64/8, "Open container must be 64 bits"); + 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); + } -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{}; + simdjson_inline simd8 reduce_or() const { + return (this->chunks[0] | this->chunks[1]) | (this->chunks[2] | this->chunks[3]); + } - 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_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 = 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); + } -} // namespace fallback + 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 fallback { +namespace arm64 { +namespace { -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; +using namespace simd; -// 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; +// 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); - _capacity = capacity; - return SUCCESS; -} + 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); } -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; } + uint32_t bs_bits; + uint32_t quote_bits; +}; // struct backslash_and_quote - _max_depth = max_depth; - return SUCCESS; +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 + }; } -} // namespace fallback +} // unnamed namespace +} // namespace arm64 } // namespace simdjson -/* end file include/simdjson/generic/dom_parser_implementation.h */ -/* begin file include/simdjson/fallback/bitmanipulation.h */ -#ifndef SIMDJSON_FALLBACK_BITMANIPULATION_H -#define SIMDJSON_FALLBACK_BITMANIPULATION_H -#include +#endif // SIMDJSON_ARM64_STRINGPARSING_DEFS_H +/* end file simdjson/arm64/stringparsing_defs.h */ + +#define SIMDJSON_SKIP_BACKSLASH_SHORT_CIRCUIT 1 +/* end file simdjson/arm64/begin.h */ +/* including simdjson/generic/amalgamated.h for arm64: #include "simdjson/generic/amalgamated.h" */ +/* begin file simdjson/generic/amalgamated.h for arm64 */ +#if defined(SIMDJSON_CONDITIONAL_INCLUDE) && !defined(SIMDJSON_GENERIC_DEPENDENCIES_H) +#error simdjson/generic/dependencies.h must be included before simdjson/generic/amalgamated.h! +#endif + +/* including simdjson/generic/base.h for arm64: #include "simdjson/generic/base.h" */ +/* begin file simdjson/generic/base.h for arm64 */ +#ifndef SIMDJSON_GENERIC_BASE_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_BASE_H */ +/* amalgamation skipped (editor-only): #include "simdjson/base.h" */ +/* amalgamation skipped (editor-only): // If we haven't got an implementation yet, we're in the editor, editing a generic file! Just */ +/* amalgamation skipped (editor-only): // use the most advanced one we can so the most possible stuff can be tested. */ +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_IMPLEMENTATION */ +/* amalgamation skipped (editor-only): #include "simdjson/implementation_detection.h" */ +/* amalgamation skipped (editor-only): #if SIMDJSON_IMPLEMENTATION_ICELAKE */ +/* amalgamation skipped (editor-only): #include "simdjson/icelake/begin.h" */ +/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_HASWELL */ +/* amalgamation skipped (editor-only): #include "simdjson/haswell/begin.h" */ +/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_WESTMERE */ +/* amalgamation skipped (editor-only): #include "simdjson/westmere/begin.h" */ +/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_ARM64 */ +/* amalgamation skipped (editor-only): #include "simdjson/arm64/begin.h" */ +/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_PPC64 */ +/* amalgamation skipped (editor-only): #include "simdjson/ppc64/begin.h" */ +/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_FALLBACK */ +/* amalgamation skipped (editor-only): #include "simdjson/fallback/begin.h" */ +/* amalgamation skipped (editor-only): #else */ +/* amalgamation skipped (editor-only): #error "All possible implementations (including fallback) have been disabled! simdjson will not run." */ +/* amalgamation skipped (editor-only): #endif */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_IMPLEMENTATION */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { -namespace fallback { -namespace { +namespace arm64 { -#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 +struct open_container; +class dom_parser_implementation; -/* 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 -} +/** + * 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 +}; -} // unnamed namespace -} // namespace fallback +} // namespace arm64 } // namespace simdjson -#endif // SIMDJSON_FALLBACK_BITMANIPULATION_H -/* end file include/simdjson/fallback/bitmanipulation.h */ -/* begin file include/simdjson/generic/jsoncharutils.h */ +#endif // SIMDJSON_GENERIC_BASE_H +/* end file simdjson/generic/base.h for arm64 */ +/* including simdjson/generic/jsoncharutils.h for arm64: #include "simdjson/generic/jsoncharutils.h" */ +/* begin file simdjson/generic/jsoncharutils.h for arm64 */ +#ifndef SIMDJSON_GENERIC_JSONCHARUTILS_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_JSONCHARUTILS_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/internal/jsoncharutils_tables.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/internal/numberparsing_tables.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { -namespace fallback { +namespace arm64 { namespace { namespace jsoncharutils { @@ -12410,34 +11307,27 @@ static simdjson_inline uint64_t _umul128(uint64_t ab, uint64_t cd, uint64_t *hi) } #endif -using internal::value128; - -simdjson_inline value128 full_multiplication(uint64_t value1, uint64_t value2) { - value128 answer; -#if SIMDJSON_REGULAR_VISUAL_STUDIO || SIMDJSON_IS_32BITS -#ifdef _M_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 // _M_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 jsoncharutils } // unnamed namespace -} // namespace fallback +} // namespace arm64 } // namespace simdjson -/* end file include/simdjson/generic/jsoncharutils.h */ -/* begin file include/simdjson/generic/atomparsing.h */ + +#endif // SIMDJSON_GENERIC_JSONCHARUTILS_H +/* end file simdjson/generic/jsoncharutils.h for arm64 */ +/* including simdjson/generic/atomparsing.h for arm64: #include "simdjson/generic/atomparsing.h" */ +/* begin file simdjson/generic/atomparsing.h for arm64 */ +#ifndef SIMDJSON_GENERIC_ATOMPARSING_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ATOMPARSING_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/jsoncharutils.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +#include + namespace simdjson { -namespace fallback { +namespace arm64 { namespace { /// @private namespace atomparsing { @@ -12499,153 +11389,317 @@ simdjson_inline bool is_valid_null_atom(const uint8_t *src, size_t len) { } // namespace atomparsing } // unnamed namespace -} // namespace fallback +} // namespace arm64 } // namespace simdjson -/* end file include/simdjson/generic/atomparsing.h */ -/* begin file include/simdjson/fallback/stringparsing.h */ -#ifndef SIMDJSON_FALLBACK_STRINGPARSING_H -#define SIMDJSON_FALLBACK_STRINGPARSING_H +#endif // SIMDJSON_GENERIC_ATOMPARSING_H +/* end file simdjson/generic/atomparsing.h for arm64 */ +/* including simdjson/generic/dom_parser_implementation.h for arm64: #include "simdjson/generic/dom_parser_implementation.h" */ +/* begin file simdjson/generic/dom_parser_implementation.h for arm64 */ +#ifndef SIMDJSON_GENERIC_DOM_PARSER_IMPLEMENTATION_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_DOM_PARSER_IMPLEMENTATION_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/internal/dom_parser_implementation.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { -namespace fallback { -namespace { +namespace arm64 { -// Holds backslashes and quotes locations. -struct backslash_and_quote { +// 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: - static constexpr uint32_t BYTES_PROCESSED = 1; - simdjson_inline static backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst); + /** 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{}; - 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; } + 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; - uint8_t c; -}; // struct backslash_and_quote + 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_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 +} // namespace arm64 } // namespace simdjson -#endif // SIMDJSON_FALLBACK_STRINGPARSING_H -/* end file include/simdjson/fallback/stringparsing.h */ -/* begin file include/simdjson/fallback/numberparsing.h */ -#ifndef SIMDJSON_FALLBACK_NUMBERPARSING_H -#define SIMDJSON_FALLBACK_NUMBERPARSING_H +namespace simdjson { +namespace arm64 { -#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 +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; -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); +// 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; } -/** @private */ -static simdjson_inline uint32_t parse_eight_digits_unrolled(const uint8_t *chars) { - return parse_eight_digits_unrolled(reinterpret_cast(chars)); + +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 numberparsing -} // namespace fallback +} // namespace arm64 } // namespace simdjson -#define SIMDJSON_SWAR_NUMBER_PARSING 1 +#endif // SIMDJSON_GENERIC_DOM_PARSER_IMPLEMENTATION_H +/* end file simdjson/generic/dom_parser_implementation.h for arm64 */ +/* including simdjson/generic/implementation_simdjson_result_base.h for arm64: #include "simdjson/generic/implementation_simdjson_result_base.h" */ +/* begin file simdjson/generic/implementation_simdjson_result_base.h for arm64 */ +#ifndef SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_H -/* begin file include/simdjson/generic/numberparsing.h */ -#include +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { -namespace fallback { -/// @private -namespace numberparsing { +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 type of a JSON number + * 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. */ -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 -}; +template +struct implementation_simdjson_result_base { -#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))) -#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)) -#endif + /** + * Create a new empty result with error = UNINITIALIZED. + */ + simdjson_inline implementation_simdjson_result_base() noexcept = default; -namespace { + /** + * Create a new error result. + */ + simdjson_inline implementation_simdjson_result_base(error_code error) noexcept; -// 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; -} + /** + * Create a new successful result. + */ + simdjson_inline implementation_simdjson_result_base(T &&value) noexcept; -// 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 + /** + * 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_inline error_code get(T &value) && noexcept; + + /** + * The error. + */ + simdjson_inline error_code error() const noexcept; + +#if SIMDJSON_EXCEPTIONS + + /** + * Get the result value. + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T& value() & noexcept(false); + + /** + * Take the result value (move it). + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T&& value() && noexcept(false); + + /** + * Take the result value (move it). + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T&& take_value() && noexcept(false); + + /** + * Cast to the value (will throw on error). + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline operator T&&() && noexcept(false); + + +#endif // SIMDJSON_EXCEPTIONS + + /** + * Get the result value. This function is safe if and only + * the error() method returns a value that evaluates to false. + */ + simdjson_inline const T& value_unsafe() const& noexcept; + /** + * 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 + +} // namespace arm64 +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_H +/* end file simdjson/generic/implementation_simdjson_result_base.h for arm64 */ +/* including simdjson/generic/numberparsing.h for arm64: #include "simdjson/generic/numberparsing.h" */ +/* begin file simdjson/generic/numberparsing.h for arm64 */ +#ifndef SIMDJSON_GENERIC_NUMBERPARSING_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_NUMBERPARSING_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/jsoncharutils.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/internal/numberparsing_tables.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +#include +#include +#include + +namespace simdjson { +namespace arm64 { +namespace numberparsing { + +#ifdef JSON_TEST_NUMBERS +#define INVALID_NUMBER(SRC) (found_invalid_number((SRC)), NUMBER_ERROR) +#define WRITE_INTEGER(VALUE, SRC, WRITER) (found_integer((VALUE), (SRC)), (WRITER).append_s64((VALUE))) +#define WRITE_UNSIGNED(VALUE, SRC, WRITER) (found_unsigned_integer((VALUE), (SRC)), (WRITER).append_u64((VALUE))) +#define WRITE_DOUBLE(VALUE, SRC, WRITER) (found_float((VALUE), (SRC)), (WRITER).append_double((VALUE))) +#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)) +#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) { @@ -12738,7 +11792,7 @@ simdjson_inline bool compute_float_64(int64_t power, uint64_t i, bool negative, // 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 = jsoncharutils::full_multiplication(i, simdjson::internal::power_of_five_128[index]); + 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 @@ -12772,7 +11826,7 @@ simdjson_inline bool compute_float_64(int64_t power, uint64_t i, bool negative, // 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 = jsoncharutils::full_multiplication(i, simdjson::internal::power_of_five_128[index + 1]); + 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++; } // At this point, we might need to add at most one to firstproduct, but this @@ -13823,6 +12877,8 @@ simdjson_unused simdjson_inline simdjson_result parse_double_in_string(c } // 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; @@ -13833,700 +12889,393 @@ inline std::ostream& operator<<(std::ostream& out, number_type type) noexcept { return out; } -} // namespace numberparsing -} // namespace fallback +} // namespace arm64 } // namespace simdjson -/* end file include/simdjson/generic/numberparsing.h */ - -#endif // SIMDJSON_FALLBACK_NUMBERPARSING_H -/* end file include/simdjson/fallback/numberparsing.h */ -/* begin file include/simdjson/fallback/end.h */ -/* end file include/simdjson/fallback/end.h */ - -#endif // SIMDJSON_IMPLEMENTATION_FALLBACK -#endif // SIMDJSON_FALLBACK_H -/* end file include/simdjson/fallback.h */ -/* begin file include/simdjson/icelake.h */ -#ifndef SIMDJSON_ICELAKE_H -#define SIMDJSON_ICELAKE_H +#endif // SIMDJSON_GENERIC_NUMBERPARSING_H +/* end file simdjson/generic/numberparsing.h for arm64 */ -#if SIMDJSON_IMPLEMENTATION_ICELAKE +/* including simdjson/generic/implementation_simdjson_result_base-inl.h for arm64: #include "simdjson/generic/implementation_simdjson_result_base-inl.h" */ +/* begin file simdjson/generic/implementation_simdjson_result_base-inl.h for arm64 */ +#ifndef SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_INL_H -#if SIMDJSON_CAN_ALWAYS_RUN_ICELAKE -#define SIMDJSON_TARGET_ICELAKE -#define SIMDJSON_UNTARGET_ICELAKE -#else -#define SIMDJSON_TARGET_ICELAKE SIMDJSON_TARGET_REGION("avx512f,avx512dq,avx512cd,avx512bw,avx512vbmi,avx512vbmi2,avx512vl,avx2,bmi,pclmul,lzcnt,popcnt") -#define SIMDJSON_UNTARGET_ICELAKE SIMDJSON_UNTARGET_REGION -#endif +/* 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 { -/** - * Implementation for Icelake (Intel AVX512). - */ -namespace icelake { -} // namespace icelake -} // namespace simdjson +namespace arm64 { // -// These two need to be included outside SIMDJSON_TARGET_ICELAKE +// internal::implementation_simdjson_result_base inline implementation // -/* begin file include/simdjson/icelake/implementation.h */ -#ifndef SIMDJSON_ICELAKE_IMPLEMENTATION_H -#define SIMDJSON_ICELAKE_IMPLEMENTATION_H - - -// The constructor may be executed on any host, so we take care not to use SIMDJSON_TARGET_ICELAKE -namespace simdjson { -namespace icelake { -using namespace simdjson; +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; + } +} -/** - * @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; -}; +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; +} -} // namespace icelake -} // namespace simdjson +template +simdjson_inline error_code implementation_simdjson_result_base::error() const noexcept { + return this->second; +} -#endif // SIMDJSON_ICELAKE_IMPLEMENTATION_H -/* end file include/simdjson/icelake/implementation.h */ -/* begin file include/simdjson/icelake/intrinsics.h */ -#ifndef SIMDJSON_ICELAKE_INTRINSICS_H -#define SIMDJSON_ICELAKE_INTRINSICS_H +#if SIMDJSON_EXCEPTIONS +template +simdjson_inline T& implementation_simdjson_result_base::value() & noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return this->first; +} -#if SIMDJSON_VISUAL_STUDIO -// under clang within visual studio, this will include -#include // visual studio or clang -#else -#include // elsewhere -#endif // SIMDJSON_VISUAL_STUDIO +template +simdjson_inline T&& implementation_simdjson_result_base::value() && noexcept(false) { + return std::forward>(*this).take_value(); +} -#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 +template +simdjson_inline T&& implementation_simdjson_result_base::take_value() && noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return std::forward(this->first); +} -static_assert(sizeof(__m512i) <= simdjson::SIMDJSON_PADDING, "insufficient padding for icelake"); +template +simdjson_inline implementation_simdjson_result_base::operator T&&() && noexcept(false) { + return std::forward>(*this).take_value(); +} -#endif // SIMDJSON_ICELAKE_INTRINSICS_H -/* end file include/simdjson/icelake/intrinsics.h */ +#endif // SIMDJSON_EXCEPTIONS -// -// The rest need to be inside the region -// -/* begin file include/simdjson/icelake/begin.h */ -// redefining SIMDJSON_IMPLEMENTATION to "icelake" -// #define SIMDJSON_IMPLEMENTATION icelake -SIMDJSON_TARGET_ICELAKE -/* end file include/simdjson/icelake/begin.h */ +template +simdjson_inline const T& implementation_simdjson_result_base::value_unsafe() const& noexcept { + return this->first; +} -// Declarations -/* begin file include/simdjson/generic/dom_parser_implementation.h */ +template +simdjson_inline T& implementation_simdjson_result_base::value_unsafe() & noexcept { + return this->first; +} -namespace simdjson { -namespace icelake { +template +simdjson_inline T&& implementation_simdjson_result_base::value_unsafe() && noexcept { + return std::forward(this->first); +} -// 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 +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) {} -static_assert(sizeof(open_container) == 64/8, "Open container must be 64 bits"); +} // namespace arm64 +} // namespace simdjson -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{}; +#endif // SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_INL_H +/* end file simdjson/generic/implementation_simdjson_result_base-inl.h for arm64 */ +/* end file simdjson/generic/amalgamated.h for arm64 */ +/* including simdjson/arm64/end.h: #include "simdjson/arm64/end.h" */ +/* begin file simdjson/arm64/end.h */ +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #include "simdjson/arm64/base.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ - 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; +#undef SIMDJSON_SKIP_BACKSLASH_SHORT_CIRCUIT +/* undefining SIMDJSON_IMPLEMENTATION from "arm64" */ +#undef SIMDJSON_IMPLEMENTATION +/* end file simdjson/arm64/end.h */ - 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); +#endif // SIMDJSON_ARM64_H +/* end file simdjson/arm64.h */ +#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(fallback) +/* including simdjson/fallback.h: #include "simdjson/fallback.h" */ +/* begin file simdjson/fallback.h */ +#ifndef SIMDJSON_FALLBACK_H +#define SIMDJSON_FALLBACK_H -}; +/* including simdjson/fallback/begin.h: #include "simdjson/fallback/begin.h" */ +/* begin file simdjson/fallback/begin.h */ +/* defining SIMDJSON_IMPLEMENTATION to "fallback" */ +#define SIMDJSON_IMPLEMENTATION fallback +/* including simdjson/fallback/base.h: #include "simdjson/fallback/base.h" */ +/* begin file simdjson/fallback/base.h */ +#ifndef SIMDJSON_FALLBACK_BASE_H +#define SIMDJSON_FALLBACK_BASE_H -} // namespace icelake -} // namespace simdjson +/* 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 { -namespace icelake { - -inline dom_parser_implementation::dom_parser_implementation() noexcept = default; -inline dom_parser_implementation::dom_parser_implementation(dom_parser_implementation &&other) noexcept = default; -inline dom_parser_implementation &dom_parser_implementation::operator=(dom_parser_implementation &&other) noexcept = default; - -// Leaving these here so they can be inlined if so desired -inline simdjson_warn_unused error_code dom_parser_implementation::set_capacity(size_t capacity) noexcept { - if(capacity > SIMDJSON_MAXSIZE_BYTES) { 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; +/** + * Fallback implementation (runs on any machine). + */ +namespace fallback { - _capacity = capacity; - return SUCCESS; -} +class implementation; -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; } +} // namespace fallback +} // namespace simdjson - _max_depth = max_depth; - return SUCCESS; -} +#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 -} // namespace icelake -} // namespace simdjson -/* end file include/simdjson/generic/dom_parser_implementation.h */ -/* begin file include/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/fallback/base.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { -namespace icelake { +namespace fallback { 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 +#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; } - -/* 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); +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) { - 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)); +#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_uaddll_overflow(value1, value2, - reinterpret_cast(result)); -#endif + return __builtin_clzll(input_num); +#endif// _MSC_VER } } // unnamed namespace -} // namespace icelake +} // namespace fallback } // namespace simdjson -#endif // SIMDJSON_ICELAKE_BITMANIPULATION_H -/* end file include/simdjson/icelake/bitmanipulation.h */ -/* begin file include/simdjson/icelake/bitmask.h */ -#ifndef SIMDJSON_ICELAKE_BITMASK_H -#define SIMDJSON_ICELAKE_BITMASK_H +#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 icelake { +namespace fallback { 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); +// 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); + + 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] }; } } // unnamed namespace -} // namespace icelake +} // namespace fallback } // namespace simdjson -#endif // SIMDJSON_ICELAKE_BITMASK_H -/* end file include/simdjson/icelake/bitmask.h */ -/* begin file include/simdjson/icelake/simd.h */ -#ifndef SIMDJSON_ICELAKE_SIMD_H -#define SIMDJSON_ICELAKE_SIMD_H - +#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 -#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 - - +#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 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 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 - )); - } - }; - - // 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)); } - }; +namespace fallback { +namespace numberparsing { - 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]; +// 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); +} - simd8x64(const simd8x64& o) = delete; // no copy allowed - simd8x64& operator=(const simd8& other) = delete; // no assignment allowed - simd8x64() = delete; // no default constructor allowed +/** @private */ +static simdjson_inline uint32_t parse_eight_digits_unrolled(const uint8_t *chars) { + return parse_eight_digits_unrolled(reinterpret_cast(chars)); +} - 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)} {} +#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 - simdjson_inline uint64_t compress(uint64_t mask, T * output) const { - this->chunks[0].compress(mask, output); - return 64 - count_ones(mask); - } +/** @private */ +simdjson_inline internal::value128 full_multiplication(uint64_t value1, uint64_t value2) { + internal::value128 answer; +#if SIMDJSON_REGULAR_VISUAL_STUDIO || SIMDJSON_IS_32BITS +#ifdef _M_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 // _M_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; +} - simdjson_inline void store(T ptr[64]) const { - this->chunks[0].store(ptr+sizeof(simd8)*0); - } +} // namespace numberparsing +} // namespace fallback +} // namespace simdjson - simdjson_inline simd8 reduce_or() const { - return this->chunks[0]; - } +#define SIMDJSON_SWAR_NUMBER_PARSING 1 - simdjson_inline simd8x64 bit_or(const T m) const { - const simd8 mask = simd8::splat(m); - return simd8x64( - this->chunks[0] | mask - ); - } +#endif // SIMDJSON_FALLBACK_NUMBERPARSING_DEFS_H +/* end file simdjson/fallback/numberparsing_defs.h */ +/* end file simdjson/fallback/begin.h */ +/* including simdjson/generic/amalgamated.h for fallback: #include "simdjson/generic/amalgamated.h" */ +/* begin file simdjson/generic/amalgamated.h for fallback */ +#if defined(SIMDJSON_CONDITIONAL_INCLUDE) && !defined(SIMDJSON_GENERIC_DEPENDENCIES_H) +#error simdjson/generic/dependencies.h must be included before simdjson/generic/amalgamated.h! +#endif - simdjson_inline uint64_t eq(const T m) const { - const simd8 mask = simd8::splat(m); - return this->chunks[0] == mask; - } +/* including simdjson/generic/base.h for fallback: #include "simdjson/generic/base.h" */ +/* begin file simdjson/generic/base.h for fallback */ +#ifndef SIMDJSON_GENERIC_BASE_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_BASE_H */ +/* amalgamation skipped (editor-only): #include "simdjson/base.h" */ +/* amalgamation skipped (editor-only): // If we haven't got an implementation yet, we're in the editor, editing a generic file! Just */ +/* amalgamation skipped (editor-only): // use the most advanced one we can so the most possible stuff can be tested. */ +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_IMPLEMENTATION */ +/* amalgamation skipped (editor-only): #include "simdjson/implementation_detection.h" */ +/* amalgamation skipped (editor-only): #if SIMDJSON_IMPLEMENTATION_ICELAKE */ +/* amalgamation skipped (editor-only): #include "simdjson/icelake/begin.h" */ +/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_HASWELL */ +/* amalgamation skipped (editor-only): #include "simdjson/haswell/begin.h" */ +/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_WESTMERE */ +/* amalgamation skipped (editor-only): #include "simdjson/westmere/begin.h" */ +/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_ARM64 */ +/* amalgamation skipped (editor-only): #include "simdjson/arm64/begin.h" */ +/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_PPC64 */ +/* amalgamation skipped (editor-only): #include "simdjson/ppc64/begin.h" */ +/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_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 */ - simdjson_inline uint64_t eq(const simd8x64 &other) const { - return this->chunks[0] == other.chunks[0]; - } +namespace simdjson { +namespace fallback { - simdjson_inline uint64_t lteq(const T m) const { - const simd8 mask = simd8::splat(m); - return this->chunks[0] <= mask; - } - }; // struct simd8x64 +struct open_container; +class dom_parser_implementation; -} // namespace simd +/** + * 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 +}; -} // unnamed namespace -} // namespace icelake +} // namespace fallback } // namespace simdjson -#endif // SIMDJSON_ICELAKE_SIMD_H -/* end file include/simdjson/icelake/simd.h */ -/* begin file include/simdjson/generic/jsoncharutils.h */ +#endif // SIMDJSON_GENERIC_BASE_H +/* end file simdjson/generic/base.h for fallback */ +/* including simdjson/generic/jsoncharutils.h for fallback: #include "simdjson/generic/jsoncharutils.h" */ +/* begin file simdjson/generic/jsoncharutils.h for fallback */ +#ifndef SIMDJSON_GENERIC_JSONCHARUTILS_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_JSONCHARUTILS_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/internal/jsoncharutils_tables.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/internal/numberparsing_tables.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { -namespace icelake { +namespace fallback { namespace { namespace jsoncharutils { @@ -14614,34 +13363,27 @@ static simdjson_inline uint64_t _umul128(uint64_t ab, uint64_t cd, uint64_t *hi) } #endif -using internal::value128; - -simdjson_inline value128 full_multiplication(uint64_t value1, uint64_t value2) { - value128 answer; -#if SIMDJSON_REGULAR_VISUAL_STUDIO || SIMDJSON_IS_32BITS -#ifdef _M_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 // _M_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 jsoncharutils } // unnamed namespace -} // namespace icelake +} // namespace fallback } // namespace simdjson -/* end file include/simdjson/generic/jsoncharutils.h */ -/* begin file include/simdjson/generic/atomparsing.h */ + +#endif // SIMDJSON_GENERIC_JSONCHARUTILS_H +/* end file simdjson/generic/jsoncharutils.h for fallback */ +/* including simdjson/generic/atomparsing.h for fallback: #include "simdjson/generic/atomparsing.h" */ +/* begin file simdjson/generic/atomparsing.h for fallback */ +#ifndef SIMDJSON_GENERIC_ATOMPARSING_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ATOMPARSING_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/jsoncharutils.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +#include + namespace simdjson { -namespace icelake { +namespace fallback { namespace { /// @private namespace atomparsing { @@ -14703,102 +13445,258 @@ simdjson_inline bool is_valid_null_atom(const uint8_t *src, size_t len) { } // namespace atomparsing } // unnamed namespace -} // namespace icelake +} // namespace fallback } // namespace simdjson -/* end file include/simdjson/generic/atomparsing.h */ -/* begin file include/simdjson/icelake/stringparsing.h */ -#ifndef SIMDJSON_ICELAKE_STRINGPARSING_H -#define SIMDJSON_ICELAKE_STRINGPARSING_H +#endif // SIMDJSON_GENERIC_ATOMPARSING_H +/* end file simdjson/generic/atomparsing.h for fallback */ +/* including simdjson/generic/dom_parser_implementation.h for fallback: #include "simdjson/generic/dom_parser_implementation.h" */ +/* begin file simdjson/generic/dom_parser_implementation.h for fallback */ +#ifndef SIMDJSON_GENERIC_DOM_PARSER_IMPLEMENTATION_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_DOM_PARSER_IMPLEMENTATION_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/internal/dom_parser_implementation.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { -namespace icelake { -namespace { +namespace fallback { -using namespace simd; +// 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 -// Holds backslashes and quotes locations. -struct backslash_and_quote { +static_assert(sizeof(open_container) == 64/8, "Open container must be 64 bits"); + +class dom_parser_implementation final : public internal::dom_parser_implementation { public: - static constexpr uint32_t BYTES_PROCESSED = 32; - simdjson_inline static backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst); + /** 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{}; - 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); } + 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; - uint64_t bs_bits; - uint64_t quote_bits; -}; // struct backslash_and_quote + 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_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 +} // namespace fallback } // namespace simdjson -#endif // SIMDJSON_ICELAKE_STRINGPARSING_H -/* end file include/simdjson/icelake/stringparsing.h */ -/* begin file include/simdjson/icelake/numberparsing.h */ -#ifndef SIMDJSON_ICELAKE_NUMBERPARSING_H -#define SIMDJSON_ICELAKE_NUMBERPARSING_H - namespace simdjson { -namespace icelake { -namespace numberparsing { +namespace fallback { -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 +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; } -} // namespace numberparsing -} // namespace icelake +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 fallback } // namespace simdjson -#define SIMDJSON_SWAR_NUMBER_PARSING 1 +#endif // SIMDJSON_GENERIC_DOM_PARSER_IMPLEMENTATION_H +/* end file simdjson/generic/dom_parser_implementation.h for fallback */ +/* including simdjson/generic/implementation_simdjson_result_base.h for fallback: #include "simdjson/generic/implementation_simdjson_result_base.h" */ +/* begin file simdjson/generic/implementation_simdjson_result_base.h for fallback */ +#ifndef SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_H -/* begin file include/simdjson/generic/numberparsing.h */ -#include +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { -namespace icelake { -/// @private -namespace numberparsing { +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 type of a JSON number + * 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. */ -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 -}; +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_inline error_code get(T &value) && noexcept; + + /** + * The error. + */ + simdjson_inline error_code error() const noexcept; + +#if SIMDJSON_EXCEPTIONS + + /** + * Get the result value. + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T& value() & noexcept(false); + + /** + * Take the result value (move it). + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T&& value() && noexcept(false); + + /** + * Take the result value (move it). + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T&& take_value() && noexcept(false); + + /** + * Cast to the value (will throw on error). + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline operator T&&() && noexcept(false); + + +#endif // SIMDJSON_EXCEPTIONS + + /** + * Get the result value. This function is safe if and only + * the error() method returns a value that evaluates to false. + */ + simdjson_inline const T& value_unsafe() const& noexcept; + /** + * 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 + +} // namespace fallback +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_H +/* end file simdjson/generic/implementation_simdjson_result_base.h for fallback */ +/* including simdjson/generic/numberparsing.h for fallback: #include "simdjson/generic/numberparsing.h" */ +/* begin file simdjson/generic/numberparsing.h for fallback */ +#ifndef SIMDJSON_GENERIC_NUMBERPARSING_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_NUMBERPARSING_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/jsoncharutils.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/internal/numberparsing_tables.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +#include +#include +#include + +namespace simdjson { +namespace fallback { +namespace numberparsing { #ifdef JSON_TEST_NUMBERS #define INVALID_NUMBER(SRC) (found_invalid_number((SRC)), NUMBER_ERROR) @@ -14950,7 +13848,7 @@ simdjson_inline bool compute_float_64(int64_t power, uint64_t i, bool negative, // 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 = jsoncharutils::full_multiplication(i, simdjson::internal::power_of_five_128[index]); + 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 @@ -14984,7 +13882,7 @@ simdjson_inline bool compute_float_64(int64_t power, uint64_t i, bool negative, // 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 = jsoncharutils::full_multiplication(i, simdjson::internal::power_of_five_128[index + 1]); + 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++; } // At this point, we might need to add at most one to firstproduct, but this @@ -16035,6 +14933,8 @@ simdjson_unused simdjson_inline simdjson_result parse_double_in_string(c } // 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; @@ -16045,85 +14945,167 @@ inline std::ostream& operator<<(std::ostream& out, number_type type) noexcept { return out; } -} // namespace numberparsing -} // namespace icelake +} // namespace fallback } // namespace simdjson -/* end file include/simdjson/generic/numberparsing.h */ - -#endif // SIMDJSON_ICELAKE_NUMBERPARSING_H -/* end file include/simdjson/icelake/numberparsing.h */ -/* begin file include/simdjson/icelake/end.h */ -SIMDJSON_UNTARGET_ICELAKE -/* end file include/simdjson/icelake/end.h */ - -#endif // SIMDJSON_IMPLEMENTATION_ICELAKE -#endif // SIMDJSON_ICELAKE_H -/* end file include/simdjson/icelake.h */ -/* begin file include/simdjson/haswell.h */ -#ifndef SIMDJSON_HASWELL_H -#define SIMDJSON_HASWELL_H +#endif // SIMDJSON_GENERIC_NUMBERPARSING_H +/* end file simdjson/generic/numberparsing.h for fallback */ -#if SIMDJSON_IMPLEMENTATION_HASWELL +/* including simdjson/generic/implementation_simdjson_result_base-inl.h for fallback: #include "simdjson/generic/implementation_simdjson_result_base-inl.h" */ +/* begin file simdjson/generic/implementation_simdjson_result_base-inl.h for fallback */ +#ifndef SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_INL_H -#if SIMDJSON_CAN_ALWAYS_RUN_HASWELL -#define SIMDJSON_TARGET_HASWELL -#define SIMDJSON_UNTARGET_HASWELL -#else -#define SIMDJSON_TARGET_HASWELL SIMDJSON_TARGET_REGION("avx2,bmi,pclmul,lzcnt,popcnt") -#define SIMDJSON_UNTARGET_HASWELL SIMDJSON_UNTARGET_REGION -#endif +/* 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 { -/** - * Implementation for Haswell (Intel AVX2). - */ -namespace haswell { -} // namespace haswell -} // namespace simdjson +namespace fallback { // -// These two need to be included outside SIMDJSON_TARGET_HASWELL +// internal::implementation_simdjson_result_base inline implementation // -/* begin file include/simdjson/haswell/implementation.h */ -#ifndef SIMDJSON_HASWELL_IMPLEMENTATION_H -#define SIMDJSON_HASWELL_IMPLEMENTATION_H +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_inline error_code implementation_simdjson_result_base::error() const noexcept { + return this->second; +} + +#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::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 fallback +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_INL_H +/* end file simdjson/generic/implementation_simdjson_result_base-inl.h for fallback */ +/* end file simdjson/generic/amalgamated.h for fallback */ +/* including simdjson/fallback/end.h: #include "simdjson/fallback/end.h" */ +/* begin file simdjson/fallback/end.h */ +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #include "simdjson/fallback/base.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +/* undefining SIMDJSON_IMPLEMENTATION from "fallback" */ +#undef SIMDJSON_IMPLEMENTATION +/* end file simdjson/fallback/end.h */ + +#endif // SIMDJSON_FALLBACK_H +/* end file simdjson/fallback.h */ +#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(haswell) +/* including simdjson/haswell.h: #include "simdjson/haswell.h" */ +/* begin file simdjson/haswell.h */ +#ifndef SIMDJSON_HASWELL_H +#define SIMDJSON_HASWELL_H + +/* including simdjson/haswell/begin.h: #include "simdjson/haswell/begin.h" */ +/* begin file simdjson/haswell/begin.h */ +/* defining SIMDJSON_IMPLEMENTATION to "haswell" */ +#define SIMDJSON_IMPLEMENTATION haswell + +/* including simdjson/haswell/base.h: #include "simdjson/haswell/base.h" */ +/* begin file simdjson/haswell/base.h */ +#ifndef SIMDJSON_HASWELL_BASE_H +#define SIMDJSON_HASWELL_BASE_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #include "simdjson/base.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ // The constructor may be executed on any host, so we take care not to use SIMDJSON_TARGET_HASWELL namespace simdjson { +/** + * Implementation for Haswell (Intel AVX2). + */ namespace haswell { -using namespace simdjson; +class implementation; -/** - * @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 { +namespace simd { +template struct simd8; +template struct simd8x64; +} // namespace simd +} // unnamed namespace } // namespace haswell } // namespace simdjson -#endif // SIMDJSON_HASWELL_IMPLEMENTATION_H -/* end file include/simdjson/haswell/implementation.h */ -/* begin file include/simdjson/haswell/intrinsics.h */ +#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 @@ -16170,104 +15152,23 @@ class implementation final : public simdjson::implementation { static_assert(sizeof(__m256i) <= simdjson::SIMDJSON_PADDING, "insufficient padding for haswell kernel."); #endif // SIMDJSON_HASWELL_INTRINSICS_H -/* end file include/simdjson/haswell/intrinsics.h */ +/* end file simdjson/haswell/intrinsics.h */ -// -// The rest need to be inside the region -// -/* begin file include/simdjson/haswell/begin.h */ -// redefining SIMDJSON_IMPLEMENTATION to "haswell" -// #define SIMDJSON_IMPLEMENTATION haswell -SIMDJSON_TARGET_HASWELL -/* end file include/simdjson/haswell/begin.h */ - -// Declarations -/* begin file include/simdjson/generic/dom_parser_implementation.h */ - -namespace simdjson { -namespace haswell { - -// expectation: sizeof(open_container) = 64/8. -struct open_container { - uint32_t tape_index; // where, on the tape, does the scope ([,{) begins - uint32_t count; // how many elements in the scope -}; // struct open_container - -static_assert(sizeof(open_container) == 64/8, "Open container must be 64 bits"); - -class dom_parser_implementation final : public internal::dom_parser_implementation { -public: - /** Tape location of each open { or [ */ - std::unique_ptr open_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 haswell -} // namespace simdjson - -namespace simdjson { -namespace haswell { - -inline dom_parser_implementation::dom_parser_implementation() noexcept = default; -inline dom_parser_implementation::dom_parser_implementation(dom_parser_implementation &&other) noexcept = default; -inline dom_parser_implementation &dom_parser_implementation::operator=(dom_parser_implementation &&other) noexcept = default; - -// Leaving these here so they can be inlined if so desired -inline simdjson_warn_unused error_code dom_parser_implementation::set_capacity(size_t capacity) noexcept { - if(capacity > SIMDJSON_MAXSIZE_BYTES) { 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; -} +#if !SIMDJSON_CAN_ALWAYS_RUN_HASWELL +SIMDJSON_TARGET_REGION("avx2,bmi,pclmul,lzcnt,popcnt") +#endif -} // namespace haswell -} // namespace simdjson -/* end file include/simdjson/generic/dom_parser_implementation.h */ -/* begin file include/simdjson/haswell/bitmanipulation.h */ +/* 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 { @@ -16305,7 +15206,7 @@ simdjson_inline int leading_zeroes(uint64_t 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 + // note: we do not support legacy 32-bit Windows in this kernel return __popcnt64(input_num);// Visual Studio wants two underscores } #else @@ -16330,11 +15231,17 @@ simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, } // namespace simdjson #endif // SIMDJSON_HASWELL_BITMANIPULATION_H -/* end file include/simdjson/haswell/bitmanipulation.h */ -/* begin file include/simdjson/haswell/bitmask.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 { @@ -16357,11 +15264,82 @@ simdjson_inline uint64_t prefix_xor(const uint64_t bitmask) { } // namespace simdjson #endif // SIMDJSON_HASWELL_BITMASK_H -/* end file include/simdjson/haswell/bitmask.h */ -/* begin file include/simdjson/haswell/simd.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 +#ifdef _M_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 // _M_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 { @@ -16725,8 +15703,124 @@ namespace simd { } // namespace simdjson #endif // SIMDJSON_HASWELL_SIMD_H -/* end file include/simdjson/haswell/simd.h */ -/* begin file include/simdjson/generic/jsoncharutils.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 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 ((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 + }; +} + +} // unnamed namespace +} // namespace haswell +} // namespace simdjson + +#endif // SIMDJSON_HASWELL_STRINGPARSING_DEFS_H +/* end file simdjson/haswell/stringparsing_defs.h */ +/* end file simdjson/haswell/begin.h */ +/* including simdjson/generic/amalgamated.h for haswell: #include "simdjson/generic/amalgamated.h" */ +/* begin file simdjson/generic/amalgamated.h for haswell */ +#if defined(SIMDJSON_CONDITIONAL_INCLUDE) && !defined(SIMDJSON_GENERIC_DEPENDENCIES_H) +#error simdjson/generic/dependencies.h must be included before simdjson/generic/amalgamated.h! +#endif + +/* including simdjson/generic/base.h for haswell: #include "simdjson/generic/base.h" */ +/* begin file simdjson/generic/base.h for haswell */ +#ifndef SIMDJSON_GENERIC_BASE_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_BASE_H */ +/* amalgamation skipped (editor-only): #include "simdjson/base.h" */ +/* amalgamation skipped (editor-only): // If we haven't got an implementation yet, we're in the editor, editing a generic file! Just */ +/* amalgamation skipped (editor-only): // use the most advanced one we can so the most possible stuff can be tested. */ +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_IMPLEMENTATION */ +/* amalgamation skipped (editor-only): #include "simdjson/implementation_detection.h" */ +/* amalgamation skipped (editor-only): #if SIMDJSON_IMPLEMENTATION_ICELAKE */ +/* amalgamation skipped (editor-only): #include "simdjson/icelake/begin.h" */ +/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_HASWELL */ +/* amalgamation skipped (editor-only): #include "simdjson/haswell/begin.h" */ +/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_WESTMERE */ +/* amalgamation skipped (editor-only): #include "simdjson/westmere/begin.h" */ +/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_ARM64 */ +/* amalgamation skipped (editor-only): #include "simdjson/arm64/begin.h" */ +/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_PPC64 */ +/* amalgamation skipped (editor-only): #include "simdjson/ppc64/begin.h" */ +/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_FALLBACK */ +/* amalgamation skipped (editor-only): #include "simdjson/fallback/begin.h" */ +/* amalgamation skipped (editor-only): #else */ +/* amalgamation skipped (editor-only): #error "All possible implementations (including fallback) have been disabled! simdjson will not run." */ +/* amalgamation skipped (editor-only): #endif */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_IMPLEMENTATION */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { +namespace haswell { + +struct open_container; +class dom_parser_implementation; + +/** + * The type of a JSON number + */ +enum class number_type { + floating_point_number=1, /// a binary64 number + signed_integer, /// a signed integer that fits in a 64-bit word using two's complement + unsigned_integer /// a positive integer larger or equal to 1<<63 +}; + +} // namespace haswell +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_BASE_H +/* end file simdjson/generic/base.h for haswell */ +/* including simdjson/generic/jsoncharutils.h for haswell: #include "simdjson/generic/jsoncharutils.h" */ +/* begin file simdjson/generic/jsoncharutils.h for haswell */ +#ifndef SIMDJSON_GENERIC_JSONCHARUTILS_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_JSONCHARUTILS_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/internal/jsoncharutils_tables.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/internal/numberparsing_tables.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { namespace haswell { @@ -16817,32 +15911,25 @@ static simdjson_inline uint64_t _umul128(uint64_t ab, uint64_t cd, uint64_t *hi) } #endif -using internal::value128; - -simdjson_inline value128 full_multiplication(uint64_t value1, uint64_t value2) { - value128 answer; -#if SIMDJSON_REGULAR_VISUAL_STUDIO || SIMDJSON_IS_32BITS -#ifdef _M_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 // _M_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 jsoncharutils } // unnamed namespace } // namespace haswell } // namespace simdjson -/* end file include/simdjson/generic/jsoncharutils.h */ -/* begin file include/simdjson/generic/atomparsing.h */ + +#endif // SIMDJSON_GENERIC_JSONCHARUTILS_H +/* end file simdjson/generic/jsoncharutils.h for haswell */ +/* including simdjson/generic/atomparsing.h for haswell: #include "simdjson/generic/atomparsing.h" */ +/* begin file simdjson/generic/atomparsing.h for haswell */ +#ifndef SIMDJSON_GENERIC_ATOMPARSING_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ATOMPARSING_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/jsoncharutils.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +#include + namespace simdjson { namespace haswell { namespace { @@ -16908,101 +15995,256 @@ simdjson_inline bool is_valid_null_atom(const uint8_t *src, size_t len) { } // unnamed namespace } // namespace haswell } // namespace simdjson -/* end file include/simdjson/generic/atomparsing.h */ -/* begin file include/simdjson/haswell/stringparsing.h */ -#ifndef SIMDJSON_HASWELL_STRINGPARSING_H -#define SIMDJSON_HASWELL_STRINGPARSING_H +#endif // SIMDJSON_GENERIC_ATOMPARSING_H +/* end file simdjson/generic/atomparsing.h for haswell */ +/* including simdjson/generic/dom_parser_implementation.h for haswell: #include "simdjson/generic/dom_parser_implementation.h" */ +/* begin file simdjson/generic/dom_parser_implementation.h for haswell */ +#ifndef SIMDJSON_GENERIC_DOM_PARSER_IMPLEMENTATION_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_DOM_PARSER_IMPLEMENTATION_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/internal/dom_parser_implementation.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { namespace haswell { -namespace { -using namespace simd; +// 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 -// Holds backslashes and quotes locations. -struct backslash_and_quote { +static_assert(sizeof(open_container) == 64/8, "Open container must be 64 bits"); + +class dom_parser_implementation final : public internal::dom_parser_implementation { public: - static constexpr uint32_t BYTES_PROCESSED = 32; - simdjson_inline static backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst); + /** 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{}; - 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); } + 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; - uint32_t bs_bits; - uint32_t quote_bits; -}; // struct backslash_and_quote + 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_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 } // namespace simdjson -#endif // SIMDJSON_HASWELL_STRINGPARSING_H -/* end file include/simdjson/haswell/stringparsing.h */ -/* begin file include/simdjson/haswell/numberparsing.h */ -#ifndef SIMDJSON_HASWELL_NUMBERPARSING_H -#define SIMDJSON_HASWELL_NUMBERPARSING_H - 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 +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 numberparsing } // namespace haswell } // namespace simdjson -#define SIMDJSON_SWAR_NUMBER_PARSING 1 +#endif // SIMDJSON_GENERIC_DOM_PARSER_IMPLEMENTATION_H +/* end file simdjson/generic/dom_parser_implementation.h for haswell */ +/* including simdjson/generic/implementation_simdjson_result_base.h for haswell: #include "simdjson/generic/implementation_simdjson_result_base.h" */ +/* begin file simdjson/generic/implementation_simdjson_result_base.h for haswell */ +#ifndef SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_H -/* begin file include/simdjson/generic/numberparsing.h */ -#include +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { namespace haswell { -/// @private -namespace numberparsing { +// 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 type of a JSON number + * 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. */ -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 -}; +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_inline error_code get(T &value) && noexcept; + + /** + * The error. + */ + simdjson_inline error_code error() const noexcept; + +#if SIMDJSON_EXCEPTIONS + + /** + * Get the result value. + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T& value() & noexcept(false); + + /** + * Take the result value (move it). + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T&& value() && noexcept(false); + + /** + * Take the result value (move it). + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T&& take_value() && noexcept(false); + + /** + * Cast to the value (will throw on error). + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline operator T&&() && noexcept(false); + + +#endif // SIMDJSON_EXCEPTIONS + + /** + * Get the result value. This function is safe if and only + * the error() method returns a value that evaluates to false. + */ + simdjson_inline const T& value_unsafe() const& noexcept; + /** + * 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 + +} // namespace haswell +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_H +/* end file simdjson/generic/implementation_simdjson_result_base.h for haswell */ +/* including simdjson/generic/numberparsing.h for haswell: #include "simdjson/generic/numberparsing.h" */ +/* begin file simdjson/generic/numberparsing.h for haswell */ +#ifndef SIMDJSON_GENERIC_NUMBERPARSING_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_NUMBERPARSING_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/jsoncharutils.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/internal/numberparsing_tables.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +#include +#include +#include + +namespace simdjson { +namespace haswell { +namespace numberparsing { #ifdef JSON_TEST_NUMBERS #define INVALID_NUMBER(SRC) (found_invalid_number((SRC)), NUMBER_ERROR) @@ -17154,7 +16396,7 @@ simdjson_inline bool compute_float_64(int64_t power, uint64_t i, bool negative, // 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 = jsoncharutils::full_multiplication(i, simdjson::internal::power_of_five_128[index]); + 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 @@ -17188,7 +16430,7 @@ simdjson_inline bool compute_float_64(int64_t power, uint64_t i, bool negative, // 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 = jsoncharutils::full_multiplication(i, simdjson::internal::power_of_five_128[index + 1]); + 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++; } // At this point, we might need to add at most one to firstproduct, but this @@ -18239,6 +17481,8 @@ simdjson_unused simdjson_inline simdjson_result parse_double_in_string(c } // 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; @@ -18249,189 +17493,235 @@ inline std::ostream& operator<<(std::ostream& out, number_type type) noexcept { return out; } -} // namespace numberparsing } // namespace haswell } // namespace simdjson -/* end file include/simdjson/generic/numberparsing.h */ - -#endif // SIMDJSON_HASWELL_NUMBERPARSING_H -/* end file include/simdjson/haswell/numberparsing.h */ -/* begin file include/simdjson/haswell/end.h */ -SIMDJSON_UNTARGET_HASWELL -/* end file include/simdjson/haswell/end.h */ -#endif // SIMDJSON_IMPLEMENTATION_HASWELL -#endif // SIMDJSON_HASWELL_COMMON_H -/* end file include/simdjson/haswell.h */ -/* begin file include/simdjson/ppc64.h */ -#ifndef SIMDJSON_PPC64_H -#define SIMDJSON_PPC64_H +#endif // SIMDJSON_GENERIC_NUMBERPARSING_H +/* end file simdjson/generic/numberparsing.h for haswell */ +/* including simdjson/generic/implementation_simdjson_result_base-inl.h for haswell: #include "simdjson/generic/implementation_simdjson_result_base-inl.h" */ +/* begin file simdjson/generic/implementation_simdjson_result_base-inl.h for haswell */ +#ifndef SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_INL_H -#if SIMDJSON_IMPLEMENTATION_PPC64 +/* 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 { -/** - * Implementation for ALTIVEC (PPC64). - */ -namespace ppc64 { -} // namespace ppc64 -} // namespace simdjson - -/* begin file include/simdjson/ppc64/implementation.h */ -#ifndef SIMDJSON_PPC64_IMPLEMENTATION_H -#define SIMDJSON_PPC64_IMPLEMENTATION_H - +namespace haswell { -namespace simdjson { -namespace ppc64 { +// +// internal::implementation_simdjson_result_base inline implementation +// -namespace { -using namespace simdjson; -using namespace simdjson::dom; -} // namespace +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; + } +} -/** - * @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; -}; +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; +} -} // namespace ppc64 -} // namespace simdjson +template +simdjson_inline error_code implementation_simdjson_result_base::error() const noexcept { + return this->second; +} -#endif // SIMDJSON_PPC64_IMPLEMENTATION_H -/* end file include/simdjson/ppc64/implementation.h */ +#if SIMDJSON_EXCEPTIONS -/* begin file include/simdjson/ppc64/begin.h */ -// redefining SIMDJSON_IMPLEMENTATION to "ppc64" -// #define SIMDJSON_IMPLEMENTATION ppc64 -/* end file include/simdjson/ppc64/begin.h */ +template +simdjson_inline T& implementation_simdjson_result_base::value() & noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return this->first; +} -// Declarations -/* begin file include/simdjson/generic/dom_parser_implementation.h */ +template +simdjson_inline T&& implementation_simdjson_result_base::value() && noexcept(false) { + return std::forward>(*this).take_value(); +} -namespace simdjson { -namespace ppc64 { +template +simdjson_inline T&& implementation_simdjson_result_base::take_value() && noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return std::forward(this->first); +} -// 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 +template +simdjson_inline implementation_simdjson_result_base::operator T&&() && noexcept(false) { + return std::forward>(*this).take_value(); +} -static_assert(sizeof(open_container) == 64/8, "Open container must be 64 bits"); +#endif // SIMDJSON_EXCEPTIONS -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{}; +template +simdjson_inline const T& implementation_simdjson_result_base::value_unsafe() const& noexcept { + return this->first; +} - 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; +template +simdjson_inline T& implementation_simdjson_result_base::value_unsafe() & noexcept { + return this->first; +} - 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); +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 ppc64 +} // namespace haswell } // namespace simdjson -namespace simdjson { -namespace ppc64 { +#endif // SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_INL_H +/* end file simdjson/generic/implementation_simdjson_result_base-inl.h for haswell */ +/* end file simdjson/generic/amalgamated.h for haswell */ +/* including simdjson/haswell/end.h: #include "simdjson/haswell/end.h" */ +/* begin file simdjson/haswell/end.h */ +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #include "simdjson/haswell/base.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +#if !SIMDJSON_CAN_ALWAYS_RUN_HASWELL +SIMDJSON_UNTARGET_REGION +#endif -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; +/* undefining SIMDJSON_IMPLEMENTATION from "haswell" */ +#undef SIMDJSON_IMPLEMENTATION +/* end file simdjson/haswell/end.h */ -// 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; +#endif // SIMDJSON_HASWELL_H +/* end file simdjson/haswell.h */ +#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(icelake) +/* including simdjson/icelake.h: #include "simdjson/icelake.h" */ +/* begin file simdjson/icelake.h */ +#ifndef SIMDJSON_ICELAKE_H +#define SIMDJSON_ICELAKE_H - _capacity = capacity; - return SUCCESS; -} +/* 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 -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; } +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #include "simdjson/base.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ - _max_depth = max_depth; - return SUCCESS; -} +// 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 { -} // namespace ppc64 +class implementation; + +} // namespace icelake } // namespace simdjson -/* end file include/simdjson/generic/dom_parser_implementation.h */ -/* begin file include/simdjson/ppc64/intrinsics.h */ -#ifndef SIMDJSON_PPC64_INTRINSICS_H -#define SIMDJSON_PPC64_INTRINSICS_H +#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 -// This should be the correct header whether -// you use visual studio or other compilers. -#include +/* 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 */ -// These are defined by altivec.h in GCC toolchain, it is safe to undef them. -#ifdef bool -#undef bool -#endif +#if SIMDJSON_VISUAL_STUDIO +// under clang within visual studio, this will include +#include // visual studio or clang +#else +#include // elsewhere +#endif // SIMDJSON_VISUAL_STUDIO -#ifdef vector -#undef vector +#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 -static_assert(sizeof(__vector unsigned char) <= simdjson::SIMDJSON_PADDING, "insufficient padding for ppc64"); +/* 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 -#endif // SIMDJSON_PPC64_INTRINSICS_H -/* end file include/simdjson/ppc64/intrinsics.h */ -/* begin file include/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/icelake/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/icelake/intrinsics.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { -namespace ppc64 { +namespace icelake { namespace { // We sometimes call trailing_zero on inputs that are zero, @@ -18444,52 +17734,43 @@ SIMDJSON_NO_SANITIZE_UNDEFINED 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 (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 input_num & (input_num - 1); + return _blsr_u64(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; -#else - return __builtin_clzll(input_num); -#endif // SIMDJSON_REGULAR_VISUAL_STUDIO + return int(_lzcnt_u64(input_num)); } #if SIMDJSON_REGULAR_VISUAL_STUDIO -simdjson_inline int count_ones(uint64_t input_num) { +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 + 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 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) { + uint64_t *result) { #if SIMDJSON_REGULAR_VISUAL_STUDIO - *result = value1 + value2; - return *result < value1; + return _addcarry_u64(0, value1, value2, + reinterpret_cast(result)); #else return __builtin_uaddll_overflow(value1, value2, reinterpret_cast(result)); @@ -18497,4410 +17778,60228 @@ simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, } } // unnamed namespace -} // namespace ppc64 +} // namespace icelake } // namespace simdjson -#endif // SIMDJSON_PPC64_BITMANIPULATION_H -/* end file include/simdjson/ppc64/bitmanipulation.h */ -/* begin file include/simdjson/ppc64/bitmask.h */ -#ifndef SIMDJSON_PPC64_BITMASK_H -#define SIMDJSON_PPC64_BITMASK_H +#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 ppc64 { +namespace icelake { namespace { // -// Perform a "cumulative bitwise xor," flipping bits each time a 1 is -// encountered. +// 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; +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 ppc64 +} // namespace icelake } // namespace simdjson -#endif -/* end file include/simdjson/ppc64/bitmask.h */ -/* begin file include/simdjson/ppc64/simd.h */ -#ifndef SIMDJSON_PPC64_SIMD_H -#define SIMDJSON_PPC64_SIMD_H +#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 -#include +/* 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 */ -namespace simdjson { -namespace ppc64 { -namespace { -namespace simd { +#if defined(__GNUC__) && !defined(__clang__) +#if __GNUC__ == 8 +#define SIMDJSON_GCC8 1 +#endif // __GNUC__ == 8 +#endif // defined(__GNUC__) && !defined(__clang__) -using __m128i = __vector unsigned char; +#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 -template struct base { - __m128i value; - // Zero constructor - simdjson_inline base() : value{__m128i()} {} - // Conversion from SIMD register - simdjson_inline base(const __m128i _value) : value(_value) {} +namespace simdjson { +namespace icelake { +namespace { +namespace simd { - // Conversion to SIMD register - simdjson_inline operator const __m128i &() const { - return this->value; - } - simdjson_inline operator __m128i &() { return this->value; } + // Forward-declared so they can be used by splat and friends. + template + struct base { + __m512i 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; - } -}; + // Zero constructor + simdjson_inline base() : value{__m512i()} {} -// Forward-declared so they can be used by splat and friends. -template struct simd8; + // Conversion from SIMD register + simdjson_inline base(const __m512i _value) : value(_value) {} -template > -struct base8 : base> { - typedef uint16_t bitmask_t; - typedef uint32_t bitmask2_t; + // Conversion to SIMD register + simdjson_inline operator const __m512i&() const { return this->value; } + simdjson_inline operator __m512i&() { return this->value; } - simdjson_inline base8() : base>() {} - simdjson_inline base8(const __m128i _value) : base>(_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; } + }; - friend simdjson_inline Mask operator==(const simd8 lhs, const simd8 rhs) { - return (__m128i)vec_cmpeq(lhs.value, (__m128i)rhs); - } + // Forward-declared so they can be used by splat and friends. + template + struct simd8; - static const int SIZE = sizeof(base>::value); + template> + struct base8: base> { + typedef uint32_t bitmask_t; + typedef uint64_t bitmask2_t; - 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; - } -}; + simdjson_inline base8() : base>() {} + simdjson_inline base8(const __m512i _value) : base>(_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))); - } + friend simdjson_really_inline uint64_t operator==(const simd8 lhs, const simd8 rhs) { + return _mm512_cmpeq_epi8_mask(lhs, rhs); + } - simdjson_inline simd8() : base8() {} - simdjson_inline simd8(const __m128i _value) - : base8(_value) {} - // Splat constructor - simdjson_inline simd8(bool _value) - : base8(splat(_value)) {} + static const int SIZE = sizeof(base::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}; + 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); + } + }; - 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); - } -}; + // 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))); } -template struct base8_numeric : base8 { + 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 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 + )); + } + }; + + // 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]; + + 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 = 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 ((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 + }; +} + +} // 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 +#ifdef _M_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 // _M_ARM64 +#else // SIMDJSON_REGULAR_VISUAL_STUDIO || SIMDJSON_IS_32BITS + __uint128_t r = (static_cast<__uint128_t>(value1)) * value2; + answer.low = uint64_t(r); + answer.high = uint64_t(r >> 64); +#endif + return answer; +} + +} // namespace numberparsing +} // namespace icelake +} // namespace simdjson + +#define SIMDJSON_SWAR_NUMBER_PARSING 1 + +#endif // SIMDJSON_ICELAKE_NUMBERPARSING_DEFS_H +/* end file simdjson/icelake/numberparsing_defs.h */ +/* end file simdjson/icelake/begin.h */ +/* including simdjson/generic/amalgamated.h for icelake: #include "simdjson/generic/amalgamated.h" */ +/* begin file simdjson/generic/amalgamated.h for icelake */ +#if defined(SIMDJSON_CONDITIONAL_INCLUDE) && !defined(SIMDJSON_GENERIC_DEPENDENCIES_H) +#error simdjson/generic/dependencies.h must be included before simdjson/generic/amalgamated.h! +#endif + +/* including simdjson/generic/base.h for icelake: #include "simdjson/generic/base.h" */ +/* begin file simdjson/generic/base.h for icelake */ +#ifndef SIMDJSON_GENERIC_BASE_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_BASE_H */ +/* amalgamation skipped (editor-only): #include "simdjson/base.h" */ +/* amalgamation skipped (editor-only): // If we haven't got an implementation yet, we're in the editor, editing a generic file! Just */ +/* amalgamation skipped (editor-only): // use the most advanced one we can so the most possible stuff can be tested. */ +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_IMPLEMENTATION */ +/* amalgamation skipped (editor-only): #include "simdjson/implementation_detection.h" */ +/* amalgamation skipped (editor-only): #if SIMDJSON_IMPLEMENTATION_ICELAKE */ +/* amalgamation skipped (editor-only): #include "simdjson/icelake/begin.h" */ +/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_HASWELL */ +/* amalgamation skipped (editor-only): #include "simdjson/haswell/begin.h" */ +/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_WESTMERE */ +/* amalgamation skipped (editor-only): #include "simdjson/westmere/begin.h" */ +/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_ARM64 */ +/* amalgamation skipped (editor-only): #include "simdjson/arm64/begin.h" */ +/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_PPC64 */ +/* amalgamation skipped (editor-only): #include "simdjson/ppc64/begin.h" */ +/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_FALLBACK */ +/* amalgamation skipped (editor-only): #include "simdjson/fallback/begin.h" */ +/* amalgamation skipped (editor-only): #else */ +/* amalgamation skipped (editor-only): #error "All possible implementations (including fallback) have been disabled! simdjson will not run." */ +/* amalgamation skipped (editor-only): #endif */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_IMPLEMENTATION */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { +namespace icelake { + +struct open_container; +class dom_parser_implementation; + +/** + * The type of a JSON number + */ +enum class number_type { + floating_point_number=1, /// a binary64 number + signed_integer, /// a signed integer that fits in a 64-bit word using two's complement + unsigned_integer /// a positive integer larger or equal to 1<<63 +}; + +} // namespace icelake +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_BASE_H +/* end file simdjson/generic/base.h for icelake */ +/* including simdjson/generic/jsoncharutils.h for icelake: #include "simdjson/generic/jsoncharutils.h" */ +/* begin file simdjson/generic/jsoncharutils.h for icelake */ +#ifndef SIMDJSON_GENERIC_JSONCHARUTILS_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_JSONCHARUTILS_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/internal/jsoncharutils_tables.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/internal/numberparsing_tables.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { +namespace icelake { +namespace { +namespace jsoncharutils { + +// return non-zero if not a structural or whitespace char +// zero otherwise +simdjson_inline uint32_t is_not_structural_or_whitespace(uint8_t c) { + return internal::structural_or_whitespace_negated[c]; +} + +simdjson_inline uint32_t is_structural_or_whitespace(uint8_t c) { + return internal::structural_or_whitespace[c]; +} + +// returns a value with the high 16 bits set if not valid +// otherwise returns the conversion of the 4 hex digits at src into the bottom +// 16 bits of the 32-bit return register +// +// see +// https://lemire.me/blog/2019/04/17/parsing-short-hexadecimal-strings-efficiently/ +static inline uint32_t hex_to_u32_nocheck( + const uint8_t *src) { // strictly speaking, static inline is a C-ism + uint32_t v1 = internal::digit_to_val32[630 + src[0]]; + uint32_t v2 = internal::digit_to_val32[420 + src[1]]; + uint32_t v3 = internal::digit_to_val32[210 + src[2]]; + uint32_t v4 = internal::digit_to_val32[0 + src[3]]; + return v1 | v2 | v3 | v4; +} + +// given a code point cp, writes to c +// the utf-8 code, outputting the length in +// bytes, if the length is zero, the code point +// is invalid +// +// This can possibly be made faster using pdep +// and clz and table lookups, but JSON documents +// have few escaped code points, and the following +// function looks cheap. +// +// Note: we assume that surrogates are treated separately +// +simdjson_inline size_t codepoint_to_utf8(uint32_t cp, uint8_t *c) { + if (cp <= 0x7F) { + c[0] = uint8_t(cp); + return 1; // ascii + } + if (cp <= 0x7FF) { + c[0] = uint8_t((cp >> 6) + 192); + c[1] = uint8_t((cp & 63) + 128); + return 2; // universal plane + // Surrogates are treated elsewhere... + //} //else if (0xd800 <= cp && cp <= 0xdfff) { + // return 0; // surrogates // could put assert here + } else if (cp <= 0xFFFF) { + c[0] = uint8_t((cp >> 12) + 224); + c[1] = uint8_t(((cp >> 6) & 63) + 128); + c[2] = uint8_t((cp & 63) + 128); + return 3; + } else if (cp <= 0x10FFFF) { // if you know you have a valid code point, this + // is not needed + c[0] = uint8_t((cp >> 18) + 240); + c[1] = uint8_t(((cp >> 12) & 63) + 128); + c[2] = uint8_t(((cp >> 6) & 63) + 128); + c[3] = uint8_t((cp & 63) + 128); + return 4; + } + // will return 0 when the code point was too large. + return 0; // bad r +} + +#if SIMDJSON_IS_32BITS // _umul128 for x86, arm +// this is a slow emulation routine for 32-bit +// +static simdjson_inline uint64_t __emulu(uint32_t x, uint32_t y) { + return x * (uint64_t)y; +} +static simdjson_inline uint64_t _umul128(uint64_t ab, uint64_t cd, uint64_t *hi) { + uint64_t ad = __emulu((uint32_t)(ab >> 32), (uint32_t)cd); + uint64_t bd = __emulu((uint32_t)ab, (uint32_t)cd); + uint64_t adbc = ad + __emulu((uint32_t)ab, (uint32_t)(cd >> 32)); + uint64_t adbc_carry = !!(adbc < ad); + uint64_t lo = bd + (adbc << 32); + *hi = __emulu((uint32_t)(ab >> 32), (uint32_t)(cd >> 32)) + (adbc >> 32) + + (adbc_carry << 32) + !!(lo < bd); + return lo; +} +#endif + +} // namespace jsoncharutils +} // unnamed namespace +} // namespace icelake +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_JSONCHARUTILS_H +/* end file simdjson/generic/jsoncharutils.h for icelake */ +/* including simdjson/generic/atomparsing.h for icelake: #include "simdjson/generic/atomparsing.h" */ +/* begin file simdjson/generic/atomparsing.h for icelake */ +#ifndef SIMDJSON_GENERIC_ATOMPARSING_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ATOMPARSING_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/jsoncharutils.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +#include + +namespace simdjson { +namespace icelake { +namespace { +/// @private +namespace atomparsing { + +// The string_to_uint32 is exclusively used to map literal strings to 32-bit values. +// We use memcpy instead of a pointer cast to avoid undefined behaviors since we cannot +// be certain that the character pointer will be properly aligned. +// You might think that using memcpy makes this function expensive, but you'd be wrong. +// All decent optimizing compilers (GCC, clang, Visual Studio) will compile string_to_uint32("false"); +// to the compile-time constant 1936482662. +simdjson_inline uint32_t string_to_uint32(const char* str) { uint32_t val; std::memcpy(&val, str, sizeof(uint32_t)); return val; } + + +// 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 icelake +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_ATOMPARSING_H +/* end file simdjson/generic/atomparsing.h for icelake */ +/* including simdjson/generic/dom_parser_implementation.h for icelake: #include "simdjson/generic/dom_parser_implementation.h" */ +/* begin file simdjson/generic/dom_parser_implementation.h for icelake */ +#ifndef SIMDJSON_GENERIC_DOM_PARSER_IMPLEMENTATION_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_DOM_PARSER_IMPLEMENTATION_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/internal/dom_parser_implementation.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { +namespace icelake { + +// expectation: sizeof(open_container) = 64/8. +struct open_container { + uint32_t tape_index; // where, on the tape, does the scope ([,{) begins + uint32_t count; // how many elements in the scope +}; // struct open_container + +static_assert(sizeof(open_container) == 64/8, "Open container must be 64 bits"); + +class dom_parser_implementation final : public internal::dom_parser_implementation { +public: + /** Tape location of each open { or [ */ + std::unique_ptr open_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 icelake +} // namespace simdjson + +namespace simdjson { +namespace icelake { + +inline dom_parser_implementation::dom_parser_implementation() noexcept = default; +inline dom_parser_implementation::dom_parser_implementation(dom_parser_implementation &&other) noexcept = default; +inline dom_parser_implementation &dom_parser_implementation::operator=(dom_parser_implementation &&other) noexcept = default; + +// Leaving these here so they can be inlined if so desired +inline simdjson_warn_unused error_code dom_parser_implementation::set_capacity(size_t capacity) noexcept { + if(capacity > SIMDJSON_MAXSIZE_BYTES) { 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 icelake +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_DOM_PARSER_IMPLEMENTATION_H +/* end file simdjson/generic/dom_parser_implementation.h for icelake */ +/* including simdjson/generic/implementation_simdjson_result_base.h for icelake: #include "simdjson/generic/implementation_simdjson_result_base.h" */ +/* begin file simdjson/generic/implementation_simdjson_result_base.h for icelake */ +#ifndef SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { +namespace icelake { + +// This is a near copy of include/error.h's implementation_simdjson_result_base, except it doesn't use std::pair +// so we can avoid inlining errors +// TODO reconcile these! +/** + * The result of a simdjson operation that could fail. + * + * Gives the option of reading error codes, or throwing an exception by casting to the desired result. + * + * This is a base class for implementations that want to add functions to the result type for + * chaining. + * + * Override like: + * + * struct simdjson_result : 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_inline error_code get(T &value) && noexcept; + + /** + * The error. + */ + simdjson_inline error_code error() const noexcept; + +#if SIMDJSON_EXCEPTIONS + + /** + * Get the result value. + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T& value() & noexcept(false); + + /** + * Take the result value (move it). + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T&& value() && noexcept(false); + + /** + * Take the result value (move it). + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T&& take_value() && noexcept(false); + + /** + * Cast to the value (will throw on error). + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline operator T&&() && noexcept(false); + + +#endif // SIMDJSON_EXCEPTIONS + + /** + * Get the result value. This function is safe if and only + * the error() method returns a value that evaluates to false. + */ + simdjson_inline const T& value_unsafe() const& noexcept; + /** + * 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 + +} // namespace icelake +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_H +/* end file simdjson/generic/implementation_simdjson_result_base.h for icelake */ +/* including simdjson/generic/numberparsing.h for icelake: #include "simdjson/generic/numberparsing.h" */ +/* begin file simdjson/generic/numberparsing.h for icelake */ +#ifndef SIMDJSON_GENERIC_NUMBERPARSING_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_NUMBERPARSING_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/jsoncharutils.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/internal/numberparsing_tables.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +#include +#include +#include + +namespace simdjson { +namespace icelake { +namespace numberparsing { + +#ifdef JSON_TEST_NUMBERS +#define INVALID_NUMBER(SRC) (found_invalid_number((SRC)), NUMBER_ERROR) +#define WRITE_INTEGER(VALUE, SRC, WRITER) (found_integer((VALUE), (SRC)), (WRITER).append_s64((VALUE))) +#define WRITE_UNSIGNED(VALUE, SRC, WRITER) (found_unsigned_integer((VALUE), (SRC)), (WRITER).append_u64((VALUE))) +#define WRITE_DOUBLE(VALUE, SRC, WRITER) (found_float((VALUE), (SRC)), (WRITER).append_double((VALUE))) +#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)) +#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 + // 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; + + + // 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. + + // 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. In very rare cases, even that + // will not suffice, though it is seemingly very hard to find such a scenario. + // + // 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++; } + // At this point, we might need to add at most one to firstproduct, but this + // can only change the value of firstproduct.high if firstproduct.low is maximal. + if(simdjson_unlikely(firstproduct.low == 0xFFFFFFFFFFFFFFFF)) { + // This is very unlikely, but if so, we need to do much more work! + return false; + } + } + 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 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_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 doesn't 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 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 */ +template +error_code slow_float_parsing(simdjson_unused const uint8_t * src, W writer) { + double d; + if (parse_float_fallback(src, &d)) { + writer.append_double(d); + 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: This makes a *copy* of the writer and passes it to slow_float_parsing. This happens + // because slow_float_parsing is a non-inlined function. 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. + // This is what forces the skip_double, as well. + error_code error = slow_float_parsing(src, writer); + writer.skip_double(); + 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; +} + +// 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 +} + +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_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 INVALID_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 INVALID_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 doesn't 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 = start_digits-src > 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++; } + if ( p == src ) { return NUMBER_ERROR; } + if (jsoncharutils::is_structural_or_whitespace(*p)) { + // We have an integer. + // If the number is negative and valid, it must be a signed integer. + if(negative) { return number_type::signed_integer; } + // We want values larger or equal to 9223372036854775808 to be unsigned + // integers, and the other values to be signed integers. + int digit_count = int(p - src); + if(digit_count >= 19) { + const uint8_t * smaller_big_integer = reinterpret_cast("9223372036854775808"); + if((digit_count >= 20) || (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 = start_digits-src > 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; + default: SIMDJSON_UNREACHABLE(); + } + return out; +} + +} // namespace icelake +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_NUMBERPARSING_H +/* end file simdjson/generic/numberparsing.h for icelake */ + +/* including simdjson/generic/implementation_simdjson_result_base-inl.h for icelake: #include "simdjson/generic/implementation_simdjson_result_base-inl.h" */ +/* begin file simdjson/generic/implementation_simdjson_result_base-inl.h for icelake */ +#ifndef SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_INL_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_INL_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { +namespace icelake { + +// +// internal::implementation_simdjson_result_base 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_inline error_code implementation_simdjson_result_base::error() const noexcept { + return this->second; +} + +#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::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 icelake +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_INL_H +/* end file simdjson/generic/implementation_simdjson_result_base-inl.h for icelake */ +/* end file simdjson/generic/amalgamated.h for icelake */ +/* including simdjson/icelake/end.h: #include "simdjson/icelake/end.h" */ +/* begin file simdjson/icelake/end.h */ +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #include "simdjson/icelake/base.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +#if !SIMDJSON_CAN_ALWAYS_RUN_ICELAKE +SIMDJSON_UNTARGET_REGION +#endif + +/* undefining SIMDJSON_IMPLEMENTATION from "icelake" */ +#undef SIMDJSON_IMPLEMENTATION +/* end file simdjson/icelake/end.h */ + +#endif // SIMDJSON_ICELAKE_H +/* end file simdjson/icelake.h */ +#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(ppc64) +/* including simdjson/ppc64.h: #include "simdjson/ppc64.h" */ +/* begin file simdjson/ppc64.h */ +#ifndef SIMDJSON_PPC64_H +#define SIMDJSON_PPC64_H + +/* including simdjson/ppc64/begin.h: #include "simdjson/ppc64/begin.h" */ +/* begin file simdjson/ppc64/begin.h */ +/* defining SIMDJSON_IMPLEMENTATION to "ppc64" */ +#define SIMDJSON_IMPLEMENTATION ppc64 +/* including simdjson/ppc64/base.h: #include "simdjson/ppc64/base.h" */ +/* begin file simdjson/ppc64/base.h */ +#ifndef SIMDJSON_PPC64_BASE_H +#define SIMDJSON_PPC64_BASE_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #include "simdjson/base.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { +/** + * Implementation for ALTIVEC (PPC64). + */ +namespace ppc64 { + +class implementation; + +namespace { +namespace simd { +template 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 +#ifdef _M_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 // _M_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 + +#define SIMDJSON_SWAR_NUMBER_PARSING 1 + +#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]; + + 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 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 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 + }; +} + +} // unnamed namespace +} // namespace ppc64 +} // namespace simdjson + +#endif // SIMDJSON_PPC64_STRINGPARSING_DEFS_H +/* end file simdjson/ppc64/stringparsing_defs.h */ + +#define SIMDJSON_SKIP_BACKSLASH_SHORT_CIRCUIT 1 +/* end file simdjson/ppc64/begin.h */ +/* including simdjson/generic/amalgamated.h for ppc64: #include "simdjson/generic/amalgamated.h" */ +/* begin file simdjson/generic/amalgamated.h for ppc64 */ +#if defined(SIMDJSON_CONDITIONAL_INCLUDE) && !defined(SIMDJSON_GENERIC_DEPENDENCIES_H) +#error simdjson/generic/dependencies.h must be included before simdjson/generic/amalgamated.h! +#endif + +/* including simdjson/generic/base.h for ppc64: #include "simdjson/generic/base.h" */ +/* begin file simdjson/generic/base.h for ppc64 */ +#ifndef SIMDJSON_GENERIC_BASE_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_BASE_H */ +/* amalgamation skipped (editor-only): #include "simdjson/base.h" */ +/* amalgamation skipped (editor-only): // If we haven't got an implementation yet, we're in the editor, editing a generic file! Just */ +/* amalgamation skipped (editor-only): // use the most advanced one we can so the most possible stuff can be tested. */ +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_IMPLEMENTATION */ +/* amalgamation skipped (editor-only): #include "simdjson/implementation_detection.h" */ +/* amalgamation skipped (editor-only): #if SIMDJSON_IMPLEMENTATION_ICELAKE */ +/* amalgamation skipped (editor-only): #include "simdjson/icelake/begin.h" */ +/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_HASWELL */ +/* amalgamation skipped (editor-only): #include "simdjson/haswell/begin.h" */ +/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_WESTMERE */ +/* amalgamation skipped (editor-only): #include "simdjson/westmere/begin.h" */ +/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_ARM64 */ +/* amalgamation skipped (editor-only): #include "simdjson/arm64/begin.h" */ +/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_PPC64 */ +/* amalgamation skipped (editor-only): #include "simdjson/ppc64/begin.h" */ +/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_FALLBACK */ +/* amalgamation skipped (editor-only): #include "simdjson/fallback/begin.h" */ +/* amalgamation skipped (editor-only): #else */ +/* amalgamation skipped (editor-only): #error "All possible implementations (including fallback) have been disabled! simdjson will not run." */ +/* amalgamation skipped (editor-only): #endif */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_IMPLEMENTATION */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { +namespace ppc64 { + +struct open_container; +class dom_parser_implementation; + +/** + * The type of a JSON number + */ +enum class number_type { + floating_point_number=1, /// a binary64 number + signed_integer, /// a signed integer that fits in a 64-bit word using two's complement + unsigned_integer /// a positive integer larger or equal to 1<<63 +}; + +} // namespace ppc64 +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_BASE_H +/* end file simdjson/generic/base.h for ppc64 */ +/* including simdjson/generic/jsoncharutils.h for ppc64: #include "simdjson/generic/jsoncharutils.h" */ +/* begin file simdjson/generic/jsoncharutils.h for ppc64 */ +#ifndef SIMDJSON_GENERIC_JSONCHARUTILS_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_JSONCHARUTILS_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/internal/jsoncharutils_tables.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/internal/numberparsing_tables.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { +namespace ppc64 { +namespace { +namespace jsoncharutils { + +// return non-zero if not a structural or whitespace char +// zero otherwise +simdjson_inline uint32_t is_not_structural_or_whitespace(uint8_t c) { + return internal::structural_or_whitespace_negated[c]; +} + +simdjson_inline uint32_t is_structural_or_whitespace(uint8_t c) { + return internal::structural_or_whitespace[c]; +} + +// returns a value with the high 16 bits set if not valid +// otherwise returns the conversion of the 4 hex digits at src into the bottom +// 16 bits of the 32-bit return register +// +// see +// https://lemire.me/blog/2019/04/17/parsing-short-hexadecimal-strings-efficiently/ +static inline uint32_t hex_to_u32_nocheck( + const uint8_t *src) { // strictly speaking, static inline is a C-ism + uint32_t v1 = internal::digit_to_val32[630 + src[0]]; + uint32_t v2 = internal::digit_to_val32[420 + src[1]]; + uint32_t v3 = internal::digit_to_val32[210 + src[2]]; + uint32_t v4 = internal::digit_to_val32[0 + src[3]]; + return v1 | v2 | v3 | v4; +} + +// given a code point cp, writes to c +// the utf-8 code, outputting the length in +// bytes, if the length is zero, the code point +// is invalid +// +// This can possibly be made faster using pdep +// and clz and table lookups, but JSON documents +// have few escaped code points, and the following +// function looks cheap. +// +// Note: we assume that surrogates are treated separately +// +simdjson_inline size_t codepoint_to_utf8(uint32_t cp, uint8_t *c) { + if (cp <= 0x7F) { + c[0] = uint8_t(cp); + return 1; // ascii + } + if (cp <= 0x7FF) { + c[0] = uint8_t((cp >> 6) + 192); + c[1] = uint8_t((cp & 63) + 128); + return 2; // universal plane + // Surrogates are treated elsewhere... + //} //else if (0xd800 <= cp && cp <= 0xdfff) { + // return 0; // surrogates // could put assert here + } else if (cp <= 0xFFFF) { + c[0] = uint8_t((cp >> 12) + 224); + c[1] = uint8_t(((cp >> 6) & 63) + 128); + c[2] = uint8_t((cp & 63) + 128); + return 3; + } else if (cp <= 0x10FFFF) { // if you know you have a valid code point, this + // is not needed + c[0] = uint8_t((cp >> 18) + 240); + c[1] = uint8_t(((cp >> 12) & 63) + 128); + c[2] = uint8_t(((cp >> 6) & 63) + 128); + c[3] = uint8_t((cp & 63) + 128); + return 4; + } + // will return 0 when the code point was too large. + return 0; // bad r +} + +#if SIMDJSON_IS_32BITS // _umul128 for x86, arm +// this is a slow emulation routine for 32-bit +// +static simdjson_inline uint64_t __emulu(uint32_t x, uint32_t y) { + return x * (uint64_t)y; +} +static simdjson_inline uint64_t _umul128(uint64_t ab, uint64_t cd, uint64_t *hi) { + uint64_t ad = __emulu((uint32_t)(ab >> 32), (uint32_t)cd); + uint64_t bd = __emulu((uint32_t)ab, (uint32_t)cd); + uint64_t adbc = ad + __emulu((uint32_t)ab, (uint32_t)(cd >> 32)); + uint64_t adbc_carry = !!(adbc < ad); + uint64_t lo = bd + (adbc << 32); + *hi = __emulu((uint32_t)(ab >> 32), (uint32_t)(cd >> 32)) + (adbc >> 32) + + (adbc_carry << 32) + !!(lo < bd); + return lo; +} +#endif + +} // namespace jsoncharutils +} // unnamed namespace +} // namespace ppc64 +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_JSONCHARUTILS_H +/* end file simdjson/generic/jsoncharutils.h for ppc64 */ +/* including simdjson/generic/atomparsing.h for ppc64: #include "simdjson/generic/atomparsing.h" */ +/* begin file simdjson/generic/atomparsing.h for ppc64 */ +#ifndef SIMDJSON_GENERIC_ATOMPARSING_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ATOMPARSING_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/jsoncharutils.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +#include + +namespace simdjson { +namespace ppc64 { +namespace { +/// @private +namespace atomparsing { + +// The string_to_uint32 is exclusively used to map literal strings to 32-bit values. +// We use memcpy instead of a pointer cast to avoid undefined behaviors since we cannot +// be certain that the character pointer will be properly aligned. +// You might think that using memcpy makes this function expensive, but you'd be wrong. +// All decent optimizing compilers (GCC, clang, Visual Studio) will compile string_to_uint32("false"); +// to the compile-time constant 1936482662. +simdjson_inline uint32_t string_to_uint32(const char* str) { uint32_t val; std::memcpy(&val, str, sizeof(uint32_t)); return val; } + + +// 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 ppc64 +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_ATOMPARSING_H +/* end file simdjson/generic/atomparsing.h for ppc64 */ +/* including simdjson/generic/dom_parser_implementation.h for ppc64: #include "simdjson/generic/dom_parser_implementation.h" */ +/* begin file simdjson/generic/dom_parser_implementation.h for ppc64 */ +#ifndef SIMDJSON_GENERIC_DOM_PARSER_IMPLEMENTATION_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_DOM_PARSER_IMPLEMENTATION_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/internal/dom_parser_implementation.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { +namespace ppc64 { + +// expectation: sizeof(open_container) = 64/8. +struct open_container { + uint32_t tape_index; // where, on the tape, does the scope ([,{) begins + uint32_t count; // how many elements in the scope +}; // struct open_container + +static_assert(sizeof(open_container) == 64/8, "Open container must be 64 bits"); + +class dom_parser_implementation final : public internal::dom_parser_implementation { +public: + /** Tape location of each open { or [ */ + std::unique_ptr open_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 ppc64 +} // namespace simdjson + +namespace simdjson { +namespace ppc64 { + +inline dom_parser_implementation::dom_parser_implementation() noexcept = default; +inline dom_parser_implementation::dom_parser_implementation(dom_parser_implementation &&other) noexcept = default; +inline dom_parser_implementation &dom_parser_implementation::operator=(dom_parser_implementation &&other) noexcept = default; + +// Leaving these here so they can be inlined if so desired +inline simdjson_warn_unused error_code dom_parser_implementation::set_capacity(size_t capacity) noexcept { + if(capacity > SIMDJSON_MAXSIZE_BYTES) { 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 ppc64 +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_DOM_PARSER_IMPLEMENTATION_H +/* end file simdjson/generic/dom_parser_implementation.h for ppc64 */ +/* including simdjson/generic/implementation_simdjson_result_base.h for ppc64: #include "simdjson/generic/implementation_simdjson_result_base.h" */ +/* begin file simdjson/generic/implementation_simdjson_result_base.h for ppc64 */ +#ifndef SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { +namespace ppc64 { + +// This is a near copy of include/error.h's implementation_simdjson_result_base, except it doesn't use std::pair +// so we can avoid inlining errors +// TODO reconcile these! +/** + * The result of a simdjson operation that could fail. + * + * Gives the option of reading error codes, or throwing an exception by casting to the desired result. + * + * This is a base class for implementations that want to add functions to the result type for + * chaining. + * + * Override like: + * + * struct simdjson_result : 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_inline error_code get(T &value) && noexcept; + + /** + * The error. + */ + simdjson_inline error_code error() const noexcept; + +#if SIMDJSON_EXCEPTIONS + + /** + * Get the result value. + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T& value() & noexcept(false); + + /** + * Take the result value (move it). + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T&& value() && noexcept(false); + + /** + * Take the result value (move it). + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T&& take_value() && noexcept(false); + + /** + * Cast to the value (will throw on error). + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline operator T&&() && noexcept(false); + + +#endif // SIMDJSON_EXCEPTIONS + + /** + * Get the result value. This function is safe if and only + * the error() method returns a value that evaluates to false. + */ + simdjson_inline const T& value_unsafe() const& noexcept; + /** + * 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 + +} // namespace ppc64 +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_H +/* end file simdjson/generic/implementation_simdjson_result_base.h for ppc64 */ +/* including simdjson/generic/numberparsing.h for ppc64: #include "simdjson/generic/numberparsing.h" */ +/* begin file simdjson/generic/numberparsing.h for ppc64 */ +#ifndef SIMDJSON_GENERIC_NUMBERPARSING_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_NUMBERPARSING_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/jsoncharutils.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/internal/numberparsing_tables.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +#include +#include +#include + +namespace simdjson { +namespace ppc64 { +namespace numberparsing { + +#ifdef JSON_TEST_NUMBERS +#define INVALID_NUMBER(SRC) (found_invalid_number((SRC)), NUMBER_ERROR) +#define WRITE_INTEGER(VALUE, SRC, WRITER) (found_integer((VALUE), (SRC)), (WRITER).append_s64((VALUE))) +#define WRITE_UNSIGNED(VALUE, SRC, WRITER) (found_unsigned_integer((VALUE), (SRC)), (WRITER).append_u64((VALUE))) +#define WRITE_DOUBLE(VALUE, SRC, WRITER) (found_float((VALUE), (SRC)), (WRITER).append_double((VALUE))) +#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)) +#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 + // 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; + + + // 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. + + // 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. In very rare cases, even that + // will not suffice, though it is seemingly very hard to find such a scenario. + // + // 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++; } + // At this point, we might need to add at most one to firstproduct, but this + // can only change the value of firstproduct.high if firstproduct.low is maximal. + if(simdjson_unlikely(firstproduct.low == 0xFFFFFFFFFFFFFFFF)) { + // This is very unlikely, but if so, we need to do much more work! + return false; + } + } + 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 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_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 doesn't 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 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 */ +template +error_code slow_float_parsing(simdjson_unused const uint8_t * src, W writer) { + double d; + if (parse_float_fallback(src, &d)) { + writer.append_double(d); + 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: This makes a *copy* of the writer and passes it to slow_float_parsing. This happens + // because slow_float_parsing is a non-inlined function. 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. + // This is what forces the skip_double, as well. + error_code error = slow_float_parsing(src, writer); + writer.skip_double(); + 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; +} + +// 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 +} + +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_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 INVALID_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 INVALID_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 doesn't 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 = start_digits-src > 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++; } + if ( p == src ) { return NUMBER_ERROR; } + if (jsoncharutils::is_structural_or_whitespace(*p)) { + // We have an integer. + // If the number is negative and valid, it must be a signed integer. + if(negative) { return number_type::signed_integer; } + // We want values larger or equal to 9223372036854775808 to be unsigned + // integers, and the other values to be signed integers. + int digit_count = int(p - src); + if(digit_count >= 19) { + const uint8_t * smaller_big_integer = reinterpret_cast("9223372036854775808"); + if((digit_count >= 20) || (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 = start_digits-src > 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; + default: SIMDJSON_UNREACHABLE(); + } + return out; +} + +} // namespace ppc64 +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_NUMBERPARSING_H +/* end file simdjson/generic/numberparsing.h for ppc64 */ + +/* including simdjson/generic/implementation_simdjson_result_base-inl.h for ppc64: #include "simdjson/generic/implementation_simdjson_result_base-inl.h" */ +/* begin file simdjson/generic/implementation_simdjson_result_base-inl.h for ppc64 */ +#ifndef SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_INL_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_INL_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { +namespace ppc64 { + +// +// internal::implementation_simdjson_result_base 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_inline error_code implementation_simdjson_result_base::error() const noexcept { + return this->second; +} + +#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::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 ppc64 +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_INL_H +/* end file simdjson/generic/implementation_simdjson_result_base-inl.h for ppc64 */ +/* end file simdjson/generic/amalgamated.h for ppc64 */ +/* including simdjson/ppc64/end.h: #include "simdjson/ppc64/end.h" */ +/* begin file simdjson/ppc64/end.h */ +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #include "simdjson/ppc64/base.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +#undef SIMDJSON_SKIP_BACKSLASH_SHORT_CIRCUIT +/* undefining SIMDJSON_IMPLEMENTATION from "ppc64" */ +#undef SIMDJSON_IMPLEMENTATION +/* end file simdjson/ppc64/end.h */ + +#endif // SIMDJSON_PPC64_H +/* end file simdjson/ppc64.h */ +#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(westmere) +/* including simdjson/westmere.h: #include "simdjson/westmere.h" */ +/* begin file simdjson/westmere.h */ +#ifndef SIMDJSON_WESTMERE_H +#define SIMDJSON_WESTMERE_H + +/* including simdjson/westmere/begin.h: #include "simdjson/westmere/begin.h" */ +/* begin file simdjson/westmere/begin.h */ +/* defining SIMDJSON_IMPLEMENTATION to "westmere" */ +#define SIMDJSON_IMPLEMENTATION westmere +/* including simdjson/westmere/base.h: #include "simdjson/westmere/base.h" */ +/* begin file simdjson/westmere/base.h */ +#ifndef SIMDJSON_WESTMERE_BASE_H +#define SIMDJSON_WESTMERE_BASE_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #include "simdjson/base.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +// The constructor may be executed on any host, so we take care not to use SIMDJSON_TARGET_WESTMERE +namespace simdjson { +/** + * Implementation for Westmere (Intel SSE4.2). + */ +namespace westmere { + +class implementation; + +namespace { +namespace simd { + +template 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 +#ifdef _M_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 // _M_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]; + + 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]; + + 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 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 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 +} // namespace simdjson + +#endif // SIMDJSON_WESTMERE_STRINGPARSING_DEFS_H +/* end file simdjson/westmere/stringparsing_defs.h */ +/* end file simdjson/westmere/begin.h */ +/* including simdjson/generic/amalgamated.h for westmere: #include "simdjson/generic/amalgamated.h" */ +/* begin file simdjson/generic/amalgamated.h for westmere */ +#if defined(SIMDJSON_CONDITIONAL_INCLUDE) && !defined(SIMDJSON_GENERIC_DEPENDENCIES_H) +#error simdjson/generic/dependencies.h must be included before simdjson/generic/amalgamated.h! +#endif + +/* including simdjson/generic/base.h for westmere: #include "simdjson/generic/base.h" */ +/* begin file simdjson/generic/base.h for westmere */ +#ifndef SIMDJSON_GENERIC_BASE_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_BASE_H */ +/* amalgamation skipped (editor-only): #include "simdjson/base.h" */ +/* amalgamation skipped (editor-only): // If we haven't got an implementation yet, we're in the editor, editing a generic file! Just */ +/* amalgamation skipped (editor-only): // use the most advanced one we can so the most possible stuff can be tested. */ +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_IMPLEMENTATION */ +/* amalgamation skipped (editor-only): #include "simdjson/implementation_detection.h" */ +/* amalgamation skipped (editor-only): #if SIMDJSON_IMPLEMENTATION_ICELAKE */ +/* amalgamation skipped (editor-only): #include "simdjson/icelake/begin.h" */ +/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_HASWELL */ +/* amalgamation skipped (editor-only): #include "simdjson/haswell/begin.h" */ +/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_WESTMERE */ +/* amalgamation skipped (editor-only): #include "simdjson/westmere/begin.h" */ +/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_ARM64 */ +/* amalgamation skipped (editor-only): #include "simdjson/arm64/begin.h" */ +/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_PPC64 */ +/* amalgamation skipped (editor-only): #include "simdjson/ppc64/begin.h" */ +/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_FALLBACK */ +/* amalgamation skipped (editor-only): #include "simdjson/fallback/begin.h" */ +/* amalgamation skipped (editor-only): #else */ +/* amalgamation skipped (editor-only): #error "All possible implementations (including fallback) have been disabled! simdjson will not run." */ +/* amalgamation skipped (editor-only): #endif */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_IMPLEMENTATION */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { +namespace westmere { + +struct open_container; +class dom_parser_implementation; + +/** + * The type of a JSON number + */ +enum class number_type { + floating_point_number=1, /// a binary64 number + signed_integer, /// a signed integer that fits in a 64-bit word using two's complement + unsigned_integer /// a positive integer larger or equal to 1<<63 +}; + +} // namespace westmere +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_BASE_H +/* end file simdjson/generic/base.h for westmere */ +/* including simdjson/generic/jsoncharutils.h for westmere: #include "simdjson/generic/jsoncharutils.h" */ +/* begin file simdjson/generic/jsoncharutils.h for westmere */ +#ifndef SIMDJSON_GENERIC_JSONCHARUTILS_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_JSONCHARUTILS_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/internal/jsoncharutils_tables.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/internal/numberparsing_tables.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { +namespace westmere { +namespace { +namespace jsoncharutils { + +// return non-zero if not a structural or whitespace char +// zero otherwise +simdjson_inline uint32_t is_not_structural_or_whitespace(uint8_t c) { + return internal::structural_or_whitespace_negated[c]; +} + +simdjson_inline uint32_t is_structural_or_whitespace(uint8_t c) { + return internal::structural_or_whitespace[c]; +} + +// returns a value with the high 16 bits set if not valid +// otherwise returns the conversion of the 4 hex digits at src into the bottom +// 16 bits of the 32-bit return register +// +// see +// https://lemire.me/blog/2019/04/17/parsing-short-hexadecimal-strings-efficiently/ +static inline uint32_t hex_to_u32_nocheck( + const uint8_t *src) { // strictly speaking, static inline is a C-ism + uint32_t v1 = internal::digit_to_val32[630 + src[0]]; + uint32_t v2 = internal::digit_to_val32[420 + src[1]]; + uint32_t v3 = internal::digit_to_val32[210 + src[2]]; + uint32_t v4 = internal::digit_to_val32[0 + src[3]]; + return v1 | v2 | v3 | v4; +} + +// given a code point cp, writes to c +// the utf-8 code, outputting the length in +// bytes, if the length is zero, the code point +// is invalid +// +// This can possibly be made faster using pdep +// and clz and table lookups, but JSON documents +// have few escaped code points, and the following +// function looks cheap. +// +// Note: we assume that surrogates are treated separately +// +simdjson_inline size_t codepoint_to_utf8(uint32_t cp, uint8_t *c) { + if (cp <= 0x7F) { + c[0] = uint8_t(cp); + return 1; // ascii + } + if (cp <= 0x7FF) { + c[0] = uint8_t((cp >> 6) + 192); + c[1] = uint8_t((cp & 63) + 128); + return 2; // universal plane + // Surrogates are treated elsewhere... + //} //else if (0xd800 <= cp && cp <= 0xdfff) { + // return 0; // surrogates // could put assert here + } else if (cp <= 0xFFFF) { + c[0] = uint8_t((cp >> 12) + 224); + c[1] = uint8_t(((cp >> 6) & 63) + 128); + c[2] = uint8_t((cp & 63) + 128); + return 3; + } else if (cp <= 0x10FFFF) { // if you know you have a valid code point, this + // is not needed + c[0] = uint8_t((cp >> 18) + 240); + c[1] = uint8_t(((cp >> 12) & 63) + 128); + c[2] = uint8_t(((cp >> 6) & 63) + 128); + c[3] = uint8_t((cp & 63) + 128); + return 4; + } + // will return 0 when the code point was too large. + return 0; // bad r +} + +#if SIMDJSON_IS_32BITS // _umul128 for x86, arm +// this is a slow emulation routine for 32-bit +// +static simdjson_inline uint64_t __emulu(uint32_t x, uint32_t y) { + return x * (uint64_t)y; +} +static simdjson_inline uint64_t _umul128(uint64_t ab, uint64_t cd, uint64_t *hi) { + uint64_t ad = __emulu((uint32_t)(ab >> 32), (uint32_t)cd); + uint64_t bd = __emulu((uint32_t)ab, (uint32_t)cd); + uint64_t adbc = ad + __emulu((uint32_t)ab, (uint32_t)(cd >> 32)); + uint64_t adbc_carry = !!(adbc < ad); + uint64_t lo = bd + (adbc << 32); + *hi = __emulu((uint32_t)(ab >> 32), (uint32_t)(cd >> 32)) + (adbc >> 32) + + (adbc_carry << 32) + !!(lo < bd); + return lo; +} +#endif + +} // namespace jsoncharutils +} // unnamed namespace +} // namespace westmere +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_JSONCHARUTILS_H +/* end file simdjson/generic/jsoncharutils.h for westmere */ +/* including simdjson/generic/atomparsing.h for westmere: #include "simdjson/generic/atomparsing.h" */ +/* begin file simdjson/generic/atomparsing.h for westmere */ +#ifndef SIMDJSON_GENERIC_ATOMPARSING_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ATOMPARSING_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/jsoncharutils.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +#include + +namespace simdjson { +namespace westmere { +namespace { +/// @private +namespace atomparsing { + +// The string_to_uint32 is exclusively used to map literal strings to 32-bit values. +// We use memcpy instead of a pointer cast to avoid undefined behaviors since we cannot +// be certain that the character pointer will be properly aligned. +// You might think that using memcpy makes this function expensive, but you'd be wrong. +// All decent optimizing compilers (GCC, clang, Visual Studio) will compile string_to_uint32("false"); +// to the compile-time constant 1936482662. +simdjson_inline uint32_t string_to_uint32(const char* str) { uint32_t val; std::memcpy(&val, str, sizeof(uint32_t)); return val; } + + +// 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 westmere +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_ATOMPARSING_H +/* end file simdjson/generic/atomparsing.h for westmere */ +/* including simdjson/generic/dom_parser_implementation.h for westmere: #include "simdjson/generic/dom_parser_implementation.h" */ +/* begin file simdjson/generic/dom_parser_implementation.h for westmere */ +#ifndef SIMDJSON_GENERIC_DOM_PARSER_IMPLEMENTATION_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_DOM_PARSER_IMPLEMENTATION_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/internal/dom_parser_implementation.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { +namespace westmere { + +// expectation: sizeof(open_container) = 64/8. +struct open_container { + uint32_t tape_index; // where, on the tape, does the scope ([,{) begins + uint32_t count; // how many elements in the scope +}; // struct open_container + +static_assert(sizeof(open_container) == 64/8, "Open container must be 64 bits"); + +class dom_parser_implementation final : public internal::dom_parser_implementation { +public: + /** Tape location of each open { or [ */ + std::unique_ptr open_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 westmere +} // namespace simdjson + +namespace simdjson { +namespace westmere { + +inline dom_parser_implementation::dom_parser_implementation() noexcept = default; +inline dom_parser_implementation::dom_parser_implementation(dom_parser_implementation &&other) noexcept = default; +inline dom_parser_implementation &dom_parser_implementation::operator=(dom_parser_implementation &&other) noexcept = default; + +// Leaving these here so they can be inlined if so desired +inline simdjson_warn_unused error_code dom_parser_implementation::set_capacity(size_t capacity) noexcept { + if(capacity > SIMDJSON_MAXSIZE_BYTES) { 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 westmere +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_DOM_PARSER_IMPLEMENTATION_H +/* end file simdjson/generic/dom_parser_implementation.h for westmere */ +/* including simdjson/generic/implementation_simdjson_result_base.h for westmere: #include "simdjson/generic/implementation_simdjson_result_base.h" */ +/* begin file simdjson/generic/implementation_simdjson_result_base.h for westmere */ +#ifndef SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { +namespace westmere { + +// This is a near copy of include/error.h's implementation_simdjson_result_base, except it doesn't use std::pair +// so we can avoid inlining errors +// TODO reconcile these! +/** + * The result of a simdjson operation that could fail. + * + * Gives the option of reading error codes, or throwing an exception by casting to the desired result. + * + * This is a base class for implementations that want to add functions to the result type for + * chaining. + * + * Override like: + * + * struct simdjson_result : 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_inline error_code get(T &value) && noexcept; + + /** + * The error. + */ + simdjson_inline error_code error() const noexcept; + +#if SIMDJSON_EXCEPTIONS + + /** + * Get the result value. + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T& value() & noexcept(false); + + /** + * Take the result value (move it). + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T&& value() && noexcept(false); + + /** + * Take the result value (move it). + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T&& take_value() && noexcept(false); + + /** + * Cast to the value (will throw on error). + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline operator T&&() && noexcept(false); + + +#endif // SIMDJSON_EXCEPTIONS + + /** + * Get the result value. This function is safe if and only + * the error() method returns a value that evaluates to false. + */ + simdjson_inline const T& value_unsafe() const& noexcept; + /** + * 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 + +} // namespace westmere +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_H +/* end file simdjson/generic/implementation_simdjson_result_base.h for westmere */ +/* including simdjson/generic/numberparsing.h for westmere: #include "simdjson/generic/numberparsing.h" */ +/* begin file simdjson/generic/numberparsing.h for westmere */ +#ifndef SIMDJSON_GENERIC_NUMBERPARSING_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_NUMBERPARSING_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/jsoncharutils.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/internal/numberparsing_tables.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +#include +#include +#include + +namespace simdjson { +namespace westmere { +namespace numberparsing { + +#ifdef JSON_TEST_NUMBERS +#define INVALID_NUMBER(SRC) (found_invalid_number((SRC)), NUMBER_ERROR) +#define WRITE_INTEGER(VALUE, SRC, WRITER) (found_integer((VALUE), (SRC)), (WRITER).append_s64((VALUE))) +#define WRITE_UNSIGNED(VALUE, SRC, WRITER) (found_unsigned_integer((VALUE), (SRC)), (WRITER).append_u64((VALUE))) +#define WRITE_DOUBLE(VALUE, SRC, WRITER) (found_float((VALUE), (SRC)), (WRITER).append_double((VALUE))) +#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)) +#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 + // 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; + + + // 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. + + // 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. In very rare cases, even that + // will not suffice, though it is seemingly very hard to find such a scenario. + // + // 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++; } + // At this point, we might need to add at most one to firstproduct, but this + // can only change the value of firstproduct.high if firstproduct.low is maximal. + if(simdjson_unlikely(firstproduct.low == 0xFFFFFFFFFFFFFFFF)) { + // This is very unlikely, but if so, we need to do much more work! + return false; + } + } + 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 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_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 doesn't 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 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 */ +template +error_code slow_float_parsing(simdjson_unused const uint8_t * src, W writer) { + double d; + if (parse_float_fallback(src, &d)) { + writer.append_double(d); + 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: This makes a *copy* of the writer and passes it to slow_float_parsing. This happens + // because slow_float_parsing is a non-inlined function. 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. + // This is what forces the skip_double, as well. + error_code error = slow_float_parsing(src, writer); + writer.skip_double(); + 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; +} + +// 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 +} + +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_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 INVALID_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 INVALID_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 doesn't 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 = start_digits-src > 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++; } + if ( p == src ) { return NUMBER_ERROR; } + if (jsoncharutils::is_structural_or_whitespace(*p)) { + // We have an integer. + // If the number is negative and valid, it must be a signed integer. + if(negative) { return number_type::signed_integer; } + // We want values larger or equal to 9223372036854775808 to be unsigned + // integers, and the other values to be signed integers. + int digit_count = int(p - src); + if(digit_count >= 19) { + const uint8_t * smaller_big_integer = reinterpret_cast("9223372036854775808"); + if((digit_count >= 20) || (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 = start_digits-src > 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; + default: SIMDJSON_UNREACHABLE(); + } + return out; +} + +} // namespace westmere +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_NUMBERPARSING_H +/* end file simdjson/generic/numberparsing.h for westmere */ + +/* including simdjson/generic/implementation_simdjson_result_base-inl.h for westmere: #include "simdjson/generic/implementation_simdjson_result_base-inl.h" */ +/* begin file simdjson/generic/implementation_simdjson_result_base-inl.h for westmere */ +#ifndef SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_INL_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_INL_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { +namespace westmere { + +// +// internal::implementation_simdjson_result_base 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_inline error_code implementation_simdjson_result_base::error() const noexcept { + return this->second; +} + +#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::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 westmere +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_INL_H +/* end file simdjson/generic/implementation_simdjson_result_base-inl.h for westmere */ +/* end file simdjson/generic/amalgamated.h for westmere */ +/* including simdjson/westmere/end.h: #include "simdjson/westmere/end.h" */ +/* begin file simdjson/westmere/end.h */ +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #include "simdjson/westmere/base.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +#if !SIMDJSON_CAN_ALWAYS_RUN_WESTMERE +SIMDJSON_UNTARGET_REGION +#endif + +/* undefining SIMDJSON_IMPLEMENTATION from "westmere" */ +#undef SIMDJSON_IMPLEMENTATION +/* end file simdjson/westmere/end.h */ + +#endif // SIMDJSON_WESTMERE_H +/* end file simdjson/westmere.h */ +#else +#error Unknown SIMDJSON_BUILTIN_IMPLEMENTATION +#endif + +/* undefining SIMDJSON_CONDITIONAL_INCLUDE */ +#undef SIMDJSON_CONDITIONAL_INCLUDE + +#endif // SIMDJSON_BUILTIN_H +/* end file simdjson/builtin.h */ +/* skipped duplicate #include "simdjson/builtin/base.h" */ + +/* including simdjson/generic/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" */ + +#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) { +#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; +#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) { +#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; +#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) { +#ifdef 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 _M_ARM64 +// __umulh requires intrin.h +#include +#endif // _M_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 +#ifdef _M_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 // _M_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 + +#define SIMDJSON_SWAR_NUMBER_PARSING 1 + +#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 { + +#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO +namespace { +// Start of private section with Visual Studio workaround + + +/** + * make_uint8x16_t initializes a SIMD register (uint8x16_t). + * This is needed because, incredibly, the syntax uint8x16_t x = {1,2,3...} + * is not recognized under Visual Studio! This is a workaround. + * Using a std::initializer_list as a parameter resulted in + * inefficient code. With the current approach, if the parameters are + * compile-time constants, + * GNU GCC compiles it to ldr, the same as uint8x16_t x = {1,2,3...}. + * You should not use this function except for compile-time constants: + * it is not efficient. + */ +simdjson_inline uint8x16_t make_uint8x16_t(uint8_t x1, uint8_t x2, uint8_t x3, uint8_t x4, + uint8_t x5, uint8_t x6, uint8_t x7, uint8_t x8, + uint8_t x9, uint8_t x10, uint8_t x11, uint8_t x12, + uint8_t x13, uint8_t x14, uint8_t x15, uint8_t x16) { + // Doing a load like so end ups generating worse code. + // uint8_t array[16] = {x1, x2, x3, x4, x5, x6, x7, x8, + // x9, x10,x11,x12,x13,x14,x15,x16}; + // return vld1q_u8(array); + uint8x16_t x{}; + // incredibly, Visual Studio does not allow x[0] = x1 + x = vsetq_lane_u8(x1, x, 0); + x = vsetq_lane_u8(x2, x, 1); + x = vsetq_lane_u8(x3, x, 2); + x = vsetq_lane_u8(x4, x, 3); + x = vsetq_lane_u8(x5, x, 4); + x = vsetq_lane_u8(x6, x, 5); + x = vsetq_lane_u8(x7, x, 6); + x = vsetq_lane_u8(x8, x, 7); + x = vsetq_lane_u8(x9, x, 8); + x = vsetq_lane_u8(x10, x, 9); + x = vsetq_lane_u8(x11, x, 10); + x = vsetq_lane_u8(x12, x, 11); + x = vsetq_lane_u8(x13, x, 12); + x = vsetq_lane_u8(x14, x, 13); + x = vsetq_lane_u8(x15, x, 14); + x = vsetq_lane_u8(x16, x, 15); + return x; +} + +simdjson_inline uint8x8_t make_uint8x8_t(uint8_t x1, uint8_t x2, uint8_t x3, uint8_t x4, + uint8_t x5, uint8_t x6, uint8_t x7, uint8_t x8) { + uint8x8_t x{}; + x = vset_lane_u8(x1, x, 0); + x = vset_lane_u8(x2, x, 1); + x = vset_lane_u8(x3, x, 2); + x = vset_lane_u8(x4, x, 3); + x = vset_lane_u8(x5, x, 4); + x = vset_lane_u8(x6, x, 5); + x = vset_lane_u8(x7, x, 6); + x = vset_lane_u8(x8, x, 7); + return x; +} + +// We have to do the same work for make_int8x16_t +simdjson_inline int8x16_t make_int8x16_t(int8_t x1, int8_t x2, int8_t x3, int8_t x4, + int8_t x5, int8_t x6, int8_t x7, int8_t x8, + int8_t x9, int8_t x10, int8_t x11, int8_t x12, + int8_t x13, int8_t x14, int8_t x15, int8_t x16) { + // Doing a load like so end ups generating worse code. + // int8_t array[16] = {x1, x2, x3, x4, x5, x6, x7, x8, + // x9, x10,x11,x12,x13,x14,x15,x16}; + // return vld1q_s8(array); + int8x16_t x{}; + // incredibly, Visual Studio does not allow x[0] = x1 + x = vsetq_lane_s8(x1, x, 0); + x = vsetq_lane_s8(x2, x, 1); + x = vsetq_lane_s8(x3, x, 2); + x = vsetq_lane_s8(x4, x, 3); + x = vsetq_lane_s8(x5, x, 4); + x = vsetq_lane_s8(x6, x, 5); + x = vsetq_lane_s8(x7, x, 6); + x = vsetq_lane_s8(x8, x, 7); + x = vsetq_lane_s8(x9, x, 8); + x = vsetq_lane_s8(x10, x, 9); + x = vsetq_lane_s8(x11, x, 10); + x = vsetq_lane_s8(x12, x, 11); + x = vsetq_lane_s8(x13, x, 12); + x = vsetq_lane_s8(x14, x, 13); + x = vsetq_lane_s8(x15, x, 14); + x = vsetq_lane_s8(x16, x, 15); + return x; +} + +// 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 { +#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO + const uint8x16_t bit_mask = 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); + } + simdjson_inline bool any() const { return vmaxvq_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 +#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(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 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); } + + // 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 = 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 +#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO + uint8x8_t inc = 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 +#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(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]; + + 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 { +#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO + const uint8x16_t bit_mask = 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 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 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 + }; +} + +} // unnamed namespace +} // namespace arm64 +} // namespace simdjson + +#endif // SIMDJSON_ARM64_STRINGPARSING_DEFS_H +/* end file simdjson/arm64/stringparsing_defs.h */ + +#define SIMDJSON_SKIP_BACKSLASH_SHORT_CIRCUIT 1 +/* end file simdjson/arm64/begin.h */ +/* including simdjson/generic/ondemand/amalgamated.h for arm64: #include "simdjson/generic/ondemand/amalgamated.h" */ +/* begin file simdjson/generic/ondemand/amalgamated.h for arm64 */ +#if defined(SIMDJSON_CONDITIONAL_INCLUDE) && !defined(SIMDJSON_GENERIC_ONDEMAND_DEPENDENCIES_H) +#error simdjson/generic/ondemand/dependencies.h must be included before simdjson/generic/ondemand/amalgamated.h! +#endif + +// Stuff other things depend on +/* including simdjson/generic/ondemand/base.h for arm64: #include "simdjson/generic/ondemand/base.h" */ +/* begin file simdjson/generic/ondemand/base.h for arm64 */ +#ifndef SIMDJSON_GENERIC_ONDEMAND_BASE_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_BASE_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { +namespace arm64 { +/** + * A fast, simple, DOM-like interface that parses JSON as you use it. + * + * Designed for maximum speed and a lower memory profile. + */ +namespace ondemand { + +/** Represents the depth of a JSON value (number of nested arrays/objects). */ +using depth_t = int32_t; + +/** @copydoc simdjson::arm64::number_type */ +using number_type = simdjson::arm64::number_type; + +/** @private Position in the JSON buffer indexes */ +using token_position = const uint32_t *; + +class array; +class array_iterator; +class document; +class document_reference; +class document_stream; +class field; +class json_iterator; +enum class json_type; +struct number; +class object; +class object_iterator; +class parser; +class raw_json_string; +class token_iterator; +class value; +class value_iterator; + +} // 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): #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). + */ + 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; + 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; + 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; + + /** + * 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; +}; // 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 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."); + } + + /** + * 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; + + + /** + * 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 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 wasn't there when they aren't). + * + * @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 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 + * 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. + * + * 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 + */ + simdjson_inline std::string_view raw_json_token() 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; + +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; +}; + +} // 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; + 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 + 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 wasn't there when they aren't). + * + * @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_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; + + /** @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; +}; + +} // 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 isn't 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; + + /** + * 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 isn't 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 isn't 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 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 isn't 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; + /// 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; + 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::slow_float_parsing(simdjson_unused const uint8_t * src, W writer); + 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. + * + * ### 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; + /** @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(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). + * + * 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_inline size_t capacity() const noexcept; + /** The maximum capacity of this parser (the largest document it is allowed to process). */ + 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_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; + +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; + /** + * 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 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; + /** + * 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. + * + * @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 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."); + } + /** @overload template simdjson_result get() & noexcept */ + template simdjson_inline simdjson_result get() && 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."); + } + + /** + * 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_inline error_code get(T &out) && noexcept; + +#if SIMDJSON_EXCEPTIONS + /** + * 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. + * + * @returns A value value. + * @exception if a JSON value cannot be found + */ + 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 wasn't there when they aren't). + * + * 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 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 + * get_number_type() is number_type::signed_integer if we have an + * integer that is less 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 + * + * 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; + /** + * 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; + 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 raw_json() noexcept; + simdjson_inline operator document&() const noexcept; + +#if SIMDJSON_EXCEPTIONS + 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 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; +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; + 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 + 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 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; +}; + + +} // 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; + 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 + 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 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; +}; + + +} // 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 = value_type; + + using difference_type = std::ptrdiff_t; + + using iterator_category = std::input_iterator_tag; + + /** + * Default constructor. + */ + simdjson_inline iterator() noexcept; + /** + * Get the current document (or error). + */ + simdjson_inline simdjson_result 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 caller is responsible for omitting it, UTF-8 BOM are + * discouraged. + * + * 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 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) noexcept; + /** + * Get the key as a raw_json_string. Can be used for direct comparison with + * an unescaped C string: e.g., key() == "test". + */ + simdjson_inline raw_json_string 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; + simdjson_inline simdjson_result 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. + * + * @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 wasn't there when they aren't). + * + * 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. + * + * @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; + + /** + * 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 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; + 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 + +/** + * 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 */ + +// 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; +} + +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::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-inl.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_iterator-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_type.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */ +/* 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 { + +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. + iter.assert_at_document_depth(); + 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); +} +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_raw_json_string(); } +template<> simdjson_inline simdjson_result document::get() && noexcept { return get_string(false); } +template<> simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_double(); } +template<> simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_uint64(); } +template<> simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_int64(); } +template<> simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_bool(); } +template<> 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_inline error_code document::get(T &out) && noexcept { + return std::forward(*this).get().get(out); +} + +#if SIMDJSON_EXCEPTIONS +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 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_start_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; + } +} + +} // 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); +} +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_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_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 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 +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); +} + + +} // 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); } +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); } + +#if SIMDJSON_EXCEPTIONS +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 raw_json_string(*doc); } +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::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::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); +} +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_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 +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); +} + + +} // 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: Remove any trailing whitespaces + // This returns a string spanning from start of value to the beginning of the next document (excluded) + return std::string_view(reinterpret_cast(stream->buf) + current_index(), stream->parser->implementation->structural_indexes[++cur_struct_index] - current_index() - 1); + } + 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 doesn't 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; +} + +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 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::unescaped_key(bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.unescaped_key(allow_replacement); +} +simdjson_inline simdjson_result simdjson_result::value() noexcept { + if (error()) { return error(); } + return std::move(first.value()); +} + +} // 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 +} + +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 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 { + return parser->unescape(in, _string_buf_loc, allow_replacement); +} + +simdjson_inline simdjson_result json_iterator::unescape_wobbly(raw_json_string in) noexcept { + return parser->unescape_wobbly(in, _string_buf_loc); +} + +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 + +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): #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; +} + +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(); +} +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); +} + +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; +} + +simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(padded_string_view json) & noexcept { + if (json.padding() < SIMDJSON_PADDING) { return INSUFFICIENT_PADDING; } + + // 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 }); +} + +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(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; } + + // 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(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_inline size_t parser::capacity() const noexcept { + return _capacity; +} +simdjson_inline size_t parser::max_capacity() const noexcept { + return _max_capacity; +} +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()); +} +#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 */ +/* 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 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-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); +} +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 +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 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::current_location() noexcept { + return iter.json_iter().current_location(); +} + +simdjson_inline int32_t value::current_depth() const noexcept{ + return iter.json_iter().depth(); +} + +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: + 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); +} +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_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 +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::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); +} + +} // 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/json_type-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string-inl.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 { + +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 isn't 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 isn't 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); +} +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_start_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_start_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)) { + logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters"); + 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_start_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)) { + logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters"); + 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); +} +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_start_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_start_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_start_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_start_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_start_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_start_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_start_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_start_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 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 */ +/* 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 static 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] }; +} + +} // 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 +#ifdef _M_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 // _M_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 + +#define SIMDJSON_SWAR_NUMBER_PARSING 1 + +#endif // SIMDJSON_FALLBACK_NUMBERPARSING_DEFS_H +/* end file simdjson/fallback/numberparsing_defs.h */ +/* end file simdjson/fallback/begin.h */ +/* including simdjson/generic/ondemand/amalgamated.h for fallback: #include "simdjson/generic/ondemand/amalgamated.h" */ +/* begin file simdjson/generic/ondemand/amalgamated.h for fallback */ +#if defined(SIMDJSON_CONDITIONAL_INCLUDE) && !defined(SIMDJSON_GENERIC_ONDEMAND_DEPENDENCIES_H) +#error simdjson/generic/ondemand/dependencies.h must be included before simdjson/generic/ondemand/amalgamated.h! +#endif + +// Stuff other things depend on +/* including simdjson/generic/ondemand/base.h for fallback: #include "simdjson/generic/ondemand/base.h" */ +/* begin file simdjson/generic/ondemand/base.h for fallback */ +#ifndef SIMDJSON_GENERIC_ONDEMAND_BASE_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_BASE_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { +namespace fallback { +/** + * A fast, simple, DOM-like interface that parses JSON as you use it. + * + * Designed for maximum speed and a lower memory profile. + */ +namespace ondemand { + +/** Represents the depth of a JSON value (number of nested arrays/objects). */ +using depth_t = int32_t; + +/** @copydoc simdjson::fallback::number_type */ +using number_type = simdjson::fallback::number_type; + +/** @private Position in the JSON buffer indexes */ +using token_position = const uint32_t *; + +class array; +class array_iterator; +class document; +class document_reference; +class document_stream; +class field; +class json_iterator; +enum class json_type; +struct number; +class object; +class object_iterator; +class parser; +class raw_json_string; +class token_iterator; +class value; +class value_iterator; + +} // 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): #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). + */ + 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; + 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; + 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; + + /** + * 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; +}; // 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 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."); + } + + /** + * 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; + + + /** + * 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 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 wasn't there when they aren't). + * + * @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 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 + * 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. + * + * 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 + */ + simdjson_inline std::string_view raw_json_token() 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; + +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; +}; + +} // 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; + 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 + 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 wasn't there when they aren't). + * + * @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_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; + + /** @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; +}; + +} // 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 isn't 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; + + /** + * 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 isn't 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 isn't 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 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 isn't 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; + /// 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; + 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::slow_float_parsing(simdjson_unused const uint8_t * src, W writer); + 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. + * + * ### 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; + /** @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(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). + * + * 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_inline size_t capacity() const noexcept; + /** The maximum capacity of this parser (the largest document it is allowed to process). */ + 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_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; + +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; + /** + * 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 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; + /** + * 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. + * + * @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 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."); + } + /** @overload template simdjson_result get() & noexcept */ + template simdjson_inline simdjson_result get() && 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."); + } + + /** + * 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_inline error_code get(T &out) && noexcept; + +#if SIMDJSON_EXCEPTIONS + /** + * 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. + * + * @returns A value value. + * @exception if a JSON value cannot be found + */ + 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 wasn't there when they aren't). + * + * 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 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 + * get_number_type() is number_type::signed_integer if we have an + * integer that is less 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 + * + * 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; + /** + * 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; + 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 raw_json() noexcept; + simdjson_inline operator document&() const noexcept; + +#if SIMDJSON_EXCEPTIONS + 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 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; +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; + 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 + 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 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; +}; + + +} // 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; + 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 + 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 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; +}; + + +} // 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 = value_type; + + using difference_type = std::ptrdiff_t; + + using iterator_category = std::input_iterator_tag; + + /** + * Default constructor. + */ + simdjson_inline iterator() noexcept; + /** + * Get the current document (or error). + */ + simdjson_inline simdjson_result 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 caller is responsible for omitting it, UTF-8 BOM are + * discouraged. + * + * 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 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) noexcept; + /** + * Get the key as a raw_json_string. Can be used for direct comparison with + * an unescaped C string: e.g., key() == "test". + */ + simdjson_inline raw_json_string 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; + simdjson_inline simdjson_result 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. + * + * @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 wasn't there when they aren't). + * + * 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. + * + * @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; + + /** + * 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 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; + 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 + +/** + * 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 */ + +// 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; +} + +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::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-inl.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_iterator-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_type.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */ +/* 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 { + +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. + iter.assert_at_document_depth(); + 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); +} +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_raw_json_string(); } +template<> simdjson_inline simdjson_result document::get() && noexcept { return get_string(false); } +template<> simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_double(); } +template<> simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_uint64(); } +template<> simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_int64(); } +template<> simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_bool(); } +template<> 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_inline error_code document::get(T &out) && noexcept { + return std::forward(*this).get().get(out); +} + +#if SIMDJSON_EXCEPTIONS +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 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_start_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; + } +} + +} // 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); +} +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_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_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 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 +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); +} + + +} // 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); } +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); } + +#if SIMDJSON_EXCEPTIONS +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 raw_json_string(*doc); } +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::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::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); +} +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_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 +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); +} + + +} // 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: Remove any trailing whitespaces + // This returns a string spanning from start of value to the beginning of the next document (excluded) + return std::string_view(reinterpret_cast(stream->buf) + current_index(), stream->parser->implementation->structural_indexes[++cur_struct_index] - current_index() - 1); + } + 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 doesn't 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; +} + +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 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::unescaped_key(bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.unescaped_key(allow_replacement); +} +simdjson_inline simdjson_result simdjson_result::value() noexcept { + if (error()) { return error(); } + return std::move(first.value()); +} + +} // 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 +} + +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 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 { + return parser->unescape(in, _string_buf_loc, allow_replacement); +} + +simdjson_inline simdjson_result json_iterator::unescape_wobbly(raw_json_string in) noexcept { + return parser->unescape_wobbly(in, _string_buf_loc); +} + +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 + +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): #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; +} + +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(); +} +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); +} + +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; +} + +simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(padded_string_view json) & noexcept { + if (json.padding() < SIMDJSON_PADDING) { return INSUFFICIENT_PADDING; } + + // 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 }); +} + +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(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; } + + // 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(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_inline size_t parser::capacity() const noexcept { + return _capacity; +} +simdjson_inline size_t parser::max_capacity() const noexcept { + return _max_capacity; +} +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()); +} +#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 */ +/* 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 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-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); +} +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 +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 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::current_location() noexcept { + return iter.json_iter().current_location(); +} + +simdjson_inline int32_t value::current_depth() const noexcept{ + return iter.json_iter().depth(); +} + +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: + 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); +} +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_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 +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::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); +} + +} // 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/json_type-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string-inl.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 { + +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 isn't 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 isn't 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); +} +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_start_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_start_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)) { + logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters"); + 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_start_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)) { + logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters"); + 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); +} +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_start_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_start_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_start_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_start_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_start_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_start_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_start_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_start_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 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 */ +/* 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 +SIMDJSON_TARGET_REGION("avx2,bmi,pclmul,lzcnt,popcnt") +#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 +#ifdef _M_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 // _M_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]; + + 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 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 ((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 + }; +} + +} // unnamed namespace +} // namespace haswell +} // namespace simdjson + +#endif // SIMDJSON_HASWELL_STRINGPARSING_DEFS_H +/* end file simdjson/haswell/stringparsing_defs.h */ +/* end file simdjson/haswell/begin.h */ +/* including simdjson/generic/ondemand/amalgamated.h for haswell: #include "simdjson/generic/ondemand/amalgamated.h" */ +/* begin file simdjson/generic/ondemand/amalgamated.h for haswell */ +#if defined(SIMDJSON_CONDITIONAL_INCLUDE) && !defined(SIMDJSON_GENERIC_ONDEMAND_DEPENDENCIES_H) +#error simdjson/generic/ondemand/dependencies.h must be included before simdjson/generic/ondemand/amalgamated.h! +#endif + +// Stuff other things depend on +/* including simdjson/generic/ondemand/base.h for haswell: #include "simdjson/generic/ondemand/base.h" */ +/* begin file simdjson/generic/ondemand/base.h for haswell */ +#ifndef SIMDJSON_GENERIC_ONDEMAND_BASE_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_BASE_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { +namespace haswell { +/** + * A fast, simple, DOM-like interface that parses JSON as you use it. + * + * Designed for maximum speed and a lower memory profile. + */ +namespace ondemand { + +/** Represents the depth of a JSON value (number of nested arrays/objects). */ +using depth_t = int32_t; + +/** @copydoc simdjson::haswell::number_type */ +using number_type = simdjson::haswell::number_type; + +/** @private Position in the JSON buffer indexes */ +using token_position = const uint32_t *; + +class array; +class array_iterator; +class document; +class document_reference; +class document_stream; +class field; +class json_iterator; +enum class json_type; +struct number; +class object; +class object_iterator; +class parser; +class raw_json_string; +class token_iterator; +class value; +class value_iterator; + +} // 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): #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). + */ + 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; + 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; + 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; + + /** + * 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; +}; // 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 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."); + } + + /** + * 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; + + + /** + * 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 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 wasn't there when they aren't). + * + * @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 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 + * 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. + * + * 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 + */ + simdjson_inline std::string_view raw_json_token() 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; + +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; +}; + +} // 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; + 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 + 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 wasn't there when they aren't). + * + * @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_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; + + /** @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; +}; + +} // 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 isn't 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; + + /** + * 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 isn't 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 isn't 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 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 isn't 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; + /// 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; + 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::slow_float_parsing(simdjson_unused const uint8_t * src, W writer); + 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. + * + * ### 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; + /** @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(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). + * + * 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_inline size_t capacity() const noexcept; + /** The maximum capacity of this parser (the largest document it is allowed to process). */ + 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_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; + +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; + /** + * 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 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; + /** + * 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. + * + * @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 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."); + } + /** @overload template simdjson_result get() & noexcept */ + template simdjson_inline simdjson_result get() && 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."); + } + + /** + * 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_inline error_code get(T &out) && noexcept; + +#if SIMDJSON_EXCEPTIONS + /** + * 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. + * + * @returns A value value. + * @exception if a JSON value cannot be found + */ + 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 wasn't there when they aren't). + * + * 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 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 + * get_number_type() is number_type::signed_integer if we have an + * integer that is less 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 + * + * 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; + /** + * 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; + 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 raw_json() noexcept; + simdjson_inline operator document&() const noexcept; + +#if SIMDJSON_EXCEPTIONS + 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 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; +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; + 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 + 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 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; +}; + + +} // 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; + 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 + 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 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; +}; + + +} // 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 = value_type; + + using difference_type = std::ptrdiff_t; + + using iterator_category = std::input_iterator_tag; + + /** + * Default constructor. + */ + simdjson_inline iterator() noexcept; + /** + * Get the current document (or error). + */ + simdjson_inline simdjson_result 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 caller is responsible for omitting it, UTF-8 BOM are + * discouraged. + * + * 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 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) noexcept; + /** + * Get the key as a raw_json_string. Can be used for direct comparison with + * an unescaped C string: e.g., key() == "test". + */ + simdjson_inline raw_json_string 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; + simdjson_inline simdjson_result 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. + * + * @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 wasn't there when they aren't). + * + * 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. + * + * @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; + + /** + * 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 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; + 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 + +/** + * 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 */ + +// 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; +} + +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::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-inl.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_iterator-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_type.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */ +/* 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 { + +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. + iter.assert_at_document_depth(); + 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); +} +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_raw_json_string(); } +template<> simdjson_inline simdjson_result document::get() && noexcept { return get_string(false); } +template<> simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_double(); } +template<> simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_uint64(); } +template<> simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_int64(); } +template<> simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_bool(); } +template<> 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_inline error_code document::get(T &out) && noexcept { + return std::forward(*this).get().get(out); +} + +#if SIMDJSON_EXCEPTIONS +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 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_start_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; + } +} + +} // 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); +} +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_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_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 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 +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); +} + + +} // 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); } +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); } + +#if SIMDJSON_EXCEPTIONS +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 raw_json_string(*doc); } +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::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::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); +} +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_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 +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); +} + + +} // 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: Remove any trailing whitespaces + // This returns a string spanning from start of value to the beginning of the next document (excluded) + return std::string_view(reinterpret_cast(stream->buf) + current_index(), stream->parser->implementation->structural_indexes[++cur_struct_index] - current_index() - 1); + } + 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 doesn't 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; +} + +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 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::unescaped_key(bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.unescaped_key(allow_replacement); +} +simdjson_inline simdjson_result simdjson_result::value() noexcept { + if (error()) { return error(); } + return std::move(first.value()); +} + +} // 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 +} + +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 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 { + return parser->unescape(in, _string_buf_loc, allow_replacement); +} + +simdjson_inline simdjson_result json_iterator::unescape_wobbly(raw_json_string in) noexcept { + return parser->unescape_wobbly(in, _string_buf_loc); +} + +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 + +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): #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; +} + +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(); +} +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); +} + +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; +} + +simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(padded_string_view json) & noexcept { + if (json.padding() < SIMDJSON_PADDING) { return INSUFFICIENT_PADDING; } + + // 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 }); +} + +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(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; } + + // 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(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_inline size_t parser::capacity() const noexcept { + return _capacity; +} +simdjson_inline size_t parser::max_capacity() const noexcept { + return _max_capacity; +} +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()); +} +#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 */ +/* 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 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-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); +} +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 +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 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::current_location() noexcept { + return iter.json_iter().current_location(); +} + +simdjson_inline int32_t value::current_depth() const noexcept{ + return iter.json_iter().depth(); +} + +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: + 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); +} +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_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 +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::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); +} + +} // 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/json_type-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string-inl.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 { + +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 isn't 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 isn't 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); +} +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_start_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_start_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)) { + logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters"); + 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_start_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)) { + logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters"); + 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); +} +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_start_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_start_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_start_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_start_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_start_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_start_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_start_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_start_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 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 */ +/* 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 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 + )); + } + }; + + // 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]; + + 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 = 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 ((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 + }; +} + +} // 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 +#ifdef _M_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 // _M_ARM64 +#else // SIMDJSON_REGULAR_VISUAL_STUDIO || SIMDJSON_IS_32BITS + __uint128_t r = (static_cast<__uint128_t>(value1)) * value2; + answer.low = uint64_t(r); + answer.high = uint64_t(r >> 64); +#endif + return answer; +} + +} // namespace numberparsing +} // namespace icelake +} // namespace simdjson + +#define SIMDJSON_SWAR_NUMBER_PARSING 1 + +#endif // SIMDJSON_ICELAKE_NUMBERPARSING_DEFS_H +/* end file simdjson/icelake/numberparsing_defs.h */ +/* end file simdjson/icelake/begin.h */ +/* including simdjson/generic/ondemand/amalgamated.h for icelake: #include "simdjson/generic/ondemand/amalgamated.h" */ +/* begin file simdjson/generic/ondemand/amalgamated.h for icelake */ +#if defined(SIMDJSON_CONDITIONAL_INCLUDE) && !defined(SIMDJSON_GENERIC_ONDEMAND_DEPENDENCIES_H) +#error simdjson/generic/ondemand/dependencies.h must be included before simdjson/generic/ondemand/amalgamated.h! +#endif + +// Stuff other things depend on +/* including simdjson/generic/ondemand/base.h for icelake: #include "simdjson/generic/ondemand/base.h" */ +/* begin file simdjson/generic/ondemand/base.h for icelake */ +#ifndef SIMDJSON_GENERIC_ONDEMAND_BASE_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_BASE_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { +namespace icelake { +/** + * A fast, simple, DOM-like interface that parses JSON as you use it. + * + * Designed for maximum speed and a lower memory profile. + */ +namespace ondemand { + +/** Represents the depth of a JSON value (number of nested arrays/objects). */ +using depth_t = int32_t; + +/** @copydoc simdjson::icelake::number_type */ +using number_type = simdjson::icelake::number_type; + +/** @private Position in the JSON buffer indexes */ +using token_position = const uint32_t *; + +class array; +class array_iterator; +class document; +class document_reference; +class document_stream; +class field; +class json_iterator; +enum class json_type; +struct number; +class object; +class object_iterator; +class parser; +class raw_json_string; +class token_iterator; +class value; +class value_iterator; + +} // 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): #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). + */ + 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; + 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; + 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; + + /** + * 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; +}; // 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 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."); + } + + /** + * 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; + + + /** + * 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 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 wasn't there when they aren't). + * + * @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 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 + * 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. + * + * 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 + */ + simdjson_inline std::string_view raw_json_token() 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; + +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; +}; + +} // 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; + 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 + 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 wasn't there when they aren't). + * + * @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_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; + + /** @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; +}; + +} // 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 isn't 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; + + /** + * 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 isn't 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 isn't 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 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 isn't 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; + /// 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; + 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::slow_float_parsing(simdjson_unused const uint8_t * src, W writer); + 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. + * + * ### 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; + /** @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(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). + * + * 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_inline size_t capacity() const noexcept; + /** The maximum capacity of this parser (the largest document it is allowed to process). */ + 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_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; + +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; + /** + * 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 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; + /** + * 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. + * + * @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 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."); + } + /** @overload template simdjson_result get() & noexcept */ + template simdjson_inline simdjson_result get() && 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."); + } + + /** + * 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_inline error_code get(T &out) && noexcept; + +#if SIMDJSON_EXCEPTIONS + /** + * 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. + * + * @returns A value value. + * @exception if a JSON value cannot be found + */ + 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 wasn't there when they aren't). + * + * 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 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 + * get_number_type() is number_type::signed_integer if we have an + * integer that is less 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 + * + * 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; + /** + * 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; + 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 raw_json() noexcept; + simdjson_inline operator document&() const noexcept; + +#if SIMDJSON_EXCEPTIONS + 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 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; +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; + 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 + 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 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; +}; + + +} // 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; + 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 + 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 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; +}; + + +} // 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 = value_type; + + using difference_type = std::ptrdiff_t; + + using iterator_category = std::input_iterator_tag; + + /** + * Default constructor. + */ + simdjson_inline iterator() noexcept; + /** + * Get the current document (or error). + */ + simdjson_inline simdjson_result 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 caller is responsible for omitting it, UTF-8 BOM are + * discouraged. + * + * 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 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) noexcept; + /** + * Get the key as a raw_json_string. Can be used for direct comparison with + * an unescaped C string: e.g., key() == "test". + */ + simdjson_inline raw_json_string 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; + simdjson_inline simdjson_result 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. + * + * @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 wasn't there when they aren't). + * + * 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. + * + * @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; + + /** + * 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 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; + 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 + +/** + * 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 */ + +// 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; +} + +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::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-inl.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_iterator-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_type.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */ +/* 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 { + +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. + iter.assert_at_document_depth(); + 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); +} +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_raw_json_string(); } +template<> simdjson_inline simdjson_result document::get() && noexcept { return get_string(false); } +template<> simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_double(); } +template<> simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_uint64(); } +template<> simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_int64(); } +template<> simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_bool(); } +template<> 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_inline error_code document::get(T &out) && noexcept { + return std::forward(*this).get().get(out); +} + +#if SIMDJSON_EXCEPTIONS +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 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_start_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; + } +} + +} // 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); +} +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_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_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 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 +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); +} + + +} // 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); } +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); } + +#if SIMDJSON_EXCEPTIONS +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 raw_json_string(*doc); } +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::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::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); +} +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_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 +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); +} + + +} // 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: Remove any trailing whitespaces + // This returns a string spanning from start of value to the beginning of the next document (excluded) + return std::string_view(reinterpret_cast(stream->buf) + current_index(), stream->parser->implementation->structural_indexes[++cur_struct_index] - current_index() - 1); + } + 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 doesn't 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; +} + +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 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::unescaped_key(bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.unescaped_key(allow_replacement); +} +simdjson_inline simdjson_result simdjson_result::value() noexcept { + if (error()) { return error(); } + return std::move(first.value()); +} + +} // 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 +} + +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 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 { + return parser->unescape(in, _string_buf_loc, allow_replacement); +} + +simdjson_inline simdjson_result json_iterator::unescape_wobbly(raw_json_string in) noexcept { + return parser->unescape_wobbly(in, _string_buf_loc); +} + +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 + +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): #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; +} + +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(); +} +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); +} + +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; +} + +simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(padded_string_view json) & noexcept { + if (json.padding() < SIMDJSON_PADDING) { return INSUFFICIENT_PADDING; } + + // 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 }); +} + +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(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; } + + // 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(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_inline size_t parser::capacity() const noexcept { + return _capacity; +} +simdjson_inline size_t parser::max_capacity() const noexcept { + return _max_capacity; +} +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()); +} +#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 */ +/* 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 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-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); +} +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 +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 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::current_location() noexcept { + return iter.json_iter().current_location(); +} + +simdjson_inline int32_t value::current_depth() const noexcept{ + return iter.json_iter().depth(); +} + +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: + 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); +} +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_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 +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::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); +} + +} // 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/json_type-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string-inl.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 { + +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 isn't 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 isn't 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); +} +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_start_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_start_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)) { + logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters"); + 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_start_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)) { + logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters"); + 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); +} +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_start_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_start_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_start_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_start_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_start_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_start_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_start_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_start_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 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 */ +/* 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 +#ifdef _M_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 // _M_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 + +#define SIMDJSON_SWAR_NUMBER_PARSING 1 + +#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))); + 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]; + + 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 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 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 + }; +} + +} // unnamed namespace +} // namespace ppc64 +} // namespace simdjson + +#endif // SIMDJSON_PPC64_STRINGPARSING_DEFS_H +/* end file simdjson/ppc64/stringparsing_defs.h */ + +#define SIMDJSON_SKIP_BACKSLASH_SHORT_CIRCUIT 1 +/* end file simdjson/ppc64/begin.h */ +/* including simdjson/generic/ondemand/amalgamated.h for ppc64: #include "simdjson/generic/ondemand/amalgamated.h" */ +/* begin file simdjson/generic/ondemand/amalgamated.h for ppc64 */ +#if defined(SIMDJSON_CONDITIONAL_INCLUDE) && !defined(SIMDJSON_GENERIC_ONDEMAND_DEPENDENCIES_H) +#error simdjson/generic/ondemand/dependencies.h must be included before simdjson/generic/ondemand/amalgamated.h! +#endif + +// Stuff other things depend on +/* including simdjson/generic/ondemand/base.h for ppc64: #include "simdjson/generic/ondemand/base.h" */ +/* begin file simdjson/generic/ondemand/base.h for ppc64 */ +#ifndef SIMDJSON_GENERIC_ONDEMAND_BASE_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_BASE_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { +namespace ppc64 { +/** + * A fast, simple, DOM-like interface that parses JSON as you use it. + * + * Designed for maximum speed and a lower memory profile. + */ +namespace ondemand { + +/** Represents the depth of a JSON value (number of nested arrays/objects). */ +using depth_t = int32_t; + +/** @copydoc simdjson::ppc64::number_type */ +using number_type = simdjson::ppc64::number_type; + +/** @private Position in the JSON buffer indexes */ +using token_position = const uint32_t *; + +class array; +class array_iterator; +class document; +class document_reference; +class document_stream; +class field; +class json_iterator; +enum class json_type; +struct number; +class object; +class object_iterator; +class parser; +class raw_json_string; +class token_iterator; +class value; +class value_iterator; + +} // 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): #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). + */ + 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; + 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; + 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; + + /** + * 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; +}; // 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 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."); + } + + /** + * 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; + + + /** + * 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 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 wasn't there when they aren't). + * + * @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 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 + * 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. + * + * 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 + */ + simdjson_inline std::string_view raw_json_token() 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; + +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; +}; + +} // 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; + 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 + 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 wasn't there when they aren't). + * + * @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_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; + + /** @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; +}; + +} // 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 isn't 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; + + /** + * 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 isn't 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 isn't 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 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 isn't 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; + /// 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; + 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::slow_float_parsing(simdjson_unused const uint8_t * src, W writer); + 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. + * + * ### 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; + /** @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(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). + * + * 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_inline size_t capacity() const noexcept; + /** The maximum capacity of this parser (the largest document it is allowed to process). */ + 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_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; + +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; + /** + * 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 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; + /** + * 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. + * + * @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 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."); + } + /** @overload template simdjson_result get() & noexcept */ + template simdjson_inline simdjson_result get() && 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."); + } + + /** + * 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_inline error_code get(T &out) && noexcept; + +#if SIMDJSON_EXCEPTIONS + /** + * 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. + * + * @returns A value value. + * @exception if a JSON value cannot be found + */ + 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 wasn't there when they aren't). + * + * 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 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 + * get_number_type() is number_type::signed_integer if we have an + * integer that is less 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 + * + * 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; + /** + * 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; + 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 raw_json() noexcept; + simdjson_inline operator document&() const noexcept; + +#if SIMDJSON_EXCEPTIONS + 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 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; +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; + 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 + 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 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; +}; + + +} // 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; + 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 + 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 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; +}; + + +} // 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 = value_type; + + using difference_type = std::ptrdiff_t; + + using iterator_category = std::input_iterator_tag; + + /** + * Default constructor. + */ + simdjson_inline iterator() noexcept; + /** + * Get the current document (or error). + */ + simdjson_inline simdjson_result 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 caller is responsible for omitting it, UTF-8 BOM are + * discouraged. + * + * 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 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) noexcept; + /** + * Get the key as a raw_json_string. Can be used for direct comparison with + * an unescaped C string: e.g., key() == "test". + */ + simdjson_inline raw_json_string 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; + simdjson_inline simdjson_result 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. + * + * @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 wasn't there when they aren't). + * + * 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. + * + * @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; + + /** + * 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 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; + 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 + +/** + * 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 */ + +// 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; +} + +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::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-inl.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_iterator-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_type.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */ +/* 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 { + +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. + iter.assert_at_document_depth(); + 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); +} +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_raw_json_string(); } +template<> simdjson_inline simdjson_result document::get() && noexcept { return get_string(false); } +template<> simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_double(); } +template<> simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_uint64(); } +template<> simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_int64(); } +template<> simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_bool(); } +template<> 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_inline error_code document::get(T &out) && noexcept { + return std::forward(*this).get().get(out); +} + +#if SIMDJSON_EXCEPTIONS +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 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_start_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; + } +} + +} // 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); +} +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_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_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 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 +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); +} + + +} // 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); } +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); } + +#if SIMDJSON_EXCEPTIONS +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 raw_json_string(*doc); } +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::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::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); +} +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_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 +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); +} + + +} // 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); } - // 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); + 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: Remove any trailing whitespaces + // This returns a string spanning from start of value to the beginning of the next document (excluded) + return std::string_view(reinterpret_cast(stream->buf) + current_index(), stream->parser->implementation->structural_indexes[++cur_struct_index] - current_index() - 1); + } + 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 doesn't 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; +} + +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 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::unescaped_key(bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.unescaped_key(allow_replacement); +} +simdjson_inline simdjson_result simdjson_result::value() noexcept { + if (error()) { return error(); } + return std::move(first.value()); +} + +} // 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 +} + +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; + } } - simdjson_inline base8_numeric() : base8() {} - simdjson_inline base8_numeric(const __m128i _value) - : base8(_value) {} + return report_error(TAPE_ERROR, "not enough close braces"); +} - // Store to array - simdjson_inline void store(T dst[16]) const { - vec_vsx_st(this->value, 0, reinterpret_cast<__m128i *>(dst)); - } +SIMDJSON_POP_DISABLE_WARNINGS - // Override to distinguish from bool version - simdjson_inline simd8 operator~() const { return *this ^ 0xFFu; } +simdjson_inline bool json_iterator::at_root() const noexcept { + return position() == root_position(); +} - // 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 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()); + } } - simdjson_inline simd8 &operator-=(const simd8 other) { - *this = *this - other; - return *static_cast *>(this); + if (at_end()) { + return OUT_OF_BOUNDS; } + return reinterpret_cast(token.peek()); +} - // 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); - } +simdjson_inline bool json_iterator::is_alive() const noexcept { + return parser; +} - // 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); +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 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 { + return parser->unescape(in, _string_buf_loc, allow_replacement); +} + +simdjson_inline simdjson_result json_iterator::unescape_wobbly(raw_json_string in) noexcept { + return parser->unescape_wobbly(in, _string_buf_loc); +} + +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 - // we increment by 0x08 the second half of the mask - shufmask = ((__m128i)shufmask) + - ((__m128i)(__vector int){0, 0, 0x08080808, 0x08080808}); +#endif + token.set_position(position); + _depth = child_depth; +} - // 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)); +simdjson_inline error_code json_iterator::consume_character(char c) noexcept { + if (*peek() == c) { + return_current_and_advance(); + return SUCCESS; } + return TAPE_ERROR; +} - 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)); - } -}; +#if SIMDJSON_DEVELOPMENT_CHECKS -// 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); - } +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; +} - // 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 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); } - simdjson_inline simd8 - operator>(const simd8 other) const { - return (__m128i)vec_cmpgt((__vector signed char)this->value, - (__vector signed char)(__m128i)other); + 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; } - simdjson_inline simd8 - operator<(const simd8 other) const { - return (__m128i)vec_cmplt((__vector signed char)this->value, - (__vector signed char)(__m128i)other); + if(is_int64()) { + return double(payload.signed_integer); } -}; + return double(payload.unsigned_integer); +} -// 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); - } +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 { - // 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); - } +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. - // 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(); +// Helper to turn unprintable or newline characters into spaces +static inline char printable_char(char c) { + if (c >= 0x20) { + return c; + } else { + return ' '; } +} - // 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 +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 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]; +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; +} - simd8x64(const simd8x64 &o) = delete; // no copy allowed - simd8x64 & - operator=(const simd8& other) = delete; // no assignment allowed - simd8x64() = delete; // no default constructor allowed +static inline log_level log_threshold() +{ + static log_level threshold = get_log_level_from_env(); + return threshold; +} - 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)} {} +static inline bool should_log(log_level level) +{ + return level >= log_threshold(); +} - 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); - } +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); +} - simdjson_inline simd8 reduce_or() const { - return (this->chunks[0] | this->chunks[1]) | - (this->chunks[2] | this->chunks[3]); - } +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); +} - 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); - } +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++; } +} - 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); - } +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); +} - 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(); - } +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); +} - 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(); +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); + } } +} - 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(); +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); + } } -}; // struct simd8x64 +} -} // namespace simd -} // unnamed namespace +} // namespace logger +} // namespace ondemand } // namespace ppc64 } // namespace simdjson -#endif // SIMDJSON_PPC64_SIMD_INPUT_H -/* end file include/simdjson/ppc64/simd.h */ -/* begin file include/simdjson/generic/jsoncharutils.h */ +#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 { -namespace jsoncharutils { +namespace ondemand { -// 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 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 uint32_t is_structural_or_whitespace(uint8_t c) { - return internal::structural_or_whitespace[c]; +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; } -// 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; +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)); } -// 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 +simdjson_inline simdjson_result object::started(value_iterator &iter) noexcept { + SIMDJSON_TRY( iter.started_object().error() ); + return object(iter); } -#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; +simdjson_inline object object::resume(const value_iterator &iter) noexcept { + return iter; } -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; + +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); +} -using internal::value128; +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; -simdjson_inline value128 full_multiplication(uint64_t value1, uint64_t value2) { - value128 answer; -#if SIMDJSON_REGULAR_VISUAL_STUDIO || SIMDJSON_IS_32BITS -#ifdef _M_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 // _M_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; + // 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; } -} // namespace jsoncharutils -} // unnamed namespace +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 -/* end file include/simdjson/generic/jsoncharutils.h */ -/* begin file include/simdjson/generic/atomparsing.h */ -namespace simdjson { -namespace ppc64 { -namespace { -/// @private -namespace atomparsing { -// The string_to_uint32 is exclusively used to map literal strings to 32-bit values. -// We use memcpy instead of a pointer cast to avoid undefined behaviors since we cannot -// be certain that the character pointer will be properly aligned. -// You might think that using memcpy makes this function expensive, but you'd be wrong. -// All decent optimizing compilers (GCC, clang, Visual Studio) will compile string_to_uint32("false"); -// to the compile-time constant 1936482662. -simdjson_inline uint32_t string_to_uint32(const char* str) { uint32_t val; std::memcpy(&val, str, sizeof(uint32_t)); return val; } +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) {} -// 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_inline simdjson_result simdjson_result::begin() noexcept { + if (error()) { return error(); } + return first.begin(); } - -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_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_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_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { + if (error()) { return error(); } + return first.at_pointer(json_pointer); } -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; +inline simdjson_result simdjson_result::reset() noexcept { + if (error()) { return error(); } + return first.reset(); } -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; } +inline simdjson_result simdjson_result::is_empty() noexcept { + if (error()) { return error(); } + return first.is_empty(); } -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_inline simdjson_result simdjson_result::count_fields() & noexcept { + if (error()) { return error(); } + return first.count_fields(); } -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_inline simdjson_result simdjson_result::raw_json() noexcept { + if (error()) { return error(); } + return first.raw_json(); } - -} // namespace atomparsing -} // unnamed namespace -} // namespace ppc64 } // namespace simdjson -/* end file include/simdjson/generic/atomparsing.h */ -/* begin file include/simdjson/ppc64/stringparsing.h */ -#ifndef SIMDJSON_PPC64_STRINGPARSING_H -#define SIMDJSON_PPC64_STRINGPARSING_H +#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 { +namespace ondemand { -using namespace simd; +// +// object_iterator +// -// 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 object_iterator::object_iterator(const value_iterator &_iter) noexcept + : iter{_iter} +{} - 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 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(); +} - uint32_t bs_bits; - uint32_t quote_bits; -}; // struct backslash_and_quote +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_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_unused error_code error; + if ((error = iter.skip_child() )) { return *this; } - // 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 - }; + simdjson_unused bool has_value; + if ((error = iter.has_next_field().get(has_value) )) { return *this; }; + return *this; } +SIMDJSON_POP_DISABLE_WARNINGS -} // unnamed namespace +// +// ### 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 -#endif // SIMDJSON_PPC64_STRINGPARSING_H -/* end file include/simdjson/ppc64/stringparsing.h */ -/* begin file include/simdjson/ppc64/numberparsing.h */ -#ifndef SIMDJSON_PPC64_NUMBERPARSING_H -#define SIMDJSON_PPC64_NUMBERPARSING_H - -#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); +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) +{ } -} // namespace numberparsing -} // namespace ppc64 -} // namespace simdjson +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; +} -#define SIMDJSON_SWAR_NUMBER_PARSING 1 +} // namespace simdjson -/* begin file include/simdjson/generic/numberparsing.h */ -#include +#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 { -/// @private -namespace numberparsing { - -/** - * 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 -}; - -#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))) -#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)) -#endif - -namespace { +namespace ondemand { -// 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; +simdjson_inline parser::parser(size_t max_capacity) noexcept + : _max_capacity{max_capacity} { } -// 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) +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 - { - // 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; + 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) ); } - // 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 + _capacity = new_capacity; + _max_depth = new_max_depth; + return SUCCESS; +} - // The fast path has now failed, so we are failing back on the slower path. +simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(padded_string_view json) & noexcept { + if (json.padding() < SIMDJSON_PADDING) { return INSUFFICIENT_PADDING; } - // 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; + // 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 }); +} - // 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; - - - // 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 = jsoncharutils::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. - - // 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. In very rare cases, even that - // will not suffice, though it is seemingly very hard to find such a scenario. - // - // 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 = jsoncharutils::full_multiplication(i, simdjson::internal::power_of_five_128[index + 1]); - firstproduct.low += secondproduct.high; - if(secondproduct.high > firstproduct.low) { firstproduct.high++; } - // At this point, we might need to add at most one to firstproduct, but this - // can only change the value of firstproduct.high if firstproduct.low is maximal. - if(simdjson_unlikely(firstproduct.low == 0xFFFFFFFFFFFFFFFF)) { - // This is very unlikely, but if so, we need to do much more work! - return false; - } - } - 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); +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)); +} - // 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 - } - } +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)); +} - mantissa += mantissa & 1; - mantissa >>= 1; +simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(std::string_view json, size_t allocated) & noexcept { + return iterate(padded_string_view(json, allocated)); +} - // 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; +simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(const std::string &json) & noexcept { + return iterate(padded_string_view(json)); } -// 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. +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); +} - // 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()); +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); } -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. +simdjson_warn_unused simdjson_inline simdjson_result parser::iterate_raw(padded_string_view json) & noexcept { + if (json.padding() < SIMDJSON_PADDING) { return INSUFFICIENT_PADDING; } - // 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()); + // 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); } -// 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); +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(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); } -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; +simdjson_inline size_t parser::capacity() const noexcept { + return _capacity; +} +simdjson_inline size_t parser::max_capacity() const noexcept { + return _max_capacity; +} +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; } - // 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 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; +} -#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_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 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 +} // namespace ondemand +} // namespace ppc64 +} // namespace simdjson - // 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. +namespace simdjson { - // 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. +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) {} - // If there were no digits, it's an error. - if (simdjson_unlikely(p == start_exp)) { - return INVALID_NUMBER(src); +} // 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; + } } - // We have a valid positive exponent in exp_number at this point, except that - // it may have overflowed. + return true; +} - // If there were more than 18 digits, we may have overflowed the integer. We have to do - // something!!!! - if (simdjson_unlikely(p > start_exp+18)) { - // Skip leading zeroes: 1e000000000000000000001 is technically valid and doesn't overflow - 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; } +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; + } } - // 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; + 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 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()); } -} // unnamed namespace +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; +} -/** @private */ -template -error_code slow_float_parsing(simdjson_unused const uint8_t * src, W writer) { - double d; - if (parse_float_fallback(src, &d)) { - writer.append_double(d); - return SUCCESS; +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; + } } - return INVALID_NUMBER(src); + if(r[pos] != '"') { return false; } + return true; } -/** @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: This makes a *copy* of the writer and passes it to slow_float_parsing. This happens - // because slow_float_parsing is a non-inlined function. 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. - // This is what forces the skip_double, as well. - error_code error = slow_float_parsing(src, writer); - writer.skip_double(); - return error; + +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; } } - // 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); + 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; } } - 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; + if(r[pos] != '"') { return false; } + return true; } -// for performance analysis, it is sometimes useful to skip parsing -#ifdef SIMDJSON_SKIPNUMBERPARSING +simdjson_unused simdjson_inline bool operator==(const raw_json_string &a, std::string_view c) noexcept { + return a.unsafe_is_equal(c); +} -template -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 bool operator==(std::string_view c, const raw_json_string &a) noexcept { + return a == c; } -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 +simdjson_unused simdjson_inline bool operator!=(const raw_json_string &a, std::string_view c) noexcept { + return !(a == c); +} -// 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) { +simdjson_unused simdjson_inline bool operator!=(std::string_view c, const raw_json_string &a) noexcept { + return !(a == c); +} - // - // 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++; } +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); +} - // If there were no digits, or if the integer starts with 0 and has more than 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); } +simdjson_inline simdjson_warn_unused simdjson_result raw_json_string::unescape_wobbly(json_iterator &iter) const noexcept { + return iter.unescape_wobbly(*this); +} - // - // 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; +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++; } +} - // 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 INVALID_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 INVALID_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); } - } +} // namespace ondemand +} // namespace ppc64 +} // namespace simdjson - // Write unsigned if it doesn't 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; +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 -// Inlineable functions -namespace { +#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 */ -// 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"); +namespace simdjson { -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}; +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)); +} -// 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]); } +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); +} - 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; } +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()); } +} - return i; +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); +} -// 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++; } +inline simdjson_result to_json_string(simdjson_result x) { + if (x.error()) { return x.error(); } + return to_json_string(x.value_unsafe()); +} - // If there were no digits, or if the integer starts with 0 and has more than 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]); } +inline simdjson_result to_json_string(simdjson_result x) { + if (x.error()) { return x.error(); } + return to_json_string(x.value_unsafe()); +} - 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; } - } +inline simdjson_result to_json_string(simdjson_result x) { + if (x.error()) { return x.error(); } + return to_json_string(x.value_unsafe()); +} - return i; +inline simdjson_result to_json_string(simdjson_result x) { + if (x.error()) { return x.error(); } + return to_json_string(x.value_unsafe()); } -// 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++; } +inline simdjson_result to_json_string(simdjson_result x) { + if (x.error()) { return x.error(); } + return to_json_string(x.value_unsafe()); +} +} // namespace simdjson - // If there were no digits, or if the integer starts with 0 and has more than 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; } +namespace simdjson { namespace ppc64 { namespace ondemand { - 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; } +#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); } - - return i; } +#endif -// 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); +#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 - // - // Parse the integer part. - // - // PERF NOTE: we don't use 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 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 there were no digits, or if the integer starts with 0 and has more than 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; +#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 -// 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); +#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 - // - // Parse the integer part. - // - // PERF NOTE: we don't use 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++; } +/* 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 */ - // If there were no digits, or if the integer starts with 0 and has more than 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; +namespace simdjson { +namespace ppc64 { +namespace ondemand { + +simdjson_inline token_iterator::token_iterator( + const uint8_t *_buf, + token_position position +) noexcept : buf{_buf}, _position{position} +{ } -// 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; +simdjson_inline uint32_t token_iterator::current_offset() const noexcept { + return *(_position); +} - // - // Parse the integer part. - // - // PERF 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_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(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_unused simdjson_inline simdjson_result parse_double(const uint8_t * src) noexcept { - // - // Check for minus sign - // - bool negative = (*src == '-'); - src += uint8_t(negative); +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; +} - // - // 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; } +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; +} - // - // 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); +} // namespace ondemand +} // namespace ppc64 +} // namespace simdjson - // 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; - } +namespace simdjson { - // - // Parse the exponent - // - if (*p == 'e' || *p == 'E') { - p++; - bool exp_neg = *p == '-'; - p += exp_neg || *p == '+'; +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) {} - 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; } +} // namespace simdjson - exponent += exp_neg ? 0-exp : exp; - } +#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 + +/* 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 */ - if (jsoncharutils::is_not_structural_or_whitespace(*p)) { return NUMBER_ERROR; } +namespace simdjson { +namespace ppc64 { +namespace ondemand { - overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power; +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; +} - // - // 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; +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); } - return d; } -simdjson_unused simdjson_inline bool is_negative(const uint8_t * src) noexcept { - return (*src == '-'); +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); +} +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(); } -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; +template simdjson_inline error_code value::get(T &out) noexcept { + return get().get(out); } -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++; } - if ( p == src ) { return NUMBER_ERROR; } - if (jsoncharutils::is_structural_or_whitespace(*p)) { - // We have an integer. - // If the number is negative and valid, it must be a signed integer. - if(negative) { return number_type::signed_integer; } - // We want values larger or equal to 9223372036854775808 to be unsigned - // integers, and the other values to be signed integers. - int digit_count = int(p - src); - if(digit_count >= 19) { - const uint8_t * smaller_big_integer = reinterpret_cast("9223372036854775808"); - if((digit_count >= 20) || (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; +#if SIMDJSON_EXCEPTIONS +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 -// 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); +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); +} - // - // 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; } +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); +} - // - // 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); +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); +} - // 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; - } +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]; +} - // - // 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 == '+'; +simdjson_inline simdjson_result value::type() noexcept { + return iter.type(); +} - 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; } +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)); +} - exponent += exp_neg ? 0-exp : exp; - } +simdjson_inline bool value::is_negative() noexcept { + return iter.is_negative(); +} - if ((p != src_end) && jsoncharutils::is_not_structural_or_whitespace(*p)) { return NUMBER_ERROR; } +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(); +} - overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power; +simdjson_inline std::string_view value::raw_json_token() noexcept { + return std::string_view(reinterpret_cast(iter.peek_start()), iter.peek_start_length()); +} - // - // 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; +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(); +} + +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: + return INVALID_JSON_POINTER; } - 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; +} // namespace ondemand +} // namespace ppc64 +} // namespace simdjson - // - // 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; } +namespace simdjson { - // - // 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); +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 {}; +} - // 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; - } +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); +} - // - // Parse the exponent - // - if (*p == 'e' || *p == 'E') { - p++; - bool exp_neg = *p == '-'; - p += exp_neg || *p == '+'; +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); +} +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(); +} - 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; } +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); +} - exponent += exp_neg ? 0-exp : exp; - } +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; +} - if (*p != '"') { return NUMBER_ERROR; } +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_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 +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 - overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power; +simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { + if (error()) { return error(); } + return first.raw_json_token(); +} - // - // 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_inline simdjson_result simdjson_result::current_location() noexcept { + if (error()) { return error(); } + return first.current_location(); } -} // unnamed namespace -#endif // SIMDJSON_SKIPNUMBERPARSING +simdjson_inline simdjson_result simdjson_result::current_depth() const noexcept { + if (error()) { return error(); } + return first.current_depth(); +} -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; - default: SIMDJSON_UNREACHABLE(); - } - return out; +simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { + if (error()) { return error(); } + return first.at_pointer(json_pointer); } -} // namespace numberparsing -} // namespace ppc64 } // namespace simdjson -/* end file include/simdjson/generic/numberparsing.h */ -#endif // SIMDJSON_PPC64_NUMBERPARSING_H -/* end file include/simdjson/ppc64/numberparsing.h */ -/* begin file include/simdjson/ppc64/end.h */ -/* end file include/simdjson/ppc64/end.h */ +#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/json_type-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ -#endif // SIMDJSON_IMPLEMENTATION_PPC64 +namespace simdjson { +namespace ppc64 { +namespace ondemand { -#endif // SIMDJSON_PPC64_H -/* end file include/simdjson/ppc64.h */ -/* begin file include/simdjson/westmere.h */ -#ifndef SIMDJSON_WESTMERE_H -#define SIMDJSON_WESTMERE_H +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(); +} -#if SIMDJSON_IMPLEMENTATION_WESTMERE +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(); +} -#if SIMDJSON_CAN_ALWAYS_RUN_WESTMERE -#define SIMDJSON_TARGET_WESTMERE -#define SIMDJSON_UNTARGET_WESTMERE -#else -#define SIMDJSON_TARGET_WESTMERE SIMDJSON_TARGET_REGION("sse4.2,pclmul,popcnt") -#define SIMDJSON_UNTARGET_WESTMERE SIMDJSON_UNTARGET_REGION +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; +} -namespace simdjson { -/** - * Implementation for Westmere (Intel SSE4.2). - */ -namespace westmere { -} // namespace westmere -} // namespace simdjson - -// -// These two need to be included outside SIMDJSON_TARGET_WESTMERE -// -/* begin file include/simdjson/westmere/implementation.h */ -#ifndef SIMDJSON_WESTMERE_IMPLEMENTATION_H -#define SIMDJSON_WESTMERE_IMPLEMENTATION_H - - -// The constructor may be executed on any host, so we take care not to use SIMDJSON_TARGET_WESTMERE -namespace simdjson { -namespace westmere { - -namespace { -using namespace simdjson; -using namespace simdjson::dom; +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; } -/** - * @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; -}; +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(); +} -} // namespace westmere -} // namespace simdjson +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; +} -#endif // SIMDJSON_WESTMERE_IMPLEMENTATION_H -/* end file include/simdjson/westmere/implementation.h */ -/* begin file include/simdjson/westmere/intrinsics.h */ -#ifndef SIMDJSON_WESTMERE_INTRINSICS_H -#define SIMDJSON_WESTMERE_INTRINSICS_H +simdjson_warn_unused simdjson_inline simdjson_result value_iterator::has_next_field() noexcept { + assert_at_next(); -#if SIMDJSON_VISUAL_STUDIO -// under clang within visual studio, this will include -#include // visual studio or clang -#else -#include // elsewhere -#endif // SIMDJSON_VISUAL_STUDIO + // 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; -#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 + // + // 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 isn't 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; -static_assert(sizeof(__m128i) <= simdjson::SIMDJSON_PADDING, "insufficient padding for westmere"); + // 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; + } -#endif // SIMDJSON_WESTMERE_INTRINSICS_H -/* end file include/simdjson/westmere/intrinsics.h */ + // 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; } + } -// -// The rest need to be inside the region -// -/* begin file include/simdjson/westmere/begin.h */ -// redefining SIMDJSON_IMPLEMENTATION to "westmere" -// #define SIMDJSON_IMPLEMENTATION westmere -SIMDJSON_TARGET_WESTMERE -/* end file include/simdjson/westmere/begin.h */ + // If the loop ended, we're out of fields to look at. + return false; +} -// Declarations -/* begin file include/simdjson/generic/dom_parser_implementation.h */ +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; -namespace simdjson { -namespace westmere { + // 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(); -// 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 + // 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; -static_assert(sizeof(open_container) == 64/8, "Open container must be 64 bits"); + // 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()) { -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{}; +#if SIMDJSON_DEVELOPMENT_CHECKS + // If we're past the end of the object, we're being iterated out of order. + // Note: this isn't 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 + } - 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; + // 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 - 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); + // 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; + } -} // namespace westmere -} // namespace simdjson + // 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; } -namespace simdjson { -namespace westmere { + // 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 -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; + // 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); -// 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; + // 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; + } - _capacity = capacity; - return SUCCESS; + // 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 -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; } +simdjson_warn_unused simdjson_inline simdjson_result value_iterator::field_key() noexcept { + assert_at_next(); - _max_depth = max_depth; - return SUCCESS; + 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); } -} // namespace westmere -} // namespace simdjson -/* end file include/simdjson/generic/dom_parser_implementation.h */ -/* begin file include/simdjson/westmere/bitmanipulation.h */ -#ifndef SIMDJSON_WESTMERE_BITMANIPULATION_H -#define SIMDJSON_WESTMERE_BITMANIPULATION_H - -namespace simdjson { -namespace westmere { -namespace { +simdjson_warn_unused simdjson_inline error_code value_iterator::field_value() noexcept { + assert_at_next(); -// 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 + 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; } -/* 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); +simdjson_warn_unused simdjson_inline simdjson_result value_iterator::start_array() noexcept { + SIMDJSON_TRY( start_container('[', "Not an array", "array") ); + return started_array(); } -/* 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 +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(); } -#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); +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; } -#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)); +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; } -} // unnamed namespace -} // namespace westmere -} // namespace simdjson - -#endif // SIMDJSON_WESTMERE_BITMANIPULATION_H -/* end file include/simdjson/westmere/bitmanipulation.h */ -/* begin file include/simdjson/westmere/bitmask.h */ -#ifndef SIMDJSON_WESTMERE_BITMASK_H -#define SIMDJSON_WESTMERE_BITMASK_H - -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); +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; } -} // unnamed namespace -} // namespace westmere -} // namespace simdjson - -#endif // SIMDJSON_WESTMERE_BITMASK_H -/* end file include/simdjson/westmere/bitmask.h */ -/* begin file include/simdjson/westmere/simd.h */ -#ifndef SIMDJSON_WESTMERE_SIMD_H -#define SIMDJSON_WESTMERE_SIMD_H - - -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; } - }; - - // Forward-declared so they can be used by splat and friends. - template - struct simd8; - - 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); +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(); +} - template - simdjson_inline simd8 prev(const simd8 prev_chunk) const { - return _mm_alignr_epi8(*this, prev_chunk, 16 - N); - } - }; +simdjson_warn_unused simdjson_inline simdjson_result value_iterator::has_next_element() noexcept { + assert_at_next(); - // 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))); } + 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_inline simd8() : base8() {} - simdjson_inline simd8(const __m128i _value) : base8(_value) {} - // Splat constructor - simdjson_inline simd8(bool _value) : base8(splat(_value)) {} +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_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_warn_unused simdjson_inline simdjson_result value_iterator::get_string(bool allow_replacement) noexcept { + return get_raw_json_string().unescape(json_iter(), allow_replacement); +} +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; +} - 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 simdjson_result value_iterator::is_root_integer(bool check_trailing) noexcept { + auto max_len = peek_start_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 base8_numeric() : base8() {} - simdjson_inline base8_numeric(const __m128i _value) : base8(_value) {} +simdjson_inline simdjson_result value_iterator::get_root_number_type(bool check_trailing) noexcept { + auto max_len = peek_start_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)) { + logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters"); + 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_start_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)) { + logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters"); + 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); +} +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_start_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_start_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_start_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; + } - // Store to array - simdjson_inline void store(T dst[16]) const { return _mm_storeu_si128(reinterpret_cast<__m128i *>(dst), *this); } + 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_start_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; + } - // Override to distinguish from bool version - simdjson_inline simd8 operator~() const { return *this ^ 0xFFu; } + 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_start_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; +} - // 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); } +simdjson_warn_unused simdjson_inline simdjson_result value_iterator::get_root_double_in_string(bool check_trailing) noexcept { + auto max_len = peek_start_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_start_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_start_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; +} - // 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); - } +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 ); - // 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); - } + return _json_iter->skip_child(depth()); +} - 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 - )); - } - }; +simdjson_inline value_iterator value_iterator::child() const noexcept { + assert_at_child(); + return { _json_iter, depth()+1, _json_iter->token.position() }; +} - // 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 - ); - } +// 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 - // 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); } - }; +simdjson_inline bool value_iterator::at_end() const noexcept { + return _json_iter->at_end(); +} - // 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 - ); - } +simdjson_inline bool value_iterator::at_start() const noexcept { + return _json_iter->token.position() == start_position(); +} - // 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); } +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; +} - // 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(); } +simdjson_inline void value_iterator::abandon() noexcept { + _json_iter->abandon(); +} - // 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)); } - }; +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; +} - 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]; +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()); +} - 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 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_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)} {} + // 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 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 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 simd8 reduce_or() const { - return (this->chunks[0] | this->chunks[1]) | (this->chunks[2] | this->chunks[3]); - } + // 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 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 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(); + } - 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(); - } + return SUCCESS; +} - 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 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(); } -} // namespace simd -} // unnamed namespace -} // namespace westmere -} // namespace simdjson + 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(); } -#endif // SIMDJSON_WESTMERE_SIMD_INPUT_H -/* end file include/simdjson/westmere/simd.h */ -/* begin file include/simdjson/generic/jsoncharutils.h */ + assert_at_non_root_start(); + return _json_iter->peek(); +} -namespace simdjson { -namespace westmere { -namespace { -namespace jsoncharutils { +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; } -// 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]; + 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; } -simdjson_inline uint32_t is_structural_or_whitespace(uint8_t c) { - return internal::structural_or_whitespace[c]; + assert_at_non_root_start(); + _json_iter->return_current_and_advance(); + _json_iter->ascend_to(depth()-1); } -// 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; +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; } -// 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 +simdjson_inline bool value_iterator::is_at_start() const noexcept { + return position() == start_position(); } -#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; +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() == '"'; } -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; + +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; } -#endif -using internal::value128; +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 ); +} -simdjson_inline value128 full_multiplication(uint64_t value1, uint64_t value2) { - value128 answer; -#if SIMDJSON_REGULAR_VISUAL_STUDIO || SIMDJSON_IS_32BITS -#ifdef _M_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 // _M_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; +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 ); } -} // namespace jsoncharutils -} // unnamed namespace -} // namespace westmere -} // namespace simdjson -/* end file include/simdjson/generic/jsoncharutils.h */ -/* begin file include/simdjson/generic/atomparsing.h */ -namespace simdjson { -namespace westmere { -namespace { -/// @private -namespace atomparsing { - -// The string_to_uint32 is exclusively used to map literal strings to 32-bit values. -// We use memcpy instead of a pointer cast to avoid undefined behaviors since we cannot -// be certain that the character pointer will be properly aligned. -// You might think that using memcpy makes this function expensive, but you'd be wrong. -// All decent optimizing compilers (GCC, clang, Visual Studio) will compile string_to_uint32("false"); -// to the compile-time constant 1936482662. -simdjson_inline uint32_t string_to_uint32(const char* str) { uint32_t val; std::memcpy(&val, str, sizeof(uint32_t)); return val; } +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); +} -// 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_inline void value_iterator::move_at_container_start() noexcept { + _json_iter->_depth = _depth; + _json_iter->token.set_position(_start_position + 1); } -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_inline simdjson_result value_iterator::reset_array() noexcept { + if(error()) { return error(); } + move_at_container_start(); + return started_array(); } -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_inline simdjson_result value_iterator::reset_object() noexcept { + if(error()) { return error(); } + move_at_container_start(); + return started_object(); } -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; +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 ); } -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; } +inline void value_iterator::assert_at_root() const noexcept { + assert_at_start(); + SIMDJSON_ASSUME( _depth == 1 ); } -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; +inline void value_iterator::assert_at_non_root_start() const noexcept { + assert_at_start(); + SIMDJSON_ASSUME( _depth > 1 ); } -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; } +inline void value_iterator::assert_is_valid() const noexcept { + SIMDJSON_ASSUME( _json_iter != nullptr ); } -} // namespace atomparsing -} // unnamed namespace -} // namespace westmere -} // namespace simdjson -/* end file include/simdjson/generic/atomparsing.h */ -/* begin file include/simdjson/westmere/stringparsing.h */ -#ifndef SIMDJSON_WESTMERE_STRINGPARSING_H -#define SIMDJSON_WESTMERE_STRINGPARSING_H +simdjson_inline bool value_iterator::is_valid() const noexcept { + return _json_iter != nullptr; +} -namespace simdjson { -namespace westmere { -namespace { +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; + } +} -using namespace simd; +simdjson_inline token_position value_iterator::start_position() const noexcept { + return _start_position; +} -// 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 token_position value_iterator::position() const noexcept { + return _json_iter->position(); +} - 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 token_position value_iterator::end_position() const noexcept { + return _json_iter->end_position(); +} - uint32_t bs_bits; - uint32_t quote_bits; -}; // struct backslash_and_quote +simdjson_inline token_position value_iterator::last_position() const noexcept { + return _json_iter->last_position(); +} -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 error_code value_iterator::report_error(error_code error, const char *message) noexcept { + return _json_iter->report_error(error, message); } -} // unnamed namespace -} // namespace westmere +} // namespace ondemand +} // namespace ppc64 } // namespace simdjson -#endif // SIMDJSON_WESTMERE_STRINGPARSING_H -/* end file include/simdjson/westmere/stringparsing.h */ -/* begin file include/simdjson/westmere/numberparsing.h */ -#ifndef SIMDJSON_WESTMERE_NUMBERPARSING_H -#define SIMDJSON_WESTMERE_NUMBERPARSING_H - 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 -} +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 numberparsing -} // namespace westmere } // namespace simdjson -#define SIMDJSON_SWAR_NUMBER_PARSING 1 - -/* begin file include/simdjson/generic/numberparsing.h */ -#include +#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 */ +/* 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 { -namespace westmere { -/// @private -namespace numberparsing { - /** - * The type of a JSON number + * Implementation for Westmere (Intel SSE4.2). */ -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 -}; +namespace westmere { -#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))) -#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)) -#endif +class implementation; namespace { +namespace simd { -// 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 +template struct simd8; +template struct simd8x64; - // The fast path has now failed, so we are failing back on the slower path. +} // namespace simd +} // unnamed namespace - // 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; - } +} // 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 - // 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; +/* 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 - // We want the most significant bit of i to be 1. Shift if needed. - int lz = leading_zeroes(i); - i <<= lz; +#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 - // 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 = jsoncharutils::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. +static_assert(sizeof(__m128i) <= simdjson::SIMDJSON_PADDING, "insufficient padding for westmere"); - // 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. In very rare cases, even that - // will not suffice, though it is seemingly very hard to find such a scenario. - // - // 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 = jsoncharutils::full_multiplication(i, simdjson::internal::power_of_five_128[index + 1]); - firstproduct.low += secondproduct.high; - if(secondproduct.high > firstproduct.low) { firstproduct.high++; } - // At this point, we might need to add at most one to firstproduct, but this - // can only change the value of firstproduct.high if firstproduct.low is maximal. - if(simdjson_unlikely(firstproduct.low == 0xFFFFFFFFFFFFFFFF)) { - // This is very unlikely, but if so, we need to do much more work! - return false; - } - } - 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); +#endif // SIMDJSON_WESTMERE_INTRINSICS_H +/* end file simdjson/westmere/intrinsics.h */ - // 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 - } - } +#if !SIMDJSON_CAN_ALWAYS_RUN_WESTMERE +SIMDJSON_TARGET_REGION("sse4.2,pclmul,popcnt") +#endif - mantissa += mantissa & 1; - mantissa >>= 1; +/* 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 - // 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; -} +/* 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 */ -// 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. +namespace simdjson { +namespace westmere { +namespace { - // 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 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 } -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. +/* 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); +} - // 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()); +/* 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 } -// 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); +#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 -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 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 } -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; +} // unnamed namespace +} // namespace westmere +} // namespace simdjson -#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; +#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); } -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 +} // unnamed namespace +} // namespace westmere +} // namespace simdjson - // 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. +#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 - // 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. +/* 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 - // 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. +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #include "simdjson/base.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ - // If there were more than 18 digits, we may have overflowed the integer. We have to do - // something!!!! - if (simdjson_unlikely(p > start_exp+18)) { - // Skip leading zeroes: 1e000000000000000000001 is technically valid and doesn't overflow - 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; -} +// 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 { -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); -} +class implementation; -} // unnamed namespace +namespace { +namespace simd { -/** @private */ -template -error_code slow_float_parsing(simdjson_unused const uint8_t * src, W writer) { - double d; - if (parse_float_fallback(src, &d)) { - writer.append_double(d); - return SUCCESS; - } - return INVALID_NUMBER(src); -} +template struct simd8; +template struct simd8x64; -/** @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: This makes a *copy* of the writer and passes it to slow_float_parsing. This happens - // because slow_float_parsing is a non-inlined function. 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. - // This is what forces the skip_double, as well. - error_code error = slow_float_parsing(src, writer); - writer.skip_double(); - 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; -} +} // namespace simd +} // unnamed namespace -// for performance analysis, it is sometimes useful to skip parsing -#ifdef SIMDJSON_SKIPNUMBERPARSING +} // namespace westmere +} // namespace simdjson -template -simdjson_inline error_code parse_number(const uint8_t *const, W &writer) { - writer.append_s64(0); // always write zero - return SUCCESS; // always succeeds -} +#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 -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; } +/* 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 -// 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) { - // - // Check for minus sign - // - bool negative = (*src == '-'); - const uint8_t *p = src + uint8_t(negative); +#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 - // - // Parse the integer part. - // - // PERF NOTE: we don't use 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++; } +static_assert(sizeof(__m128i) <= simdjson::SIMDJSON_PADDING, "insufficient padding for westmere"); - // If there were no digits, or if the integer starts with 0 and has more than 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); } +#endif // SIMDJSON_WESTMERE_INTRINSICS_H +/* end file simdjson/westmere/intrinsics.h */ - // - // 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; - } +/* 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 */ - // 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 INVALID_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 INVALID_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); } - } +namespace simdjson { +namespace westmere { +namespace numberparsing { - // Write unsigned if it doesn't 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; +/** @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 } -// Inlineable functions +/** @private */ +simdjson_inline internal::value128 full_multiplication(uint64_t value1, uint64_t value2) { + internal::value128 answer; +#if SIMDJSON_REGULAR_VISUAL_STUDIO || SIMDJSON_IS_32BITS +#ifdef _M_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 // _M_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 { -// 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"); + template + struct base { + __m128i value; -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}; + // 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; } -// 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++; } + // 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; } + }; - // If there were no digits, or if the integer starts with 0 and has more than 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]); } + template> + struct base8: base> { + typedef uint16_t bitmask_t; + typedef uint32_t bitmask2_t; - 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; } - } + simdjson_inline base8() : base>() {} + simdjson_inline base8(const __m128i _value) : base>(_value) {} - return i; -} + friend simdjson_inline Mask operator==(const simd8 lhs, const simd8 rhs) { return _mm_cmpeq_epi8(lhs, rhs); } + static const int SIZE = sizeof(base>::value); -// 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++; } + template + simdjson_inline simd8 prev(const simd8 prev_chunk) const { + return _mm_alignr_epi8(*this, prev_chunk, 16 - N); + } + }; - // If there were no digits, or if the integer starts with 0 and has more than 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]); } + // 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))); } - 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; } - } + simdjson_inline simd8() : base8() {} + simdjson_inline simd8(const __m128i _value) : base8(_value) {} + // Splat constructor + simdjson_inline simd8(bool _value) : base8(splat(_value)) {} - return i; -} + 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; } + }; -// 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++; } + 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 + ); + } - // If there were no digits, or if the integer starts with 0 and has more than 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; } + simdjson_inline base8_numeric() : base8() {} + simdjson_inline base8_numeric(const __m128i _value) : base8(_value) {} - 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; } - } + // Store to array + simdjson_inline void store(T dst[16]) const { return _mm_storeu_si128(reinterpret_cast<__m128i *>(dst), *this); } - return i; -} + // Override to distinguish from bool version + simdjson_inline simd8 operator~() const { return *this ^ 0xFFu; } -// 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); + // 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); } - // - // Parse the integer part. - // - // PERF NOTE: we don't use 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++; } + // 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); + } - // If there were no digits, or if the integer starts with 0 and has more than 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; -} + // 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); + } -// 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); + 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 + )); + } + }; - // - // Parse the integer part. - // - // PERF NOTE: we don't use 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++; } + // 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 + ); + } - // If there were no digits, or if the integer starts with 0 and has more than 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; -} + // 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); } + }; -// 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; + // 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 + ); + } - // - // Parse the integer part. - // - // PERF 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++; } + // 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); } - // If there were no digits, or if the integer starts with 0 and 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; -} + // 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(); } -simdjson_unused simdjson_inline simdjson_result parse_double(const uint8_t * src) noexcept { - // - // Check for minus sign - // - bool negative = (*src == '-'); - src += uint8_t(negative); + // 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]; + + 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); + } - // - // 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; } + 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(); + } - // - // 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); + 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(); + } - // 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; + 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(); } - } else { - overflow = p-src > 19; - } + }; // struct simd8x64 - // - // Parse the exponent - // - if (*p == 'e' || *p == 'E') { - p++; - bool exp_neg = *p == '-'; - p += exp_neg || *p == '+'; +} // namespace simd +} // unnamed namespace +} // namespace westmere +} // namespace simdjson - 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; } +#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 - exponent += exp_neg ? 0-exp : exp; - } +/* 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 */ - if (jsoncharutils::is_not_structural_or_whitespace(*p)) { return NUMBER_ERROR; } +namespace simdjson { +namespace westmere { +namespace { - overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power; +// 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 +} - // - // 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; +/* 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); } -simdjson_unused simdjson_inline bool is_negative(const uint8_t * src) noexcept { - return (*src == '-'); +/* 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 } -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; +#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_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++; } - if ( p == src ) { return NUMBER_ERROR; } - if (jsoncharutils::is_structural_or_whitespace(*p)) { - // We have an integer. - // If the number is negative and valid, it must be a signed integer. - if(negative) { return number_type::signed_integer; } - // We want values larger or equal to 9223372036854775808 to be unsigned - // integers, and the other values to be signed integers. - int digit_count = int(p - src); - if(digit_count >= 19) { - const uint8_t * smaller_big_integer = reinterpret_cast("9223372036854775808"); - if((digit_count >= 20) || (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; +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 } -// 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); +} // unnamed namespace +} // namespace westmere +} // namespace simdjson - // - // 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; } +#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 - // - // 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); +/* 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 */ - // 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; - } +namespace simdjson { +namespace westmere { +namespace { +namespace simd { - // - // 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 == '+'; + template + struct base { + __m128i value; - 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; } + // Zero constructor + simdjson_inline base() : value{__m128i()} {} - exponent += exp_neg ? 0-exp : exp; - } + // Conversion from SIMD register + simdjson_inline base(const __m128i _value) : value(_value) {} - if ((p != src_end) && jsoncharutils::is_not_structural_or_whitespace(*p)) { return NUMBER_ERROR; } + // Conversion to SIMD register + simdjson_inline operator const __m128i&() const { return this->value; } + simdjson_inline operator __m128i&() { return this->value; } - overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power; + // 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; } + }; - // - // 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; -} + template> + struct base8: base> { + typedef uint16_t bitmask_t; + typedef uint32_t bitmask2_t; -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; + simdjson_inline base8() : base>() {} + simdjson_inline base8(const __m128i _value) : base>(_value) {} - // - // 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; } + friend simdjson_inline Mask operator==(const simd8 lhs, const simd8 rhs) { return _mm_cmpeq_epi8(lhs, rhs); } - // - // 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); + static const int SIZE = sizeof(base>::value); - // 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; + template + simdjson_inline simd8 prev(const simd8 prev_chunk) const { + return _mm_alignr_epi8(*this, prev_chunk, 16 - N); } - } else { - overflow = p-src > 19; - } + }; - // - // Parse the exponent - // - if (*p == 'e' || *p == 'E') { - p++; - bool exp_neg = *p == '-'; - p += exp_neg || *p == '+'; + // 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))); } - 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; } + simdjson_inline simd8() : base8() {} + simdjson_inline simd8(const __m128i _value) : base8(_value) {} + // Splat constructor + simdjson_inline simd8(bool _value) : base8(splat(_value)) {} - exponent += exp_neg ? 0-exp : exp; - } + 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; } + }; - if (*p != '"') { return NUMBER_ERROR; } + 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 + ); + } - overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power; + simdjson_inline base8_numeric() : base8() {} + simdjson_inline base8_numeric(const __m128i _value) : base8(_value) {} - // - // 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; -} + // Store to array + simdjson_inline void store(T dst[16]) const { return _mm_storeu_si128(reinterpret_cast<__m128i *>(dst), *this); } -} // unnamed namespace -#endif // SIMDJSON_SKIPNUMBERPARSING + // Override to distinguish from bool version + simdjson_inline simd8 operator~() const { return *this ^ 0xFFu; } -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; - default: SIMDJSON_UNREACHABLE(); + // 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 + ); } - return out; -} - -} // namespace numberparsing -} // namespace westmere -} // namespace simdjson -/* end file include/simdjson/generic/numberparsing.h */ -#endif // SIMDJSON_WESTMERE_NUMBERPARSING_H -/* end file include/simdjson/westmere/numberparsing.h */ -/* begin file include/simdjson/westmere/end.h */ -SIMDJSON_UNTARGET_WESTMERE -/* end file include/simdjson/westmere/end.h */ + // 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); } -#endif // SIMDJSON_IMPLEMENTATION_WESTMERE -#endif // SIMDJSON_WESTMERE_COMMON_H -/* end file include/simdjson/westmere.h */ + // 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(); } -// Builtin implementation + // 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)); } + }; -SIMDJSON_POP_DISABLE_WARNINGS + 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]; -#endif // SIMDJSON_IMPLEMENTATIONS_H -/* end file include/simdjson/implementations.h */ + simd8x64(const simd8x64& o) = delete; // no copy allowed + simd8x64& operator=(const simd8& other) = delete; // no assignment allowed + simd8x64() = delete; // no default constructor allowed -// Determine the best builtin implementation -#ifndef SIMDJSON_BUILTIN_IMPLEMENTATION -#if SIMDJSON_CAN_ALWAYS_RUN_ICELAKE -#define SIMDJSON_BUILTIN_IMPLEMENTATION icelake -#elif SIMDJSON_CAN_ALWAYS_RUN_HASWELL -#define SIMDJSON_BUILTIN_IMPLEMENTATION haswell -#elif SIMDJSON_CAN_ALWAYS_RUN_WESTMERE -#define SIMDJSON_BUILTIN_IMPLEMENTATION westmere -#elif SIMDJSON_CAN_ALWAYS_RUN_ARM64 -#define SIMDJSON_BUILTIN_IMPLEMENTATION arm64 -#elif SIMDJSON_CAN_ALWAYS_RUN_PPC64 -#define SIMDJSON_BUILTIN_IMPLEMENTATION ppc64 -#elif SIMDJSON_CAN_ALWAYS_RUN_FALLBACK -#define SIMDJSON_BUILTIN_IMPLEMENTATION fallback -#else -#error "All possible implementations (including fallback) have been disabled! simdjson will not run." -#endif -#endif // SIMDJSON_BUILTIN_IMPLEMENTATION + 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)} {} -// redefining SIMDJSON_IMPLEMENTATION to "SIMDJSON_BUILTIN_IMPLEMENTATION" -// #define SIMDJSON_IMPLEMENTATION SIMDJSON_BUILTIN_IMPLEMENTATION + 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); + } -// ondemand is only compiled as part of the builtin implementation at present + simdjson_inline simd8 reduce_or() const { + return (this->chunks[0] | this->chunks[1]) | (this->chunks[2] | this->chunks[3]); + } -// Interface declarations -/* begin file include/simdjson/generic/implementation_simdjson_result_base.h */ -namespace simdjson { -namespace SIMDJSON_BUILTIN_IMPLEMENTATION { + 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); + } -// 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 { + 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); + } - /** - * Create a new empty result with error = UNINITIALIZED. - */ - simdjson_inline implementation_simdjson_result_base() noexcept = default; + 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(); + } - /** - * Create a new error result. - */ - simdjson_inline implementation_simdjson_result_base(error_code error) noexcept; + 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(); + } - /** - * Create a new successful result. - */ - simdjson_inline implementation_simdjson_result_base(T &&value) noexcept; + 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 - /** - * 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; +} // namespace simd +} // unnamed namespace +} // namespace westmere +} // namespace simdjson - /** - * 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; +#endif // SIMDJSON_WESTMERE_SIMD_INPUT_H +/* end file simdjson/westmere/simd.h */ - /** - * 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; +namespace simdjson { +namespace westmere { +namespace { - /** - * The error. - */ - simdjson_inline error_code error() const noexcept; +using namespace simd; -#if SIMDJSON_EXCEPTIONS +// 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); - /** - * Get the result value. - * - * @throw simdjson_error if there was an error. - */ - simdjson_inline T& value() & noexcept(false); + 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); } - /** - * Take the result value (move it). - * - * @throw simdjson_error if there was an error. - */ - simdjson_inline T&& value() && noexcept(false); + uint32_t bs_bits; + uint32_t quote_bits; +}; // struct backslash_and_quote - /** - * Take the result value (move it). - * - * @throw simdjson_error if there was an error. - */ - simdjson_inline T&& take_value() && noexcept(false); +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 + }; +} - /** - * Cast to the value (will throw on error). - * - * @throw simdjson_error if there was an error. - */ - simdjson_inline operator T&&() && noexcept(false); +} // unnamed namespace +} // namespace westmere +} // namespace simdjson +#endif // SIMDJSON_WESTMERE_STRINGPARSING_DEFS_H +/* end file simdjson/westmere/stringparsing_defs.h */ +/* end file simdjson/westmere/begin.h */ +/* including simdjson/generic/ondemand/amalgamated.h for westmere: #include "simdjson/generic/ondemand/amalgamated.h" */ +/* begin file simdjson/generic/ondemand/amalgamated.h for westmere */ +#if defined(SIMDJSON_CONDITIONAL_INCLUDE) && !defined(SIMDJSON_GENERIC_ONDEMAND_DEPENDENCIES_H) +#error simdjson/generic/ondemand/dependencies.h must be included before simdjson/generic/ondemand/amalgamated.h! +#endif -#endif // SIMDJSON_EXCEPTIONS +// Stuff other things depend on +/* including simdjson/generic/ondemand/base.h for westmere: #include "simdjson/generic/ondemand/base.h" */ +/* begin file simdjson/generic/ondemand/base.h for westmere */ +#ifndef SIMDJSON_GENERIC_ONDEMAND_BASE_H - /** - * 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 +/* 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_BUILTIN_IMPLEMENTATION -} // namespace simdjson -/* end file include/simdjson/generic/implementation_simdjson_result_base.h */ -/* begin file include/simdjson/generic/ondemand.h */ namespace simdjson { -namespace SIMDJSON_BUILTIN_IMPLEMENTATION { +namespace westmere { /** * A fast, simple, DOM-like interface that parses JSON as you use it. * @@ -22911,2593 +78010,2845 @@ namespace ondemand { /** Represents the depth of a JSON value (number of nested arrays/objects). */ using depth_t = int32_t; -/** @copydoc simdjson::SIMDJSON_BUILTIN_IMPLEMENTATION::numberparsing::number_type */ -using number_type = simdjson::SIMDJSON_BUILTIN_IMPLEMENTATION::numberparsing::number_type; - -} // namespace ondemand -} // namespace SIMDJSON_BUILTIN_IMPLEMENTATION -} // namespace simdjson - -/* begin file include/simdjson/generic/ondemand/json_type.h */ -namespace simdjson { -namespace SIMDJSON_BUILTIN_IMPLEMENTATION { -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) -}; - -class value_iterator; - -/** - * 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 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::slow_float_parsing(simdjson_unused const uint8_t * src, W writer); - 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 SIMDJSON_BUILTIN_IMPLEMENTATION -} // namespace simdjson - -namespace simdjson { - -template<> -struct simdjson_result : public SIMDJSON_BUILTIN_IMPLEMENTATION::implementation_simdjson_result_base { -public: - simdjson_inline simdjson_result(SIMDJSON_BUILTIN_IMPLEMENTATION::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 -/* end file include/simdjson/generic/ondemand/json_type.h */ -/* begin file include/simdjson/generic/ondemand/token_position.h */ -namespace simdjson { -namespace SIMDJSON_BUILTIN_IMPLEMENTATION { -namespace ondemand { +/** @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 *; -} // namespace ondemand -} // namespace SIMDJSON_BUILTIN_IMPLEMENTATION -} // namespace simdjson -/* end file include/simdjson/generic/ondemand/token_position.h */ -/* begin file include/simdjson/generic/ondemand/logger.h */ -namespace simdjson { -namespace SIMDJSON_BUILTIN_IMPLEMENTATION { -namespace ondemand { - +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; -// 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 { - -#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; -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) noexcept; -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) 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 SIMDJSON_BUILTIN_IMPLEMENTATION +} // namespace westmere } // namespace simdjson -/* end file include/simdjson/generic/ondemand/logger.h */ -/* begin file include/simdjson/generic/ondemand/raw_json_string.h */ + +#endif // SIMDJSON_GENERIC_ONDEMAND_BASE_H +/* end file simdjson/generic/ondemand/base.h for westmere */ +/* including simdjson/generic/ondemand/value_iterator.h for westmere: #include "simdjson/generic/ondemand/value_iterator.h" */ +/* begin file simdjson/generic/ondemand/value_iterator.h for westmere */ +#ifndef SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { -namespace SIMDJSON_BUILTIN_IMPLEMENTATION { +namespace westmere { namespace ondemand { -class object; -class parser; -class json_iterator; - /** - * 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. + * Iterates through a single JSON value at a particular depth. * - * 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(). + * 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 raw_json_string { +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; + /** - * Create a new invalid raw_json_string. - * - * Exists so you can declare a variable and later assign to it before use. + * Denote that we're starting a document. */ - simdjson_inline raw_json_string() noexcept = default; + simdjson_inline void start_document() 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. + * Skips a non-iterated or partially-iterated JSON value, whether it is a scalar, array or object. * - * It *must* be terminated by a ", and be a valid JSON string. + * Optimized for scalars. */ - simdjson_inline raw_json_string(const uint8_t * _buf) noexcept; + simdjson_warn_unused simdjson_inline error_code skip_child() noexcept; + /** - * Get the raw pointer to the beginning of the string in the JSON (just after the "). + * 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. * - * It is possible for this function to return a null pointer if the instance - * has outlived its existence. + * @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse". */ - simdjson_inline const char * raw() const noexcept; + simdjson_inline simdjson_result type() 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. + * @addtogroup object Object iteration * - * 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. + * 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. * - * 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. + * Start an object iteration. * - * Performance: the comparison is done byte-by-byte which might be inefficient for - * long strings. + * @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. * - * If target is a compile-time constant, and your compiler likes you, - * you should be able to do the following without performance penalty... + * @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. * - * static_assert(raw_json_string::is_free_from_unescaped_quote(target), ""); - * s.unsafe_is_equal(target); + * @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_inline bool unsafe_is_equal(std::string_view target) const noexcept; - + simdjson_warn_unused simdjson_inline error_code check_root_object() 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. + * Start an object iteration after the user has already checked and moved past the {. * - * If target is a compile-time constant, and your compiler likes you, - * you should be able to do the following without performance penalty... + * Does not move the iterator unless the object is empty ({}). * - * static_assert(raw_json_string::is_free_from_unescaped_quote(target), ""); - * s.unsafe_is_equal(target); + * @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_inline bool unsafe_is_equal(const char* target) const noexcept; - + simdjson_warn_unused simdjson_inline simdjson_result started_object() 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). + * 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_inline bool is_equal(std::string_view target) const noexcept; + simdjson_warn_unused simdjson_inline simdjson_result started_root_object() noexcept; /** - * This compares the current instance to the C string target: returns true if - * they are byte-by-byte equal (no escaping is done). + * 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_inline bool is_equal(const char* target) const noexcept; + simdjson_warn_unused simdjson_inline simdjson_result has_next_field() 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!). + * Get the current field's key. */ - 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: - + simdjson_warn_unused simdjson_inline simdjson_result field_key() noexcept; /** - * This will set the inner pointer to zero, effectively making - * this instance unusable. + * Pass the : in the field and move to its value. */ - simdjson_inline void consume() noexcept { buf = nullptr; } + simdjson_warn_unused simdjson_inline error_code field_value() noexcept; /** - * Checks whether the inner pointer is non-null and thus usable. + * 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_inline simdjson_warn_unused bool alive() const noexcept { return buf != nullptr; } + simdjson_warn_unused simdjson_inline error_code find_field(const std::string_view key) noexcept; /** - * Unescape this JSON string, replacing \\ with \, \n with newline, etc. - * The result will be a valid UTF-8. + * 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. * - * ## IMPORTANT: string_view lifetime + * Assumes you have called next_field() or otherwise matched the previous value. * - * The string_view is only valid until the next parse() call on the parser. + * This means the iterator must be sitting at the next key: * - * @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. + * ``` + * { "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_inline simdjson_warn_unused simdjson_result unescape(json_iterator &iter, bool allow_replacement) const noexcept; + simdjson_warn_unused simdjson_inline simdjson_result find_field_raw(const std::string_view key) 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/ + * Find the field with the given key without regard to order, and *without* unescaping. * - * ## IMPORTANT: string_view lifetime + * This is an unordered object lookup: if the field is not found initially, it will cycle around and scan from the beginning. * - * The string_view is only valid until the next parse() call on the parser. + * Assumes you have called next_field() or otherwise matched the previous value. * - * @param iter A json_iterator, which contains a buffer where the string will be written. + * 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_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 SIMDJSON_BUILTIN_IMPLEMENTATION -} // namespace simdjson - -namespace simdjson { - -template<> -struct simdjson_result : public SIMDJSON_BUILTIN_IMPLEMENTATION::implementation_simdjson_result_base { -public: - simdjson_inline simdjson_result(SIMDJSON_BUILTIN_IMPLEMENTATION::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(SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::json_iterator &iter, bool allow_replacement) const noexcept; - simdjson_inline simdjson_warn_unused simdjson_result unescape_wobbly(SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::json_iterator &iter) const noexcept; -}; + simdjson_warn_unused simdjson_inline simdjson_result find_field_unordered_raw(const std::string_view key) noexcept; -} // namespace simdjson -/* end file include/simdjson/generic/ondemand/raw_json_string.h */ -/* begin file include/simdjson/generic/ondemand/token_iterator.h */ -namespace simdjson { -namespace SIMDJSON_BUILTIN_IMPLEMENTATION { -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. + * @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. + * @{ */ - 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). + * 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_inline const uint8_t *return_current_and_advance() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result start_array() noexcept; /** - * Reports the current offset in bytes from the start of the underlying buffer. + * 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_inline uint32_t current_offset() const noexcept; + simdjson_warn_unused simdjson_inline simdjson_result start_root_array() 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. + * Checks whether an array could be started from the root. May be called by start_root_array. * - * TODO consider a string_view, assuming the length will get stripped out by the optimizer when - * it isn't used ... + * @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_inline const uint8_t *peek(int32_t delta=0) const noexcept; + simdjson_warn_unused simdjson_inline error_code check_root_array() noexcept; /** - * Get the maximum length of the JSON text for a given token. + * Start an array iteration, after the user has already checked and moved past the [. * - * The length will include any whitespace at the end of the token. + * Does not move the iterator unless the array is empty ([]). * - * @param delta The relative position of the token to retrieve. e.g. 0 = current token, - * 1 = next token, -1 = prev token. + * @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_inline uint32_t peek_length(int32_t delta=0) const noexcept; - + simdjson_warn_unused simdjson_inline simdjson_result started_array() noexcept; /** - * Get the JSON text for a given token. - * - * This is not null-terminated; it is a view into the JSON. + * Start an array iteration from the root, after the user has already checked and moved past the [. * - * @param position The position of the token. + * 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_inline const uint8_t *peek(token_position position) const noexcept; + simdjson_warn_unused simdjson_inline simdjson_result started_root_array() noexcept; + /** - * Get the maximum length of the JSON text for a given token. + * Moves to the next element in an array. * - * The length will include any whitespace at the end of the token. + * Looks for , and ]. If ] is found, the array is finished and the iterator advances past it. + * Otherwise, it advances to the next value. * - * @param position The position of the token. + * @return Whether there is another element in the array. + * @error TAPE_ERROR If there is a comma missing between elements. */ - simdjson_inline uint32_t peek_length(token_position position) const noexcept; + simdjson_warn_unused simdjson_inline simdjson_result has_next_element() noexcept; /** - * Return the current index. + * Get a child value iterator. */ - simdjson_inline token_position position() const noexcept; + simdjson_warn_unused simdjson_inline value_iterator child() const noexcept; + + /** @} */ + /** - * Reset to a previously saved index. + * @defgroup scalar Scalar values + * @addtogroup scalar + * @{ */ - 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_warn_unused simdjson_inline simdjson_result get_string(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_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_warn_unused simdjson_inline simdjson_result get_root_string(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: - simdjson_inline token_iterator(const uint8_t *buf, token_position position) noexcept; + /** + * 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; /** - * Get the index of the JSON text for a given token (relative). + * move_at_container_start(): moves us so that we are pointing at the beginning of + * the container so that assert_at_container_start() passes. * - * This is not null-terminated; it is a view into the JSON. + * 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; + + /** + * 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. * - * @param delta The relative position of the token to retrieve. e.g. 0 = current token, - * 1 = next token, -1 = prev token. + * 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 uint32_t peek_index(int32_t delta=0) const 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 error_code start_container(uint8_t start_char, const char *incorrect_type_message, const char *type) noexcept; + simdjson_inline error_code end_container() noexcept; + /** - * Get the index of the JSON text for a given token. + * Advance to a place expecting a value (increasing depth). * - * This is not null-terminated; it is a view into the JSON. + * @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. * - * @param position The position of the token. + * 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 uint32_t peek_index(token_position position) const noexcept; + 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; - const uint8_t *buf{}; - token_position _position{}; + /** Get the starting position of this value */ + simdjson_inline token_position start_position() const noexcept; - friend class json_iterator; - friend class value_iterator; + /** @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 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) noexcept; - 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) noexcept; -}; + friend class array; + friend class value; +}; // value_iterator } // namespace ondemand -} // namespace SIMDJSON_BUILTIN_IMPLEMENTATION +} // namespace westmere } // namespace simdjson namespace simdjson { template<> -struct simdjson_result : public SIMDJSON_BUILTIN_IMPLEMENTATION::implementation_simdjson_result_base { +struct simdjson_result : public westmere::implementation_simdjson_result_base { public: - simdjson_inline simdjson_result(SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::token_iterator &&value) noexcept; ///< @private + 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; - simdjson_inline ~simdjson_result() noexcept = default; ///< @private }; } // namespace simdjson -/* end file include/simdjson/generic/ondemand/token_iterator.h */ -/* begin file include/simdjson/generic/ondemand/json_iterator.h */ + +#endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_H +/* end file simdjson/generic/ondemand/value_iterator.h for westmere */ +/* including simdjson/generic/ondemand/value.h for westmere: #include "simdjson/generic/ondemand/value.h" */ +/* begin file simdjson/generic/ondemand/value.h for westmere */ +#ifndef SIMDJSON_GENERIC_ONDEMAND_VALUE_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_VALUE_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + namespace simdjson { -namespace SIMDJSON_BUILTIN_IMPLEMENTATION { +namespace westmere { namespace ondemand { -class document; -class document_stream; -class object; -class array; -class value; -class raw_json_string; -class parser; - /** - * Iterates through JSON tokens, keeping track of depth and string buffer. - * - * @private This is not intended for external use. + * 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 json_iterator { -protected: - token_iterator token{}; - ondemand::parser *parser{}; +class value { +public: /** - * Next free location in the string buffer. + * Create a new invalid value. * - * Used by raw_json_string::unescape() to have a place to unescape strings to. + * Exists so you can declare a variable and later assign to it before use. */ - uint8_t *_string_buf_loc{}; + simdjson_inline value() noexcept = default; + /** - * JSON error, if there is one. + * Get this value as the given type. * - * INCORRECT_TYPE and NO_SUCH_FIELD are *not* stored here, ever. + * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, bool * - * 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. + * You may use get_double(), get_bool(), get_uint64(), get_int64(), + * get_object(), get_array(), get_raw_json_string(), or get_string() instead. * - * - 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. + * @returns A value of the given type, parsed from the JSON. + * @returns INCORRECT_TYPE If the JSON value is not the given type. */ - simdjson_warn_unused simdjson_inline error_code skip_child(depth_t parent_depth) noexcept; + template simdjson_inline simdjson_result get() 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."); + } /** - * Tell whether the iterator is still at the start + * 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. */ - simdjson_inline bool at_root() const noexcept; + template simdjson_inline error_code get(T &out) 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(). + * 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 bool streaming() const noexcept; + simdjson_inline simdjson_result get_array() 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 + * 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 void assert_at_root() const noexcept; + simdjson_inline simdjson_result get_object() noexcept; /** - * Tell whether the iterator is at the EOF mark + * 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 bool at_end() const noexcept; + simdjson_inline simdjson_result get_uint64() noexcept; /** - * Tell whether the iterator is live (has not been moved). + * 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 bool is_alive() const noexcept; + simdjson_inline simdjson_result get_uint64_in_string() noexcept; /** - * Abandon this iterator, setting depth to 0 (as if the document is finished). + * 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 void abandon() noexcept; + simdjson_inline simdjson_result get_int64() noexcept; /** - * Advance the current token without modifying depth. + * 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 const uint8_t *return_current_and_advance() noexcept; + simdjson_inline simdjson_result get_int64_in_string() 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). + * Cast this JSON value to a double. * - * @return whether there is a single token + * @returns A double. + * @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number. */ - simdjson_inline bool is_single_token() const noexcept; + simdjson_inline simdjson_result get_double() noexcept; /** - * Assert that there are at least the given number of tokens left. + * Cast this JSON value (inside string) to a double * - * Has no effect in release builds. + * @returns A double. + * @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number. */ - simdjson_inline void assert_more_tokens(uint32_t required_tokens=1) const noexcept; + simdjson_inline simdjson_result get_double_in_string() noexcept; + /** - * Assert that the given position addresses an actual token (is within bounds). + * Cast this JSON value to a string. * - * 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). + * The string is guaranteed to be valid UTF-8. * - * This is not null-terminated; it is a view into the JSON. + * Equivalent to get(). * - * @param delta The relative position of the token to retrieve. e.g. 0 = next token, -1 = prev token. + * Important: a value should be consumed once. Calling get_string() twice on the same value + * is an error. * - * TODO consider a string_view, assuming the length will get stripped out by the optimizer when - * it isn't used ... + * @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 const uint8_t *peek(int32_t delta=0) const noexcept; + simdjson_inline simdjson_result get_string(bool allow_replacement = false) 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. + * Cast this JSON value to a "wobbly" string. * - * @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. + * The string is may not be a valid UTF-8 string. + * See https://simonsapin.github.io/wtf-8/ * - * This is not null-terminated; it is a view into the JSON. + * Important: a value should be consumed once. Calling get_wobbly_string() twice on the same value + * is an error. * - * You may be pointing outside of the input buffer: it is not generally - * safe to dereference this pointer. + * @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 const uint8_t *unsafe_pointer() const noexcept; + simdjson_inline simdjson_result get_wobbly_string() noexcept; /** - * Get the JSON text for a given token. - * - * This is not null-terminated; it is a view into the JSON. + * Cast this JSON value to a raw_json_string. * - * @param position The position of the token to retrieve. + * The string is guaranteed to be valid UTF-8, and may have escapes in it (e.g. \\ or \n). * - * TODO consider a string_view, assuming the length will get stripped out by the optimizer when - * it isn't used ... + * @returns A pointer to the raw JSON for the given string. + * @returns INCORRECT_TYPE if the JSON value is not a string. */ - simdjson_inline const uint8_t *peek(token_position position) const noexcept; + simdjson_inline simdjson_result get_raw_json_string() 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. + * Cast this JSON value to a bool. * - * @param position The position of the token to retrieve. + * @returns A bool value. + * @returns INCORRECT_TYPE if the JSON value is not true or false. */ - simdjson_inline uint32_t peek_length(token_position position) const noexcept; + simdjson_inline simdjson_result get_bool() noexcept; + /** - * Get the JSON text for the last token in the document. - * - * This is not null-terminated; it is a view into the JSON. + * 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. * - * TODO consider a string_view, assuming the length will get stripped out by the optimizer when - * it isn't used ... + * @returns Whether the value is null. + * @returns INCORRECT_TYPE If the JSON value begins with 'n' and is not 'null'. */ - simdjson_inline const uint8_t *peek_last() const noexcept; + simdjson_inline simdjson_result is_null() noexcept; +#if SIMDJSON_EXCEPTIONS /** - * Ascend one level. - * - * Validates that the depth - 1 == parent_depth. + * Cast this JSON value to an array. * - * @param parent_depth the expected parent depth. + * @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 void ascend_to(depth_t parent_depth) noexcept; - + simdjson_inline operator array() noexcept(false); /** - * Descend one level. - * - * Validates that the new depth == child_depth. + * Cast this JSON value to an object. * - * @param child_depth the expected child depth. + * @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 void descend_to(depth_t child_depth) noexcept; - simdjson_inline void descend_to(depth_t child_depth, int32_t delta) noexcept; - + simdjson_inline operator object() noexcept(false); /** - * Get current depth. + * 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 depth_t depth() const noexcept; - + simdjson_inline operator uint64_t() noexcept(false); /** - * Get current (writeable) location in the string buffer. + * 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 uint8_t *&string_buf_loc() noexcept; - + simdjson_inline operator int64_t() noexcept(false); /** - * Report an unrecoverable error, preventing further iteration. + * Cast this JSON value to a double. * - * @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. + * @returns A double. + * @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not a valid floating-point number. */ - simdjson_inline error_code report_error(error_code error, const char *message) noexcept; - + simdjson_inline operator double() noexcept(false); /** - * 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. + * 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 error_code optional_error(error_code error, const char *message) noexcept; - + simdjson_inline operator std::string_view() noexcept(false); /** - * 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. + * 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_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; + simdjson_inline operator raw_json_string() noexcept(false); /** - * 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. + * 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 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; + simdjson_inline operator bool() noexcept(false); #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; - /// 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 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) noexcept; - 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) noexcept; -}; // json_iterator - -} // namespace ondemand -} // namespace SIMDJSON_BUILTIN_IMPLEMENTATION -} // namespace simdjson - -namespace simdjson { - -template<> -struct simdjson_result : public SIMDJSON_BUILTIN_IMPLEMENTATION::implementation_simdjson_result_base { -public: - simdjson_inline simdjson_result(SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::json_iterator &&value) noexcept; ///< @private - simdjson_inline simdjson_result(error_code error) noexcept; ///< @private - - simdjson_inline simdjson_result() noexcept = default; -}; - -} // namespace simdjson -/* end file include/simdjson/generic/ondemand/json_iterator.h */ -/* begin file include/simdjson/generic/ondemand/value_iterator.h */ -namespace simdjson { -namespace SIMDJSON_BUILTIN_IMPLEMENTATION { -namespace ondemand { - -class document; -class object; -class array; -class value; -class raw_json_string; -class parser; - -/** - * 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. + * Begin array iteration. * - * Optimized for scalars. + * Part of the std::iterable interface. + * + * @returns INCORRECT_TYPE If the JSON value is not an array. */ - simdjson_warn_unused simdjson_inline error_code skip_child() noexcept; - + simdjson_inline simdjson_result begin() & noexcept; /** - * Tell whether the iterator is at the EOF mark + * Sentinel representing the end of the array. + * + * Part of the std::iterable interface. */ - simdjson_inline bool at_end() const noexcept; - + simdjson_inline simdjson_result end() & noexcept; /** - * Tell whether the iterator is at the start of the value + * 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 bool at_start() const noexcept; - + simdjson_inline simdjson_result count_elements() & noexcept; /** - * Tell whether the value is open--if the value has not been used, or the array/object is still open. + * 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 bool is_open() const noexcept; - + simdjson_inline simdjson_result count_fields() & noexcept; /** - * Tell whether the value is at an object's first field (just after the {). + * 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 bool at_first_field() const noexcept; - + simdjson_inline simdjson_result at(size_t index) noexcept; /** - * Abandon all iteration. - */ - simdjson_inline void abandon() 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. - /** - * Get the child value as a value_iterator. + * **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 value_iterator child_value() const noexcept; + 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; /** - * Get the depth of this value. + * 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 wasn't there when they aren't). + * + * @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 int32_t depth() const 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 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 JSON type of this value. + * 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() const noexcept; + simdjson_inline simdjson_result type() 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. + * 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; /** - * Start an object iteration. + * Checks whether the value is a negative number. * - * @returns Whether the object had any fields (returns false for empty). - * @error INCORRECT_TYPE if there is no opening { + * @returns true if the number if negative. */ - simdjson_warn_unused simdjson_inline simdjson_result start_object() noexcept; + simdjson_inline bool is_negative() noexcept; /** - * Start an object iteration from the root. + * 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). * - * @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. + * Performance note: if you call this function systematically + * before parsing a number, you may have fallen for a performance + * anti-pattern. * - * @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 + * @returns true if the number if negative. */ - simdjson_warn_unused simdjson_inline error_code check_root_object() noexcept; + simdjson_inline simdjson_result is_integer() noexcept; /** - * Start an object iteration after the user has already checked and moved past the {. + * 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. * - * Does not move the iterator unless the object is empty ({}). + * 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(). * - * @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 {. + * 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 + * Otherwise, get_number_type() has value number_type::floating_point_number * - * Does not move the iterator unless the object is empty ({}). + * 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 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). + * @returns the type of the number */ - simdjson_warn_unused simdjson_inline simdjson_result started_root_object() noexcept; + simdjson_inline simdjson_result get_number_type() noexcept; /** - * Moves to the next field in an object. + * 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. * - * Looks for , and }. If } is found, the object is finished and the iterator advances past it. - * Otherwise, it advances to the next value. + * 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. * - * @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. + * 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. + * + * 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 has_next_field() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_number() noexcept; + /** - * Get the current field's key. + * 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 */ - simdjson_warn_unused simdjson_inline simdjson_result field_key() noexcept; + simdjson_inline std::string_view raw_json_token() noexcept; /** - * Pass the : in the field and move to its value. + * Returns the current location in the document if in bounds. */ - simdjson_warn_unused simdjson_inline error_code field_value() noexcept; + simdjson_inline simdjson_result current_location() 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 } - * ^ - * ``` + * Returns the current depth in the document if in bounds. * - * 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.). + * 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_warn_unused simdjson_inline error_code find_field(const std::string_view key) noexcept; + simdjson_inline int32_t current_depth() const 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. + * Get the value associated with the given JSON pointer. We use the RFC 6901 + * https://tools.ietf.org/html/rfc6901 standard. * - * Assumes you have called next_field() or otherwise matched the previous value. + * ondemand::parser parser; + * auto json = R"({ "foo": { "a": [ 10, 20, 30 ] }})"_padded; + * auto doc = parser.iterate(json); + * doc.at_pointer("/foo/a/1") == 20 * - * This means the iterator must be sitting at the next key: + * It is allowed for a key to be the empty string: * - * ``` - * { "a": 1, "b": 2 } - * ^ - * ``` + * ondemand::parser parser; + * auto json = R"({ "": { "a": [ 10, 20, 30 ] }})"_padded; + * auto doc = parser.iterate(json); + * doc.at_pointer("//a/1") == 20 * - * 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. + * 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. * - * This is an unordered object lookup: if the field is not found initially, it will cycle around and scan from the beginning. + * 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. * - * Assumes you have called next_field() or otherwise matched the previous value. + * 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. * - * This means the iterator must be sitting at the next key: + * 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. * - * ``` - * { "a": 1, "b": 2 } - * ^ - * ``` + * Also note that at_pointer() relies on find_field() which implies that we do not unescape keys when matching * - * 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.). + * @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_warn_unused simdjson_inline simdjson_result find_field_unordered_raw(const std::string_view key) noexcept; + simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; - /** @} */ +protected: + /** + * Create a value. + */ + simdjson_inline value(const value_iterator &iter) 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. - * @{ + * Skip this value, allowing iteration to continue. */ + simdjson_inline void skip() noexcept; /** - * Check for an opening [ and start an array iteration. + * Start a value at the current position. * - * @returns Whether the array had any elements (returns false for empty). - * @error INCORRECT_TYPE If there is no [. + * (It should already be started; this is just a self-documentation method.) */ - simdjson_warn_unused simdjson_inline simdjson_result start_array() noexcept; + static simdjson_inline value start(const value_iterator &iter) 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 + * Resume a value. */ - simdjson_warn_unused simdjson_inline simdjson_result start_root_array() noexcept; + static simdjson_inline value resume(const value_iterator &iter) 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 + * Get the object, starting or resuming it as necessary */ - simdjson_warn_unused simdjson_inline error_code check_root_array() noexcept; + 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; +}; + +} // 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; + + 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; + 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 + 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; + /** - * Start an array iteration, after the user has already checked and moved past the [. + * Look up a field by name on an object (order-sensitive). * - * Does not move the iterator unless the array is empty ([]). + * 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 }`: * - * @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 [. + * ```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"); + * ``` * - * Does not move the iterator unless the array is empty ([]). + * **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 }`. * - * @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). + * @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_warn_unused simdjson_inline simdjson_result started_root_array() noexcept; + 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; /** - * Moves to the next element in an array. + * Look up a field by name on an object, without regard to key order. * - * Looks for , and ]. If ] is found, the array is finished and the iterator advances past it. - * Otherwise, it advances to the next value. + * **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. * - * @return Whether there is another element in the array. - * @error TAPE_ERROR If there is a comma missing between elements. + * 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 wasn't there when they aren't). + * + * @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_warn_unused simdjson_inline simdjson_result has_next_element() 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 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 a child value iterator. + * 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_warn_unused simdjson_inline value_iterator child() const noexcept; + simdjson_inline simdjson_result type() noexcept; + simdjson_inline simdjson_result is_scalar() 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; - /** - * @defgroup scalar Scalar values - * @addtogroup scalar - * @{ - */ + /** @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_warn_unused simdjson_inline simdjson_result get_string(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; +} // namespace simdjson - simdjson_warn_unused simdjson_inline simdjson_result get_root_string(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; +#endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_H +/* end file simdjson/generic/ondemand/value.h for westmere */ +/* including simdjson/generic/ondemand/logger.h for westmere: #include "simdjson/generic/ondemand/logger.h" */ +/* begin file simdjson/generic/ondemand/logger.h for westmere */ +#ifndef SIMDJSON_GENERIC_ONDEMAND_LOGGER_H - 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; +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_LOGGER_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { +namespace westmere { +namespace ondemand { + +// Logging should be free unless SIMDJSON_VERBOSE_LOGGING is set. Importantly, it is critical +// that the call to the log functions be side-effect free. Thus, for example, you should not +// create temporary std::string instances. +namespace logger { + +enum class log_level : int32_t { + info = 0, + error = 1 +}; + +#if SIMDJSON_VERBOSE_LOGGING + static constexpr const bool LOG_ENABLED = true; +#else + static constexpr const bool LOG_ENABLED = false; +#endif + +// We do not want these functions to be 'really inlined' since real inlining is +// for performance purposes and if you are using the loggers, you do not care about +// performance (or should not). +static inline void log_headers() noexcept; +// If args are provided, title will be treated as format string +template +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 westmere +} // namespace simdjson - simdjson_inline void assert_is_valid() const noexcept; - simdjson_inline bool is_valid() const noexcept; +#endif // SIMDJSON_GENERIC_ONDEMAND_LOGGER_H +/* end file simdjson/generic/ondemand/logger.h for westmere */ +/* including simdjson/generic/ondemand/token_iterator.h for westmere: #include "simdjson/generic/ondemand/token_iterator.h" */ +/* begin file simdjson/generic/ondemand/token_iterator.h for westmere */ +#ifndef SIMDJSON_GENERIC_ONDEMAND_TOKEN_ITERATOR_H - /** @} */ -protected: +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_TOKEN_ITERATOR_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/logger.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { +namespace westmere { +namespace ondemand { + +/** + * Iterates through JSON tokens (`{` `}` `[` `]` `,` `:` `""` `123` `true` `false` `null`) + * detected by stage 1. + * + * @private This is not intended for external use. + */ +class token_iterator { +public: /** - * Restarts an array iteration. - * @returns Whether the array has any elements (returns false for empty). + * Create a new invalid token_iterator. + * + * Exists so you can declare a variable and later assign to it before use. */ - simdjson_inline simdjson_result reset_array() noexcept; + 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; + /** - * Restarts an object iteration. - * @returns Whether the object has any fields (returns false for empty). + * Advance to the next token (returning the current one). */ - simdjson_inline simdjson_result reset_object() noexcept; + simdjson_inline const uint8_t *return_current_and_advance() 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. + * 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). * - * Usage: used with value::count_elements(). - **/ - simdjson_inline void move_at_start() noexcept; - + * 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 isn't used ... + */ + simdjson_inline const uint8_t *peek(int32_t delta=0) const 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. + * Get the maximum length of the JSON text for a given token. * - * 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; + * 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; /** - * 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... + * Get the JSON text for a given token. * - * ({"globals":{"a":{"shadowable":[}}}}) + * This is not null-terminated; it is a view into the JSON. * - * 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. + * @param position The position of the token. * - * 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. + */ + simdjson_inline const uint8_t *peek(token_position position) const noexcept; + /** + * Get the maximum length of the JSON text for a given token. * - * So the contract is: first call 'peek_...' and then call 'advance_...' only - * if you have determined that it is a type you can handle. + * The length will include any whitespace at the end of the token. * - * Unfortunately, it makes the code more verbose, longer and maybe more error prone. + * @param position The position of the token. */ + simdjson_inline uint32_t peek_length(token_position position) const 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; + /** + * 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; - 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; + // 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 error_code start_container(uint8_t start_char, const char *incorrect_type_message, const char *type) noexcept; - simdjson_inline error_code end_container() noexcept; +protected: + simdjson_inline token_iterator(const uint8_t *buf, token_position position) noexcept; /** - * Advance to a place expecting a value (increasing depth). + * Get the index of the JSON text for a given token (relative). * - * @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. + * This is not null-terminated; it is a view into the JSON. * - * Usage: used by array::begin() in debug mode (SIMDJSON_DEVELOPMENT_CHECKS) + * @param delta The relative position of the token to retrieve. e.g. 0 = current token, + * 1 = next token, -1 = prev token. */ - simdjson_inline bool is_at_iterator_start() const noexcept; - + simdjson_inline uint32_t peek_index(int32_t delta=0) const noexcept; /** - * Assuming that we are within an object, this returns true if we - * are pointing at a key. + * 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. * - * 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; + simdjson_inline uint32_t peek_index(token_position 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; + const uint8_t *buf{}; + token_position _position{}; - friend class document; + friend class json_iterator; + friend class value_iterator; friend class object; - friend class array; - friend class value; -}; // value_iterator + 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 SIMDJSON_BUILTIN_IMPLEMENTATION +} // namespace westmere } // namespace simdjson namespace simdjson { template<> -struct simdjson_result : public SIMDJSON_BUILTIN_IMPLEMENTATION::implementation_simdjson_result_base { +struct simdjson_result : public westmere::implementation_simdjson_result_base { public: - simdjson_inline simdjson_result(SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::value_iterator &&value) noexcept; ///< @private + 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 -/* end file include/simdjson/generic/ondemand/value_iterator.h */ -/* begin file include/simdjson/generic/ondemand/array_iterator.h */ + +#endif // SIMDJSON_GENERIC_ONDEMAND_TOKEN_ITERATOR_H +/* end file simdjson/generic/ondemand/token_iterator.h for westmere */ +/* including simdjson/generic/ondemand/json_iterator.h for westmere: #include "simdjson/generic/ondemand/json_iterator.h" */ +/* begin file simdjson/generic/ondemand/json_iterator.h for westmere */ +#ifndef SIMDJSON_GENERIC_ONDEMAND_JSON_ITERATOR_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_JSON_ITERATOR_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/token_iterator.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { -namespace SIMDJSON_BUILTIN_IMPLEMENTATION { +namespace westmere { namespace ondemand { -class array; -class value; -class document; - /** - * A forward-only JSON array. + * Iterates through JSON tokens, keeping track of depth and string buffer. * - * 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 * (*, ++, *, ++, * ...) + * @private This is not intended for external use. */ -class array_iterator { -public: - /** Create a new, invalid array iterator. */ - simdjson_inline array_iterator() noexcept = default; - - // - // Iterator interface - // - +class json_iterator { +protected: + token_iterator token{}; + ondemand::parser *parser{}; /** - * Get the current element. + * Next free location in the string buffer. * - * Part of the std::iterator interface. + * Used by raw_json_string::unescape() to have a place to unescape strings to. */ - simdjson_inline simdjson_result operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION. + uint8_t *_string_buf_loc{}; /** - * Check if we are at the end of the JSON. + * JSON error, if there is one. * - * Part of the std::iterator interface. + * INCORRECT_TYPE and NO_SUCH_FIELD are *not* stored here, ever. * - * @return true if there are no more elements in the JSON array. + * 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. */ - simdjson_inline bool operator==(const array_iterator &) const noexcept; + error_code error{SUCCESS}; /** - * Check if there are more elements in the JSON array. - * - * Part of the std::iterator interface. + * Depth of the current token in the JSON. * - * @return true if there are more elements in the JSON array. + * - 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 bool operator!=(const array_iterator &) const noexcept; + depth_t _depth{}; /** - * Move to the next element. - * - * Part of the std::iterator interface. + * 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); */ - simdjson_inline array_iterator &operator++() noexcept; + 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}; -private: - value_iterator iter{}; +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; - simdjson_inline array_iterator(const value_iterator &iter) noexcept; + /** + * Tell whether the iterator is still at the start + */ + simdjson_inline bool at_root() const noexcept; - friend class array; - friend class value; - friend struct simdjson_result; -}; + /** + * 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; -} // namespace ondemand -} // namespace SIMDJSON_BUILTIN_IMPLEMENTATION -} // namespace simdjson + /** + * 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; -namespace simdjson { + /** + * Tell whether the iterator is at the EOF mark + */ + simdjson_inline bool at_end() const noexcept; -template<> -struct simdjson_result : public SIMDJSON_BUILTIN_IMPLEMENTATION::implementation_simdjson_result_base { -public: - simdjson_inline simdjson_result(SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::array_iterator &&value) noexcept; ///< @private - simdjson_inline simdjson_result(error_code error) noexcept; ///< @private - simdjson_inline simdjson_result() noexcept = default; + /** + * Tell whether the iterator is live (has not been moved). + */ + simdjson_inline bool is_alive() const noexcept; - // - // Iterator interface - // + /** + * Abandon this iterator, setting depth to 0 (as if the document is finished). + */ + simdjson_inline void abandon() noexcept; - 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; -}; + /** + * Advance the current token without modifying depth. + */ + simdjson_inline const uint8_t *return_current_and_advance() noexcept; -} // namespace simdjson -/* end file include/simdjson/generic/ondemand/array_iterator.h */ -/* begin file include/simdjson/generic/ondemand/object_iterator.h */ + /** + * 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; -namespace simdjson { -namespace SIMDJSON_BUILTIN_IMPLEMENTATION { -namespace ondemand { + /** + * 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 isn't 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 isn't 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 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 isn't used ... + */ + simdjson_inline const uint8_t *peek_last() const noexcept; -class field; + /** + * 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; -class object_iterator { -public: /** - * Create a new invalid object_iterator. + * Descend one level. * - * Exists so you can declare a variable and later assign to it before use. + * Validates that the new depth == child_depth. + * + * @param child_depth the expected child depth. */ - simdjson_inline object_iterator() noexcept = default; + simdjson_inline void descend_to(depth_t child_depth) noexcept; + simdjson_inline void descend_to(depth_t child_depth, int32_t delta) noexcept; - // - // Iterator interface - // + /** + * Get current depth. + */ + simdjson_inline depth_t depth() const 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 ',' - simdjson_inline object_iterator &operator++() noexcept; + /** + * Get current (writeable) location in the string buffer. + */ + simdjson_inline uint8_t *&string_buf_loc() noexcept; -private: /** - * The underlying JSON iterator. + * Report an unrecoverable error, preventing further iteration. * - * 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. + * @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. */ - value_iterator iter{}; + 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; + /// 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; - simdjson_inline object_iterator(const value_iterator &iter) noexcept; - friend struct simdjson_result; + 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; + 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 SIMDJSON_BUILTIN_IMPLEMENTATION +} // namespace westmere } // namespace simdjson namespace simdjson { template<> -struct simdjson_result : public SIMDJSON_BUILTIN_IMPLEMENTATION::implementation_simdjson_result_base { +struct simdjson_result : public westmere::implementation_simdjson_result_base { public: - simdjson_inline simdjson_result(SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::object_iterator &&value) noexcept; ///< @private + 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; - - // - // 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; + simdjson_inline simdjson_result() noexcept = default; }; } // namespace simdjson -/* end file include/simdjson/generic/ondemand/object_iterator.h */ -/* begin file include/simdjson/generic/ondemand/array.h */ + +#endif // SIMDJSON_GENERIC_ONDEMAND_JSON_ITERATOR_H +/* end file simdjson/generic/ondemand/json_iterator.h for westmere */ +/* including simdjson/generic/ondemand/json_type.h for westmere: #include "simdjson/generic/ondemand/json_type.h" */ +/* begin file simdjson/generic/ondemand/json_type.h for westmere */ +#ifndef SIMDJSON_GENERIC_ONDEMAND_JSON_TYPE_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_JSON_TYPE_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/numberparsing.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { -namespace SIMDJSON_BUILTIN_IMPLEMENTATION { +namespace westmere { namespace ondemand { -class value; -class document; +/** + * 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 forward-only JSON array. + * A type representing a JSON number. + * The design of the struct is deliberately straight-forward. All + * functions return standard values with no error check. */ -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; +struct number { /** - * 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. + * return the automatically determined type of + * the number: number_type::floating_point_number, + * number_type::signed_integer or number_type::unsigned_integer. * - * To check that an array is empty, it is more performant to use - * the is_empty() method. + * 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 simdjson_result count_elements() & noexcept; + simdjson_inline ondemand::number_type get_number_type() const 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. + * return true if the automatically determined type of + * the number is number_type::unsigned_integer. */ - simdjson_inline simdjson_result is_empty() & noexcept; + simdjson_inline bool is_uint64() const 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) + * return the value as a uint64_t, only valid if is_uint64() is true. */ - inline simdjson_result reset() & noexcept; + simdjson_inline uint64_t get_uint64() const noexcept; + simdjson_inline operator uint64_t() 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. - * - * 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 + * return true if the automatically determined type of + * the number is number_type::signed_integer. */ - inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline bool is_int64() const noexcept; /** - * Consumes the array and returns a string_view instance corresponding to the - * array as represented in JSON. It points inside the original document. + * return the value as a int64_t, only valid if is_int64() is true. */ - simdjson_inline simdjson_result raw_json() noexcept; + simdjson_inline int64_t get_int64() const noexcept; + simdjson_inline operator int64_t() const 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 + * return true if the automatically determined type of + * the number is number_type::floating_point_number. */ - simdjson_inline simdjson_result at(size_t index) noexcept; -protected: + simdjson_inline bool is_double() const noexcept; /** - * Go to the end of the array, no matter where you are right now. + * return the value as a double, only valid if is_double() is true. */ - simdjson_inline error_code consume() noexcept; + simdjson_inline double get_double() const noexcept; + simdjson_inline operator double() const 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 [. + * Convert the number to a double. Though it always succeed, the conversion + * may be lossy if the number cannot be represented exactly. */ - static simdjson_inline simdjson_result start(value_iterator &iter) noexcept; + simdjson_inline double as_double() const noexcept; + + +protected: /** - * 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. + * 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. */ - static simdjson_inline simdjson_result start_root(value_iterator &iter) noexcept; + friend class value_iterator; + template + friend error_code numberparsing::slow_float_parsing(simdjson_unused const uint8_t * src, W writer); + 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; /** - * 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. + * End of friend declarations. */ - 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. + * Our attributes are a union type (size = 64 bits) + * followed by a type indicator. */ - simdjson_inline array(const value_iterator &iter) noexcept; + union { + double floating_point_number; + int64_t signed_integer; + uint64_t unsigned_integer; + } payload{0}; + number_type type{number_type::signed_integer}; +}; - /** - * Iterator marking current position. - * - * iter.is_alive() == false indicates iteration is complete. - */ - value_iterator iter{}; +/** + * 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; - friend class value; - friend class document; - friend struct simdjson_result; - friend struct simdjson_result; - friend class array_iterator; -}; +#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 SIMDJSON_BUILTIN_IMPLEMENTATION +} // namespace westmere } // namespace simdjson namespace simdjson { template<> -struct simdjson_result : public SIMDJSON_BUILTIN_IMPLEMENTATION::implementation_simdjson_result_base { +struct simdjson_result : public westmere::implementation_simdjson_result_base { public: - simdjson_inline simdjson_result(SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::array &&value) noexcept; ///< @private + 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 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 raw_json() noexcept; - + simdjson_inline ~simdjson_result() noexcept = default; ///< @private }; } // namespace simdjson -/* end file include/simdjson/generic/ondemand/array.h */ -/* begin file include/simdjson/generic/ondemand/document.h */ + +#endif // SIMDJSON_GENERIC_ONDEMAND_JSON_TYPE_H +/* end file simdjson/generic/ondemand/json_type.h for westmere */ +/* including simdjson/generic/ondemand/raw_json_string.h for westmere: #include "simdjson/generic/ondemand/raw_json_string.h" */ +/* begin file simdjson/generic/ondemand/raw_json_string.h for westmere */ +#ifndef SIMDJSON_GENERIC_ONDEMAND_RAW_JSON_STRING_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_RAW_JSON_STRING_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { -namespace SIMDJSON_BUILTIN_IMPLEMENTATION { +namespace westmere { namespace ondemand { -class parser; -class array; -class object; -class value; -class raw_json_string; -class array_iterator; -class document_stream; - /** - * A JSON document. It holds a json_iterator instance. + * A string escaped per JSON rules, terminated with quote ("). They are used to represent + * unescaped keys inside JSON documents. * - * Used by tokens to get text, and string buffer location. + * (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(). * - * You must keep the document around during iteration. */ -class document { +class raw_json_string { public: /** - * Create a new invalid document. + * Create a new invalid raw_json_string. * * 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; + simdjson_inline raw_json_string() noexcept = default; /** - * Cast this JSON value to an array. + * Create a new invalid raw_json_string pointed at the given location in the JSON. * - * @returns An object that can be used to iterate the array. - * @returns INCORRECT_TYPE If the JSON value is not an array. + * 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 simdjson_result get_array() & noexcept; + simdjson_inline raw_json_string(const uint8_t * _buf) noexcept; /** - * Cast this JSON value to an object. + * Get the raw pointer to the beginning of the string in the JSON (just after the "). * - * @returns An object that can be used to look up or iterate fields. - * @returns INCORRECT_TYPE If the JSON value is not an object. + * It is possible for this function to return a null pointer if the instance + * has outlived its existence. */ - simdjson_inline simdjson_result get_object() & noexcept; + simdjson_inline const char * raw() const noexcept; + /** - * Cast this JSON value to an unsigned integer. + * 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. * - * @returns A signed 64-bit integer. - * @returns INCORRECT_TYPE If the JSON value is not a 64-bit unsigned integer. + * 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 simdjson_result get_uint64() noexcept; + simdjson_inline bool unsafe_is_equal(size_t length, std::string_view target) const noexcept; + /** - * Cast this JSON value (inside string) to an unsigned integer. + * 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. * - * @returns A signed 64-bit integer. - * @returns INCORRECT_TYPE If the JSON value is not a 64-bit unsigned integer. + * 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 simdjson_result get_uint64_in_string() noexcept; + simdjson_inline bool unsafe_is_equal(std::string_view target) const noexcept; + /** - * Cast this JSON value to a signed integer. + * 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. * - * @returns A signed 64-bit integer. - * @returns INCORRECT_TYPE If the JSON value is not a 64-bit integer. + * 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 simdjson_result get_int64() noexcept; + simdjson_inline bool unsafe_is_equal(const char* target) const 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. + * 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 simdjson_result get_int64_in_string() noexcept; + simdjson_inline bool is_equal(std::string_view target) const 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. + * 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 simdjson_result get_double() noexcept; + simdjson_inline bool is_equal(const char* target) const 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. + * 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!). */ - simdjson_inline simdjson_result get_double_in_string() noexcept; + 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: + + /** - * 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. + * This will set the inner pointer to zero, effectively making + * this instance unusable. */ - simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; + simdjson_inline void consume() noexcept { buf = nullptr; } + /** - * 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. + * Checks whether the inner pointer is non-null and thus usable. */ - simdjson_inline simdjson_result get_wobbly_string() noexcept; + simdjson_inline simdjson_warn_unused bool alive() const noexcept { return buf != nullptr; } + /** - * Cast this JSON value to a raw_json_string. + * Unescape this JSON string, replacing \\ with \, \n with newline, etc. + * The result will be a valid UTF-8. * - * The string is guaranteed to be valid UTF-8, and may have escapes in it (e.g. \\ or \n). + * ## IMPORTANT: string_view lifetime * - * @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. + * The string_view is only valid until the next parse() call on the parser. * - * @returns A bool value. - * @returns INCORRECT_TYPE if the JSON value is not true or false. + * @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_result get_bool() noexcept; + simdjson_inline simdjson_warn_unused simdjson_result unescape(json_iterator &iter, bool allow_replacement) const noexcept; + /** - * Cast this JSON value to a value when the document is an object or an array. + * 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/ * - * @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). + * ## 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_result get_value() noexcept; + 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 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; +}; + +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_ONDEMAND_RAW_JSON_STRING_H +/* end file simdjson/generic/ondemand/raw_json_string.h for westmere */ +/* including simdjson/generic/ondemand/parser.h for westmere: #include "simdjson/generic/ondemand/parser.h" */ +/* begin file simdjson/generic/ondemand/parser.h for westmere */ +#ifndef SIMDJSON_GENERIC_ONDEMAND_PARSER_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_PARSER_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +#include + +namespace simdjson { +namespace westmere { +namespace ondemand { + +/** + * The default batch size for document_stream instances for this On Demand kernel. + * Note that different On Demand kernel may use a different DEFAULT_BATCH_SIZE value + * in the future. + */ +static constexpr size_t DEFAULT_BATCH_SIZE = 1000000; +/** + * Some adversary might try to set the batch size to 0 or 1, which might cause problems. + * We set a minimum of 32B since anything else is highly likely to be an error. In practice, + * most users will want a much larger batch size. + * + * All non-negative MINIMAL_BATCH_SIZE values should be 'safe' except that, obviously, no JSON + * document can ever span 0 or 1 byte and that very large values would create memory allocation issues. + */ +static constexpr size_t MINIMAL_BATCH_SIZE = 32; +/** + * A JSON fragment iterator. + * + * This holds the actual iterator as well as the buffer for writing strings. + */ +class parser { +public: /** - * 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. + * Create a JSON parser. * - * @returns Whether the value is null. - * @returns INCORRECT_TYPE If the JSON value begins with 'n' and is not 'null'. + * The new parser will have zero capacity. */ - simdjson_inline simdjson_result is_null() noexcept; + 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; /** - * Get this value as the given type. + * Start iterating an on-demand JSON document. * - * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, bool + * ondemand::parser parser; + * document doc = parser.iterate(json); * - * You may use get_double(), get_bool(), get_uint64(), get_int64(), - * get_object(), get_array(), get_raw_json_string(), or get_string() instead. + * 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. * - * @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 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."); - } - /** @overload template simdjson_result get() & noexcept */ - template simdjson_inline simdjson_result get() && 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."); - } - - /** - * Get this value as the given type. + * ### IMPORTANT: Validate what you use * - * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, bool, value + * 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. * - * Be mindful that the document instance must remain in scope while you are accessing object, array and value instances. + * ### IMPORTANT: Buffer Lifetime * - * @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_inline error_code get(T &out) && noexcept; - -#if SIMDJSON_EXCEPTIONS - /** - * Cast this JSON value to an array. + * Because parsing is done while you iterate, you *must* keep the JSON buffer around at least as + * long as the document iteration. * - * @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. + * ### IMPORTANT: Document Lifetime * - * @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. + * 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. * - * @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. + * ### REQUIRED: Buffer Padding * - * @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. + * 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. * - * @returns A double. - * @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not a valid floating-point number. + * @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_inline operator double() noexcept(false); + simdjson_warn_unused simdjson_result iterate(padded_string_view json) & noexcept; + /** @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(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; + /** - * Cast this JSON value to a string. + * @private * - * The string is guaranteed to be valid UTF-8. + * Start iterating an on-demand JSON document. * - * @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. + * ondemand::parser parser; + * json_iterator doc = parser.iterate(json); * - * The string is guaranteed to be valid UTF-8, and may have escapes in it (e.g. \\ or \n). + * ### IMPORTANT: Buffer Lifetime * - * @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. + * Because parsing is done while you iterate, you *must* keep the JSON buffer around at least as + * long as the document iteration. * - * @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. + * ### IMPORTANT: Document Lifetime * - * @returns A value value. - * @exception if a JSON value cannot be found - */ - 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. + * 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. * - * 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. + * 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. * - * @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. + * ### REQUIRED: Buffer Padding * - * Part of the std::iterable interface. - */ - simdjson_inline simdjson_result begin() & noexcept; - /** - * Sentinel representing the end of the array. + * 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. * - * Part of the std::iterable interface. + * @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_inline simdjson_result end() & noexcept; + simdjson_warn_unused simdjson_result iterate_raw(padded_string_view json) & noexcept; + /** - * Look up a field by name on an object (order-sensitive). + * Parse a buffer containing many JSON documents. * - * 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 }`: + * 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 * - * ```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"); - * ``` + * No copy of the input buffer is made. * - * **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 }`. + * 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. * - * 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. + * ### Format * - * 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. + * 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.) * - * @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. + * 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. * - * **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. + * The characters inside a JSON document, and between JSON documents, must be valid Unicode (UTF-8). * - * 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. + * 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. * - * Use find_field() if you are sure fields will be in order (or are willing to treat it as if the - * field wasn't there when they aren't). + * ### REQUIRED: Buffer Padding * - * 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. + * 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. * - * 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. + * ### Threads * - * @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. + * 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). */ - 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; + 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_inline size_t capacity() const noexcept; + /** The maximum capacity of this parser (the largest document it is allowed to process). */ + simdjson_inline size_t max_capacity() const noexcept; + simdjson_inline void set_max_capacity(size_t max_capacity) 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". + * 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_inline simdjson_result type() noexcept; + simdjson_inline size_t max_depth() const noexcept; /** - * Checks whether the document is a scalar (string, number, null, Boolean). - * Returns false when there it is an array or object. + * Ensure this parser has enough memory to process JSON documents up to `capacity` bytes in length + * and `max_depth` depth. * - * @returns true if the type is string, number, null, Boolean - * @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse". + * 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_inline simdjson_result is_scalar() noexcept; + simdjson_warn_unused error_code allocate(size_t capacity, size_t max_depth=DEFAULT_MAX_DEPTH) noexcept; + #ifdef SIMDJSON_THREADS_ENABLED /** - * Checks whether the document is a negative number. - * - * @returns true if the number if negative. + * 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. */ - simdjson_inline bool is_negative() noexcept; + bool threaded{true}; + #endif + /** - * 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). + * 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. * - * @returns true if the number if negative. + * 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 is_integer() noexcept; + simdjson_inline simdjson_result unescape(raw_json_string in, uint8_t *&dst, bool allow_replacement = false) const 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. + * 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. * - * 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(). + * 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()). * - * 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 - * Otherwise, get_number_type() has value number_type::floating_point_number + * ## IMPORTANT: string_view lifetime * - * 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. + * The string_view is only valid as long as the bytes in dst. * - * @returns the type of the number + * @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 get_number_type() noexcept; + simdjson_inline simdjson_result unescape_wobbly(raw_json_string in, uint8_t *&dst) const noexcept; + +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 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; +}; + +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_ONDEMAND_PARSER_H +/* end file simdjson/generic/ondemand/parser.h for westmere */ + +// All other declarations +/* including simdjson/generic/ondemand/array.h for westmere: #include "simdjson/generic/ondemand/array.h" */ +/* begin file simdjson/generic/ondemand/array.h for westmere */ +#ifndef SIMDJSON_GENERIC_ONDEMAND_ARRAY_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_ARRAY_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { +namespace westmere { +namespace ondemand { +/** + * A forward-only JSON array. + */ +class array { +public: /** - * 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. + * Create a new invalid array. * - * 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. + * Exists so you can declare a variable and later assign to it before use. */ - simdjson_warn_unused simdjson_inline simdjson_result get_number() noexcept; + simdjson_inline array() noexcept = default; /** - * 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. + * Begin array iteration. * - * 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. + * Part of the std::iterable interface. */ - inline std::string to_debug_string() noexcept; + simdjson_inline simdjson_result begin() noexcept; /** - * Some unrecoverable error conditions may render the document instance unusable. - * The is_alive() method returns true when the document is still suitable. + * Sentinel representing the end of the array. + * + * Part of the std::iterable interface. */ - inline bool is_alive() noexcept; - + simdjson_inline simdjson_result end() noexcept; /** - * Returns the current location in the document if in bounds. + * 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. */ - inline simdjson_result current_location() const noexcept; - + simdjson_inline simdjson_result count_elements() & 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. + * 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. */ - inline bool at_end() const noexcept; - + simdjson_inline simdjson_result is_empty() & noexcept; /** - * Returns the current depth in the document if in bounds. + * 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. * - * 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. + * @returns true if the array contains some elements (not empty) */ - simdjson_inline int32_t current_depth() const noexcept; - + 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. + * 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 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: + * doc.at_pointer("/0/foo/a/1") == 20 * - * 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 array + * instance: there is no rewind and no invalidation. * - * 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. + * 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 + * 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; + inline simdjson_result at_pointer(std::string_view json_pointer) 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. + * 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: /** - * Consumes the document. + * Go to the end of the array, no matter where you are right now. */ 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; + /** + * 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; - 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; + /** + * 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; - // - // Fields - // - json_iterator iter{}; ///< Current position in the document - static constexpr depth_t DOCUMENT_DEPTH = 0; ///< document depth is always 0 + /** + * Iterator marking current position. + * + * iter.is_alive() == false indicates iteration is complete. + */ + value_iterator iter{}; - 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; + friend class document; + friend struct simdjson_result; + friend struct simdjson_result; + friend class array_iterator; }; +} // namespace ondemand +} // namespace westmere +} // namespace simdjson + +namespace simdjson { -/** - * A document_reference is a thin wrapper around a document reference instance. - */ -class document_reference { +template<> +struct simdjson_result : public westmere::implementation_simdjson_result_base { 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; - 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(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 is_null() 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 raw_json() noexcept; - simdjson_inline operator document&() const noexcept; - -#if SIMDJSON_EXCEPTIONS - 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 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; -private: - document *doc{nullptr}; }; -} // namespace ondemand -} // namespace SIMDJSON_BUILTIN_IMPLEMENTATION + } // namespace simdjson +#endif // SIMDJSON_GENERIC_ONDEMAND_ARRAY_H +/* end file simdjson/generic/ondemand/array.h for westmere */ +/* including simdjson/generic/ondemand/array_iterator.h for westmere: #include "simdjson/generic/ondemand/array_iterator.h" */ +/* begin file simdjson/generic/ondemand/array_iterator.h for westmere */ +#ifndef SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + + namespace simdjson { +namespace westmere { +namespace ondemand { -template<> -struct simdjson_result : public SIMDJSON_BUILTIN_IMPLEMENTATION::implementation_simdjson_result_base { +/** + * 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: - simdjson_inline simdjson_result(SIMDJSON_BUILTIN_IMPLEMENTATION::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; + /** Create a new, invalid array iterator. */ + simdjson_inline array_iterator() 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; - 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; + // + // Iterator interface + // - template simdjson_inline simdjson_result get() & noexcept; - template simdjson_inline simdjson_result get() && noexcept; + /** + * 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; - template simdjson_inline error_code get(T &out) & noexcept; - template simdjson_inline error_code get(T &out) && noexcept; +private: + value_iterator iter{}; -#if SIMDJSON_EXCEPTIONS - simdjson_inline operator SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::array() & noexcept(false); - simdjson_inline operator SIMDJSON_BUILTIN_IMPLEMENTATION::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 SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::raw_json_string() noexcept(false); - simdjson_inline operator bool() noexcept(false); - simdjson_inline operator SIMDJSON_BUILTIN_IMPLEMENTATION::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 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 array_iterator(const value_iterator &iter) noexcept; - simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + friend class array; + friend class value; + friend struct simdjson_result; }; - +} // namespace ondemand +} // namespace westmere } // namespace simdjson - - namespace simdjson { template<> -struct simdjson_result : public SIMDJSON_BUILTIN_IMPLEMENTATION::implementation_simdjson_result_base { +struct simdjson_result : public westmere::implementation_simdjson_result_base { public: - simdjson_inline simdjson_result(SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::document_reference value, error_code error) noexcept; + 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 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; - 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 - simdjson_inline operator SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::array() & noexcept(false); - simdjson_inline operator SIMDJSON_BUILTIN_IMPLEMENTATION::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 SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::raw_json_string() noexcept(false); - simdjson_inline operator bool() noexcept(false); - simdjson_inline operator SIMDJSON_BUILTIN_IMPLEMENTATION::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 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; + // + // Iterator interface + // - simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + 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 -/* end file include/simdjson/generic/ondemand/document.h */ -/* begin file include/simdjson/generic/ondemand/value.h */ + +#endif // SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_H +/* end file simdjson/generic/ondemand/array_iterator.h for westmere */ +/* including simdjson/generic/ondemand/document.h for westmere: #include "simdjson/generic/ondemand/document.h" */ +/* begin file simdjson/generic/ondemand/document.h for westmere */ +#ifndef SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { -namespace SIMDJSON_BUILTIN_IMPLEMENTATION { +namespace westmere { namespace ondemand { -class array; -class document; -class field; -class object; -class raw_json_string; - /** - * 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. + * 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 value { +class document { public: /** - * Create a new invalid value. + * Create a new invalid document. * * 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 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."); - } - - /** - * 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; + 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. @@ -25505,32 +80856,28 @@ class value { * @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; - + 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; - + simdjson_inline simdjson_result get_object() & noexcept; /** * Cast this JSON value to an unsigned integer. * - * @returns A unsigned 64-bit 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 a unsigned integer. + * Cast this JSON value (inside string) to an unsigned integer. * - * @returns A unsigned 64-bit 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. * @@ -25538,7 +80885,6 @@ class value { * @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. * @@ -25546,7 +80892,6 @@ class value { * @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. * @@ -25556,38 +80901,31 @@ class value { simdjson_inline simdjson_result get_double() noexcept; /** - * Cast this JSON value (inside string) to a double + * 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. + * 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; - - /** - * Cast this JSON value to a "wobbly" string. + * Cast this JSON value to a string. * - * The string is may not be a valid UTF-8 string. - * See https://simonsapin.github.io/wtf-8/ + * The string is not guaranteed to be valid UTF-8. 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. + * 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. @@ -25603,7 +80941,6 @@ class value { * @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. * @@ -25611,9 +80948,16 @@ class 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. + * + * @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 + * 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. * @@ -25622,6 +80966,44 @@ class value { */ 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 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."); + } + /** @overload template simdjson_result get() & noexcept */ + template simdjson_inline simdjson_result get() && 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."); + } + + /** + * 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_inline error_code get(T &out) && noexcept; + #if SIMDJSON_EXCEPTIONS /** * Cast this JSON value to an array. @@ -25629,14 +81011,14 @@ class value { * @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); + 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); + simdjson_inline operator object() & noexcept(false); /** * Cast this JSON value to an unsigned integer. * @@ -25663,8 +81045,6 @@ class value { * * 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. @@ -25686,22 +81066,14 @@ class 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. + * Cast this JSON value to a value. * - * Part of the std::iterable interface. + * @returns A value value. + * @exception if a JSON value cannot be found */ - simdjson_inline simdjson_result end() & noexcept; + 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 @@ -25712,12 +81084,9 @@ class value { * 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 @@ -25729,10 +81098,7 @@ class value { * 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. + * the is_empty() method. */ simdjson_inline simdjson_result count_fields() & noexcept; /** @@ -25742,7 +81108,20 @@ class value { * @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; + 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). * @@ -25756,18 +81135,28 @@ class value { * 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. 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; + 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. @@ -25782,22 +81171,30 @@ class value { * 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 wasn't there when they aren't). * + * 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_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. @@ -25808,14 +81205,12 @@ class value { * 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). + * 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 @@ -25824,22 +81219,18 @@ class value { simdjson_inline simdjson_result is_scalar() noexcept; /** - * Checks whether the value is a negative number. + * Checks whether the document 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 + * 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). * - * 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; @@ -25879,454 +81270,88 @@ class value { * 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. - * - * 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 - */ - simdjson_inline std::string_view raw_json_token() 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; - -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; -}; - -} // namespace ondemand -} // namespace SIMDJSON_BUILTIN_IMPLEMENTATION -} // namespace simdjson - -namespace simdjson { - -template<> -struct simdjson_result : public SIMDJSON_BUILTIN_IMPLEMENTATION::implementation_simdjson_result_base { -public: - simdjson_inline simdjson_result(SIMDJSON_BUILTIN_IMPLEMENTATION::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; - 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 - simdjson_inline operator SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::array() noexcept(false); - simdjson_inline operator SIMDJSON_BUILTIN_IMPLEMENTATION::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 SIMDJSON_BUILTIN_IMPLEMENTATION::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 wasn't there when they aren't). - * - * @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_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; - - /** @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; -}; - -} // namespace simdjson -/* end file include/simdjson/generic/ondemand/value.h */ -/* begin file include/simdjson/generic/ondemand/field.h */ - -namespace simdjson { -namespace SIMDJSON_BUILTIN_IMPLEMENTATION { -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) noexcept; - /** - * Get the key as a raw_json_string. Can be used for direct comparison with - * an unescaped C string: e.g., key() == "test". - */ - simdjson_inline raw_json_string 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 SIMDJSON_BUILTIN_IMPLEMENTATION -} // namespace simdjson - -namespace simdjson { - -template<> -struct simdjson_result : public SIMDJSON_BUILTIN_IMPLEMENTATION::implementation_simdjson_result_base { -public: - simdjson_inline simdjson_result(SIMDJSON_BUILTIN_IMPLEMENTATION::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; - simdjson_inline simdjson_result key() noexcept; - simdjson_inline simdjson_result value() noexcept; -}; - -} // namespace simdjson -/* end file include/simdjson/generic/ondemand/field.h */ -/* begin file include/simdjson/generic/ondemand/object.h */ - -namespace simdjson { -namespace SIMDJSON_BUILTIN_IMPLEMENTATION { -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. + * 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. * - * 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. + * 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. * - * @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. + * 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_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; + simdjson_warn_unused simdjson_inline simdjson_result get_number() 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. + * Get the raw JSON for this token. * - * Use find_field() if you are sure fields will be in order (or are willing to treat it as if the - * field wasn't there when they aren't). + * The string_view will always point into the input buffer. * - * If you have multiple fields with a matching key ({"x": 1, "x": 1}) be mindful - * that only one field is returned. + * 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. * - * 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. + * 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. * - * 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. + * 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. * - * @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. + * 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 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; + 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, interpreting the current node - * as the root of its own JSON document. + * 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; @@ -26340,474 +81365,279 @@ class object { * 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. + * 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. + * 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). */ - inline simdjson_result at_pointer(std::string_view json_pointer) 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 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; + simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) 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 + * 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: /** - * Go to the end of the object, no matter where you are right now. + * Consumes the document. */ 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 SIMDJSON_BUILTIN_IMPLEMENTATION -} // namespace simdjson -namespace simdjson { + simdjson_inline document(ondemand::json_iterator &&iter) noexcept; + simdjson_inline const uint8_t *text(uint32_t idx) const noexcept; -template<> -struct simdjson_result : public SIMDJSON_BUILTIN_IMPLEMENTATION::implementation_simdjson_result_base { -public: - simdjson_inline simdjson_result(SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::object &&value) noexcept; ///< @private - simdjson_inline simdjson_result(error_code error) noexcept; ///< @private - simdjson_inline simdjson_result() noexcept = default; + 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; - 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; - inline simdjson_result reset() noexcept; - inline simdjson_result is_empty() noexcept; - inline simdjson_result count_fields() & noexcept; - inline simdjson_result raw_json() 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; }; -} // namespace simdjson -/* end file include/simdjson/generic/ondemand/object.h */ -/* begin file include/simdjson/generic/ondemand/parser.h */ - -namespace simdjson { -namespace SIMDJSON_BUILTIN_IMPLEMENTATION { -namespace ondemand { - -class array; -class object; -class value; -class raw_json_string; -class document_stream; - -/** - * 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. + * A document_reference is a thin wrapper around a document reference instance. */ -class parser { +class document_reference { 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. - * - * ### 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; - /** @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(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; + 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; + 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; - /** - * @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; + simdjson_inline simdjson_result is_null() noexcept; + simdjson_inline simdjson_result raw_json() noexcept; + simdjson_inline operator document&() const noexcept; +#if SIMDJSON_EXCEPTIONS + 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; - /** - * 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). - * - * 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 + simdjson_inline simdjson_result type() noexcept; + simdjson_inline simdjson_result is_scalar() 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; + 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; +private: + document *doc{nullptr}; +}; +} // namespace ondemand +} // namespace westmere +} // namespace simdjson - /** The capacity of this parser (the largest document it can process). */ - simdjson_inline size_t capacity() const noexcept; - /** The maximum capacity of this parser (the largest document it is allowed to process). */ - 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_inline size_t max_depth() const noexcept; +namespace simdjson { - /** - * 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; +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; - #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 + 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; + 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; - /** - * 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; + template simdjson_inline simdjson_result get() & noexcept; + template simdjson_inline simdjson_result get() && 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; + template simdjson_inline error_code get(T &out) & noexcept; + template simdjson_inline error_code get(T &out) && noexcept; -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{}; +#if SIMDJSON_EXCEPTIONS + 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 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; - friend class json_iterator; - friend class document_stream; + simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; }; -} // namespace ondemand -} // namespace SIMDJSON_BUILTIN_IMPLEMENTATION + } // namespace simdjson + + namespace simdjson { template<> -struct simdjson_result : public SIMDJSON_BUILTIN_IMPLEMENTATION::implementation_simdjson_result_base { +struct simdjson_result : public westmere::implementation_simdjson_result_base { public: - simdjson_inline simdjson_result(SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::parser &&value) noexcept; ///< @private - simdjson_inline simdjson_result(error_code error) noexcept; ///< @private + 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; + 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 + 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 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; }; + } // namespace simdjson -/* end file include/simdjson/generic/ondemand/parser.h */ -/* begin file include/simdjson/generic/ondemand/document_stream.h */ + +#endif // SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_H +/* end file simdjson/generic/ondemand/document.h for westmere */ +/* including simdjson/generic/ondemand/document_stream.h for westmere: #include "simdjson/generic/ondemand/document_stream.h" */ +/* begin file simdjson/generic/ondemand/document_stream.h for westmere */ +#ifndef SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/parser.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + #ifdef SIMDJSON_THREADS_ENABLED #include #include @@ -26815,13 +81645,9 @@ struct simdjson_result : publ #endif namespace simdjson { -namespace SIMDJSON_BUILTIN_IMPLEMENTATION { +namespace westmere { namespace ondemand { -class parser; -class json_iterator; -class document; - #ifdef SIMDJSON_THREADS_ENABLED /** @private Custom worker class **/ struct stage1_worker { @@ -27097,50 +81923,469 @@ class document_stream { /** 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; + + /** 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 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; +}; + +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_H +/* end file simdjson/generic/ondemand/document_stream.h for westmere */ +/* including simdjson/generic/ondemand/field.h for westmere: #include "simdjson/generic/ondemand/field.h" */ +/* begin file simdjson/generic/ondemand/field.h for westmere */ +#ifndef SIMDJSON_GENERIC_ONDEMAND_FIELD_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_FIELD_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { +namespace westmere { +namespace ondemand { + +/** + * A JSON field (key/value pair) in an object. + * + * Returned from object iteration. + * + * Extends from std::pair 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) noexcept; + /** + * Get the key as a raw_json_string. Can be used for direct comparison with + * an unescaped C string: e.g., key() == "test". + */ + simdjson_inline raw_json_string 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 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; + + simdjson_inline simdjson_result unescaped_key(bool allow_replacement = false) noexcept; + simdjson_inline simdjson_result key() noexcept; + simdjson_inline simdjson_result value() noexcept; +}; + +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_ONDEMAND_FIELD_H +/* end file simdjson/generic/ondemand/field.h for westmere */ +/* including simdjson/generic/ondemand/object.h for westmere: #include "simdjson/generic/ondemand/object.h" */ +/* begin file simdjson/generic/ondemand/object.h for westmere */ +#ifndef SIMDJSON_GENERIC_ONDEMAND_OBJECT_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_OBJECT_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { +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; + + 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. + * + * @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 wasn't there when they aren't). + * + * 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. + * + * @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; + + /** + * 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 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 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; + + 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; + 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 westmere */ +/* including simdjson/generic/ondemand/object_iterator.h for westmere: #include "simdjson/generic/ondemand/object_iterator.h" */ +/* begin file simdjson/generic/ondemand/object_iterator.h for westmere */ +#ifndef SIMDJSON_GENERIC_ONDEMAND_OBJECT_ITERATOR_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_OBJECT_ITERATOR_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { +namespace westmere { +namespace ondemand { + +class object_iterator { +public: + /** + * Create a new invalid object_iterator. + * + * Exists so you can declare a variable and later assign to it before use. + */ + simdjson_inline object_iterator() noexcept = default; - /** Start a thread to run stage 1 on the next batch. */ - inline void start_stage1_thread() noexcept; + // + // Iterator interface + // - /** Wait for the stage 1 thread to finish and capture the results. */ - inline void finish_stage1_thread() 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 ',' + simdjson_inline object_iterator &operator++() 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()}; +private: /** - * The parser used to run stage 1 in the background. Will be swapped - * with the regular parser when finished. + * 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. */ - ondemand::parser stage1_thread_parser{}; - - friend struct stage1_worker; - #endif // SIMDJSON_THREADS_ENABLED + value_iterator iter{}; - friend class parser; - friend class document; - friend class json_iterator; - friend struct simdjson_result; - friend struct internal::simdjson_result_base; -}; // document_stream + simdjson_inline object_iterator(const value_iterator &iter) noexcept; + friend struct simdjson_result; + friend class object; +}; } // namespace ondemand -} // namespace SIMDJSON_BUILTIN_IMPLEMENTATION +} // namespace westmere } // namespace simdjson namespace simdjson { + template<> -struct simdjson_result : public SIMDJSON_BUILTIN_IMPLEMENTATION::implementation_simdjson_result_base { +struct simdjson_result : public westmere::implementation_simdjson_result_base { public: - simdjson_inline simdjson_result(SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::document_stream &&value) noexcept; ///< @private + simdjson_inline simdjson_result(westmere::ondemand::object_iterator &&value) noexcept; ///< @private simdjson_inline simdjson_result(error_code error) noexcept; ///< @private simdjson_inline simdjson_result() noexcept = default; + + // + // Iterator interface + // + + // Reads key and value, yielding them to the user. + simdjson_inline simdjson_result 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 -/* end file include/simdjson/generic/ondemand/document_stream.h */ -/* begin file include/simdjson/generic/ondemand/serialization.h */ + +#endif // SIMDJSON_GENERIC_ONDEMAND_OBJECT_ITERATOR_H +/* end file simdjson/generic/ondemand/object_iterator.h for westmere */ +/* including simdjson/generic/ondemand/serialization.h for westmere: #include "simdjson/generic/ondemand/serialization.h" */ +/* begin file simdjson/generic/ondemand/serialization.h for westmere */ +#ifndef SIMDJSON_GENERIC_ONDEMAND_SERIALIZATION_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_SERIALIZATION_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { /** @@ -27148,30 +82393,30 @@ namespace simdjson { * contains JSON text that is suitable to be parsed as JSON again. It does not * validate the content. */ -inline simdjson_result to_json_string(SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::document& x) noexcept; +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(SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::value& x) noexcept; +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(SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::object& x) noexcept; +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(SIMDJSON_BUILTIN_IMPLEMENTATION::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); +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); } // namespace simdjson /** @@ -27181,7 +82426,7 @@ inline simdjson_result to_json_string(simdjson_result x); +inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result x); #endif /** * Print JSON to an output stream. It does not @@ -27203,9 +82448,9 @@ 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 @@ -27215,13 +82460,13 @@ 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::SIMDJSON_BUILTIN_IMPLEMENTATION::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 @@ -27231,3306 +82476,3556 @@ 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::SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand -/* end file include/simdjson/generic/ondemand/serialization.h */ -/* end file include/simdjson/generic/ondemand.h */ +}}} // namespace simdjson::westmere::ondemand + +#endif // SIMDJSON_GENERIC_ONDEMAND_SERIALIZATION_H +/* end file simdjson/generic/ondemand/serialization.h for westmere */ // Inline definitions -/* begin file include/simdjson/generic/implementation_simdjson_result_base-inl.h */ +/* including simdjson/generic/ondemand/array-inl.h for westmere: #include "simdjson/generic/ondemand/array-inl.h" */ +/* begin file simdjson/generic/ondemand/array-inl.h for westmere */ +#ifndef SIMDJSON_GENERIC_ONDEMAND_ARRAY_INL_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_ARRAY_INL_H */ +/* amalgamation skipped (editor-only): #include "simdjson/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 SIMDJSON_BUILTIN_IMPLEMENTATION { +namespace westmere { +namespace ondemand { // -// internal::implementation_simdjson_result_base inline implementation +// ### 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. // -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_inline error_code implementation_simdjson_result_base::error() const noexcept { - return this->second; -} - -#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::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; +simdjson_inline array::array(const value_iterator &_iter) noexcept + : iter{_iter} +{ } -template -simdjson_inline T& implementation_simdjson_result_base::value_unsafe() & noexcept { - return this->first; +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); } - -template -simdjson_inline T&& implementation_simdjson_result_base::value_unsafe() && noexcept { - return std::forward(this->first); +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); } - -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 SIMDJSON_BUILTIN_IMPLEMENTATION -} // namespace simdjson -/* end file include/simdjson/generic/implementation_simdjson_result_base-inl.h */ -/* begin file include/simdjson/generic/ondemand-inl.h */ -/* begin file include/simdjson/generic/ondemand/json_type-inl.h */ -namespace simdjson { -namespace SIMDJSON_BUILTIN_IMPLEMENTATION { -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; +simdjson_inline simdjson_result array::started(value_iterator &iter) noexcept { + bool has_value; + SIMDJSON_TRY(iter.started_array().get(has_value)); + return array(iter); } -#if SIMDJSON_EXCEPTIONS -inline std::ostream& operator<<(std::ostream& out, simdjson_result &type) noexcept(false) { - return out << type.value(); -} +simdjson_inline simdjson_result array::begin() noexcept { +#if SIMDJSON_DEVELOPMENT_CHECKS + if (!iter.is_at_iterator_start()) { return OUT_OF_ORDER_ITERATION; } #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 SIMDJSON_BUILTIN_IMPLEMENTATION -} // namespace simdjson - -namespace simdjson { - -simdjson_inline simdjson_result::simdjson_result(SIMDJSON_BUILTIN_IMPLEMENTATION::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 -/* end file include/simdjson/generic/ondemand/json_type-inl.h */ -/* begin file include/simdjson/generic/ondemand/logger-inl.h */ -namespace simdjson { -namespace SIMDJSON_BUILTIN_IMPLEMENTATION { -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 ' '; - } -} - -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); -} - -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); -} -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); -} - -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); - 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); - 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); -} - -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); -} -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); -} - -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) { - // 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); - } -} - -inline void log_line(const json_iterator &iter, const char *title_prefix, const char *title, std::string_view detail, int delta, int depth_delta) noexcept { - log_line(iter, iter.position()+delta, depth_t(iter.depth()+depth_delta), title_prefix, title, detail); -} -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) noexcept { - if (LOG_ENABLED) { - const int indent = depth*2; - const auto buf = iter.token.buf; - printf("| %*s%s%-*s ", - indent, "", - title_prefix, - LOG_EVENT_LEN - indent - int(strlen(title_prefix)), title - ); - { - // 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(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; + return array_iterator(iter); } - - -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_result array::end() noexcept { + return array_iterator(iter); } - -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 error_code array::consume() noexcept { + auto error = iter.json_iter().skip_child(iter.depth()-1); + if(error) { iter.abandon(); } + return error; } -simdjson_unused simdjson_inline bool operator==(const raw_json_string &a, std::string_view c) noexcept { - return a.unsafe_is_equal(c); +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_unused simdjson_inline bool operator==(std::string_view c, const raw_json_string &a) noexcept { - return a == c; +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_unused simdjson_inline bool operator!=(const raw_json_string &a, std::string_view c) noexcept { - return !(a == c); +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_unused simdjson_inline bool operator!=(std::string_view c, const raw_json_string &a) noexcept { - return !(a == c); +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; + } -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); -} + // 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" -simdjson_inline simdjson_warn_unused simdjson_result raw_json_string::unescape_wobbly(json_iterator &iter) const noexcept { - return iter.unescape_wobbly(*this); + // 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; } -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_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 SIMDJSON_BUILTIN_IMPLEMENTATION +} // namespace westmere } // namespace simdjson namespace simdjson { -simdjson_inline simdjson_result::simdjson_result(SIMDJSON_BUILTIN_IMPLEMENTATION::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::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::raw() const noexcept { +simdjson_inline simdjson_result simdjson_result::begin() noexcept { if (error()) { return error(); } - return first.raw(); + return first.begin(); } -simdjson_inline simdjson_warn_unused simdjson_result simdjson_result::unescape(SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::json_iterator &iter, bool allow_replacement) const noexcept { +simdjson_inline simdjson_result simdjson_result::end() noexcept { if (error()) { return error(); } - return first.unescape(iter, allow_replacement); + return first.end(); } -simdjson_inline simdjson_warn_unused simdjson_result simdjson_result::unescape_wobbly(SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::json_iterator &iter) const noexcept { +simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { if (error()) { return error(); } - return first.unescape_wobbly(iter); + 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::raw_json() noexcept { + if (error()) { return error(); } + return first.raw_json(); } } // namespace simdjson -/* end file include/simdjson/generic/ondemand/raw_json_string-inl.h */ -/* begin file include/simdjson/generic/ondemand/token_iterator-inl.h */ -namespace simdjson { -namespace SIMDJSON_BUILTIN_IMPLEMENTATION { -namespace ondemand { -simdjson_inline token_iterator::token_iterator( - const uint8_t *_buf, - token_position position -) noexcept : buf{_buf}, _position{position} -{ -} +#endif // SIMDJSON_GENERIC_ONDEMAND_ARRAY_INL_H +/* end file simdjson/generic/ondemand/array-inl.h for westmere */ +/* including simdjson/generic/ondemand/array_iterator-inl.h for westmere: #include "simdjson/generic/ondemand/array_iterator-inl.h" */ +/* begin file simdjson/generic/ondemand/array_iterator-inl.h for westmere */ +#ifndef SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_INL_H -simdjson_inline uint32_t token_iterator::current_offset() const noexcept { - return *(_position); -} +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_INL_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array_iterator.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator-inl.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ +namespace simdjson { +namespace westmere { +namespace ondemand { -simdjson_inline const uint8_t *token_iterator::return_current_and_advance() noexcept { - return &buf[*(_position++)]; -} +simdjson_inline array_iterator::array_iterator(const value_iterator &_iter) noexcept + : iter{_iter} +{} -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 simdjson_result array_iterator::operator*() noexcept { + if (iter.error()) { iter.abandon(); return iter.error(); } + return value(iter.child()); } - -simdjson_inline const uint8_t *token_iterator::peek(int32_t delta) const noexcept { - return &buf[*(_position+delta)]; +simdjson_inline bool array_iterator::operator==(const array_iterator &other) const noexcept { + return !(*this != other); } -simdjson_inline uint32_t token_iterator::peek_index(int32_t delta) const noexcept { - return *(_position+delta); +simdjson_inline bool array_iterator::operator!=(const array_iterator &) const noexcept { + return iter.is_open(); } -simdjson_inline uint32_t token_iterator::peek_length(int32_t delta) const noexcept { - return *(_position+delta+1) - *(_position+delta); +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 token_position token_iterator::position() const noexcept { - return _position; -} -simdjson_inline void token_iterator::set_position(token_position target_position) noexcept { - _position = target_position; -} +} // namespace ondemand +} // namespace westmere +} // namespace simdjson -simdjson_inline bool token_iterator::operator==(const token_iterator &other) const noexcept { - return _position == other._position; +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 bool token_iterator::operator!=(const token_iterator &other) const noexcept { - return _position != other._position; +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : westmere::implementation_simdjson_result_base({}, error) +{ } -simdjson_inline bool token_iterator::operator>(const token_iterator &other) const noexcept { - return _position > other._position; + +simdjson_inline simdjson_result simdjson_result::operator*() noexcept { + if (error()) { return error(); } + return *first; } -simdjson_inline bool token_iterator::operator>=(const token_iterator &other) const noexcept { - return _position >= other._position; +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 token_iterator::operator<(const token_iterator &other) const noexcept { - return _position < other._position; +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 token_iterator::operator<=(const token_iterator &other) const noexcept { - return _position <= other._position; +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 ondemand -} // namespace SIMDJSON_BUILTIN_IMPLEMENTATION } // namespace simdjson -namespace simdjson { - -simdjson_inline simdjson_result::simdjson_result(SIMDJSON_BUILTIN_IMPLEMENTATION::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) {} +#endif // SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_INL_H +/* end file simdjson/generic/ondemand/array_iterator-inl.h for westmere */ +/* including simdjson/generic/ondemand/document-inl.h for westmere: #include "simdjson/generic/ondemand/document-inl.h" */ +/* begin file simdjson/generic/ondemand/document-inl.h for westmere */ +#ifndef SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array-inl.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_iterator-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_type.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */ +/* 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 -/* end file include/simdjson/generic/ondemand/token_iterator-inl.h */ -/* begin file include/simdjson/generic/ondemand/json_iterator-inl.h */ namespace simdjson { -namespace SIMDJSON_BUILTIN_IMPLEMENTATION { +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} +simdjson_inline document::document(ondemand::json_iterator &&_iter) noexcept + : iter{std::forward(_iter)} { - other.parser = nullptr; + logger::log_start_value(iter, "document"); } -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 document document::start(json_iterator &&iter) noexcept { + return document(std::forward(iter)); } -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} +inline void document::rewind() noexcept { + iter.rewind(); +} -{ - logger::log_headers(); -#if SIMDJSON_CHECK_EOF - assert_more_tokens(); -#endif +inline std::string document::to_debug_string() noexcept { + return iter.to_string(); } -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 simdjson_result document::current_location() const noexcept { + return iter.current_location(); } -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 int32_t document::current_depth() const noexcept { + return iter.depth(); } +inline bool document::at_end() const noexcept { + return iter.at_end(); +} -// 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; +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()); } - - // 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; +} +simdjson_inline simdjson_result document::get_value() noexcept { + // Make sure we start any arrays or objects before returning, so that start_root_() + // gets called. + iter.assert_at_document_depth(); + 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); +} - return report_error(TAPE_ERROR, "not enough close braces"); +/** + * 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); +} +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_POP_DISABLE_WARNINGS +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(); } -simdjson_inline bool json_iterator::at_root() const noexcept { - return position() == root_position(); +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 std::forward(*this).get_double(); } +template<> simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_uint64(); } +template<> simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_int64(); } +template<> simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_bool(); } +template<> 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_inline error_code document::get(T &out) && noexcept { + return std::forward(*this).get().get(out); +} + +#if SIMDJSON_EXCEPTIONS +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 bool json_iterator::is_single_token() const noexcept { - return parser->implementation->n_structural_indexes == 1; +simdjson_inline error_code document::consume() noexcept { + auto error = iter.skip_child(0); + if(error) { iter.abandon(); } + return error; } -simdjson_inline bool json_iterator::streaming() const noexcept { - return _streaming; +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 token_position json_iterator::root_position() const noexcept { - return _root; +simdjson_inline simdjson_result document::type() noexcept { + return get_root_value_iterator().type(); } -simdjson_inline void json_iterator::assert_at_document_depth() const noexcept { - SIMDJSON_ASSUME( _depth == 1 ); +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 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 bool document::is_negative() noexcept { + return get_root_value_iterator().is_root_negative(); } -simdjson_inline void json_iterator::assert_more_tokens(uint32_t required_tokens) const noexcept { - assert_valid_position(token._position + required_tokens - 1); +simdjson_inline simdjson_result document::is_integer() noexcept { + return get_root_value_iterator().is_root_integer(true); } -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 simdjson_result document::get_number_type() noexcept { + return get_root_value_iterator().get_root_number_type(true); } -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 simdjson_result document::get_number() noexcept { + return get_root_value_iterator().get_root_number(true); } -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(" ]"); + +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_start_length()); } -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()); - } +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(); } - if (at_end()) { - return OUT_OF_BOUNDS; + 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; } - return reinterpret_cast(token.peek()); } -simdjson_inline bool json_iterator::is_alive() const noexcept { - return parser; -} +} // namespace ondemand +} // namespace westmere +} // namespace simdjson -simdjson_inline void json_iterator::abandon() noexcept { - parser = nullptr; - _depth = 0; -} +namespace simdjson { -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 simdjson_result::simdjson_result( + westmere::ondemand::document &&value +) noexcept : + implementation_simdjson_result_base( + std::forward(value) + ) +{ } - -simdjson_inline const uint8_t *json_iterator::unsafe_pointer() const noexcept { - // deliberately done without safety guard: - return token.peek(0); +simdjson_inline simdjson_result::simdjson_result( + error_code error +) noexcept : + implementation_simdjson_result_base( + error + ) +{ } - -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 simdjson_result simdjson_result::count_elements() & noexcept { + if (error()) { return error(); } + return first.count_elements(); } - -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 simdjson_result simdjson_result::count_fields() & noexcept { + if (error()) { return error(); } + return first.count_fields(); } - -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 simdjson_result simdjson_result::at(size_t index) & noexcept { + if (error()) { return error(); } + return first.at(index); } - -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 error_code simdjson_result::rewind() noexcept { + if (error()) { return error(); } + first.rewind(); + return SUCCESS; } - -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 simdjson_result simdjson_result::begin() & noexcept { + if (error()) { return error(); } + return first.begin(); } -simdjson_inline const uint8_t *json_iterator::peek_last() const noexcept { - return token.peek(last_position()); +simdjson_inline simdjson_result simdjson_result::end() & noexcept { + return {}; } - -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 simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); } - -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 simdjson_result simdjson_result::find_field_unordered(const char *key) & noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); } - -simdjson_inline depth_t json_iterator::depth() const noexcept { - return _depth; +simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { + if (error()) { return error(); } + return first[key]; } - -simdjson_inline uint8_t *&json_iterator::string_buf_loc() noexcept { - return _string_buf_loc; +simdjson_inline simdjson_result simdjson_result::operator[](const char *key) & noexcept { + if (error()) { return error(); } + return first[key]; } - -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 simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { + if (error()) { return error(); } + return first.find_field(key); } - -simdjson_inline token_position json_iterator::position() const noexcept { - return token.position(); +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 json_iterator::unescape(raw_json_string in, bool allow_replacement) noexcept { - return parser->unescape(in, _string_buf_loc, allow_replacement); +simdjson_inline simdjson_result simdjson_result::get_array() & noexcept { + if (error()) { return error(); } + return first.get_array(); } - -simdjson_inline simdjson_result json_iterator::unescape_wobbly(raw_json_string in) noexcept { - return parser->unescape_wobbly(in, _string_buf_loc); +simdjson_inline simdjson_result simdjson_result::get_object() & noexcept { + if (error()) { return error(); } + return first.get_object(); } - -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 simdjson_result simdjson_result::get_uint64() noexcept { + if (error()) { return error(); } + return first.get_uint64(); } - -simdjson_inline error_code json_iterator::consume_character(char c) noexcept { - if (*peek() == c) { - return_current_and_advance(); - return SUCCESS; - } - return TAPE_ERROR; +simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { + if (error()) { return error(); } + return first.get_uint64_in_string(); } - -#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 simdjson_result simdjson_result::get_int64() noexcept { + if (error()) { return error(); } + return first.get_int64(); } - -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 simdjson_result simdjson_result::get_int64_in_string() noexcept { + if (error()) { return error(); } + return first.get_int64_in_string(); } - -#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_inline simdjson_result simdjson_result::get_double() noexcept { + if (error()) { return error(); } + return first.get_double(); } - - -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_inline simdjson_result simdjson_result::get_double_in_string() noexcept { + if (error()) { return error(); } + return first.get_double_in_string(); } - -} // namespace ondemand -} // namespace SIMDJSON_BUILTIN_IMPLEMENTATION -} // namespace simdjson - -namespace simdjson { - -simdjson_inline simdjson_result::simdjson_result(SIMDJSON_BUILTIN_IMPLEMENTATION::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 -/* end file include/simdjson/generic/ondemand/json_iterator-inl.h */ -/* begin file include/simdjson/generic/ondemand/value_iterator-inl.h */ -namespace simdjson { -namespace SIMDJSON_BUILTIN_IMPLEMENTATION { -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 simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.get_string(allow_replacement); } - -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::start_object() noexcept { - SIMDJSON_TRY( start_container('{', "Not an object", "object") ); - return started_object(); +simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { + if (error()) { return error(); } + return first.get_wobbly_string(); } - -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_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_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; +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_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); } -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"); - } - } +template<> simdjson_inline simdjson_result simdjson_result::get() & noexcept = delete; +template<> 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_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_inline simdjson_result simdjson_result::type() noexcept { + if (error()) { return error(); } + return first.type(); } -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_inline simdjson_result simdjson_result::is_scalar() noexcept { + if (error()) { return error(); } + return first.is_scalar(); } -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_inline bool simdjson_result::is_negative() noexcept { + if (error()) { return error(); } + return first.is_negative(); } -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_inline simdjson_result simdjson_result::is_integer() noexcept { + if (error()) { return error(); } + return first.is_integer(); +} - // - // 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 isn't 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; +simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { + if (error()) { return error(); } + return first.get_number_type(); +} - // 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; - } +simdjson_inline simdjson_result simdjson_result::get_number() noexcept { + if (error()) { return error(); } + return first.get_number(); +} + + +#if SIMDJSON_EXCEPTIONS +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 - // 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_inline simdjson_result simdjson_result::current_location() noexcept { + if (error()) { return error(); } + return first.current_location(); } -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_inline bool simdjson_result::at_end() const noexcept { + if (error()) { return error(); } + return first.at_end(); +} - // 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; +simdjson_inline int32_t simdjson_result::current_depth() const noexcept { + if (error()) { return error(); } + return first.current_depth(); +} - // 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()) { +simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { + if (error()) { return error(); } + return first.raw_json_token(); +} -#if SIMDJSON_DEVELOPMENT_CHECKS - // If we're past the end of the object, we're being iterated out of order. - // Note: this isn't 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 - } +simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { + if (error()) { return error(); } + return first.at_pointer(json_pointer); +} - // 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; } +} // namespace simdjson - // 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; } +namespace simdjson { +namespace westmere { +namespace ondemand { - // 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 +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); } +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); } - // 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 SIMDJSON_EXCEPTIONS +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 raw_json_string(*doc); } +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::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::raw_json() noexcept { return doc->raw_json();} +simdjson_inline document_reference::operator document&() const noexcept { return *doc; } - // 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; - } +} // namespace ondemand +} // namespace westmere +} // namespace simdjson - // 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); -} +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_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); +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); +} +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_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 +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_warn_unused simdjson_inline simdjson_result value_iterator::start_array() noexcept { - SIMDJSON_TRY( start_container('[', "Not an array", "array") ); - return started_array(); +simdjson_inline simdjson_result simdjson_result::current_location() noexcept { + if (error()) { return error(); } + return first.current_location(); } -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_inline simdjson_result simdjson_result::raw_json_token() noexcept { + if (error()) { return error(); } + return first.raw_json_token(); } -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_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { + if (error()) { return error(); } + return first.at_pointer(json_pointer); } -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; + +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H +/* end file simdjson/generic/ondemand/document-inl.h for westmere */ +/* including simdjson/generic/ondemand/document_stream-inl.h for westmere: #include "simdjson/generic/ondemand/document_stream-inl.h" */ +/* begin file simdjson/generic/ondemand/document_stream-inl.h for westmere */ +#ifndef SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_INL_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_INL_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document_stream.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base-inl.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +#include +#include + +namespace simdjson { +namespace westmere { +namespace ondemand { + +#ifdef SIMDJSON_THREADS_ENABLED + +inline void stage1_worker::finish() { + // After calling "run" someone would call finish() to wait + // for the end of the processing. + // This function will wait until either the thread has done + // the processing or, else, the destructor has been called. + std::unique_lock lock(locking_mutex); + cond_var.wait(lock, [this]{return has_work == false;}); } -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"); +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(); } - 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(); +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(); } -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::has_next_element() noexcept { - assert_at_next(); +#endif // SIMDJSON_THREADS_ENABLED - 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_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_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_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_warn_unused simdjson_inline simdjson_result value_iterator::get_string(bool allow_replacement) noexcept { - return get_raw_json_string().unescape(json_iter(), allow_replacement); -} -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_inline document_stream::~document_stream() noexcept +{ + #ifdef SIMDJSON_THREADS_ENABLED + worker.reset(); + #endif } -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; + +inline size_t document_stream::size_in_bytes() const noexcept { + return len; } -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; + +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 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 document_stream::iterator::iterator() noexcept + : stream{nullptr}, finished{true} { } -simdjson_inline bool value_iterator::is_negative() noexcept { - return numberparsing::is_negative(peek_non_root_scalar("numbersign")); + +simdjson_inline document_stream::iterator::iterator(document_stream* _stream, bool is_end) noexcept + : stream{_stream}, finished{is_end} { } -simdjson_inline bool value_iterator::is_root_negative() noexcept { - return numberparsing::is_negative(peek_root_scalar("numbersign")); + +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 value_iterator::is_integer() noexcept { - return numberparsing::is_integer(peek_non_root_scalar("integer")); + +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 simdjson_result value_iterator::get_number_type() noexcept { - return numberparsing::get_number_type(peek_non_root_scalar("integer")); + +simdjson_inline bool document_stream::iterator::operator!=(const document_stream::iterator &other) const noexcept { + return finished != other.finished; } -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 document_stream::iterator document_stream::begin() noexcept { + start(); + // If there are no documents, we're finished. + return iterator(this, error == EMPTY); } -simdjson_inline simdjson_result value_iterator::is_root_integer(bool check_trailing) noexcept { - auto max_len = peek_start_length(); - auto json = peek_root_scalar("is_root_integer"); - uint8_t tmpbuf[20+1]; // <20 digits> is the longest possible unsigned integer - 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 document_stream::iterator document_stream::end() noexcept { + return iterator(this, true); } -simdjson_inline simdjson_result value_iterator::get_root_number_type(bool check_trailing) noexcept { - auto max_len = peek_start_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]; - 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; +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); } - 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_start_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]; - 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; + 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; } } - 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); -} -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); + #endif // SIMDJSON_THREADS_ENABLED } -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::get_root_uint64(bool check_trailing) noexcept { - auto max_len = peek_start_length(); - auto json = peek_root_scalar("uint64"); - uint8_t tmpbuf[20+1]; // <20 digits> is the longest possible unsigned integer - 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"); + +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; + } } - return result; } -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::get_root_uint64_in_string(bool check_trailing) noexcept { - auto max_len = peek_start_length(); - auto json = peek_root_scalar("uint64"); - uint8_t tmpbuf[20+1]; // <20 digits> is the longest possible unsigned integer - 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; + +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(); } -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::get_root_int64(bool check_trailing) noexcept { - auto max_len = peek_start_length(); - auto json = peek_root_scalar("int64"); - uint8_t tmpbuf[20+1]; // -<19 digits> is the longest possible integer - 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; +inline size_t document_stream::next_batch_start() const noexcept { + return batch_start + parser->implementation->structural_indexes[parser->implementation->n_structural_indexes]; } -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::get_root_int64_in_string(bool check_trailing) noexcept { - auto max_len = peek_start_length(); - auto json = peek_root_scalar("int64"); - uint8_t tmpbuf[20+1]; // -<19 digits> is the longest possible integer - 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"); +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); } - return result; } -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::get_root_double(bool check_trailing) noexcept { - auto max_len = peek_start_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]; - 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_inline size_t document_stream::iterator::current_index() const noexcept { + return stream->doc_index; } -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::get_root_double_in_string(bool check_trailing) noexcept { - auto max_len = peek_start_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]; - 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"); +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: Remove any trailing whitespaces + // This returns a string spanning from start of value to the beginning of the next document (excluded) + return std::string_view(reinterpret_cast(stream->buf) + current_index(), stream->parser->implementation->structural_indexes[++cur_struct_index] - current_index() - 1); + } + cur_struct_index++; } - return result; -} -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::get_root_bool(bool check_trailing) noexcept { - auto max_len = peek_start_length(); - auto json = peek_root_scalar("bool"); - uint8_t tmpbuf[5+1]; - 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"); + + 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 result; + + 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 simdjson_result value_iterator::is_root_null(bool check_trailing) noexcept { - auto max_len = peek_start_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"); + +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(); } - 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 ); +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(); - return _json_iter->skip_child(depth()); + worker->run(this, & this->stage1_thread_parser, _next_batch_start); } -simdjson_inline value_iterator value_iterator::child() const noexcept { - assert_at_child(); - return { _json_iter, depth()+1, _json_iter->token.position() }; +#endif // SIMDJSON_THREADS_ENABLED + +} // 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) + ) +{ } -// 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(); +#endif // SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_INL_H +/* end file simdjson/generic/ondemand/document_stream-inl.h for westmere */ +/* including simdjson/generic/ondemand/field-inl.h for westmere: #include "simdjson/generic/ondemand/field-inl.h" */ +/* begin file simdjson/generic/ondemand/field-inl.h for westmere */ +#ifndef SIMDJSON_GENERIC_ONDEMAND_FIELD_INL_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_FIELD_INL_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/field.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator-inl.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { +namespace westmere { +namespace ondemand { + +// clang 6 doesn't 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 bool value_iterator::at_start() const noexcept { - return _json_iter->token.position() == start_position(); +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 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 simdjson_result field::start(const value_iterator &parent_iter, raw_json_string key) noexcept { + return field(key, parent_iter.child()); } -simdjson_inline void value_iterator::abandon() noexcept { - _json_iter->abandon(); +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_warn_unused simdjson_inline depth_t value_iterator::depth() const noexcept { - return _depth; +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_warn_unused simdjson_inline error_code value_iterator::error() const noexcept { - return _json_iter->error; + +simdjson_inline value &field::value() & noexcept { + return second; } -simdjson_warn_unused simdjson_inline uint8_t *&value_iterator::string_buf_loc() noexcept { - return _json_iter->string_buf_loc(); + +simdjson_inline value field::value() && noexcept { + return std::forward(*this).second; } -simdjson_warn_unused simdjson_inline const json_iterator &value_iterator::json_iter() const noexcept { - return *_json_iter; + +} // 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_warn_unused simdjson_inline json_iterator &value_iterator::json_iter() noexcept { - return *_json_iter; +simdjson_inline simdjson_result::simdjson_result( + error_code error +) noexcept : + implementation_simdjson_result_base(error) +{ } -simdjson_inline const uint8_t *value_iterator::peek_start() const noexcept { - return _json_iter->peek(start_position()); +simdjson_inline simdjson_result simdjson_result::key() noexcept { + if (error()) { return error(); } + return first.key(); } -simdjson_inline uint32_t value_iterator::peek_start_length() const noexcept { - return _json_iter->peek_length(start_position()); +simdjson_inline simdjson_result simdjson_result::unescaped_key(bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.unescaped_key(allow_replacement); +} +simdjson_inline simdjson_result simdjson_result::value() noexcept { + if (error()) { return error(); } + return std::move(first.value()); } -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(); } +} // namespace simdjson - // Get the JSON and advance the cursor, decreasing depth to signify that we have retrieved the value. - assert_at_start(); - return _json_iter->peek(); -} +#endif // SIMDJSON_GENERIC_ONDEMAND_FIELD_INL_H +/* end file simdjson/generic/ondemand/field-inl.h for westmere */ +/* including simdjson/generic/ondemand/json_iterator-inl.h for westmere: #include "simdjson/generic/ondemand/json_iterator-inl.h" */ +/* begin file simdjson/generic/ondemand/json_iterator-inl.h for westmere */ +#ifndef SIMDJSON_GENERIC_ONDEMAND_JSON_ITERATOR_INL_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_JSON_ITERATOR_INL_H */ +/* amalgamation skipped (editor-only): #include "simdjson/internal/dom_parser_implementation.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/parser.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/logger-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/parser-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/token_iterator-inl.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ -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; } +namespace simdjson { +namespace westmere { +namespace ondemand { - // 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 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 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; } +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 - 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(); - } +} +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; +} - return SUCCESS; +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; } -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(); } +// 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 - 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(); } + // 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; + } - assert_at_non_root_start(); - return _json_iter->peek(); + // 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_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_POP_DISABLE_WARNINGS - assert_at_root(); - _json_iter->return_current_and_advance(); - _json_iter->ascend_to(depth()-1); +simdjson_inline bool json_iterator::at_root() const noexcept { + return position() == root_position(); } -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 bool json_iterator::is_single_token() const noexcept { + return parser->implementation->n_structural_indexes == 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 json_iterator::streaming() const noexcept { + return _streaming; } -simdjson_inline bool value_iterator::is_at_start() const noexcept { - return position() == start_position(); +simdjson_inline token_position json_iterator::root_position() const noexcept { + return _root; } -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 void json_iterator::assert_at_document_depth() const noexcept { + SIMDJSON_ASSUME( _depth == 1 ); } -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 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 } -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 ); +simdjson_inline void json_iterator::assert_more_tokens(uint32_t required_tokens) const noexcept { + assert_valid_position(token._position + required_tokens - 1); } -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 ); +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 } -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 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 void value_iterator::move_at_start() noexcept { - _json_iter->_depth = _depth; - _json_iter->token.set_position(_start_position); +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(" ]"); } -simdjson_inline void value_iterator::move_at_container_start() noexcept { - _json_iter->_depth = _depth; - _json_iter->token.set_position(_start_position + 1); +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 simdjson_result value_iterator::reset_array() noexcept { - if(error()) { return error(); } - move_at_container_start(); - return started_array(); +simdjson_inline bool json_iterator::is_alive() const noexcept { + return parser; } -simdjson_inline simdjson_result value_iterator::reset_object() noexcept { - if(error()) { return error(); } - move_at_container_start(); - return started_object(); +simdjson_inline void json_iterator::abandon() noexcept { + parser = nullptr; + _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 ); +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(); } -inline void value_iterator::assert_at_root() const noexcept { - assert_at_start(); - SIMDJSON_ASSUME( _depth == 1 ); +simdjson_inline const uint8_t *json_iterator::unsafe_pointer() const noexcept { + // deliberately done without safety guard: + return token.peek(); } -inline void value_iterator::assert_at_non_root_start() const noexcept { - assert_at_start(); - SIMDJSON_ASSUME( _depth > 1 ); +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); } -inline void value_iterator::assert_is_valid() const noexcept { - SIMDJSON_ASSUME( _json_iter != nullptr ); +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 bool value_iterator::is_valid() const noexcept { - return _json_iter != nullptr; +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 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 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 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 value_iterator::start_position() const noexcept { - return _start_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 token_position value_iterator::position() const noexcept { - return _json_iter->position(); +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 token_position value_iterator::end_position() const noexcept { - return _json_iter->end_position(); +simdjson_inline depth_t json_iterator::depth() const noexcept { + return _depth; } -simdjson_inline token_position value_iterator::last_position() const noexcept { - return _json_iter->last_position(); +simdjson_inline uint8_t *&json_iterator::string_buf_loc() noexcept { + return _string_buf_loc; } -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 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; } -} // namespace ondemand -} // namespace SIMDJSON_BUILTIN_IMPLEMENTATION -} // namespace simdjson - -namespace simdjson { - -simdjson_inline simdjson_result::simdjson_result(SIMDJSON_BUILTIN_IMPLEMENTATION::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 -/* end file include/simdjson/generic/ondemand/value_iterator-inl.h */ -/* begin file include/simdjson/generic/ondemand/array_iterator-inl.h */ -namespace simdjson { -namespace SIMDJSON_BUILTIN_IMPLEMENTATION { -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 token_position json_iterator::position() const noexcept { + return token.position(); } -simdjson_inline bool array_iterator::operator==(const array_iterator &other) const noexcept { - return !(*this != other); + +simdjson_inline simdjson_result json_iterator::unescape(raw_json_string in, bool allow_replacement) noexcept { + return parser->unescape(in, _string_buf_loc, allow_replacement); } -simdjson_inline bool array_iterator::operator!=(const array_iterator &) const noexcept { - return iter.is_open(); + +simdjson_inline simdjson_result json_iterator::unescape_wobbly(raw_json_string in) noexcept { + return parser->unescape_wobbly(in, _string_buf_loc); } -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 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; } -} // namespace ondemand -} // namespace SIMDJSON_BUILTIN_IMPLEMENTATION -} // namespace simdjson +simdjson_inline error_code json_iterator::consume_character(char c) noexcept { + if (*peek() == c) { + return_current_and_advance(); + return SUCCESS; + } + return TAPE_ERROR; +} -namespace simdjson { +#if SIMDJSON_DEVELOPMENT_CHECKS -simdjson_inline simdjson_result::simdjson_result( - SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::array_iterator &&value -) noexcept - : SIMDJSON_BUILTIN_IMPLEMENTATION::implementation_simdjson_result_base(std::forward(value)) -{ - first.iter.assert_is_valid(); -} -simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : SIMDJSON_BUILTIN_IMPLEMENTATION::implementation_simdjson_result_base({}, error) -{ +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 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 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; } } -} // namespace simdjson -/* end file include/simdjson/generic/ondemand/array_iterator-inl.h */ -/* begin file include/simdjson/generic/ondemand/object_iterator-inl.h */ -namespace simdjson { -namespace SIMDJSON_BUILTIN_IMPLEMENTATION { -namespace ondemand { - -// -// object_iterator -// +#endif -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 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_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_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_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 SIMDJSON_BUILTIN_IMPLEMENTATION +} // namespace westmere } // namespace simdjson namespace simdjson { -simdjson_inline simdjson_result::simdjson_result( - SIMDJSON_BUILTIN_IMPLEMENTATION::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(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 -/* end file include/simdjson/generic/ondemand/object_iterator-inl.h */ -/* begin file include/simdjson/generic/ondemand/array-inl.h */ + +#endif // SIMDJSON_GENERIC_ONDEMAND_JSON_ITERATOR_INL_H +/* end file simdjson/generic/ondemand/json_iterator-inl.h for westmere */ +/* including simdjson/generic/ondemand/json_type-inl.h for westmere: #include "simdjson/generic/ondemand/json_type-inl.h" */ +/* begin file simdjson/generic/ondemand/json_type-inl.h for westmere */ +#ifndef SIMDJSON_GENERIC_ONDEMAND_JSON_TYPE_INL_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_JSON_TYPE_INL_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_type.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base-inl.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + namespace simdjson { -namespace SIMDJSON_BUILTIN_IMPLEMENTATION { +namespace westmere { namespace ondemand { -// -// ### Live States -// -// While iterating or looking up values, depth >= iter->depth. at_start may vary. Error is -// always SUCCESS: -// -// - Start: This is the state when the array is first found and the iterator is just past the `{`. -// In this state, at_start == true. -// - Next: After we hand a scalar value to the user, or an array/object which they then fully -// iterate over, the iterator is at the `,` before the next value (or `]`). In this state, -// depth == iter->depth, at_start == false, and error == SUCCESS. -// - Unfinished Business: When we hand an array/object to the user which they do not fully -// iterate over, we need to finish that iteration by skipping child values until we reach the -// Next state. In this state, depth > iter->depth, at_start == false, and error == SUCCESS. -// -// ## Error States -// -// In error states, we will yield exactly one more value before stopping. iter->depth == depth -// and at_start is always false. We decrement after yielding the error, moving to the Finished -// state. -// -// - Chained Error: When the array iterator is part of an error chain--for example, in -// `for (auto tweet : doc["tweets"])`, where the tweet element may be missing or not be an -// array--we yield that error in the loop, exactly once. In this state, error != SUCCESS and -// iter->depth == depth, and at_start == false. We decrement depth when we yield the error. -// - Missing Comma Error: When the iterator ++ method discovers there is no comma between elements, -// we flag that as an error and treat it exactly the same as a Chained Error. In this state, -// error == TAPE_ERROR, iter->depth == depth, and at_start == false. -// -// ## Terminal State -// -// The terminal state has iter->depth < depth. at_start is always false. -// -// - Finished: When we have reached a `]` or have reported an error, we are finished. We signal this -// by decrementing depth. In this state, iter->depth < depth, at_start == false, and -// error == SUCCESS. -// - -simdjson_inline array::array(const value_iterator &_iter) noexcept - : iter{_iter} -{ +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; } -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); +#if SIMDJSON_EXCEPTIONS +inline std::ostream& operator<<(std::ostream& out, simdjson_result &type) noexcept(false) { + return out << type.value(); } -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); +#endif + + + +simdjson_inline number_type number::get_number_type() const noexcept { + return type; } -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 bool number::is_uint64() const noexcept { + return get_number_type() == number_type::unsigned_integer; } -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 uint64_t number::get_uint64() const noexcept { + return payload.unsigned_integer; } -simdjson_inline simdjson_result array::end() noexcept { - return array_iterator(iter); + +simdjson_inline number::operator uint64_t() const noexcept { + return get_uint64(); } -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 bool number::is_int64() const noexcept { + return get_number_type() == number_type::signed_integer; } -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 int64_t number::get_int64() const noexcept { + return payload.signed_integer; } -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_inline number::operator int64_t() const noexcept { + return get_int64(); } -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 bool number::is_double() const noexcept { + return get_number_type() == number_type::floating_point_number; } -inline simdjson_result array::reset() & noexcept { - return iter.reset_array(); +simdjson_inline double number::get_double() const noexcept { + return payload.floating_point_number; } -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; } +simdjson_inline number::operator double() const noexcept { + return get_double(); +} - // 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; +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); +} - // 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" +simdjson_inline void number::append_s64(int64_t value) noexcept { + payload.signed_integer = value; + type = number_type::signed_integer; +} - // 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; - } +simdjson_inline void number::append_u64(uint64_t value) noexcept { + payload.unsigned_integer = value; + type = number_type::unsigned_integer; +} - // 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; +simdjson_inline void number::append_double(double value) noexcept { + payload.floating_point_number = value; + type = number_type::floating_point_number; } -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; +simdjson_inline void number::skip_double() noexcept { + type = number_type::floating_point_number; } } // namespace ondemand -} // namespace SIMDJSON_BUILTIN_IMPLEMENTATION +} // namespace westmere } // namespace simdjson namespace simdjson { -simdjson_inline simdjson_result::simdjson_result( - SIMDJSON_BUILTIN_IMPLEMENTATION::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::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::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::raw_json() noexcept { - if (error()) { return error(); } - return first.raw_json(); -} } // namespace simdjson -/* end file include/simdjson/generic/ondemand/array-inl.h */ -/* begin file include/simdjson/generic/ondemand/document-inl.h */ -namespace simdjson { -namespace SIMDJSON_BUILTIN_IMPLEMENTATION { -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(); -} +#endif // SIMDJSON_GENERIC_ONDEMAND_JSON_TYPE_INL_H +/* end file simdjson/generic/ondemand/json_type-inl.h for westmere */ +/* including simdjson/generic/ondemand/logger-inl.h for westmere: #include "simdjson/generic/ondemand/logger-inl.h" */ +/* begin file simdjson/generic/ondemand/logger-inl.h for westmere */ +#ifndef SIMDJSON_GENERIC_ONDEMAND_LOGGER_INL_H -inline simdjson_result document::current_location() const noexcept { - return iter.current_location(); -} +/* 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 */ -inline int32_t document::current_depth() const noexcept { - return iter.depth(); -} +#include +#include -inline bool document::at_end() const noexcept { - return iter.at_end(); -} +namespace simdjson { +namespace westmere { +namespace ondemand { +namespace logger { +static constexpr const char * DASHES = "----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"; +static constexpr const int LOG_EVENT_LEN = 20; +static constexpr const int LOG_BUFFER_LEN = 30; +static constexpr const int LOG_SMALL_BUFFER_LEN = 10; +static int log_depth = 0; // Not threadsafe. Log only. -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(); +// Helper to turn unprintable or newline characters into spaces +static inline char printable_char(char c) { + if (c >= 0x20) { + return c; } 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. - iter.assert_at_document_depth(); - 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; + return ' '; } } -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. - */ +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); +} -simdjson_inline simdjson_result document::get_uint64() noexcept { - return get_root_value_iterator().get_root_uint64(true); +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; } -simdjson_inline simdjson_result document::get_uint64_in_string() noexcept { - return get_root_value_iterator().get_root_uint64_in_string(true); + +static inline log_level log_threshold() +{ + static log_level threshold = get_log_level_from_env(); + return threshold; } -simdjson_inline simdjson_result document::get_int64() noexcept { - return get_root_value_iterator().get_root_int64(true); + +static inline bool should_log(log_level level) +{ + return level >= log_threshold(); } -simdjson_inline simdjson_result document::get_int64_in_string() noexcept { - return get_root_value_iterator().get_root_int64_in_string(true); + +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); } -simdjson_inline simdjson_result document::get_double() noexcept { - return get_root_value_iterator().get_root_double(true); + +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); } -simdjson_inline simdjson_result document::get_double_in_string() noexcept { - return get_root_value_iterator().get_root_double_in_string(true); +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); } -simdjson_inline simdjson_result document::get_string(bool allow_replacement) noexcept { - return get_root_value_iterator().get_root_string(true, allow_replacement); + +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++; } } -simdjson_inline simdjson_result document::get_wobbly_string() noexcept { - return get_root_value_iterator().get_root_wobbly_string(true); +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++; } } -simdjson_inline simdjson_result document::get_raw_json_string() noexcept { - return get_root_value_iterator().get_root_raw_json_string(true); + +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); } -simdjson_inline simdjson_result document::get_bool() noexcept { - return get_root_value_iterator().get_root_bool(true); + +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); } -simdjson_inline simdjson_result document::is_null() noexcept { - return get_root_value_iterator().is_root_null(true); +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); } -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_raw_json_string(); } -template<> simdjson_inline simdjson_result document::get() && noexcept { return get_string(false); } -template<> simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_double(); } -template<> simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_uint64(); } -template<> simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_int64(); } -template<> simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_bool(); } -template<> simdjson_inline simdjson_result document::get() && noexcept { return get_value(); } - -template simdjson_inline error_code document::get(T &out) & noexcept { - return get().get(out); +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); } -template simdjson_inline error_code document::get(T &out) && noexcept { - return std::forward(*this).get().get(out); + +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); } -#if SIMDJSON_EXCEPTIONS -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(); } +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); +} -#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; +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); } -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; + +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); } -simdjson_inline simdjson_result document::at(size_t index) & noexcept { - auto a = get_array(); - return a.at(index); + +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); + } + } } -simdjson_inline simdjson_result document::begin() & noexcept { - return get_array().begin(); + +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)...); } -simdjson_inline simdjson_result document::end() & noexcept { - return {}; + +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); + } + } } -simdjson_inline simdjson_result document::find_field(std::string_view key) & noexcept { - return start_or_resume_object().find_field(key); +} // namespace logger +} // namespace ondemand +} // namespace westmere +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_ONDEMAND_LOGGER_INL_H +/* end file simdjson/generic/ondemand/logger-inl.h for westmere */ +/* including simdjson/generic/ondemand/object-inl.h for westmere: #include "simdjson/generic/ondemand/object-inl.h" */ +/* begin file simdjson/generic/ondemand/object-inl.h for westmere */ +#ifndef SIMDJSON_GENERIC_ONDEMAND_OBJECT_INL_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_OBJECT_INL_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/field.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object_iterator.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value-inl.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { +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 document::find_field(const char *key) & noexcept { - return start_or_resume_object().find_field(key); +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 document::find_field_unordered(std::string_view key) & noexcept { - return start_or_resume_object().find_field_unordered(key); +simdjson_inline simdjson_result object::operator[](const std::string_view key) & noexcept { + return 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 object::operator[](const std::string_view key) && noexcept { + return std::forward(*this).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 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 document::operator[](const char *key) & noexcept { - return start_or_resume_object()[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 error_code document::consume() noexcept { - auto error = iter.skip_child(0); - if(error) { iter.abandon(); } - return error; +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 document::raw_json() noexcept { - auto _iter = get_root_value_iterator(); - const uint8_t * starting_point{_iter.peek_start()}; +simdjson_inline simdjson_result object::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.unsafe_pointer()}; + 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 document::type() noexcept { - return get_root_value_iterator().type(); +simdjson_inline simdjson_result object::started(value_iterator &iter) noexcept { + SIMDJSON_TRY( iter.started_object().error() ); + return object(iter); } -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 object object::resume(const value_iterator &iter) noexcept { + return iter; } -simdjson_inline bool document::is_negative() noexcept { - return get_root_value_iterator().is_root_negative(); +simdjson_inline object::object(const value_iterator &_iter) noexcept + : iter{_iter} +{ } -simdjson_inline simdjson_result document::is_integer() noexcept { - return get_root_value_iterator().is_root_integer(true); +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 document::get_number_type() noexcept { - return get_root_value_iterator().get_root_number_type(true); +simdjson_inline simdjson_result object::end() noexcept { + return object_iterator(iter); } -simdjson_inline simdjson_result document::get_number() noexcept { - return get_root_value_iterator().get_root_number(true); +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; } +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 document::raw_json_token() noexcept { - auto _iter = get_root_value_iterator(); - return std::string_view(reinterpret_cast(_iter.peek_start()), _iter.peek_start_length()); +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 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 object::reset() & noexcept { + return iter.reset_object(); } } // namespace ondemand -} // namespace SIMDJSON_BUILTIN_IMPLEMENTATION +} // namespace westmere } // namespace simdjson namespace simdjson { -simdjson_inline simdjson_result::simdjson_result( - SIMDJSON_BUILTIN_IMPLEMENTATION::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 { +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 { - return {}; -} -simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { +simdjson_inline simdjson_result simdjson_result::end() noexcept { if (error()) { return error(); } - return first.find_field_unordered(key); + return first.end(); } -simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char *key) & noexcept { +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::operator[](std::string_view key) & noexcept { +simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) && noexcept { if (error()) { return error(); } - return first[key]; + return std::forward(first).find_field_unordered(key); } -simdjson_inline simdjson_result simdjson_result::operator[](const char *key) & noexcept { +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::find_field(std::string_view key) & noexcept { +simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) && noexcept { if (error()) { return error(); } - return first.find_field(key); + return std::forward(first)[key]; } -simdjson_inline simdjson_result simdjson_result::find_field(const char *key) & noexcept { +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::get_array() & noexcept { - if (error()) { return error(); } - return first.get_array(); -} -simdjson_inline simdjson_result simdjson_result::get_object() & noexcept { +simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) && 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(); + return std::forward(first).find_field(key); } -simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { + +simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { if (error()) { return error(); } - return first.get_uint64_in_string(); + return first.at_pointer(json_pointer); } -simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { + +inline simdjson_result simdjson_result::reset() noexcept { if (error()) { return error(); } - return first.get_int64(); + return first.reset(); } -simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { + +inline simdjson_result simdjson_result::is_empty() noexcept { if (error()) { return error(); } - return first.get_int64_in_string(); + return first.is_empty(); } -simdjson_inline simdjson_result simdjson_result::get_double() noexcept { + +simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { if (error()) { return error(); } - return first.get_double(); + return first.count_fields(); } -simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { + +simdjson_inline simdjson_result simdjson_result::raw_json() noexcept { if (error()) { return error(); } - return first.get_double_in_string(); + return first.raw_json(); } -simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { - if (error()) { return error(); } - return first.get_string(allow_replacement); +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_ONDEMAND_OBJECT_INL_H +/* end file simdjson/generic/ondemand/object-inl.h for westmere */ +/* including simdjson/generic/ondemand/object_iterator-inl.h for westmere: #include "simdjson/generic/ondemand/object_iterator-inl.h" */ +/* begin file simdjson/generic/ondemand/object_iterator-inl.h for westmere */ +#ifndef SIMDJSON_GENERIC_ONDEMAND_OBJECT_ITERATOR_INL_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_OBJECT_ITERATOR_INL_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object_iterator.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/field-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator-inl.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { +namespace westmere { +namespace ondemand { + +// +// object_iterator +// + +simdjson_inline object_iterator::object_iterator(const value_iterator &_iter) noexcept + : iter{_iter} +{} + +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 simdjson_result simdjson_result::get_wobbly_string() noexcept { - if (error()) { return error(); } - return first.get_wobbly_string(); +simdjson_inline bool object_iterator::operator==(const object_iterator &other) const noexcept { + return !(*this != other); } -simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { - if (error()) { return error(); } - return first.get_raw_json_string(); +simdjson_inline bool object_iterator::operator!=(const object_iterator &) const noexcept { + return iter.is_open(); } -simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { - if (error()) { return error(); } - return first.get_bool(); + +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_inline simdjson_result simdjson_result::get_value() noexcept { - if (error()) { return error(); } - return first.get_value(); +SIMDJSON_POP_DISABLE_WARNINGS + +// +// ### Live States +// +// While iterating or looking up values, depth >= iter.depth. at_start may vary. Error is +// always SUCCESS: +// +// - Start: This is the state when the object is first found and the iterator is just past the {. +// In this state, at_start == true. +// - Next: After we hand a scalar value to the user, or an array/object which they then fully +// iterate over, the iterator is at the , or } before the next value. In this state, +// depth == iter.depth, at_start == false, and error == SUCCESS. +// - Unfinished Business: When we hand an array/object to the user which they do not fully +// iterate over, we need to finish that iteration by skipping child values until we reach the +// Next state. In this state, depth > iter.depth, at_start == false, and error == SUCCESS. +// +// ## Error States +// +// In error states, we will yield exactly one more value before stopping. iter.depth == depth +// and at_start is always false. We decrement after yielding the error, moving to the Finished +// state. +// +// - Chained Error: When the object iterator is part of an error chain--for example, in +// `for (auto tweet : doc["tweets"])`, where the tweet field may be missing or not be an +// object--we yield that error in the loop, exactly once. In this state, error != SUCCESS and +// iter.depth == depth, and at_start == false. We decrement depth when we yield the error. +// - Missing Comma Error: When the iterator ++ method discovers there is no comma between fields, +// we flag that as an error and treat it exactly the same as a Chained Error. In this state, +// error == TAPE_ERROR, iter.depth == depth, and at_start == false. +// +// Errors that occur while reading a field to give to the user (such as when the key is not a +// string or the field is missing a colon) are yielded immediately. Depth is then decremented, +// moving to the Finished state without transitioning through an Error state at all. +// +// ## Terminal State +// +// The terminal state has iter.depth < depth. at_start is always false. +// +// - Finished: When we have reached a }, we are finished. We signal this by decrementing depth. +// In this state, iter.depth < depth, at_start == false, and error == SUCCESS. +// + +} // namespace ondemand +} // namespace westmere +} // namespace simdjson + +namespace simdjson { + +simdjson_inline simdjson_result::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::is_null() noexcept { - if (error()) { return error(); } - return first.is_null(); +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : implementation_simdjson_result_base({}, error) +{ } -template -simdjson_inline simdjson_result simdjson_result::get() & noexcept { +simdjson_inline simdjson_result simdjson_result::operator*() noexcept { if (error()) { return error(); } - return first.get(); + return *first; } -template -simdjson_inline simdjson_result simdjson_result::get() && noexcept { - if (error()) { return error(); } - return std::forward(first).get(); +// 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; } -template -simdjson_inline error_code simdjson_result::get(T &out) & noexcept { - if (error()) { return error(); } - return first.get(out); +// 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; } -template -simdjson_inline error_code simdjson_result::get(T &out) && noexcept { - if (error()) { return error(); } - return std::forward(first).get(out); +// 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; } -template<> simdjson_inline simdjson_result simdjson_result::get() & noexcept = delete; -template<> simdjson_inline simdjson_result simdjson_result::get() && noexcept { - if (error()) { return error(); } - return std::forward(first); +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_ONDEMAND_OBJECT_ITERATOR_INL_H +/* end file simdjson/generic/ondemand/object_iterator-inl.h for westmere */ +/* including simdjson/generic/ondemand/parser-inl.h for westmere: #include "simdjson/generic/ondemand/parser-inl.h" */ +/* begin file simdjson/generic/ondemand/parser-inl.h for westmere */ +#ifndef SIMDJSON_GENERIC_ONDEMAND_PARSER_INL_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_PARSER_INL_H */ +/* amalgamation skipped (editor-only): #include "simdjson/padded_string.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/padded_string_view.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/implementation.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/internal/dom_parser_implementation.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/dom/base.h" // for MINIMAL_DOCUMENT_CAPACITY */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document_stream.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/parser.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { +namespace westmere { +namespace ondemand { + +simdjson_inline parser::parser(size_t max_capacity) noexcept + : _max_capacity{max_capacity} { } -template<> simdjson_inline error_code simdjson_result::get(SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::document &out) & noexcept = delete; -template<> simdjson_inline error_code simdjson_result::get(SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::document &out) && noexcept { - if (error()) { return error(); } - out = std::forward(first); + +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; } -simdjson_inline simdjson_result simdjson_result::type() noexcept { - if (error()) { return error(); } - return first.type(); +simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(padded_string_view json) & noexcept { + if (json.padding() < SIMDJSON_PADDING) { return INSUFFICIENT_PADDING; } + + // 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 }); } -simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { - if (error()) { return error(); } - return first.is_scalar(); +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_inline bool simdjson_result::is_negative() noexcept { - if (error()) { return error(); } - return first.is_negative(); +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_inline simdjson_result simdjson_result::is_integer() noexcept { - if (error()) { return error(); } - return first.is_integer(); +simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(const std::string &json) & noexcept { + return iterate(padded_string_view(json)); } -simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { - if (error()) { return error(); } - return first.get_number_type(); +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_inline simdjson_result simdjson_result::get_number() noexcept { - if (error()) { return error(); } - return first.get_number(); +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; } -#if SIMDJSON_EXCEPTIONS -simdjson_inline simdjson_result::operator SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::array() & noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; + // 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); } -simdjson_inline simdjson_result::operator SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::object() & noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; + +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(allow_comma_separated && batch_size < len) { batch_size = len; } + return document_stream(*this, buf, len, batch_size, allow_comma_separated); } -simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; +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); } -simdjson_inline simdjson_result::operator int64_t() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; +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); } -simdjson_inline simdjson_result::operator double() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; +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_inline simdjson_result::operator std::string_view() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; + +simdjson_inline size_t parser::capacity() const noexcept { + return _capacity; } -simdjson_inline simdjson_result::operator SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::raw_json_string() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; +simdjson_inline size_t parser::max_capacity() const noexcept { + return _max_capacity; } -simdjson_inline simdjson_result::operator bool() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; +simdjson_inline size_t parser::max_depth() const noexcept { + return _max_depth; } -simdjson_inline simdjson_result::operator SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::value() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; + +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; + } } -#endif +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_result simdjson_result::current_location() noexcept { - if (error()) { return error(); } - return first.current_location(); +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 bool simdjson_result::at_end() const noexcept { - if (error()) { return error(); } - return first.at_end(); +} // 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) {} + +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_ONDEMAND_PARSER_INL_H +/* end file simdjson/generic/ondemand/parser-inl.h for westmere */ +/* including simdjson/generic/ondemand/raw_json_string-inl.h for westmere: #include "simdjson/generic/ondemand/raw_json_string-inl.h" */ +/* begin file simdjson/generic/ondemand/raw_json_string-inl.h for westmere */ +#ifndef SIMDJSON_GENERIC_ONDEMAND_RAW_JSON_STRING_INL_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_RAW_JSON_STRING_INL_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base-inl.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { + +namespace westmere { +namespace ondemand { + +simdjson_inline raw_json_string::raw_json_string(const uint8_t * _buf) noexcept : buf{_buf} {} + +simdjson_inline const char * raw_json_string::raw() const noexcept { return reinterpret_cast(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 int32_t simdjson_result::current_depth() const noexcept { - if (error()) { return error(); } - return first.current_depth(); +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 simdjson_result simdjson_result::raw_json_token() noexcept { - if (error()) { return error(); } - return first.raw_json_token(); +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 simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { - if (error()) { return error(); } - return first.at_pointer(json_pointer); +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; } -} // namespace simdjson +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; +} -namespace simdjson { -namespace SIMDJSON_BUILTIN_IMPLEMENTATION { -namespace ondemand { +simdjson_unused simdjson_inline bool operator==(const raw_json_string &a, std::string_view c) noexcept { + return a.unsafe_is_equal(c); +} -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); } -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); } +simdjson_unused simdjson_inline bool operator==(std::string_view c, const raw_json_string &a) noexcept { + return a == c; +} -#if SIMDJSON_EXCEPTIONS -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 raw_json_string(*doc); } -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::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::raw_json() noexcept { return doc->raw_json();} -simdjson_inline document_reference::operator document&() const noexcept { return *doc; } +simdjson_unused simdjson_inline bool operator!=(const raw_json_string &a, std::string_view c) noexcept { + return !(a == c); +} -} // namespace ondemand -} // namespace SIMDJSON_BUILTIN_IMPLEMENTATION -} // namespace simdjson +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 simdjson namespace simdjson { -simdjson_inline simdjson_result::simdjson_result(SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::document_reference value, error_code error) - noexcept : implementation_simdjson_result_base(std::forward(value), 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::count_elements() & noexcept { +simdjson_inline simdjson_result simdjson_result::raw() const noexcept { if (error()) { return error(); } - return first.count_elements(); + return first.raw(); } -simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { +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.count_fields(); + return first.unescape(iter, allow_replacement); } -simdjson_inline simdjson_result simdjson_result::at(size_t index) & noexcept { +simdjson_inline simdjson_warn_unused simdjson_result simdjson_result::unescape_wobbly(westmere::ondemand::json_iterator &iter) const noexcept { if (error()) { return error(); } - return first.at(index); + return first.unescape_wobbly(iter); } -simdjson_inline error_code simdjson_result::rewind() noexcept { - if (error()) { return error(); } - first.rewind(); - return SUCCESS; +} // 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)); } -simdjson_inline simdjson_result simdjson_result::begin() & noexcept { - if (error()) { return error(); } - return first.begin(); + + +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); } -simdjson_inline simdjson_result simdjson_result::end() & noexcept { - return {}; + +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); } -simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { - if (error()) { return error(); } - return first.find_field_unordered(key); + +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()); + } } -simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char *key) & noexcept { - if (error()) { return error(); } - return first.find_field_unordered(key); + +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()); } -simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { - if (error()) { return error(); } - return first[key]; + +inline simdjson_result to_json_string(simdjson_result x) { + if (x.error()) { return x.error(); } + return to_json_string(x.value_unsafe()); } -simdjson_inline simdjson_result simdjson_result::operator[](const char *key) & noexcept { - if (error()) { return error(); } - return first[key]; + +inline simdjson_result to_json_string(simdjson_result x) { + if (x.error()) { return x.error(); } + return to_json_string(x.value_unsafe()); } -simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { - if (error()) { return error(); } - return first.find_field(key); + +inline simdjson_result to_json_string(simdjson_result x) { + if (x.error()) { return x.error(); } + return to_json_string(x.value_unsafe()); } -simdjson_inline simdjson_result simdjson_result::find_field(const char *key) & noexcept { - if (error()) { return error(); } - return first.find_field(key); +} // 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); + } } -simdjson_inline simdjson_result simdjson_result::get_array() & noexcept { - if (error()) { return error(); } - return first.get_array(); +inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result x) { + if (x.error()) { throw simdjson::simdjson_error(x.error()); } + return (out << x.value()); } -simdjson_inline simdjson_result simdjson_result::get_object() & noexcept { - if (error()) { return error(); } - return first.get_object(); +#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); + } } -simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { - if (error()) { return error(); } - return first.get_uint64(); +#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); + } } -simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { - if (error()) { return error(); } - return first.get_uint64_in_string(); +inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result x) { + if (x.error()) { throw simdjson::simdjson_error(x.error()); } + return (out << x.value()); } -simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { - if (error()) { return error(); } - return first.get_int64(); +#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); + } } -simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { - if (error()) { return error(); } - return first.get_int64_in_string(); +#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); + } } -simdjson_inline simdjson_result simdjson_result::get_double() noexcept { - if (error()) { return error(); } - return first.get_double(); +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); + } } -simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { - if (error()) { return error(); } - return first.get_double_in_string(); +inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result&& x) { + if (x.error()) { throw simdjson::simdjson_error(x.error()); } + return (out << x.value()); } -simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { - if (error()) { return error(); } - return first.get_string(allow_replacement); +inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result&& x) { + if (x.error()) { throw simdjson::simdjson_error(x.error()); } + return (out << x.value()); } -simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { - if (error()) { return error(); } - return first.get_wobbly_string(); +#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); + } } -simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { - if (error()) { return error(); } - return first.get_raw_json_string(); +#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); + } } -simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { - if (error()) { return error(); } - return first.get_bool(); +inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result x) { + if (x.error()) { throw simdjson::simdjson_error(x.error()); } + return (out << x.value()); } -simdjson_inline simdjson_result simdjson_result::get_value() noexcept { - if (error()) { return error(); } - return first.get_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); + } } -simdjson_inline simdjson_result simdjson_result::is_null() noexcept { - if (error()) { return error(); } - return first.is_null(); +#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 + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_TOKEN_ITERATOR_INL_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/token_iterator.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base-inl.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { +namespace westmere { +namespace ondemand { + +simdjson_inline token_iterator::token_iterator( + const uint8_t *_buf, + token_position position +) noexcept : buf{_buf}, _position{position} +{ } -simdjson_inline simdjson_result simdjson_result::type() noexcept { - if (error()) { return error(); } - return first.type(); + +simdjson_inline uint32_t token_iterator::current_offset() const noexcept { + return *(_position); } -simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { - if (error()) { return error(); } - return first.is_scalar(); + + +simdjson_inline const uint8_t *token_iterator::return_current_and_advance() noexcept { + return &buf[*(_position++)]; } -simdjson_inline simdjson_result simdjson_result::is_negative() noexcept { - if (error()) { return error(); } - return first.is_negative(); + +simdjson_inline const uint8_t *token_iterator::peek(token_position position) const noexcept { + return &buf[*position]; } -simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { - if (error()) { return error(); } - return first.is_integer(); +simdjson_inline uint32_t token_iterator::peek_index(token_position position) const noexcept { + return *position; } -simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { - if (error()) { return error(); } - return first.get_number_type(); +simdjson_inline uint32_t token_iterator::peek_length(token_position position) const noexcept { + return *(position+1) - *position; } -simdjson_inline simdjson_result simdjson_result::get_number() noexcept { - if (error()) { return error(); } - return first.get_number(); + +simdjson_inline const uint8_t *token_iterator::peek(int32_t delta) const noexcept { + return &buf[*(_position+delta)]; } -#if SIMDJSON_EXCEPTIONS -simdjson_inline simdjson_result::operator SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::array() & noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; +simdjson_inline uint32_t token_iterator::peek_index(int32_t delta) const noexcept { + return *(_position+delta); } -simdjson_inline simdjson_result::operator SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::object() & noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; +simdjson_inline uint32_t token_iterator::peek_length(int32_t delta) const noexcept { + return *(_position+delta+1) - *(_position+delta); } -simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; + +simdjson_inline token_position token_iterator::position() const noexcept { + return _position; } -simdjson_inline simdjson_result::operator int64_t() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; +simdjson_inline void token_iterator::set_position(token_position target_position) noexcept { + _position = target_position; } -simdjson_inline simdjson_result::operator double() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; + +simdjson_inline bool token_iterator::operator==(const token_iterator &other) const noexcept { + return _position == other._position; } -simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; +simdjson_inline bool token_iterator::operator!=(const token_iterator &other) const noexcept { + return _position != other._position; } -simdjson_inline simdjson_result::operator SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::raw_json_string() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; +simdjson_inline bool token_iterator::operator>(const token_iterator &other) const noexcept { + return _position > other._position; } -simdjson_inline simdjson_result::operator bool() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; +simdjson_inline bool token_iterator::operator>=(const token_iterator &other) const noexcept { + return _position >= other._position; } -simdjson_inline simdjson_result::operator SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::value() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; +simdjson_inline bool token_iterator::operator<(const token_iterator &other) const noexcept { + return _position < other._position; } -#endif - -simdjson_inline simdjson_result simdjson_result::current_location() noexcept { - if (error()) { return error(); } - return first.current_location(); +simdjson_inline bool token_iterator::operator<=(const token_iterator &other) const noexcept { + return _position <= other._position; } -simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { - if (error()) { return error(); } - return first.raw_json_token(); -} +} // namespace ondemand +} // namespace westmere +} // namespace simdjson -simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { - if (error()) { return error(); } - return first.at_pointer(json_pointer); -} +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) {} } // namespace simdjson -/* end file include/simdjson/generic/ondemand/document-inl.h */ -/* begin file include/simdjson/generic/ondemand/value-inl.h */ + +#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 SIMDJSON_BUILTIN_IMPLEMENTATION { +namespace westmere { namespace ondemand { simdjson_inline value::value(const value_iterator &_iter) noexcept @@ -30733,1356 +86228,1335 @@ simdjson_inline simdjson_result value::at_pointer(std::string_view json_p } } // namespace ondemand -} // namespace SIMDJSON_BUILTIN_IMPLEMENTATION +} // namespace westmere } // namespace simdjson namespace simdjson { -simdjson_inline simdjson_result::simdjson_result( - SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::value &&value +simdjson_inline simdjson_result::simdjson_result( + westmere::ondemand::value &&value ) noexcept : - implementation_simdjson_result_base( - std::forward(value) + implementation_simdjson_result_base( + std::forward(value) ) { } -simdjson_inline simdjson_result::simdjson_result( +simdjson_inline simdjson_result::simdjson_result( error_code error ) noexcept : - implementation_simdjson_result_base(error) + implementation_simdjson_result_base(error) { } -simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { +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 { +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 { +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 { +simdjson_inline simdjson_result simdjson_result::begin() & noexcept { if (error()) { return error(); } return first.begin(); } -simdjson_inline simdjson_result simdjson_result::end() & noexcept { +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 { +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 { +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 { +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 { +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 { +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 { +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 { +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 { +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 { +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 { +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 { +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 { +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 { +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 { +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 { +simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { if (error()) { return error(); } return first.get_string(allow_replacement); } -simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { +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 { +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 { +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 { +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 { +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 { +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 { +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(SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::value &out) noexcept { +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 { +simdjson_inline simdjson_result simdjson_result::type() noexcept { if (error()) { return error(); } return first.type(); } -simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { +simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { if (error()) { return error(); } return first.is_scalar(); } -simdjson_inline simdjson_result simdjson_result::is_negative() noexcept { +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 { +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 { +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 { +simdjson_inline simdjson_result simdjson_result::get_number() noexcept { if (error()) { return error(); } return first.get_number(); } #if SIMDJSON_EXCEPTIONS -simdjson_inline simdjson_result::operator SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::array() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -simdjson_inline simdjson_result::operator SIMDJSON_BUILTIN_IMPLEMENTATION::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 SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::raw_json_string() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -simdjson_inline simdjson_result::operator bool() noexcept(false) { +simdjson_inline simdjson_result::operator westmere::ondemand::array() 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::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); -} - -} // namespace simdjson -/* end file include/simdjson/generic/ondemand/value-inl.h */ -/* begin file include/simdjson/generic/ondemand/field-inl.h */ -namespace simdjson { -namespace SIMDJSON_BUILTIN_IMPLEMENTATION { -namespace ondemand { - -// clang 6 doesn't 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; -} - -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 value &field::value() & noexcept { - return second; -} - -simdjson_inline value field::value() && noexcept { - return std::forward(*this).second; -} - -} // namespace ondemand -} // namespace SIMDJSON_BUILTIN_IMPLEMENTATION -} // namespace simdjson - -namespace simdjson { - -simdjson_inline simdjson_result::simdjson_result( - SIMDJSON_BUILTIN_IMPLEMENTATION::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::unescaped_key(bool allow_replacement) noexcept { - if (error()) { return error(); } - return first.unescaped_key(allow_replacement); -} -simdjson_inline simdjson_result simdjson_result::value() noexcept { - if (error()) { return error(); } - return std::move(first.value()); -} - -} // namespace simdjson -/* end file include/simdjson/generic/ondemand/field-inl.h */ -/* begin file include/simdjson/generic/ondemand/object-inl.h */ -namespace simdjson { -namespace SIMDJSON_BUILTIN_IMPLEMENTATION { -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) { 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) { 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) { 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) { 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(0)}; - 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; -} - -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 SIMDJSON_BUILTIN_IMPLEMENTATION -} // namespace simdjson - -namespace simdjson { - -simdjson_inline simdjson_result::simdjson_result(SIMDJSON_BUILTIN_IMPLEMENTATION::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(); + return first; } -simdjson_inline simdjson_result simdjson_result::end() noexcept { - if (error()) { return error(); } - return first.end(); +simdjson_inline simdjson_result::operator westmere::ondemand::object() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; } -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::operator uint64_t() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; } -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::operator int64_t() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; } -simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { - if (error()) { return error(); } - return first[key]; +simdjson_inline simdjson_result::operator double() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; } -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::operator std::string_view() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; } -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::operator westmere::ondemand::raw_json_string() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; } -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::operator bool() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; } +#endif -simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { +simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { if (error()) { return error(); } - return first.at_pointer(json_pointer); + return first.raw_json_token(); } -inline simdjson_result simdjson_result::reset() noexcept { +simdjson_inline simdjson_result simdjson_result::current_location() noexcept { if (error()) { return error(); } - return first.reset(); + return first.current_location(); } -inline simdjson_result simdjson_result::is_empty() noexcept { +simdjson_inline simdjson_result simdjson_result::current_depth() const noexcept { if (error()) { return error(); } - return first.is_empty(); + return first.current_depth(); } -simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { +simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { if (error()) { return error(); } - return first.count_fields(); + return first.at_pointer(json_pointer); } -simdjson_inline simdjson_result simdjson_result::raw_json() noexcept { - if (error()) { return error(); } - return first.raw_json(); -} } // namespace simdjson -/* end file include/simdjson/generic/ondemand/object-inl.h */ -/* begin file include/simdjson/generic/ondemand/parser-inl.h */ + +#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 + +/* 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/json_type-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + namespace simdjson { -namespace SIMDJSON_BUILTIN_IMPLEMENTATION { +namespace westmere { namespace ondemand { -simdjson_inline parser::parser(size_t max_capacity) noexcept - : _max_capacity{max_capacity} { +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 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 simdjson_result value_iterator::start_object() noexcept { + SIMDJSON_TRY( start_container('{', "Not an object", "object") ); + return started_object(); +} - // 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]); +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 - start_positions.reset(new (std::nothrow) token_position[new_max_depth]); + _json_iter->set_start_position(_depth, start_position()); #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) ); + 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"); + } } - _capacity = new_capacity; - _max_depth = new_max_depth; return SUCCESS; } -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 value_iterator::started_root_object() noexcept { + auto error = check_root_object(); + if(error) { return error; } + return started_object(); +} - // Allocate if needed - if (capacity() < json.length() || !string_buf) { - SIMDJSON_TRY( allocate(json.length(), max_depth()) ); +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"); } +} - // Run stage 1. - SIMDJSON_TRY( implementation->stage1(reinterpret_cast(json.data()), json.length(), stage1_mode::regular) ); - return document::start({ reinterpret_cast(json.data()), this }); +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 isn't 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_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_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 isn't 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 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 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 simdjson_result parser::iterate(std::string_view json, size_t allocated) & noexcept { - return iterate(padded_string_view(json, allocated)); +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 parser::iterate(const std::string &json) & noexcept { - return iterate(padded_string_view(json)); +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 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 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 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 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 parser::iterate_raw(padded_string_view json) & noexcept { - if (json.padding() < SIMDJSON_PADDING) { return INSUFFICIENT_PADDING; } +simdjson_warn_unused simdjson_inline simdjson_result value_iterator::has_next_element() noexcept { + assert_at_next(); - // Allocate if needed - if (capacity() < json.length()) { - SIMDJSON_TRY( allocate(json.length(), max_depth()) ); + 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"); } - - // 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(allow_comma_separated && batch_size < len) { batch_size = len; } - return document_stream(*this, buf, len, batch_size, allow_comma_separated); +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); } -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); +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; } -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); + +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); } -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_warn_unused simdjson_inline simdjson_result value_iterator::get_wobbly_string() noexcept { + return get_raw_json_string().unescape_wobbly(json_iter()); } - -simdjson_inline size_t parser::capacity() const noexcept { - return _capacity; +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_inline size_t parser::max_capacity() const noexcept { - return _max_capacity; +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_inline size_t parser::max_depth() const noexcept { - return _max_depth; +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_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_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_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; +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_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; +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; } - -} // namespace ondemand -} // namespace SIMDJSON_BUILTIN_IMPLEMENTATION -} // namespace simdjson - -namespace simdjson { - -simdjson_inline simdjson_result::simdjson_result(SIMDJSON_BUILTIN_IMPLEMENTATION::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 -/* end file include/simdjson/generic/ondemand/parser-inl.h */ -/* begin file include/simdjson/generic/ondemand/document_stream-inl.h */ -#include -#include -#include -namespace simdjson { -namespace SIMDJSON_BUILTIN_IMPLEMENTATION { -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;}); +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; } - -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(); +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; } - -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(); - } - } - ); +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; } - - -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(); - } +simdjson_inline bool value_iterator::is_negative() noexcept { + return numberparsing::is_negative(peek_non_root_scalar("numbersign")); } - -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(); +simdjson_inline bool value_iterator::is_root_negative() noexcept { + return numberparsing::is_negative(peek_root_scalar("numbersign")); } - -#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 simdjson_result value_iterator::is_integer() noexcept { + return numberparsing::is_integer(peek_non_root_scalar("integer")); } - -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 simdjson_result value_iterator::get_number_type() noexcept { + return numberparsing::get_number_type(peek_non_root_scalar("integer")); } - -simdjson_inline document_stream::~document_stream() noexcept -{ - #ifdef SIMDJSON_THREADS_ENABLED - worker.reset(); - #endif +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; } -inline size_t document_stream::size_in_bytes() const noexcept { - return len; +simdjson_inline simdjson_result value_iterator::is_root_integer(bool check_trailing) noexcept { + auto max_len = peek_start_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; } -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 simdjson_result value_iterator::get_root_number_type(bool check_trailing) noexcept { + auto max_len = peek_start_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)) { + logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters"); + 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 document_stream::iterator::iterator() noexcept - : stream{nullptr}, finished{true} { +simdjson_inline simdjson_result value_iterator::get_root_number(bool check_trailing) noexcept { + auto max_len = peek_start_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)) { + logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters"); + 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_inline document_stream::iterator::iterator(document_stream* _stream, bool is_end) noexcept - : stream{_stream}, finished{is_end} { +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); } - -simdjson_inline simdjson_result document_stream::iterator::operator*() noexcept { - //if(stream->error) { return stream->error; } - return simdjson_result(stream->doc, stream->error); +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_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_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_inline bool document_stream::iterator::operator!=(const document_stream::iterator &other) const noexcept { - return finished != other.finished; +simdjson_warn_unused simdjson_inline simdjson_result value_iterator::get_root_uint64(bool check_trailing) noexcept { + auto max_len = peek_start_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_inline document_stream::iterator document_stream::begin() noexcept { - start(); - // If there are no documents, we're finished. - return iterator(this, error == EMPTY); +simdjson_warn_unused simdjson_inline simdjson_result value_iterator::get_root_uint64_in_string(bool check_trailing) noexcept { + auto max_len = peek_start_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_start_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 document_stream::iterator document_stream::end() noexcept { - return iterator(this, true); + 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; } - -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); +simdjson_warn_unused simdjson_inline simdjson_result value_iterator::get_root_int64_in_string(bool check_trailing) noexcept { + auto max_len = peek_start_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; } - 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; } + 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"); } - #endif // SIMDJSON_THREADS_ENABLED + return result; } - -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; - } +simdjson_warn_unused simdjson_inline simdjson_result value_iterator::get_root_double(bool check_trailing) noexcept { + auto max_len = peek_start_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; } -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(); +simdjson_warn_unused simdjson_inline simdjson_result value_iterator::get_root_double_in_string(bool check_trailing) noexcept { + auto max_len = peek_start_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; } - -inline size_t document_stream::next_batch_start() const noexcept { - return batch_start + parser->implementation->structural_indexes[parser->implementation->n_structural_indexes]; +simdjson_warn_unused simdjson_inline simdjson_result value_iterator::get_root_bool(bool check_trailing) noexcept { + auto max_len = peek_start_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; } - -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 simdjson_result value_iterator::is_root_null(bool check_trailing) noexcept { + auto max_len = peek_start_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_inline size_t document_stream::iterator::current_index() const noexcept { - return stream->doc_index; +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 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(); +simdjson_inline value_iterator value_iterator::child() const noexcept { + assert_at_child(); + return { _json_iter, depth()+1, _json_iter->token.position() }; +} - // 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: Remove any trailing whitespaces - // This returns a string spanning from start of value to the beginning of the next document (excluded) - return std::string_view(reinterpret_cast(stream->buf) + current_index(), stream->parser->implementation->structural_indexes[++cur_struct_index] - current_index() - 1); - } - cur_struct_index++; - } +// 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 - 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 bool value_iterator::at_end() const noexcept { + return _json_iter->at_end(); +} - 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 bool value_iterator::at_start() const noexcept { + return _json_iter->token.position() == start_position(); } -inline error_code document_stream::iterator::error() const noexcept { - return stream->error; +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; } -#ifdef SIMDJSON_THREADS_ENABLED +simdjson_inline void value_iterator::abandon() noexcept { + _json_iter->abandon(); +} -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; } +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; +} - // If there's anything left, start the stage 1 thread! - if (next_batch_start() < len) { - start_stage1_thread(); - } +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()); } -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(); +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(); } - worker->run(this, & this->stage1_thread_parser, _next_batch_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(); } -#endif // SIMDJSON_THREADS_ENABLED +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; } -} // namespace ondemand -} // namespace SIMDJSON_BUILTIN_IMPLEMENTATION -} // namespace simdjson + // 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); +} -namespace simdjson { +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(); + } -simdjson_inline simdjson_result::simdjson_result( - error_code error -) noexcept : - implementation_simdjson_result_base(error) -{ -} -simdjson_inline simdjson_result::simdjson_result( - SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::document_stream &&value -) noexcept : - implementation_simdjson_result_base( - std::forward(value) - ) -{ -} + return SUCCESS; } -/* end file include/simdjson/generic/ondemand/document_stream-inl.h */ -/* begin file include/simdjson/generic/ondemand/serialization-inl.h */ -namespace simdjson { +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(); } -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)); + 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(); +} -inline simdjson_result to_json_string(SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::document& x) noexcept { - std::string_view v; - auto error = x.raw_json().get(v); - if(error) {return error; } - return trim(v); +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; } -inline simdjson_result to_json_string(SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::document_reference& x) noexcept { - std::string_view v; - auto error = x.raw_json().get(v); - if(error) {return error; } - return trim(v); + assert_at_non_root_start(); + _json_iter->return_current_and_advance(); + _json_iter->ascend_to(depth()-1); } -inline simdjson_result to_json_string(SIMDJSON_BUILTIN_IMPLEMENTATION::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 SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand; - SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::json_type t; - auto error = x.type().get(t); - if(error != SUCCESS) { return error; } - switch (t) - { - case json_type::array: - { - SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::array array; - error = x.get_array().get(array); - if(error) { return error; } - return to_json_string(array); - } - case json_type::object: - { - SIMDJSON_BUILTIN_IMPLEMENTATION::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()); - } +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; } -inline simdjson_result to_json_string(SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::object& x) noexcept { - std::string_view v; - auto error = x.raw_json().get(v); - if(error) {return error; } - return trim(v); +simdjson_inline bool value_iterator::is_at_start() const noexcept { + return position() == start_position(); } -inline simdjson_result to_json_string(SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::array& x) noexcept { - std::string_view v; - auto error = x.raw_json().get(v); - if(error) {return error; } - return trim(v); +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() == '"'; } -inline simdjson_result to_json_string(simdjson_result x) { - if (x.error()) { return x.error(); } - return to_json_string(x.value_unsafe()); +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 simdjson_result to_json_string(simdjson_result x) { - if (x.error()) { return x.error(); } - return to_json_string(x.value_unsafe()); +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 simdjson_result to_json_string(simdjson_result x) { - if (x.error()) { return x.error(); } - return to_json_string(x.value_unsafe()); +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 simdjson_result to_json_string(simdjson_result x) { - if (x.error()) { return x.error(); } - return to_json_string(x.value_unsafe()); +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 simdjson_result to_json_string(simdjson_result x) { - if (x.error()) { return x.error(); } - return to_json_string(x.value_unsafe()); +simdjson_inline void value_iterator::move_at_start() noexcept { + _json_iter->_depth = _depth; + _json_iter->token.set_position(_start_position); } -} // namespace simdjson -namespace simdjson { namespace SIMDJSON_BUILTIN_IMPLEMENTATION { namespace ondemand { +simdjson_inline void value_iterator::move_at_container_start() noexcept { + _json_iter->_depth = _depth; + _json_iter->token.set_position(_start_position + 1); +} -#if SIMDJSON_EXCEPTIONS -inline std::ostream& operator<<(std::ostream& out, simdjson::SIMDJSON_BUILTIN_IMPLEMENTATION::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); - } +simdjson_inline simdjson_result value_iterator::reset_array() noexcept { + if(error()) { return error(); } + move_at_container_start(); + return started_array(); } -inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result x) { - if (x.error()) { throw simdjson::simdjson_error(x.error()); } - return (out << x.value()); + +simdjson_inline simdjson_result value_iterator::reset_object() noexcept { + if(error()) { return error(); } + move_at_container_start(); + return started_object(); } -#else -inline std::ostream& operator<<(std::ostream& out, simdjson::SIMDJSON_BUILTIN_IMPLEMENTATION::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); - } + +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 ); } -#endif -#if SIMDJSON_EXCEPTIONS -inline std::ostream& operator<<(std::ostream& out, simdjson::SIMDJSON_BUILTIN_IMPLEMENTATION::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 void value_iterator::assert_at_root() const noexcept { + assert_at_start(); + SIMDJSON_ASSUME( _depth == 1 ); } -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 void value_iterator::assert_at_non_root_start() const noexcept { + assert_at_start(); + SIMDJSON_ASSUME( _depth > 1 ); } -#else -inline std::ostream& operator<<(std::ostream& out, simdjson::SIMDJSON_BUILTIN_IMPLEMENTATION::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); - } + +inline void value_iterator::assert_is_valid() const noexcept { + SIMDJSON_ASSUME( _json_iter != nullptr ); } -#endif -#if SIMDJSON_EXCEPTIONS -inline std::ostream& operator<<(std::ostream& out, simdjson::SIMDJSON_BUILTIN_IMPLEMENTATION::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); - } +simdjson_inline bool value_iterator::is_valid() const noexcept { + return _json_iter != nullptr; } -inline std::ostream& operator<<(std::ostream& out, simdjson::SIMDJSON_BUILTIN_IMPLEMENTATION::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); + +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; } } -inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result&& x) { - if (x.error()) { throw simdjson::simdjson_error(x.error()); } - return (out << x.value()); + +simdjson_inline token_position value_iterator::start_position() const noexcept { + return _start_position; } -inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result&& x) { - if (x.error()) { throw simdjson::simdjson_error(x.error()); } - return (out << x.value()); + +simdjson_inline token_position value_iterator::position() const noexcept { + return _json_iter->position(); } -#else -inline std::ostream& operator<<(std::ostream& out, simdjson::SIMDJSON_BUILTIN_IMPLEMENTATION::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); - } + +simdjson_inline token_position value_iterator::end_position() const noexcept { + return _json_iter->end_position(); } -#endif -#if SIMDJSON_EXCEPTIONS -inline std::ostream& operator<<(std::ostream& out, simdjson::SIMDJSON_BUILTIN_IMPLEMENTATION::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); - } +simdjson_inline token_position value_iterator::last_position() const noexcept { + return _json_iter->last_position(); } -inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result x) { - if (x.error()) { throw simdjson::simdjson_error(x.error()); } - return (out << x.value()); + +simdjson_inline error_code value_iterator::report_error(error_code error, const char *message) noexcept { + return _json_iter->report_error(error, message); } + +} // namespace ondemand +} // namespace westmere +} // namespace simdjson + +namespace simdjson { + +simdjson_inline simdjson_result::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 */ +/* end file simdjson/generic/ondemand/amalgamated.h for westmere */ +/* including simdjson/westmere/end.h: #include "simdjson/westmere/end.h" */ +/* begin file simdjson/westmere/end.h */ +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #include "simdjson/westmere/base.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +#if !SIMDJSON_CAN_ALWAYS_RUN_WESTMERE +SIMDJSON_UNTARGET_REGION +#endif + +/* undefining SIMDJSON_IMPLEMENTATION from "westmere" */ +#undef SIMDJSON_IMPLEMENTATION +/* end file simdjson/westmere/end.h */ + +#endif // SIMDJSON_WESTMERE_IMPLEMENTATION_H +/* end file simdjson/westmere/ondemand.h */ #else -inline std::ostream& operator<<(std::ostream& out, simdjson::SIMDJSON_BUILTIN_IMPLEMENTATION::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); - } -} +#error Unknown SIMDJSON_BUILTIN_IMPLEMENTATION #endif -}}} // namespace simdjson::SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand -/* end file include/simdjson/generic/ondemand/serialization-inl.h */ -/* end file include/simdjson/generic/ondemand-inl.h */ +/* undefining SIMDJSON_CONDITIONAL_INCLUDE */ +#undef SIMDJSON_CONDITIONAL_INCLUDE namespace simdjson { - /** - * 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; /** * @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 { /** - * 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(). + * @copydoc simdjson::builtin::ondemand */ - const implementation * builtin_implementation(); + namespace ondemand = builtin::ondemand; } // namespace simdjson -#endif // SIMDJSON_BUILTIN_H -/* end file include/simdjson/builtin.h */ +#endif // SIMDJSON_ONDEMAND_H +/* end file simdjson/ondemand.h */ #endif // SIMDJSON_H -/* end file include/simdjson.h */ +/* end file simdjson.h */ diff --git a/hybridse/src/codegen/udf_ir_builder_test.cc b/hybridse/src/codegen/udf_ir_builder_test.cc index bd70b4ab2ab..13a82a1a925 100644 --- a/hybridse/src/codegen/udf_ir_builder_test.cc +++ b/hybridse/src/codegen/udf_ir_builder_test.cc @@ -1463,6 +1463,9 @@ TEST_F(UdfIRBuilderTest, AddMonths) { CheckUdf, Date, int64_t>("add_months", Date(2013, 3, 31), Date(2012, 1, 31), 14); } +// ========================================================================= // +// JSON functions +// ========================================================================= // TEST_F(UdfIRBuilderTest, JsonArrayLength) { CheckUdf, Nullable>("json_array_length", 0, "[]"); CheckUdf, Nullable>("json_array_length", 3, "[1,2,3]"); @@ -1473,6 +1476,73 @@ TEST_F(UdfIRBuilderTest, JsonArrayLength) { CheckUdf, Nullable>("json_array_length", nullptr, nullptr); } +TEST_F(UdfIRBuilderTest, GetJsonObject) { + std::string_view json = R"( + { + "foo": ["bar", "baz"], + "": 0, + "a/b": 1, + "c%d": 2, + "e^f": 3, + "g|h": 4, + "i\\j": 5, + "k\"l": 6, + " ": 7, + "m~n": 8, + "o\p": 9, + })"; + std::initializer_list>> cases = { + // empty json path evaluate to whole document + {"[]", "[]", ""}, + {R"({"k": "val"})", R"({"k": "val"})", ""}, + + {absl::StripAsciiWhitespace(json), json, ""}, + {R"(["bar", "baz"])", json, "/foo"}, + {"bar", json, "/foo/0"}, + {"baz", json, "/foo/1"}, + {nullptr, json, "/foo/2"}, + {"0", json, "/"}, + {"1", json, "/a~1b"}, // '/' encoded as '~1' + {"2", json, "/c%d"}, + {"3", json, "/e^f"}, + {"4", json, "/g|h"}, + {"5", json, R"(/i\\j)"}, + {"6", json, R"(/k\"l)"}, + {"7", json, "/ "}, + {"8", json, "/m~0n"}, // '~' encoded as '~0' + {"9", json, R"(/o\p)"}, // any character can be escaped + {nullptr, json, "/bar"}, + {nullptr, json, "/bar/0"}, + + {"", R"({"a": ""})", "/a"}, + {"str", R"({"a": "str"})", "/a"}, + {"1", R"({"a": 1})", "/a"}, + {"null", R"({"a": null})", "/a"}, + {"true", R"({"a": true})", "/a"}, + {"false", R"({"a": false})", "/a"}, + {R"({"c": "d"})", R"({"a": {"c": "d"}})", "/a"}, + + {nullptr, "{", ""}, + {nullptr, R"({"a"})", "/a"}, + + // get_json_object do not fully valid the querying object + {"flase", R"({"a": flase})", "/a"}, + {"ni", R"({"a": ni})", "/a"}, + {"9n", R"({"a": 9n})", "/a"}, + {"-x", R"({"a": -x})", "/a"}, + {"[nx]", R"({"a": [nx]})", "/a"}, + {R"({"g": trx})", R"({"a": {"g": trx}})", "/a"}, + + // invalid array/object part result in strange behavior, won't assert in tests + // {R"({"g":}})", R"({"a": {"g":}})", "/a"}, + // {"[xxy}", R"({"a": [xxy})", "/a"}, + }; + + for (auto cs : cases) { + CheckUdf, Nullable, Nullable>("get_json_object", cs[0], cs[1], cs[2]); + } +} + } // namespace codegen } // namespace hybridse diff --git a/hybridse/src/udf/default_defs/json_defs.cc b/hybridse/src/udf/default_defs/json_defs.cc index d48a64b53cb..03f8ef3e404 100644 --- a/hybridse/src/udf/default_defs/json_defs.cc +++ b/hybridse/src/udf/default_defs/json_defs.cc @@ -16,13 +16,14 @@ #include "simdjson.h" #include "udf/default_udf_library.h" +#include "udf/udf.h" #include "udf/udf_library.h" #include "udf/udf_registry.h" namespace hybridse { namespace udf { -void json_array_length(openmldb::base::StringRef* in, int32_t* sz, bool* is_null) { +void json_array_length(openmldb::base::StringRef* in, int32_t* sz, bool* is_null) noexcept { *is_null = true; simdjson::ondemand::parser parser; @@ -47,6 +48,63 @@ void json_array_length(openmldb::base::StringRef* in, int32_t* sz, bool* is_null *sz = static_cast(arr_sz); } +void get_json_object(openmldb::base::StringRef* in, openmldb::base::StringRef* json_path, + openmldb::base::StringRef* out, bool* is_null) noexcept { + *is_null = true; + + simdjson::ondemand::parser parser; + simdjson::padded_string json(in->data_, in->size_); + simdjson::ondemand::document doc; + simdjson::error_code err = simdjson::error_code::SUCCESS; + + if (parser.iterate(json).tie(doc, err); err) { + return; + } + + simdjson::ondemand::value val; + std::string_view path(json_path->data_, json_path->size_); + if (doc.at_pointer(path).tie(val, err); err) { + return; + } + + simdjson::ondemand::json_type type; + if (val.type().tie(type, err); err) { + return; + } + std::string_view raw_str; + switch (type) { + // NOTE: JSON validation skipped for null/bool/number/array/object simplify for performance, + // string value always checked. + // Recheck here if u think more accurate syntax check is necessary. + case simdjson::ondemand::json_type::null: + case simdjson::ondemand::json_type::boolean: + case simdjson::ondemand::json_type::number: + case simdjson::ondemand::json_type::array: + case simdjson::ondemand::json_type::object: { + if (simdjson::to_json_string(val).tie(raw_str, err); err) { + return; + } + break; + } + case simdjson::ondemand::json_type::string: { + if (val.get_string().tie(raw_str, err); err) { + return; + } + break; + } + default: + return; + } + + size_t sz = raw_str.size(); + char* buf = v1::AllocManagedStringBuf(sz); + memcpy(buf, raw_str.data(), sz); + + out->size_ = sz; + out->data_ = buf; + *is_null = false; +} + void DefaultUdfLibrary::InitJsonUdfs() { RegisterExternal("json_array_length") .args(json_array_length) @@ -70,6 +128,39 @@ void DefaultUdfLibrary::InitJsonUdfs() { -- NULL @endcode + @since 0.9.0)"); + + RegisterExternal("get_json_object") + .args(get_json_object) + .doc(R"( + @brief Extracts a JSON object from [JSON Pointer](https://datatracker.ietf.org/doc/html/rfc6901) + + NOTE JSON string is not fully validated. Which means that the function may still returns values even though returned string does not valid for JSON. + It's your responsibility to make sure input string is valid JSON + + @param expr A string expression contains well formed JSON + @param path A string expression of JSON string representation from [JSON Pointer](https://datatracker.ietf.org/doc/html/rfc6901) + + Example: + + @code{.sql} + select get_json_object('{"boo": "baz"}', "/boo") + -- baz + + select get_json_object('{"boo": [1, 2]}', "/boo/0") + -- 1 + + select get_json_object('{"m~n": 1}', "/m~0n") + -- 1 + + select get_json_object('{"m/n": 1}', "/m~1n") + -- 1 + + select get_json_object('{"foo": {"bar": bz}}', "/foo") + -- {"bar": bz} + --- returns value even input JSON is not a valid JSON + @endcode + @since 0.9.0)"); } diff --git a/hybridse/src/udf/literal_traits.h b/hybridse/src/udf/literal_traits.h index 2c3c83bc750..13c876951e8 100644 --- a/hybridse/src/udf/literal_traits.h +++ b/hybridse/src/udf/literal_traits.h @@ -88,9 +88,14 @@ struct Nullable { template <> struct Nullable { Nullable(std::nullptr_t) : data_(nullptr), is_null_(true) {} // NOLINT + Nullable() : is_null_(true) {} Nullable(const StringRef& t) : data_(t), is_null_(false) {} // NOLINT Nullable(const char* buf) : data_(buf), is_null_(false) {} // NOLINT - Nullable() : is_null_(false) {} + +#if __cplusplus >= 201703L + template + Nullable(std::basic_string_view v) : data_(v), is_null_(false) {} // NOLINT +#endif const StringRef& value() const { return data_; } bool is_null() const { return is_null_; } From 8d7094d84d40724834275d9337b4990e0067edde Mon Sep 17 00:00:00 2001 From: aceforeverd Date: Wed, 16 Aug 2023 11:36:14 +0800 Subject: [PATCH 12/63] docs(monitoring): update description (#3436) * docs(monitoring): update * docs: prometheus -> Prometheus --- docs/en/maintain/monitoring.md | 172 +++++++++++++++++++-------------- docs/zh/maintain/monitoring.md | 171 ++++++++++++++++++-------------- 2 files changed, 198 insertions(+), 145 deletions(-) diff --git a/docs/en/maintain/monitoring.md b/docs/en/maintain/monitoring.md index 9369627ddf6..356dd08eb2a 100644 --- a/docs/en/maintain/monitoring.md +++ b/docs/en/maintain/monitoring.md @@ -5,7 +5,7 @@ The monitoring scheme of OpenMLDB is outlined as follows: -- Use [prometheus](https://prometheus.io) to collect monitoring metrics, [grafana](https://grafana.com/oss/grafana/) to visualize metrics +- Use [Prometheus](https://prometheus.io) to collect monitoring metrics, [Grafana](https://grafana.com/oss/grafana/) to visualize metrics - OpenMLDB exporter exposes database-level metrics - Each component as a server itself expose component-level metrics - Uses [node_exporter](https://github.com/prometheus/node_exporter) to expose machine and operating system related metrics @@ -17,7 +17,7 @@ The monitoring scheme of OpenMLDB is outlined as follows: [![PyPI](https://img.shields.io/pypi/v/openmldb-exporter?label=openmldb-exporter)](https://pypi.org/project/openmldb-exporter/) ![PyPI - Python Version](https://img.shields.io/pypi/pyversions/openmldb-exporter?style=flat-square) -The OpenMLDB exporter is a prometheus exporter implemented in Python. The core connects the OpenMLDB instance through the database SDK and will query the exposed monitoring indicators through SQL statements. Exporter publish into PyPI. You can install the latest `openmldb-exporter` through pip. For development instructions, please refer to the code directory [README](https://github.com/4paradigm/OpenMLDB/tree/main/monitoring). +The OpenMLDB exporter is a Prometheus exporter implemented in Python. The core connects the OpenMLDB instance through the database SDK and will query the exposed monitoring indicators through SQL statements. Exporter publish into PyPI. You can install the latest `openmldb-exporter` through pip. For development instructions, please refer to the code directory [README](https://github.com/4paradigm/openmldb-exporter). ### Environmental Requirements @@ -39,94 +39,120 @@ The OpenMLDB exporter is a prometheus exporter implemented in Python. The core c The startup script `bin/start.sh` should enable server status by default. -3. Note: Make sure to select the binding IP addresses of OpenMLDB components OpenMLDB exporter as well as prometheus and grafana to ensure that grafana can access prometheus, and that prometheus, OpenMLDB exporter, and OpenMLDB components can access each other. +3. Note: Make sure to select the binding IP addresses of OpenMLDB components OpenMLDB exporter as well as Prometheus and Grafana to ensure that Grafana can access Prometheus, and that Prometheus, OpenMLDB exporter, and OpenMLDB components can access each other. ### Deploy the OpenMLDB exporter -1. Install openmldb-exporter from PyPI - - ```bash - pip install openmldb-exporter - ``` - -2. Run - - An executable `openmldb-exporter` will be installed by default, make sure pip install path is in your $PATH environment variable. - - ```bash - openmldb-exporter - ``` - - Appropriate parameters are required in order to work, use `openmldb-exporter -h` to see the latest help message, here is a example output: - - ```bash - usage: openmldb-exporter [-h] [--log.level LOG.LEVEL] [--web.listen-address WEB.LISTEN_ADDRESS] - [--web.telemetry-path WEB.TELEMETRY_PATH] [--config.zk_root CONFIG.ZK_ROOT] - [--config.zk_path CONFIG.ZK_PATH] [--config.interval CONFIG.INTERVAL] - - OpenMLDB exporter - - optional arguments: - -h, --help show this help message and exit - --log.level LOG.LEVEL - config log level, default WARN - --web.listen-address WEB.LISTEN_ADDRESS - process listen port, default 8000 - --web.telemetry-path WEB.TELEMETRY_PATH - Path under which to expose metrics, default metrics - --config.zk_root CONFIG.ZK_ROOT - endpoint to zookeeper, default 127.0.0.1:6181 - --config.zk_path CONFIG.ZK_PATH - root path in zookeeper for OpenMLDB, default / - --config.interval CONFIG.INTERVAL - interval in seconds to pull metrics periodically, default 30.0 - - ``` - -3. View the list of metrics - - ```bash - $ curl http://127.0.0.1:8000/metrics - # HELP openmldb_connected_seconds_total duration for a component conncted time in seconds - # TYPE openmldb_connected_seconds_total counter - openmldb_connected_seconds_total{endpoint="172.17.0.15:9520",role="tablet"} 208834.70900011063 - openmldb_connected_seconds_total{endpoint="172.17.0.15:9521",role="tablet"} 208834.70700001717 - openmldb_connected_seconds_total{endpoint="172.17.0.15:9522",role="tablet"} 208834.71399998665 - openmldb_connected_seconds_total{endpoint="172.17.0.15:9622",role="nameserver"} 208833.70000004768 - openmldb_connected_seconds_total{endpoint="172.17.0.15:9623",role="nameserver"} 208831.70900011063 - openmldb_connected_seconds_total{endpoint="172.17.0.15:9624",role="nameserver"} 208829.7230000496 - # HELP openmldb_connected_seconds_created duration for a component conncted time in seconds - # TYPE openmldb_connected_seconds_created gauge - openmldb_connected_seconds_created{endpoint="172.17.0.15:9520",role="tablet"} 1.6501813860467942e+09 - openmldb_connected_seconds_created{endpoint="172.17.0.15:9521",role="tablet"} 1.6501813860495396e+09 - openmldb_connected_seconds_created{endpoint="172.17.0.15:9522",role="tablet"} 1.650181386050323e+09 - openmldb_connected_seconds_created{endpoint="172.17.0.15:9622",role="nameserver"} 1.6501813860512116e+09 - openmldb_connected_seconds_created{endpoint="172.17.0.15:9623",role="nameserver"} 1.650181386051238e+09 - openmldb_connected_seconds_created{endpoint="172.17.0.15:9624",role="nameserver"} 1.6501813860512598e+09 - - ``` +**You can run openmdlb-exporter from docker, or install and run directly from PyPI.** + +
Use docker + +```sh +docker run ghcr.io/4paradigm/openmldb-exporter \ + --config.zk_root= \ + --config.zk_path= +``` + +
+ +
Install and Run from PyPI + +```sh +pip install openmldb-exporter + +# start +openmldb-exporter \ + --config.zk_root= \ + --config.zk_path= +``` +

+ +And replace `` and `` to correct value. Afterwards, you can check metrics with curl: + +```sh +curl http://:8000/metrics +``` +`` is docker container IP, or `127.0.0.1` if installing from PyPI. + +
Example output + +```sh +# HELP openmldb_connected_seconds_total duration for a component conncted time in seconds +# TYPE openmldb_connected_seconds_total counter +openmldb_connected_seconds_total{endpoint="172.17.0.15:9520",role="tablet"} 208834.70900011063 +openmldb_connected_seconds_total{endpoint="172.17.0.15:9521",role="tablet"} 208834.70700001717 +openmldb_connected_seconds_total{endpoint="172.17.0.15:9522",role="tablet"} 208834.71399998665 +openmldb_connected_seconds_total{endpoint="172.17.0.15:9622",role="nameserver"} 208833.70000004768 +openmldb_connected_seconds_total{endpoint="172.17.0.15:9623",role="nameserver"} 208831.70900011063 +openmldb_connected_seconds_total{endpoint="172.17.0.15:9624",role="nameserver"} 208829.7230000496 +# HELP openmldb_connected_seconds_created duration for a component conncted time in seconds +# TYPE openmldb_connected_seconds_created gauge +openmldb_connected_seconds_created{endpoint="172.17.0.15:9520",role="tablet"} 1.6501813860467942e+09 +openmldb_connected_seconds_created{endpoint="172.17.0.15:9521",role="tablet"} 1.6501813860495396e+09 +openmldb_connected_seconds_created{endpoint="172.17.0.15:9522",role="tablet"} 1.650181386050323e+09 +openmldb_connected_seconds_created{endpoint="172.17.0.15:9622",role="nameserver"} 1.6501813860512116e+09 +openmldb_connected_seconds_created{endpoint="172.17.0.15:9623",role="nameserver"} 1.650181386051238e+09 +openmldb_connected_seconds_created{endpoint="172.17.0.15:9624",role="nameserver"} 1.6501813860512598e+09 +``` + +
+ +### Configuration + +You can view the help from: +```sh +openmldb-exporter -h +``` +`--config.zk_root` and `--config.zk_path` are mandatory. + +
Available options + +``` +usage: openmldb-exporter [-h] [--log.level LOG.LEVEL] [--web.listen-address WEB.LISTEN_ADDRESS] + [--web.telemetry-path WEB.TELEMETRY_PATH] [--config.zk_root CONFIG.ZK_ROOT] + [--config.zk_path CONFIG.ZK_PATH] [--config.interval CONFIG.INTERVAL] + +OpenMLDB exporter + +optional arguments: + -h, --help show this help message and exit + --log.level LOG.LEVEL + config log level, default WARN + --web.listen-address WEB.LISTEN_ADDRESS + process listen port, default 8000 + --web.telemetry-path WEB.TELEMETRY_PATH + Path under which to expose metrics, default metrics + --config.zk_root CONFIG.ZK_ROOT + endpoint to zookeeper, default 127.0.0.1:6181 + --config.zk_path CONFIG.ZK_PATH + root path in zookeeper for OpenMLDB, default / + --config.interval CONFIG.INTERVAL + interval in seconds to pull metrics periodically, default 30.0 +``` + +
## Deploy Node Exporter -[node_exporter](https://github.com/prometheus/node_exporter) is an official implementation of prometheus that exposes system metrics, read their README about setup. +[node_exporter](https://github.com/prometheus/node_exporter) is an official implementation of Prometheus that exposes system metrics, read their README about setup. ## Deploy Prometheus and Grafana -For installation and deployment of prometheus and grafana, please refer to the official documents [promtheus get started](https://prometheus.io/docs/prometheus/latest/getting_started/) and [grafana get started](https://grafana.com/docs/ grafana/latest/getting-started/getting-started-prometheus/). We recommend quick start with docker images, and use Grafana >= 8.3 and Prometheus >= 1.0.0 . +For installation and deployment of Prometheus and Grafana, please refer to the official documents [promtheus get started](https://prometheus.io/docs/prometheus/latest/getting_started/) and [Grafana get started](https://grafana.com/docs/ grafana/latest/getting-started/getting-started-prometheus/). We recommend quick start with docker images, and use Grafana >= 8.3 and Prometheus >= 1.0.0 . -OpenMLDB provides prometheus and grafana configuration files for reference, see [OpenMLDB mixin](https://github.com/4paradigm/OpenMLDB/tree/main/monitoring/openmldb_mixin/README.md): +OpenMLDB provides Prometheus and Grafana configuration files for reference, see [OpenMLDB mixin](https://github.com/4paradigm/openmldb-exporter/tree/main/openmldb_mixin): - prometheus_example.yml: Prometheus configuration example, remember to modify the target address in `node`, `openmldb_components` and `openmldb_exporter` jobs for your environment -- openmldb_dashboard.json: grafana dashboard configuration for OpenMLDB metrics, import it two steps: - 1. Under the grafana data source page, add the started prometheus server address as the data source +- openmldb_dashboard.json: Grafana dashboard configuration for OpenMLDB metrics, import it two steps: + 1. Under the Grafana data source page, add the started Prometheus server address as the data source 2. Under the dashboard browsing page, click the `Import` button, paste the dashboard ID `17843`, or upload this json file directly - Rad more in [Grafana import dashboard](https://grafana.com/docs/grafana/latest/dashboards/manage-dashboards/#import-a-dashboard) - - Page in grafana dashboard: https://grafana.com/grafana/dashboards/17843 + - Page in Grafana dashboard: https://grafana.com/grafana/dashboards/17843 ## Understand Existing Monitoring Metrics -Taking the OpenMLDB cluster system as an example, there are two type of metrics categorized by prometheus pull jobs: +Taking the OpenMLDB cluster system as an example, there are two type of metrics categorized by Prometheus pull jobs: ### 1. DB-Level metrics @@ -161,7 +187,7 @@ Exposed through the OpenMLDB exporter, the `job_name=openmldb_exporter` entry in ### 2. Component-Level metrics -The related components of OpenMLDB (nameserver, tablet, etc), themselves as BRPC server, and expose [prometheus related metrics](https://github.com/apache/incubator-brpc/blob/master/docs/en/bvar .md#export-to-prometheus), you only need to configure the prometheus server to pull metrics from the corresponding address. It corresponds to the `job_name=openmldb_components` item in `prometheus_example.yml`: +The related components of OpenMLDB (nameserver, tablet, etc), themselves as BRPC server, and expose [prometheus related metrics](https://github.com/apache/incubator-brpc/blob/master/docs/en/bvar .md#export-to-prometheus), you only need to configure the Prometheus server to pull metrics from the corresponding address. It corresponds to the `job_name=openmldb_components` item in `prometheus_example.yml`: ```yaml - job_name: openmldb_components diff --git a/docs/zh/maintain/monitoring.md b/docs/zh/maintain/monitoring.md index 6d993d65053..905644c74df 100644 --- a/docs/zh/maintain/monitoring.md +++ b/docs/zh/maintain/monitoring.md @@ -4,7 +4,7 @@ OpenMLDB 的监控方案概述如下: -- 使用 [prometheus](https://prometheus.io) 收集监控指标,[grafana](https://grafana.com/oss/grafana/) 可视化指标 +- 使用 [Prometheus](https://prometheus.io) 收集监控指标,[Grafana](https://grafana.com/oss/grafana/) 可视化指标 - OpenMLDB exporter 暴露数据库级别的监控指标 - 每个组件作为独立的 server 暴露组件级别的监控指标 - 使用 [node_exporter](https://github.com/prometheus/node_exporter) 暴露机器和操作系统相关指标 @@ -16,7 +16,7 @@ OpenMLDB 的监控方案概述如下: [![PyPI](https://img.shields.io/pypi/v/openmldb-exporter?label=openmldb-exporter)](https://pypi.org/project/openmldb-exporter/) ![PyPI - Python Version](https://img.shields.io/pypi/pyversions/openmldb-exporter?style=flat-square) -OpenMLDB exporter 是以 Python 实现的 prometheus exporter,核心是通过数据库 SDK 连接 OpenMLDB 实例并通过 SQL 语句查询暴露监控指标。Exporter 会发布到 PyPI,可以通过 pip 安装最新发布的 `openmldb-exporter`,开发使用说明详见代码目录 [README](https://github.com/4paradigm/OpenMLDB/tree/main/monitoring)。 +OpenMLDB exporter 是以 Python 实现的 Prometheus exporter,核心是通过数据库 SDK 连接 OpenMLDB 实例并通过 SQL 语句查询暴露监控指标。Exporter 会发布到 PyPI,可以通过 pip 安装最新发布的 `openmldb-exporter`,开发使用说明详见代码目录 [README](https://github.com/4paradigm/openmldb-exporter)。 ### 环境要求 @@ -35,94 +35,121 @@ OpenMLDB exporter 是以 Python 实现的 prometheus exporter,核心是通过 默认启动脚本 `bin/start.sh` 开启了 server status, 不需要额外配置。 -3. 注意:合理选择 OpenMLDB 各组件和 OpenMLDB exporter, 以及 prometheus, grafana 的绑定 IP 地址,确保 grafana 可以访问到 prometheus, 并且 prometheus,OpenMLDB exporter 和 OpenMLDB 各个组件之间可以相互访问。 +3. 注意:合理选择 OpenMLDB 各组件和 OpenMLDB exporter, 以及 Prometheus, Grafana 的绑定 IP 地址,确保 Grafana 可以访问到 Prometheus, 并且 Prometheus,OpenMLDB exporter 和 OpenMLDB 各个组件之间可以相互访问。 ### 部署 OpenMLDB exporter -1. 从 PyPI 安装 openmldb-exporter +使用 Docker 或者 Pip 安装运行 openmdlb-exporter + +
Docker + +```sh +docker run ghcr.io/4paradigm/openmldb-exporter \ + --config.zk_root= \ + --config.zk_path= +``` + +
+ +
Pip + +```sh +pip install openmldb-exporter + +# start +openmldb-exporter \ + --config.zk_root= \ + --config.zk_path= +``` +

+ +注意将 `` and `` 替换成正确的值. 成功后就可以用 curl 查询状态: + +```sh +curl http://:8000/metrics +``` +`` 是容器的 IP, 如果从 pip 安装运行则是本机 IP. + +
样例输出 + +```sh +# HELP openmldb_connected_seconds_total duration for a component conncted time in seconds +# TYPE openmldb_connected_seconds_total counter +openmldb_connected_seconds_total{endpoint="172.17.0.15:9520",role="tablet"} 208834.70900011063 +openmldb_connected_seconds_total{endpoint="172.17.0.15:9521",role="tablet"} 208834.70700001717 +openmldb_connected_seconds_total{endpoint="172.17.0.15:9522",role="tablet"} 208834.71399998665 +openmldb_connected_seconds_total{endpoint="172.17.0.15:9622",role="nameserver"} 208833.70000004768 +openmldb_connected_seconds_total{endpoint="172.17.0.15:9623",role="nameserver"} 208831.70900011063 +openmldb_connected_seconds_total{endpoint="172.17.0.15:9624",role="nameserver"} 208829.7230000496 +# HELP openmldb_connected_seconds_created duration for a component conncted time in seconds +# TYPE openmldb_connected_seconds_created gauge +openmldb_connected_seconds_created{endpoint="172.17.0.15:9520",role="tablet"} 1.6501813860467942e+09 +openmldb_connected_seconds_created{endpoint="172.17.0.15:9521",role="tablet"} 1.6501813860495396e+09 +openmldb_connected_seconds_created{endpoint="172.17.0.15:9522",role="tablet"} 1.650181386050323e+09 +openmldb_connected_seconds_created{endpoint="172.17.0.15:9622",role="nameserver"} 1.6501813860512116e+09 +openmldb_connected_seconds_created{endpoint="172.17.0.15:9623",role="nameserver"} 1.650181386051238e+09 +openmldb_connected_seconds_created{endpoint="172.17.0.15:9624",role="nameserver"} 1.6501813860512598e+09 +``` + +
+ +### OpenMLDB exporter 配置 + +查看帮助信息: +```sh +openmldb-exporter -h +``` +`--config.zk_root` 和 `--config.zk_path` 是必须的. + +
所有选项 + +``` +usage: openmldb-exporter [-h] [--log.level LOG.LEVEL] [--web.listen-address WEB.LISTEN_ADDRESS] + [--web.telemetry-path WEB.TELEMETRY_PATH] [--config.zk_root CONFIG.ZK_ROOT] + [--config.zk_path CONFIG.ZK_PATH] [--config.interval CONFIG.INTERVAL] + +OpenMLDB exporter + +optional arguments: + -h, --help show this help message and exit + --log.level LOG.LEVEL + config log level, default WARN + --web.listen-address WEB.LISTEN_ADDRESS + process listen port, default 8000 + --web.telemetry-path WEB.TELEMETRY_PATH + Path under which to expose metrics, default metrics + --config.zk_root CONFIG.ZK_ROOT + endpoint to zookeeper, default 127.0.0.1:6181 + --config.zk_path CONFIG.ZK_PATH + root path in zookeeper for OpenMLDB, default / + --config.interval CONFIG.INTERVAL + interval in seconds to pull metrics periodically, default 30.0 +``` + +
- ```bash - pip install openmldb-exporter - ``` - -2. 运行 - - 默认会安装一个可执行文件 `openmldb-exporter`, 确认 pip 安装路径在你的 $PATH 环境变量里面。 - - ```bash - openmldb-exporter - ``` - - 注意传入合适的参数,`openmldb-exporter -h` 查看 help: - - ```bash - usage: openmldb-exporter [-h] [--log.level LOG.LEVEL] [--web.listen-address WEB.LISTEN_ADDRESS] - [--web.telemetry-path WEB.TELEMETRY_PATH] [--config.zk_root CONFIG.ZK_ROOT] - [--config.zk_path CONFIG.ZK_PATH] [--config.interval CONFIG.INTERVAL] - - OpenMLDB exporter - - optional arguments: - -h, --help show this help message and exit - --log.level LOG.LEVEL - config log level, default WARN - --web.listen-address WEB.LISTEN_ADDRESS - process listen port, default 8000 - --web.telemetry-path WEB.TELEMETRY_PATH - Path under which to expose metrics, default metrics - --config.zk_root CONFIG.ZK_ROOT - endpoint to zookeeper, default 127.0.0.1:6181 - --config.zk_path CONFIG.ZK_PATH - root path in zookeeper for OpenMLDB, default / - --config.interval CONFIG.INTERVAL - interval in seconds to pull metrics periodically, default 30.0 - - ``` - -3. 查看 metrics 列表 - - ```bash - $ curl http://127.0.0.1:8000/metrics - # HELP openmldb_connected_seconds_total duration for a component conncted time in seconds - # TYPE openmldb_connected_seconds_total counter - openmldb_connected_seconds_total{endpoint="172.17.0.15:9520",role="tablet"} 208834.70900011063 - openmldb_connected_seconds_total{endpoint="172.17.0.15:9521",role="tablet"} 208834.70700001717 - openmldb_connected_seconds_total{endpoint="172.17.0.15:9522",role="tablet"} 208834.71399998665 - openmldb_connected_seconds_total{endpoint="172.17.0.15:9622",role="nameserver"} 208833.70000004768 - openmldb_connected_seconds_total{endpoint="172.17.0.15:9623",role="nameserver"} 208831.70900011063 - openmldb_connected_seconds_total{endpoint="172.17.0.15:9624",role="nameserver"} 208829.7230000496 - # HELP openmldb_connected_seconds_created duration for a component conncted time in seconds - # TYPE openmldb_connected_seconds_created gauge - openmldb_connected_seconds_created{endpoint="172.17.0.15:9520",role="tablet"} 1.6501813860467942e+09 - openmldb_connected_seconds_created{endpoint="172.17.0.15:9521",role="tablet"} 1.6501813860495396e+09 - openmldb_connected_seconds_created{endpoint="172.17.0.15:9522",role="tablet"} 1.650181386050323e+09 - openmldb_connected_seconds_created{endpoint="172.17.0.15:9622",role="nameserver"} 1.6501813860512116e+09 - openmldb_connected_seconds_created{endpoint="172.17.0.15:9623",role="nameserver"} 1.650181386051238e+09 - openmldb_connected_seconds_created{endpoint="172.17.0.15:9624",role="nameserver"} 1.6501813860512598e+09 - - ``` ## 部署 node exporter -[node_exporter](https://github.com/prometheus/node_exporter) 是 prometheus 官方实现的暴露系统指标的组件。 安装使用详见它的 README。 +[node_exporter](https://github.com/prometheus/node_exporter) 是 Prometheus 官方实现的暴露系统指标的组件。 安装使用详见它的 README。 ## 部署 Prometheus 和 Grafana -如何安装部署 prometheus, grafana 详见官方文档 [promtheus get started](https://prometheus.io/docs/prometheus/latest/getting_started/) 和 [grafana get started](https://grafana.com/docs/grafana/latest/getting-started/getting-started-prometheus/) 。我们建议使用 Docker 容器快速部署, 并且 Grafana >= 8.3, Prometheus >= 1.0.0 。 +如何安装部署 Prometheus, Grafana 详见官方文档 [promtheus get started](https://prometheus.io/docs/prometheus/latest/getting_started/) 和 [Grafana get started](https://grafana.com/docs/grafana/latest/getting-started/getting-started-prometheus/) 。我们建议使用 Docker 容器快速部署, 并且 Grafana >= 8.3, Prometheus >= 1.0.0 。 -OpenMLDB 提供了 prometheus 和 grafana 配置文件以作参考,详见 [OpenMLDB mixin](https://github.com/4paradigm/OpenMLDB/tree/main/monitoring/openmldb_mixin/README.md): +OpenMLDB 提供了 Prometheus 和 Grafana 配置文件以作参考,详见 [OpenMLDB mixin](https://github.com/4paradigm/openmldb-exporter/tree/main/openmldb_mixin): -- prometheus_example.yml: prometheus 配置示例, 注意修改 `node`, `openmldb_components` 和 `openmldb_exporter` job 中的 target 地址 -- openmldb_dashboard.json: OpenMLDB metrics 的 grafana dashboard 配置, 分为两步: - 1. 在 grafana data source 页面下,添加启动的 prometheus server 地址作为数据源 +- prometheus_example.yml: Prometheus 配置示例, 注意修改 `node`, `openmldb_components` 和 `openmldb_exporter` job 中的 target 地址 +- openmldb_dashboard.json: OpenMLDB metrics 的 Grafana dashboard 配置, 分为两步: + 1. 在 Grafana data source 页面下,添加启动的 Prometheus server 地址作为数据源 2. 在 dashboard 浏览页面下,点击导入一个 dashboard, 输入 dashboard ID `17843`, 或者直接上传该 json 配置文件 - 导入详细说明见 [Grafana import dashboard](https://grafana.com/docs/grafana/latest/dashboards/manage-dashboards/#import-a-dashboard) - Grafana dashboard 配置见 https://grafana.com/grafana/dashboards/17843 ## 理解现有的监控指标 -以 OpenMLDB 集群系统为例,监控指标根据 prometheus pull job 不同,分为两类: +以 OpenMLDB 集群系统为例,监控指标根据 Prometheus pull job 不同,分为两类: ### 1. DB-Level 指标 @@ -159,7 +186,7 @@ OpenMLDB 提供了 prometheus 和 grafana 配置文件以作参考,详见 [Ope ### 2. Component-Level 指标 -OpenMLDB 的相关组件(即 nameserver, tablet, etc), 本身作为 BRPC server,暴露了 [prometheus 相关指标](https://github.com/apache/incubator-brpc/blob/master/docs/en/bvar.md#export-to-prometheus), 只需要配置 prometheus server 从对应地址拉取指标即可。对应 `prometheus_example.yml`中 `job_name=openmldb_components` 项: +OpenMLDB 的相关组件(即 nameserver, tablet, etc), 本身作为 BRPC server,暴露了 [Prometheus 相关指标](https://github.com/apache/incubator-brpc/blob/master/docs/en/bvar.md#export-to-prometheus), 只需要配置 Prometheus server 从对应地址拉取指标即可。对应 `prometheus_example.yml`中 `job_name=openmldb_components` 项: ```yaml - job_name: openmldb_components From fd728c68abc496adb83aea40393f0ced8c891537 Mon Sep 17 00:00:00 2001 From: "qiyuan.liu" <1043276694@qq.com> Date: Thu, 17 Aug 2023 15:53:55 +0800 Subject: [PATCH 13/63] ci: add selfhost muliti-machine integration test (#3283) --- .github/workflows/selfhost_intergration.yml | 396 ++++++++++++++++++ cases/integration_test/ddl/test_options.yaml | 4 +- .../expression/test_condition.yaml | 45 +- .../expression/test_like.yaml | 2 +- .../long_window/test_long_window.yaml | 2 + .../openmldb-sdk-test/pom.xml | 4 +- .../java_sdk_test/common/OpenMLDBConfig.java | 3 +- .../java_sdk_test/common/OpenMLDBTest.java | 2 +- .../yarn/TestExternalFunction.java | 2 +- .../openmldb-test-common/pom.xml | 2 +- test/steps/format_config.sh | 126 ++++++ .../openmldb-deploy/cases/test_upgrade.py | 4 +- 12 files changed, 575 insertions(+), 17 deletions(-) create mode 100644 .github/workflows/selfhost_intergration.yml create mode 100755 test/steps/format_config.sh diff --git a/.github/workflows/selfhost_intergration.yml b/.github/workflows/selfhost_intergration.yml new file mode 100644 index 00000000000..dcaf34cce8d --- /dev/null +++ b/.github/workflows/selfhost_intergration.yml @@ -0,0 +1,396 @@ +name: SELFHOST-INTEGRATION-TEST +on: + + schedule: + - cron: '0 14 * * *' + + workflow_dispatch: + inputs: + EXEC_TEST_TYPE: + description: 'Which tests need to be executed? The options are all, python, java, batch, cli, standalone-cli, apiserver, yarn' + required: true + default: 'all' + EXEC_VERSION: + description: 'Which version needs to be tested?' + required: true + default: 'main' +env: + GIT_SUBMODULE_STRATEGY: recursive + HYBRIDSE_SOURCE: local + E_VERSION: ${{ github.event.inputs.EXEC_VERSION || 'main'}} + ETYPE: ${{ github.event.inputs.EXEC_TEST_TYPE || 'all'}} + +jobs: + build-openmldb: + + runs-on: [self-hosted,generic] + if: github.repository == '4paradigm/OpenMLDB' + container: + image: ghcr.io/4paradigm/hybridsql:latest + env: + OS: linux + steps: + - uses: actions/checkout@v2 + - name: build + if: ${{ env.E_VERSION == 'main' }} + run: | + make configure CMAKE_INSTALL_PREFIX=openmldb-linux + make SQL_JAVASDK_ENABLE=ON && make SQL_JAVASDK_ENABLE=ON install + mv openmldb-linux openmldb-main-linux + tar -zcf openmldb-linux.tar.gz openmldb-main-linux + - name: download + if: ${{ env.E_VERSION != 'main' }} + run: | + curl -SLO https://github.com/4paradigm/OpenMLDB/releases/download/v${{ env.E_VERSION }}/openmldb-${{ env.E_VERSION }}-linux.tar.gz + tar -zxf openmldb-${{ env.E_VERSION }}-linux.tar.gz + mv openmldb-${{ env.E_VERSION }}-linux.tar.gz openmldb-linux.tar.gz + - name: upload artifact + uses: actions/upload-artifact@v3 + with: + name: openmldb-package + path: openmldb-linux.tar.gz + + + + java-sdk-cluster-memory-0: + needs: build-openmldb + + runs-on: [self-hosted,common-user] + steps: + - uses: actions/checkout@v3 + - name: before test + if: ${{ env.ETYPE == 'all' || env.ETYPE == 'java' }} + run: mkdir ${{ github.job }} + - name: download artifact + uses: actions/download-artifact@v3 + with: + name: openmldb-package + - name: install openmldb + run: | + tar -zxf openmldb-linux.tar.gz -C ${{ github.job }}/ + bash test/steps/format_config.sh $(pwd)/${{ github.job }}/openmldb-${{ env.E_VERSION }}-linux ${{ github.job }} 20001 21000 java + bash ${{ github.job }}/openmldb-${{ env.E_VERSION }}-linux/sbin/deploy-all.sh + bash ${{ github.job }}/openmldb-${{ env.E_VERSION }}-linux/sbin/start-all.sh + - name: test + run: | + mkdir mvnrepo + export MAVEN_OPTS="-Dmaven.repo.local=$(pwd)/mvnrepo" + echo $MAVEN_OPTS + bash test/steps/openmldb-sdk-test-java-src.sh -c test_cluster.xml -d deploy -l "0" -s "memory" + - name: stop openmldb + if: always() + run: bash ${{ github.job }}/openmldb-${{ env.E_VERSION }}-linux/sbin/stop-all.sh + - name: remove openmldb + if: success() + run: bash ${{ github.job }}/openmldb-${{ env.E_VERSION }}-linux/sbin/clear-all.sh + - name: TEST Results + if: always() + uses: EnricoMi/publish-unit-test-result-action@v1 + with: + files: test/integration-test/openmldb-test-java/openmldb-sdk-test/target/surefire-reports/TEST-*.xml + check_name: "SRC java-sdk-cluster-memory-0 Report" + comment_title: "SRC java-sdk-cluster-memory-0 Report" + - name: tar test report + if: ${{ failure() }} + run: tar -zcvf surefire-reports.tar.gz test/integration-test/openmldb-test-java/openmldb-sdk-test/target/surefire-reports + - name: Send Email + if: ${{ failure() }} + uses: dawidd6/action-send-mail@master + with: + server_address: smtp.partner.outlook.cn + server_port: 587 + username: ${{ secrets.MAIL_USERNAME }} + password: ${{ secrets.MAIL_PASSWORD }} + subject: OpenMLDB Memory Test + body: OpenMLDB Memory Test Failed + html_body: test/integration-test/openmldb-test-java/openmldb-sdk-test/target/surefire-reports/html/overview.html + to: ${{ secrets.MAIL_TO }} + from: GitHub Actions + attachments: surefire-reports.tar.gz + + + + java-sdk-cluster-memory-1: + needs: build-openmldb + runs-on: [self-hosted,common-user] + steps: + - uses: actions/checkout@v3 + - name: before test + if: ${{ env.ETYPE == 'all' || env.ETYPE == 'java' }} + run: mkdir ${{ github.job }} + - name: download artifact + uses: actions/download-artifact@v3 + with: + name: openmldb-package + - name: install openmldb + run: | + tar -zxf openmldb-linux.tar.gz -C ${{ github.job }}/ + bash test/steps/format_config.sh $(pwd)/${{ github.job }}/openmldb-${{ env.E_VERSION }}-linux ${{ github.job }} 21001 22000 java + bash ${{ github.job }}/openmldb-${{ env.E_VERSION }}-linux/sbin/deploy-all.sh + bash ${{ github.job }}/openmldb-${{ env.E_VERSION }}-linux/sbin/start-all.sh + - name: test + run: | + mkdir mvnrepo + export MAVEN_OPTS="-Dmaven.repo.local=$(pwd)/mvnrepo" + echo $MAVEN_OPTS + bash test/steps/openmldb-sdk-test-java-src.sh -c test_cluster.xml -d deploy -l "1,2,3,4,5" -s "memory" + - name: stop openmldb + if: always() + run: bash ${{ github.job }}/openmldb-${{ env.E_VERSION }}-linux/sbin/stop-all.sh + - name: remove openmldb + if: success() + run: bash ${{ github.job }}/openmldb-${{ env.E_VERSION }}-linux/sbin/clear-all.sh + - name: TEST Results + if: always() + uses: EnricoMi/publish-unit-test-result-action@v1 + with: + files: test/integration-test/openmldb-test-java/openmldb-sdk-test/target/surefire-reports/TEST-*.xml + check_name: "SRC java-sdk-cluster-memory-1 Report" + comment_title: "SRC java-sdk-cluster-memory-1 Report" + - name: tar test report + if: ${{ failure() }} + run: tar -zcvf surefire-reports.tar.gz test/integration-test/openmldb-test-java/openmldb-sdk-test/target/surefire-reports + - name: Send Email + if: ${{ failure() }} + uses: dawidd6/action-send-mail@master + with: + server_address: smtp.partner.outlook.cn + server_port: 587 + username: ${{ secrets.MAIL_USERNAME }} + password: ${{ secrets.MAIL_PASSWORD }} + subject: OpenMLDB Memory Test + body: OpenMLDB Memory Test Failed + html_body: test/integration-test/openmldb-test-java/openmldb-sdk-test/target/surefire-reports/html/overview.html + to: ${{ secrets.MAIL_TO }} + from: GitHub Actions + attachments: surefire-reports.tar.gz + + + + + java-sdk-cluster-hdd: + needs: build-openmldb + runs-on: [self-hosted,common-user] + steps: + - uses: actions/checkout@v3 + - name: before test + if: ${{ env.ETYPE == 'all' || env.ETYPE == 'java' }} + run: mkdir ${{ github.job }} + - name: download artifact + uses: actions/download-artifact@v3 + with: + name: openmldb-package + - name: install openmldb + run: | + tar -zxf openmldb-linux.tar.gz -C ${{ github.job }}/ + bash test/steps/format_config.sh $(pwd)/${{ github.job }}/openmldb-${{ env.E_VERSION }}-linux ${{ github.job }} 22001 23000 java + bash ${{ github.job }}/openmldb-${{ env.E_VERSION }}-linux/sbin/deploy-all.sh + bash ${{ github.job }}/openmldb-${{ env.E_VERSION }}-linux/sbin/start-all.sh + - name: test + run: | + mkdir mvnrepo + export MAVEN_OPTS="-Dmaven.repo.local=$(pwd)/mvnrepo" + echo $MAVEN_OPTS + bash test/steps/openmldb-sdk-test-java-src.sh -c test_cluster_disk.xml -d deploy -l "0" -s "hdd" + - name: stop openmldb + if: always() + run: bash ${{ github.job }}/openmldb-${{ env.E_VERSION }}-linux/sbin/stop-all.sh + - name: remove openmldb + if: success() + run: bash ${{ github.job }}/openmldb-${{ env.E_VERSION }}-linux/sbin/clear-all.sh + - name: TEST Results + if: always() + uses: EnricoMi/publish-unit-test-result-action@v1 + with: + files: test/integration-test/openmldb-test-java/openmldb-sdk-test/target/surefire-reports/TEST-*.xml + check_name: "SRC java-sdk-cluster-hdd Report" + comment_title: "SRC java-sdk-cluster-hdd Report" + - name: tar test report + if: ${{ failure() }} + run: tar -zcvf surefire-reports.tar.gz test/integration-test/openmldb-test-java/openmldb-sdk-test/target/surefire-reports + - name: Send Email + if: ${{ failure() }} + uses: dawidd6/action-send-mail@master + with: + server_address: smtp.partner.outlook.cn + server_port: 587 + username: ${{ secrets.MAIL_USERNAME }} + password: ${{ secrets.MAIL_PASSWORD }} + subject: OpenMLDB HDD Test + body: OpenMLDB HDD Test Failed + html_body: test/integration-test/openmldb-test-java/openmldb-sdk-test/target/surefire-reports/html/overview.html + to: ${{ secrets.MAIL_TO }} + from: GitHub Actions + attachments: surefire-reports.tar.gz + + + + java-sdk-cluster-ssd: + needs: build-openmldb + runs-on: [self-hosted,common-user] + steps: + - uses: actions/checkout@v3 + - name: before test + if: ${{ env.ETYPE == 'all' || env.ETYPE == 'java' }} + run: mkdir ${{ github.job }} + - name: download artifact + uses: actions/download-artifact@v3 + with: + name: openmldb-package + - name: install openmldb + run: | + tar -zxf openmldb-linux.tar.gz -C ${{ github.job }}/ + bash test/steps/format_config.sh $(pwd)/${{ github.job }}/openmldb-${{ env.E_VERSION }}-linux java-sdk-cluster-memory-0 23001 24000 java ssd + bash ${{ github.job }}/openmldb-${{ env.E_VERSION }}-linux/sbin/deploy-all.sh + bash ${{ github.job }}/openmldb-${{ env.E_VERSION }}-linux/sbin/start-all.sh + - name: test + run: | + mkdir mvnrepo + export MAVEN_OPTS="-Dmaven.repo.local=$(pwd)/mvnrepo" + echo $MAVEN_OPTS + bash test/steps/openmldb-sdk-test-java-src.sh -c test_cluster_disk.xml -d deploy -l "0" -s "ssd" + - name: stop openmldb + if: always() + run: bash ${{ github.job }}/openmldb-${{ env.E_VERSION }}-linux/sbin/stop-all.sh + - name: remove openmldb + if: success() + run: bash ${{ github.job }}/openmldb-${{ env.E_VERSION }}-linux/sbin/clear-all.sh + - name: TEST Results + if: always() + uses: EnricoMi/publish-unit-test-result-action@v1 + with: + files: test/integration-test/openmldb-test-java/openmldb-sdk-test/target/surefire-reports/TEST-*.xml + check_name: "SRC java-sdk-cluster-ssd Report" + comment_title: "SRC java-sdk-cluster-ssd Report" + - name: tar test report + if: ${{ failure() }} + run: tar -zcvf surefire-reports.tar.gz test/integration-test/openmldb-test-java/openmldb-sdk-test/target/surefire-reports + - name: Send Email + if: ${{ failure() }} + uses: dawidd6/action-send-mail@master + with: + server_address: smtp.partner.outlook.cn + server_port: 587 + username: ${{ secrets.MAIL_USERNAME }} + password: ${{ secrets.MAIL_PASSWORD }} + subject: OpenMLDB SSD Test + body: OpenMLDB SSD Test Failed + html_body: test/integration-test/openmldb-test-java/openmldb-sdk-test/target/surefire-reports/html/overview.html + to: ${{ secrets.MAIL_TO }} + from: GitHub Actions + attachments: surefire-reports.tar.gz + + + java-sdk-yarn: + needs: build-openmldb + runs-on: [self-hosted,common-user] + steps: + - uses: actions/checkout@v3 + - name: before test + if: ${{ env.ETYPE == 'all' || env.ETYPE == 'java' }} + run: mkdir ${{ github.job }} + - name: download artifact + uses: actions/download-artifact@v3 + with: + name: openmldb-package + - name: install openmldb + run: | + tar -zxf openmldb-linux.tar.gz -C ${{ github.job }}/ + bash test/steps/format_config.sh $(pwd)/${{ github.job }}/openmldb-${{ env.E_VERSION }}-linux ${{ github.job }} 24001 25000 java hadoop + bash ${{ github.job }}/openmldb-${{ env.E_VERSION }}-linux/sbin/deploy-all.sh + bash ${{ github.job }}/openmldb-${{ env.E_VERSION }}-linux/sbin/start-all.sh + bash ${{ github.job }}/openmldb-${{ env.E_VERSION }}-linux/sbin/stop-taskmanager.sh + bash HADOOP_CONF_DIR=/mnt/hdd0/denglong/openmldb_runner_work/hadoop ${{ github.job }}/openmldb-${{ env.E_VERSION }}-linux/sbin/stop-taskmanager.sh + - name: test + run: | + mkdir mvnrepo + export MAVEN_OPTS="-Dmaven.repo.local=$(pwd)/mvnrepo" + echo $MAVEN_OPTS + bash test/steps/openmldb-sdk-test-java-src.sh -c test_yarn.xml -d deploy -l "0" -s "memory" + - name: stop openmldb + if: always() + run: bash ${{ github.job }}/openmldb-${{ env.E_VERSION }}-linux/sbin/stop-all.sh + - name: remove openmldb + if: success() + run: bash ${{ github.job }}/openmldb-${{ env.E_VERSION }}-linux/sbin/clear-all.sh + - name: TEST Results + if: always() + uses: EnricoMi/publish-unit-test-result-action@v1 + with: + files: test/integration-test/openmldb-test-java/openmldb-sdk-test/target/surefire-reports/TEST-*.xml + check_name: "SRC java-sdk-yarn Report" + comment_title: "SRC java-yarn Report" + - name: tar test report + if: ${{ failure() }} + run: tar -zcvf surefire-reports.tar.gz test/integration-test/openmldb-test-java/openmldb-sdk-test/target/surefire-reports + - name: Send Email + if: ${{ failure() }} + uses: dawidd6/action-send-mail@master + with: + server_address: smtp.partner.outlook.cn + server_port: 587 + username: ${{ secrets.MAIL_USERNAME }} + password: ${{ secrets.MAIL_PASSWORD }} + subject: OpenMLDB yarn Test + body: OpenMLDB yarn Test Failed + html_body: test/integration-test/openmldb-test-java/openmldb-sdk-test/target/surefire-reports/html/overview.html + to: ${{ secrets.MAIL_TO }} + from: GitHub Actions + attachments: surefire-reports.tar.gz + + + + java-sdk-kafka: + needs: build-openmldb + runs-on: [self-hosted,common-user] + steps: + - uses: actions/checkout@v3 + - name: before test + if: ${{ env.ETYPE == 'all' || env.ETYPE == 'java' }} + run: mkdir ${{ github.job }} + - name: download artifact + uses: actions/download-artifact@v3 + with: + name: openmldb-package + - name: install openmldb + run: | + tar -zxf openmldb-linux.tar.gz -C ${{ github.job }}/ + bash test/steps/format_config.sh $(pwd)/${{ github.job }}/openmldb-${{ env.E_VERSION }}-linux ${{ github.job }} 25001 26000 java kafka + bash ${{ github.job }}/openmldb-${{ env.E_VERSION }}-linux/sbin/deploy-all.sh + bash ${{ github.job }}/openmldb-${{ env.E_VERSION }}-linux/sbin/start-all.sh + - name: test + run: | + mkdir mvnrepo + export MAVEN_OPTS="-Dmaven.repo.local=$(pwd)/mvnrepo" + echo $MAVEN_OPTS + - name: stop openmldb + if: always() + run: bash ${{ github.job }}/openmldb-${{ env.E_VERSION }}-linux/sbin/stop-all.sh + - name: remove openmldb + if: success() + run: bash ${{ github.job }}/openmldb-${{ env.E_VERSION }}-linux/sbin/clear-all.sh + # - name: TEST Results + # if: always() + # uses: EnricoMi/publish-unit-test-result-action@v1 + # with: + # files: test/integration-test/openmldb-test-java/openmldb-sdk-test/target/surefire-reports/TEST-*.xml + # check_name: "SRC java-sdk-yarn Report" + # comment_title: "SRC java-yarn Report" + # - name: tar test report + # if: ${{ failure() }} + # run: tar -zcvf surefire-reports.tar.gz test/integration-test/openmldb-test-java/openmldb-sdk-test/target/surefire-reports + # - name: Send Email + # if: ${{ failure() }} + # uses: dawidd6/action-send-mail@master + # with: + # server_address: smtp.partner.outlook.cn + # server_port: 587 + # username: ${{ secrets.MAIL_USERNAME }} + # password: ${{ secrets.MAIL_PASSWORD }} + # subject: OpenMLDB yarn Test + # body: OpenMLDB yarn Test Failed + # html_body: test/integration-test/openmldb-test-java/openmldb-sdk-test/target/surefire-reports/html/overview.html + # to: ${{ secrets.MAIL_TO }} + # from: GitHub Actions + # content_type: text/plain + # attachments: surefire-reports.tar.gz diff --git a/cases/integration_test/ddl/test_options.yaml b/cases/integration_test/ddl/test_options.yaml index d35fb6bec31..29511b8fe30 100644 --- a/cases/integration_test/ddl/test_options.yaml +++ b/cases/integration_test/ddl/test_options.yaml @@ -358,7 +358,7 @@ cases: - id: 22 desc: test-case - mode: standalone-unsupport + mode: standalone-unsupport,disk-unsupport inputs: - columns : ["c1 string","c2 smallint","c3 int","c4 bigint","c5 float","c6 double","c7 timestamp","c8 date","c9 bool"] @@ -397,7 +397,7 @@ cases: - id: 24 desc: 没有partitionnum和replicanum,指定distribution - mode: standalone-unsupport + mode: standalone-unsupport,disk-unsupport inputs: - name: t3 sql: | diff --git a/cases/integration_test/expression/test_condition.yaml b/cases/integration_test/expression/test_condition.yaml index 54d1dd4ad4d..b63ecdf67c6 100644 --- a/cases/integration_test/expression/test_condition.yaml +++ b/cases/integration_test/expression/test_condition.yaml @@ -252,7 +252,12 @@ cases: sql: | select col1,ifnull(col2,"abc") as e1 from {0}; expect: - success: false + columns: ["col1 int", "e1 string"] + order: col1 + rows: + - [1, '0'] + - [2, 'abc'] + - [3, '1'] - id: 10 desc: IFNULL-表达式 sqlDialect: ["HybridSQL"] @@ -285,7 +290,12 @@ cases: sql: | select col1,ifnull(col2 /0 ,100) as e3 from {0}; expect: - success: false + columns: ["col1 int", "e3 double"] + order: col1 + rows: + - [1, 100] + - [2, 100] + - [3, 100] - id: 11-2 mode: cli-unsupport desc: NVL is synonyms to ifnull @@ -317,7 +327,12 @@ cases: sql: | select col1,nvl(col2 /0 ,100) as e3 from {0}; expect: - success: false + columns: ["col1 int", "e3 double"] + order: col1 + rows: + - [1, 100] + - [2, 100] + - [3, 100] - id: 12 desc: IFNULL-兼容类型 sqlDialect: ["HybridSQL"] @@ -331,7 +346,13 @@ cases: sql: | select col1,ifnull(col2,100) as e1 from {0}; expect: - success: false + columns: ["col1 int", "e1 bigint"] + order: col1 + rows: + - [1, 0] + - [2, 100] + - [3, 1] + - id: 13 desc: IFNULL-浮点型 sqlDialect: ["HybridSQL"] @@ -345,7 +366,13 @@ cases: sql: | select col1,ifnull(col2,1.1) as e2 from {0}; expect: - success: false + columns: ["col1 int", "e2 double"] + order: col1 + rows: + - [1, 0] + - [2, 1.1] + - [3, 1] + - id: NVL2-1 desc: NVL2 @@ -378,7 +405,13 @@ cases: sql: | select col1,nvl2(col2, "abc", col1 + 1) as e1 from {0}; expect: - success: false + columns: ["col1 int", "e1 string"] + order: col1 + rows: + - [1, 'abc'] + - [2, '3'] + - [3, 'abc'] + - id: NVL2-3 desc: NVL2, sub expression diff --git a/cases/integration_test/expression/test_like.yaml b/cases/integration_test/expression/test_like.yaml index d47bb57b616..7f0d47daf5f 100644 --- a/cases/integration_test/expression/test_like.yaml +++ b/cases/integration_test/expression/test_like.yaml @@ -496,7 +496,7 @@ cases: columns : ["id bigint","c1 string","c7 timestamp"] indexs: ["index1:id:c7"] rows: - - [1,'\\\%a_b',1590738990000] + - [1,'\\%a_b',1590738990000] - [2,'\\\aabb',1590738991000] - [3,"_a%_b",1590738992000] - [4,"ba_c",1590738993000] diff --git a/cases/integration_test/long_window/test_long_window.yaml b/cases/integration_test/long_window/test_long_window.yaml index 95a5b6f2594..4f49cb0736b 100644 --- a/cases/integration_test/long_window/test_long_window.yaml +++ b/cases/integration_test/long_window/test_long_window.yaml @@ -322,6 +322,7 @@ cases: id: 10 version: 0.6.1 desc: delete pk + mode: cluster-unsupport longWindow: w1:2s inputs: - @@ -359,6 +360,7 @@ cases: id: 11 version: 0.6.1 desc: delete 组合索引 + mode: cluster-unsupport longWindow: w1:2s inputs: - diff --git a/test/integration-test/openmldb-test-java/openmldb-sdk-test/pom.xml b/test/integration-test/openmldb-test-java/openmldb-sdk-test/pom.xml index b5839033256..32c81920daa 100644 --- a/test/integration-test/openmldb-test-java/openmldb-sdk-test/pom.xml +++ b/test/integration-test/openmldb-test-java/openmldb-sdk-test/pom.xml @@ -15,8 +15,8 @@ 8 8 UTF-8 - 0.7.0 - 0.7.0-allinone + 0.7.0-SNAPSHOT + 0.7.0-SNAPSHOT 2.2.0 test_suite/test_tmp.xml 1.8.9 diff --git a/test/integration-test/openmldb-test-java/openmldb-sdk-test/src/main/java/com/_4paradigm/openmldb/java_sdk_test/common/OpenMLDBConfig.java b/test/integration-test/openmldb-test-java/openmldb-sdk-test/src/main/java/com/_4paradigm/openmldb/java_sdk_test/common/OpenMLDBConfig.java index 2cdcd096c0a..03f413b71dd 100644 --- a/test/integration-test/openmldb-test-java/openmldb-sdk-test/src/main/java/com/_4paradigm/openmldb/java_sdk_test/common/OpenMLDBConfig.java +++ b/test/integration-test/openmldb-test-java/openmldb-sdk-test/src/main/java/com/_4paradigm/openmldb/java_sdk_test/common/OpenMLDBConfig.java @@ -72,7 +72,8 @@ public class OpenMLDBConfig { } public static boolean isCluster() { - return OpenMLDBGlobalVar.env.equals("cluster"); + + return OpenMLDBGlobalVar.env.equals("cluster")||OpenMLDBGlobalVar.env.equals("deploy"); } } diff --git a/test/integration-test/openmldb-test-java/openmldb-sdk-test/src/main/java/com/_4paradigm/openmldb/java_sdk_test/common/OpenMLDBTest.java b/test/integration-test/openmldb-test-java/openmldb-sdk-test/src/main/java/com/_4paradigm/openmldb/java_sdk_test/common/OpenMLDBTest.java index 09e3e895543..3e39ddbb0a0 100644 --- a/test/integration-test/openmldb-test-java/openmldb-sdk-test/src/main/java/com/_4paradigm/openmldb/java_sdk_test/common/OpenMLDBTest.java +++ b/test/integration-test/openmldb-test-java/openmldb-sdk-test/src/main/java/com/_4paradigm/openmldb/java_sdk_test/common/OpenMLDBTest.java @@ -61,7 +61,7 @@ public void beforeTest(@Optional("qa") String env,@Optional("main") String versi openMLDBDeploy.setCluster(false); OpenMLDBGlobalVar.mainInfo = openMLDBDeploy.deployCluster(2, 3); }else if(env.equalsIgnoreCase("deploy")){ - OpenMLDBGlobalVar.mainInfo = YamlUtil.getObject("out/openmldb_info.yaml",OpenMLDBInfo.class); + OpenMLDBGlobalVar.mainInfo = YamlUtil.getObject(Tool.openMLDBDir().getAbsolutePath()+"/out/openmldb_info.yaml",OpenMLDBInfo.class); } else if(env.equalsIgnoreCase("yarn")) { OpenMLDBDeploy openMLDBDeploy = new OpenMLDBDeploy(version); openMLDBDeploy.setOpenMLDBPath(openMLDBPath); diff --git a/test/integration-test/openmldb-test-java/openmldb-sdk-test/src/test/java/com/_4paradigm/openmldb/java_sdk_test/yarn/TestExternalFunction.java b/test/integration-test/openmldb-test-java/openmldb-sdk-test/src/test/java/com/_4paradigm/openmldb/java_sdk_test/yarn/TestExternalFunction.java index 104552c08c1..bd1bd229e53 100644 --- a/test/integration-test/openmldb-test-java/openmldb-sdk-test/src/test/java/com/_4paradigm/openmldb/java_sdk_test/yarn/TestExternalFunction.java +++ b/test/integration-test/openmldb-test-java/openmldb-sdk-test/src/test/java/com/_4paradigm/openmldb/java_sdk_test/yarn/TestExternalFunction.java @@ -31,7 +31,7 @@ public class TestExternalFunction extends OpenMLDBTest { @Story("ExternalFunction") - @Test(enabled = true) + @Test(enabled = false) public void testFunctionMethods() { Statement statement = executor.getStatement(); diff --git a/test/integration-test/openmldb-test-java/openmldb-test-common/pom.xml b/test/integration-test/openmldb-test-java/openmldb-test-common/pom.xml index 6741f69688e..57d6a1d0cb4 100644 --- a/test/integration-test/openmldb-test-java/openmldb-test-common/pom.xml +++ b/test/integration-test/openmldb-test-java/openmldb-test-common/pom.xml @@ -15,7 +15,7 @@ 8 8 - 0.6.4 + 0.7.0-SNAPSHOT 0.7.0-SNAPSHOT diff --git a/test/steps/format_config.sh b/test/steps/format_config.sh new file mode 100755 index 00000000000..9700d330974 --- /dev/null +++ b/test/steps/format_config.sh @@ -0,0 +1,126 @@ +#! /usr/bin/env bash + +#set DeployDir +rootPath="$1" +jobName="$2" +portFrom="$3" +portTo="$4" +Type="$5" +Dependency="$6" +version=$(grep 'OPENMLDB_VERSION' "$rootPath"/conf/openmldb-env.sh | awk -F= '{print $2}') +curTime=$(date "+%m%d%H%M") +dirName="${jobName}-${version}-${curTime}" + +#set Deploy Host and Ports +Hosts=(node-3 node-4 node-1) + +AvaNode1Ports=$(ssh "${Hosts[0]}" "comm -23 <(seq $portFrom $portTo | sort) <(sudo ss -Htan | awk '{print $4}' | cut -d':' -f2 | sort -u) | shuf | head -n 8") +AvaNode2Ports=$(ssh "${Hosts[1]}" "comm -23 <(seq $portFrom $portTo | sort) <(sudo ss -Htan | awk '{print $4}' | cut -d':' -f2 | sort -u) | shuf | head -n 2") +AvaNode3Ports=$(ssh "${Hosts[2]}" "comm -23 <(seq $portFrom $portTo | sort) <(sudo ss -Htan | awk '{print $4}' | cut -d':' -f2 | sort -u) | shuf | head -n 1") + + +taskmanagerHost=$(hostname | awk -F"." '{print $1}' ) + +taskmanagerPort=$(ssh "${taskmanagerHost}" "comm -23 <(seq $portFrom $portTo | sort) <(sudo ss -Htan | awk '{print $4}' | cut -d':' -f2 | sort -u) | shuf | head -n 1") + + +tablet1Port=$(echo "$AvaNode1Ports" | awk 'BEGIN{ RS="";FS="\n"}{print $1}') +tablet2Port=$(echo "$AvaNode2Ports" | awk 'BEGIN{ RS="";FS="\n"}{print $1}') +tablet3Port=$(echo "$AvaNode3Ports" | awk 'BEGIN{ RS="";FS="\n"}{print $1}') +ns1Port=$(echo "$AvaNode1Ports" | awk 'BEGIN{ RS="";FS="\n"}{print $2}') +ns2Port=$(echo "$AvaNode2Ports" | awk 'BEGIN{ RS="";FS="\n"}{print $2}') +apiserverPort=$(echo "$AvaNode1Ports" | awk 'BEGIN{ RS="";FS="\n"}{print $4}') +#taskmanagerPort=$(echo $AvaNode1Ports | awk '{print $5}') +zookeeperPort1=$(echo "$AvaNode1Ports" | awk 'BEGIN{ RS="";FS="\n"}{print $6}') +zookeeperPort2=$(echo "$AvaNode1Ports" | awk 'BEGIN{ RS="";FS="\n"}{print $7}') +zookeeperPort3=$(echo "$AvaNode1Ports" | awk 'BEGIN{ RS="";FS="\n"}{print $8}') + +# write addr to hosts +cat >"$rootPath"/conf/hosts<"$rootPath"/conf/openmldb-env.sh<out/openmldb_info.yaml<>"$rootPath"/conf/tablet.flags.template<"$rootPath"/conf/taskmanager.properties<$rootPath/test/integration-test/openmldb-test-java/openmldb-ecosystem/src/test/resources/kafka_test_cases.ymls< 0 + assert status.OK() status, unalive_cnt = self.get_unalive_cnt("test", table_name) assert status.OK() and unalive_cnt == 0 From a6d7322dd17b0be4ca5b0d85c23e9a5a65cbc011 Mon Sep 17 00:00:00 2001 From: dl239 Date: Sun, 20 Aug 2023 22:50:28 -0500 Subject: [PATCH 14/63] fix: create function (#3441) --- src/cmd/single_tablet_test.cc | 6 +++++- src/nameserver/name_server_impl.cc | 3 ++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/cmd/single_tablet_test.cc b/src/cmd/single_tablet_test.cc index be2335c8cc5..2c564b30546 100644 --- a/src/cmd/single_tablet_test.cc +++ b/src/cmd/single_tablet_test.cc @@ -65,7 +65,7 @@ TEST_P(DBSDKTest, CreateFunction) { sr = cli->sr; ::openmldb::sdk::SQLClusterRouter* sr_2 = nullptr; if (cs->IsClusterMode()) { - ::openmldb::sdk::ClusterOptions copt; + ::openmldb::sdk::ClusterOptions copt; copt.zk_cluster = mc.GetZkCluster(); copt.zk_path = mc.GetZkPath(); auto cur_cs = new ::openmldb::sdk::ClusterSDK(copt); @@ -75,6 +75,10 @@ TEST_P(DBSDKTest, CreateFunction) { ProcessSQLs(sr_2, {"set @@execute_mode = 'online'"}); } hybridse::sdk::Status status; + std::string err_cut2_sql = absl::StrCat("CREATE FUNCTION cut2(x STRING) RETURNS STRING " + "OPTIONS (FILE='libnotfound_udf.so');"); + sr->ExecuteSQL(err_cut2_sql, &status); + ASSERT_FALSE(status.IsOK()); std::string so_path = openmldb::test::GetParentDir(openmldb::test::GetExeDir()) + "/libtest_udf.so"; std::string cut2_sql = absl::StrCat("CREATE FUNCTION cut2(x STRING) RETURNS STRING " "OPTIONS (FILE='", so_path, "');"); diff --git a/src/nameserver/name_server_impl.cc b/src/nameserver/name_server_impl.cc index 550558b6132..3af886384e4 100644 --- a/src/nameserver/name_server_impl.cc +++ b/src/nameserver/name_server_impl.cc @@ -9735,8 +9735,9 @@ void NameServerImpl::CreateFunction(RpcController* controller, const CreateFunct std::string msg; if (!tablet->client_->CreateFunction(request->fun(), &msg)) { error_msgs.append("create function failed on " + tablet->client_->GetEndpoint() + ", reason: " + msg + ";"); + } else { + succ_tablets.emplace_back(tablet); } - succ_tablets.emplace_back(tablet); } // rollback and return, it's ok if tablet rollback failed if (succ_tablets.size() < tablets.size()) { From 5343823cf05e48de0331f6eff1945ee1c8578b1f Mon Sep 17 00:00:00 2001 From: dl239 Date: Sun, 20 Aug 2023 22:50:56 -0500 Subject: [PATCH 15/63] fix: fix nameserver fatal coredump (#3433) --- ...{data_collector_te1st.cc => data_collector_test.cc} | 10 ++++------ src/nameserver/name_server_impl.cc | 6 +++--- 2 files changed, 7 insertions(+), 9 deletions(-) rename src/datacollector/{data_collector_te1st.cc => data_collector_test.cc} (97%) diff --git a/src/datacollector/data_collector_te1st.cc b/src/datacollector/data_collector_test.cc similarity index 97% rename from src/datacollector/data_collector_te1st.cc rename to src/datacollector/data_collector_test.cc index efff494a8b5..d08cd4b71b8 100644 --- a/src/datacollector/data_collector_te1st.cc +++ b/src/datacollector/data_collector_test.cc @@ -104,9 +104,7 @@ TEST_F(DataCollectorTest, readSnapshot) { // read the manifest to get the offset api::Manifest manifest; std::string manifest_file = snapshot_hardlink_path + "MANIFEST"; - if (storage::Snapshot::GetLocalManifest(manifest_file, manifest) != 0) { - LOG(FATAL) << "get manifest failed"; - } + ASSERT_TRUE(storage::Snapshot::GetLocalManifest(manifest_file, manifest) == 0) << "get manifest failed"; LOG(INFO) << "manifest: " << manifest.ShortDebugString(); // mock table auto meta = GetTableSchema(6, 0); @@ -221,7 +219,7 @@ TEST_F(DataCollectorTest, taskAndRate) { datasync::AddSyncTaskRequest request; request.set_tid(6); request.set_pid(0); - request.set_mode(datasync::SyncMode::FULL); + request.set_mode(datasync::SyncMode::kFull); auto sync_point = request.mutable_sync_point(); sync_point->set_type(datasync::SyncType::kSNAPSHOT); // no pk&ts, means start from 0 @@ -236,7 +234,7 @@ TEST_F(DataCollectorTest, taskAndRate) { // sleep to call SyncOnce more sleep(1); // new token task but snapshot in db doesn't change, so the snapshot env is not changed - request.set_mode(datasync::SyncMode::INCREMENTAL_BY_TIMESTAMP); + request.set_mode(datasync::SyncMode::kIncrementalByTimestamp); request.set_start_ts(3); request.set_token("newer_one"); dc.AddSyncTask(nullptr, &request, &response, &closure); @@ -280,7 +278,7 @@ int main(int argc, char** argv) { srand(time(nullptr)); // TODO(hw): skip this test, cuz it needs sync tool. It's better to mock the sync tool - bool ok = true; + int ok = 0; // ok = RUN_ALL_TESTS(); // ::openmldb::sdk::mc_->Close(); return ok; diff --git a/src/nameserver/name_server_impl.cc b/src/nameserver/name_server_impl.cc index 3af886384e4..252fefd5917 100644 --- a/src/nameserver/name_server_impl.cc +++ b/src/nameserver/name_server_impl.cc @@ -5391,7 +5391,7 @@ void NameServerImpl::OnLocked() { CreateDatabaseOrExit(INTERNAL_DB); if (IsClusterMode()) { if (tablets_.size() < FLAGS_system_table_replica_num) { - LOG(FATAL) << "tablet num " << tablets_.size() << " is less then system table replica num " + LOG(ERROR) << "tablet num " << tablets_.size() << " is less then system table replica num " << FLAGS_system_table_replica_num; exit(1); } @@ -9596,7 +9596,7 @@ std::shared_ptr NameServerImpl::GetTablet(const std::string& endpoin void NameServerImpl::CreateDatabaseOrExit(const std::string& db) { auto status = CreateDatabase(db, true); if (!status.OK() && status.code != ::openmldb::base::ReturnCode::kDatabaseAlreadyExists) { - LOG(FATAL) << "create database failed. code=" << status.GetCode() << ", msg=" << status.GetMsg(); + LOG(ERROR) << "create database failed. code=" << status.GetCode() << ", msg=" << status.GetMsg(); exit(1); } } @@ -9604,7 +9604,7 @@ void NameServerImpl::CreateDatabaseOrExit(const std::string& db) { void NameServerImpl::CreateSystemTableOrExit(SystemTableType type) { auto status = CreateSystemTable(type); if (!status.OK()) { - LOG(FATAL) << "create system table " << GetSystemTableName(type) << " failed. code=" << status.GetCode() + LOG(ERROR) << "create system table " << GetSystemTableName(type) << " failed. code=" << status.GetCode() << ", msg=" << status.GetMsg(); exit(1); } From 29529f28ac5a4bd7f898d7ce4f36e6f50293c351 Mon Sep 17 00:00:00 2001 From: Wangerry Date: Mon, 21 Aug 2023 18:53:40 +0800 Subject: [PATCH 16/63] docs: fix typo (#3451) --- docs/zh/openmldb_sql/sql_difference.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/zh/openmldb_sql/sql_difference.md b/docs/zh/openmldb_sql/sql_difference.md index 4957f80649f..3118f8f71bb 100644 --- a/docs/zh/openmldb_sql/sql_difference.md +++ b/docs/zh/openmldb_sql/sql_difference.md @@ -54,7 +54,7 @@ | LAST JOIN | ✓ | ✓ | ✕ | | 子查询 / WITH 子句 | ✓ | ✓ | ✕ | -虽然在线请求模式无法支持支持 `WHERE` 子句,但是部分功能可以通过带有 `_where` 后缀的计算函数实现,比如 `count_where`, `avg_wgere` 等,详情查看[内置计算函数文档](functions_and_operators/Files/udfs_8h.md)。 +虽然在线请求模式无法支持 `WHERE` 子句,但是部分功能可以通过带有 `_where` 后缀的计算函数实现,比如 `count_where`, `avg_where` 等,详情查看[内置计算函数文档](functions_and_operators/Files/udfs_8h.md)。 ### LIMIT 子句 From 7aa1de757253e9e615b6889e9941253388ac96a4 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 22 Aug 2023 16:48:23 +0800 Subject: [PATCH 17/63] docs(udf): upgrade udf list (#3442) Co-authored-by: aceforeverd --- .../functions_and_operators/Files/udfs_8h.md | 50 +++++++++++++++++++ .../functions_and_operators/Files/udfs_8h.md | 50 +++++++++++++++++++ 2 files changed, 100 insertions(+) diff --git a/docs/en/reference/sql/functions_and_operators/Files/udfs_8h.md b/docs/en/reference/sql/functions_and_operators/Files/udfs_8h.md index fe8bf35b6b4..ac96c6bfc3f 100644 --- a/docs/en/reference/sql/functions_and_operators/Files/udfs_8h.md +++ b/docs/en/reference/sql/functions_and_operators/Files/udfs_8h.md @@ -57,6 +57,7 @@ title: udfs/udfs.h | **[first_value](/openmldb_sql/functions_and_operators/Files/udfs_8h.md#function-first-value)**()|
Returns the value of expr from the latest row (last row) of the window frame. | | **[float](/openmldb_sql/functions_and_operators/Files/udfs_8h.md#function-float)**()|
Cast string expression to float. | | **[floor](/openmldb_sql/functions_and_operators/Files/udfs_8h.md#function-floor)**()|
Return the largest integer value not less than the expr. | +| **[get_json_object](/openmldb_sql/functions_and_operators/Files/udfs_8h.md#function-get-json-object)**()|
Extracts a JSON object from [JSON Pointer](https://datatracker.ietf.org/doc/html/rfc6901)| | **[hash64](/openmldb_sql/functions_and_operators/Files/udfs_8h.md#function-hash64)**()|
Returns a hash value of the arguments. It is not a cryptographic hash function and should not be used as such. | | **[hex](/openmldb_sql/functions_and_operators/Files/udfs_8h.md#function-hex)**()|
Convert integer to hexadecimal. | | **[hour](/openmldb_sql/functions_and_operators/Files/udfs_8h.md#function-hour)**()|
Return the hour for a timestamp. | @@ -1716,6 +1717,55 @@ SELECT FLOOR(1.23); * [`bool`] * [`number`] +### function get_json_object + +```cpp +get_json_object() +``` + +**Description**: + +Extracts a JSON object from [JSON Pointer](https://datatracker.ietf.org/doc/html/rfc6901) + +**Parameters**: + + * **expr** A string expression contains well formed JSON + * **path** A string expression of JSON string representation from [JSON Pointer](https://datatracker.ietf.org/doc/html/rfc6901) + + +**Since**: +0.9.0 + + +NOTE JSON string is not fully validated. Which means that the function may still returns values even though returned string does not valid for JSON. It's your responsibility to make sure input string is valid JSON + + +Example: + +```sql + +select get_json_object('{"boo": "baz"}', "/boo") +-- baz + +select get_json_object('{"boo": [1, 2]}', "/boo/0") +-- 1 + +select get_json_object('{"m~n": 1}', "/m~0n") +-- 1 + +select get_json_object('{"m/n": 1}', "/m~1n") +-- 1 + +select get_json_object('{"foo": {"bar": bz}}', "/foo") +-- {"bar": bz} +--- returns value even input JSON is not a valid JSON +``` + + +**Supported Types**: + +* [`string`, `string`] + ### function hash64 ```cpp diff --git a/docs/zh/openmldb_sql/functions_and_operators/Files/udfs_8h.md b/docs/zh/openmldb_sql/functions_and_operators/Files/udfs_8h.md index fe8bf35b6b4..ac96c6bfc3f 100644 --- a/docs/zh/openmldb_sql/functions_and_operators/Files/udfs_8h.md +++ b/docs/zh/openmldb_sql/functions_and_operators/Files/udfs_8h.md @@ -57,6 +57,7 @@ title: udfs/udfs.h | **[first_value](/openmldb_sql/functions_and_operators/Files/udfs_8h.md#function-first-value)**()|
Returns the value of expr from the latest row (last row) of the window frame. | | **[float](/openmldb_sql/functions_and_operators/Files/udfs_8h.md#function-float)**()|
Cast string expression to float. | | **[floor](/openmldb_sql/functions_and_operators/Files/udfs_8h.md#function-floor)**()|
Return the largest integer value not less than the expr. | +| **[get_json_object](/openmldb_sql/functions_and_operators/Files/udfs_8h.md#function-get-json-object)**()|
Extracts a JSON object from [JSON Pointer](https://datatracker.ietf.org/doc/html/rfc6901)| | **[hash64](/openmldb_sql/functions_and_operators/Files/udfs_8h.md#function-hash64)**()|
Returns a hash value of the arguments. It is not a cryptographic hash function and should not be used as such. | | **[hex](/openmldb_sql/functions_and_operators/Files/udfs_8h.md#function-hex)**()|
Convert integer to hexadecimal. | | **[hour](/openmldb_sql/functions_and_operators/Files/udfs_8h.md#function-hour)**()|
Return the hour for a timestamp. | @@ -1716,6 +1717,55 @@ SELECT FLOOR(1.23); * [`bool`] * [`number`] +### function get_json_object + +```cpp +get_json_object() +``` + +**Description**: + +Extracts a JSON object from [JSON Pointer](https://datatracker.ietf.org/doc/html/rfc6901) + +**Parameters**: + + * **expr** A string expression contains well formed JSON + * **path** A string expression of JSON string representation from [JSON Pointer](https://datatracker.ietf.org/doc/html/rfc6901) + + +**Since**: +0.9.0 + + +NOTE JSON string is not fully validated. Which means that the function may still returns values even though returned string does not valid for JSON. It's your responsibility to make sure input string is valid JSON + + +Example: + +```sql + +select get_json_object('{"boo": "baz"}', "/boo") +-- baz + +select get_json_object('{"boo": [1, 2]}', "/boo/0") +-- 1 + +select get_json_object('{"m~n": 1}', "/m~0n") +-- 1 + +select get_json_object('{"m/n": 1}', "/m~1n") +-- 1 + +select get_json_object('{"foo": {"bar": bz}}', "/foo") +-- {"bar": bz} +--- returns value even input JSON is not a valid JSON +``` + + +**Supported Types**: + +* [`string`, `string`] + ### function hash64 ```cpp From c19aa7f901958dcce1ae99b50acdc739147e37ed Mon Sep 17 00:00:00 2001 From: HuangWei Date: Wed, 23 Aug 2023 17:06:36 +0800 Subject: [PATCH 18/63] feat: support inherit env variables for spark (#3450) --- docs/zh/deploy/conf.md | 9 ++++ .../taskmanager/config/TaskManagerConfig.java | 46 +++++++++---------- .../taskmanager/spark/SparkJobManager.scala | 17 +++++-- 3 files changed, 45 insertions(+), 27 deletions(-) diff --git a/docs/zh/deploy/conf.md b/docs/zh/deploy/conf.md index 3c20d83fe83..f9283a3b748 100644 --- a/docs/zh/deploy/conf.md +++ b/docs/zh/deploy/conf.md @@ -271,6 +271,15 @@ hadoop.conf.dir= Spark Config中重点关注的配置如下: +```{note} +理解配置项与环境变量的关系。 + +TaskManager会通过SparkSubmit创建Spark进程,因此环境变量不会简单的直接继承。举例说明:在0.8.2及以前的版本中,为了Spark进程可以读写HADOOP,可以连接YARN集群,需要配置环境变量`HADOOP_CONF_DIR`。在以后的版本中,可以通过配置项`hadoop.conf.dir`指定Hadoop配置文件所在目录,TaskManager会将此项作为环境变量传递给Spark进程。但最优先的是Spark自身的spark-env.sh,如果此处已经配置好了,TaskManager无法覆盖此项。 +所以,优先级为:spark-env.sh > TaskManager配置 > 当前环境变量HADOOP_CONF_DIR。 + +其中,`spark.home`仅用于TaskManager来识别Spark安装目录,不会传递给Spark进程。`hadoop.conf.dir`, `hadoop.user.name` 将会传递给Spark进程。如果有其他的变量需要传递,需要修改代码。 +``` + #### spark.home `spark.home`配置为Spark安装目录,TaskManager会使用该目录下的Spark执行离线任务。通常配置为下载的[OpenMLDB Spark 发行版](../../tutorial/openmldbspark_distribution.md)解压后的目录。 diff --git a/java/openmldb-taskmanager/src/main/java/com/_4paradigm/openmldb/taskmanager/config/TaskManagerConfig.java b/java/openmldb-taskmanager/src/main/java/com/_4paradigm/openmldb/taskmanager/config/TaskManagerConfig.java index e37a6ab16fa..3be3bcf39ee 100644 --- a/java/openmldb-taskmanager/src/main/java/com/_4paradigm/openmldb/taskmanager/config/TaskManagerConfig.java +++ b/java/openmldb-taskmanager/src/main/java/com/_4paradigm/openmldb/taskmanager/config/TaskManagerConfig.java @@ -132,18 +132,12 @@ public static void doParse() throws ConfigException { } } - SPARK_HOME = prop.getProperty("spark.home", ""); - if (SPARK_HOME.isEmpty()) { - try { - if(System.getenv("SPARK_HOME") == null) { - throw new ConfigException("spark.home", "should set config 'spark.home' or environment variable 'SPARK_HOME'"); - } else { - SPARK_HOME = System.getenv("SPARK_HOME"); - } - } catch (Exception e) { - throw new ConfigException("spark.home", "should set environment variable 'SPARK_HOME' if 'spark.home' is null"); - } + SPARK_HOME = firstNonEmpty(prop.getProperty("spark.home"), System.getenv("SPARK_HOME")); + // isEmpty checks null and empty + if(isEmpty(SPARK_HOME)) { + throw new ConfigException("spark.home", "should set config 'spark.home' or environment variable 'SPARK_HOME'"); } + // TODO: Check if we can get spark-submit PREFETCH_JOBID_NUM = Integer.parseInt(prop.getProperty("prefetch.jobid.num", "1")); @@ -242,19 +236,12 @@ public static void doParse() throws ConfigException { } } - HADOOP_USER_NAME = prop.getProperty("hadoop.user.name", "root"); + // TODO(hw): need default root? + HADOOP_USER_NAME = firstNonEmpty(prop.getProperty("hadoop.user.name"), System.getenv("HADOOP_USER_NAME")); - HADOOP_CONF_DIR = prop.getProperty("hadoop.conf.dir", ""); - if (isYarn && HADOOP_CONF_DIR.isEmpty()) { - try { - if(System.getenv("HADOOP_CONF_DIR") == null) { - throw new ConfigException("hadoop.conf.dir", "should set config 'hadoop.conf.dir' or environment variable 'HADOOP_CONF_DIR'"); - } else { - HADOOP_CONF_DIR = System.getenv("HADOOP_CONF_DIR"); - } - } catch (Exception e) { - throw new ConfigException("hadoop.conf.dir", "should set environment variable 'HADOOP_CONF_DIR' if 'hadoop.conf.dir' is null"); - } + HADOOP_CONF_DIR = firstNonEmpty(prop.getProperty("hadoop.conf.dir"), System.getenv("HADOOP_CONF_DIR")); + if (isYarn && isEmpty(HADOOP_CONF_DIR)) { + throw new ConfigException("hadoop.conf.dir", "should set config 'hadoop.conf.dir' or environment variable 'HADOOP_CONF_DIR'"); } // TODO: Check if we can get core-site.xml @@ -276,4 +263,17 @@ public static boolean isYarnCluster() throws ConfigException { parse(); return SPARK_MASTER.equals("yarn") || SPARK_MASTER.equals("yarn-cluster"); } + + // ref org.apache.spark.launcher.CommandBuilderUtils + public static String firstNonEmpty(String... strings) { + for (String s : strings) { + if (!isEmpty(s)) { + return s; + } + } + return null; + } + public static boolean isEmpty(String s) { + return s == null || s.isEmpty(); + } } diff --git a/java/openmldb-taskmanager/src/main/scala/com/_4paradigm/openmldb/taskmanager/spark/SparkJobManager.scala b/java/openmldb-taskmanager/src/main/scala/com/_4paradigm/openmldb/taskmanager/spark/SparkJobManager.scala index 20b2938b74e..8d2410fd13a 100644 --- a/java/openmldb-taskmanager/src/main/scala/com/_4paradigm/openmldb/taskmanager/spark/SparkJobManager.scala +++ b/java/openmldb-taskmanager/src/main/scala/com/_4paradigm/openmldb/taskmanager/spark/SparkJobManager.scala @@ -36,12 +36,21 @@ object SparkJobManager { * @return the SparkLauncher object */ def createSparkLauncher(mainClass: String): SparkLauncher = { - - val launcher = new SparkLauncher() + val env: java.util.Map[String, String] = new java.util.HashMap[String, String] + // config may empty, need check + if (!TaskManagerConfig.isEmpty(TaskManagerConfig.HADOOP_CONF_DIR)) { + env.put("HADOOP_CONF_DIR", TaskManagerConfig.HADOOP_CONF_DIR) + } + // env.put("YARN_CONF_DIR", TaskManagerConfig.HADOOP_CONF_DIR) // unused now + if (!TaskManagerConfig.isEmpty(TaskManagerConfig.HADOOP_USER_NAME)){ + env.put("HADOOP_USER_NAME", TaskManagerConfig.HADOOP_USER_NAME) + } + + val launcher = new SparkLauncher(env) .setAppResource(TaskManagerConfig.BATCHJOB_JAR_PATH) .setMainClass(mainClass) - if (TaskManagerConfig.SPARK_HOME != null && TaskManagerConfig.SPARK_HOME.nonEmpty) { + if (!TaskManagerConfig.isEmpty(TaskManagerConfig.SPARK_HOME)) { launcher.setSparkHome(TaskManagerConfig.SPARK_HOME) } @@ -57,7 +66,7 @@ object SparkJobManager { } } - if (TaskManagerConfig.SPARK_YARN_JARS != null && TaskManagerConfig.SPARK_YARN_JARS.nonEmpty) { + if (!TaskManagerConfig.isEmpty(TaskManagerConfig.SPARK_YARN_JARS)) { launcher.setConf("spark.yarn.jars", TaskManagerConfig.SPARK_YARN_JARS) } From 1b8d596504a283269164b811d4896665a483a10f Mon Sep 17 00:00:00 2001 From: HuangWei Date: Wed, 23 Aug 2023 17:09:00 +0800 Subject: [PATCH 19/63] feat: support write single in spark connector (#3443) --- .../reference/sql/dml/LOAD_DATA_STATEMENT.md | 23 ++-- .../openmldb_sql/dml/LOAD_DATA_STATEMENT.md | 23 ++-- .../openmldb/batch/nodes/LoadDataPlan.scala | 5 +- .../openmldb/batch/utils/HybridseUtil.scala | 1 + .../openmldb/spark/OpenmldbSource.java | 9 +- .../openmldb/spark/OpenmldbTable.java | 7 +- .../spark/write/OpenmldbDataSingleWriter.java | 111 ++++++++++++++++++ .../spark/write/OpenmldbDataWriter.java | 12 +- .../write/OpenmldbDataWriterFactory.java | 3 + .../spark/write/OpenmldbWriteConfig.java | 5 +- 10 files changed, 164 insertions(+), 35 deletions(-) create mode 100644 java/openmldb-spark-connector/src/main/java/com/_4paradigm/openmldb/spark/write/OpenmldbDataSingleWriter.java diff --git a/docs/en/reference/sql/dml/LOAD_DATA_STATEMENT.md b/docs/en/reference/sql/dml/LOAD_DATA_STATEMENT.md index 403442af6e2..8210b92217c 100644 --- a/docs/en/reference/sql/dml/LOAD_DATA_STATEMENT.md +++ b/docs/en/reference/sql/dml/LOAD_DATA_STATEMENT.md @@ -43,17 +43,18 @@ Supports loading data from Hive, but needs extra settings, see [Hive Support](#H The following table introduces the parameters of `LOAD DATA INFILE`. -| Parameter | Type | Default Value | Note | -| ---------- | ------- | ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| delimiter | String | , | It defines the column separator, the default value is `,`. | -| header | Boolean | true | It indicates that whether the table to import has a header. If the value is `true`, the table has a header. | -| null_value | String | null | It defines the string that will be used to replace the `NULL` value when loading data. | -| format | String | csv | It defines the format of the input file.
`csv` is the default format.
`parquet` format is supported in the cluster version. | -| quote | String | " | It defines the string surrounding the input data. The string length should be <= 1.
load_mode='cluster': default is `"`, the content surrounded by a pair of the quote characters will be parsed as a whole. For example, if the surrounding string is `"#"` then the original data like `1, 1.0, #This is a string, with comma#, normal_string` will be converted to four fields. The first field is an integer 1, the second is a float 1.0, the third field is a string "This is a string, with comma" and the 4th is "normal_string" even it's no quote.
load_mode='local': default is `\0`, which means that the string surrounding the input data is empty. | -| mode | String | "error_if_exists" | It defines the input mode.
`error_if_exists` is the default mode which indicates that an error will be thrown out if the offline table already has data. This input mode is only supported by the offline execution mode.
`overwrite` indicates that if the file already exists, the data will overwrite the contents of the original file. This input mode is only supported by the offline execution mode.
`append` indicates that if the table already exists, the data will be appended to the original table. Both offline and online execution modes support this input mode. | -| deep_copy | Boolean | true | It defines whether `deep_copy` is used. Only offline load supports `deep_copy=false`, you can specify the `INFILE` path as the offline storage address of the table to avoid hard copy. | -| load_mode | String | cluster | `load_mode='local'` only supports loading the `csv` local files into the `online` storage; It loads the data synchronously by the client process.
`load_mode='cluster'` only supports the cluster version. It loads the data via Spark synchronously or asynchronously. | -| thread | Integer | 1 | It only works for data loading locally, i.e., `load_mode='local'` or in the standalone version; It defines the number of threads used for data loading. The max value is `50`. | +| Parameter | Type | Default Value | Note | +| ----------- | ------- | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| delimiter | String | , | It defines the column separator, the default value is `,`. | +| header | Boolean | true | It indicates that whether the table to import has a header. If the value is `true`, the table has a header. | +| null_value | String | null | It defines the string that will be used to replace the `NULL` value when loading data. | +| format | String | csv | It defines the format of the input file.
`csv` is the default format.
`parquet` format is supported in the cluster version. | +| quote | String | " | It defines the string surrounding the input data. The string length should be <= 1.
load_mode='cluster': default is `"`, the content surrounded by a pair of the quote characters will be parsed as a whole. For example, if the surrounding string is `"#"` then the original data like `1, 1.0, #This is a string, with comma#, normal_string` will be converted to four fields. The first field is an integer 1, the second is a float 1.0, the third field is a string "This is a string, with comma" and the 4th is "normal_string" even it's no quote.
load_mode='local': default is `\0`, which means that the string surrounding the input data is empty. | +| mode | String | "error_if_exists" | It defines the input mode.
`error_if_exists` is the default mode which indicates that an error will be thrown out if the offline table already has data. This input mode is only supported by the offline execution mode.
`overwrite` indicates that if the file already exists, the data will overwrite the contents of the original file. This input mode is only supported by the offline execution mode.
`append` indicates that if the table already exists, the data will be appended to the original table. Both offline and online execution modes support this input mode. | +| deep_copy | Boolean | true | It defines whether `deep_copy` is used. Only offline load supports `deep_copy=false`, you can specify the `INFILE` path as the offline storage address of the table to avoid hard copy. | +| load_mode | String | cluster | `load_mode='local'` only supports loading the `csv` local files into the `online` storage; It loads the data synchronously by the client process.
`load_mode='cluster'` only supports the cluster version. It loads the data via Spark synchronously or asynchronously. | +| thread | Integer | 1 | It only works for data loading locally, i.e., `load_mode='local'` or in the standalone version; It defines the number of threads used for data loading. The max value is `50`. | +| writer_type | String | single | The writer type for online loading in cluster mode, `single` or `batch`, default is `single`。`single` means write to cluster when reading, cost less memory. `batch` will read the whole rdd partition, check all data is right to pack, then write to cluster, it needs more memory. In some cases, `batch` is better to get the unwritten data, and retry the unwritten part | ```{note} - In the cluster version, the specified execution mode (defined by `execute_mode`) determines whether to import data to online or offline storage when the `LOAD DATA INFILE` statement is executed. For the standalone version, there is no difference in storage mode and the `deep_copy` option is not supported. diff --git a/docs/zh/openmldb_sql/dml/LOAD_DATA_STATEMENT.md b/docs/zh/openmldb_sql/dml/LOAD_DATA_STATEMENT.md index 0bea7f2f98d..6be6d934f5f 100644 --- a/docs/zh/openmldb_sql/dml/LOAD_DATA_STATEMENT.md +++ b/docs/zh/openmldb_sql/dml/LOAD_DATA_STATEMENT.md @@ -40,17 +40,18 @@ FilePathPattern 下表展示了`LOAD DATA INFILE`语句的配置项。 -| 配置项 | 类型 | 默认值 | 描述 | -| ---------- | ------- | ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| delimiter | String | , | 列分隔符,默认为`,`。 | -| header | Boolean | true | 是否包含表头, 默认为`true` 。 | -| null_value | String | null | NULL值,默认填充`"null"`。加载时,遇到null_value的字符串将被转换为`"null"`,插入表中。 | -| format | String | csv | 导入文件的格式:
`csv`: 不显示指明format时,默认为该值。
`parquet`: 集群版还支持导入parquet格式文件,单机版不支持。 | -| quote | String | " | 输入数据的包围字符串。字符串长度<=1。
load_mode=`cluster`默认为双引号`"`。配置包围字符后,被包围字符包围的内容将作为一个整体解析。例如,当配置包围字符串为"#"时, `1, 1.0, #This is a string field, even there is a comma#, normal_string`将为解析为三个filed,第一个是整数1,第二个是浮点1.0,第三个是一个字符串,第四个虽然没有quote,但也是一个字符串。
**local_mode=`local`默认为`\0`,不处理包围字符。**| -| mode | String | "error_if_exists" | 导入模式:
`error_if_exists`: 仅离线模式可用,若离线表已有数据则报错。
`overwrite`: 仅离线模式可用,数据将覆盖离线表数据。
`append`:离线在线均可用,若文件已存在,数据将追加到原文件后面。 | -| deep_copy | Boolean | true | `deep_copy=false`仅支持离线load, 可以指定`INFILE` Path为该表的离线存储地址,从而不需要硬拷贝。 | -| load_mode | String | cluster | `load_mode='local'`仅支持从csv本地文件导入在线存储, 它通过本地客户端同步插入数据;
`load_mode='cluster'`仅支持集群版, 通过spark插入数据,支持同步或异步模式 | -| thread | Integer | 1 | 仅在本地文件导入时生效,即`load_mode='local'`或者单机版,表示本地插入数据的线程数。 最大值为`50`。 | +| 配置项 | 类型 | 默认值 | 描述 | +| ----------- | ------- | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| delimiter | String | , | 列分隔符,默认为`,`。 | +| header | Boolean | true | 是否包含表头, 默认为`true` 。 | +| null_value | String | null | NULL值,默认填充`"null"`。加载时,遇到null_value的字符串将被转换为`"null"`,插入表中。 | +| format | String | csv | 导入文件的格式:
`csv`: 不显示指明format时,默认为该值。
`parquet`: 集群版还支持导入parquet格式文件,单机版不支持。 | +| quote | String | " | 输入数据的包围字符串。字符串长度<=1。
load_mode=`cluster`默认为双引号`"`。配置包围字符后,被包围字符包围的内容将作为一个整体解析。例如,当配置包围字符串为"#"时, `1, 1.0, #This is a string field, even there is a comma#, normal_string`将为解析为三个filed,第一个是整数1,第二个是浮点1.0,第三个是一个字符串,第四个虽然没有quote,但也是一个字符串。
**local_mode=`local`默认为`\0`,不处理包围字符。** | +| mode | String | "error_if_exists" | 导入模式:
`error_if_exists`: 仅离线模式可用,若离线表已有数据则报错。
`overwrite`: 仅离线模式可用,数据将覆盖离线表数据。
`append`:离线在线均可用,若文件已存在,数据将追加到原文件后面。 | +| deep_copy | Boolean | true | `deep_copy=false`仅支持离线load, 可以指定`INFILE` Path为该表的离线存储地址,从而不需要硬拷贝。 | +| load_mode | String | cluster | `load_mode='local'`仅支持从csv本地文件导入在线存储, 它通过本地客户端同步插入数据;
`load_mode='cluster'`仅支持集群版, 通过spark插入数据,支持同步或异步模式 | +| thread | Integer | 1 | 仅在本地文件导入时生效,即`load_mode='local'`或者单机版,表示本地插入数据的线程数。 最大值为`50`。 | +| writer_type | String | single | 集群版在线导入中插入数据的writer类型。可选值为`single`和`batch`,默认为`single`。`single`表示数据即读即写,节省内存。`batch`则是将整个rdd分区读完,确认数据类型有效性后,再写入集群,需要更多内存。在部分情况下,`batch`模式有利于筛选未写入的数据,方便重试这部分数据。 | ```{note} 在集群版中,`LOAD DATA INFILE`语句会根据当前执行模式(execute_mode)决定将数据导入到在线或离线存储。单机版中没有存储区别,只会导入到在线存储中,同时也不支持`deep_copy`选项。 diff --git a/java/openmldb-batch/src/main/scala/com/_4paradigm/openmldb/batch/nodes/LoadDataPlan.scala b/java/openmldb-batch/src/main/scala/com/_4paradigm/openmldb/batch/nodes/LoadDataPlan.scala index 6e962675e53..36398d199cf 100644 --- a/java/openmldb-batch/src/main/scala/com/_4paradigm/openmldb/batch/nodes/LoadDataPlan.scala +++ b/java/openmldb-batch/src/main/scala/com/_4paradigm/openmldb/batch/nodes/LoadDataPlan.scala @@ -55,12 +55,13 @@ object LoadDataPlan { logger.info("write data to storage {}, writer[mode {}], is deep? {}", storage, mode, deepCopy.toString) if (storage == "online") { // Import online data require(deepCopy && mode == "append", "import to online storage, can't do soft copy, and mode must be append") - + val writeType = extra.get("writer_type").get val writeOptions = Map( "db" -> db, "table" -> table, "zkCluster" -> ctx.getConf.openmldbZkCluster, - "zkPath" -> ctx.getConf.openmldbZkRootPath + "zkPath" -> ctx.getConf.openmldbZkRootPath, + "writerType" -> writeType ) df.write.options(writeOptions).format("openmldb").mode(mode).save() } else { // Import offline data diff --git a/java/openmldb-batch/src/main/scala/com/_4paradigm/openmldb/batch/utils/HybridseUtil.scala b/java/openmldb-batch/src/main/scala/com/_4paradigm/openmldb/batch/utils/HybridseUtil.scala index 3a5ca64732d..4e0edd9b8a2 100644 --- a/java/openmldb-batch/src/main/scala/com/_4paradigm/openmldb/batch/utils/HybridseUtil.scala +++ b/java/openmldb-batch/src/main/scala/com/_4paradigm/openmldb/batch/utils/HybridseUtil.scala @@ -251,6 +251,7 @@ object HybridseUtil { // only for select into, "" means N/A extraOptions += ("coalesce" -> parseOption(getOptionFromNode(node, "coalesce"), "0", getIntOrDefault)) + extraOptions += ("writer_type") -> parseOption(getOptionFromNode(node, "writer_type"), "single", getStringOrDefault) (format, options.toMap, mode, extraOptions.toMap) } diff --git a/java/openmldb-spark-connector/src/main/java/com/_4paradigm/openmldb/spark/OpenmldbSource.java b/java/openmldb-spark-connector/src/main/java/com/_4paradigm/openmldb/spark/OpenmldbSource.java index d31fc377dd0..5595e313af5 100644 --- a/java/openmldb-spark-connector/src/main/java/com/_4paradigm/openmldb/spark/OpenmldbSource.java +++ b/java/openmldb-spark-connector/src/main/java/com/_4paradigm/openmldb/spark/OpenmldbSource.java @@ -37,6 +37,9 @@ public class OpenmldbSource implements TableProvider, DataSourceRegister { private String dbName; private String tableName; private SdkOption option = null; + // single: insert when read one row + // batch: insert when commit(after read a whole partition) + private String writerType = "single"; @Override public StructType inferSchema(CaseInsensitiveStringMap options) { @@ -63,12 +66,16 @@ public StructType inferSchema(CaseInsensitiveStringMap options) { option.setEnableDebug(Boolean.valueOf(debug)); } + if (options.containsKey("writerType")) { + writerType = options.get("writerType"); + } + return null; } @Override public Table getTable(StructType schema, Transform[] partitioning, Map properties) { - return new OpenmldbTable(dbName, tableName, option); + return new OpenmldbTable(dbName, tableName, option, writerType); } @Override diff --git a/java/openmldb-spark-connector/src/main/java/com/_4paradigm/openmldb/spark/OpenmldbTable.java b/java/openmldb-spark-connector/src/main/java/com/_4paradigm/openmldb/spark/OpenmldbTable.java index 8f6b588e84f..0cf98b7d19e 100644 --- a/java/openmldb-spark-connector/src/main/java/com/_4paradigm/openmldb/spark/OpenmldbTable.java +++ b/java/openmldb-spark-connector/src/main/java/com/_4paradigm/openmldb/spark/OpenmldbTable.java @@ -48,25 +48,28 @@ public class OpenmldbTable implements SupportsWrite, SupportsRead { private final String dbName; private final String tableName; private final SdkOption option; + private final String writerType; private SqlExecutor executor = null; private Set capabilities; - public OpenmldbTable(String dbName, String tableName, SdkOption option) { + public OpenmldbTable(String dbName, String tableName, SdkOption option, String writerType) { this.dbName = dbName; this.tableName = tableName; this.option = option; + this.writerType = writerType; try { this.executor = new SqlClusterExecutor(option); // no need to check table exists, schema() will check it later } catch (SqlException e) { e.printStackTrace(); } + // TODO: cache schema & delete executor? } @Override public WriteBuilder newWriteBuilder(LogicalWriteInfo info) { - OpenmldbWriteConfig config = new OpenmldbWriteConfig(dbName, tableName, option); + OpenmldbWriteConfig config = new OpenmldbWriteConfig(dbName, tableName, option, writerType); return new OpenmldbWriteBuilder(config, info); } diff --git a/java/openmldb-spark-connector/src/main/java/com/_4paradigm/openmldb/spark/write/OpenmldbDataSingleWriter.java b/java/openmldb-spark-connector/src/main/java/com/_4paradigm/openmldb/spark/write/OpenmldbDataSingleWriter.java new file mode 100644 index 00000000000..fd767d4eebb --- /dev/null +++ b/java/openmldb-spark-connector/src/main/java/com/_4paradigm/openmldb/spark/write/OpenmldbDataSingleWriter.java @@ -0,0 +1,111 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com._4paradigm.openmldb.spark.write; + +import com._4paradigm.openmldb.sdk.Schema; +import com._4paradigm.openmldb.sdk.SdkOption; +import com._4paradigm.openmldb.sdk.SqlException; +import com._4paradigm.openmldb.sdk.impl.SqlClusterExecutor; +import com.google.common.base.Preconditions; +import org.apache.spark.sql.catalyst.InternalRow; +import org.apache.spark.sql.connector.write.DataWriter; +import org.apache.spark.sql.connector.write.WriterCommitMessage; + +import java.io.IOException; +import java.sql.Date; +import java.sql.PreparedStatement; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; +import java.sql.Timestamp; +import java.sql.Types; + +public class OpenmldbDataSingleWriter implements DataWriter { + private final int partitionId; + private final long taskId; + private PreparedStatement preparedStatement = null; + + public OpenmldbDataSingleWriter(OpenmldbWriteConfig config, int partitionId, long taskId) { + try { + SdkOption option = new SdkOption(); + option.setZkCluster(config.zkCluster); + option.setZkPath(config.zkPath); + SqlClusterExecutor executor = new SqlClusterExecutor(option); + String dbName = config.dbName; + String tableName = config.tableName; + + Schema schema = executor.getTableSchema(dbName, tableName); + // create insert placeholder + StringBuilder insert = new StringBuilder("insert into " + tableName + " values(?"); + for (int i = 1; i < schema.getColumnList().size(); i++) { + insert.append(",?"); + } + insert.append(");"); + preparedStatement = executor.getInsertPreparedStmt(dbName, insert.toString()); + } catch (SQLException | SqlException e) { + e.printStackTrace(); + } + + this.partitionId = partitionId; + this.taskId = taskId; + } + + @Override + public void write(InternalRow record) throws IOException { + try { + // record to openmldb row + ResultSetMetaData metaData = preparedStatement.getMetaData(); + Preconditions.checkState(record.numFields() == metaData.getColumnCount()); + OpenmldbDataWriter.addRow(record, preparedStatement); + preparedStatement.execute(); + } catch (Exception e) { + throw new IOException("write row to openmldb failed on " + record, e); + } + } + + @Override + public WriterCommitMessage commit() throws IOException { + try { + preparedStatement.close(); + } catch (SQLException e) { + e.printStackTrace(); + throw new IOException("commit error", e); + } + // TODO(hw): need to return new WriterCommitMessageImpl(partitionId, taskId); ? + return null; + } + + @Override + public void abort() throws IOException { + try { + preparedStatement.close(); + } catch (SQLException e) { + e.printStackTrace(); + throw new IOException("abort error", e); + } + } + + @Override + public void close() throws IOException { + try { + preparedStatement.close(); + } catch (SQLException e) { + e.printStackTrace(); + throw new IOException("close error", e); + } + } +} diff --git a/java/openmldb-spark-connector/src/main/java/com/_4paradigm/openmldb/spark/write/OpenmldbDataWriter.java b/java/openmldb-spark-connector/src/main/java/com/_4paradigm/openmldb/spark/write/OpenmldbDataWriter.java index 50f2a0f4b5c..3cb7632ae0b 100644 --- a/java/openmldb-spark-connector/src/main/java/com/_4paradigm/openmldb/spark/write/OpenmldbDataWriter.java +++ b/java/openmldb-spark-connector/src/main/java/com/_4paradigm/openmldb/spark/write/OpenmldbDataWriter.java @@ -73,11 +73,11 @@ public void write(InternalRow record) throws IOException { addRow(record, preparedStatement); preparedStatement.addBatch(); } catch (Exception e) { - throw new IOException("convert to openmldb row failed on " + record + ", err: "+ e, e); + throw new IOException("convert to openmldb row failed on " + record, e); } } - private void addRow(InternalRow record, PreparedStatement preparedStatement) throws SQLException { + static void addRow(InternalRow record, PreparedStatement preparedStatement) throws SQLException { // idx in preparedStatement starts from 1 ResultSetMetaData metaData = preparedStatement.getMetaData(); for (int i = 0; i < record.numFields(); i++) { @@ -131,13 +131,13 @@ public WriterCommitMessage commit() throws IOException { for(int i = 0; i < rc.length; i++) { int code = rc[i]; if (code < 0) { - throw new SQLException("insert failed. result code: " + code); + throw new SQLException("insert failed on " + i + ", result code " + code); } } preparedStatement.close(); } catch (SQLException e) { e.printStackTrace(); - throw new IOException(e.getMessage()); + throw new IOException("commit(write to openmldb) error", e); } // TODO(hw): need to return new WriterCommitMessageImpl(partitionId, taskId); ? return null; @@ -149,7 +149,7 @@ public void abort() throws IOException { preparedStatement.close(); } catch (SQLException e) { e.printStackTrace(); - throw new IOException(e.getMessage()); + throw new IOException("abort error", e); } } @@ -159,7 +159,7 @@ public void close() throws IOException { preparedStatement.close(); } catch (SQLException e) { e.printStackTrace(); - throw new IOException(e.getMessage()); + throw new IOException("close error", e); } } } diff --git a/java/openmldb-spark-connector/src/main/java/com/_4paradigm/openmldb/spark/write/OpenmldbDataWriterFactory.java b/java/openmldb-spark-connector/src/main/java/com/_4paradigm/openmldb/spark/write/OpenmldbDataWriterFactory.java index 9f52a08fe4d..96e78979b2f 100644 --- a/java/openmldb-spark-connector/src/main/java/com/_4paradigm/openmldb/spark/write/OpenmldbDataWriterFactory.java +++ b/java/openmldb-spark-connector/src/main/java/com/_4paradigm/openmldb/spark/write/OpenmldbDataWriterFactory.java @@ -30,6 +30,9 @@ public OpenmldbDataWriterFactory(OpenmldbWriteConfig config) { @Override public DataWriter createWriter(int partitionId, long taskId) { + if (!config.writerType.equals("batch")) { + return new OpenmldbDataSingleWriter(config, partitionId, taskId); + } return new OpenmldbDataWriter(config, partitionId, taskId); } } diff --git a/java/openmldb-spark-connector/src/main/java/com/_4paradigm/openmldb/spark/write/OpenmldbWriteConfig.java b/java/openmldb-spark-connector/src/main/java/com/_4paradigm/openmldb/spark/write/OpenmldbWriteConfig.java index 64f5d03820e..89c2d801ca5 100644 --- a/java/openmldb-spark-connector/src/main/java/com/_4paradigm/openmldb/spark/write/OpenmldbWriteConfig.java +++ b/java/openmldb-spark-connector/src/main/java/com/_4paradigm/openmldb/spark/write/OpenmldbWriteConfig.java @@ -23,13 +23,14 @@ // Must serializable public class OpenmldbWriteConfig implements Serializable { - public final String dbName, tableName, zkCluster, zkPath; + public final String dbName, tableName, zkCluster, zkPath, writerType; - public OpenmldbWriteConfig(String dbName, String tableName, SdkOption option) { + public OpenmldbWriteConfig(String dbName, String tableName, SdkOption option, String writerType) { this.dbName = dbName; this.tableName = tableName; this.zkCluster = option.getZkCluster(); this.zkPath = option.getZkPath(); + this.writerType = writerType; // TODO(hw): other configs in SdkOption } } From 1b527085c4e55ab682a85fcb28fa063b0b69a6f6 Mon Sep 17 00:00:00 2001 From: HuangWei Date: Fri, 25 Aug 2023 10:58:43 +0800 Subject: [PATCH 20/63] feat: deploy with bias (#3456) * feat: deploy with bias * ut sleep * avoid change unbounded(0) ttl --- .../sql/deployment_manage/DEPLOY_STATEMENT.md | 17 ++- .../deployment_manage/DEPLOY_STATEMENT.md | 17 ++- src/base/ddl_parser.cc | 3 +- src/cmd/sql_cmd_test.cc | 100 ++++++++++++- src/sdk/sql_cluster_router.cc | 140 ++++++++++++++---- src/sdk/sql_cluster_router.h | 50 ++++++- 6 files changed, 292 insertions(+), 35 deletions(-) diff --git a/docs/en/reference/sql/deployment_manage/DEPLOY_STATEMENT.md b/docs/en/reference/sql/deployment_manage/DEPLOY_STATEMENT.md index d8b5f31f153..06e2fdedcff 100644 --- a/docs/en/reference/sql/deployment_manage/DEPLOY_STATEMENT.md +++ b/docs/en/reference/sql/deployment_manage/DEPLOY_STATEMENT.md @@ -99,6 +99,11 @@ DeployOption DeployOptionItem ::= 'LONG_WINDOWS' '=' LongWindowDefinitions | 'SKIP_INDEX_CHECK' '=' string_literal + | 'RANGE_BIAS' '=' RangeBiasValueExpr + | 'ROWS_BIAS' '=' RowsBiasValueExpr + +RangeBiasValueExpr ::= int_literal | interval_literal | string_literal +RowsBiasValueExpr ::= int_literal | string_literal ``` #### Long Window Optimization @@ -164,7 +169,7 @@ DEPLOY demo OPTIONS (SKIP_INDEX_CHECK="TRUE") SELECT * FROM t1 LAST JOIN t2 ORDER BY t2.col3 ON t1.col1 = t2.col1; ``` -### Synchronization/Asynchronization Settings +#### Synchronization/Asynchronization Settings When executing deploy, you can set the synchronous/asynchronous mode through the `SYNC` option. The default value of `SYNC` is `true`, that is, the synchronous mode. If the relevant tables involved in the deploy statement have data and needs to add one or more indexs, executing deploy will initiate a job to execute a series of tasks such as loading data. In this case a job id will be returned if the `SYNC` option is set to `false`. You can get the job execution status by `SHOW JOBS FROM NAMESERVER LIKE '{job_id}'` **Example** @@ -173,6 +178,16 @@ deploy demo options(SYNC="false") SELECT t1.col1, t2.col2, sum(col4) OVER w1 as WINDOW w1 AS (PARTITION BY t1.col2 ORDER BY t1.col3 ROWS BETWEEN 2 PRECEDING AND CURRENT ROW); ``` +#### BIAS + +If you don't want data to be expired by the deployed index, or want data expired later, you can set bias when deploy, which is usually used in the case of data timestamp is not real-time, test, etc. If the index ttl after deploy is abs 3h, but the data timestamp is 3h ago(based on system time), then the data will be eliminated and cannot participate in the calculation. Setting a certain time or permanent bias can make the data stay in the online table for a longer time. + +Range bias can be `s`, `m`, `h`, `d`, or integer(unit is ms), or `inf`(means infinite, never expire). Rows bias can be integer, or `inf`(means infinite, never expire). In both type, 0 means no bias. + +Notice that, we only add bias to deployed index, which is new index. It's not the final index. The final index is `bias + new_index` if deployment will create index. And the final index is `merge(old_index, bias + new_index)` if deployment will update index. + +And range bias unit is `min`, we'll convert it to `min` and get upper bound. e.g. deployed index ttl is abs 2min, add range bias 20s, the result is `2min + ub(20s) = 3min`, and merge with old index 1min, the final index is `max(1min, 3min) = 3min`. + ## Relevant SQL [USE DATABASE](../ddl/USE_DATABASE_STATEMENT.md) diff --git a/docs/zh/openmldb_sql/deployment_manage/DEPLOY_STATEMENT.md b/docs/zh/openmldb_sql/deployment_manage/DEPLOY_STATEMENT.md index 7332add2aa4..4f94e228357 100644 --- a/docs/zh/openmldb_sql/deployment_manage/DEPLOY_STATEMENT.md +++ b/docs/zh/openmldb_sql/deployment_manage/DEPLOY_STATEMENT.md @@ -100,6 +100,11 @@ DeployOptionItem ::= 'LONG_WINDOWS' '=' LongWindowDefinitions | 'SKIP_INDEX_CHECK' '=' string_literal | 'SYNC' '=' string_literal + | 'RANGE_BIAS' '=' RangeBiasValueExpr + | 'ROWS_BIAS' '=' RowsBiasValueExpr + +RangeBiasValueExpr ::= int_literal | interval_literal | string_literal +RowsBiasValueExpr ::= int_literal | string_literal ``` #### 长窗口优化 @@ -161,7 +166,7 @@ DEPLOY demo OPTIONS (SKIP_INDEX_CHECK="TRUE") SELECT * FROM t1 LAST JOIN t2 ORDER BY t2.col3 ON t1.col1 = t2.col1; ``` -### 设置同步/异步 +#### 设置同步/异步 执行deploy的时候可以通过`SYNC`选项来设置同步/异步模式, 默认为`true`即同步模式。如果deploy语句中涉及的相关表有数据,并且需要添加索引的情况下,执行deploy会发起数据加载等任务,如果`SYNC`选项设置为`false`就会返回一个任务id。可以通过`SHOW JOBS FROM NAMESERVER LIKE '{job_id}'`来查看任务执行状态。 **Example** @@ -170,6 +175,16 @@ deploy demo options(SYNC="false") SELECT t1.col1, t2.col2, sum(col4) OVER w1 as WINDOW w1 AS (PARTITION BY t1.col2 ORDER BY t1.col3 ROWS BETWEEN 2 PRECEDING AND CURRENT ROW); ``` +#### 设置偏移 + +如果你并不希望数据根据deploy的索引淘汰,或者希望晚一点淘汰,可以在deploy时设置偏移,常用于数据时间戳并不实时的情况、测试等情况。如果deploy后的索引ttl为abs 3h,但是数据的时间戳是3h前的(以系统时间为基准),那么这条数据就会被淘汰,无法参与计算。设置一定时间或永久的偏移,则可以让数据更久的停留在在线表中。 + +时间偏移,单位可以是`s`、`m`、`h`、`d`,也可以是整数,单位为`ms`,也可以是`inf`,表示永不淘汰;如果是行数偏移,可以是整数,单位是`row`,也可以是`inf`,表示永不淘汰。两种偏移中,0均表示不偏移。 + +注意,我们只将偏移加在deploy的解析索引中,也就是新索引,它们并不是最终索引。最终索引的计算方式是,如果是创建索引,最终索引是`解析索引 + 偏移`;如果是更新索引,最终索引是`merge(旧索引, 新索引 + 偏移)`。 + +而时间偏移的单位是`min`,我们会在内部将其转换为`min`,并且取上界。比如,新索引ttl是abs 2min,加上偏移20s,结果是`2min + ub(20s) = 3min`,然后和旧索引1min取上界,最终索引ttl是`max(1min, 3min) = 3min`。 + ## 相关SQL [USE DATABASE](../ddl/USE_DATABASE_STATEMENT.md) diff --git a/src/base/ddl_parser.cc b/src/base/ddl_parser.cc index 6bb116ecb0a..c69b6b502ed 100644 --- a/src/base/ddl_parser.cc +++ b/src/base/ddl_parser.cc @@ -564,7 +564,8 @@ int64_t LatTTLConvert(int64_t lat, bool zero_eq_unbounded) { // history_range_start == INT64_MIN: unbounded // history_range_start == 0: not unbounded -// NOTICE: do not convert invalid range/rows start. It'll get 0 from `GetHistoryRangeStart`. +// And after convert, 0 means unbounded, history_range_start 0 will be converted to 1 +// NOTICE: do not convert invalid range/rows start, it'll return 0 by `GetHistoryRangeStart`. int64_t AbsTTLConvert(int64_t history_range_start) { return history_range_start == INT64_MIN ? 0 : AbsTTLConvert(-1 * history_range_start, false); } diff --git a/src/cmd/sql_cmd_test.cc b/src/cmd/sql_cmd_test.cc index 1c9fd6b1dc6..e6f2b88012a 100644 --- a/src/cmd/sql_cmd_test.cc +++ b/src/cmd/sql_cmd_test.cc @@ -944,6 +944,101 @@ TEST_P(DBSDKTest, DeployWithData) { ASSERT_TRUE(cs->GetNsClient()->DropDatabase("test2", msg)); } +TEST_P(DBSDKTest, DeployWithBias) { + auto cli = GetParam(); + cs = cli->cs; + sr = cli->sr; + std::string db = "test_bias"; + std::string ddl1 = + "create table if not exists t1 (col1 string, col2 string, col3 bigint, col4 int, " + "index(key=col1, ts=col3, TTL_TYPE=absolute)) options (partitionnum=2, replicanum=1);"; + std::string ddl2 = + "create table if not exists t2 (col1 string, col2 string, col3 bigint, " + "index(key=col1, ts=col3, TTL_TYPE=absolute)) options (partitionnum=2, replicanum=1);"; + ProcessSQLs(sr, { + "set @@execute_mode = 'online';", + "create database " + db , + "use " + db, + ddl1, + ddl2, + }); + hybridse::sdk::Status status; + for (int i = 0; i < 100; i++) { + std::string key1 = absl::StrCat("col1", i); + std::string key2 = absl::StrCat("col2", i); + sr->ExecuteSQL(absl::StrCat("insert into t1 values ('", key1, "', '", key2, "', 1635247427000, 5);"), &status); + sr->ExecuteSQL(absl::StrCat("insert into t2 values ('", key1, "', '", key2, "', 1635247427000);"), &status); + } + sleep(2); + + std::string rows_deployment_part = + "SELECT t1.col1, t2.col2, sum(col4) OVER w1 as w1_col4_sum " + "FROM t1 " + "LAST JOIN t2 ORDER BY t2.col3 ON t1.col2 = t2.col2 " + "WINDOW w1 AS (PARTITION BY t1.col2 ORDER BY t1.col3 ROWS BETWEEN 2 PRECEDING AND CURRENT ROW);"; + auto range_deployment_part = + "SELECT t1.col1, t2.col2, sum(col4) OVER w1 as w1_col4_sum " + "FROM t1 " + "LAST JOIN t2 ORDER BY t2.col3 ON t1.col2 = t2.col2 " + "WINDOW w1 AS (PARTITION BY t1.col2 ORDER BY t1.col3 ROWS_RANGE BETWEEN 2 PRECEDING AND CURRENT ROW);"; + + // test rows bias + auto i = 0; + auto rows_test = [&](std::string option, bool expect = true) { + sr->ExecuteSQL(absl::StrCat("DEPLOY d", i++, " OPTIONS(", option, ") ", rows_deployment_part), &status); + if (expect) + EXPECT_TRUE(status.IsOK()); + else + EXPECT_FALSE(status.IsOK()); + // check table index + auto info = sr->GetTableInfo(db, "t1"); + return info.column_key().Get(1); + }; + + auto index_res = rows_test("rows_bias=0"); + ASSERT_EQ(index_res.ttl().lat_ttl(), 2); + // range bias won't work cuz no new abs index in deploy + index_res = rows_test("rows_bias=20, range_bias='inf'"); + ASSERT_EQ(index_res.ttl().lat_ttl(), 22); + rows_test("rows_bias=20s", false); + i--; // last one is failed, reset the num + + // test range bias + auto range_test = [&](std::string option, bool expect = true) { + sr->ExecuteSQL(absl::StrCat("DEPLOY d", i++, " OPTIONS(", option, ") ", range_deployment_part), &status); + if (expect) + EXPECT_TRUE(status.IsOK()); + else + EXPECT_FALSE(status.IsOK()); + // check table index + auto info = sr->GetTableInfo(db, "t1"); + return info.column_key().Get(1); + }; + index_res = range_test("range_bias=0"); + ASSERT_EQ(index_res.ttl().abs_ttl(), 1); + index_res = range_test("range_bias=20"); + ASSERT_EQ(index_res.ttl().abs_ttl(), 2); + // rows bias won't work cuz no **new** lat index in deploy, just new abs index + the old index + index_res = range_test("range_bias=1d, rows_bias=100"); + ASSERT_EQ(index_res.ttl().abs_ttl(), 1441); + + // set inf in the end, if not, all bias + inf = inf + index_res = rows_test("range_bias='inf'"); + ASSERT_EQ(index_res.ttl().abs_ttl(), 0); + index_res = rows_test("rows_bias='inf'"); + ASSERT_EQ(index_res.ttl().lat_ttl(), 0); + + // sp in tablet may be stored a bit late, wait + sleep(3); + std::string msg; + for (int j = 0; j < i; j++) { + ASSERT_TRUE(cs->GetNsClient()->DropProcedure(db, "d" + std::to_string(j), msg)); + } + ASSERT_TRUE(cs->GetNsClient()->DropTable(db, "t1", msg)); + ASSERT_TRUE(cs->GetNsClient()->DropTable(db, "t2", msg)); + ASSERT_TRUE(cs->GetNsClient()->DropDatabase(db, msg)); +} + TEST_P(DBSDKTest, DeletetRange) { auto cli = GetParam(); sr = cli->sr; @@ -994,7 +1089,8 @@ TEST_P(DBSDKTest, DeletetSameColIndex) { sr = cli->sr; std::string db_name = "test2"; std::string table_name = "test1"; - std::string ddl = "create table test1 (c1 string, c2 int, c3 bigint, c4 bigint, " + std::string ddl = + "create table test1 (c1 string, c2 int, c3 bigint, c4 bigint, " "INDEX(KEY=c1, ts=c3), INDEX(KEY=c1, ts=c4));"; ProcessSQLs(sr, { "set @@execute_mode = 'online'", @@ -1008,7 +1104,7 @@ TEST_P(DBSDKTest, DeletetSameColIndex) { for (int j = 0; j < 10; j++) { uint64_t ts = 1000 + j; sr->ExecuteSQL(absl::StrCat("insert into ", table_name, " values ('", key, "', 11, ", ts, ",", ts, ");"), - &status); + &status); } } diff --git a/src/sdk/sql_cluster_router.cc b/src/sdk/sql_cluster_router.cc index 5d821dbe884..89575f02a58 100644 --- a/src/sdk/sql_cluster_router.cc +++ b/src/sdk/sql_cluster_router.cc @@ -72,6 +72,8 @@ using hybridse::plan::PlanAPI; constexpr const char* SKIP_INDEX_CHECK_OPTION = "skip_index_check"; constexpr const char* SYNC_OPTION = "sync"; +constexpr const char* RANGE_BIAS_OPTION = "range_bias"; +constexpr const char* ROWS_BIAS_OPTION = "rows_bias"; class ExplainInfoImpl : public ExplainInfo { public: @@ -385,8 +387,8 @@ std::shared_ptr SQLClusterRouter::GetDeleteRow(cons if (delete_cache) { status->code = 0; return std::make_shared( - delete_cache->GetDatabase(), delete_cache->GetTableName(), - delete_cache->GetDefaultCondition(), delete_cache->GetCondition()); + delete_cache->GetDatabase(), delete_cache->GetTableName(), delete_cache->GetDefaultCondition(), + delete_cache->GetCondition()); } } ::hybridse::node::NodeManager nm; @@ -422,18 +424,19 @@ std::shared_ptr SQLClusterRouter::GetDeleteRow(cons std::vector condition_vec; std::vector parameter_vec; auto binary_node = dynamic_cast(condition); - *status = NodeAdapter::ExtractCondition(binary_node, col_map, table_info->column_key(), - &condition_vec, ¶meter_vec); + *status = + NodeAdapter::ExtractCondition(binary_node, col_map, table_info->column_key(), &condition_vec, ¶meter_vec); if (!status->IsOK()) { LOG(WARNING) << status->ToString(); return {}; } - auto delete_cache = std::make_shared( - db, table_info->tid(), table_name, condition_vec, parameter_vec); + auto delete_cache = + std::make_shared(db, table_info->tid(), table_name, condition_vec, parameter_vec); SetCache(db, sql, hybridse::vm::kBatchMode, delete_cache); *status = {}; return std::make_shared(delete_cache->GetDatabase(), delete_cache->GetTableName(), - delete_cache->GetDefaultCondition(), delete_cache->GetCondition()); + delete_cache->GetDefaultCondition(), + delete_cache->GetCondition()); } std::shared_ptr SQLClusterRouter::GetInsertRow(const std::string& db, const std::string& sql, @@ -1921,7 +1924,9 @@ std::shared_ptr SQLClusterRouter::HandleSQLCmd(const h const auto& args = cmd_node->GetArgs(); return ExecuteShowTableStatus(db, args.size() > 0 ? args[0] : "", status); } - default: { *status = {StatusCode::kCmdError, "fail to execute script with unsupported type"}; } + default: { + *status = {StatusCode::kCmdError, "fail to execute script with unsupported type"}; + } } return {}; } @@ -1941,7 +1946,7 @@ base::Status SQLClusterRouter::HandleSQLCreateTable(hybridse::node::CreatePlanNo std::string db_name = create_node->GetDatabase().empty() ? db : create_node->GetDatabase(); if (db_name.empty()) { return base::Status(base::ReturnCode::kSQLCmdRunError, "ERROR: Please use database first"); - } + } if (create_node->like_clause_ == nullptr) { ::openmldb::nameserver::TableInfo table_info; @@ -1967,8 +1972,8 @@ base::Status SQLClusterRouter::HandleSQLCreateTable(hybridse::node::CreatePlanNo auto dbs = cluster_sdk_->GetAllDbs(); auto it = std::find(dbs.begin(), dbs.end(), db_name); if (it == dbs.end()) { - return base::Status(base::ReturnCode::kSQLCmdRunError, "fail to create, database does not exist!"); - } + return base::Status(base::ReturnCode::kSQLCmdRunError, "fail to create, database does not exist!"); + } LOG(WARNING) << "CREATE TABLE LIKE will run in offline job, please wait."; @@ -3208,8 +3213,8 @@ hybridse::sdk::Status SQLClusterRouter::HandleDelete(const std::string& db, cons std::vector parameter_vec; auto binary_node = dynamic_cast(condition); auto col_map = schema::SchemaAdapter::GetColMap(*table_info); - auto status = NodeAdapter::ExtractCondition(binary_node, col_map, table_info->column_key(), - &condition_vec, ¶meter_vec); + auto status = + NodeAdapter::ExtractCondition(binary_node, col_map, table_info->column_key(), &condition_vec, ¶meter_vec); if (!status.IsOK()) { return status; } @@ -3225,8 +3230,7 @@ hybridse::sdk::Status SQLClusterRouter::HandleDelete(const std::string& db, cons } hybridse::sdk::Status SQLClusterRouter::SendDeleteRequst( - const std::shared_ptr<::openmldb::nameserver::TableInfo>& table_info, - const DeleteOption* option) { + const std::shared_ptr<::openmldb::nameserver::TableInfo>& table_info, const DeleteOption* option) { if (option->index_map.empty()) { std::vector> tablets; if (!cluster_sdk_->GetTablet(table_info->db(), table_info->name(), &tablets)) { @@ -3239,8 +3243,9 @@ hybridse::sdk::Status SQLClusterRouter::SendDeleteRequst( } for (size_t idx = 0; idx < tablets.size(); idx++) { auto tablet_client = tablets.at(idx)->GetClient(); - if (auto status = tablet_client->Delete(table_info->tid(), idx, - option->index_map, option->ts_name, option->start_ts, option->end_ts); !status.OK()) { + if (auto status = tablet_client->Delete(table_info->tid(), idx, option->index_map, option->ts_name, + option->start_ts, option->end_ts); + !status.OK()) { return {StatusCode::kCmdError, status.GetMsg()}; } } @@ -3263,8 +3268,8 @@ hybridse::sdk::Status SQLClusterRouter::SendDeleteRequst( if (!tablet_client) { return {StatusCode::kCmdError, "tablet client is null"}; } - auto ret = tablet_client->Delete(table_info->tid(), kv.first, kv.second, - option->ts_name, option->start_ts, option->end_ts); + auto ret = tablet_client->Delete(table_info->tid(), kv.first, kv.second, option->ts_name, option->start_ts, + option->end_ts); if (!ret.OK()) { return {StatusCode::kCmdError, ret.GetMsg()}; } @@ -3456,9 +3461,26 @@ hybridse::sdk::Status SQLClusterRouter::HandleDeploy(const std::string& db, } } + // bias parse + // range bias: int means xx ms, int & interval will covert to minutes, if == 0, no bias, if has part < 1min, add + // 1min + Bias bias; + iter = deploy_node->Options()->find(RANGE_BIAS_OPTION); + if (iter != deploy_node->Options()->end()) { + if (!bias.SetRange(iter->second)) { + return {StatusCode::kCmdError, "range bias '" + iter->second->GetExprString() + "' is illegal"}; + } + } + iter = deploy_node->Options()->find(ROWS_BIAS_OPTION); + if (iter != deploy_node->Options()->end()) { + if (!bias.SetRows(iter->second)) { + return {StatusCode::kCmdError, "rows bias '" + iter->second->GetExprString() + "' is illegal"}; + } + } + base::MultiDBIndexMap new_index_map; - // merge index, update exists index ttl in table, get new index to create - auto get_index_status = GetNewIndex(table_map, select_sql, db, skip_index_check, &new_index_map); + // merge index, update exists index ttl(add bias) in table, get new index(add bias) to create + auto get_index_status = GetNewIndex(table_map, select_sql, db, skip_index_check, bias, &new_index_map); if (!get_index_status.IsOK()) { return get_index_status; } @@ -3536,7 +3558,7 @@ hybridse::sdk::Status SQLClusterRouter::HandleDeploy(const std::string& db, } hybridse::sdk::Status SQLClusterRouter::GetNewIndex(const TableInfoMap& table_map, const std::string& select_sql, - const std::string& db, bool skip_index_check, + const std::string& db, bool skip_index_check, const Bias& bias, base::MultiDBIndexMap* new_index_map) { // convert info map to desc map base::MultiDBTableDescMap table_desc_map; @@ -3549,6 +3571,7 @@ hybridse::sdk::Status SQLClusterRouter::GetNewIndex(const TableInfoMap& table_ma } auto index_map = base::DDLParser::ExtractIndexes(select_sql, db, table_desc_map); auto ns = cluster_sdk_->GetNsClient(); + // add bias in the loop for (auto& db_index : index_map) { auto& db_name = db_index.first; for (auto& kv : db_index.second) { @@ -3588,13 +3611,18 @@ hybridse::sdk::Status SQLClusterRouter::GetNewIndex(const TableInfoMap& table_ma // the existed ones std::vector<::openmldb::common::ColumnKey> new_indexs; for (auto& column_key : extract_column_keys) { - auto index_id = openmldb::schema::IndexUtil::GetIDStr(column_key); + // add bias on extracted index, 0 means unbounded + auto new_column_key = bias.AddBias(column_key); + LOG(INFO) << "add bias on extracted index " << column_key.ShortDebugString() << " with bias " << bias + << ", result " << new_column_key.ShortDebugString(); + + auto index_id = openmldb::schema::IndexUtil::GetIDStr(new_column_key); auto it = exists_index_map.find(index_id); if (it != exists_index_map.end()) { auto& old_column_key = it->second; common::TTLSt result; // if skip index check, we don't do update ttl, for backward compatibility(server <=0.8.0) - if (base::TTLMerge(old_column_key.ttl(), column_key.ttl(), &result) && !skip_index_check) { + if (base::TTLMerge(old_column_key.ttl(), new_column_key.ttl(), &result) && !skip_index_check) { // update ttl auto ns_ptr = cluster_sdk_->GetNsClient(); std::string err; @@ -3604,10 +3632,10 @@ hybridse::sdk::Status SQLClusterRouter::GetNewIndex(const TableInfoMap& table_ma } } } else { - column_key.set_index_name( + new_column_key.set_index_name( absl::StrCat("INDEX_", cur_index_num + add_index_num, "_", ::baidu::common::timer::now_time())); add_index_num++; - new_indexs.emplace_back(column_key); + new_indexs.emplace_back(new_column_key); } } @@ -4392,5 +4420,65 @@ std::shared_ptr SQLClusterRouter::GetNameServerJobResu return rs; } +common::ColumnKey Bias::AddBias(const common::ColumnKey& index) const { + if (!index.has_ttl()) { + LOG(WARNING) << "index has no ttl, skip bias"; + return index; + } + auto new_index = index; + auto ttl = new_index.mutable_ttl(); + if (ttl->ttl_type() != type::TTLType::kLatestTime) { + // add bias to ttl when abs / abs||. / abs&&. + if (range_inf) { + ttl->set_abs_ttl(0); + } else if (ttl->abs_ttl() > 0) { + // in ttl, 0 means unlimited, no need to add bias + ttl->set_abs_ttl(ttl->abs_ttl() + range_bias); + } + } + if (ttl->ttl_type() != type::TTLType::kAbsoluteTime) { + // add bias to ttl when lat / .||lat / .&&lat + if (rows_inf) { + ttl->set_lat_ttl(0); + } else if (ttl->lat_ttl() > 0) { + // in ttl, 0 means unlimited, no need to add bias + ttl->set_lat_ttl(ttl->lat_ttl() + rows_bias); + } + } + return new_index; +} + +bool Bias::Set(const hybridse::node::ConstNode* node, bool is_row_type) { + if (node == nullptr) { + return false; + } + // both range and rows can be int & string 'inf' + if (node->IsNumber()) { + SetBias(is_row_type, node->GetAsInt64()); + return true; + } + auto str = node->GetAsString(); + if (absl::EqualsIgnoreCase(str, "inf")) { + SetInf(is_row_type); + return true; + } + // row bias shouldn't be interval, rang bias can be 0d, 0h, ... + if (is_row_type) { + return false; + } + auto v = node->GetMillis(); + if (v == -1) { + return false; + } + // abs time should be min, 0 means no bias, if has part which < 1 min, add 1 min + SetBias(is_row_type, v); + return true; +} + +std::ostream& operator<<(std::ostream& os, const Bias& bias) { // NOLINT + os << "Bias[" << bias.ToString() << "]"; + return os; +} + } // namespace sdk } // namespace openmldb diff --git a/src/sdk/sql_cluster_router.h b/src/sdk/sql_cluster_router.h index 8bb5796240e..ae72a32edbb 100644 --- a/src/sdk/sql_cluster_router.h +++ b/src/sdk/sql_cluster_router.h @@ -47,6 +47,8 @@ constexpr const char* FORMAT_STRING_KEY = "!%$FORMAT_STRING_KEY"; class DeleteOption; using TableInfoMap = std::map>; +class Bias; + class SQLClusterRouter : public SQLRouter { public: using TableStatusMap = @@ -339,7 +341,7 @@ class SQLClusterRouter : public SQLRouter { const hybridse::node::ExprNode* condition); hybridse::sdk::Status SendDeleteRequst(const std::shared_ptr& table_info, - const DeleteOption* option); + const DeleteOption* option); hybridse::sdk::Status HandleIndex(const std::string& db, const std::set>& table_pair, @@ -347,7 +349,7 @@ class SQLClusterRouter : public SQLRouter { // update existing index, return index need to be created // NOTE: table index may be changed, can't revert hybridse::sdk::Status GetNewIndex(const TableInfoMap& table_map, const std::string& select_sql, - const std::string& db, bool skip_index_check, + const std::string& db, bool skip_index_check, const Bias& bias, base::MultiDBIndexMap* new_index_map); hybridse::sdk::Status AddNewIndex(const base::MultiDBIndexMap& new_index_map); @@ -376,9 +378,9 @@ class SQLClusterRouter : public SQLRouter { std::shared_ptr GetJobResultSet(int job_id, ::hybridse::sdk::Status* status); std::shared_ptr GetJobResultSet(::hybridse::sdk::Status* status); std::shared_ptr GetNameServerJobResult(const std::string& like_pattern, - ::hybridse::sdk::Status* status); + ::hybridse::sdk::Status* status); std::shared_ptr GetTaskManagerJobResult(const std::string& like_pattern, - ::hybridse::sdk::Status* status); + ::hybridse::sdk::Status* status); bool CheckTableStatus(const std::string& db, const std::string& table_name, uint32_t tid, const nameserver::TablePartition& partition_info, uint32_t replica_num, @@ -397,5 +399,45 @@ class SQLClusterRouter : public SQLRouter { ::openmldb::base::Random rand_; }; +class Bias { + public: + // If get failed, return false and won't change bias. Check negative bias value for your own logic + bool SetRange(const hybridse::node::ConstNode* node) { return Set(node, false); } + bool SetRows(const hybridse::node::ConstNode* node) { return Set(node, true); } + common::ColumnKey AddBias(const common::ColumnKey& index) const; + std::string ToString() const { + std::stringstream ss; + ss << "range_bias: " << range_bias << ", range_inf: " << range_inf << ", rows_bias: " << rows_bias + << ", rows_inf: " << rows_inf; + return ss.str(); + } + + private: + bool Set(const hybridse::node::ConstNode* node, bool is_row_type); + // if range type, v means ms and convert to min + void SetBias(bool is_row_type, int64_t v) { + if (is_row_type) { + rows_bias = v; + } else { + range_bias = v / 60000 + (v % 60000 ? 1 : 0); + } + } + void SetInf(bool is_row_type) { + if (is_row_type) { + rows_inf = true; + } else { + range_inf = true; + } + } + + private: + int64_t range_bias = 0; + bool range_inf = false; + int64_t rows_bias = 0; + bool rows_inf = false; +}; + +std::ostream& operator<<(std::ostream& os, const Bias& bias); // NO LINT + } // namespace openmldb::sdk #endif // SRC_SDK_SQL_CLUSTER_ROUTER_H_ From 5ba7a416fcb4fff98e87b605ddd05b7df7586689 Mon Sep 17 00:00:00 2001 From: dl239 Date: Mon, 28 Aug 2023 14:44:03 +0800 Subject: [PATCH 21/63] docs: add notice (#3453) --- docs/zh/openmldb_sql/index.rst | 3 ++- docs/zh/openmldb_sql/notice.md | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 docs/zh/openmldb_sql/notice.md diff --git a/docs/zh/openmldb_sql/index.rst b/docs/zh/openmldb_sql/index.rst index 3f0f68d3768..7d00e9ed532 100644 --- a/docs/zh/openmldb_sql/index.rst +++ b/docs/zh/openmldb_sql/index.rst @@ -14,5 +14,6 @@ OpenMLDB SQL dml/index ddl/index deployment_manage/index - task_manage/index + task_manage/index udf_develop_guide + notice diff --git a/docs/zh/openmldb_sql/notice.md b/docs/zh/openmldb_sql/notice.md new file mode 100644 index 00000000000..9c1c660bf23 --- /dev/null +++ b/docs/zh/openmldb_sql/notice.md @@ -0,0 +1,19 @@ +# SQL 命令执行注意的点 + +部分 SQL 命令的执行存在一定的危险性,或者有一些特别需要注意的点以避免误操作。本文对相关命令做了总结,如果你依然对其中的操作有疑问,欢迎在我们[社区渠道](https://github.com/4paradigm/OpenMLDB#11-community)进行交流,以避免相关操作给你的开发和生产环境带来损失。 + +| SQL 命令 | 注意点 | +| ------------- | --------------------------------------------- | +| CREATE TABLE | 1. 在建表语句中如果没有指定索引,默认会自动创建一个`absolute 0`的索引。这个索引下的数据永不过期,可能会占用大量内存
2. 磁盘表`absandlat`和`absorlat`类型没有过期删除 +| DROP TABLE | 1. 删除表默认是异步操作,执行完成后,异步删除表中的数据
2. 如果有分片在做snapshot, 会删除失败。可能存在部分分片删除部分没有删除的情况
3. 删除时默认会把数据目录放到recycle目录下。tablet的配置文件中`recycle_bin_enabled`参数可以配置是否要放到recycle, 默认是开启的
4. 由于内存碎片问题,释放的内存不一定完全释放给操作系统 +| INSERT | 如果返回失败,可能有一部分数据已经插入进去 +| DELETE | 1. 删除的数据不会立马从内存中物理删除,需要等一个过期删除时间间隔(即参数 `gc_interval`)
2. 如果设置了长窗口,不会更新预聚合表里的数据 +| CREATE INDEX | 1. 创建索引是一个异步操作,如果表里有数据需要等一段时间 `desc` 命令才能显示出来
2. 在创建索引的过程中如果有写操作,那么可能会有部分新写入的数据在新加的索引上查询不出来
3. 磁盘表不支持创建索引 +| DROP INDEX | 1. 删除一个索引之后,如果要再重新创建相同的索引需要等两个过期删除时间间隔(及参数 `gc_interval`)
2. 执行该命令后,内存中的索引并没有被真正的马上删除,需要等两个过期删除时间间隔才会在内存中真正被执行删除动作
3. 磁盘表不支持删除索引 +| DEPLOY | 1. DEPLOY 命令可能会修改相关表的TTL,执行DEPLOY前导入的数据可能在新TTL生效前被淘汰,新的TTL生效时间为2个`gc_interval`
2. 在deployment关联的表中,如果有磁盘表需要添加索引,那么部署会失败,可能有部分索引已经添加成功 +| DROP DEPLOYMENT | 1. 不会清理自动创建的索引
2. 如果指定了长窗口,删除deployment不会清理预聚合表 +| DROP FUNCTION | 如果有正在执行的deployment用到此函数,可能会执行错误或者程序崩溃 +| SHOW COMPONENTS | 1. 结果不包含 API Server
2. 结果不包含 TaskManager `standby` 状态 +| SHOW JOBS | 1. 默认显示的是TaskManager的job。如果希望显示 NameServer 的jobs,使用命令 `show jobs from nameserver`;如果希望显示 TaskManager 的 jobs,使用命令 `show jobs from taskmanager`
2. NameServer重启后,没有恢复和展示已完成和已取消的job +| SHOW JOB | 只能显示TaskManager里的job, 不支持显示NameServer里的job +| STOP JOB | 只能停止TaskManager里的job, 不支持停止NameServer里的job From 8222120fbdcffb1b211931429f2bdb517b0c57fd Mon Sep 17 00:00:00 2001 From: dl239 Date: Mon, 28 Aug 2023 18:10:09 +0800 Subject: [PATCH 22/63] docs: add the external link of online engine deployment on k8s (#3462) --- docs/zh/deploy/index.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/zh/deploy/index.rst b/docs/zh/deploy/index.rst index a86f5ee69e9..29007be2d86 100644 --- a/docs/zh/deploy/index.rst +++ b/docs/zh/deploy/index.rst @@ -10,3 +10,4 @@ compile integrate_hadoop offline_integrate_kubernetes + [Alpha]在线引擎基于 Kubernetes 部署 From 1ea8e5811d920efe34cce3e315406116d68d54da Mon Sep 17 00:00:00 2001 From: dl239 Date: Tue, 29 Aug 2023 09:22:56 +0800 Subject: [PATCH 23/63] feat: drop success if pid does not exist (#3427) --- docs/en/deploy/conf.md | 2 ++ docs/zh/deploy/conf.md | 2 ++ release/conf/tablet.flags.template | 2 ++ src/client/tablet_client.cc | 2 +- src/nameserver/name_server_impl.cc | 8 ++++---- 5 files changed, 11 insertions(+), 5 deletions(-) diff --git a/docs/en/deploy/conf.md b/docs/en/deploy/conf.md index 6857cbdfd19..11667427247 100644 --- a/docs/en/deploy/conf.md +++ b/docs/en/deploy/conf.md @@ -135,6 +135,8 @@ --ssd_root_path=./db_ssd #Configure the data recycle bin directory, where the data of the drop table will be placed --recycle_bin_ssd_root_path=./recycle_ssd +# Configure whether to enable recycle +#--recycle_bin_enabled=true # snapshot conf # Configure the time to do snapshots, the time of day. For example, 23 means taking a snapshot at 23 o'clock every day. diff --git a/docs/zh/deploy/conf.md b/docs/zh/deploy/conf.md index f9283a3b748..ef05f0c8dc9 100644 --- a/docs/zh/deploy/conf.md +++ b/docs/zh/deploy/conf.md @@ -138,6 +138,8 @@ --ssd_root_path=./db_ssd # 配置数据回收站目录,drop表的数据就会放在这里 --recycle_bin_ssd_root_path=./recycle_ssd +# 配置是否开启回收站, 如果开启drop table后的数据会放到recycle目录里 +#--recycle_bin_enabled=true # snapshot conf # 配置做snapshot的时间,配置为一天中的几点。如23就表示每天23点做snapshot diff --git a/release/conf/tablet.flags.template b/release/conf/tablet.flags.template index c5e2e0add58..3d126d74123 100644 --- a/release/conf/tablet.flags.template +++ b/release/conf/tablet.flags.template @@ -48,6 +48,8 @@ #--ssd_root_path=./db_ssd # 配置数据回收站目录,drop表的数据就会放在这里 #--recycle_bin_ssd_root_path=./recycle_ssd +# 配置是否开启回收站, 如果开启drop table后的数据会放到recycle目录里 +#--recycle_bin_enabled=true # snapshot conf # 每天23点做snapshot diff --git a/src/client/tablet_client.cc b/src/client/tablet_client.cc index e1d3ef9fa28..aca61c6cad9 100644 --- a/src/client/tablet_client.cc +++ b/src/client/tablet_client.cc @@ -635,7 +635,7 @@ bool TabletClient::DropTable(uint32_t id, uint32_t pid, std::shared_ptrDropTable(tid, pkv.first)) { PDLOG(WARNING, "drop table failed. tid[%u] pid[%u] endpoint[%s]", tid, pkv.first, kv.first.c_str()); - code = 313; // if drop table failed, return error + code = base::ReturnCode::kDropTableError; // if drop table failed, return error continue; } PDLOG(INFO, "drop table. tid[%u] pid[%u] endpoint[%s]", tid, pkv.first, kv.first.c_str()); @@ -2993,7 +2993,7 @@ void NameServerImpl::DropTableInternel(const DropTableRequest& request, GeneralR if (!request.db().empty()) { if (IsClusterMode() && !zk_client_->DeleteNode(zk_path_.db_table_data_path_ + "/" + std::to_string(tid))) { PDLOG(WARNING, "delete db table node[%s/%u] failed!", zk_path_.db_table_data_path_.c_str(), tid); - code = 304; + code = base::ReturnCode::kSetZkFailed; } else { PDLOG(INFO, "delete table node[%s/%u]", zk_path_.db_table_data_path_.c_str(), tid); db_table_info_[db].erase(name); @@ -3001,7 +3001,7 @@ void NameServerImpl::DropTableInternel(const DropTableRequest& request, GeneralR } else { if (IsClusterMode() && !zk_client_->DeleteNode(zk_path_.table_data_path_ + "/" + name)) { PDLOG(WARNING, "delete table node[%s/%s] failed!", zk_path_.table_data_path_.c_str(), name.c_str()); - code = 304; + code = base::ReturnCode::kSetZkFailed; } else { PDLOG(INFO, "delete table node[%s/%s]", zk_path_.table_data_path_.c_str(), name.c_str()); table_info_.erase(name); @@ -3017,7 +3017,7 @@ void NameServerImpl::DropTableInternel(const DropTableRequest& request, GeneralR FLAGS_name_server_task_concurrency_for_replica_cluster) < 0) { PDLOG(WARNING, "create DropTableRemoteOP for replica cluster failed, table_name: %s, alias: %s", name.c_str(), kv.first.c_str()); - code = 505; + code = base::ReturnCode::kCreateDroptableremoteopForReplicaClusterFailed; continue; } } From badf7ab2cfd7d7e2d7df45769417596e0117e446 Mon Sep 17 00:00:00 2001 From: "qiyuan.liu" <1043276694@qq.com> Date: Wed, 30 Aug 2023 17:28:37 +0800 Subject: [PATCH 24/63] remove format_config.sh sudo request (#3473) --- test/steps/format_config.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/steps/format_config.sh b/test/steps/format_config.sh index 9700d330974..62d99ba99b7 100755 --- a/test/steps/format_config.sh +++ b/test/steps/format_config.sh @@ -14,14 +14,14 @@ dirName="${jobName}-${version}-${curTime}" #set Deploy Host and Ports Hosts=(node-3 node-4 node-1) -AvaNode1Ports=$(ssh "${Hosts[0]}" "comm -23 <(seq $portFrom $portTo | sort) <(sudo ss -Htan | awk '{print $4}' | cut -d':' -f2 | sort -u) | shuf | head -n 8") -AvaNode2Ports=$(ssh "${Hosts[1]}" "comm -23 <(seq $portFrom $portTo | sort) <(sudo ss -Htan | awk '{print $4}' | cut -d':' -f2 | sort -u) | shuf | head -n 2") -AvaNode3Ports=$(ssh "${Hosts[2]}" "comm -23 <(seq $portFrom $portTo | sort) <(sudo ss -Htan | awk '{print $4}' | cut -d':' -f2 | sort -u) | shuf | head -n 1") +AvaNode1Ports=$(ssh "${Hosts[0]}" "comm -23 <(seq $portFrom $portTo | sort) <(/usr/sbin/ss -Htan | awk '{print $4}' | cut -d':' -f2 | sort -u) | shuf | head -n 8") +AvaNode2Ports=$(ssh "${Hosts[1]}" "comm -23 <(seq $portFrom $portTo | sort) <(/usr/sbin/ss -Htan | awk '{print $4}' | cut -d':' -f2 | sort -u) | shuf | head -n 2") +AvaNode3Ports=$(ssh "${Hosts[2]}" "comm -23 <(seq $portFrom $portTo | sort) <(/usr/sbin/ss -Htan | awk '{print $4}' | cut -d':' -f2 | sort -u) | shuf | head -n 1") taskmanagerHost=$(hostname | awk -F"." '{print $1}' ) -taskmanagerPort=$(ssh "${taskmanagerHost}" "comm -23 <(seq $portFrom $portTo | sort) <(sudo ss -Htan | awk '{print $4}' | cut -d':' -f2 | sort -u) | shuf | head -n 1") +taskmanagerPort=$(ssh "${taskmanagerHost}" "comm -23 <(seq $portFrom $portTo | sort) <(/usr/sbin/ss -Htan | awk '{print $4}' | cut -d':' -f2 | sort -u) | shuf | head -n 1") tablet1Port=$(echo "$AvaNode1Ports" | awk 'BEGIN{ RS="";FS="\n"}{print $1}') From bc00d674468ad96f4b0b5aaebad716e618974000 Mon Sep 17 00:00:00 2001 From: dl239 Date: Thu, 31 Aug 2023 16:12:01 +0800 Subject: [PATCH 25/63] fix: run addindex failed (#3393) --- .../sql/ddl/CREATE_INDEX_STATEMENT.md | 8 +-- .../sql/ddl/CREATE_TABLE_STATEMENT.md | 2 +- .../ddl/CREATE_INDEX_STATEMENT.md | 10 ++- .../ddl/CREATE_TABLE_STATEMENT.md | 2 +- src/base/status.h | 2 + src/nameserver/name_server_impl.cc | 70 +++++++++++++++---- src/nameserver/name_server_impl.h | 2 + src/schema/index_util.cc | 30 ++++++-- src/schema/index_util.h | 2 + src/sdk/sql_cluster_router.cc | 5 +- src/sdk/sql_cluster_test.cc | 54 +++++++++----- .../ha_cases/test_addindex.py | 5 ++ 12 files changed, 142 insertions(+), 50 deletions(-) diff --git a/docs/en/reference/sql/ddl/CREATE_INDEX_STATEMENT.md b/docs/en/reference/sql/ddl/CREATE_INDEX_STATEMENT.md index 6222b98942b..051c1a9e3ba 100644 --- a/docs/en/reference/sql/ddl/CREATE_INDEX_STATEMENT.md +++ b/docs/en/reference/sql/ddl/CREATE_INDEX_STATEMENT.md @@ -1,7 +1,6 @@ # CREATE INDEX -The `CREATE INDEX` statement is used to create a new index on existing table. If there is data in the table, data will be loaded asynchronously. -The job status can be checked through the `showopstatus` command of `ns_client`, see [Operations in CLI](../../../maintain/cli.md#showopstatus). +The `CREATE INDEX` statement is used to create a new index on existing table. Running `CREATE INDEX` will initiates an asynchronous job, and you can check the status of the job by executing `SHOW JOBS FROM NAMESERVER`. ## Syntax @@ -48,7 +47,8 @@ CREATE INDEX index2 ON t5 (col2); -- SUCCEED ``` ```{note} -If `OPTIONS` is not provided, the SQL with the created index cannot be deployed online, since the index doesn't have TS (timestamp). +1. If `OPTIONS` is not provided, the SQL with the created index cannot be deployed online, since the index doesn't have TS (timestamp). +2. The data type of `TS` column should be BigInt or Timestamp. ``` We can also set `TS` column as below: ```SQL @@ -59,4 +59,4 @@ Please refer [here](./CREATE_TABLE_STATEMENT.md) for more details about `TTL` an ## Related SQL -[DROP INDEX](./DROP_INDEX_STATEMENT.md) \ No newline at end of file +[DROP INDEX](./DROP_INDEX_STATEMENT.md) diff --git a/docs/en/reference/sql/ddl/CREATE_TABLE_STATEMENT.md b/docs/en/reference/sql/ddl/CREATE_TABLE_STATEMENT.md index 7205343efb4..a0d11d90657 100644 --- a/docs/en/reference/sql/ddl/CREATE_TABLE_STATEMENT.md +++ b/docs/en/reference/sql/ddl/CREATE_TABLE_STATEMENT.md @@ -226,7 +226,7 @@ The index key must be configured, and other configuration items are optional. Th | Configuration Item | Note | Expression | Example | |------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------| | `KEY` | It defines the index column (required). OpenMLDB supports single-column indexes as well as joint indexes. When `KEY`=one column, a single-column index is configured. When `KEY`=multiple columns, the joint index of these columns is configured: several columns are concatenated into a new string as an index in order. | Single-column index: `ColumnName`
Joint index:
`(ColumnName (, ColumnName)* ) ` | Single-column index: `INDEX(KEY=col1)`
Joint index: `INDEX(KEY=(col1, col2))` | -| `TS` | It defines the index time column (optional). Data on the same index will be sorted by the index time column. When `TS` is not explicitly configured, the timestamp of data insertion is used as the index time. | `ColumnName` | `INDEX(KEY=col1, TS=std_time)`。 The index column is col1, and the data rows with the same col1 value are sorted by std_time. | +| `TS` | It defines the index time column (optional). Data on the same index will be sorted by the index time column. When `TS` is not explicitly configured, the timestamp of data insertion is used as the index time. The data type of time column should be BigInt or Timestamp | `ColumnName` | `INDEX(KEY=col1, TS=std_time)`。 The index column is col1, and the data rows with the same col1 value are sorted by std_time. | | `TTL_TYPE` | It defines the elimination rules (optional). Including four types. When `TTL_TYPE` is not explicitly configured, the `ABSOLUTE` expiration configuration is used by default. | Supported expr: `ABSOLUTE`
`LATEST`
`ABSORLAT`
`ABSANDLAT`。 | For specific usage, please refer to **Configuration Rules for TTL and TTL_TYP** below. | | `TTL` | It defines the maximum survival time/number. Different TTL_TYPEs determines different `TTL` configuration methods. When `TTL` is not explicitly configured, `TTL=0` which means OpenMLDB will not evict records. | Supported expr: `int_literal`
`interval_literal`
`( interval_literal , int_literal )` | For specific usage, please refer to "Configuration Rules for TTL and TTL_TYPE" below. | diff --git a/docs/zh/openmldb_sql/ddl/CREATE_INDEX_STATEMENT.md b/docs/zh/openmldb_sql/ddl/CREATE_INDEX_STATEMENT.md index 692f4fc7d6d..dd8813f0afa 100644 --- a/docs/zh/openmldb_sql/ddl/CREATE_INDEX_STATEMENT.md +++ b/docs/zh/openmldb_sql/ddl/CREATE_INDEX_STATEMENT.md @@ -1,7 +1,6 @@ # CREATE INDEX -`CREATE INDEX` 语句用来创建索引。 如果表里有数据,添加索引会发起异步任务来加载数据。 -通过`ns_client`中的`showopstatus`命令可以查看任务状态,详见[运维 CLI](../../maintain/cli.md#showopstatus)。 +`CREATE INDEX` 语句用来创建索引。添加索引会发起异步任务来加载数据, 可以通过执行`SHOW JOBS FROM NAMESERVER`来查看任务状态 ## 语法 @@ -40,15 +39,14 @@ OptionEntry ::= ``` - - ## **示例** ```SQL CREATE INDEX index2 ON t5 (col2); -- SUCCEED ``` ```{note} -如果不指定Options, 创建的索引就没有指定`TS`列,因此不能用在需要上线的SQL中。 +1. 如果不指定Options, 创建的索引就没有指定`TS`列,因此不能用在需要上线的SQL中。 +2. 指定`TS`列的类型只能是BitInt或者Timestamp ``` 我们可以通过类似如下命令在创建索引时指定`TS`列: ```SQL @@ -59,4 +57,4 @@ CREATE INDEX index3 ON t5 (col3) OPTIONS (ts=ts1, ttl_type=absolute, ttl=30d); ## 相关SQL -[DROP INDEX](./DROP_INDEX_STATEMENT.md) \ No newline at end of file +[DROP INDEX](./DROP_INDEX_STATEMENT.md) diff --git a/docs/zh/openmldb_sql/ddl/CREATE_TABLE_STATEMENT.md b/docs/zh/openmldb_sql/ddl/CREATE_TABLE_STATEMENT.md index 3c6c4444241..1dffc9d4cae 100644 --- a/docs/zh/openmldb_sql/ddl/CREATE_TABLE_STATEMENT.md +++ b/docs/zh/openmldb_sql/ddl/CREATE_TABLE_STATEMENT.md @@ -223,7 +223,7 @@ IndexOption ::= | 配置项 | 描述 | expr | 用法示例 | |------------|---------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------| | `KEY` | 索引列(必选)。OpenMLDB支持单列索引,也支持联合索引。当`KEY`后只有一列时,仅在该列上建立索引。当`KEY`后有多列时,建立这几列的联合索引:将多列按顺序拼接成一个字符串作为索引。 | 支持单列索引:`ColumnName`
或联合索引:
`(ColumnName (, ColumnName)* ) ` | 单列索引:`INDEX(KEY=col1)`
联合索引:`INDEX(KEY=(col1, col2))` | -| `TS` | 索引时间列(可选)。同一个索引上的数据将按照时间索引列排序。当不显式配置`TS`时,使用数据插入的时间戳作为索引时间。 | `ColumnName` | `INDEX(KEY=col1, TS=std_time)`。索引列为col1,col1相同的数据行按std_time排序。 | +| `TS` | 索引时间列(可选)。同一个索引上的数据将按照时间索引列排序。当不显式配置`TS`时,使用数据插入的时间戳作为索引时间。时间列的类型只能为BigInt或者Timestamp | `ColumnName` | `INDEX(KEY=col1, TS=std_time)`。索引列为col1,col1相同的数据行按std_time排序。 | | `TTL_TYPE` | 淘汰规则(可选)。包括四种类型,当不显式配置`TTL_TYPE`时,默认使用`ABSOLUTE`过期配置。 | 支持的expr如下:`ABSOLUTE`
`LATEST`
`ABSORLAT`
`ABSANDLAT`。 | 具体用法可以参考下文“TTL和TTL_TYPE的配置细则” | | `TTL` | 最大存活时间/条数(可选)。依赖于`TTL_TYPE`,不同的`TTL_TYPE`有不同的`TTL` 配置方式。当不显式配置`TTL`时,`TTL=0`,表示不设置淘汰规则,OpenMLDB将不会淘汰记录。 | 支持数值:`int_literal`
或数值带时间单位(`S,M,H,D`):`interval_literal`
或元组形式:`( interval_literal , int_literal )` |具体用法可以参考下文“TTL和TTL_TYPE的配置细则” | diff --git a/src/base/status.h b/src/base/status.h index 803231378ef..1e1d5cc6f4d 100644 --- a/src/base/status.h +++ b/src/base/status.h @@ -88,6 +88,7 @@ enum ReturnCode { kCreateFunctionFailed = 159, kExceedMaxMemory = 160, kInvalidArgs = 161, + kCheckIndexFailed = 162, kNameserverIsNotLeader = 300, kAutoFailoverIsEnabled = 301, kEndpointIsNotExist = 302, @@ -122,6 +123,7 @@ enum ReturnCode { kCheckParameterFailed = 331, kCreateProcedureFailedOnTablet = 332, kCreateFunctionFailedOnTablet = 333, + kOPAlreadyExists = 317, kReplicaClusterAliasDuplicate = 400, kConnectRelicaClusterZkFailed = 401, kNotSameReplicaName = 402, diff --git a/src/nameserver/name_server_impl.cc b/src/nameserver/name_server_impl.cc index 622f74b959f..1dd5acf58ff 100644 --- a/src/nameserver/name_server_impl.cc +++ b/src/nameserver/name_server_impl.cc @@ -8266,14 +8266,13 @@ void NameServerImpl::DeleteIndex(RpcController* controller, const DeleteIndexReq tablet_client_map.insert(std::make_pair(kv.second->client_->GetEndpoint(), kv.second->client_)); } } - for (int idx = 0; idx < table_info->table_partition_size(); idx++) { - for (int meta_idx = 0; meta_idx < table_info->table_partition(idx).partition_meta_size(); meta_idx++) { - std::string endpoint = table_info->table_partition(idx).partition_meta(meta_idx).endpoint(); - if (!table_info->table_partition(idx).partition_meta(meta_idx).is_alive()) { + for (const auto& table_partition : table_info->table_partition()) { + for (const auto& partition_meta : table_partition.partition_meta()) { + const std::string& endpoint = partition_meta.endpoint(); + if (!partition_meta.is_alive()) { response->set_code(::openmldb::base::ReturnCode::kTableHasNoAliveLeaderPartition); response->set_msg("partition is not alive!"); - PDLOG(WARNING, "partition[%s][%d] is not alive!", endpoint.c_str(), - table_info->table_partition(idx).pid()); + PDLOG(WARNING, "partition[%s][%d] is not alive!", endpoint.c_str(), table_partition.pid()); return; } if (tablet_client_map.find(endpoint) == tablet_client_map.end()) { @@ -8285,14 +8284,14 @@ void NameServerImpl::DeleteIndex(RpcController* controller, const DeleteIndexReq } } bool delete_failed = false; - for (int idx = 0; idx < table_info->table_partition_size(); idx++) { - for (int meta_idx = 0; meta_idx < table_info->table_partition(idx).partition_meta_size(); meta_idx++) { - std::string endpoint = table_info->table_partition(idx).partition_meta(meta_idx).endpoint(); + for (const auto& table_partition : table_info->table_partition()) { + for (const auto& partition_meta : table_partition.partition_meta()) { + const std::string& endpoint = partition_meta.endpoint(); std::string msg; - if (!tablet_client_map[endpoint]->DeleteIndex(table_info->tid(), table_info->table_partition(idx).pid(), + if (!tablet_client_map[endpoint]->DeleteIndex(table_info->tid(), table_partition.pid(), request->idx_name(), &msg)) { - PDLOG(WARNING, "delete index failed. name %s pid %u endpoint %s msg %s", request->table_name().c_str(), - table_info->table_partition(idx).pid(), endpoint.c_str(), msg.c_str()); + PDLOG(WARNING, "delete index failed. name %s pid %u endpoint %s msg %s", + request->table_name().c_str(), table_partition.pid(), endpoint.c_str(), msg.c_str()); delete_failed = true; } } @@ -8519,8 +8518,21 @@ void NameServerImpl::AddIndex(RpcController* controller, const AddIndexRequest* openmldb::common::VersionPair* pair = table_info->add_schema_versions(); pair->CopyFrom(new_pair); } + if (auto status = schema::IndexUtil::CheckIndex(col_map, + schema::IndexUtil::Convert2PB(column_key_vec)); !status.OK()) { + base::SetResponseStatus(ReturnCode::kCheckIndexFailed, status.msg, response); + LOG(WARNING) << status.msg; + return; + } if (IsClusterMode() && !request->skip_load_data()) { std::lock_guard lock(mu_); + if (IsExistActiveOp(db, name, api::kAddIndexOP)) { + LOG(WARNING) << "create AddIndexOP failed. there is already a task running. db " + << db << " table " << name; + base::SetResponseStatus(ReturnCode::kOPAlreadyExists, + "there is already a task running", response); + return; + } auto status = CreateAddIndexOP(name, db, column_key_vec); if (!status.OK()) { LOG(WARNING) << "create AddIndexOP failed, table " << name << " msg " << status.GetMsg(); @@ -10475,8 +10487,14 @@ void NameServerImpl::DeploySQL(RpcController* controller, const DeploySQLRequest } } } - uint64_t op_id = 0; std::lock_guard lock(mu_); + if (IsExistActiveOp(db, "", api::OPType::kDeployOP)) { + LOG(WARNING) << "create DeployOP failed. there is already a task running in db " << db; + base::SetResponseStatus(ReturnCode::kOPAlreadyExists, + "there is already a task running", response); + return; + } + uint64_t op_id = 0; auto status = CreateDeployOP(*request, &op_id); if (!status.OK()) { PDLOG(WARNING, "%s", status.GetMsg().c_str()); @@ -10492,7 +10510,7 @@ base::Status NameServerImpl::CreateDeployOP(const DeploySQLRequest& request, uin std::string value; auto op_type = api::OPType::kDeployOP; if (CreateOPData(op_type, value, op_data, sp_info.main_table(), sp_info.db_name(), 0) < 0) { - return {-1, absl::StrCat("create AddIndexOP data error. deploy name ", deploy_name)}; + return {-1, absl::StrCat("create DeployOP data error. deploy name ", deploy_name)}; } auto task = CreateTask(op_data->GetOpId(), op_type, request.index()); if (!task) { @@ -10512,5 +10530,29 @@ base::Status NameServerImpl::CreateDeployOP(const DeploySQLRequest& request, uin return {}; } +bool NameServerImpl::IsExistActiveOp(const std::string& db, const std::string& name, api::OPType op_type) { + for (const auto& op_list : task_vec_) { + if (op_list.empty()) { + continue; + } + for (const auto& op_data : op_list) { + if (op_data->op_info_.op_type() != op_type) { + continue; + } + if (!db.empty() && op_data->op_info_.db() != db) { + continue; + } + if (!name.empty() && op_data->op_info_.name() != name) { + continue; + } + if (op_data->op_info_.task_status() == api::TaskStatus::kInited || + op_data->op_info_.task_status() == api::TaskStatus::kDoing) { + return true; + } + } + } + return false; +} + } // namespace nameserver } // namespace openmldb diff --git a/src/nameserver/name_server_impl.h b/src/nameserver/name_server_impl.h index d8fc2245401..d7e030c0eab 100644 --- a/src/nameserver/name_server_impl.h +++ b/src/nameserver/name_server_impl.h @@ -682,6 +682,8 @@ class NameServerImpl : public NameServer { bool IsExistDataBase(const std::string& db); + bool IsExistActiveOp(const std::string& db, const std::string& name, api::OPType op_type); + private: std::mutex mu_; Tablets tablets_; diff --git a/src/schema/index_util.cc b/src/schema/index_util.cc index a9a8b2596b2..81ed5cd5177 100644 --- a/src/schema/index_util.cc +++ b/src/schema/index_util.cc @@ -50,11 +50,14 @@ base::Status IndexUtil::CheckIndex(const std::mapsecond.data_type() == ::openmldb::type::kFloat) - || (iter->second.data_type() == ::openmldb::type::kDouble)))) { - return {base::ReturnCode::kError, - "float or double type column can not be index, column is: " + column_key.index_name()}; + if (iter != column_map.end()) { + if (iter->second.data_type() == ::openmldb::type::kFloat + || iter->second.data_type() == ::openmldb::type::kDouble) { + return {base::ReturnCode::kError, + "float or double type column can not be index, column is: " + column_key.index_name()}; + } + } else { + return {base::ReturnCode::kError, "can not find col in schema. col: " + column_name}; } } if (!has_iter) { @@ -67,6 +70,15 @@ base::Status IndexUtil::CheckIndex(const std::mapsecond.data_type() != ::openmldb::type::kBigInt + && iter->second.data_type() != ::openmldb::type::kTimestamp) { + return {base::ReturnCode::kError, + "ts column type should be bigint or timestamp, column is: " + column_key.ts_name()}; + } + } if (column_key.has_ttl()) { if (!CheckTTL(column_key.ttl())) { return {base::ReturnCode::kError, "ttl check failed"}; @@ -177,6 +189,14 @@ std::vector<::openmldb::common::ColumnKey> IndexUtil::Convert2Vector(const PBInd return vec; } +PBIndex IndexUtil::Convert2PB(const std::vector<::openmldb::common::ColumnKey>& index) { + PBIndex pb_index; + for (const auto& column_key : index) { + pb_index.Add()->CopyFrom(column_key); + } + return pb_index; +} + std::string IndexUtil::GetIDStr(const ::openmldb::common::ColumnKey& column_key) { std::string id_str; for (const auto& cur_col : column_key.col_name()) { diff --git a/src/schema/index_util.h b/src/schema/index_util.h index c5f6f90429d..465abac1ec1 100644 --- a/src/schema/index_util.h +++ b/src/schema/index_util.h @@ -42,6 +42,8 @@ class IndexUtil { static std::vector<::openmldb::common::ColumnKey> Convert2Vector(const PBIndex& index); + static PBIndex Convert2PB(const std::vector<::openmldb::common::ColumnKey>& index); + static base::Status CheckUnique(const PBIndex& index); static bool CheckTTL(const ::openmldb::common::TTLSt& ttl); diff --git a/src/sdk/sql_cluster_router.cc b/src/sdk/sql_cluster_router.cc index 89575f02a58..7e593d8b359 100644 --- a/src/sdk/sql_cluster_router.cc +++ b/src/sdk/sql_cluster_router.cc @@ -2504,9 +2504,10 @@ std::shared_ptr SQLClusterRouter::ExecuteSQL( } column_key.set_index_name(create_index_node->index_name_); if (ns_ptr->AddIndex(db_name, create_index_node->table_name_, column_key, nullptr, msg)) { - *status = {}; + *status = {::hybridse::common::StatusCode::kOk, + "AddIndex is an asynchronous job. Run 'SHOW JOBS FROM NAMESERVER' to see the job status"}; } else { - SET_STATUS_AND_WARN(status, StatusCode::kCmdError, "ns add index failed"); + SET_STATUS_AND_WARN(status, StatusCode::kCmdError, absl::StrCat("ns add index failed. msg: ", msg)); } return {}; } diff --git a/src/sdk/sql_cluster_test.cc b/src/sdk/sql_cluster_test.cc index 321be553905..f8cccac1832 100644 --- a/src/sdk/sql_cluster_test.cc +++ b/src/sdk/sql_cluster_test.cc @@ -260,23 +260,49 @@ TEST_F(SQLClusterDDLTest, CreateTableWithDatabase) { TEST_F(SQLClusterDDLTest, CreateTableWithDatabaseWrongDDL) { std::string name = "test" + GenRand(); ::hybridse::sdk::Status status; - std::string ddl; + std::string db = "db" + GenRand(); + ASSERT_TRUE(router->CreateDB(db, &status)); // create table db2.name when db2 not exist + std::string ddl; ddl = "create table db2." + name + - "(" - "col1 int, col2 bigint, col3 string," + "(col1 int, col2 bigint, col3 string," "index(key=col3, ts=col2));"; ASSERT_FALSE(router->ExecuteDDL(db, ddl, &status)) << "ddl: " << ddl; ASSERT_FALSE(router->ExecuteDDL(db, "drop table " + name + ";", &status)); // create table db2.name when db2 not exit ddl = "create table db2." + name + - "(" - "col1 int, col2 bigint, col3 string," + "(col1 int, col2 bigint, col3 string," "index(key=col3, ts=col2));"; ASSERT_FALSE(router->ExecuteDDL("", ddl, &status)) << "ddl: " << ddl; ASSERT_FALSE(router->ExecuteDDL("", "drop table " + name + ";", &status)); + ddl = "create table t1 (col1 string, col2 string, col3 string, index(key=col1, ts=col2));"; + ASSERT_FALSE(router->ExecuteDDL(db, ddl, &status)) << "ddl: " << ddl; + ddl = "create table t1 (col1 string, col2 string, col3 string, index(key=col4));"; + ASSERT_FALSE(router->ExecuteDDL(db, ddl, &status)) << "ddl: " << ddl; + ddl = "create table t1 (col1 string, col2 string, col3 int, index(key=col4, ts=col3));"; + ASSERT_FALSE(router->ExecuteDDL(db, ddl, &status)) << "ddl: " << ddl; + ASSERT_TRUE(router->DropDB(db, &status)); +} + +TEST_F(SQLClusterDDLTest, CreateIndexCheck) { + std::string name = "test" + GenRand(); + std::string db = "db" + GenRand(); + ::hybridse::sdk::Status status; + ASSERT_TRUE(router->CreateDB(db, &status)); + std::string ddl = absl::StrCat("create table ", name, "(col1 string, col2 string, col3 string, col4 int);"); + ASSERT_TRUE(router->RefreshCatalog()); + ASSERT_TRUE(router->ExecuteDDL(db, ddl, &status)) << "ddl: " << ddl; + router->ExecuteSQL(db, absl::StrCat("create index index1 on ", name, "(col2) OPTIONS(ts=col3);"), &status); + ASSERT_FALSE(status.IsOK()); + router->ExecuteSQL(db, absl::StrCat("create index index1 on ", name, "(col5);"), &status); + ASSERT_FALSE(status.IsOK()); + router->ExecuteSQL(db, absl::StrCat("create index index1 on ", name, "(col2) OPTIONS(ts=col4);"), &status); + ASSERT_FALSE(status.IsOK()); + + ASSERT_TRUE(router->ExecuteDDL(db, "drop table " + name + ";", &status)); + ASSERT_TRUE(router->DropDB(db, &status)); } TEST_F(SQLClusterDDLTest, TestDelete) { @@ -1044,15 +1070,9 @@ TEST_P(SQLSDKQueryTest, SqlSdkDistributeRequestProcedureAsyncTest) { } TEST_F(SQLClusterTest, CreateTable) { - SQLRouterOptions sql_opt; - sql_opt.zk_cluster = mc_->GetZkCluster(); - sql_opt.zk_path = mc_->GetZkPath(); - auto router = NewClusterSQLRouter(sql_opt); - ASSERT_TRUE(router != nullptr); - SetOnlineMode(router); std::string db = "db" + GenRand(); ::hybridse::sdk::Status status; - bool ok = router->CreateDB(db, &status); + bool ok = router_->CreateDB(db, &status); ASSERT_TRUE(ok); for (int i = 0; i < 2; i++) { std::string name = "test" + std::to_string(i); @@ -1061,10 +1081,10 @@ TEST_F(SQLClusterTest, CreateTable) { "col1 string, col2 bigint," "index(key=col1, ts=col2)) " "options(partitionnum=3);"; - ok = router->ExecuteDDL(db, ddl, &status); + ok = router_->ExecuteDDL(db, ddl, &status); ASSERT_TRUE(ok); } - ASSERT_TRUE(router->RefreshCatalog()); + ASSERT_TRUE(router_->RefreshCatalog()); auto ns_client = mc_->GetNsClient(); std::vector<::openmldb::nameserver::TableInfo> tables; std::string msg; @@ -1083,9 +1103,9 @@ TEST_F(SQLClusterTest, CreateTable) { } ASSERT_EQ(pid_map.size(), 3u); ASSERT_EQ(pid_map.begin()->second, pid_map.rbegin()->second); - ASSERT_TRUE(router->ExecuteDDL(db, "drop table test0;", &status)); - ASSERT_TRUE(router->ExecuteDDL(db, "drop table test1;", &status)); - ASSERT_TRUE(router->DropDB(db, &status)); + ASSERT_TRUE(router_->ExecuteDDL(db, "drop table test0;", &status)); + ASSERT_TRUE(router_->ExecuteDDL(db, "drop table test1;", &status)); + ASSERT_TRUE(router_->DropDB(db, &status)); } TEST_F(SQLClusterTest, GetTableSchema) { diff --git a/test/integration-test/openmldb-test-python/ha_cases/test_addindex.py b/test/integration-test/openmldb-test-python/ha_cases/test_addindex.py index f813f799388..afef45d51bb 100644 --- a/test/integration-test/openmldb-test-python/ha_cases/test_addindex.py +++ b/test/integration-test/openmldb-test-python/ha_cases/test_addindex.py @@ -73,6 +73,11 @@ def test_addindex(self, storage_mode, snapshot): time.sleep(2) result = self.cursor.execute(f"create index index2 on {table_name} (col2) options (ts=col3)") + try: + result = self.cursor.execute(f"create index index3 on {table_name} (col1, col2) options (ts=col3)") + assert False + except Exception as e: + assert True time.sleep(1) assert self.executor.WaitingOP(database, table_name, 0).OK() status, indexs = self.executor.GetIndexs(database, table_name) From 6eff0211cb0734449406d1634af9507402e82f1c Mon Sep 17 00:00:00 2001 From: dl239 Date: Thu, 31 Aug 2023 16:29:59 +0800 Subject: [PATCH 26/63] fix: the result of select on the deleted index col is empty (#3426) --- src/catalog/tablet_catalog.cc | 48 +++++++++++++++++++++++++++++------ src/catalog/tablet_catalog.h | 7 ++--- src/cmd/sql_cmd_test.cc | 5 +++- src/tablet/tablet_impl.cc | 13 ++++++++-- 4 files changed, 59 insertions(+), 14 deletions(-) diff --git a/src/catalog/tablet_catalog.cc b/src/catalog/tablet_catalog.cc index 528b570734a..7f470703b6c 100644 --- a/src/catalog/tablet_catalog.cc +++ b/src/catalog/tablet_catalog.cc @@ -92,6 +92,9 @@ bool TabletTableHandler::UpdateIndex( index_hint_vec_[pos].clear(); for (int32_t i = 0; i < indexs.size(); i++) { const auto& column_key = indexs.Get(i); + if (column_key.flag() != 0) { + continue; + } ::hybridse::vm::IndexSt index_st; index_st.index = i; index_st.ts_pos = ::hybridse::vm::INVALID_POS; @@ -229,7 +232,9 @@ int TabletTableHandler::DeleteTable(uint32_t pid) { return new_tables->size(); } -void TabletTableHandler::Update(const ::openmldb::nameserver::TableInfo& meta, const ClientManager& client_manager) { +bool TabletTableHandler::Update(const ::openmldb::nameserver::TableInfo& meta, const ClientManager& client_manager, + bool* index_updated) { + *index_updated = false; ::openmldb::storage::TableSt new_table_st(meta); for (const auto& partition_st : *(new_table_st.GetPartitions())) { uint32_t pid = partition_st.GetPid(); @@ -238,10 +243,28 @@ void TabletTableHandler::Update(const ::openmldb::nameserver::TableInfo& meta, c } table_client_manager_->UpdatePartitionClientManager(partition_st, client_manager); } - if (meta.column_key_size() != static_cast(GetIndex().size())) { - LOG(INFO) << "index size changed, update index" << meta.column_key_size() << " " << GetIndex().size(); - UpdateIndex(meta.column_key()); + const auto& index_hint = GetIndex(); + size_t index_cnt = 0; + bool has_new_index = false; + for (const auto& column_key : meta.column_key()) { + if (column_key.flag() != 0) { + continue; + } + index_cnt++; + if (index_hint.find(column_key.index_name()) == index_hint.end()) { + has_new_index = true; + break; + } + } + if (index_cnt != index_hint.size() || has_new_index) { + LOG(INFO) << "index size changed. current index size " << index_hint.size() + << " , new index size " << index_cnt; + if (!UpdateIndex(meta.column_key())) { + return false; + } + *index_updated = true; } + return true; } std::shared_ptr<::hybridse::vm::Tablet> TabletTableHandler::GetTablet(const std::string& index_name, @@ -433,7 +456,8 @@ bool TabletCatalog::UpdateTableMeta(const ::openmldb::api::TableMeta& meta) { return handler->UpdateIndex(meta.column_key()); } -bool TabletCatalog::UpdateTableInfo(const ::openmldb::nameserver::TableInfo& table_info) { +bool TabletCatalog::UpdateTableInfo(const ::openmldb::nameserver::TableInfo& table_info, bool* index_updated) { + *index_updated = false; const std::string& db_name = table_info.db(); const std::string& table_name = table_info.name(); std::shared_ptr handler; @@ -456,13 +480,18 @@ bool TabletCatalog::UpdateTableInfo(const ::openmldb::nameserver::TableInfo& tab } else { handler = it->second; } - handler->Update(table_info, client_manager_); + if (bool updated = false; !handler->Update(table_info, client_manager_, &updated)) { + return false; + } else if (updated) { + *index_updated = true; + } } return true; } void TabletCatalog::Refresh(const std::vector<::openmldb::nameserver::TableInfo>& table_info_vec, uint64_t version, - const Procedures& db_sp_map) { + const Procedures& db_sp_map, bool* updated) { + *updated = false; std::map> table_map; for (const auto& table_info : table_info_vec) { const std::string& db_name = table_info.db(); @@ -470,8 +499,10 @@ void TabletCatalog::Refresh(const std::vector<::openmldb::nameserver::TableInfo> if (db_name.empty()) { continue; } - if (!UpdateTableInfo(table_info)) { + if (bool index_updated = false; !UpdateTableInfo(table_info, &index_updated)) { continue; + } else if (index_updated) { + *updated = true; } auto cur_db_it = table_map.find(db_name); if (cur_db_it == table_map.end()) { @@ -494,6 +525,7 @@ void TabletCatalog::Refresh(const std::vector<::openmldb::nameserver::TableInfo> !table_it->second->HasLocalTable()) { LOG(INFO) << "delete table from catalog. db: " << db_it->first << ", table: " << table_it->first; table_it = db_it->second.erase(table_it); + *updated = true; continue; } ++table_it; diff --git a/src/catalog/tablet_catalog.h b/src/catalog/tablet_catalog.h index 99ec0aa6adb..6b0365f6f51 100644 --- a/src/catalog/tablet_catalog.h +++ b/src/catalog/tablet_catalog.h @@ -182,7 +182,8 @@ class TabletTableHandler : public ::hybridse::vm::TableHandler, int DeleteTable(uint32_t pid); - void Update(const ::openmldb::nameserver::TableInfo &meta, const ClientManager &client_manager); + bool Update(const ::openmldb::nameserver::TableInfo &meta, const ClientManager &client_manager, + bool* index_updated); private: inline int32_t GetColumnIndex(const std::string &column) { @@ -223,7 +224,7 @@ class TabletCatalog : public ::hybridse::vm::Catalog { bool UpdateTableMeta(const ::openmldb::api::TableMeta &meta); - bool UpdateTableInfo(const ::openmldb::nameserver::TableInfo& table_info); + bool UpdateTableInfo(const ::openmldb::nameserver::TableInfo& table_info, bool* index_updated); std::shared_ptr<::hybridse::type::Database> GetDatabase(const std::string &db) override; @@ -237,7 +238,7 @@ class TabletCatalog : public ::hybridse::vm::Catalog { bool DeleteDB(const std::string &db); void Refresh(const std::vector<::openmldb::nameserver::TableInfo> &table_info_vec, uint64_t version, - const Procedures &db_sp_map); + const Procedures &db_sp_map, bool* updated); bool AddProcedure(const std::string &db, const std::string &sp_name, const std::shared_ptr &sp_info); diff --git a/src/cmd/sql_cmd_test.cc b/src/cmd/sql_cmd_test.cc index e6f2b88012a..bd66e6db4dc 100644 --- a/src/cmd/sql_cmd_test.cc +++ b/src/cmd/sql_cmd_test.cc @@ -3681,10 +3681,13 @@ TEST_F(SqlCmdTest, SelectWithAddNewIndex) { ASSERT_EQ(res->Size(), 3); res = sr->ExecuteSQL(absl::StrCat("select id,c1,c2,c3 from ", tb1_name, " where c2=1;"), &status); ASSERT_EQ(res->Size(), 3); + ProcessSQLs(sr, {absl::StrCat("drop index ", db1_name, ".", tb1_name, ".index1")}); + absl::SleepFor(absl::Seconds(2)); + res = sr->ExecuteSQL(absl::StrCat("select id,c1,c2,c3 from ", tb1_name, " where c2=1;"), &status); + ASSERT_EQ(res->Size(), 3); ProcessSQLs(sr, { absl::StrCat("use ", db1_name, ";"), - absl::StrCat("drop index ", db1_name, ".", tb1_name, ".index1"), absl::StrCat("drop table ", tb1_name), absl::StrCat("drop database ", db1_name), }); diff --git a/src/tablet/tablet_impl.cc b/src/tablet/tablet_impl.cc index 03335c60a7a..6fb002edbd3 100644 --- a/src/tablet/tablet_impl.cc +++ b/src/tablet/tablet_impl.cc @@ -4247,7 +4247,12 @@ bool TabletImpl::RefreshSingleTable(uint32_t tid) { LOG(WARNING) << "fail to parse table proto. tid: " << tid << " value: " << value; return false; } - return catalog_->UpdateTableInfo(table_info); + if (bool index_updated = false; !catalog_->UpdateTableInfo(table_info, &index_updated)) { + return false; + } else if (index_updated) { + engine_->ClearCacheLocked(""); + } + return true; } void TabletImpl::UpdateGlobalVarTable() { @@ -4369,7 +4374,11 @@ void TabletImpl::RefreshTableInfo() { } } auto old_db_sp_map = catalog_->GetProcedures(); - catalog_->Refresh(table_info_vec, version, db_sp_map); + bool updated = false; + catalog_->Refresh(table_info_vec, version, db_sp_map, &updated); + if (updated) { + engine_->ClearCacheLocked(""); + } // skip exist procedure, don`t need recompile for (const auto& db_sp_map_kv : db_sp_map) { const auto& db = db_sp_map_kv.first; From a013ba33e4ce131353edc71e27053b1801ffb8f7 Mon Sep 17 00:00:00 2001 From: dl239 Date: Thu, 31 Aug 2023 19:42:02 +0800 Subject: [PATCH 27/63] refactor: optimize the error message of create table (#3434) --- src/catalog/distribute_iterator_test.cc | 42 +++++++-------- src/client/tablet_client.cc | 68 +++---------------------- src/client/tablet_client.h | 7 +-- src/nameserver/name_server_impl.cc | 60 ++++++++++++---------- src/nameserver/name_server_impl.h | 4 +- src/replica/binlog_test.cc | 12 ++--- src/replica/snapshot_replica_test.cc | 56 ++++++++++---------- src/schema/index_util.cc | 14 ++--- src/schema/index_util.h | 2 +- src/sdk/node_adapter.cc | 34 +++++++------ src/test/util.cc | 35 +++++++++++++ src/test/util.h | 8 +++ 12 files changed, 168 insertions(+), 174 deletions(-) diff --git a/src/catalog/distribute_iterator_test.cc b/src/catalog/distribute_iterator_test.cc index 72a716bac3d..49e4958101f 100644 --- a/src/catalog/distribute_iterator_test.cc +++ b/src/catalog/distribute_iterator_test.cc @@ -162,8 +162,8 @@ TEST_F(DistributeIteratorTest, AllInRemote) { auto client2 = std::make_shared(endpoints[1], endpoints[1]); ASSERT_EQ(client2->Init(), 0); std::vector<::openmldb::api::TableMeta> metas = {CreateTableMeta(tid, 1), CreateTableMeta(tid, 4)}; - ASSERT_TRUE(client1->CreateTable(metas[0])); - ASSERT_TRUE(client2->CreateTable(metas[1])); + ASSERT_TRUE(client1->CreateTable(metas[0]).OK()); + ASSERT_TRUE(client2->CreateTable(metas[1]).OK()); std::map> tablet_clients = {{1, client1}, {4, client2}}; FullTableIterator it(tid, {}, tablet_clients); it.SeekToFirst(); @@ -205,8 +205,8 @@ TEST_F(DistributeIteratorTest, Hybrid) { auto client2 = std::make_shared(endpoints[1], endpoints[1]); ASSERT_EQ(client2->Init(), 0); std::vector<::openmldb::api::TableMeta> metas = {CreateTableMeta(tid, 1), CreateTableMeta(tid, 4)}; - ASSERT_TRUE(client1->CreateTable(metas[0])); - ASSERT_TRUE(client2->CreateTable(metas[1])); + ASSERT_TRUE(client1->CreateTable(metas[0]).OK()); + ASSERT_TRUE(client2->CreateTable(metas[1]).OK()); std::map> tablet_clients = {{1, client1}, {4, client2}}; FullTableIterator it(tid, tables, tablet_clients); it.SeekToFirst(); @@ -265,8 +265,8 @@ TEST_F(DistributeIteratorTest, FullTableTraverseLimit) { auto client2 = std::make_shared(endpoints[1], endpoints[1]); ASSERT_EQ(client2->Init(), 0); std::vector<::openmldb::api::TableMeta> metas = {CreateTableMeta(tid, 1), CreateTableMeta(tid, 4)}; - ASSERT_TRUE(client1->CreateTable(metas[0])); - ASSERT_TRUE(client2->CreateTable(metas[1])); + ASSERT_TRUE(client1->CreateTable(metas[0]).OK()); + ASSERT_TRUE(client2->CreateTable(metas[1]).OK()); std::map> tablet_clients = {{1, client1}, {4, client2}}; FullTableIterator it(tid, tables, tablet_clients); it.SeekToFirst(); @@ -317,7 +317,7 @@ TEST_F(DistributeIteratorTest, TraverseLimitSingle) { auto client1 = std::make_shared(endpoints[0], endpoints[0]); ASSERT_EQ(client1->Init(), 0); std::vector<::openmldb::api::TableMeta> metas = {CreateTableMeta(tid, 0)}; - ASSERT_TRUE(client1->CreateTable(metas[0])); + ASSERT_TRUE(client1->CreateTable(metas[0]).OK()); std::map> tablet_clients = {{0, client1}}; for (int i = 0; i < 10; i++) { std::string key = "card" + std::to_string(i); @@ -355,8 +355,8 @@ TEST_F(DistributeIteratorTest, TraverseLimit) { auto client2 = std::make_shared(endpoints[1], endpoints[1]); ASSERT_EQ(client2->Init(), 0); std::vector<::openmldb::api::TableMeta> metas = {CreateTableMeta(tid, 1), CreateTableMeta(tid, 3)}; - ASSERT_TRUE(client1->CreateTable(metas[0])); - ASSERT_TRUE(client2->CreateTable(metas[1])); + ASSERT_TRUE(client1->CreateTable(metas[0]).OK()); + ASSERT_TRUE(client2->CreateTable(metas[1]).OK()); std::map> tablet_clients = {{1, client1}, {3, client2}}; std::map cout_map = {{0, 0}, {1, 0}, {2, 0}, {3, 0}}; for (int i = 0; i < 100; i++) { @@ -399,8 +399,8 @@ TEST_F(DistributeIteratorTest, WindowIterator) { auto client2 = std::make_shared(endpoints[1], endpoints[1]); ASSERT_EQ(client2->Init(), 0); std::vector<::openmldb::api::TableMeta> metas = {CreateTableMeta(tid, 1), CreateTableMeta(tid, 3)}; - ASSERT_TRUE(client1->CreateTable(metas[0])); - ASSERT_TRUE(client2->CreateTable(metas[1])); + ASSERT_TRUE(client1->CreateTable(metas[0]).OK()); + ASSERT_TRUE(client2->CreateTable(metas[1]).OK()); std::map> tablet_clients = {{1, client1}, {3, client2}}; for (int i = 0; i < 20; i++) { std::string key = "card" + std::to_string(i); @@ -462,7 +462,7 @@ TEST_F(DistributeIteratorTest, RemoteIterator) { auto client1 = std::make_shared(endpoints[0], endpoints[0]); ASSERT_EQ(client1->Init(), 0); std::vector<::openmldb::api::TableMeta> metas = {CreateTableMeta(tid, 0)}; - ASSERT_TRUE(client1->CreateTable(metas[0])); + ASSERT_TRUE(client1->CreateTable(metas[0]).OK()); std::map> tablet_clients = {{0, client1}}; codec::SDKCodec codec(metas[0]); int64_t now = 1999; @@ -540,7 +540,7 @@ TEST_F(DistributeIteratorTest, RemoteIteratorSecondIndex) { auto client1 = std::make_shared(endpoints[0], endpoints[0]); ASSERT_EQ(client1->Init(), 0); std::vector<::openmldb::api::TableMeta> metas = {CreateTableMeta(tid, 0)}; - ASSERT_TRUE(client1->CreateTable(metas[0])); + ASSERT_TRUE(client1->CreateTable(metas[0]).OK()); std::map> tablet_clients = {{0, client1}}; codec::SDKCodec codec(metas[0]); uint64_t now = ::baidu::common::timer::get_micros() / 1000; @@ -678,8 +678,8 @@ TEST_F(DistributeIteratorTest, MoreTsCnt) { auto client2 = std::make_shared(endpoints[1], endpoints[1]); ASSERT_EQ(client2->Init(), 0); std::vector<::openmldb::api::TableMeta> metas = {CreateTableMeta(tid, 1), CreateTableMeta(tid, 3)}; - ASSERT_TRUE(client1->CreateTable(metas[0])); - ASSERT_TRUE(client2->CreateTable(metas[1])); + ASSERT_TRUE(client1->CreateTable(metas[0]).OK()); + ASSERT_TRUE(client2->CreateTable(metas[1]).OK()); std::map> tablet_clients = {{1, client1}, {3, client2}}; std::map cout_map = {{0, 0}, {1, 0}, {2, 0}, {3, 0}}; for (int i = 0; i < 50; i++) { @@ -759,8 +759,8 @@ TEST_F(DistributeIteratorTest, TraverseSameTs) { auto client2 = std::make_shared(endpoints[1], endpoints[1]); ASSERT_EQ(client2->Init(), 0); std::vector<::openmldb::api::TableMeta> metas = {CreateTableMeta(tid, 1), CreateTableMeta(tid, 3)}; - ASSERT_TRUE(client1->CreateTable(metas[0])); - ASSERT_TRUE(client2->CreateTable(metas[1])); + ASSERT_TRUE(client1->CreateTable(metas[0]).OK()); + ASSERT_TRUE(client2->CreateTable(metas[1]).OK()); std::map> tablet_clients = {{1, client1}, {3, client2}}; std::map cout_map = {{0, 0}, {1, 0}, {2, 0}, {3, 0}}; for (int i = 0; i < 20; i++) { @@ -804,8 +804,8 @@ TEST_F(DistributeIteratorTest, WindowIteratorLimit) { auto client2 = std::make_shared(endpoints[1], endpoints[1]); ASSERT_EQ(client2->Init(), 0); std::vector<::openmldb::api::TableMeta> metas = {CreateTableMeta(tid, 1), CreateTableMeta(tid, 3)}; - ASSERT_TRUE(client1->CreateTable(metas[0])); - ASSERT_TRUE(client2->CreateTable(metas[1])); + ASSERT_TRUE(client1->CreateTable(metas[0]).OK()); + ASSERT_TRUE(client2->CreateTable(metas[1]).OK()); std::map> tablet_clients = {{1, client1}, {3, client2}}; for (int i = 0; i < 20; i++) { std::string key = "card" + std::to_string(i); @@ -921,8 +921,8 @@ TEST_F(DistributeIteratorTest, IteratorZero) { auto client2 = std::make_shared(endpoints[1], endpoints[1]); ASSERT_EQ(client2->Init(), 0); std::vector<::openmldb::api::TableMeta> metas = {CreateTableMeta(tid, 1), CreateTableMeta(tid, 3)}; - ASSERT_TRUE(client1->CreateTable(metas[0])); - ASSERT_TRUE(client2->CreateTable(metas[1])); + ASSERT_TRUE(client1->CreateTable(metas[0]).OK()); + ASSERT_TRUE(client2->CreateTable(metas[1]).OK()); std::map> tablet_clients = {{1, client1}, {3, client2}}; std::map cout_map = {{0, 0}, {1, 0}, {2, 0}, {3, 0}}; int expect = 0; diff --git a/src/client/tablet_client.cc b/src/client/tablet_client.cc index aca61c6cad9..3ed9eb596b5 100644 --- a/src/client/tablet_client.cc +++ b/src/client/tablet_client.cc @@ -153,72 +153,18 @@ bool TabletClient::SQLBatchRequestQuery(const std::string& db, const std::string return true; } -bool TabletClient::CreateTable(const std::string& name, uint32_t tid, uint32_t pid, uint64_t abs_ttl, uint64_t lat_ttl, - bool leader, const std::vector& endpoints, - const ::openmldb::type::TTLType& type, uint32_t seg_cnt, uint64_t term, - const ::openmldb::type::CompressType compress_type, - ::openmldb::common::StorageMode storage_mode) { - ::openmldb::api::CreateTableRequest request; - if (type == ::openmldb::type::kLatestTime) { - if (lat_ttl > FLAGS_latest_ttl_max) { - return false; - } - } else if (type == ::openmldb::type::TTLType::kAbsoluteTime) { - if (abs_ttl > FLAGS_absolute_ttl_max) { - return false; - } - } else { - if (abs_ttl > FLAGS_absolute_ttl_max || lat_ttl > FLAGS_latest_ttl_max) { - return false; - } - } - ::openmldb::api::TableMeta* table_meta = request.mutable_table_meta(); - table_meta->set_name(name); - table_meta->set_tid(tid); - table_meta->set_pid(pid); - table_meta->set_compress_type(compress_type); - table_meta->set_seg_cnt(seg_cnt); - table_meta->set_storage_mode(storage_mode); - if (leader) { - table_meta->set_mode(::openmldb::api::TableMode::kTableLeader); - table_meta->set_term(term); - } else { - table_meta->set_mode(::openmldb::api::TableMode::kTableFollower); - } - for (size_t i = 0; i < endpoints.size(); i++) { - table_meta->add_replicas(endpoints[i]); - } - ::openmldb::common::ColumnDesc* column_desc = table_meta->add_column_desc(); - column_desc->set_name("idx0"); - column_desc->set_data_type(::openmldb::type::kString); - ::openmldb::common::ColumnKey* index = table_meta->add_column_key(); - index->set_index_name("idx0"); - index->add_col_name("idx0"); - ::openmldb::common::TTLSt* ttl = index->mutable_ttl(); - ttl->set_abs_ttl(abs_ttl); - ttl->set_lat_ttl(lat_ttl); - ttl->set_ttl_type(type); - // table_meta->set_ttl_type(type); - ::openmldb::api::CreateTableResponse response; - bool ok = client_.SendRequest(&::openmldb::api::TabletServer_Stub::CreateTable, &request, &response, - FLAGS_request_timeout_ms * 2, 1); - if (ok && response.code() == 0) { - return true; - } - return false; -} - -bool TabletClient::CreateTable(const ::openmldb::api::TableMeta& table_meta) { +base::Status TabletClient::CreateTable(const ::openmldb::api::TableMeta& table_meta) { ::openmldb::api::CreateTableRequest request; ::openmldb::api::TableMeta* table_meta_ptr = request.mutable_table_meta(); table_meta_ptr->CopyFrom(table_meta); ::openmldb::api::CreateTableResponse response; - bool ok = client_.SendRequest(&::openmldb::api::TabletServer_Stub::CreateTable, &request, &response, - FLAGS_request_timeout_ms * 2, 1); - if (ok && response.code() == 0) { - return true; + if (!client_.SendRequest(&::openmldb::api::TabletServer_Stub::CreateTable, &request, &response, + FLAGS_request_timeout_ms * 2, 1)) { + return {base::ReturnCode::kRPCError, "send request failed!"}; + } else if (response.code() == 0) { + return {}; } - return false; + return {response.code(), response.msg()}; } bool TabletClient::UpdateTableMetaForAddField(uint32_t tid, const std::vector& cols, diff --git a/src/client/tablet_client.h b/src/client/tablet_client.h index 895960b9bdc..23a1c5df879 100644 --- a/src/client/tablet_client.h +++ b/src/client/tablet_client.h @@ -54,12 +54,7 @@ class TabletClient : public Client { int Init() override; - bool CreateTable(const std::string& name, uint32_t tid, uint32_t pid, uint64_t abs_ttl, uint64_t lat_ttl, - bool leader, const std::vector& endpoints, const ::openmldb::type::TTLType& type, - uint32_t seg_cnt, uint64_t term, const ::openmldb::type::CompressType compress_type, - ::openmldb::common::StorageMode storage_mode = ::openmldb::common::kMemory); - - bool CreateTable(const ::openmldb::api::TableMeta& table_meta); + base::Status CreateTable(const ::openmldb::api::TableMeta& table_meta); bool UpdateTableMetaForAddField(uint32_t tid, const std::vector& cols, const openmldb::common::VersionPair& pair, diff --git a/src/nameserver/name_server_impl.cc b/src/nameserver/name_server_impl.cc index 1dd5acf58ff..4aae3c594c3 100644 --- a/src/nameserver/name_server_impl.cc +++ b/src/nameserver/name_server_impl.cc @@ -2182,9 +2182,9 @@ int NameServerImpl::SetPartitionInfo(TableInfo& table_info) { return 0; } -int NameServerImpl::CreateTableOnTablet(const std::shared_ptr<::openmldb::nameserver::TableInfo>& table_info, - bool is_leader, std::map>& endpoint_map, - uint64_t term) { +base::Status NameServerImpl::CreateTableOnTablet(const std::shared_ptr<::openmldb::nameserver::TableInfo>& table_info, + bool is_leader, uint64_t term, + std::map>* endpoint_map) { ::openmldb::type::CompressType compress_type = ::openmldb::type::CompressType::kNoCompress; if (table_info->compress_type() == ::openmldb::type::kSnappy) { compress_type = ::openmldb::type::CompressType::kSnappy; @@ -2201,18 +2201,16 @@ int NameServerImpl::CreateTableOnTablet(const std::shared_ptr<::openmldb::namese table_meta.set_key_entry_max_height(table_info->key_entry_max_height()); } for (int idx = 0; idx < table_info->column_desc_size(); idx++) { - ::openmldb::common::ColumnDesc* column_desc = table_meta.add_column_desc(); - column_desc->CopyFrom(table_info->column_desc(idx)); + table_meta.add_column_desc()->CopyFrom(table_info->column_desc(idx)); } for (int idx = 0; idx < table_info->column_key_size(); idx++) { - ::openmldb::common::ColumnKey* column_key = table_meta.add_column_key(); - column_key->CopyFrom(table_info->column_key(idx)); + table_meta.add_column_key()->CopyFrom(table_info->column_key(idx)); } for (const auto& table_partition : table_info->table_partition()) { - ::openmldb::common::TablePartition* partition = table_meta.add_table_partition(); + auto partition = table_meta.add_table_partition(); partition->set_pid(table_partition.pid()); for (const auto& partition_meta : table_partition.partition_meta()) { - ::openmldb::common::PartitionMeta* meta = partition->add_partition_meta(); + auto meta = partition->add_partition_meta(); meta->set_endpoint(partition_meta.endpoint()); meta->set_is_leader(partition_meta.is_leader()); meta->set_is_alive(true); @@ -2229,36 +2227,37 @@ int NameServerImpl::CreateTableOnTablet(const std::shared_ptr<::openmldb::namese std::string endpoint = table_info->table_partition(idx).partition_meta(meta_idx).endpoint(); auto tablet_ptr = GetTablet(endpoint); if (!tablet_ptr) { - PDLOG(WARNING, "endpoint[%s] can not find client", endpoint.c_str()); - return -1; + PDLOG(WARNING, "endpoint[%s] cannot find client", endpoint.c_str()); + return {base::ReturnCode::kServerConnError, absl::StrCat("endpoint ", endpoint, " cannot find client")}; } if (is_leader) { - ::openmldb::nameserver::TablePartition* table_partition = table_info->mutable_table_partition(idx); - ::openmldb::nameserver::TermPair* term_pair = table_partition->add_term_offset(); + auto table_partition = table_info->mutable_table_partition(idx); + auto term_pair = table_partition->add_term_offset(); term_pair->set_term(term); term_pair->set_offset(0); table_meta.set_mode(::openmldb::api::TableMode::kTableLeader); table_meta.set_term(term); - for (const auto& e : endpoint_map[pid]) { + for (const auto& e : (*endpoint_map)[pid]) { table_meta.add_replicas(e); } } else { - if (endpoint_map.find(pid) == endpoint_map.end()) { - endpoint_map.insert(std::make_pair(pid, std::vector())); + auto iter = endpoint_map->find(pid); + if (iter == endpoint_map->end()) { + iter = endpoint_map->emplace(pid, std::vector()).first; } - endpoint_map[pid].push_back(endpoint); + iter->second.push_back(endpoint); table_meta.set_mode(::openmldb::api::TableMode::kTableFollower); } - if (!tablet_ptr->client_->CreateTable(table_meta)) { - PDLOG(WARNING, "create table failed. tid[%u] pid[%u] endpoint[%s]", table_info->tid(), pid, - endpoint.c_str()); - return -1; + if (auto status = tablet_ptr->client_->CreateTable(table_meta); !status.OK()) { + PDLOG(WARNING, "create table failed. tid[%u] pid[%u] endpoint[%s] msg[%s]", + table_info->tid(), pid, endpoint.c_str(), status.GetMsg().c_str()); + return status; } PDLOG(INFO, "create table success. tid[%u] pid[%u] endpoint[%s] idx[%d]", table_info->tid(), pid, endpoint.c_str(), idx); } } - return 0; + return {}; } int NameServerImpl::DropTableOnTablet(std::shared_ptr<::openmldb::nameserver::TableInfo> table_info) { @@ -3758,11 +3757,18 @@ void NameServerImpl::CreateTableInternel(GeneralResponse& response, std::shared_ptr<::openmldb::api::TaskInfo> task_ptr) { std::map> endpoint_map; do { - if (CreateTableOnTablet(table_info, false, endpoint_map, cur_term) < 0 || - CreateTableOnTablet(table_info, true, endpoint_map, cur_term) < 0) { - response.set_code(::openmldb::base::ReturnCode::kCreateTableFailedOnTablet); - response.set_msg("create table failed on tablet"); - PDLOG(WARNING, "create table failed. name[%s] tid[%u]", table_info->name().c_str(), tid); + auto status = CreateTableOnTablet(table_info, false, cur_term, &endpoint_map); + if (!status.OK()) { + base::SetResponseStatus(status, &response); + PDLOG(WARNING, "create table failed. name[%s] tid[%u] msg[%s]", + table_info->name().c_str(), tid, status.GetMsg().c_str()); + break; + } + status = CreateTableOnTablet(table_info, true, cur_term, &endpoint_map); + if (!status.OK()) { + base::SetResponseStatus(status, &response); + PDLOG(WARNING, "create table failed. name[%s] tid[%u] msg[%s]", + table_info->name().c_str(), tid, status.GetMsg().c_str()); break; } if (!IsClusterMode()) { diff --git a/src/nameserver/name_server_impl.h b/src/nameserver/name_server_impl.h index d7e030c0eab..4bfe84ad5f4 100644 --- a/src/nameserver/name_server_impl.h +++ b/src/nameserver/name_server_impl.h @@ -306,8 +306,8 @@ class NameServerImpl : public NameServer { const ::openmldb::nameserver::TableInfo& table_info_local, uint32_t pid, int& code, // NOLINT std::string& msg); // NOLINT - int CreateTableOnTablet(const std::shared_ptr<::openmldb::nameserver::TableInfo>& table_info, bool is_leader, - std::map>& endpoint_map, uint64_t term); // NOLINT + base::Status CreateTableOnTablet(const std::shared_ptr<::openmldb::nameserver::TableInfo>& table_info, + bool is_leader, uint64_t term, std::map>* endpoint_map); void CheckZkClient(); diff --git a/src/replica/binlog_test.cc b/src/replica/binlog_test.cc index 350a53d4d04..797514c76bd 100644 --- a/src/replica/binlog_test.cc +++ b/src/replica/binlog_test.cc @@ -86,19 +86,19 @@ TEST_F(BinlogTest, DeleteBinlog) { ::openmldb::client::TabletClient client(leader_point, ""); client.Init(); std::vector endpoints; - bool ret = - client.CreateTable("table1", tid, pid, 100000, 0, true, endpoints, ::openmldb::type::TTLType::kAbsoluteTime, 16, - 0, ::openmldb::type::CompressType::kNoCompress); - ASSERT_TRUE(ret); + auto status = client.CreateTable(test::CreateTableMeta("table1", tid, pid, 100000, 0, true, endpoints, + ::openmldb::type::TTLType::kAbsoluteTime, 16, + 0, ::openmldb::type::CompressType::kNoCompress)); + ASSERT_TRUE(status.OK()); uint64_t cur_time = ::baidu::common::timer::get_micros() / 1000; int count = 1000; while (count) { std::string key = "testkey_" + std::to_string(count); - ret = client.Put(tid, pid, key, cur_time, ::openmldb::test::EncodeKV(key, std::string(10 * 1024, 'a'))); + client.Put(tid, pid, key, cur_time, ::openmldb::test::EncodeKV(key, std::string(10 * 1024, 'a'))); count--; } - ret = client.MakeSnapshot(tid, pid, 0); + auto ret = client.MakeSnapshot(tid, pid, 0); std::string binlog_path = FLAGS_db_root_path + "/2_123/binlog"; std::vector vec; ASSERT_TRUE(ret); diff --git a/src/replica/snapshot_replica_test.cc b/src/replica/snapshot_replica_test.cc index ee6c67e0b36..a9302050142 100644 --- a/src/replica/snapshot_replica_test.cc +++ b/src/replica/snapshot_replica_test.cc @@ -82,13 +82,13 @@ TEST_P(SnapshotReplicaTest, AddReplicate) { ::openmldb::client::TabletClient client(leader_point, ""); client.Init(); std::vector endpoints; - bool ret = - client.CreateTable("table1", tid, pid, 100000, 0, true, endpoints, ::openmldb::type::TTLType::kAbsoluteTime, 16, - 0, ::openmldb::type::CompressType::kNoCompress, storage_mode); - ASSERT_TRUE(ret); + auto status = client.CreateTable(test::CreateTableMeta("table1", tid, pid, 100000, 0, true, endpoints, + ::openmldb::type::TTLType::kAbsoluteTime, 16, 0, + ::openmldb::type::CompressType::kNoCompress, storage_mode)); + ASSERT_TRUE(status.OK()); std::string end_point = "127.0.0.1:18530"; - ret = client.AddReplica(tid, pid, end_point); + auto ret = client.AddReplica(tid, pid, end_point); ASSERT_TRUE(ret); sleep(1); @@ -123,12 +123,12 @@ TEST_P(SnapshotReplicaTest, LeaderAndFollower) { ::openmldb::client::TabletClient client(leader_point, ""); client.Init(); std::vector endpoints; - bool ret = - client.CreateTable("table1", tid, pid, 100000, 0, true, endpoints, ::openmldb::type::TTLType::kAbsoluteTime, 16, - 0, ::openmldb::type::CompressType::kNoCompress, storage_mode); - ASSERT_TRUE(ret); + auto status = client.CreateTable(test::CreateTableMeta("table1", tid, pid, 100000, 0, true, endpoints, + ::openmldb::type::TTLType::kAbsoluteTime, 16, + 0, ::openmldb::type::CompressType::kNoCompress, storage_mode)); + ASSERT_TRUE(status.OK()); uint64_t cur_time = ::baidu::common::timer::get_micros() / 1000; - ret = client.Put(tid, pid, "testkey", cur_time, ::openmldb::test::EncodeKV("testkey", "value1")); + auto ret = client.Put(tid, pid, "testkey", cur_time, ::openmldb::test::EncodeKV("testkey", "value1")); ASSERT_TRUE(ret); uint32_t count = 0; @@ -159,9 +159,10 @@ TEST_P(SnapshotReplicaTest, LeaderAndFollower) { // server.RunUntilAskedToQuit(); ::openmldb::client::TabletClient client1(follower_point, ""); client1.Init(); - ret = client1.CreateTable("table1", tid, pid, 14400, 0, false, endpoints, ::openmldb::type::TTLType::kAbsoluteTime, - 16, 0, ::openmldb::type::CompressType::kNoCompress, storage_mode); - ASSERT_TRUE(ret); + status = client1.CreateTable(test::CreateTableMeta("table1", tid, pid, 14400, 0, false, endpoints, + ::openmldb::type::TTLType::kAbsoluteTime, + 16, 0, ::openmldb::type::CompressType::kNoCompress, storage_mode)); + ASSERT_TRUE(status.OK()); client.AddReplica(tid, pid, follower_point); sleep(3); @@ -232,12 +233,12 @@ TEST_P(SnapshotReplicaTest, SendSnapshot) { ::openmldb::client::TabletClient client(leader_point, ""); client.Init(); std::vector endpoints; - bool ret = - client.CreateTable("table1", tid, pid, 100000, 0, true, endpoints, ::openmldb::type::TTLType::kAbsoluteTime, 16, - 0, ::openmldb::type::CompressType::kNoCompress, storage_mode); - ASSERT_TRUE(ret); + auto status = client.CreateTable(test::CreateTableMeta("table1", tid, pid, 100000, 0, true, endpoints, + ::openmldb::type::TTLType::kAbsoluteTime, 16, + 0, ::openmldb::type::CompressType::kNoCompress, storage_mode)); + ASSERT_TRUE(status.OK()); uint64_t cur_time = ::baidu::common::timer::get_micros() / 1000; - ret = client.Put(tid, pid, "testkey", cur_time, ::openmldb::test::EncodeKV("testkey", "value1")); + auto ret = client.Put(tid, pid, "testkey", cur_time, ::openmldb::test::EncodeKV("testkey", "value1")); ASSERT_TRUE(ret); uint32_t count = 0; @@ -344,11 +345,11 @@ TEST_P(SnapshotReplicaTest, IncompleteSnapshot) { ::openmldb::client::TabletClient client(leader_point, ""); client.Init(); std::vector endpoints; - bool ret = - client.CreateTable("table1", tid, pid, 100000, 0, true, endpoints, ::openmldb::type::TTLType::kAbsoluteTime, - 16, 0, ::openmldb::type::CompressType::kNoCompress, storage_mode); - ASSERT_TRUE(ret); - ret = client.Put(tid, pid, "testkey", cur_time, ::openmldb::test::EncodeKV("testkey", "value1")); + auto status = client.CreateTable(test::CreateTableMeta("table1", tid, pid, 100000, 0, true, endpoints, + ::openmldb::type::TTLType::kAbsoluteTime, + 16, 0, ::openmldb::type::CompressType::kNoCompress, storage_mode)); + ASSERT_TRUE(status.OK()); + auto ret = client.Put(tid, pid, "testkey", cur_time, ::openmldb::test::EncodeKV("testkey", "value1")); ASSERT_TRUE(ret); uint32_t count = 0; @@ -571,8 +572,7 @@ TEST_P(SnapshotReplicaTest, LeaderAndFollowerTS) { SchemaCodec::SetIndex(table_meta.add_column_key(), "card1", "card", "ts2", ::openmldb::type::kAbsoluteTime, 0, 0); SchemaCodec::SetIndex(table_meta.add_column_key(), "mcc", "mcc", "ts2", ::openmldb::type::kAbsoluteTime, 0, 0); table_meta.set_mode(::openmldb::api::TableMode::kTableLeader); - bool ret = client.CreateTable(table_meta); - ASSERT_TRUE(ret); + ASSERT_TRUE(client.CreateTable(table_meta).OK()); uint64_t cur_time = ::baidu::common::timer::get_micros() / 1000; std::vector> dimensions; dimensions.push_back(std::make_pair("card0", 0)); @@ -582,8 +582,7 @@ TEST_P(SnapshotReplicaTest, LeaderAndFollowerTS) { std::vector row = {"card0", "mcc0", "1.3", std::to_string(cur_time), std::to_string(cur_time - 100)}; std::string value; sdk_codec.EncodeRow(row, &value); - ret = client.Put(tid, pid, cur_time, value, dimensions); - ASSERT_TRUE(ret); + ASSERT_TRUE(client.Put(tid, pid, cur_time, value, dimensions)); sleep(3); ::openmldb::test::TempPath temp_path; @@ -605,8 +604,7 @@ TEST_P(SnapshotReplicaTest, LeaderAndFollowerTS) { ::openmldb::client::TabletClient client1(follower_point, ""); client1.Init(); table_meta.set_mode(::openmldb::api::TableMode::kTableFollower); - ret = client1.CreateTable(table_meta); - ASSERT_TRUE(ret); + ASSERT_TRUE(client1.CreateTable(table_meta).OK()); client.AddReplica(tid, pid, follower_point); sleep(3); diff --git a/src/schema/index_util.cc b/src/schema/index_util.cc index 81ed5cd5177..fead40b5c70 100644 --- a/src/schema/index_util.cc +++ b/src/schema/index_util.cc @@ -80,19 +80,21 @@ base::Status IndexUtil::CheckIndex(const std::map FLAGS_absolute_ttl_max || ttl.lat_ttl() > FLAGS_latest_ttl_max) { - return false; +base::Status IndexUtil::CheckTTL(const ::openmldb::common::TTLSt& ttl) { + if (ttl.abs_ttl() > FLAGS_absolute_ttl_max) { + return {base::ReturnCode::kError, absl::StrCat("absolute ttl cannot be greater than ", FLAGS_absolute_ttl_max)}; + } else if (ttl.lat_ttl() > FLAGS_latest_ttl_max) { + return {base::ReturnCode::kError, absl::StrCat("latest ttl cannot be greater than ", FLAGS_latest_ttl_max)}; } - return true; + return {}; } bool IndexUtil::AddDefaultIndex(openmldb::nameserver::TableInfo* table_info) { diff --git a/src/schema/index_util.h b/src/schema/index_util.h index 465abac1ec1..31880fa0654 100644 --- a/src/schema/index_util.h +++ b/src/schema/index_util.h @@ -46,7 +46,7 @@ class IndexUtil { static base::Status CheckUnique(const PBIndex& index); - static bool CheckTTL(const ::openmldb::common::TTLSt& ttl); + static base::Status CheckTTL(const ::openmldb::common::TTLSt& ttl); static bool AddDefaultIndex(openmldb::nameserver::TableInfo* table_info); diff --git a/src/sdk/node_adapter.cc b/src/sdk/node_adapter.cc index bf64ac8d4bc..b148c8a4ca9 100644 --- a/src/sdk/node_adapter.cc +++ b/src/sdk/node_adapter.cc @@ -273,6 +273,10 @@ bool NodeAdapter::TransformToTableDef(::hybridse::node::CreatePlanNode* create_n *status = {hybridse::common::kUnsupportSql, "partitionnum should be great than 0"}; return false; } + if (storage_mode == hybridse::node::StorageMode::kUnknown) { + *status = {hybridse::common::kUnsupportSql, "invalid storage mode"}; + return false; + } // deny create table when invalid configuration in standalone mode if (!is_cluster_mode) { if (replica_num != 1) { @@ -298,7 +302,7 @@ bool NodeAdapter::TransformToTableDef(::hybridse::node::CreatePlanNode* create_n auto* column_def = dynamic_cast(column_desc); ::openmldb::common::ColumnDesc* add_column_desc = table->add_column_desc(); if (column_names.find(add_column_desc->name()) != column_names.end()) { - status->msg = "CREATE common: COLUMN NAME " + column_def->GetColumnName() + " duplicate"; + status->msg = "COLUMN NAME " + column_def->GetColumnName() + " duplicate"; status->code = hybridse::common::kUnsupportSql; return false; } @@ -307,7 +311,7 @@ bool NodeAdapter::TransformToTableDef(::hybridse::node::CreatePlanNode* create_n column_names.insert(std::make_pair(column_def->GetColumnName(), add_column_desc)); openmldb::type::DataType data_type; if (!openmldb::schema::SchemaAdapter::ConvertType(column_def->GetColumnType(), &data_type)) { - status->msg = "CREATE common: column type " + + status->msg = "column type " + hybridse::node::DataTypeName(column_def->GetColumnType()) + " is not supported"; status->code = hybridse::common::kUnsupportSql; return false; @@ -316,14 +320,14 @@ bool NodeAdapter::TransformToTableDef(::hybridse::node::CreatePlanNode* create_n auto default_val = column_def->GetDefaultValue(); if (default_val) { if (default_val->GetExprType() != hybridse::node::kExprPrimary) { - status->msg = "CREATE common: default value expression not supported"; + status->msg = "default value expression not supported"; status->code = hybridse::common::kTypeError; return false; } auto val = TransformDataType(*dynamic_cast(default_val), add_column_desc->data_type()); if (!val) { - status->msg = "CREATE common: default value type mismatch"; + status->msg = "default value type mismatch"; status->code = hybridse::common::kTypeError; return false; } @@ -339,7 +343,7 @@ bool NodeAdapter::TransformToTableDef(::hybridse::node::CreatePlanNode* create_n DCHECK(index_name.empty()); index_name = PlanAPI::GenerateName("INDEX", table->column_key_size()); if (index_names.find(index_name) != index_names.end()) { - status->msg = "CREATE common: INDEX NAME " + index_name + " duplicate"; + status->msg = "INDEX NAME " + index_name + " duplicate"; status->code = hybridse::common::kUnsupportSql; return false; } @@ -356,12 +360,12 @@ bool NodeAdapter::TransformToTableDef(::hybridse::node::CreatePlanNode* create_n } } if (!has_generate_index) { - status->msg = "CREATE common: can not found index col"; + status->msg = "can not found index col"; status->code = hybridse::common::kUnsupportSql; return false; } } else { - status->msg = "CREATE common: INDEX KEY empty"; + status->msg = "INDEX KEY empty"; status->code = hybridse::common::kUnsupportSql; return false; } @@ -401,7 +405,7 @@ bool NodeAdapter::TransformToTableDef(::hybridse::node::CreatePlanNode* create_n auto p_meta_node = dynamic_cast(partition_meta); const std::string& ep = p_meta_node->GetEndpoint(); if (endpoint_set.count(ep) > 0) { - status->msg = "CREATE common: partition meta endpoint duplicate"; + status->msg = "partition meta endpoint duplicate"; status->code = hybridse::common::kUnsupportSql; return false; } @@ -413,7 +417,7 @@ bool NodeAdapter::TransformToTableDef(::hybridse::node::CreatePlanNode* create_n } else if (p_meta_node->GetRoleType() == hybridse::node::kFollower) { meta->set_is_leader(false); } else { - status->msg = "CREATE common: role_type " + + status->msg = "role_type " + hybridse::node::RoleTypeName(p_meta_node->GetRoleType()) + " not support"; status->code = hybridse::common::kUnsupportSql; return false; @@ -475,7 +479,7 @@ bool NodeAdapter::TransformToColumnKey(hybridse::node::ColumnIndexNode* column_i std::transform(ttl_type.begin(), ttl_type.end(), ttl_type.begin(), ::tolower); openmldb::type::TTLType type; if (!::openmldb::codec::SchemaCodec::TTLTypeParse(ttl_type, &type)) { - status->msg = "CREATE common: ttl_type " + column_index->ttl_type() + " not support"; + status->msg = "ttl_type " + column_index->ttl_type() + " not support"; status->code = hybridse::common::kUnsupportSql; return false; } @@ -485,7 +489,7 @@ bool NodeAdapter::TransformToColumnKey(hybridse::node::ColumnIndexNode* column_i } if (ttl_st->ttl_type() == openmldb::type::kAbsoluteTime) { if (column_index->GetAbsTTL() == -1 || column_index->GetLatTTL() != -2) { - status->msg = "CREATE common: abs ttl format error or set lat ttl"; + status->msg = "abs ttl format error or set lat ttl"; status->code = hybridse::common::kUnsupportSql; return false; } @@ -498,7 +502,7 @@ bool NodeAdapter::TransformToColumnKey(hybridse::node::ColumnIndexNode* column_i } } else if (ttl_st->ttl_type() == openmldb::type::kLatestTime) { if (column_index->GetLatTTL() == -1 || column_index->GetAbsTTL() != -2) { - status->msg = "CREATE common: lat ttl format error"; + status->msg = "lat ttl format error"; status->code = hybridse::common::kUnsupportSql; return false; } @@ -510,7 +514,7 @@ bool NodeAdapter::TransformToColumnKey(hybridse::node::ColumnIndexNode* column_i } } else { if (column_index->GetAbsTTL() == -1) { - status->msg = "CREATE common: abs ttl format error for " + type::TTLType_Name(ttl_st->ttl_type()); + status->msg = "abs ttl format error for " + type::TTLType_Name(ttl_st->ttl_type()); status->code = hybridse::common::kUnsupportSql; return false; } @@ -520,7 +524,7 @@ bool NodeAdapter::TransformToColumnKey(hybridse::node::ColumnIndexNode* column_i ttl_st->set_abs_ttl(base::AbsTTLConvert(column_index->GetAbsTTL(), true)); } if (column_index->GetLatTTL() == -1) { - status->msg = "CREATE common: lat ttl format error for " + type::TTLType_Name(ttl_st->ttl_type()); + status->msg = "lat ttl format error for " + type::TTLType_Name(ttl_st->ttl_type()); status->code = hybridse::common::kUnsupportSql; return false; } @@ -535,7 +539,7 @@ bool NodeAdapter::TransformToColumnKey(hybridse::node::ColumnIndexNode* column_i if (!column_names.empty()) { auto it = column_names.find(column_index->GetTs()); if (it == column_names.end()) { - status->msg = "CREATE common: TS NAME " + column_index->GetTs() + " not exists"; + status->msg = "TS NAME " + column_index->GetTs() + " not exists"; status->code = hybridse::common::kUnsupportSql; return false; } diff --git a/src/test/util.cc b/src/test/util.cc index 59a772eed78..ed07eb642d4 100644 --- a/src/test/util.cc +++ b/src/test/util.cc @@ -280,5 +280,40 @@ std::string TempPath::CreateTempPath(const std::string& prefix) { return path; } +api::TableMeta CreateTableMeta(const std::string& name, uint32_t tid, uint32_t pid, + uint64_t abs_ttl, uint64_t lat_ttl, + bool leader, const std::vector& endpoints, + const ::openmldb::type::TTLType& type, uint32_t seg_cnt, uint64_t term, + ::openmldb::type::CompressType compress_type, + ::openmldb::common::StorageMode storage_mode) { + api::TableMeta table_meta; + table_meta.set_name(name); + table_meta.set_tid(tid); + table_meta.set_pid(pid); + table_meta.set_compress_type(compress_type); + table_meta.set_seg_cnt(seg_cnt); + table_meta.set_storage_mode(storage_mode); + if (leader) { + table_meta.set_mode(::openmldb::api::TableMode::kTableLeader); + table_meta.set_term(term); + } else { + table_meta.set_mode(::openmldb::api::TableMode::kTableFollower); + } + for (size_t i = 0; i < endpoints.size(); i++) { + table_meta.add_replicas(endpoints[i]); + } + ::openmldb::common::ColumnDesc* column_desc = table_meta.add_column_desc(); + column_desc->set_name("idx0"); + column_desc->set_data_type(::openmldb::type::kString); + ::openmldb::common::ColumnKey* index = table_meta.add_column_key(); + index->set_index_name("idx0"); + index->add_col_name("idx0"); + ::openmldb::common::TTLSt* ttl = index->mutable_ttl(); + ttl->set_abs_ttl(abs_ttl); + ttl->set_lat_ttl(lat_ttl); + ttl->set_ttl_type(type); + return table_meta; +} + } // namespace test } // namespace openmldb diff --git a/src/test/util.h b/src/test/util.h index db466995dde..02bd3fd2a04 100644 --- a/src/test/util.h +++ b/src/test/util.h @@ -100,6 +100,14 @@ std::string GetExeDir(); std::string GetParentDir(const std::string& path); +api::TableMeta CreateTableMeta(const std::string& name, uint32_t tid, uint32_t pid, + uint64_t abs_ttl, uint64_t lat_ttl, + bool leader, const std::vector& endpoints, + const ::openmldb::type::TTLType& type, uint32_t seg_cnt, uint64_t term, + ::openmldb::type::CompressType compress_type, + common::StorageMode storage_mode = ::openmldb::common::kMemory); + + } // namespace test } // namespace openmldb #endif // SRC_TEST_UTIL_H_ From e796978518bdb0148d7bb047eeb58f28af63bf9c Mon Sep 17 00:00:00 2001 From: TanZiYen <104113819+TanZiYen@users.noreply.github.com> Date: Mon, 4 Sep 2023 14:58:29 +0800 Subject: [PATCH 28/63] docs: update the about section (#3447) --- docs/en/about/community.md | 28 ++++++++++++++++++++++++++++ docs/en/about/index.rst | 7 ++++--- docs/en/about/milestones.md | 37 ------------------------------------- 3 files changed, 32 insertions(+), 40 deletions(-) create mode 100644 docs/en/about/community.md delete mode 100644 docs/en/about/milestones.md diff --git a/docs/en/about/community.md b/docs/en/about/community.md new file mode 100644 index 00000000000..6d76249a3b9 --- /dev/null +++ b/docs/en/about/community.md @@ -0,0 +1,28 @@ +# Community and support + +If you have any inquiries, please feel free to ask at any time for professional technical support. We welcome you to join the OpenMLDB community for collaborations! + +## Resources + +Here are the various platforms and channels: +- [OpenMLDB official blogs](https://openmldb.ai/en/blog): We publish blogs related to product updates, event updates, technical details, meetup reviews, and professional interviews. +- [GitHub repo](https://github.com/4paradigm/OpenMLDB): The GitHub repository hosting the open-sourced code. +- [GitHub Issues](https://github.com/4paradigm/OpenMLDB/issues): To submit bug reports and feedback on product requirements. +- [GitHub Discussions](https://github.com/4paradigm/OpenMLDB/discussions): To discuss any topics related to OpenMLDB. +- [Developer email groups and mailing lists](https://groups.google.com/g/openmldb-developers). +- Email: You can reach out to OpenMLDB via email at [contact@openmldb.ai](https://partner.outlook.cn/mail/deeplink/compose?mailtouri=mailto%3Acontact@openmldb.ai). +- [Slack](https://join.slack.com/t/openmldb/shared_invite/zt-ozu3llie-K~hn9Ss1GZcFW2~K_L5sMg). +- WeChat official OpenMLDB account and WeChat chat group (for Chinese users): Scan the QR Code to join the Chinese WeChat chat group. + +![wechat](images/wechat.png) + + +## Contribute to OpenMLDB + +OpenMLDB is a result of the collective efforts of all contributors for which we express our sincere gratitude! +- If you are interested in becoming an OpenMLDB contributor, please read the [Contribution Guidelines](https://github.com/4paradigm/OpenMLDB/blob/main/CONTRIBUTING.md) before submitting your code. +- To understand the different levels of open-source tasks, you can refer to [The Different Levels of Developer Documentation](https://go005qabor.feishu.cn/docs/doccn7oEU0AlCOGtYz09chIebzd). +- If you are new, you can start by exploring the [Good First Issue](https://github.com/4paradigm/OpenMLDB/issues?q=is%3Aopen+is%3Aissue+label%3A"good+first+issue") list. +- For contributors with more experience, please check the issues tagged with [Call-for-Contributions](https://github.com/4paradigm/OpenMLDB/issues?q=is%3Aopen+is%3Aissue+label%3Acall-for-contributions). +- We have also created a [User List](https://github.com/4paradigm/OpenMLDB/discussions/707) where community users can provide feedback, share use cases, opinions, or any other feedback related to OpenMLDB. +- If you would like to understand the OpenMLDB product planning, you can refer to our [Roadmap](https://github.com/4paradigm/OpenMLDB/projects/10) and leave your comments. diff --git a/docs/en/about/index.rst b/docs/en/about/index.rst index 84875542a5b..175d1a9720d 100644 --- a/docs/en/about/index.rst +++ b/docs/en/about/index.rst @@ -5,7 +5,8 @@ About .. toctree:: :maxdepth: 1 - intro - milestones - release_notes + intro + community + milestones + change_logs diff --git a/docs/en/about/milestones.md b/docs/en/about/milestones.md deleted file mode 100644 index 196835db86c..00000000000 --- a/docs/en/about/milestones.md +++ /dev/null @@ -1,37 +0,0 @@ -# Milestones - -## 1. History - -OpenMLDB originated as an important module of the commercial AI platform Sage from [4Paradigm](https://www.4paradigm.com), an industry-leading artificial intelligence platform and service provider. The AI platform Sage covers the complete life cycle of MLOps, including data governance, feature engineering, model training, inference, and model management. The product has been deployed in hundreds of enterprise-grade scenarios. - -In 2021, the development team abstracted, enhanced, and added more community-friendly features to the core module of data governance and feature engineering in the commercial products, and finally made it publicly available as an open-source project OpenMLDB, which provides a feature platform for machine learning applications. Today, OpenMLDB grows based on the open source and open community, and hopes to help more enterprises complete the AI transformation with low cost and high quality. - -## 2. Milestones - -### 2017.2 - 2021.5: The Commercial Product MLDB - -- 2017.2: The first code commit -- 2017.9: The first financial industry customer, China Guangfa Bank -- 2019.8: The first retail industry customer, Yum China -- 2020.12: The number of deployed scenarios exceeds 100 - -### 2021.6 - Today: The Open-Source Project OpenMLDB - -- 2021.6: Machine learning database OpenMLDB officially announced open source, providing a production-ready feature platform for machine learning -- 2021.7: v0.2.0 released, supporting RestAPI access, code style and comments improved -- 2021.8: OpenMLDB-based optimization innovation paper is published at VLDB 2021, the top international academic conference on databases -- 2021.8: Recognized by Gitee and awarded the title of GVP (Gitee Most Valuable Project) -- 2021.9: The first community-based enterprise user, [Akulaku](https://www.akulaku.com/) -- 2021.9: Won the "Open Source Community and Open Source Project" award from the Academy of Information and Communications Technology -- 2021.11: v0.3.0 released, supporting standalone mode for rapid deployment -- 2021.12: OpenMLDB Contributor Program launched -- 2021.12: Host the first Feature Store Meetup in China -- 2022.1: v0.4.0 released, enhancing the SQL-centric development experience, introducing the new online monitoring module -- 2022.1: OpenMLDB first community meeting -- 2022.5: v0.5.0 released, supporting long window and persistent storage engine, registering udf from external dynamic library - -## 3. Roadmap - -In the future, we will aggressively embrace the technologies that can effectively reduce the total cost of adopting machine learning, such as cloud-native and AutoML. - -For the roadmap of recent releases, please refer to our public page of [Roadmap](https://github.com/4paradigm/OpenMLDB/projects/10). From 0fa0fe92312a8ee8d8f1580c8b75a9bb0d93be31 Mon Sep 17 00:00:00 2001 From: TanZiYen <104113819+TanZiYen@users.noreply.github.com> Date: Mon, 4 Sep 2023 15:15:05 +0800 Subject: [PATCH 29/63] docs: add deploy documentation (#3463) --- docs/en/deploy/index.rst | 2 + .../en/deploy/offline_integrate_kubernetes.md | 70 ++++++++++++++ docs/en/tutorial/index.rst | 1 + docs/en/tutorial/online_offline_sync.md | 92 +++++++++++++++++++ 4 files changed, 165 insertions(+) create mode 100644 docs/en/deploy/offline_integrate_kubernetes.md create mode 100644 docs/en/tutorial/online_offline_sync.md diff --git a/docs/en/deploy/index.rst b/docs/en/deploy/index.rst index 04c5e257d51..11f12343dcd 100644 --- a/docs/en/deploy/index.rst +++ b/docs/en/deploy/index.rst @@ -9,4 +9,6 @@ Deployment conf compile integrate_hadoop + offline_integrate_kubernetes + [Alpha] Online engine based on Kubernetes deployment diff --git a/docs/en/deploy/offline_integrate_kubernetes.md b/docs/en/deploy/offline_integrate_kubernetes.md new file mode 100644 index 00000000000..6e5d2b0658e --- /dev/null +++ b/docs/en/deploy/offline_integrate_kubernetes.md @@ -0,0 +1,70 @@ +# [Alpha] Offline engine using Kubernetes backend (optional) + +## Introduction + +The OpenMLDB offline engine offers support for the integration of Kubernetes services. Users can configure Kubernetes clusters to schedule and execute offline tasks and utilize distributed storage services like HDFS to handle offline data management. + +## Deploy Kubernetes + +- To deploy Kubernetes in either stand-alone or cluster: please refer to [Kubernetes official documentation](https://kubernetes.io/docs/home/) + +- Deploy Kubernetes operator for Apache Spark: please refer to the [spark-on-k8s-operator official documentation](https://github.com/GoogleCloudPlatform/spark-on-k8s-operator). Below is an example of `Helm` deploying to the `default` command space, which can modify command space and permission information as needed. + +``` +helm install my-release spark-operator/spark-operator --namespace default --create-namespace --set webhook.enable=true + +kubectl create serviceaccount spark --namespace default + +kubectl create clusterrolebinding binding --clusterrole=edit --serviceaccount=default:spark +``` + +Once you have successfully deployed the spark operator, you can utilize the provided code examples to test the submission of Spark tasks and verify if they execute properly. + +## HDFS Support + +If you want to configure Kubernetes tasks to access HDFS data, you will need to prepare a Hadoop configuration file and create a ConfigMap beforehand. An example command is shown below, you can modify the ConfigMap name and file path accordingly. + +``` +kubectl create configmap hadoop-config --from-file=/tmp/hadoop/etc/ +``` + +## Configure Kubernetes with TaskManager + +You can specify Kubernetes configurations in the TaskManager configuration file. The relevant configurations are outlined below. + +| Config | Type | Note | +| ------ | ---- | ---- | +| spark.master | String | Supports "kuberenetes" or "k8s" | +| offline.data.prefix | String | Recommended to use HDFS path | +| k8s.hadoop.configmap | String | Default setting as "hadoop-config" | +| k8s.mount.local.path | String | Default setting as "/tmp" | + +When in Kubernetes mode, it is recommended to configure the offline storage path as HDFS path, to ensure smooth execution in the cluster. Failure to do so may result in failure to read/write data. An example of the configuration is provided below. + +``` +offline.data.prefix=hdfs:///foo/bar/ +``` + +## Submission and management of the task + +Once you have configured TaskManager and Kubernetes, you can submit offline tasks through command line. The process is similar to using Local or Yarn mode. It can be used in SQL CLI and SDKs of various languages. + +Here is an example of submitting a load data task. + +``` +LOAD DATA INFILE 'hdfs:///hosts' INTO TABLE db1.t1 OPTIONS(delimiter = ',', mode='overwrite'); +``` + +Checking of Hadoop ConfigMap content. + +``` +kubectl get configmap hdfs-config -o yaml +``` + +Checking of Spark task and Pod content and log. + +``` +kubectl get SparkApplication + +kubectl get pods +``` diff --git a/docs/en/tutorial/index.rst b/docs/en/tutorial/index.rst index 4c5e2ab266e..fbbd84eda26 100644 --- a/docs/en/tutorial/index.rst +++ b/docs/en/tutorial/index.rst @@ -13,3 +13,4 @@ Tutorials openmldbspark_distribution autofe common_architecture + online_offline_sync diff --git a/docs/en/tutorial/online_offline_sync.md b/docs/en/tutorial/online_offline_sync.md new file mode 100644 index 00000000000..b84881c35d1 --- /dev/null +++ b/docs/en/tutorial/online_offline_sync.md @@ -0,0 +1,92 @@ +# Online-offline data synchronization + +Online-offline data synchronization involves taking online data and synchronizing it to an offline location. The offline location refers to a large capacity persistent storage location, which users can specify, and is not limited to the offline data location of OpenMLDB table. Currently, we only support disk table synchronization and support only writing to the HDFS cluster. + +To enable online-offline synchronization, DataCollector and SyncTool are required to be deployed. For SyncTool, only a single deployment is supported. For DataCollector, it is required to be deployed on all the machines that have TabletServer deployed. For example, It is possible to have multiple TabletServers on a single machine, and the synchronization task will utilize the DataCollector deployed on that particular machine. If additional DataCollectors are deployed, they will not be in function until the currently running DataCollector is offline, after which another one will take over. + +Although SyncTool supports single deployment, it can resume the work progress without requiring any additional actions when it restarts. + +## Deployment method + +To ensure proper functionality, it is important to note that SyncTool might attempt to allocate synchronization tasks even if there is no active DataCollector if it is started first. Therefore, it is recommended to start all the DataCollectors before starting SyncTool. + +Online-offline synchronization requires the version of TabletServer to be newer than 0.7.3. It is crucial not to use an older version of TabletServer for synchronization purposes. DataCollector is in `bin` and SyncTool is in `synctool`. Both can be started by `bin/start.sh`. + +### DataCollector + +#### Configuration (Important) + +Update `/conf/data_collector.flags` configuration file. Ensure that you have entered the correct ZooKeeper (zk) location and path in the configuration. Additionally, make sure that the 'endpoint' configuration does not conflict with any ports. It is crucial for the endpoint to be consistent with the TabletServer configuration. If the TabletServer uses the local public IP and the DataCollector endpoint uses 127.0.0.1, automatic conversion will not occur. + +Pay close attention when selecting the `collector_datadir`. Since we will be setting up hard link to the disk data of the TabletServer during synchronization, the `collector_datadir` should be on the same disk as the data location of the TabletServer's `hdd_root_path`/`ssd_root_path`. Otherwise, an error `Invalid cross-device link` will occur. + +The default value for `max_pack_size` is set to 1M. If there are too many synchronization tasks, you may encounter the error message `[E1011]The server is overcrowded`. In such cases, it is recommended to reduce this parameter. Additionally, you can adjust the `socket_max_unwritten_bytes` parameter to increase the write cache tolerance. + +#### Start + +``` +./bin/start.sh start data_collector +``` +#### Check status + +Run the command after starting to get the real-time DataCollector RPC status. If it fails to show, please check the log. +``` +curl http:///status +``` +Currently, it is not possible to directly query the list of `DataCollector` instances. However, support for this functionality will be included in future updates of the relevant tools. + +### SyncTool + +#### Configuration +- To update, please modify the `/conf/synctool.properties` configuration file. This will overwrite `/synctool/conf/synctool.properties` when start. +- Currently, the SyncTool only supports direct writing to HDFS. To configure the HDFS connections, you can modify the `hadoop.conf.dir` parameter in the properties file or set the `HADOOP_CONF_DIR` environment variable. Ensure that the OS startup user of SyncTool has write access to the HDFS path specified when creating each synchronization task. + + Once the configurations have been updated, you can proceed with starting the tool. + +#### Start +``` +./bin/start.sh start synctool +``` + +Currently, SyncTool operates as a single process. If multiple instances are started, they operate independently of each other. It is the user's responsibility to ensure that there are no duplicated synchronization tasks. SyncTool saves the progress of the processes in real-time, allowing for immediate recovery if a process goes offline. + +SyncTool manages synchronization tasks, collects data, and writes it to offline locations. When a user submits a "table synchronization task", SyncTool divides it into multiple "sharded synchronization tasks" (referred to as subtasks) for creation and management. + +In task management, if a DataCollector instance goes offline or encounters an error, SyncTool will request it to recreate the task. If a suitable DataCollector is not available for task reassignment, the task will be marked as failed. If we don't do this, SyncTool will continue to attempt task recreation, which can result in slow progress and make it difficult to identify errors. Therefore, in order to better identify errors, we mark the task as failed. + +Since the creation of a table synchronization task does not support starting from a specific point, it is not advisable to delete and recreate the task. Doing so may result in duplicated data if the destination is the same. Instead, consider changing the synchronization destination or restarting SyncTool. During the recovery process, SyncTool does not check if the subtask has previously failed, but rather starts the task in an "init" state. + +#### SyncTool Helper + +For create, delete, and query synchronizing tasks, please use `/tools/synctool_helper.py`. + +```bash +# create +python tools/synctool_helper.py create -t db.table -m 1 -ts 233 -d /tmp/hdfs-dest [ -s ] +# delete +python tools/synctool_helper.py delete -t db.table [ -s ] +# task status +python tools/synctool_helper.py status [ -s ] +# sync tool status for dev +python tools/synctool_helper.py tool-status [ -f ] +``` + + +To configure the Mode setting, you can choose between 0, 1, or 2. Each mode corresponds to a different type of synchronization: + +- Mode 0: Full synchronization (FULL) +- Mode 1: Incremental synchronization based on time (INCREMENTAL_BY_TIMESTAMP). Use `-ts` to configure the start time, and data prior to this time will not be synchronized. +- Mode 2: Full and continuous incremental synchronization (FULL_AND_CONTINUOUS) + +In Mode 0, there is no strict end time. Each subtask will synchronize the current data and then terminate, deleting the entire table task. If a helper is used to query the status and no synchronization task is found for the table, it indicates that the synchronization task for that table has been completed. However, Mode 1 and 2 will continue running indefinitely. + +When executing the `status` command, the status of each partition task will be displayed. If you only need an overview, you can focus on the content after the `table scope`, which shows the synchronization task status at the table level. If there are any `FAILED` subtasks for a table, it will be indicated. + +Pay attention to the `status` field for each subtask. If it has just started and hasn't received the first data synchronization from the DataCollector, it will be in the INIT state. Once it receives the first data synchronization, it will change to the RUNNING state. When SyncTool restarts, if the synchronization task resumes, it will enter the RUNNING state directly. The task may enter the REASSIGNING state temporarily during the process, which is an intermediate state and doesn't mean the task is no longer available. In Mode 0, a SUCCESS status may appear, indicating that the task has been completed. Once all subtasks for a table are completed, SyncTool will automatically remove the tasks, and the tasks of the table will not be found when using a helper to query. + +Only the `FAILED` status indicates that the subtask has failed and will not be retried or deleted automatically. After identifying the cause of the failure and fixing it, you can delete and restart the synchronization task. If you don't want to lose the progress, you can restart SyncTool to allow it to continue the recovery task and resume synchronization. However, be aware that this may result in duplicated data. + +## Functional boundaries + +DataCollector does not mark the progress (snapshot progress) uniquely. If a subtask shuts down midway and a new task is created with the current progress, it may result in duplicated data. +In SyncTool, data is first written to HDFS and then the progress is saved. If SyncTool shuts down during this process, and the progress has not been updated, it may result in duplicated data. From b7d6edcb3d4280c4bad710ddbda31bc23072f958 Mon Sep 17 00:00:00 2001 From: HuangWei Date: Fri, 8 Sep 2023 13:07:45 +0800 Subject: [PATCH 30/63] feat: use user sync timeout when it > default (#3484) * feat: use user sync timeout when it > default * fix doc --- docs/en/reference/sql/ddl/SET_STATEMENT.md | 4 +++- docs/zh/openmldb_sql/ddl/SET_STATEMENT.md | 8 +++++--- docs/zh/quickstart/cli.md | 2 ++ docs/zh/quickstart/sdk/java_sdk.md | 2 ++ docs/zh/quickstart/sdk/python_sdk.md | 2 ++ src/sdk/sql_cluster_router.cc | 8 ++++++-- 6 files changed, 20 insertions(+), 6 deletions(-) diff --git a/docs/en/reference/sql/ddl/SET_STATEMENT.md b/docs/en/reference/sql/ddl/SET_STATEMENT.md index abb570a4eca..c2e8416fc67 100644 --- a/docs/en/reference/sql/ddl/SET_STATEMENT.md +++ b/docs/en/reference/sql/ddl/SET_STATEMENT.md @@ -158,7 +158,9 @@ CREATE TABLE t1 (col0 STRING, col1 int, std_time TIMESTAMP, INDEX(KEY=col1, TS=s ```{caution} If offline sync job is longer than 30min(the default timeout for offline sync job), you should change the config of TaskManager and client. - set `server.channel_keep_alive_time` bigger in TaskManager config file. -- set `--sync_job_timeout` of sql client, less than `server.channel_keep_alive_time`. SDK can't change the config now. +- choose one: + - set a bigger session job_timeout, we'll use `max(session_job_timeout, default_gflag_sync_job_timeout)`. + - set `--sync_job_timeout` of sql client, less than `server.channel_keep_alive_time`. SDK can't change the config now. ``` - Set the waiting time for offline async commands or offline admin commands (in milliseconds): diff --git a/docs/zh/openmldb_sql/ddl/SET_STATEMENT.md b/docs/zh/openmldb_sql/ddl/SET_STATEMENT.md index 00e14200b22..0284e37e19f 100644 --- a/docs/zh/openmldb_sql/ddl/SET_STATEMENT.md +++ b/docs/zh/openmldb_sql/ddl/SET_STATEMENT.md @@ -148,7 +148,7 @@ CREATE TABLE t1 (col0 STRING, col1 int, std_time TIMESTAMP, INDEX(KEY=col1, TS=s ### 离线命令配置详情 -- 设置离线命令同步执行,同步的超时时间将自动设置为30min(gflag `sync_job_timeout`,同时也是与TaskManager的最大超时时间,仅手动设置更大的此超时时间是无意义的): +- 设置离线命令同步执行,同步的超时时间将自动设置为30min(gflag `sync_job_timeout`,同时也是与TaskManager的最大超时时间),通常情况不用手动设置`job_timeout`: ```sql > SET @@sync_job = "true"; @@ -156,8 +156,10 @@ CREATE TABLE t1 (col0 STRING, col1 int, std_time TIMESTAMP, INDEX(KEY=col1, TS=s ```{caution} 如果离线同步命令执行时间超过30min,需要同时调整TaskManager配置和客户端的配置。 -- 调大TaskManager的`server.channel_keep_alive_time` -- 配置客户端`--sync_job_timeout`,不可大于`server.channel_keep_alive_time`。SDK暂不支持修改。 +- 调大TaskManager的`server.channel_keep_alive_time`,单位为秒。 +- 二选一: + - 手动设置更大的会话超时时间,我们会使用`max(session_job_timeout, default_gflag_sync_job_timeout)`。 + - 配置客户端`--sync_job_timeout`,不可大于`server.channel_keep_alive_time`,不用手动`SET`。SDK暂不支持修改。 ``` - 设置离线异步命令或离线管理命令的等待时间,单位为毫秒,默认为1min: diff --git a/docs/zh/quickstart/cli.md b/docs/zh/quickstart/cli.md index 787ce2203d1..fb644b32a6c 100644 --- a/docs/zh/quickstart/cli.md +++ b/docs/zh/quickstart/cli.md @@ -5,6 +5,8 @@ CLI,指OpenMLDB的shell客户端,使用发布包`bin`中的binary程序`open 如果是使用sbin部署的集群,可以使用sbin中的openmldb-cli.sh脚本启动CLI,脚本会自动填充参数。例如,在demo集群中,启动CLI的命令为`OPENMLDB_MODE=cluster /work/openmldb/sbin/openmldb-cli.sh`。 ``` +CLI默认执行模式为离线,其他SDK的默认可能不同。 + 本文将介绍常用的一些CLI可配置项与便利的非交互式使用方法,完整的可配置项见`bin/openmldb --help`。 ## 基本使用 diff --git a/docs/zh/quickstart/sdk/java_sdk.md b/docs/zh/quickstart/sdk/java_sdk.md index 17e5019a5b3..3f8534518b4 100644 --- a/docs/zh/quickstart/sdk/java_sdk.md +++ b/docs/zh/quickstart/sdk/java_sdk.md @@ -1,5 +1,7 @@ # Java SDK +Java SDK中,JDBC Statement的默认执行模式为在线,SqlClusterExecutor的默认执行模式则是离线,请注意。 + ## Java SDK 包安装 - Linux 下 Java SDK 包安装 diff --git a/docs/zh/quickstart/sdk/python_sdk.md b/docs/zh/quickstart/sdk/python_sdk.md index 9a4640f7528..69544b81db7 100644 --- a/docs/zh/quickstart/sdk/python_sdk.md +++ b/docs/zh/quickstart/sdk/python_sdk.md @@ -1,5 +1,7 @@ # Python SDK +Python SDK默认执行模式为在线。 + ## Python SDK 包安装 执行以下命令安装 Python SDK 包: diff --git a/src/sdk/sql_cluster_router.cc b/src/sdk/sql_cluster_router.cc index 7e593d8b359..022e5664f7c 100644 --- a/src/sdk/sql_cluster_router.cc +++ b/src/sdk/sql_cluster_router.cc @@ -2410,9 +2410,13 @@ std::shared_ptr SQLClusterRouter::ExecuteSQL(const std std::shared_ptr SQLClusterRouter::ExecuteSQL(const std::string& db, const std::string& sql, hybridse::sdk::Status* status) { - // To avoid setting sync job timeout by user, we set offline_job_timeout to the biggest value + // To avoid small sync job timeout, we set offline_job_timeout to the biggest value, user can set > + // FLAGS_sync_job_timeout auto sync_job = IsSyncJob(); - auto timeout = sync_job ? FLAGS_sync_job_timeout : GetJobTimeout(); + auto timeout = GetJobTimeout(); + if (sync_job && FLAGS_sync_job_timeout > timeout) { + timeout = FLAGS_sync_job_timeout; + } return ExecuteSQL(db, sql, IsOnlineMode(), sync_job, timeout, status); } From 21f98d4f7308bfbc2824242d33c69a47da89b40e Mon Sep 17 00:00:00 2001 From: dl239 Date: Fri, 8 Sep 2023 13:47:40 +0800 Subject: [PATCH 31/63] docs: add cluster restart docs (#3498) --- docs/zh/maintain/backup.md | 2 +- docs/zh/maintain/index.rst | 3 +- docs/zh/maintain/multi_cluster.md | 4 +- docs/zh/maintain/openmldb_ops.md | 2 +- docs/zh/maintain/restart.md | 184 ++++++++++++++++++++++++++++++ docs/zh/maintain/update_conf.md | 9 +- docs/zh/maintain/upgrade.md | 17 ++- docs/zh/openmldb_sql/notice.md | 2 +- 8 files changed, 209 insertions(+), 14 deletions(-) create mode 100644 docs/zh/maintain/restart.md diff --git a/docs/zh/maintain/backup.md b/docs/zh/maintain/backup.md index 2d0ddb43ae5..2b05251e334 100644 --- a/docs/zh/maintain/backup.md +++ b/docs/zh/maintain/backup.md @@ -1,4 +1,4 @@ -# 高可用 +# 高可用和恢复 ## 最佳实践 diff --git a/docs/zh/maintain/index.rst b/docs/zh/maintain/index.rst index 8dc28ecfb18..a114cccef15 100644 --- a/docs/zh/maintain/index.rst +++ b/docs/zh/maintain/index.rst @@ -5,11 +5,12 @@ .. toctree:: :maxdepth: 1 - upgrade + restart update_conf backup scale monitoring + upgrade cli status multi_cluster diff --git a/docs/zh/maintain/multi_cluster.md b/docs/zh/maintain/multi_cluster.md index e9873f97e75..9729e633342 100644 --- a/docs/zh/maintain/multi_cluster.md +++ b/docs/zh/maintain/multi_cluster.md @@ -1,4 +1,4 @@ -# [Alpha] 跨机房容灾方案 +# [Alpha] 跨机房容灾 ## 背景 @@ -177,4 +177,4 @@ switchmode leader 目前主从集群方案在实验阶段的 alpha 特性,其核心功能都已经开发完毕,并且进行了充分的测试。目前主要遗留问题为: -- SQL 命令目前只有创建表、删除表、以及数据插入操作可以主从集群自动同步,其余命令(比如 SQL 上线,修改 TTL 等)目前尚不支持自动同步,需要手动在主从集群上分别执行。 \ No newline at end of file +- SQL 命令目前只有创建表、删除表、以及数据插入操作可以主从集群自动同步,其余命令(比如 SQL 上线,修改 TTL 等)目前尚不支持自动同步,需要手动在主从集群上分别执行。 diff --git a/docs/zh/maintain/openmldb_ops.md b/docs/zh/maintain/openmldb_ops.md index 84b45afde23..31c4db3a8aa 100644 --- a/docs/zh/maintain/openmldb_ops.md +++ b/docs/zh/maintain/openmldb_ops.md @@ -1,4 +1,4 @@ -# OpenMLDB运维工具 +# 运维工具 ## 概述 diff --git a/docs/zh/maintain/restart.md b/docs/zh/maintain/restart.md new file mode 100644 index 00000000000..61813bf7921 --- /dev/null +++ b/docs/zh/maintain/restart.md @@ -0,0 +1,184 @@ +# 集群启停 +OpenMLDB集群目前有两种部署方式,[一键部署和手动部署](../deploy/install_deploy.md)。本文针对这两种不同的部署方式,分别描述启停步骤。注意,请不要将两种部署方式的的启停方法混合使用,可能会导致不可预知的问题。 + +```{important} +如果是第一次部署和启动集群,请参考完整的[安装部署文档](../deploy/install_deploy.md),需要额外的配置和注意事项。本文针对已经完成部署的集群,因为各类原因(比如例行维护、升级、配置更新等),而涉及到启动、停止、重启操作的场景。 +``` + + +## 一键部署的集群 + +### 启动集群 + +到部署集群的目录执行,确保该目录下 conf/hosts 文件内容和实际各组件的部署信息一致 + +``` +sbin/start-all.sh +``` + +### 停止集群 + +到部署集群的目录执行,确保该目录下 conf/hosts 文件内容和实际各组件的部署信息一致 +``` +sbin/stop-all.sh +``` + +## 手动部署的集群 + +### 启动集群 + +**1. 启动TabletServer** + +到每一个部署 TabletServer 节点的部署目录里执行如下命令 + +``` +bash bin/start.sh start tablet +``` + +启动后应有`success`提示,如下所示。 + +``` +Starting tablet ... +Start tablet success +``` + +**2. 启动 NameServer** + +到每一个部署 NameServer 节点的部署目录里执行如下命令 + +``` +bash bin/start.sh start nameserver +``` + +启动后应有`success`提示,如下所示。 + +``` +Starting nameserver ... +Start nameserver success +``` + +**3. 启动 TaskManager** + +如果没有部署 TaskManager 可以跳过此步 +到每一个部署 TaskManager 节点的部署目录里执行如下命令 + +``` +bash bin/start.sh start taskmanager +``` + +**4. 启动 APIServer** + +如果没有部署 APIServer 可以跳过此步 +到每一个部署 APIServer 节点的部署目录里执行如下命令 + +``` +bash bin/start.sh start apiserver +``` + +启动后应有`success`提示,如下所示。 + +``` +Starting apiserver ... +Start apiserver success +``` + +**5. 检查服务是否启动** + +启动 sql_client,其中`zk_cluster`和`zk_root_path`的值替换为配置文件中对应的值 + +```bash +./bin/openmldb --zk_cluster=172.27.2.52:12200 --zk_root_path=/openmldb --role=sql_client +``` + +然后执行如下命令 + +``` +show components; +``` + +结果应类似下表,包含所有集群的组件(APIServer除外)。 + +``` +------------------- ------------ --------------------- -------- --------- + Endpoint Role Connect_time Status Ns_role + ------------------- ------------ --------------------- -------- --------- + 172.24.4.39:10821 tablet 2023-09-01 11:36:58 online NULL + 172.24.4.40:10821 tablet 2023-09-01 11:36:57 online NULL + 172.24.4.56:10821 tablet 2023-09-01 11:36:58 online NULL + 172.24.4.40:7520 nameserver 2023-09-01 11:36:59 online master + ------------------- ------------ --------------------- -------- --------- + +4 rows in set +``` + +**6. 开启 auto_failover** + +启动 ns_client,其中`zk_cluster`和`zk_root_path`的值替换为配置文件中对应的值 + +``` +./bin/openmldb --zk_cluster=172.27.2.52:12200 --zk_root_path=/openmldb --role=ns_client +``` + +执行如下命令 + +``` +confset auto_failover true +``` + +**7. 恢复数据** + +使用OpenMLDB运维工具中的一键数据恢复来恢复数据。关于运维工具的使用和说明参考[OpenMLDB运维工具](./openmldb_ops.md) + +到任意一个OpenMLDB的部署目录中执行如下命令,其中`zk_cluster`和`zk_root_path`的值替换为配置文件中对应的值。此命令只需要执行一次,执行过程中不要中断 + +``` +python tools/openmldb_ops.py --openmldb_bin_path=./bin/openmldb --zk_cluster=172.27.2.52:12200 --zk_root_path=/openmldb --cmd=recoverdata +``` + +### 停止集群 + +**1. 关闭 auto_failover** + +启动 ns_client,其中`zk_cluster`和`zk_root_path`的值替换为配置文件中对应的值 +``` +./bin/openmldb --zk_cluster=172.27.2.52:12200 --zk_root_path=/openmldb --role=ns_client +``` +执行如下命令 +``` +confset auto_failover false +``` + +**2. 停止 TabletServer** + +到每一个部署 TabletServer 节点的部署目录里执行如下命令 +``` +bash bin/start.sh stop tablet +``` +**3. 停止 NameServer** + +到每一个部署 NameServer 节点的部署目录里执行如下命令 +``` +bash bin/start.sh stop nameserver +``` +**4. 停止 TaskManager** + +如果没有部署 TaskManager 可以跳过此步 +到每一个部署 TaskManager 节点的部署目录里执行如下命令 +``` +bash bin/start.sh stop taskmanager +``` + +**5. 停止 APIServer** + +如果没有部署 APIServer 可以跳过此步 +到每一个部署 TaskManager 节点的部署目录里执行如下命令 +``` +bash bin/start.sh stop apiserver +``` + +## 集群重启 + +集群重启分为两种情况,一种是对于版本升级或者更新配置文件需要,进行集群重启;还有一种是不需要进行升级和配置,正常状态下的重启(比如例行维护)。 + +- 版本升级或者更新配置文件:具体步骤查看相关操作文档进行操作,[版本升级](upgrade.md),[更新配置文件](update_conf.md)。特别注意的是,对于 tablet 的重启,务必进行顺序化操作,不要对多个 tablet 同时进行升级或者配置。需要等一个 tablet 完成升级/配置以后,确认重启状态,再进行下一个 tablet 的操作。具体操作查看版本升级和更新配置文件文档的“重启结果确认”章节。 +- 正常重启:如果不涉及到版本升级或者更新配置文件,那么依次执行上述章节的停止集群和启动集群操作即可。 diff --git a/docs/zh/maintain/update_conf.md b/docs/zh/maintain/update_conf.md index bd40224861b..0b7e095e381 100644 --- a/docs/zh/maintain/update_conf.md +++ b/docs/zh/maintain/update_conf.md @@ -18,7 +18,12 @@ ## 更新 tablet 配置文件 +```{important} +如果有多个 tablet,请务必对每一个 tablet 进行顺序操作,不要同时对多个 tablet 进行更新配置操作。即对一个 tablet 完成配置更新,结果确认以后,再进行下一个 tablet 的更新配置操作。否则会导致集群状态异常。如果误操作导致集群异常,可以尝试使用[运维工具](openmldb_ops.md)的 `recoverdata` 进行恢复。 +``` + 更新过程对服务的影响: + * 如果创建的表是单副本,用户可以选择: - 通过`pre-upgrade`和`post-upgrade`,重启前自动添加副本,重启后自动删除新添加的副本。这样行为和多副本保持一致 - 如果允许单副本表在重启过程中不可用,可以在`pre-upgrade`的时候添加`--allow_single_replica`选项,在内存紧张的环境下,可以避免添加副本可能造成的OOM @@ -47,7 +52,7 @@ ```bash python tools/openmldb_ops.py --openmldb_bin_path=./bin/openmldb --zk_cluster=172.24.4.40:30481 --zk_root_path=/openmldb --cmd=post-upgrade --endpoints=127.0.0.1:10921 ``` - + ### 重启结果确认 * `showopstatus`命令查看是否有操作为kFailed, 如果有查看日志排查原因 ```bash @@ -57,7 +62,7 @@ ```bash python tools/openmldb_ops.py --openmldb_bin_path=./bin/openmldb --zk_cluster=172.24.4.40:30481 --zk_root_path=/openmldb --cmd=showtablestatus ``` -一个tablet节点更新完成后,对其他tablet重复上述步骤。 + 一个tablet节点更新完成后,对其他tablet重复上述步骤。 所有节点更新完成后恢复写操作,可以通过执行`showtablestatus`命令查看列 `Rows`,确认是否有新数据成功写入。 diff --git a/docs/zh/maintain/upgrade.md b/docs/zh/maintain/upgrade.md index fc24afeeb23..0fdd3dee42f 100644 --- a/docs/zh/maintain/upgrade.md +++ b/docs/zh/maintain/upgrade.md @@ -1,4 +1,4 @@ -# 版本升级 +# 升级 升级过程对服务的影响: * 如果创建的表是单副本,用户可以选择: @@ -26,10 +26,15 @@ ## 2. 升级tablet +```{important} +如果有多个 tablet,请务必对每一个 tablet 进行顺序操作,不要同时对多个 tablet 进行升级操作。即对一个 tablet 完成升级,结果确认以后,再进行下一个 tablet 的升级操作。否则会导致集群状态异常。如果误操作导致集群异常,可以尝试使用[运维工具](openmldb_ops.md)的 `recoverdata` 进行恢复。 +``` + * 升级前准备`pre-upgrade`:为了避免对线上服务的影响,需要在升级tablet前,进行`pre-upgrade`操作,把该tablet上的leader分片迁移到其它tablets上(详细命令说明可以参考:[OpenMLDB运维工具](./openmldb_ops.md)) - ```bash - python tools/openmldb_ops.py --openmldb_bin_path=./bin/openmldb --zk_cluster=172.24.4.40:30481 --zk_root_path=/openmldb --cmd=pre-upgrade --endpoints=127.0.0.1:10921 - ``` + + ```bash + python tools/openmldb_ops.py --openmldb_bin_path=./bin/openmldb --zk_cluster=172.24.4.40:30481 --zk_root_path=/openmldb --cmd=pre-upgrade --endpoints=127.0.0.1:10921 + ``` 如果允许单副本表在升级过程中不可用,可以添加`--allow_single_replica`来避免添加新的副本。 * 停止tablet ```bash @@ -49,7 +54,7 @@ ```bash python tools/openmldb_ops.py --openmldb_bin_path=./bin/openmldb --zk_cluster=172.24.4.40:30481 --zk_root_path=/openmldb --cmd=post-upgrade --endpoints=127.0.0.1:10921 ``` - + ### 升级结果确认 * `showopstatus`命令查看是否有操作为kFailed, 如果有查看日志排查原因 ```bash @@ -59,7 +64,7 @@ ```bash python tools/openmldb_ops.py --openmldb_bin_path=./bin/openmldb --zk_cluster=172.24.4.40:30481 --zk_root_path=/openmldb --cmd=showtablestatus ``` -一个tablet节点升级完成后,对其他tablet重复上述步骤。 + 一个tablet节点升级完成后,对其他tablet重复上述步骤。 所有节点升级完成后恢复写操作, 执行`showtablestatus`命令查看`Rows`是否增加。 diff --git a/docs/zh/openmldb_sql/notice.md b/docs/zh/openmldb_sql/notice.md index 9c1c660bf23..c9705ca3a1f 100644 --- a/docs/zh/openmldb_sql/notice.md +++ b/docs/zh/openmldb_sql/notice.md @@ -1,4 +1,4 @@ -# SQL 命令执行注意的点 +# SQL 命令执行注意事项 部分 SQL 命令的执行存在一定的危险性,或者有一些特别需要注意的点以避免误操作。本文对相关命令做了总结,如果你依然对其中的操作有疑问,欢迎在我们[社区渠道](https://github.com/4paradigm/OpenMLDB#11-community)进行交流,以避免相关操作给你的开发和生产环境带来损失。 From aff5846768da9768a1ed77e9fc5aa9606cbb3a01 Mon Sep 17 00:00:00 2001 From: dl239 Date: Tue, 12 Sep 2023 15:58:04 +0800 Subject: [PATCH 32/63] fix: download spark if needed (#3459) --- release/sbin/deploy-all.sh | 41 ++++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/release/sbin/deploy-all.sh b/release/sbin/deploy-all.sh index f665810f7e4..3a4f101b15b 100755 --- a/release/sbin/deploy-all.sh +++ b/release/sbin/deploy-all.sh @@ -121,30 +121,37 @@ do done # deploy openmldbspark -if [[ -z "${SPARK_HOME}" ]]; then - echo "[ERROR] SPARK_HOME is not set" -else - if [[ ! -e "${SPARK_HOME}" ]]; then - echo "Downloading openmldbspark..." - spark_name=spark-3.2.1-bin-openmldbspark - spark_tar="${spark_name}".tgz - if [[ -e "${spark_tar}" ]]; then - echo "Skip downloading openmldbspark as ${spark_tar} already exists" +function download_spark { + if [[ -z "${SPARK_HOME}" ]]; then + echo "[ERROR] SPARK_HOME is not set" + else + if [[ ! -e "${SPARK_HOME}" ]]; then + echo "Downloading openmldbspark..." + spark_name=spark-3.2.1-bin-openmldbspark + spark_tar="${spark_name}".tgz + if [[ -e "${spark_tar}" ]]; then + echo "Skip downloading openmldbspark as ${spark_tar} already exists" + else + url="https://github.com/4paradigm/spark/releases/download/v3.2.1-openmldb${OPENMLDB_VERSION}/${spark_tar}" + echo "Download spark from $url" + curl -SLo ${spark_tar} "$url" + fi + tar -xzf ${spark_tar} + ln -s "$(pwd)"/"${spark_name}" "${SPARK_HOME}" else - url="https://github.com/4paradigm/spark/releases/download/v3.2.1-openmldb${OPENMLDB_VERSION}/${spark_tar}" - echo "Download spark from $url" - curl -SLo ${spark_tar} "$url" + echo "${SPARK_HOME} already exists. Skip deploy spark locally" fi - tar -xzf ${spark_tar} - ln -s "$(pwd)"/"${spark_name}" "${SPARK_HOME}" - else - echo "${SPARK_HOME} already exists. Skip deploy spark locally" fi -fi +} # deploy taskmanagers +downloaded=false for line in $(parse_host conf/hosts taskmanager) do + if ! $downloaded; then + download_spark + downloaded=true + fi host=$(echo "$line" | awk -F ' ' '{print $1}') port=$(echo "$line" | awk -F ' ' '{print $2}') dir=$(echo "$line" | awk -F ' ' '{print $3}') From 94bd110118c5c42edd7b238dfc433adfe2d0776f Mon Sep 17 00:00:00 2001 From: dl239 Date: Tue, 12 Sep 2023 17:56:04 +0800 Subject: [PATCH 33/63] feat: optimize message (#3494) --- src/base/status.h | 4 +++ src/cmd/openmldb.cc | 6 ++-- src/nameserver/name_server_impl.cc | 10 +++++- src/sdk/sql_cluster_router.cc | 56 ++++++++++++++++++++++++++++-- 4 files changed, 70 insertions(+), 6 deletions(-) diff --git a/src/base/status.h b/src/base/status.h index 1e1d5cc6f4d..4a4eb867724 100644 --- a/src/base/status.h +++ b/src/base/status.h @@ -20,10 +20,14 @@ #include #include "base/slice.h" +#include "version.h" // NOLINT namespace openmldb { namespace base { +inline const std::string NOTICE_URL = "https://openmldb.ai/docs/zh/v" + std::to_string(OPENMLDB_VERSION_MAJOR) + "." + + std::to_string(OPENMLDB_VERSION_MINOR) + "/openmldb_sql/notice.html"; + enum ReturnCode { kError = -1, // TODO(zekai): Add some notes, it is hard to use these error codes diff --git a/src/cmd/openmldb.cc b/src/cmd/openmldb.cc index 6878d00a945..3cf22b2df6d 100644 --- a/src/cmd/openmldb.cc +++ b/src/cmd/openmldb.cc @@ -479,7 +479,8 @@ void HandleNSClientSetTTL(const std::vector& parts, ::openmldb::cli } bool ok = client->UpdateTTL(parts[1], type, abs_ttl, lat_ttl, index_name, err); if (ok) { - std::cout << "Set ttl ok !" << std::endl; + std::cout << "Set ttl ok ! Note that, " + "it will take effect after two garbage collection intervals (i.e. gc_interval)." << std::endl; } else { std::cout << "Set ttl failed! " << err << std::endl; } @@ -908,7 +909,8 @@ void HandleNSClientChangeLeader(const std::vector& parts, ::openmld std::cout << "Invalid args. pid should be uint32_t" << std::endl; return; } - std::cout << "change leader ok" << std::endl; + std::cout << "change leader ok. " + "If there are writing operations while changing a leader, it may cause data loss." << std::endl; } void HandleNSClientOfflineEndpoint(const std::vector& parts, ::openmldb::client::NsClient* client) { diff --git a/src/nameserver/name_server_impl.cc b/src/nameserver/name_server_impl.cc index 4aae3c594c3..862ee42d320 100644 --- a/src/nameserver/name_server_impl.cc +++ b/src/nameserver/name_server_impl.cc @@ -8235,6 +8235,13 @@ void NameServerImpl::DeleteIndex(RpcController* controller, const DeleteIndexReq PDLOG(WARNING, "table[%s] does not exist!", request->table_name().c_str()); return; } + if (table_info->storage_mode() != ::openmldb::common::kMemory) { + response->set_code(ReturnCode::kOperatorNotSupport); + std::string msg = "DROP INDEX is not supported on a disk table, as its index is fully managed by the system."; + response->set_msg(msg); + LOG(WARNING) << msg; + return; + } { std::lock_guard lock(mu_); if (table_info->column_key_size() == 0) { @@ -8435,7 +8442,8 @@ void NameServerImpl::AddIndex(RpcController* controller, const AddIndexRequest* } if (table_info->storage_mode() != ::openmldb::common::kMemory) { response->set_code(ReturnCode::kOperatorNotSupport); - response->set_msg("only memory support addindex"); + response->set_msg("CREATE INDEX is not supported on a disk table, as its index is fully managed by the system. " + "Please refer to this link for more details: " + base::NOTICE_URL); LOG(WARNING) << "cannot add index. table " << name; return; } diff --git a/src/sdk/sql_cluster_router.cc b/src/sdk/sql_cluster_router.cc index 022e5664f7c..85eeaa31761 100644 --- a/src/sdk/sql_cluster_router.cc +++ b/src/sdk/sql_cluster_router.cc @@ -1358,7 +1358,10 @@ bool SQLClusterRouter::PutRow(uint32_t tid, const std::shared_ptr& bool ret = client->Put(tid, pid, cur_ts, row->GetRow(), kv.second); if (!ret) { SET_STATUS_AND_WARN(status, StatusCode::kCmdError, - "fail to make a put request to table. tid " + std::to_string(tid)); + "INSERT failed, tid " + std::to_string(tid) + + ". Note that data might have been partially inserted. " + "You are encouraged to perform DELETE to remove any partially " + "inserted data before trying INSERT again."); return false; } continue; @@ -1676,6 +1679,9 @@ std::shared_ptr SQLClusterRouter::HandleSQLCmd(const h } case hybridse::node::kCmdDropFunction: { std::string name = cmd_node->GetArgs()[0]; + if (!CheckAnswerIfInteractive("function", name)) { + return {}; + } auto base_status = ns_ptr->DropFunction(name, cmd_node->IsIfExists()); if (base_status.OK()) { *status = {}; @@ -1688,6 +1694,7 @@ std::shared_ptr SQLClusterRouter::HandleSQLCmd(const h if (!base_status.OK()) { LOG(WARNING) << "drop function " << name << " failed: [" << base_status.GetCode() << "] " << base_status.GetMsg(); + APPEND_FROM_BASE_AND_WARN(status, base_status, "drop function failed from taskmanager"); return {}; } } @@ -1968,6 +1975,12 @@ base::Status SQLClusterRouter::HandleSQLCreateTable(hybridse::node::CreatePlanNo if (!ns_ptr->CreateTable(table_info, create_node->GetIfNotExist(), msg)) { return base::Status(base::ReturnCode::kSQLCmdRunError, msg); } + if (interactive_ && table_info.column_key_size() == 0) { + return base::Status{base::ReturnCode::kOk, + "As there is no index specified, a default index type `absolute 0` will be created. " + "The data attached to the index will never expire to be deleted. " + "Please refer to this link for more details: " + base::NOTICE_URL}; + } } else { auto dbs = cluster_sdk_->GetAllDbs(); auto it = std::find(dbs.begin(), dbs.end(), db_name); @@ -2470,7 +2483,7 @@ std::shared_ptr SQLClusterRouter::ExecuteSQL( auto base_status = HandleSQLCreateTable(create_node, db, ns_ptr, sql); if (base_status.OK()) { RefreshCatalog(); - *status = {}; + *status = {StatusCode::kOk, base_status.msg}; } else { *status = {StatusCode::kCmdError, base_status.msg}; } @@ -2546,6 +2559,10 @@ std::shared_ptr SQLClusterRouter::ExecuteSQL( return ResultSetSQL::MakeResultSet(job_schema, {value}, status); } RefreshCatalog(); + *status = {StatusCode::kOk, + "\n- DEPLOY may modify table TTL. Data imported before DEPLOY may expire before the new TTL.\n" + "- DEPLOY will fail if the disk table requires creating an index, " + "the partial index may have been created."}; } return {}; } @@ -2901,6 +2918,30 @@ ::hybridse::sdk::Status SQLClusterRouter::ParseNamesFromArgs(const std::string& bool SQLClusterRouter::CheckAnswerIfInteractive(const std::string& drop_type, const std::string& name) { if (interactive_) { + std::string msg; + if (drop_type == "table") { + msg = "DROP TABLE is a dangerous operation. Once deleted, it is very difficult to recover. \n" + "You may also note that: \n" + "- If a snapshot of a partition is being generated while dropping a table, " + "the partition will not be deleted successfully.\n" + "- By default, the deleted data is moved to the folder `recycle`.\n" + "Please refer to this link for more details: " + base::NOTICE_URL; + } else if (drop_type == "deployment") { + msg = "- DROP DEPLOYMENT will not delete the index that is created automatically.\n" + "- DROP DEPLOYMENT will not delete data in the pre-aggregation table in the long window setting."; + } else if (drop_type == "index") { + msg = "DROP INDEX is a dangerous operation. Once deleted, it is very difficult to recover.\n" + "You may also note that: \n" + "- You have to wait for 2 garbage collection intervals (gc_interval) to create the same index.\n" + "- The index will not be deleted immediately, " + "it remains until after 2 garbage collection intervals.\n" + "Please refer to the doc for more details: " + base::NOTICE_URL; + } else if (drop_type == "function") { + msg = "This will lead to execution failure or system crash if any active deployment is using the function."; + } + if (!msg.empty()) { + printf("%s\n", msg.c_str()); + } printf("Drop %s %s? yes/no\n", drop_type.c_str(), name.c_str()); std::string input; std::cin >> input; @@ -3231,7 +3272,16 @@ hybridse::sdk::Status SQLClusterRouter::HandleDelete(const std::string& db, cons if (!status.IsOK()) { return status; } - return SendDeleteRequst(table_info, &option); + status = SendDeleteRequst(table_info, &option); + if (status.IsOK()) { + status = {StatusCode::kOk, + "DELETE is a dangerous operation. Once deleted, it is very difficult to recover. You may also note that:\n" + "- The deleted data will not be released immediately from the main memory; " + "it remains until after a garbage collection interval (gc_interval)\n" + "- Data in the pre-aggregation table will not be updated.\n" + "Please refer to this link for more details: " + base::NOTICE_URL}; + } + return status; } hybridse::sdk::Status SQLClusterRouter::SendDeleteRequst( From 0462f8a9682f8d232e8d44df7513cff66870d686 Mon Sep 17 00:00:00 2001 From: dl239 Date: Thu, 14 Sep 2023 10:13:06 +0800 Subject: [PATCH 34/63] fix: operation tools run failed if openmldb install with name (#3455) --- .github/workflows/openmldb-tool.yml | 46 +- docs/en/maintain/openmldb_ops.md | 4 +- docs/zh/maintain/openmldb_ops.md | 2 +- release/bin/start.sh | 8 +- .../openmldb-deploy/cases/case_conf.py | 2 +- .../openmldb-deploy/cases/test_scale.py | 16 +- .../openmldb-deploy/cases/test_upgrade.py | 13 +- test/test-tool/openmldb-deploy/cases/tool.py | 418 ------------------ test/test-tool/openmldb-deploy/cases/util.py | 38 ++ test/test-tool/openmldb-deploy/hosts | 15 + .../openmldb-deploy/install_with_name.sh | 75 ++++ tools/openmldb_ops.py | 202 +++++---- tools/tool.py | 113 +++-- 13 files changed, 374 insertions(+), 578 deletions(-) delete mode 100644 test/test-tool/openmldb-deploy/cases/tool.py create mode 100644 test/test-tool/openmldb-deploy/cases/util.py create mode 100644 test/test-tool/openmldb-deploy/hosts create mode 100644 test/test-tool/openmldb-deploy/install_with_name.sh diff --git a/.github/workflows/openmldb-tool.yml b/.github/workflows/openmldb-tool.yml index c8150a40539..23d3f5b1dfd 100644 --- a/.github/workflows/openmldb-tool.yml +++ b/.github/workflows/openmldb-tool.yml @@ -8,10 +8,12 @@ on: paths: - 'release/**' - 'tools/**' + - 'test/test-tool/openmldb-deploy/**' pull_request: paths: - 'release/**' - 'tools/**' + - 'test/test-tool/openmldb-deploy/**' workflow_dispatch: env: @@ -27,19 +29,53 @@ jobs: - uses: actions/checkout@v2 - name: prepare env + run: | + bash test/test-tool/openmldb-deploy/gen_conf.sh ${{ env.DEPLOY_DIR }} ${{ env.NODE_LIST }} > hosts + cp -f hosts test/test-tool/openmldb-deploy/ + pip3 install requests openmldb pytest + - name: install openmldb run: | VERSION=`git fetch --tags | git tag -l v[0-9].* | tail -n1` VERSION=${VERSION#v} - echo "OPENMLDB_VETSION=$VERSION" >> $GITHUB_ENV - bash test/test-tool/openmldb-deploy/gen_conf.sh ${{ env.DEPLOY_DIR }} ${{ env.NODE_LIST }} > hosts + bash test/test-tool/openmldb-deploy/install.sh ${VERSION} + - name: run test + run: | + cp -f tools/tool.py test/test-tool/openmldb-deploy/cases/ + cp -f tools/* openmldb/tools/ + python3 -m pytest test/test-tool/openmldb-deploy/cases --junit-xml=pytest.xml + - name: clear env + run: | + bash openmldb/sbin/stop-all.sh && bash openmldb/sbin/clear-all.sh + - name: upload python test results + if: always() + uses: actions/upload-artifact@v2 + with: + name: openmldb-tool-test-result-${{ github.sha }} + path: | + pytest.xml + + openmldb-tool-name: + runs-on: [self-hosted,generic] + if: github.repository == '4paradigm/OpenMLDB' + container: + image: ghcr.io/4paradigm/hybridsql:latest + steps: + - uses: actions/checkout@v2 + + - name: prepare env + run: | pip3 install requests openmldb pytest + yum install -y rsync - name: install openmldb - env: - OPENMLDB_VETSION: ${{ env.OPENMLDB_VETSION }} run: | - bash test/test-tool/openmldb-deploy/install.sh ${{ env.OPENMLDB_VETSION }} + git fetch --tags --force + VERSION=$(git describe --always --tags `git rev-list --tags --max-count=1`) + VERSION=${VERSION#v} + bash test/test-tool/openmldb-deploy/install_with_name.sh ${VERSION} - name: run test run: | + cp -f tools/tool.py test/test-tool/openmldb-deploy/cases/ + cp -f tools/* openmldb/tools/ python3 -m pytest test/test-tool/openmldb-deploy/cases --junit-xml=pytest.xml - name: clear env run: | diff --git a/docs/en/maintain/openmldb_ops.md b/docs/en/maintain/openmldb_ops.md index 134120a414f..46bfce18c6d 100644 --- a/docs/en/maintain/openmldb_ops.md +++ b/docs/en/maintain/openmldb_ops.md @@ -39,5 +39,5 @@ python tools/openmldb_ops.py --openmldb_bin_path=./bin/openmldb --zk_cluster=172 - --endpoints: specified the endpoints to migrate out. If there are two or more endoints, use `,` as delimiter. It will execute failed if the leftover tablet number less than replica number of tables ### System Requirements -- python >= 3.6 -- `showopstatus` and `showtablestatus` require `prettytable` dependency \ No newline at end of file +- python >= 2.7 +- `showopstatus` and `showtablestatus` require `prettytable` dependency diff --git a/docs/zh/maintain/openmldb_ops.md b/docs/zh/maintain/openmldb_ops.md index 31c4db3a8aa..10b53437b52 100644 --- a/docs/zh/maintain/openmldb_ops.md +++ b/docs/zh/maintain/openmldb_ops.md @@ -35,5 +35,5 @@ python tools/openmldb_ops.py --openmldb_bin_path=./bin/openmldb --zk_cluster=172 ``` ### 系统要求 -- 要求python3.6及以上版本 +- 要求python2.7及以上版本 - `showopstatus`和`showtablestatus`需要`prettytable`依赖 diff --git a/release/bin/start.sh b/release/bin/start.sh index 4f1c1260b29..23bcf77d4a4 100755 --- a/release/bin/start.sh +++ b/release/bin/start.sh @@ -143,7 +143,7 @@ case $OP in PID=$! echo "process pid is $PID" fi - if [ -x "$(command -v curl)" ]; then + if [ -x "$(command -v curl)" ] && ! grep -q '^--use_name=true' "conf/${COMPONENT}.flags"; then sleep 3 ENDPOINT=$(grep '^\--endpoint' ./conf/"$COMPONENT".flags | grep -v '#' | awk -F '=' '{print $2}') COUNT=1 @@ -164,10 +164,10 @@ case $OP in fi done else - echo "no curl, sleep 10s and then check the process running status" - sleep 10 + echo "no curl, sleep 12s and then check the process running status" + sleep 12 if kill -0 "$PID" > /dev/null 2>&1; then - if [ "$DAEMON_MODE" != "true" ]; then + if [ "${DAEMON_MODE}" != "true" ]; then echo $PID > "$OPENMLDB_PID_FILE" fi echo "Start ${COMPONENT} success" diff --git a/test/test-tool/openmldb-deploy/cases/case_conf.py b/test/test-tool/openmldb-deploy/cases/case_conf.py index c486bab5ed0..785db5e55f0 100644 --- a/test/test-tool/openmldb-deploy/cases/case_conf.py +++ b/test/test-tool/openmldb-deploy/cases/case_conf.py @@ -30,7 +30,7 @@ conf["base_dir"] = path.dirname(path.dirname(path.dirname(path.dirname(dirname)))) conf["components"] = {} cf = configparser.ConfigParser(strict=False, delimiters=" ", allow_no_value=True) -host_file = conf["base_dir"] + "/openmldb/conf/hosts" +host_file = conf["base_dir"] + "/test/test-tool/openmldb-deploy/hosts" cf.read(host_file) for sec in cf.sections(): if sec != "zookeeper": diff --git a/test/test-tool/openmldb-deploy/cases/test_scale.py b/test/test-tool/openmldb-deploy/cases/test_scale.py index 7ca09ca2df6..b9b71172dd4 100644 --- a/test/test-tool/openmldb-deploy/cases/test_scale.py +++ b/test/test-tool/openmldb-deploy/cases/test_scale.py @@ -21,7 +21,7 @@ from tool import Executor from tool import Partition from tool import Status -from tool import Util +from util import Util class TestScale: db = None @@ -37,8 +37,8 @@ def setup_class(cls): cls.bin_path = cls.openmldb_path + "/bin/openmldb" cls.executor = Executor(cls.bin_path, case_conf.conf["zk_cluster"], case_conf.conf["zk_root_path"]) - def execute_scale(self, scale_cmd : str, endpoint : str = ""): - cmd = ["python3"] + def execute_scale(self, python_bin, scale_cmd : str, endpoint : str = ""): + cmd = [python_bin] cmd.append(f"{self.base_dir}/tools/openmldb_ops.py") cmd.append(f"--openmldb_bin_path={self.bin_path}") cmd.append("--zk_cluster=" + case_conf.conf["zk_cluster"]) @@ -50,7 +50,8 @@ def execute_scale(self, scale_cmd : str, endpoint : str = ""): return status @pytest.mark.parametrize("replica_num, partition_num", [(2, 8), (1, 10)]) - def test_scalein(self, replica_num, partition_num): + @pytest.mark.parametrize("python_bin", ["python2", "python3"]) + def test_scalein(self, python_bin, replica_num, partition_num): assert len(case_conf.conf["components"]["tablet"]) > 2 status, distribution = Util.gen_distribution(case_conf.conf["components"]["tablet"], replica_num, partition_num) assert status.OK() @@ -74,7 +75,7 @@ def test_scalein(self, replica_num, partition_num): assert item[5] == "yes" assert len(endpoints) == len(case_conf.conf["components"]["tablet"]) - assert self.execute_scale("scalein", case_conf.conf["components"]["tablet"][0]).OK() + assert self.execute_scale(python_bin, "scalein", case_conf.conf["components"]["tablet"][0]).OK() status, result = self.executor.GetTableInfo("test", "") assert status.OK() @@ -88,7 +89,8 @@ def test_scalein(self, replica_num, partition_num): self.cursor.execute(f"drop table {table_name}") @pytest.mark.parametrize("replica_num, partition_num", [(2, 8), (1, 10)]) - def test_scaleout(self, replica_num, partition_num): + @pytest.mark.parametrize("python_bin", ["python2", "python3"]) + def test_scaleout(self, python_bin, replica_num, partition_num): assert len(case_conf.conf["components"]["tablet"]) > 2 status, distribution = Util.gen_distribution(case_conf.conf["components"]["tablet"][1:], replica_num, partition_num) assert status.OK() @@ -113,7 +115,7 @@ def test_scaleout(self, replica_num, partition_num): assert len(endpoints) == len(case_conf.conf["components"]["tablet"]) - 1 assert case_conf.conf["components"]["tablet"][0] not in endpoints - assert self.execute_scale("scaleout").OK() + assert self.execute_scale(python_bin, "scaleout").OK() status, result = self.executor.GetTableInfo("test", "") assert status.OK() diff --git a/test/test-tool/openmldb-deploy/cases/test_upgrade.py b/test/test-tool/openmldb-deploy/cases/test_upgrade.py index 950c52c8fa9..2e7177e45d6 100644 --- a/test/test-tool/openmldb-deploy/cases/test_upgrade.py +++ b/test/test-tool/openmldb-deploy/cases/test_upgrade.py @@ -21,7 +21,7 @@ from tool import Executor from tool import Partition from tool import Status -from tool import Util +from util import Util class TestUpgrade: db = None @@ -37,8 +37,8 @@ def setup_class(cls): cls.bin_path = cls.openmldb_path + "/bin/openmldb" cls.executor = Executor(cls.bin_path, case_conf.conf["zk_cluster"], case_conf.conf["zk_root_path"]) - def execute_upgrade(self, upgrade_cmd : str, endpoint : str): - cmd = ["python3"] + def execute_upgrade(self, python_bin, upgrade_cmd : str, endpoint : str): + cmd = [python_bin] cmd.append(f"{self.base_dir}/tools/openmldb_ops.py") cmd.append(f"--openmldb_bin_path={self.bin_path}") cmd.append("--zk_cluster=" + case_conf.conf["zk_cluster"]) @@ -71,7 +71,8 @@ def get_unalive_cnt(self, db, table_name) -> (Status, int): @pytest.mark.parametrize("replica_num, partition_num", [(3, 10), (2, 10), (1, 10)]) - def test_upgrade(self, replica_num, partition_num): + @pytest.mark.parametrize("python_bin", ["python2", "python3"]) + def test_upgrade(self, python_bin, replica_num, partition_num): assert len(case_conf.conf["components"]["tablet"]) > 2 status, distribution = Util.gen_distribution(case_conf.conf["components"]["tablet"], replica_num, partition_num) assert status.OK() @@ -92,7 +93,7 @@ def test_upgrade(self, replica_num, partition_num): status, unalive_cnt = self.get_unalive_cnt("test", table_name) assert status.OK() and unalive_cnt == 0 - assert self.execute_upgrade("pre-upgrade", case_conf.conf["components"]["tablet"][0]).OK() + assert self.execute_upgrade(python_bin, "pre-upgrade", case_conf.conf["components"]["tablet"][0]).OK() status, cnt1 = self.get_leader_cnt("test", table_name, case_conf.conf["components"]["tablet"][0]) assert status.OK() and cnt1 == 0 @@ -102,7 +103,7 @@ def test_upgrade(self, replica_num, partition_num): data = result.fetchall() assert len(data) == key_num - assert self.execute_upgrade("post-upgrade", case_conf.conf["components"]["tablet"][0]).OK() + assert self.execute_upgrade(python_bin, "post-upgrade", case_conf.conf["components"]["tablet"][0]).OK() status, cnt2 = self.get_leader_cnt("test", table_name, case_conf.conf["components"]["tablet"][0]) assert status.OK() and cnt2 == cnt diff --git a/test/test-tool/openmldb-deploy/cases/tool.py b/test/test-tool/openmldb-deploy/cases/tool.py deleted file mode 100644 index 1bfde47d2bd..00000000000 --- a/test/test-tool/openmldb-deploy/cases/tool.py +++ /dev/null @@ -1,418 +0,0 @@ -# Copyright 2021 4Paradigm -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import logging -import os -import random -import subprocess -import sys -import time -from typing import List, Dict, Tuple -log = logging.getLogger(__name__) -logging.basicConfig(level=logging.INFO, format = '%(levelname)s: %(message)s') - -USE_SHELL = sys.platform.startswith("win") -class Status: - def __init__(self, code = 0, msg = "ok"): - self.code = code - self.msg = msg - - def OK(self): - return True if self.code == 0 else False - - def GetMsg(self): - return self.msg - - def GetCode(self): - return self.code - -class Partition: - def __init__(self, name, tid, pid, endpoint, is_leader, is_alive, offset): - self.name = name - self.tid = tid - self.pid = pid - self.endpoint = endpoint - self.is_leader = is_leader - self.is_alive = is_alive - self.offset = 0 if offset == "-" else int(offset) - - def GetTid(self): - return self.tid - def GetName(self): - return self.name - def GetPid(self): - return self.pid - def GetOffset(self): - return self.offset - def GetEndpoint(self): - return self.endpoint - def IsAlive(self): - return self.is_alive - def IsLeader(self): - return self.is_leader - def GetKey(self): - return "{}_{}".format(self.tid, self.pid) - -class Executor: - def __init__(self, openmldb_bin_path, zk_cluster, zk_root_path): - self.openmldb_bin_path = openmldb_bin_path - self.zk_cluster = zk_cluster - self.zk_root_path = zk_root_path - self.ns_base_cmd = [self.openmldb_bin_path, - "--zk_cluster=" + self.zk_cluster, - "--zk_root_path=" + self.zk_root_path, - "--role=ns_client", - "--interactive=false"] - self.tablet_base_cmd = [self.openmldb_bin_path, "--role=client", "--interactive=false"] - self.sql_base_cmd = [self.openmldb_bin_path, - "--zk_cluster=" + self.zk_cluster, - "--zk_root_path=" + self.zk_root_path, - "--role=sql_client", - "--interactive=false"] - - def Connect(self) -> Status: - status, endpoint = self.GetNsLeader() - if status.OK() and status.GetMsg().find("zk client init failed") == -1: - self.ns_leader = endpoint - log.info(f"ns leader: {self.ns_leader}") - self.ns_base_cmd = [self.openmldb_bin_path, - "--endpoint=" + self.ns_leader, - "--role=ns_client", - "--interactive=false"] - return Status() - return Status(-1, "connect OpenMLDB failed") - - def RunWithRetuncode(self, command, - universal_newlines = True, - useshell = USE_SHELL, - env = os.environ) -> tuple([Status, str]): - try: - print(command) - p = subprocess.Popen(command, stdout = subprocess.PIPE, stderr = subprocess.PIPE, shell = useshell, universal_newlines = universal_newlines, env = env) - output = p.stdout.read() - p.wait() - errout = p.stderr.read() - p.stdout.close() - p.stderr.close() - if "error msg" in output: - return Status(-1, output), output - return Status(p.returncode, errout), output - except Exception as ex: - return Status(-1, ex), None - - def GetNsLeader(self) -> tuple([Status, str]): - cmd = list(self.ns_base_cmd) - cmd.append("--cmd=showns") - status, output = self.RunWithRetuncode(cmd) - if status.OK(): - result = self.ParseResult(output) - for record in result: - if record[2] == "leader": - return Status(), record[0] - return Status(-1, "get ns leader failed"), None - - - def ParseResult(self, output) -> list: - result = [] - lines = output.split("\n") - content_is_started = False - for line in lines: - line = line.lstrip() - if line.startswith("------"): - content_is_started = True - continue - if not content_is_started: - continue - record = line.split() - if len(record) > 0: - result.append(record) - return result - - def GetAutofailover(self) -> tuple([Status, bool]): - cmd = list(self.ns_base_cmd) - cmd.append("--cmd=confget auto_failover") - status, output = self.RunWithRetuncode(cmd) - if not status.OK(): - return status, None - if output.find("true") != -1: - return Status(), True - return Status(), False; - - def SetAutofailover(self, value) -> Status: - cmd = list(self.ns_base_cmd) - cmd.append("--cmd=confset auto_failover " + value) - status, output = self.RunWithRetuncode(cmd) - return status - - def GetAllDatabase(self) -> tuple([Status, List]): - cmd = list(self.ns_base_cmd) - cmd.append("--cmd=showdb") - status, output = self.RunWithRetuncode(cmd) - if not status.OK(): - return status, None - dbs = [] - for record in self.ParseResult(output): - if len(record) < 2: - continue - dbs.append(record[1]) - return Status(), dbs - - def GetTableInfo(self, database, table_name = '') -> tuple([Status, List]): - cmd = list(self.ns_base_cmd) - cmd.append("--cmd=showtable " + table_name) - cmd.append("--database=" + database) - status, output = self.RunWithRetuncode(cmd) - if not status.OK(): - return status, None - result = [] - for record in self.ParseResult(output): - if len(record) < 4: - continue - result.append(record) - return Status(), result - - def ParseTableInfo(self, table_info) -> Dict[str, List[Partition]]: - result = {} - for record in table_info: - is_leader = True if record[4] == "leader" else False - is_alive = True if record[5] == "yes" else False - partition = Partition(record[0], record[1], record[2], record[3], is_leader, is_alive, record[6]); - result.setdefault(record[2], []) - result[record[2]].append(partition) - return result - - def GetTablePartition(self, database, table_name) -> tuple([Status, Dict]): - status, result = self.GetTableInfo(database, table_name) - if not status.OK: - return status, None - partition_dict = self.ParseTableInfo(result) - return Status(), partition_dict - - def GetAllTable(self, database) -> Tuple[Status, List[Partition]]: - status, result = self.GetTableInfo(database) - if not status.OK(): - return status, None - tables = [] - for partition in result: - if partition[0] not in tables: - tables.append(partition[0]) - return Status(), tables - - def GetTableStatus(self, endpoint, tid = '', pid = '') -> tuple([Status, Dict]): - cmd = list(self.tablet_base_cmd) - cmd.append("--endpoint=" + endpoint) - cmd.append("--cmd=gettablestatus " + tid + " " + pid) - status, output = self.RunWithRetuncode(cmd) - if not status.OK(): - log.error("gettablestatus failed") - return status, None - if "failed" in output: - log.error("gettablestatus failed") - return Status(-1, output), None - result = {} - for record in self.ParseResult(output): - if len(record) < 4: - continue - key = "{}_{}".format(record[0], record[1]) - result[key] = record - return Status(), result - - def ShowTableStatus(self, pattern = '%') -> tuple([Status, list]): - cmd = list(self.sql_base_cmd) - cmd.append(f"--cmd=show table status like '{pattern}';") - status, output = self.RunWithRetuncode(cmd) - if not status.OK(): - log.error("show table status failed") - return status, None - if "failed" in output: - log.error("show table status failed") - return Status(-1, output), None - output = self.ParseResult(output) - output_processed = [] - if len(output) >= 1: - header = output[0] - output_processed.append(header) - col_num = len(header) - for i in range(1, len(output)): - # warnings col may be empty - if len(output[i]) == col_num - 1: - output_processed.append(output[i] + [""]) - elif len(output[i]) == col_num: - output_processed.append(output[i]) - - return Status(), output_processed - - def LoadTable(self, endpoint, name, tid, pid, sync = True) -> Status: - cmd = list(self.tablet_base_cmd) - cmd.append("--endpoint=" + endpoint) - cmd.append("--cmd=loadtable {} {} {} 0 8".format(name, tid, pid)) - status, output = self.RunWithRetuncode(cmd) - if status.OK() and output.find("LoadTable ok") != -1: - if not sync: - return Status() - while True: - status, result = self.GetTableStatus(endpoint, tid, pid) - key = "{}_{}".format(tid, pid) - if status.OK() and key in result: - table_stat = result[key][4] - if table_stat == "kTableNormal": - return Status() - elif table_stat == "kTableLoading" or table_stat == "kTableUndefined": - log.info(f"table is loading... tid {tid} pid {pid}") - else: - return Status(-1, f"table stat is {table_stat}") - time.sleep(2) - - return Status(-1, "execute load table failed") - - def GetLeaderFollowerOffset(self, endpoint, tid, pid) -> tuple([Status, List]): - cmd = list(self.tablet_base_cmd) - cmd.append("--endpoint=" + endpoint) - cmd.append("--cmd=getfollower {} {}".format(tid, pid)) - status, output = self.RunWithRetuncode(cmd) - if not status.OK(): - return status - return Status(), self.ParseResult(output) - - def RecoverTablePartition(self, database, name, pid, endpoint, sync = False) -> Status: - cmd = list(self.ns_base_cmd) - cmd.append("--cmd=recovertable {} {} {}".format(name, pid, endpoint)) - cmd.append("--database=" + database) - status, output = self.RunWithRetuncode(cmd) - if status.OK() and output.find("recover table ok") != -1: - if sync and not self.WaitingOP(database, name, pid).OK(): - return Status(-1, "recovertable failed") - return Status() - return status - - def UpdateTableAlive(self, database, name, pid, endpoint, is_alive) -> Status: - if is_alive not in ["yes", "no"]: - return Status(-1, f"invalid argument {is_alive}") - cmd = list(self.ns_base_cmd) - cmd.append("--cmd=updatetablealive {} {} {} {}".format(name, pid, endpoint, is_alive)) - cmd.append("--database=" + database) - status, output = self.RunWithRetuncode(cmd) - if status.OK() and output.find("update ok") != -1: - return Status() - return Status(-1, "update table alive failed") - - def ChangeLeader(self, database, name, pid, endpoint = "auto", sync = False) -> Status: - cmd = list(self.ns_base_cmd) - cmd.append("--cmd=changeleader {} {} {}".format(name, pid, endpoint)) - cmd.append("--database=" + database) - status, output = self.RunWithRetuncode(cmd) - if status.OK() and sync and not self.WaitingOP(database, name, pid).OK(): - return Status(-1, "changer leader failed") - return status - - def ShowOpStatus(self, database, name = '', pid = '') -> tuple([Status, List]): - cmd = list(self.ns_base_cmd) - cmd.append("--cmd=showopstatus {} {} ".format(name, pid)) - cmd.append("--database=" + database) - status, output = self.RunWithRetuncode(cmd) - if not status.OK(): - return status, None - return Status(), self.ParseResult(output) - - def CancelOp(self, database, op_id) -> Status: - cmd = list(self.ns_base_cmd) - cmd.append("--cmd=cancelop {}".format(op_id)) - cmd.append("--database=" + database) - status, output = self.RunWithRetuncode(cmd) - return status - - def Migrate(self, database, name, pid, src_endpoint, desc_endpoint, sync = False) -> Status: - if src_endpoint == desc_endpoint: - return Status(-1, "src_endpoint and desc_endpoint is same") - cmd = list(self.ns_base_cmd) - cmd.append("--cmd=migrate {} {} {} {}".format(src_endpoint, name, pid, desc_endpoint)) - cmd.append("--database=" + database) - status, output = self.RunWithRetuncode(cmd) - if status.OK() and output.find("migrate ok") != -1: - if sync and not self.WaitingOP(database, name, pid).OK(): - return Status(-1, "migrate failed") - return Status() - return status - - def ShowTablet(self) -> tuple([Status, List]): - cmd = list(self.ns_base_cmd) - cmd.append("--cmd=showtablet") - status, output = self.RunWithRetuncode(cmd) - if not status.OK(): - return status, None - return Status(), self.ParseResult(output) - - def AddReplica(self, database, name, pid, endpoint, sync = False) -> Status: - cmd = list(self.ns_base_cmd) - cmd.append("--cmd=addreplica {} {} {}".format(name, pid, endpoint)) - cmd.append("--database=" + database) - status, output = self.RunWithRetuncode(cmd) - if status.OK() and output.find("ok") != -1: - if sync and not self.WaitingOP(database, name, pid).OK(): - return Status(-1, "addreplica failed") - return Status() - return Status(-1, "add replica failed") - - def DelReplica(self, database, name, pid, endpoint, sync = False) -> Status: - cmd = list(self.ns_base_cmd) - cmd.append("--cmd=delreplica {} {} {}".format(name, pid, endpoint)) - cmd.append("--database=" + database) - status, output = self.RunWithRetuncode(cmd) - if status.OK() and output.find("ok") != -1: - if sync and not self.WaitingOP(database, name, pid).OK(): - return Status(-1, "delreplica failed") - return Status() - return Status(-1, "del replica failed") - - def WaitingOP(self, database, name, pid) -> Status: - while True: - error_try_times = 3 - while error_try_times > 0: - status, result = self.ShowOpStatus(database, name, pid) - error_try_times -= 1 - if status.OK(): - break - elif error_try_times == 0: - return Status(-1, "fail to execute showopstatus") - record = result[-1] - if record[4] == 'kDoing' or record[4] == 'kInited': - value = " ".join(record) - log.info(f"waiting {value}") - time.sleep(2) - elif record[4] == 'kFailed': - return Status(-1, f"job {record[0]} execute failed") - else: - break - return Status() - -class Util: - def gen_distribution(tablets : list, replica_num: int = 3, partition_num : int = 8) -> (Status, str): - if replica_num < 1 or len(tablets) < replica_num: - return Status(-1, "invalid args") - distribution = "[" - for i in range(partition_num): - if i > 0: - distribution += "," - endpoints = random.sample(tablets, replica_num) - distribution += f"(\'{endpoints[0]}\'" - if (replica_num) > 1: - distribution += ", [" - for endpoint in endpoints[1:]: - distribution += f"\'{endpoint}\'," - if (replica_num) > 1: - distribution = distribution[:-1] - if (replica_num) > 1: - distribution += "]" - distribution += ")" - distribution += "]" - return Status(), distribution diff --git a/test/test-tool/openmldb-deploy/cases/util.py b/test/test-tool/openmldb-deploy/cases/util.py new file mode 100644 index 00000000000..3f763682d74 --- /dev/null +++ b/test/test-tool/openmldb-deploy/cases/util.py @@ -0,0 +1,38 @@ +# Copyright 2021 4Paradigm +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import random +from tool import Status + +class Util: + def gen_distribution(tablets : list, replica_num: int = 3, partition_num : int = 8) -> (Status, str): + if replica_num < 1 or len(tablets) < replica_num: + return Status(-1, "invalid args") + distribution = "[" + for i in range(partition_num): + if i > 0: + distribution += "," + endpoints = random.sample(tablets, replica_num) + distribution += f"(\'{endpoints[0]}\'" + if (replica_num) > 1: + distribution += ", [" + for endpoint in endpoints[1:]: + distribution += f"\'{endpoint}\'," + if (replica_num) > 1: + distribution = distribution[:-1] + if (replica_num) > 1: + distribution += "]" + distribution += ")" + distribution += "]" + return Status(), distribution diff --git a/test/test-tool/openmldb-deploy/hosts b/test/test-tool/openmldb-deploy/hosts new file mode 100644 index 00000000000..047238bfe03 --- /dev/null +++ b/test/test-tool/openmldb-deploy/hosts @@ -0,0 +1,15 @@ +# format: host:port:zk_peer_port:zk_election_port WORKDIR +# only host is required, other fields are optional. +# zk_peer_port and zk_election_port only works for zookeeper + +[tablet] +localhost:10921 /tmp/openmldb/tablet-0 +localhost:10922 /tmp/openmldb/tablet-1 +localhost:10923 /tmp/openmldb/tablet-2 + +[nameserver] +localhost:7527 /tmp/openmldb/ns-0 +localhost:7528 /tmp/openmldb/ns-1 + +[zookeeper] +localhost:2181:2888:3888 /tmp/openmldb/zookeeper diff --git a/test/test-tool/openmldb-deploy/install_with_name.sh b/test/test-tool/openmldb-deploy/install_with_name.sh new file mode 100644 index 00000000000..6ce1851f103 --- /dev/null +++ b/test/test-tool/openmldb-deploy/install_with_name.sh @@ -0,0 +1,75 @@ +#! /usr/bin/env bash +# shellcheck disable=SC1091 + +# Copyright 2021 4Paradigm +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -eE -x +VERSION=$1 +if [[ -z ${VERSION} ]]; then + VERSION=0.7.2 +fi +echo "version: ${VERSION}" +curl -SLo openmldb.tar.gz "https://github.com/4paradigm/OpenMLDB/releases/download/v${VERSION}/openmldb-${VERSION}-linux.tar.gz" +mkdir -p "openmldb" +tar xzf openmldb.tar.gz -C "openmldb" --strip-components 1 +pushd "openmldb" +rm -rf sbin conf +rm -f bin/*.sh +/bin/cp -r ../release/sbin ../release/conf ./ +/bin/cp -f ../release/bin/*.sh bin/ + +/bin/cp -f ../test/test-tool/openmldb-deploy/hosts conf/hosts +sed -i"" -e "s/OPENMLDB_VERSION=[0-9]\.[0-9]\.[0-9]/OPENMLDB_VERSION=${VERSION}/g" conf/openmldb-env.sh +sed -i"" -e "s/OPENMLDB_MODE:=standalone/OPENMLDB_MODE:=cluster/g" conf/openmldb-env.sh +sh sbin/deploy-all.sh + +for (( i=0; i<=2; i++ )) +do + mkdir -p /tmp/openmldb/tablet-${i}/data && echo "tablet-${i}" > /tmp/openmldb/tablet-${i}/data/name.txt + conf_file="/tmp/openmldb/tablet-${i}/conf/tablet.flags" + sed -i "s/^--endpoint/# --endpoint/g" ${conf_file} + port=$((i + 10921)) + echo "--port=${port}" >> ${conf_file} + echo "--use_name=true" >> ${conf_file} +done +for (( i=0; i<=1; i++ )) +do + conf_file="/tmp/openmldb/ns-${i}/conf/nameserver.flags" + mkdir -p /tmp/openmldb/ns-${i}/data && echo "ns-${i}" > /tmp/openmldb/ns-${i}/data/name.txt + sed -i "s/^--endpoint/# --endpoint/g" ${conf_file} + port=$((i + 7527)) + echo "--port=${port}" >> ${conf_file} + echo "--use_name=true" >> ${conf_file} +done + +sh sbin/start-all.sh +popd + +pushd test/test-tool/openmldb-deploy +echo "collect_ignore_glob = [\"test_install.py\"]" > cases/conftest.py +cat > hosts <= max_offset: leader_pos = pos if leader_pos < 0: - log.error(f"cannot find leader partition. db {db} name {table_name} partition {pid}") + log.error("cannot find leader partition. db {db} name {table_name} partition {pid}".format( + db=db, table_name=table_name, pid=pid)) return Status(-1, "recover partition failed") tid = partitions[0].GetTid() leader_endpoint = partitions[leader_pos].GetEndpoint() # recover leader - if f"{tid}_{pid}" not in endpoint_status[leader_endpoint]: - log.info(f"leader partition is not in tablet, db {db} name {table_name} pid {pid} endpoint {leader_endpoint}. start loading data...") + if "{tid}_{pid}".format(tid=tid, pid=pid) not in endpoint_status[leader_endpoint]: + log.info("leader partition is not in tablet, db {db} name {table_name} pid {pid} endpoint {leader_endpoint}. start loading data...".format( + db=db, table_name=table_name, pid=pid, leader_endpoint=leader_endpoint)) status = executor.LoadTable(leader_endpoint, table_name, tid, pid) if not status.OK(): - log.error(f"load table failed. db {db} name {table_name} tid {tid} pid {pid} endpoint {leader_endpoint} msg {status.GetMsg()}") + log.error("load table failed. db {db} name {table_name} tid {tid} pid {pid} endpoint {leader_endpoint} msg {status.GetMsg()}".format( + db=db, table_name=table_name, tid=tid, pid=pid, leader_endpoint=leader_endpoint, status=status)) return Status(-1, "recover partition failed") if not partitions[leader_pos].IsAlive(): status = executor.UpdateTableAlive(db, table_name, pid, leader_endpoint, "yes") if not status.OK(): - log.error(f"update leader alive failed. db {db} name {table_name} pid {pid} endpoint {leader_endpoint}") + log.error("update leader alive failed. db {db} name {table_name} pid {pid} endpoint {leader_endpoint}".format( + db=db, table_name=table_name, pid=pid, leader_endpoint=leader_endpoint)) return Status(-1, "recover partition failed") # recover follower for pos in range(len(partitions)): @@ -131,21 +136,23 @@ def RecoverPartition(executor : Executor, db, partitions : list, endpoint_status if partition.IsAlive(): status = executor.UpdateTableAlive(db, table_name, pid, endpoint, "no") if not status.OK(): - log.error(f"update alive failed. db {db} name {table_name} pid {pid} endpoint {endpoint}") + log.error("update alive failed. db {db} name {table_name} pid {pid} endpoint {endpoint}".format( + db=db, table_name=table_name, pid=pid, endpoint=endpoint)) return Status(-1, "recover partition failed") if not executor.RecoverTablePartition(db, table_name, pid, endpoint).OK(): - log.error(f"recover table partition failed. db {db} name {table_name} pid {pid} endpoint {endpoint}") + log.error("recover table partition failed. db {db} name {table_name} pid {pid} endpoint {endpoint}".format( + db=db, table_name=table_name, pid=pid, endpoint=endpoint)) return Status(-1, "recover table partition failed") -def RecoverTable(executor : Executor, db, table_name) -> Status: +def RecoverTable(executor, db, table_name): if CheckTable(executor, db, table_name).OK(): - log.info(f"{table_name} in {db} is healthy") + log.info("{table_name} in {db} is healthy".format(table_name=table_name, db=db)) return Status() - log.info(f"recover {table_name} in {db}") + log.info("recover {table_name} in {db}".format(table_name=table_name, db=db)) status, table_info = executor.GetTableInfo(db, table_name) if not status.OK(): - log.warning(f"get table info failed. msg is {status.GetMsg()}") - return Status(-1, f"get table info failed. msg is {status.GetMsg()}") + log.warning("get table info failed. msg is {msg}".format(msg=status.GetMsg())) + return Status(-1, "get table info failed. msg is {msg}".format(msg=status.GetMsg())) partition_dict = executor.ParseTableInfo(table_info) endpoints = set() for record in table_info: @@ -154,13 +161,14 @@ def RecoverTable(executor : Executor, db, table_name) -> Status: for endpoint in endpoints: status, result = executor.GetTableStatus(endpoint) if not status.OK(): - log.warning(f"get table status failed. msg is {status.GetMsg()}") - return Status(-1, f"get table status failed. msg is {status.GetMsg()}") + log.warning("get table status failed. msg is {msg}".format(msg=status.GetMsg())) + return Status(-1, "get table status failed. msg is {msg}".format(msg=status.GetMsg())) endpoint_status[endpoint] = result max_pid = int(table_info[-1][2]) for pid in range(max_pid + 1): RecoverPartition(executor, db, partition_dict[str(pid)], endpoint_status) # wait op + time.sleep(1) while True: status, result = executor.ShowOpStatus(db, table_name) is_finish = True @@ -172,18 +180,18 @@ def RecoverTable(executor : Executor, db, table_name) -> Status: is_finish = False break if not is_finish: - log.info(f"waiting task") + log.info("waiting task") time.sleep(2) else: break status = CheckTable(executor, db, table_name) if status.OK(): - log.info(f"{table_name} in {db} recover success") + log.info("{table_name} in {db} recover success".format(table_name=table_name, db=db)) else: log.warning(status.GetMsg()) return status -def RecoverData(executor : Executor): +def RecoverData(executor): status, dbs = executor.GetAllDatabase() if not status.OK(): log.error("get database failed") @@ -193,36 +201,40 @@ def RecoverData(executor : Executor): for db in alldb: status, tables = executor.GetAllTable(db) if not status.OK(): - log.error(f"get all table failed") + log.error("get all table failed") return for name in tables: if not RecoverTable(executor, db, name).OK(): return -def ChangeLeader(db: str, partition: Partition, src_endpoint: str, desc_endpoint: str, one_replica: bool, - restore: bool = True) -> Status: +def ChangeLeader(db, partition, src_endpoint, desc_endpoint, one_replica, restore = True): log.info( - f"change leader of table partition {db} {partition.GetName()} {partition.GetPid()} in '{src_endpoint}' to '{desc_endpoint}' {one_replica}") + "change leader of table partition {db} {name} {pid} in '{src_endpoint}' to '{desc_endpoint}' {one_replica}".format( + db=db, name=partition.GetName(), pid=partition.GetPid(),src_endpoint=src_endpoint, desc_endpoint=desc_endpoint, one_replica=one_replica)) if one_replica and not executor.AddReplica(db, partition.GetName(), partition.GetPid(), desc_endpoint, True).OK(): - return Status(-1, f"add replica failed. {db} {partition.GetName()} {partition.GetPid()} {desc_endpoint}") + return Status(-1, "add replica failed. {db} {name} {pid} {desc_endpoint}".format( + db=db, name=partition.GetName(), pid=partition.GetPid(), desc_endpoint=desc_endpoint)) target_endpoint = "auto" if len(desc_endpoint) > 0: target_endpoint = desc_endpoint status = executor.ChangeLeader(db, partition.GetName(), partition.GetPid(), target_endpoint, True) if not status.OK(): log.error(status.msg) - return Status(-1, f"change leader failed. {db} {partition.GetName()} {partition.GetPid()}") + return Status(-1, "change leader failed. {db} {name} {pid}".format( + db=db, name=partition.GetName(), pid=partition.GetPid())) status = executor.RecoverTablePartition(db, partition.GetName(), partition.GetPid(), src_endpoint, True) if not status.OK(): log.error(status.GetMsg()) - return Status(-1, f"recover table failed. {db} {partition.GetName()} {partition.GetPid()} {src_endpoint}") + return Status(-1, "recover table failed. {db} {name} {pid} {src_endpoint}".format( + db=db, name=partition.GetName(), pid=partition.GetPid(), src_endpoint=src_endpoint)) if restore and one_replica: if not executor.DelReplica(db, partition.GetName(), partition.GetPid(), src_endpoint, True).OK(): - return Status(-1, f"del replica failed. {db} {partition.GetName()} {partition.GetPid()} {src_endpoint}") + return Status(-1, "del replica failed. {db} {name} {pid} {src_endpoint}".format( + db=db, name=partition.GetName(), pid=partition.GetPid(), src_endpoint=src_endpoint)) return Status() -def MigratePartition(db : str, partition : Partition, src_endpoint : str, desc_endpoint : str, one_replica : bool) -> Status: +def MigratePartition(db, partition, src_endpoint, desc_endpoint, one_replica): if partition.IsLeader(): des = "" if not one_replica else desc_endpoint status = ChangeLeader(db, partition, src_endpoint, des, one_replica, True) @@ -234,24 +246,25 @@ def MigratePartition(db : str, partition : Partition, src_endpoint : str, desc_e status = executor.Migrate(db, partition.GetName(), partition.GetPid(), src_endpoint, desc_endpoint, True) if not status.OK(): log.error(status.GetMsg()) - return Status(-1, f"migrate partition failed! table {partition.GetName()} partition {partition.GetPid()} {src_endpoint} {desc_endpoint}") + return Status(-1, "migrate partition failed! table {name} partition {pid} {src_endpoint} {desc_endpoint}".format( + name=partition.GetName(), pid=partition.GetPid(), src_endpoint=src_endpoint, desc_endpoint=desc_endpoint)) return Status() -def BalanceInDatabase(executor : Executor, endpoints : list, db : str) -> Status: - log.info(f"start to balance {db}") +def BalanceInDatabase(executor, endpoints, db): + log.info("start to balance {db}".format(db=db)) status, result = executor.GetTableInfo(db) if not status.OK(): - log.error(f"get table failed from {db}") - return Status(-1, f"get table failed from {db}") - all_dict : dict[str, list[Partition]] = {} + log.error("get table failed from {db}".format(db=db)) + return Status(-1, "get table failed from {db}".format(db=db)) + all_dict = {} total_partitions = 0 - endpoint_partition_map : dict[str, set] = {} + endpoint_partition_map = {} replica_map = {} for record in result: total_partitions += 1 is_leader = True if record[4] == "leader" else False is_alive = True if record[5] == "yes" else False - partition : Partition = Partition(record[0], record[1], record[2], record[3], is_leader, is_alive, record[6]) + partition = Partition(record[0], record[1], record[2], record[3], is_leader, is_alive, record[6]) all_dict.setdefault(partition.GetEndpoint(), []); all_dict[partition.GetEndpoint()].append(partition) endpoint_partition_map.setdefault(partition.GetEndpoint(), set()) @@ -270,21 +283,25 @@ def BalanceInDatabase(executor : Executor, endpoints : list, db : str) -> Status for endpoint in endpoints: if len(all_dict[endpoint]) > len(all_dict[migrate_out_endpoint]) : migrate_out_endpoint = endpoint if len(all_dict[endpoint]) < len(all_dict[migrate_in_endpoint]) : migrate_in_endpoint = endpoint - log.info(f"max partition endpoint: {migrate_out_endpoint} num: {len(all_dict[migrate_out_endpoint])}, " - f"min partition endpoint: {migrate_in_endpoint} num: {len(all_dict[migrate_in_endpoint])}") + log.info("max partition endpoint: {migrate_out_endpoint} num: {num}, ".format( + migrate_out_endpoint=migrate_out_endpoint, num=len(all_dict[migrate_out_endpoint])) + + "min partition endpoint: {migrate_in_endpoint} num: {num}".format( + migrate_in_endpoint=migrate_in_endpoint, num=len(all_dict[migrate_in_endpoint]))) if not len(all_dict[migrate_out_endpoint]) > len(all_dict[migrate_in_endpoint]) + 1 : break candidate_partition = list(all_dict[migrate_out_endpoint]) while len(candidate_partition) > 0: idx = random.randint(0, len(candidate_partition) - 1) - partition : Partition = candidate_partition.pop(idx) + partition = candidate_partition.pop(idx) if partition.GetKey() in endpoint_partition_map[migrate_in_endpoint]: continue - log.info(f"migrate table {partition.GetName()} partition {partition.GetPid()} in {db} from {partition.GetEndpoint()} to {migrate_in_endpoint}") + log.info("migrate table {name} partition {pid} in {db} from {endpoint} to {migrate_in_endpoint}".format( + name=partition.GetName(), pid=partition.GetPid(), db=db, endpoint=partition.GetEndpoint(),migrate_in_endpoint=migrate_in_endpoint)) status = MigratePartition(db, partition, migrate_out_endpoint, migrate_in_endpoint, replica_map[partition.GetKey()] == 1) if not status.OK(): log.error(status.GetMsg()) return status - log.info(f"migrate table {partition.GetName()} partition {partition.GetPid()} in {db} from {partition.GetEndpoint()} to {migrate_in_endpoint} success") + log.info("migrate table {name} partition {pid} in {db} from {endpoint} to {migrate_in_endpoint} success".format( + name=partition.GetName(), pid=partition.GetPid(), endpoint=partition.GetEndpoint(), db=db, migrate_in_endpoint=migrate_in_endpoint)) all_dict[migrate_in_endpoint].append(partition) endpoint_partition_map[migrate_in_endpoint].add(partition.GetKey()) for pos in range(len(all_dict[migrate_out_endpoint])): @@ -295,10 +312,10 @@ def BalanceInDatabase(executor : Executor, endpoints : list, db : str) -> Status break return Status() -def ScaleOut(executor : Executor): +def ScaleOut(executor): status, result = executor.ShowTablet() if not status.OK(): - log.error(f"execute showtablet failed") + log.error("execute showtablet failed") return endpoints = [] for record in result: @@ -312,20 +329,20 @@ def ScaleOut(executor : Executor): return log.info("execute scale-out success") -def ScaleInEndpoint(executor : Executor, endpoint : str, desc_endpoints : list) -> Status: - log.info(f"start to scale-in {endpoint}") +def ScaleInEndpoint(executor, endpoint, desc_endpoints): + log.info("start to scale-in {endpoint}".format(endpoint=endpoint)) status, status_result = executor.GetTableStatus(endpoint) if not status.OK(): - log.error(f"get table status failed from {endpoint}") - return Status(-1, f"get table status failed from {endpoint}") + log.error("get table status failed from {endpoint}".format(endpoint=endpoint)) + return Status(-1, "get table status failed from {endpoint}".format(endpoint=endpoint)) status, user_dbs = executor.GetAllDatabase() if not status.OK(): log.error("get data base failed") return Status(-1, "get data base failed") dbs = list(INTERNAL_DB) dbs.extend(user_dbs) - all_dict : dict[str, list[Partition]] = {} - endpoint_partition_map : dict[str, set] = {} + all_dict = {} + endpoint_partition_map = {} db_map = {} replica_map = {} for db in dbs: @@ -336,7 +353,7 @@ def ScaleInEndpoint(executor : Executor, endpoint : str, desc_endpoints : list) for record in result: is_leader = True if record[4] == "leader" else False is_alive = True if record[5] == "yes" else False - partition : Partition = Partition(record[0], record[1], record[2], record[3], is_leader, is_alive, record[6]) + partition = Partition(record[0], record[1], record[2], record[3], is_leader, is_alive, record[6]) all_dict.setdefault(partition.GetEndpoint(), []) all_dict[partition.GetEndpoint()].append(partition) endpoint_partition_map.setdefault(partition.GetEndpoint(), set()) @@ -347,12 +364,13 @@ def ScaleInEndpoint(executor : Executor, endpoint : str, desc_endpoints : list) for key, value in replica_map.items(): if value > len(desc_endpoints): db, name = db_map[key] - log.error(f"replica num of table {name} in {db} is {value}, left endpoints num is {len(desc_endpoints)}, cannot execute scale-in") + log.error("replica num of table {name} in {db} is {value}, left endpoints num is {len}, cannot execute scale-in".format( + name=name, db=db, value=value, len=len(desc_endpoints))) return Status(-1, "cannot execute scale-in") for key, record in status_result.items(): is_leader = True if record[3] == "kTableLeader" else False db, name = db_map.get("{}_{}".format(record[0], record[1])) - partition : Partition = Partition(name, record[0], record[1], endpoint, is_leader, True, record[2]) + partition = Partition(name, record[0], record[1], endpoint, is_leader, True, record[2]) desc_endpoint = "" min_partition_num = sys.maxsize for cur_endpoint in all_dict: @@ -362,29 +380,33 @@ def ScaleInEndpoint(executor : Executor, endpoint : str, desc_endpoints : list) min_partition_num = len(all_dict[cur_endpoint]) desc_endpoint = cur_endpoint if desc_endpoint == "": - log.error(f"can not find endpoint to migrate. {db} {name} {record[1]} in {endpoint}") + log.error("can not find endpoint to migrate. {db} {name} {pid} in {endpoint}".format( + db=db, name=name, pid=record[1], endpoint=endpoint)) continue - log.info(f"migrate table {partition.GetName()} partition {partition.GetPid()} in {db} from {endpoint} to {desc_endpoint}") + log.info("migrate table {name} partition {pid} in {db} from {endpoint} to {desc_endpoint}".format( + name=partition.GetName(), pid=partition.GetPid(), db=db, endpoint=endpoint, desc_endpoint=desc_endpoint)) status = MigratePartition(db, partition, endpoint, desc_endpoint, replica_map[partition.GetKey()] == 1) if not status.OK(): log.error(status.GetMsg()) - log.error(f"migrate table {partition.GetName()} partition {partition.GetPid()} in {db} from {endpoint} to {desc_endpoint} failed") + log.error("migrate table {name} partition {pid} in {db} from {endpoint} to {desc_endpoint} failed".format( + name=partition.GetName(), pid=partition.GetPid(), db=db, endpoint=endpoint, desc_endpoint=desc_endpoint)) return status - log.info(f"migrate table {partition.GetName()} partition {partition.GetPid()} in {db} from {endpoint} to {desc_endpoint} success") + log.info("migrate table {name} partition {pid} in {db} from {endpoint} to {desc_endpoint} success".format( + name=partition.GetName(), pid=partition.GetPid(), db=db, endpoint=endpoint, desc_endpoint=desc_endpoint)) return Status() -def ScaleIn(executor : Executor, endpoints : list): +def ScaleIn(executor, endpoints): status, result = executor.ShowTablet() if not status.OK(): - log.error(f"execute showtablet failed") + log.error("execute showtablet failed") return alive_endpoints = [] for record in result: if record[2] == "kHealthy" : alive_endpoints.append(record[0]) for endpoint in endpoints: if endpoint not in alive_endpoints: - log.error(f"{endpoint} is not alive, cannot execute scale-in") + log.error("{endpoint} is not alive, cannot execute scale-in".format(endpoint=endpoint)) return alive_endpoints.remove(endpoint) for endpoint in endpoints: @@ -392,7 +414,7 @@ def ScaleIn(executor : Executor, endpoints : list): return log.info("execute scale-in success") -def GetOpStatus(executor : Executor, db : str = None, filter : str = None, wait_done : bool = False) -> tuple([Status, list]): +def GetOpStatus(executor, db = None, filter = None, wait_done = False): all_results = [] if not db: status, user_dbs = executor.GetAllDatabase() @@ -422,29 +444,29 @@ def GetOpStatus(executor : Executor, db : str = None, filter : str = None, wait_ all_results.extend([[db] + record for record in result if (not filter) or (record[4] == filter)]) break - log.info(f"waiting {wait_op}") + log.info("waiting {wait_op}".format(wait_op=wait_op)) time.sleep(2) return Status(), all_results -def ShowTableStatus(executor : Executor, pattern : str = '%') -> tuple([Status, list]): +def ShowTableStatus(executor, pattern = '%'): status, result = executor.ShowTableStatus(pattern) return status, result -def PreUpgrade(executor : Executor, endpoint : str, statfile : str, allow_single_replica : bool) -> Status: +def PreUpgrade(executor, endpoint, statfile, allow_single_replica): leaders = [] # get all leader partitions - log.info(f"start to pre-upgrade {endpoint}") + log.info("start to pre-upgrade {endpoint}".format(endpoint=endpoint)) status, status_result = executor.GetTableStatus(endpoint) if not status.OK(): - log.error(f"get table status failed from {endpoint}") - return Status(-1, f"get table status failed from {endpoint}") + log.error("get table status failed from {endpoint}".format(endpoint=endpoint)) + return Status(-1, "get table status failed from {endpoint}".format(endpoint=endpoint)) status, user_dbs = executor.GetAllDatabase() if not status.OK(): log.error("get database failed") return Status(-1, "get database failed") dbs = list(INTERNAL_DB) dbs.extend(user_dbs) - all_dict : dict[str, list[Partition]] = {} + all_dict = {} db_map = {} replica_map = {} for db in dbs: @@ -455,7 +477,7 @@ def PreUpgrade(executor : Executor, endpoint : str, statfile : str, allow_single for record in result: is_leader = True if record[4] == "leader" else False is_alive = True if record[5] == "yes" else False - partition : Partition = Partition(record[0], record[1], record[2], record[3], is_leader, is_alive, int(record[6])) + partition = Partition(record[0], record[1], record[2], record[3], is_leader, is_alive, int(record[6])) all_dict.setdefault(partition.GetEndpoint(), []) all_dict[partition.GetEndpoint()].append(partition) db_map.setdefault(partition.GetKey(), (db, partition.GetName())) @@ -465,7 +487,7 @@ def PreUpgrade(executor : Executor, endpoint : str, statfile : str, allow_single for key, record in status_result.items(): if record[3] == "kTableLeader": db, name = db_map.get("{}_{}".format(record[0], record[1])) - partition : Partition = Partition(name, record[0], record[1], endpoint, is_leader, True, int(record[2])) + partition = Partition(name, record[0], record[1], endpoint, is_leader, True, int(record[2])) one_replica = replica_map[partition.GetKey()] == 1 # if one_replica, add a new replica @@ -482,7 +504,8 @@ def PreUpgrade(executor : Executor, endpoint : str, statfile : str, allow_single min_partition_num = len(all_dict[cur_endpoint]) desc_endpoint = cur_endpoint if desc_endpoint == "": - log.error(f"can not find endpoint to add replica to. {db} {name} {record[1]} in {endpoint}") + log.error("can not find endpoint to add replica to. {db} {name} {pid} in {endpoint}".format( + db=db, name=name, pid=record[1], endpoint=endpoint)) continue # change leader @@ -501,13 +524,13 @@ def PreUpgrade(executor : Executor, endpoint : str, statfile : str, allow_single else: return status -def PostUpgrade(executor : Executor, endpoint : str, statfile: str) -> Status: +def PostUpgrade(executor, endpoint, statfile): leaders = [] # check all the op status to ensure all are in stable states (i.e., kDone/kFailed) - log.info(f"check all ops are complete") + log.info("check all ops are complete") GetOpStatus(executor, None, None, True) - log.info(f"start to post-upgrade {endpoint}") + log.info("start to post-upgrade {endpoint}".format(endpoint=endpoint)) # get all leader partitions from statfile with open(statfile, "r") as reader: for line in reader.readlines(): @@ -526,20 +549,20 @@ def PostUpgrade(executor : Executor, endpoint : str, statfile: str) -> Status: key = "{}_{}".format(tid, pid) status, status_result = executor.GetTableStatus(endpoint) if not status.OK(): - log.error(f"get table status failed from {endpoint}: {status.GetMsg()}") - return Status(-1, f"get table status failed from {endpoint}: {status.GetMsg()}") + log.error("get table status failed from {endpoint}: {msg}".format(endpoint=endpoint, msg=status.GetMsg())) + return Status(-1, "get table status failed from {endpoint}: {msg}".format(endpoint=endpoint, msg=status.GetMsg())) table_status = status_result.get(key) if table_status is None: - log.error(f"get empty table status for partition {key} from {endpoint}") - return Status(-1, f"get empty table status for partition {key} from {endpoint}") + log.error("get empty table status for partition {key} from {endpoint}".format(key=key, endpoint=endpoint)) + return Status(-1, "get empty table status for partition {key} from {endpoint}".format(key=key, endpoint=endpoint)) is_leader = table_status[3] == 'kTableLeader' is_alive = table_status[4] != "kTableUndefined" if is_leader: - log.warning(f"{db} {name} {pid} in {endpoint} is already leader") + log.warning("{db} {name} {pid} in {endpoint} is already leader".format(db=db, name=name, pid=pid, endpoint=endpoint)) continue - partition : Partition = Partition(name, tid, pid, endpoint, is_leader, is_alive, int(table_status[2])) + partition = Partition(name, tid, pid, endpoint, is_leader, is_alive, int(table_status[2])) # desc_endpoint is not empty, meaning we added an extra replica for this partition in pre-upgrade one_replica = True if curr_leader == "": @@ -547,7 +570,7 @@ def PostUpgrade(executor : Executor, endpoint : str, statfile: str) -> Status: one_replica = False status, partitions = executor.GetTablePartition(db, name) if not status.OK(): - msg = f"get table partition {db} {name} failed" + msg = "get table partition {db} {name} failed".format(db=db, name=name) log.error(msg) return Status(-1, msg) for p in partitions.get(pid): @@ -556,7 +579,7 @@ def PostUpgrade(executor : Executor, endpoint : str, statfile: str) -> Status: break if curr_leader == "": - msg = f"cannot find leader endpoint for {partition.GetName()} {partition.GetPid()}" + msg = "cannot find leader endpoint for {name} {pid}".format(name=partition.GetName(), pid=partition.GetPid()) log.warning(msg) return Status(-1, msg) @@ -568,12 +591,13 @@ def PostUpgrade(executor : Executor, endpoint : str, statfile: str) -> Status: if one_replica: # if one_replica, del the extra replica which is the current leader if not executor.DelReplica(db, partition.GetName(), partition.GetPid(), curr_leader, True).OK(): - return Status(-1, f"del replica failed. {db} {partition.GetName()} {partition.GetPid()} {curr_leader}") + return Status(-1, "del replica failed. {db} {name} {pid} {curr_leader}".format( + db=db, name=partition.GetName(), pid=partition.GetPid(), curr_leader=curr_leader)) os.remove(statfile) return Status() -def PrettyPrint(data : list, header : list = None): +def PrettyPrint(data, header = None): from prettytable import PrettyTable t = PrettyTable(header) for record in data: @@ -585,8 +609,8 @@ def PrettyPrint(data : list, header : list = None): manage_ops = set(["recoverdata", "scalein", "scaleout", "pre-upgrade", "post-upgrade"]) query_ops = set(["showopstatus", "showtablestatus"]) if options.cmd not in manage_ops and options.cmd not in query_ops: - print(f"unsupported cmd: {options.cmd}") - print(f"available cmds: {list(manage_ops) + list(query_ops)}") + print("unsupported cmd: {cmd}".format(cmd=options.cmd)) + print("available cmds: {msg}".format(msg=list(manage_ops) + list(query_ops))) sys.exit() executor = Executor(options.openmldb_bin_path, options.zk_cluster, options.zk_root_path) @@ -645,7 +669,7 @@ def PrettyPrint(data : list, header : list = None): else: print(status.msg) else: - print(f"cmd {options.cmd} is not handled") + print("cmd {cmd} is not handled".format(cmd=options.cmd)) if options.cmd in manage_ops: if auto_failover and not executor.SetAutofailover("true").OK(): diff --git a/tools/tool.py b/tools/tool.py index 358751b4db9..e64b172b49b 100644 --- a/tools/tool.py +++ b/tools/tool.py @@ -16,7 +16,6 @@ import subprocess import sys import time -from typing import List, Dict, Tuple log = logging.getLogger(__name__) logging.basicConfig(level=logging.INFO, format = '%(levelname)s: %(message)s') @@ -78,23 +77,46 @@ def __init__(self, openmldb_bin_path, zk_cluster, zk_root_path): "--zk_root_path=" + self.zk_root_path, "--role=sql_client", "--interactive=false"] + self.endpoint_map = {} - def Connect(self) -> Status: - status, endpoint = self.GetNsLeader() - if status.OK() and status.GetMsg().find("zk client init failed") == -1: - self.ns_leader = endpoint - log.info(f"ns leader: {self.ns_leader}") - self.ns_base_cmd = [self.openmldb_bin_path, - "--endpoint=" + self.ns_leader, - "--role=ns_client", - "--interactive=false"] - return Status() - return Status(-1, "connect OpenMLDB failed") + def Connect(self): + cmd = list(self.ns_base_cmd) + cmd.append("--cmd=showns") + status, output = self.RunWithRetuncode(cmd) + if not status.OK() or status.GetMsg().find("zk client init failed") != -1: + return Status(-1, "get ns failed"), None + result = self.ParseResult(output) + for record in result: + if record[2] == "leader": + self.ns_leader = record[0] + if record[1] != '-': + self.endpoint_map[record[0]] = record[1] + else: + self.endpoint_map[record[0]] = record[0] + cmd = list(self.ns_base_cmd) + cmd.append("--cmd=showtablet") + status, output = self.RunWithRetuncode(cmd) + if not status.OK(): + return Status(-1, "get tablet failed"), None + result = self.ParseResult(output) + for record in result: + if record[1] != '-': + self.endpoint_map[record[0]] = record[1] + else: + self.endpoint_map[record[0]] = record[0] + + + log.info("ns leader: {ns_leader}".format(ns_leader = self.ns_leader)) + self.ns_base_cmd = [self.openmldb_bin_path, + "--endpoint=" + self.endpoint_map[self.ns_leader], + "--role=ns_client", + "--interactive=false"] + return Status() def RunWithRetuncode(self, command, universal_newlines = True, useshell = USE_SHELL, - env = os.environ) -> tuple([Status, str]): + env = os.environ): try: p = subprocess.Popen(command, stdout = subprocess.PIPE, stderr = subprocess.PIPE, shell = useshell, universal_newlines = universal_newlines, env = env) output = p.stdout.read() @@ -108,7 +130,7 @@ def RunWithRetuncode(self, command, except Exception as ex: return Status(-1, ex), None - def GetNsLeader(self) -> tuple([Status, str]): + def GetNsLeader(self): cmd = list(self.ns_base_cmd) cmd.append("--cmd=showns") status, output = self.RunWithRetuncode(cmd) @@ -120,7 +142,7 @@ def GetNsLeader(self) -> tuple([Status, str]): return Status(-1, "get ns leader failed"), None - def ParseResult(self, output) -> list: + def ParseResult(self, output): result = [] lines = output.split("\n") content_is_started = False @@ -136,7 +158,7 @@ def ParseResult(self, output) -> list: result.append(record) return result - def GetAutofailover(self) -> tuple([Status, bool]): + def GetAutofailover(self): cmd = list(self.ns_base_cmd) cmd.append("--cmd=confget auto_failover") status, output = self.RunWithRetuncode(cmd) @@ -146,13 +168,13 @@ def GetAutofailover(self) -> tuple([Status, bool]): return Status(), True return Status(), False; - def SetAutofailover(self, value) -> Status: + def SetAutofailover(self, value): cmd = list(self.ns_base_cmd) cmd.append("--cmd=confset auto_failover " + value) status, output = self.RunWithRetuncode(cmd) return status - def GetAllDatabase(self) -> tuple([Status, List]): + def GetAllDatabase(self): cmd = list(self.ns_base_cmd) cmd.append("--cmd=showdb") status, output = self.RunWithRetuncode(cmd) @@ -165,7 +187,7 @@ def GetAllDatabase(self) -> tuple([Status, List]): dbs.append(record[1]) return Status(), dbs - def GetTableInfo(self, database, table_name = '') -> tuple([Status, List]): + def GetTableInfo(self, database, table_name = ''): cmd = list(self.ns_base_cmd) cmd.append("--cmd=showtable " + table_name) cmd.append("--database=" + database) @@ -179,7 +201,7 @@ def GetTableInfo(self, database, table_name = '') -> tuple([Status, List]): result.append(record) return Status(), result - def ParseTableInfo(self, table_info) -> Dict[str, List[Partition]]: + def ParseTableInfo(self, table_info): result = {} for record in table_info: is_leader = True if record[4] == "leader" else False @@ -189,14 +211,14 @@ def ParseTableInfo(self, table_info) -> Dict[str, List[Partition]]: result[record[2]].append(partition) return result - def GetTablePartition(self, database, table_name) -> tuple([Status, Dict]): + def GetTablePartition(self, database, table_name): status, result = self.GetTableInfo(database, table_name) if not status.OK: return status, None partition_dict = self.ParseTableInfo(result) return Status(), partition_dict - def GetAllTable(self, database) -> Tuple[Status, List[Partition]]: + def GetAllTable(self, database): status, result = self.GetTableInfo(database) if not status.OK(): return status, None @@ -206,9 +228,9 @@ def GetAllTable(self, database) -> Tuple[Status, List[Partition]]: tables.append(partition[0]) return Status(), tables - def GetTableStatus(self, endpoint, tid = '', pid = '') -> tuple([Status, Dict]): + def GetTableStatus(self, endpoint, tid = '', pid = ''): cmd = list(self.tablet_base_cmd) - cmd.append("--endpoint=" + endpoint) + cmd.append("--endpoint=" + self.endpoint_map[endpoint]) cmd.append("--cmd=gettablestatus " + tid + " " + pid) status, output = self.RunWithRetuncode(cmd) if not status.OK(): @@ -225,9 +247,9 @@ def GetTableStatus(self, endpoint, tid = '', pid = '') -> tuple([Status, Dict]): result[key] = record return Status(), result - def ShowTableStatus(self, pattern = '%') -> tuple([Status, list]): + def ShowTableStatus(self, pattern = '%'): cmd = list(self.sql_base_cmd) - cmd.append(f"--cmd=show table status like '{pattern}';") + cmd.append("--cmd=show table status like '{pattern}';".format(pattern = pattern)) status, output = self.RunWithRetuncode(cmd) if not status.OK(): log.error("show table status failed") @@ -250,11 +272,12 @@ def ShowTableStatus(self, pattern = '%') -> tuple([Status, list]): return Status(), output_processed - def LoadTable(self, endpoint, name, tid, pid, sync = True) -> Status: + def LoadTable(self, endpoint, name, tid, pid, sync = True): cmd = list(self.tablet_base_cmd) - cmd.append("--endpoint=" + endpoint) + cmd.append("--endpoint=" + self.endpoint_map[endpoint]) cmd.append("--cmd=loadtable {} {} {} 0 8".format(name, tid, pid)) status, output = self.RunWithRetuncode(cmd) + time.sleep(1) if status.OK() and output.find("LoadTable ok") != -1: if not sync: return Status() @@ -266,23 +289,23 @@ def LoadTable(self, endpoint, name, tid, pid, sync = True) -> Status: if table_stat == "kTableNormal": return Status() elif table_stat == "kTableLoading" or table_stat == "kTableUndefined": - log.info(f"table is loading... tid {tid} pid {pid}") + log.info("table is loading... tid {tid} pid {pid}".format(tid, pid)) else: - return Status(-1, f"table stat is {table_stat}") + return Status(-1, "table stat is {table_stat}".format(table_stat)) time.sleep(2) return Status(-1, "execute load table failed") - def GetLeaderFollowerOffset(self, endpoint, tid, pid) -> tuple([Status, List]): + def GetLeaderFollowerOffset(self, endpoint, tid, pid): cmd = list(self.tablet_base_cmd) - cmd.append("--endpoint=" + endpoint) + cmd.append("--endpoint=" + self.endpoint_map[endpoint]) cmd.append("--cmd=getfollower {} {}".format(tid, pid)) status, output = self.RunWithRetuncode(cmd) if not status.OK(): return status return Status(), self.ParseResult(output) - def RecoverTablePartition(self, database, name, pid, endpoint, sync = False) -> Status: + def RecoverTablePartition(self, database, name, pid, endpoint, sync = False): cmd = list(self.ns_base_cmd) cmd.append("--cmd=recovertable {} {} {}".format(name, pid, endpoint)) cmd.append("--database=" + database) @@ -293,9 +316,9 @@ def RecoverTablePartition(self, database, name, pid, endpoint, sync = False) -> return Status() return status - def UpdateTableAlive(self, database, name, pid, endpoint, is_alive) -> Status: + def UpdateTableAlive(self, database, name, pid, endpoint, is_alive): if is_alive not in ["yes", "no"]: - return Status(-1, f"invalid argument {is_alive}") + return Status(-1, "invalid argument {is_alive}".format(is_alive)) cmd = list(self.ns_base_cmd) cmd.append("--cmd=updatetablealive {} {} {} {}".format(name, pid, endpoint, is_alive)) cmd.append("--database=" + database) @@ -304,7 +327,7 @@ def UpdateTableAlive(self, database, name, pid, endpoint, is_alive) -> Status: return Status() return Status(-1, "update table alive failed") - def ChangeLeader(self, database, name, pid, endpoint = "auto", sync = False) -> Status: + def ChangeLeader(self, database, name, pid, endpoint = "auto", sync = False): cmd = list(self.ns_base_cmd) cmd.append("--cmd=changeleader {} {} {}".format(name, pid, endpoint)) cmd.append("--database=" + database) @@ -313,7 +336,7 @@ def ChangeLeader(self, database, name, pid, endpoint = "auto", sync = False) -> return Status(-1, "changer leader failed") return status - def ShowOpStatus(self, database, name = '', pid = '') -> tuple([Status, List]): + def ShowOpStatus(self, database, name = '', pid = ''): cmd = list(self.ns_base_cmd) cmd.append("--cmd=showopstatus {} {} ".format(name, pid)) cmd.append("--database=" + database) @@ -322,14 +345,14 @@ def ShowOpStatus(self, database, name = '', pid = '') -> tuple([Status, List]): return status, None return Status(), self.ParseResult(output) - def CancelOp(self, database, op_id) -> Status: + def CancelOp(self, database, op_id): cmd = list(self.ns_base_cmd) cmd.append("--cmd=cancelop {}".format(op_id)) cmd.append("--database=" + database) status, output = self.RunWithRetuncode(cmd) return status - def Migrate(self, database, name, pid, src_endpoint, desc_endpoint, sync = False) -> Status: + def Migrate(self, database, name, pid, src_endpoint, desc_endpoint, sync = False): if src_endpoint == desc_endpoint: return Status(-1, "src_endpoint and desc_endpoint is same") cmd = list(self.ns_base_cmd) @@ -342,7 +365,7 @@ def Migrate(self, database, name, pid, src_endpoint, desc_endpoint, sync = False return Status() return status - def ShowTablet(self) -> tuple([Status, List]): + def ShowTablet(self): cmd = list(self.ns_base_cmd) cmd.append("--cmd=showtablet") status, output = self.RunWithRetuncode(cmd) @@ -350,7 +373,7 @@ def ShowTablet(self) -> tuple([Status, List]): return status, None return Status(), self.ParseResult(output) - def AddReplica(self, database, name, pid, endpoint, sync = False) -> Status: + def AddReplica(self, database, name, pid, endpoint, sync = False): cmd = list(self.ns_base_cmd) cmd.append("--cmd=addreplica {} {} {}".format(name, pid, endpoint)) cmd.append("--database=" + database) @@ -361,7 +384,7 @@ def AddReplica(self, database, name, pid, endpoint, sync = False) -> Status: return Status() return Status(-1, "add replica failed") - def DelReplica(self, database, name, pid, endpoint, sync = False) -> Status: + def DelReplica(self, database, name, pid, endpoint, sync = False): cmd = list(self.ns_base_cmd) cmd.append("--cmd=delreplica {} {} {}".format(name, pid, endpoint)) cmd.append("--database=" + database) @@ -372,7 +395,7 @@ def DelReplica(self, database, name, pid, endpoint, sync = False) -> Status: return Status() return Status(-1, "del replica failed") - def WaitingOP(self, database, name, pid) -> Status: + def WaitingOP(self, database, name, pid): while True: error_try_times = 3 while error_try_times > 0: @@ -385,10 +408,10 @@ def WaitingOP(self, database, name, pid) -> Status: record = result[-1] if record[4] == 'kDoing' or record[4] == 'kInited': value = " ".join(record) - log.info(f"waiting {value}") + log.info("waiting {value}".format(value = value)) time.sleep(2) elif record[4] == 'kFailed': - return Status(-1, f"job {record[0]} execute failed") + return Status(-1, "job {id} execute failed".format(id = record[0])) else: break return Status() From d06067eef12129c3d2721e72211183c5e07a5174 Mon Sep 17 00:00:00 2001 From: TanZiYen <104113819+TanZiYen@users.noreply.github.com> Date: Thu, 14 Sep 2023 13:57:38 +0800 Subject: [PATCH 35/63] docs: change_for_install_deploy_of_deploy_folder (#3466) --- docs/en/deploy/images/tablet_ps.png | Bin 0 -> 18431 bytes docs/en/deploy/images/zk_cli.png | Bin 0 -> 5654 bytes docs/en/deploy/images/zk_ps.png | Bin 0 -> 100680 bytes docs/en/deploy/images/zk_started.png | Bin 0 -> 16039 bytes docs/en/deploy/install_deploy.md | 678 ++++++++++++++++++--------- 5 files changed, 444 insertions(+), 234 deletions(-) create mode 100644 docs/en/deploy/images/tablet_ps.png create mode 100644 docs/en/deploy/images/zk_cli.png create mode 100644 docs/en/deploy/images/zk_ps.png create mode 100644 docs/en/deploy/images/zk_started.png diff --git a/docs/en/deploy/images/tablet_ps.png b/docs/en/deploy/images/tablet_ps.png new file mode 100644 index 0000000000000000000000000000000000000000..064f9c651bfaaa73f07d74601aa2a4433610d111 GIT binary patch literal 18431 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 Q;N=3WFW6a@orfj-ANHU?q5uE@ literal 0 HcmV?d00001 diff --git a/docs/en/deploy/images/zk_ps.png b/docs/en/deploy/images/zk_ps.png new file mode 100644 index 0000000000000000000000000000000000000000..8101843919d5bf2a6fc360cb54fff0f5b24b7657 GIT binary patch literal 100680 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⪼0LIzQj2h`m&TkWb-|P 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 G|NS4K1gf6^ literal 0 HcmV?d00001 diff --git a/docs/en/deploy/images/zk_started.png b/docs/en/deploy/images/zk_started.png new file mode 100644 index 0000000000000000000000000000000000000000..da9095a7d2e894d065af37d6751800f8fe407e31 GIT binary patch literal 16039 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 10.15. Where Linux glibc version >= 2.17. Other operating system versions have not been fully tested and cannot be guaranteed to be fully compatible. -* Memory: Depends on the amount of data, 8 GB and above is recommended. -* CPU: - * Currently only the x86 architecture is supported, and architectures such as ARM are currently not supported. - * The number of cores is recommended to be no less than 4 cores. If the CPU does not support the AVX2 instruction set in the Linux environment, the deployment package needs to be recompiled from the source code. +### Operating System + +The pre-compiled packages that have been released offer support for the following operating systems: CentOS 7.x, Ubuntu 20.04, SUSE 12 SP3, and macOS 12. For Linux, a glibc version of >= 2.17 is required. While pre-compiled packages for other operating system distributions have not undergone comprehensive testing and therefore cannot guarantee complete compatibility, you can explore [compiling from source code](compile.md) to extend support to other operating systems. + +````{note} +Linux users can assess their system's compatibility through the following commands: + +```shell +cat /etc/os-release # most linux +cat /etc/redhat-release # redhat only +ldd --version +strings /lib64/libc.so.6 | grep ^GLIBC_ +``` +Generally, ldd version should be >= 2.17, and GLIBC_2.17 should be present in libc.so.6. These factors indicate compatibility with glibc 2.17 for program and dynamic library operations. If the system's glibc version falls below 2.17, compiling from source code is necessary. +```` + +### Third-party Component Dependencies + +If you need to deploy ZooKeeper and TaskManager, you need a Java runtime environment. + +### Hardware + +Regarding hardware requirements: + +- CPU: + - X86 CPU is recommended, preferably with a minimum of 4 cores. + - For users of pre-compiled packages, AVX2 instruction set support is required. Otherwise, consider [compiling from source code](compile.md). +- Memory: + - It is recommended to have at least 8 GB of RAM. For business scenarios involving substantial data loads, 128 GB or more is advisable. +- Other hardware components: + - No specific requirements. However, hard disk and network performance will affect OpenMLDB's latency and throughput performance. ## Deployment Package -The precompiled OpenMLDB deployment package is used by default in this documentation ([Linux](https://github.com/4paradigm/OpenMLDB/releases/download/v0.8.2/openmldb-0.8.2-linux.tar.gz) , [macOS](https://github.com/4paradigm/OpenMLDB/releases/download/v0.8.2/openmldb-0.8.2-darwin.tar.gz)), the supported operating system requirements are: CentOS 7, Ubuntu 20.04, macOS >= 10.15. If the user wishes to compile by himself (for example, for OpenMLDB source code development, the operating system or CPU architecture is not in the support list of the precompiled deployment package, etc.), the user can choose to compile and use in the docker container or compile from the source code. For details, please refer to our [compile documentation](compile.md). -## Configure Environment (Linux) +### Download/Source Compilation + +If your operating system is capable of running pre-compiled packages directly, you can download them from the following sources: + +- GitHub Release: https://github.com/4paradigm/OpenMLDB/releases +- Mirror Site (China): http://43.138.115.238/download/ + +The compatible operating systems are as follows: + +- `openmldb-x.x.x-linux.tar.gz`: CentOS 7.x, Ubuntu 20.04, SUSE 12 SP3 +- `openmldb-x.x.x-darwin.tar.gz`: macOS 12 + +If your operating system is not mentioned above or if you want to compile from source code, please refer [here](compile.md) to compile from source code. + +### Linux Platform Compatibility pre-test + +Due to the variations among Linux platforms, the distribution package may not be entirely compatible with your machine. Therefore, it's recommended to conduct a preliminary compatibility test. Download the pre-compiled package `openmldb-0.8.2-linux.tar.gz`, and execute: + +``` +tar -zxvf openmldb-0.8.2-linux.tar.gz +./openmldb-0.8.2-linux/bin/openmldb --version +``` + +The result should display the version number of the program, as shown below: + +``` +openmldb version 0.8.2-xxxx +Debug build (NDEBUG not #defined) +``` + +If it does not run successfully, OpenMLDB needs to be compiled from source code. + +## Environment Configuration + +To ensure a deployment that's both correct and stable, it's advisable to perform the following system configuration steps. The following operations assume that the commands are executed on a Linux system. + +### Configuration of Limits -### configure the size limit of coredump file and open file limit ```bash ulimit -c unlimited ulimit -n 655360 ``` -The configurations set by `ulimit` command only take effect in the current session. -If you want to persist the configurations, add the following configurations in `/etc/security/limits.conf`: +The parameters configured through the `ulimit` command are applicable solely to the current session. For persistent configuration, you should incorporate the following settings into the `/etc/security/limits.conf` file: + ```bash * soft core unlimited * hard core unlimited @@ -28,26 +88,26 @@ If you want to persist the configurations, add the following configurations in ` * hard nofile 655360 ``` -### Disable system swap +### Disable System Swap -Check the status of the swap area. +Check if the current system swap is disabled ```bash $ free - total used free shared buff/cache available -Mem: 264011292 67445840 2230676 3269180 194334776 191204160 -Swap: 0 0 0 + total used free shared buff/cache available +Mem: 264011292 67445840 2230676 3269180 194334776 191204160 +Swap: 0 0 0 ``` -If the swap item is all 0, it means it has been closed, otherwise run the following command to disable all swap. +If the swap item is all 0, it means it has been disabled, otherwise run the following command to disable all swap. ``` swapoff -a ``` -### Disable THP (Transparent Huge Pages) +### Disable THP(Transparent Huge Pages) -Check the status of THP. +Use the following command to check if THP is turned off ``` $ cat /sys/kernel/mm/transparent_hugepage/enabled @@ -56,14 +116,14 @@ $ cat /sys/kernel/mm/transparent_hugepage/defrag [always] madvise never ``` -If "never" is not surrounded by square brackets in the above two configurations, it needs to be set. +If `never` is not set (`[never]`) in the above two configurations, use the following command to configure: ```bash echo 'never' > /sys/kernel/mm/transparent_hugepage/enabled echo 'never' > /sys/kernel/mm/transparent_hugepage/defrag ``` -Check whether the setting is successful. If "never" is surrounded by square brackets, it means that the setting has been successful, as shown below: +Check if the settings were successful, as shown below: ```bash $ cat /sys/kernel/mm/transparent_hugepage/enabled @@ -72,128 +132,82 @@ $ cat /sys/kernel/mm/transparent_hugepage/defrag always madvise [never] ``` -Note: You can also use the script to modify the above configurations, refer [here](#configure-node-environment-optional) - -### Time and zone settings - -The OpenMLDB data expiration deletion mechanism relies on the system clock. If the system clock is incorrect, the expired data will not be deleted or the data that has not expired will be deleted. - -```bash -$date -Wed Aug 22 16:33:50 CST 2018 -``` -Please make sure the time is correct. - -## Deploy Standalone Version +Note: The above three configurations can also be modified through a script, refer to [Modify Machine Environment Configuration](#modify-machine-environment-configuration-optional) -OpenMLDB standalone version needs to deploy a nameserver and a tablet. The nameserver is used for table management and metadata storage, and the tablet is used for data storage. APIServer is optional. If you want to interact with OpenMLDB using REST APIs, you need to deploy this module. +### Time Zone Settings -### Download the OpenMLDB Release Package +OpenMLDB's data expiration and deletion mechanism relies on system clock. Incorrect system clock settings can result in either expired data not being removed or non-expired data being deleted. -``` -wget https://github.com/4paradigm/OpenMLDB/releases/download/v0.8.2/openmldb-0.8.2-linux.tar.gz -tar -zxvf openmldb-0.8.2-linux.tar.gz -cd openmldb-0.8.2-linux -``` +### Network Whitelist -### Configuration -If OpenMLDB is only accessed locally, can skip this step. -#### 1. Configure tablet: conf/standalone_tablet.flags +The components comprising the OpenMLDB cluster's services require consistent network connectivity. When it comes to client communication with OpenMLDB clusters, two scenarios exist: -* Modify `endpoint`. The endpoint is the deployment machine ip/domain name and port number separated by colons. +- To establish a connection between the client (CLI and SDKs) and the OpenMLDB cluster, it is essential to not only have connectivity with ZooKeeper but also ensure access to Nameserver/ TabletServer/ TaskManager. +- If the service solely utilizes APIServer for communication, then the client is only required to ensure access to the APIServer port. -``` ---endpoint=172.27.128.33:9527 -``` +## High Availability Clusters -**Notice:** +In production environments, we strongly recommend deploying OpenMLDB clusters with high availability. For a high availability deployment architecture, we provide our recommended [High Availability Deployment Best Practices](../maintain/backup.md). -* The endpoint cannot use 0.0.0.0 and 127.0.0.1. -* If the domain name is used here, all the machines where the client using OpenMLDB is located must be equipped with the corresponding host. Otherwise, it will not be accessible. +## Daemon Startup Method -#### 2. Configure nameserver: conf/standalone_nameserver.flags +OpenMLDB offers two startup modes: Normal and Daemon. Daemon startup provides an additional layer of safeguard by automatically restarting the service process in case of unexpected termination. -* Modify `endpoint`. The endpoint is the deployment machine ip/domain name and port number separated by colons. -* The `tablet` configuration item needs to be configured with the address of the tablet that was started earlier. - -``` ---endpoint=172.27.128.33:6527 ---tablet=172.27.128.33:9527 -``` +- Daemon is not a system service; if a daemon unexpectedly exits, it will lose its daemon functionality +- Each process corresponds to an independent daemon. +- Killing a daemon using the `SIGKILL` signal will not lead to the exit of the associated daemon. To revert to normal startup mode for the daemon, you need to halt the associated process and then initiate it as a daemon. +- If the daemon is killed by a non-`SIGKILL` signal, the associated processes will exit upon the daemon's termination. -**Notice**: The endpoint cannot use 0.0.0.0 and 127.0.0.1. +To commence daemon mode, use either `bash bin/start.sh start mon` or `sbin/start-all.sh mon`. In daemon mode, `bin/.pid` contains the PID of the mon process, while `bin/.pid.child` stores the actual PID of the component. +## Deployment Method 1: One-click Deployment (Recommended) +The OpenMLDB cluster version requires the deployment of ZooKeeper, NameServer, TabletServer, and TaskManager. ZooKeeper serves for service discovery and metadata preservation. NameServer manages TabletServer for achieving high availability and failover. TabletServer stores data and synchronizes master-slave data. APIServer is an optional component; if interaction with OpenMLDB via HTTP is desired, this module must be deployed. TaskManager oversees offline jobs. We provide a one-click deployment script to simplify the process, eliminating the need for manual downloading and configuration on each machine. -#### 3. Configure apiserver: conf/standalone_apiserver.flags -APIServer is responsible for receiving http requests, forwarding them to OpenMLDB and returning results. It is stateless and is not a must-deploy component of OpenMLDB. -Before starting the APIServer, make sure that the OpenMLDB cluster has been started, otherwise APIServer will fail to initialize and exit the process. +**Note**: When deploying multiple components on the same machine, it's crucial to place them in distinct directories for streamlined management. This is particularly important while deploying TabletServer, as it's essential to avoid directory reuse to prevent conflicts between data and log files. -* Modify `endpoint`. The endpoint is the deployment machine ip/domain name and port number separated by colons. -* Modify `nameserver` to be the address of Nameserver. +DataCollector and SyncTool currently do not support one-click deployment. Please refer to the manual deployment method for these components. -``` ---endpoint=172.27.128.33:8080 ---nameserver=172.27.128.33:6527 -``` +### Environment Requirement -**Notice:** +- Deployment machines (machines executing deployment scripts) should have password-less access to other deployment nodes. +- Deployment machine: `rsync` +- Deployment machine: Python3 +- ZooKeeper and TaskManager machines: Java Runtime Environment -* The endpoint cannot use 0.0.0.0 and 127.0.0.1. You can also choose not to set `--endpoint`, and only configure the port number `--port`. +### Download OpenMLDB -### Start All the Service -Make sure `OPENMLDB_MODE=standalone` in the `conf/openmldb-env.sh` (default) ``` -sbin/start-all.sh +wget https://github.com/4paradigm/OpenMLDB/releases/download/v0.8.2/openmldb-0.8.2-linux.tar.gz +tar -zxvf openmldb-0.8.2-linux.tar.gz +cd openmldb-0.8.2-linux ``` -After the service is started, the standalone_tablet.pid, standalone_nameserver.pid and standalone_apiserver.pid files will be generated in the `bin` directory, and the process numbers at startup will be saved in them. If the pid inside the file is running, the startup will fail. - -## Deploy Cluster Version (Auto) -OpenMLDB cluster version needs to deploy Zookeeper, Nameserver, Tablet, TaskManager and other modules. Among them, Zookeeper is used for service discovery and saving metadata information. The Nameserver is used to manage the tablet, achieve high availability and failover. Tablets are used to store data and synchronize data between master and slave. APIServer is optional. If you want to interact with OpenMLDB in http, you need to deploy this module. +### Environment Configuration -Notice: It is best to deploy different components in different directories for easy upgrades individually. If multiple tablets are deployed on the same machine, they also need to be deployed in different directories. +The environment variables are defined in `conf/openmldb-env.sh`, as shown in the following table: -Environment Requirements: -- the deploy node has password-free login to other nodes -- `rsync` is required -- Python3 is required -- JRE (Java Runtime Environment) is required on the node where Zookeeper and TaskManager are deployed +| Environment Variable | Default Value | Note | +| --------------------------------- | ------------------------------------------------------- | ------------------------------------------------------------ | +| OPENMLDB_VERSION | 0.8.2 | OpenMLDB version | +| OPENMLDB_MODE | standalone | standalone or cluster | +| OPENMLDB_HOME | root directory of the release folder | openmldb root directory | +| SPARK_HOME | $OPENMLDB_HOME/spark | openmldb spark root directory,If the directory does not exist, it will be downloaded automatically.| +| OPENMLDB_TABLET_PORT | 10921 | TabletServer default port | +| OPENMLDB_NAMESERVER_PORT | 7527 | NameServer default port | +| OPENMLDB_TASKMANAGER_PORT | 9902 | taskmanager default port | +| OPENMLDB_APISERVER_PORT | 9080 | APIServer default port | +| OPENMLDB_USE_EXISTING_ZK_CLUSTER | false | Whether to use an already deployed ZooKeeper cluster. If 'false,' the deployment script will automatically start the ZooKeeper cluster. | +| OPENMLDB_ZK_HOME | $OPENMLDB_HOME/zookeeper | ZooKeeper root directory | +| OPENMLDB_ZK_CLUSTER | auto derived from `[zookeeper]` section in `conf/hosts` | ZooKeeper cluster address | +| OPENMLDB_ZK_ROOT_PATH | /openmldb | OpenMLDB root directory in ZooKeeper | +| OPENMLDB_ZK_CLUSTER_CLIENT_PORT | 2181 | ZooKeeper client port, the client port in zoo.cfg | +| OPENMLDB_ZK_CLUSTER_PEER_PORT | 2888 | ZooKeeper peer port,the first port in settings like "server.1=zoo1:2888:3888" in zoo.cfg | +| OPENMLDB_ZK_CLUSTER_ELECTION_PORT | 3888 | ZooKeeper election port, the second port in settings like "server.1=zoo1:2888:3888" in zoo.cfg | +### Node Configuration -### Download the OpenMLDB Deployment Package +The node configuration file is `conf/hosts`, as shown in the following example: -``` -wget https://github.com/4paradigm/OpenMLDB/releases/download/v0.8.2/openmldb-0.8.2-linux.tar.gz -tar -zxvf openmldb-0.8.2-linux.tar.gz -cd openmldb-0.8.2-linux -``` - -### Configuration -#### Configure the environment -The environment variables are defined in `conf/openmldb-env.sh`, -which are listed below. - -| Environment Variables | Default Values | Definitons | -|-----------------------------------|---------------------------------------------------------|-------------------------------------------------------------------------------------------------------| -| OPENMLDB_VERSION | 0.8.2 | OpenMLDB version | -| OPENMLDB_MODE | standalone | standalone or cluster mode | -| OPENMLDB_HOME | root directory of the release folder | openmldb root path | -| SPARK_HOME | $OPENMLDB_HOME/spark | the root path of openmldb spark release. if not exists, download from online | -| OPENMLDB_TABLET_PORT | 10921 | the default port for tablet | -| OPENMLDB_NAMESERVER_PORT | 7527 | the default port for nameserver | -| OPENMLDB_TASKMANAGER_PORT | 9902 | the default port for taskmanager | -| OPENMLDB_APISERVER_PORT | 9080 | the default port for apiserver | -| OPENMLDB_USE_EXISTING_ZK_CLUSTER | false | whether use an existing zookeeper cluster. If `false`, start its own zk cluster in the deploy scripts | -| OPENMLDB_ZK_HOME | $OPENMLDB_HOME/zookeeper | the root path of zookeeper release | -| OPENMLDB_ZK_CLUSTER | auto derived from `[zookeeper]` section in `conf/hosts` | the zookeeper cluster address | -| OPENMLDB_ZK_ROOT_PATH | /openmldb | zookeeper root path | -| OPENMLDB_ZK_CLUSTER_CLIENT_PORT | 2181 | zookeeper client port, i.e., clientPort in zoo.cfg | -| OPENMLDB_ZK_CLUSTER_PEER_PORT | 2888 | zookeeper peer port, which is the first port in this config server.1=zoo1:2888:3888 in zoo.cfg | -| OPENMLDB_ZK_CLUSTER_ELECTION_PORT | 3888 | zookeeper election port, which is the second port in this config server.1=zoo1:2888:3888 in zoo.cfg | - -#### Configure the deployment node - -The node list is defined in `conf/hosts`, for example: ```bash [tablet] node1:10921 /tmp/openmldb/tablet @@ -206,81 +220,91 @@ node3:7527 node3:9080 [taskmanager] -localhost:9902 +node3:9902 [zookeeper] node3:2181:2888:3888 /tmp/openmldb/zk-1 ``` -The configuration file is divided into four sections, identified by `[]`: -- `[tablet]`:tablet node list -- `[nameserver]`:nameserver node list -- `[apiserver]`:apiserver node list -- `[taskmanager]`:taskmanager node list -- `[zookeeper]`:zookeeper node list +The configuration file is segmented into five sections, each identified by `[ ]` brackets: -For each node list, one line represents one node, with the format of `host:port WORKDIR`. -Specially, for the `[zookeeper]`, its format is `host:port:zk_peer_port:zk_election_port WORKDIR`, -where there are extra port parameters, -including `zk_peer_port` for the connection between followers and leader, -and `zk_election_port` for the leader election. +- `[tablet]`: Configure the node list for deploying TabletServer. +- `[nameserver]`: Specify the node list for deploying NameServer. +- `[apiserver]`: Define the node list for deploying APIServer. +- `[taskmanager]`: List the nodes for configuring and deploying TaskManager. +- `[zookeeper]`: Indicate the node list for deploying ZooKeeper. -Note that for every node configuration, only the `host` is required, while others are optional. -If the optional parameters are not provided, -default values are used, which are defined in `conf/openmldb-env.sh`. +For each section, each entry represents one node, following the format `host:port WORKDIR`. In the case of `[zookeeper]`, additional ports are included: `zk_peer_port` for follower to connect to the leader, and `zk_election_port` for leader election. The format is `host:port:zk_peer_port:zk_election_port WORKDIR`. + +In the node list, except for the mandatory `host`, other elements are optional. If omitted, default configurations will be utilized, please refer to `conf/openmldb-env.sh` for default settings. ```{warning} -If multiple TaskManager instances are deployed in different machines,the `offline.data.prefix` should be configured to be globally accessabile by these machines (e.g., hdfs path). +If multiple TaskManager instances are deployed on distinct machines, the configured paths for offline.data.prefix must be accessible across these machines. Configuring the HDFS path is recommended. ``` -### Configure Node Environment (Optional) +### Modify Machine Environment Configuration (Optional) + ``` bash sbin/init_env.sh ``` Note: -- The script needs to be executed by the `root` user. -- The script will modify the `limit`, disable the `swap` and `THP`. - +- This script requires root execution. Other scripts does not require root privileges. +- The script only modifies limit configurations, disabling swap and THP. ### Deployment + ```bash sbin/deploy-all.sh ``` -It will distribute all the required files to the nodes defined in `conf/hosts`, -and conduct custom configuration based on `conf/hosts` and `conf/openmldb-env.sh`. -If users want to do other custom configurations, users can modify the `conf/xx.template` -before running the `deploy-all.sh` script, -so that the updated configurations will be reflected in the deployed nodes. +This script will distribute pertinent files to machines configured in `conf/hosts`. Simultaneously, it will incorporate updates to the configuration of related components based on `conf/hosts` and `conf/openmldb-env.sh`. + +If you wish to include additional customized configurations for each node, you can modify the settings in `conf/xx.template` prior to running the deployment script. By doing so, when distributing configuration files, each node can utilize the altered configuration. Running `sbin/deploy-all.sh` repeatedly will overwrite previous configurations. -Repeated execution of `sbin/deploy-all.sh` will overwrite the last deployment. +For comprehensive configuration instructions, kindly consult the [Configuration File](conf.md). Please pay attention to the detailed configuration of Spark for TaskManager, as outlined in [Spark Config Details](https://chat.openai.com/c/conf.md#spark-config-details). ### Start the Services + +Start in normal mode: + ```bash sbin/start-all.sh ``` -This script will launch all the services defined in `conf/hosts`. -Users can use `sbin/openmldb-cli.sh` to connect the cluster and verify -the services are launched successfully. +Alternatively, use daemon mode to start: + +```bash +sbin/start-all.sh mon +``` + +This script will initiate all services configured in `conf/hosts`. After startup, you can utilize the script (`sbin/openmldb-cli.sh`) to initiate the CLI to verify if the cluster has been started normally. + +```{tip} +start-all.sh is an immensely useful tool. Beyond the deployment phase, it can be employed during operation and maintenance to launch an offline OpenMLDB process. For instance, if a tablet process unexpectedly goes offline, you can directly execute start-all.sh. This script won't impact already initiated processes, but will automatically start configured but uninitiated processes. +``` + +### Stop the services + +If you need to stop all services, execute the following script: -#### Stop the Services -If users want to stop all the services, can execute the following script: ```bash sbin/stop-all.sh ``` -## Deploy Cluster Version (Manual) -OpenMLDB cluster version needs to deploy Zookeeper, Nameserver, Tablet, TaskManager and other modules. Among them, Zookeeper is used for service discovery and saving metadata information. The Nameserver is used to manage the tablet, achieve high availability and failover. Tablets are used to store data and synchronize data between master and slave. APIServer is optional. If you want to interact with OpenMLDB in http, you need to deploy this module. +## Deployment Method 2: Manual Deployment + +The OpenMLDB cluster version requires the deployment of ZooKeeper, NameServer, TabletServer, and TaskManager. ZooKeeper serves for service discovery and metadata preservation. NameServer manages TabletServer for achieving high availability and failover. TabletServer stores data and synchronizes master-slave data. APIServer is an optional component; if interaction with OpenMLDB via HTTP is desired, this module must be deployed. TaskManager oversees offline jobs. -**Notice:** It is best to deploy different components in different directories for easy upgrades individually. If multiple tablets are deployed on the same machine, they also need to be deployed in different directories. +**Note 1**: When deploying multiple components on the same machine, it's crucial to place them in distinct directories for streamlined management. This is particularly important while deploying TabletServer, as it's essential to avoid directory reuse to prevent conflicts between data and log files. -### Deploy Zookeeper +**Note 2**: In the following sections, we use normal mode to initiate components. If you prefer to start components in daemon mode, use `bash bin/start.sh start mon`. -The required zookeeper version is >= 3.4 and <= 3.6, it is recommended to deploy version 3.4.14. You can skip this step if there is an available zookeeper cluster. +(zookeeper_addr)= +### Deploy ZooKeeper +ZooKeeper requires a version between 3.4 and 3.6. We recommend version 3.4.14 for deployment. If existing ZooKeeper clusters are available, this step can be skipped. If you're aiming to deploy a ZooKeeper cluster, refer to [here](https://zookeeper.apache.org/doc/r3.4.14/zookeeperStarted.html#sc_RunningReplicatedZooKeeper). We only demonstrate standalone ZooKeeper deployment. -#### 1. Download the Zookeeper Installation Package +**1. Download the ZooKeeper installation package** ``` wget https://archive.apache.org/dist/zookeeper/zookeeper-3.4.14/zookeeper-3.4.14.tar.gz @@ -289,8 +313,7 @@ cd zookeeper-3.4.14 cp conf/zoo_sample.cfg conf/zoo.cfg ``` -#### 2. Modify the Configuration File - +**2. Modify the configuration file** Open the file `conf/zoo.cfg` and modify `dataDir` and `clientPort`. ``` @@ -298,17 +321,44 @@ dataDir=./data clientPort=7181 ``` -#### 3. Start Zookeeper +**3. Start ZooKeeper** ``` bash bin/zkServer.sh start ``` -Deploy the Zookeeper cluster [refer to here](https://zookeeper.apache.org/doc/r3.4.14/zookeeperStarted.html#sc_RunningReplicatedZooKeeper). +The successful startup will be shown in the following figure, with a prompt of `STARTED`. + +![zk started](images/zk_started.png) + +You can also see the ZooKeeper process running through `ps f|grep zoo.cfg`. + +![zk ps](images/zk_ps.png) + +```{attention} +If ZooKeeper process fails to start, please check ZooKeeper.out in the current directory. +``` + +**4. ZooKeeper Service Address and Connection Test** +You'll need to configure the ZooKeeper service address, while connecting TabletServer, NameServer, and TaskManager to ZooKeeper. To enable cross-host access to the ZooKeeper service, you'll need a public IP (assuming `172.27.128.33` here; remember to input your actual ZooKeeper deployment machine IP). The ZooKeeper service address is inferred from the `clientPort` value entered in the second step, which in this case is `172.27.128.33:7181`. + +To test the connection with ZooKeeper, use `zookeeper-3.4.14/bin/zkCli.sh`, and ensure you run it from within the `zookeeper-3.4.14` directory. + +``` +bash bin/zkCli.sh -server 172.27.128.33:7181 +``` + +You can enter the zk client program, as shown in the following figure, with a prompt of `CONNECTED`. + +![zk cli](images/zk_cli.png) + +Enter `quit` or `Ctrl+C` to exit the zk client. -### Deploy Tablet +### Deploy TabletServer -#### 1. Download the OpenMLDB Deployment Package +Note that at least two TabletServer need to be deployed, otherwise errors may occur. + +**1. Download the OpenMLDB deployment package** ``` wget https://github.com/4paradigm/OpenMLDB/releases/download/v0.8.2/openmldb-0.8.2-linux.tar.gz @@ -317,98 +367,181 @@ mv openmldb-0.8.2-linux openmldb-tablet-0.8.2 cd openmldb-tablet-0.8.2 ``` -#### 2. Modify the Configuration File: conf/tablet.flags +**2. Modify the configuration file `conf/tablet.flags`** + ```bash -# can update the config based on the template provided +# Modifications can be made based on the sample configuration file cp conf/tablet.flags.template conf/tablet.flags ``` -* Modify `endpoint`. The endpoint is the deployment machine ip/domain name and port number separated by colons. -* Modify `zk_cluster` to the already started zk cluster address. -* If you share zk with other OpenMLDB, you need to modify `zk_root_path`. +```{attention} +Please note that the configuration file to modify is `conf/tablet.flags`, not any other configuration file. Even when starting multiple TabletServers (with independent, non-shareable directories for each), the same configuration file should be modified. +``` + +Here are the steps for modification: + +- Modify the `endpoint`: The `endpoint` comprises an IP address/domain name and port number, separated by a colon (Note: endpoint cannot be set to 0.0.0.0 or 127.0.0.1; it must be a public IP). +- Update `zk_cluster` with the address of the ZooKeeper service that has already been initiated (refer to [Deploy ZooKeeper - 4. ZooKeeper Service Address and Connection Test](zookeeper_addr)). If the ZooKeeper service is in a cluster, multiple addresses can be separated by commas. For instance: `172.27.128.33:7181,172.27.128.32:7181,172.27.128.31:7181`. +- Modify `zk_root_path` with the appropriate value; in this example, `/openmldb_cluster` is used. It's crucial to note that **components within the same cluster share the same `zk_root_path`**. In this deployment, all component `zk_root_path` values are set to `/openmldb_cluster`. ``` --endpoint=172.27.128.33:9527 --role=tablet -# If tablet run as cluster mode zk_cluster and zk_root_path should be set: ---zk_cluster=172.27.128.33:7181,172.27.128.32:7181,172.27.128.31:7181 +# if tablet run as cluster mode zk_cluster and zk_root_path should be set +--zk_cluster=172.27.128.33:7181 --zk_root_path=/openmldb_cluster ``` -**Notice:** +**Note:** -* The endpoint cannot use 0.0.0.0 and 127.0.0.1. -* If the domain name is used here, all the machines with OpenMLDB clients must be configured with the corresponding host. Otherwise, it will not be accessible. -* The configuration of `zk_cluster` and `zk_root_path` is consistent with that of Nameserver. +* If the endpoint configuration employs a domain name, all machines utilizing the OpenMLDB client must have the corresponding host configured. Otherwise, it will not be accessible. -#### 3. Start the Service +**3. Start the service** ``` bash bin/start.sh start tablet ``` -Repeat the above steps to deploy multiple tablets. +After startup, there should be a `success` prompt, as shown below. + +``` +Starting tablet ... +Start tablet success +``` -**Notice:** +View the process status through the `ps f | grep tablet`. +![tablet ps](images/tablet_ps.png) -* After the service is started, the tablet.pid file will be generated in the bin directory, and the process number at startup will be saved in it. If the pid inside the file is running, the startup will fail. -* Cluster version needs to deploy at least 2 tablets. -* If you need to deploy multiple tablets, deploy all the tablets before deploying the Nameserver. +Through `curl http://:/status` You can also test whether TabletServer is running normally. -### Deploy Nameserver +```{attention} +If you encounter issues like TabletServer failing to start or the process exiting after running for a while, you can examine the logs/tablet.WARNING file within the TabletServer's startup directory. For more detailed information, refer to the logs/tablet.INFO file. If the IP address is already in use, modify the TabletServer's endpoint port and restart it. Prior to starting, ensure that there are no `db, logs, or recycle` directories within the startup directory. You can use the command `rm -rf db logs` recycle to delete these directories, preventing legacy files and logs from interfering with current operations. If you are unable to resolve the issue, reach out to the community and provide the logs. +``` -#### 1. Download the OpenMLDB Deployment Package +**4. Repeat the above steps to deploy multiple TabletServers** +```{important} +For clustered versions, the number of TabletServers must be 2 or more. If there's only 1 TabletServer, starting the NameServer will fail. The NameServer logs (logs/nameserver.WARNING) will contain logs indicating "is less than system table replica num." ``` + +To start the next TabletServer on a different machine, simply repeat the aforementioned steps on that machine. If starting the next TabletServer on the same machine, ensure it's in a different directory, and do not reuse a directory where the TabletServer is already running. + +For instance, you can decompress the package again (avoid using a directory where TabletServer is already running, as files generated after startup may be affected), and name the directory `openmldb-tablet-0.8.2-2`. + +``` +tar -zxvf openmldb-0.8.2-linux.tar.gz +mv openmldb-0.8.2-linux openmldb-tablet-0.8.2-2 +cd openmldb-tablet-0.8.2-2 +``` + +Modify the configuration again and start the TabletServer. Note that if all TabletServers are on the same machine, use different port numbers to avoid "Fail to listen" error in the log (`logs/tablet.WARNING`). + +**Note:** + +- After the service starts, a `tablet.pid` file will be generated in the `bin` directory, storing the process number during startup. If the PID in this file is already running, starting the service will fail. + +### Deploy NameServer + +```{attention} +Please ensure that all TabletServer have been successfully started before deploying NameServer. The deployment order cannot be changed. +``` + +**1. Download the OpenMLDB deployment package** + +```` wget https://github.com/4paradigm/OpenMLDB/releases/download/v0.8.2/openmldb-0.8.2-linux.tar.gz tar -zxvf openmldb-0.8.2-linux.tar.gz mv openmldb-0.8.2-linux openmldb-ns-0.8.2 cd openmldb-ns-0.8.2 -``` +```` + +**2. Modify the configuration file conf/nameserver.flags** -#### 2. Modify the Configuration File: conf/nameserver.flags ```bash -# can update the config based on the template provided +# Modifications can be made based on the sample configuration file cp conf/nameserver.flags.template conf/nameserver.flags ``` -* Modify `endpoint`. The endpoint is the deployment machine ip/domain name and port number separated by colons. -* Modify `zk_cluster` to the address of the zk cluster that has been started. IP is the machine address where zk is located, and port is the port number configured by clientPort in the zk configuration file. If zk is in cluster mode, separate it with commas, and the format is ip1:port1,ip2:port2, ip3:port3. -* If you share zk with other OpenMLDB, you need to modify `zk_root_path`. +```{attention} +Please note that the configuration file is `conf/nameserver.flags` and not any other configuration file. When starting multiple NameServers (each in an independent directory, not shareable), you should modify the configuration file accordingly. +``` + +* Modify the `endpoint`. The `endpoint` consists of a colon-separated deployment machine IP/domain name and port number (endpoints cannot use 0.0.0.0 and 127.0.0.1, and must be a public IP). +* Modify `zk_cluster` to point to the address of the ZooKeeper service that has already been started (see [Deploy ZooKeeper - 4. ZooKeeper Service Address and Connection Test](zookeeper_addr)). If the ZooKeeper service is a cluster, separate the addresses with commas, for example, `172.27.128.33:7181,172.27.128.32:7181,172.27.128.31:7181`. +* Modify `zk_root_path`. In this example, `/openmldb_cluster` is used. Note that **components under the same cluster share the same `zk_root_path`**. So in this deployment, the `zk_root_path` for each component's configuration is `/openmldb_cluster`. ``` --endpoint=172.27.128.31:6527 ---zk_cluster=172.27.128.33:7181,172.27.128.32:7181,172.27.128.31:7181 +--zk_cluster=172.27.128.33:7181 --zk_root_path=/openmldb_cluster ``` -**Notice:** The endpoint cannot use 0.0.0.0 and 127.0.0.1. - -#### 3. Start the Service +**3. Start the service** ``` bash bin/start.sh start nameserver ``` -Repeat the above steps to deploy multiple nameservers. +After startup, there should be a `success` prompt, as shown below. + +``` +Starting nameserver ... +Start nameserver success +``` + +You can also use `curl http://:/status` to check if NameServer is running properly. + +**4. Repeat the above steps to deploy multiple NameServer** + +You can have only one NameServer, but if you need high availability, you can deploy multiple NameServers. + +To start the next NameServer on another machine, simply repeat the above steps on that machine. If starting the next NameServer on the same machine, ensure it's in a different directory and do not reuse the directory where NameServer has already been started. + +For instance, you can decompress the package again (avoid using the directory where NameServer is already running, as files generated after startup may be affected) and name the directory `openmldb-ns-0.8.2-2`. + +``` +tar -zxvf openmldb-0.8.2-linux.tar.gz +mv openmldb-0.8.2-linux openmldb-ns-0.8.2-2 +cd openmldb-ns-0.8.2-2 +``` + +Then modify the configuration and start. + +**Note:** + +- After the service starts, a `nameserver.pid` file will be generated in the `bin` directory, storing the process number at startup. If the PID in this file is already running, starting the service will fail. +- Please deploy all TabletServers before deploying the NameServer. -#### 4. Verify the Running Status of the Service +**5. Check if the service is started** + +```{attention} +At least one NameServer must be deployed to query the service components that have been started by the **current** cluster using the following method. +``` ```bash -$ ./bin/openmldb --zk_cluster=172.27.128.31:7181,172.27.128.32:7181,172.27.128.33:7181 --zk_root_path=/openmldb_cluster --role=ns_client -> shown - endpoint role ------------------------------ - 172.27.128.31:6527 leader +echo "show components;" | ./bin/openmldb --zk_cluster=172.27.128.33:7181 --zk_root_path=/openmldb_cluster --role=sql_client +``` + +The result is **similar** to the figure below, where you can see all the TabletServer and NameServer that you have already deployed. + +``` + ------------------- ------------ --------------- -------- --------- + Endpoint Role Connect_time Status Ns_role + ------------------- ------------ --------------- -------- --------- + 172.27.128.33:9527 tablet 1665568158749 online NULL + 172.27.128.33:9528 tablet 1665568158741 online NULL + 172.27.128.31:6527 nameserver 1665568159782 online master + ------------------- ------------ --------------- -------- --------- ``` ### Deploy APIServer -APIServer is responsible for receiving http requests, forwarding them to OpenMLDB and returning results. It is stateless and is not a must-deploy component of OpenMLDB. -Before running, make sure that the OpenMLDB cluster has been started, otherwise APIServer will fail to initialize and exit the process. +APIServer is responsible for receiving HTTP requests, forwarding them to the OpenMLDB cluster, and returning the results. It operates in a stateless manner. APIServer is an optional component for OpenMLDB deployment. If you don't require the HTTP interface, you can skip this step and proceed to the next step [Deploy TaskManager](deploy_taskmanager). + +Before running APIServer, ensure that the TabletServer and NameServer processes of the OpenMLDB cluster have been started (TaskManager doesn't affect the startup of APIServer). Failure to do so will result in APIServer failing to initialize and exiting the process. -#### 1. Download the OpenMLDB Deployment Package +**1. Download the OpenMLDB deployment package** ``` wget https://github.com/4paradigm/OpenMLDB/releases/download/v0.8.2/openmldb-0.8.2-linux.tar.gz @@ -417,70 +550,97 @@ mv openmldb-0.8.2-linux openmldb-apiserver-0.8.2 cd openmldb-apiserver-0.8.2 ``` -#### 2. Modify the Configuration File: conf/apiserver.flags +**2. Modify the configuration file conf/apiserver.flags** + ```bash -# can update the config based on the template provided +# Modifications can be made based on the sample configuration file cp conf/apiserver.flags.template conf/apiserver.flags ``` -* Modify `endpoint`. The endpoint is the deployment machine ip/domain name and port number separated by colons. -* Modify ``zk_cluster`` to the zk cluster address of OpenMLDB to be forwarded to. +* Modify the `endpoint`. The `endpoint` consists of a colon-separated deployment machine IP/domain name and port number (endpoints cannot use 0.0.0.0 and 127.0.0.1, and must be a public IP). +* Modify `zk_cluster` to point to the address of the ZooKeeper service that has already been started (see [Deploy ZooKeeper - 4. ZooKeeper Service Address and Connection Test](zookeeper_addr)). If the ZooKeeper service is a cluster, separate the addresses with commas, for example, `172.27.128.33:7181,172.27.128.32:7181,172.27.128.31:7181`. +* Modify `zk_root_path`. In this example, `/openmldb_cluster` is used. Note that **components under the same cluster share the same `zk_root_path`**. So in this deployment, the `zk_root_path` for each component's configuration is `/openmldb_cluster`. ``` --endpoint=172.27.128.33:8080 ---role=apiserver ---zk_cluster=172.27.128.33:7181,172.27.128.32:7181,172.27.128.31:7181 +--zk_cluster=172.27.128.33:7181 --zk_root_path=/openmldb_cluster ---openmldb_log_dir=./logs ``` -**Notice:** +**Note**: -* The endpoint cannot use 0.0.0.0 and 127.0.0.1. You can also choose not to set `--endpoint`, and only configure the port number `--port`. -* You can also configure the number of threads of APIServer, `--thread_pool_size`, the default is 16. +- If the concurrency of HTTP requests is high, you can increase the number of `--thread_pool_size` on the APIServer, default is 16, and restart for changes to take effect. -#### 3. Start the Service +**3. Start the service** ``` bash bin/start.sh start apiserver ``` -**Notice:** If the program crashes when starting the nameserver/tablet/apiserver using the OpenMLDB release package, it is very likely that the instruction set is incompatible, and you need to compile OpenMLDB through the source code. For source code compilation, please refer to [here](./compile.md), you need to use method 3 to compile the complete source code. +After startup, there should be a `success` prompt, as shown below. + +``` +Starting apiserver ... +Start apiserver success +``` + +```{attention} +APIServer is a non essential component, so it will not appear in `show components;`. +``` + +You can use `curl http://:/status` to check if APIServer is running normally. However, it is recommended to test its normal operation by executing SQL commands +: +``` +curl http://:/dbs/foo -X POST -d'{"mode":"online","sql":"show components;"}' +``` + +The results should include information about all TabletServer and NameServer that have been started. + +(deploy_taskmanager)= ### Deploy TaskManager -TaskManager can be deployed in single server. You can deploy multiple instances for high availability. If the master server of TaskManagers fails, the slaves will replace the master for failover and the client will reconnect automatically. +You can have only one TaskManager, but if you require high availability, you can deploy multiple TaskManagers, taking care to avoid IP and port conflicts. If the TaskManager master node experiences a failure, a slave node will automatically recover and replace the master node. Clients can continue accessing the TaskManager service without any modifications. -#### 1. Download the OpenMLDB Spark Distribution that is Optimized for Feature Engineering +**1. Download the OpenMLDB deployment package and Spark distribution for feature engineering optimization** -``` +Spark distribution: + +```shell wget https://github.com/4paradigm/spark/releases/download/v3.2.1-openmldb0.8.2/spark-3.2.1-bin-openmldbspark.tgz -tar -zxvf spark-3.2.1-bin-openmldbspark.tgz +# Image address (China):http://43.138.115.238/download/v0.8.2/spark-3.2.1-bin-openmldbspark.tgz +tar -zxvf spark-3.2.1-bin-openmldbspark.tgz +export SPARK_HOME=`pwd`/spark-3.2.1-bin-openmldbspark/ +``` + +OpenMLDB deployment package: +``` wget https://github.com/4paradigm/OpenMLDB/releases/download/v0.8.2/openmldb-0.8.2-linux.tar.gz tar -zxvf openmldb-0.8.2-linux.tar.gz mv openmldb-0.8.2-linux openmldb-taskmanager-0.8.2 cd openmldb-taskmanager-0.8.2 ``` -#### 2. Modify the Configuration File conf/taskmanager.properties +**2. Modify the configuration file conf/taskmanager.properties** + ```bash -# can update the config based on the template provided +# Modifications can be made based on the sample configuration file cp conf/taskmanager.properties.template conf/taskmanager.properties ``` -* Modify `server.host`. The host is the ip/domain name of the deployment machine. -* Modify `server.port`. The port is the port number of the deployment machine. -* Modify `zk_cluster` to the address of the zk cluster that has been started. IP is the address of the machine where zk is located, and port is the port number configured by clientPort in the zk configuration file. If zk is in cluster mode, it is separated by commas, and the format is ip1:port1,ip2:port2,ip3:port3. -* If you share zk with other OpenMLDB, you need to modify zookeeper.root_path. -* Modify `batchjob.jar.path` to the BatchJob Jar file path. If it is set to empty, it will search in the upper-level lib directory. If you use Yarn mode, you need to modify it to the corresponding HDFS path. -* Modify `offline.data.prefix` to the offline table storage path. If Yarn mode is used, it needs to be modified to the corresponding HDFS path. -* Modify `spark.master` to run in offline task mode, currently supports local and yarn modes. -* Modify `spark.home` to the Spark environment path. If it is not configured or the configuration is empty, the configuration of the SPARK_HOME environment variable will be used. It needs to be set as the directory where the spark-optimized package is extracted in the first step, and the path is an absolute path. +* Modify `server.host`: Set it to the IP address or domain name of the deployment machine. +* Modify `server.port`: Set it to the port number of the deployment machine. +* Modify `zk_cluster`: Set it to the address of the ZooKeeper cluster that has been started. The IP should point to the machine where ZooKeeper is located, and the port should match the `clientPort` configured in the ZooKeeper configuration file. If ZooKeeper is in cluster mode, separate addresses using commas in the format `ip1:port1,ip2:port2,ip3:port3`. +* If sharing ZooKeeper with other OpenMLDB instances, modify `zookeeper.root_path`. +* Modify `batchjob.jar.path`: Set it to the BatchJob Jar file path. If left empty, the system will search in the upper-level lib directory. In Yarn mode, modify it to the corresponding HDFS path. +* Modify `offline.data.prefix`: Set it to the storage path for offline tables. In Yarn mode, modify it to the corresponding HDFS path. +* Modify `spark.master`: Set it according to the desired mode. Currently supports local and yarn modes for running offline tasks. +* Modify `spark.home`: Set it to the Spark environment path. If not configured, the `SPARK_HOME` environment variable will be used. It should be the directory where the spark-optimized package was extracted in the first step, and it must be an absolute path. ``` -server.host=0.0.0.0 +server.host=172.27.128.33 server.port=9902 -zookeeper.cluster=172.27.128.31:7181,172.27.128.32:7181,172.27.128.33:7181 +zookeeper.cluster=172.27.128.33:7181 zookeeper.root_path=/openmldb_cluster batchjob.jar.path= offline.data.prefix=file:///tmp/openmldb_offline_storage/ @@ -488,18 +648,68 @@ spark.master=local spark.home= ``` -#### 3. Start the Service +For more instructions on Spark related configurations, please refer to the [Spark Config Detail](./conf.md#spark-config-detail). -```bash +```{attention} +For distributed deployment clusters, avoid using local files from clients as source data imports. It is strongly recommended to use HDFS paths for this purpose. + +When spark.master=yarn, you must use HDFS paths. +When spark.master=local, if you must use a local file, you can copy the file to the host where TaskManager runs and provide the absolute path on the TaskManager host. + +For cases involving large amounts of offline data, it's also advisable to use HDFS for offline.data.prefix instead of local files. +``` + +**3. Start the service** + +``` bash bin/start.sh start taskmanager ``` -#### 4. Verify the Running Status of the Service +`ps f|grep taskmanager` should run normally, you can query the status of taskmanager process in `curl http://:/status `. + +```{note} +TaskManager logs include TaskManager process logs and job logs for each offline command. These logs are located in the /taskmanager/bin/logs path: +- taskmanager.log and taskmanager.out are the TaskManager process logs. Review these logs if the TaskManager process exits unexpectedly. +- job_x_error.log contains the log of each single job, while job_x.log contains the print log of a single job (results of asynchronous selects are printed here). In case of offline task failures, for example, for job 10, you can retrieve log information using command SHOW `JOBLOG 10`;. If your version does not support JOBLOG, locate the corresponding log job on the machine where the **TaskManager** is located. These logs are named job_10.log and job_10_error.log. +``` + +**4. Check if service is started** ```bash -$ ./bin/openmldb --zk_cluster=172.27.128.31:7181,172.27.128.32:7181,172.27.128.33:7181 --zk_root_path=/openmldb_cluster --role=sql_client -> show jobs; ----- ---------- ------- ------------ ---------- ------- ---- --------- ---------------- ------- - id job_type state start_time end_time parameter cluster application_id error ----- ---------- ------- ------------ ---------- ------- ---- --------- ---------------- ------- +$ ./bin/openmldb --zk_cluster=172.27.128.33:7181 --zk_root_path=/openmldb_cluster --role=sql_client +> show components; +``` + +The results should be similar to the following table, including all cluster components (except for APIServer). + +``` + ------------------- ------------ --------------- -------- --------- + Endpoint Role Connect_time Status Ns_role + ------------------- ------------ --------------- -------- --------- + 172.27.128.33:9527 tablet 1665568158749 online NULL + 172.27.128.33:9528 tablet 1665568158741 online NULL + 172.27.128.31:6527 nameserver 1665568159782 online master + 172.27.128.33:9902 taskmanager 1665649276766 online NULL + ------------------- ------------ --------------- -------- --------- +``` + +To test the normal functioning of the cluster in the SQL client, you can execute the following SQL commands to read and write simple tables (online only, for simplicity). + +``` +create database simple_test; +use simple_test; +create table t1(c1 int, c2 string); +set @@execute_mode='online'; +Insert into t1 values (1, 'a'),(2,'b'); +select * from t1; ``` + +### Deployed in offline synchronization tool (optional) + +DataCollector in the offline synchronization tool needs to be deployed on the same machine as the TabletServer. Therefore, if offline synchronization is required, DataCollector can be deployed in all TabletServer deployment directories. + +SyncTool requires a Java runtime environment and doesn't have any additional requirements. It is recommended to deploy it separately on a single machine. + +SyncTool Helper, a synchronization task management tool for SyncTool, can be found in `tools/synctool_helper.py`. It requires a Python3 runtime environment, no additional requirements, and can be used remotely. However, viewing the debugging information requires using SyncTool Helper on the machine where SyncTool is located. + +For detailed deployment instructions, refer to the [Offline Synchronization Tool](../tutorial/online_offline_sync.md). Pay close attention to the version conditions and functional boundaries of the offline synchronization tool. From c888eab1bea8d6d1137aa8e7acf6da37898b0433 Mon Sep 17 00:00:00 2001 From: tobe Date: Thu, 14 Sep 2023 14:09:29 +0800 Subject: [PATCH 36/63] feat: support load data for hive with sql option (#3380) --- .../integration/offline_data_sources/hive.md | 10 +++- .../openmldb/batch/nodes/LoadDataPlan.scala | 5 +- .../openmldb/batch/utils/HybridseUtil.scala | 58 ++++++++++++++----- 3 files changed, 55 insertions(+), 18 deletions(-) diff --git a/docs/zh/integration/offline_data_sources/hive.md b/docs/zh/integration/offline_data_sources/hive.md index ec317971990..286f14d9684 100644 --- a/docs/zh/integration/offline_data_sources/hive.md +++ b/docs/zh/integration/offline_data_sources/hive.md @@ -102,7 +102,7 @@ CREATE TABLE db1.t1 LIKE HIVE 'hive://hive_db.t1'; - 离线和在线引擎均可以导入 Hive 数据源 - Hive 导入支持软连接,可以减少硬拷贝并且保证 OpenMLDB 随时读取到 Hive 的最新数据。启用软链接方式进行数据导入:使用参数 `deep_copy=false` -- `OPTIONS` 参数仅有 `deep_copy` 和 `mode` 有效 +- `OPTIONS` 参数仅有 `deep_copy` 、`mode` 和 `sql` 有效 举例: @@ -110,6 +110,14 @@ CREATE TABLE db1.t1 LIKE HIVE 'hive://hive_db.t1'; LOAD DATA INFILE 'hive://db1.t1' INTO TABLE t1 OPTIONS(deep_copy=false); ``` +加载数据还支持使用 SQL 语句筛选 Hive 数据表特定数据,注意 SQL 必须符合 SparkSQL 语法,数据表为注册后的表名,不带 `hive://` 前缀。 + +举例: + +```sql +LOAD DATA INFILE 'hive://db1.t1' INTO TABLE db1.t1 OPTIONS(deep_copy=true, sql='SELECT * FROM db1.t1 where key=\"foo\"') +``` + ## 导出 OpenMLDB 数据到 Hive 对于 Hive 数据源的导出是通过 API [`SELECT INTO`](../../openmldb_sql/dql/SELECT_INTO_STATEMENT.md) 进行支持,通过使用特定的 URI 接口 `hive://[db].table` 的格式进行导出到 Hive 数仓。注意: diff --git a/java/openmldb-batch/src/main/scala/com/_4paradigm/openmldb/batch/nodes/LoadDataPlan.scala b/java/openmldb-batch/src/main/scala/com/_4paradigm/openmldb/batch/nodes/LoadDataPlan.scala index 36398d199cf..a04b46ab650 100644 --- a/java/openmldb-batch/src/main/scala/com/_4paradigm/openmldb/batch/nodes/LoadDataPlan.scala +++ b/java/openmldb-batch/src/main/scala/com/_4paradigm/openmldb/batch/nodes/LoadDataPlan.scala @@ -47,9 +47,12 @@ object LoadDataPlan { logger.info("table info: {}", info) require(info != null && info.getName.nonEmpty, s"table $db.$table info is not existed(no table name): $info") + val loadDataSql = extra.get("sql").get + // we read input file even in soft copy, // cause we want to check if "the input file schema == openmldb table schema" - val df = HybridseUtil.autoLoad(ctx.getOpenmldbSession, inputFile, format, options, info.getColumnDescList) + val df = HybridseUtil.autoLoad(ctx.getOpenmldbSession, inputFile, format, options, info.getColumnDescList, + loadDataSql) // write logger.info("write data to storage {}, writer[mode {}], is deep? {}", storage, mode, deepCopy.toString) diff --git a/java/openmldb-batch/src/main/scala/com/_4paradigm/openmldb/batch/utils/HybridseUtil.scala b/java/openmldb-batch/src/main/scala/com/_4paradigm/openmldb/batch/utils/HybridseUtil.scala index 4e0edd9b8a2..bdb9f30a727 100644 --- a/java/openmldb-batch/src/main/scala/com/_4paradigm/openmldb/batch/utils/HybridseUtil.scala +++ b/java/openmldb-batch/src/main/scala/com/_4paradigm/openmldb/batch/utils/HybridseUtil.scala @@ -233,6 +233,7 @@ object HybridseUtil { updateOptionsMap(options, getOptionFromNode(node, "null_value"), "nullValue", getStr) updateOptionsMap(options, getOptionFromNode(node, "quote"), "quote", getStr) } + // load data: write mode(load data may write to offline storage or online storage, needs mode too) // select into: write mode val modeStr = parseOption(getOptionFromNode(node, "mode"), "error_if_exists", getStringOrDefault).toLowerCase @@ -250,8 +251,9 @@ object HybridseUtil { // only for select into, "" means N/A extraOptions += ("coalesce" -> parseOption(getOptionFromNode(node, "coalesce"), "0", getIntOrDefault)) - + extraOptions += ("sql" -> parseOption(getOptionFromNode(node, "sql"), "", getStringOrDefault)) extraOptions += ("writer_type") -> parseOption(getOptionFromNode(node, "writer_type"), "single", getStringOrDefault) + (format, options.toMap, mode, extraOptions.toMap) } @@ -314,7 +316,12 @@ object HybridseUtil { def autoLoad(openmldbSession: OpenmldbSession, file: String, format: String, options: Map[String, String], columns: util.List[Common.ColumnDesc]): DataFrame = { - autoLoad(openmldbSession, file, List.empty[String], format, options, columns) + autoLoad(openmldbSession, file, List.empty[String], format, options, columns, "") + } + + def autoLoad(openmldbSession: OpenmldbSession, file: String, format: String, options: Map[String, String], + columns: util.List[Common.ColumnDesc], loadDataSql: String): DataFrame = { + autoLoad(openmldbSession, file, List.empty[String], format, options, columns, loadDataSql) } // Load df from file **and** symbol paths, they should in the same format and options. @@ -327,7 +334,8 @@ object HybridseUtil { // We use OpenmldbSession for running sparksql in hiveLoad. If in 4pd Spark distribution, SparkSession.sql // will do openmldbSql first, and if DISABLE_OPENMLDB_FALLBACK, we can't use sparksql. def autoLoad(openmldbSession: OpenmldbSession, file: String, symbolPaths: List[String], format: String, - options: Map[String, String], columns: util.List[Common.ColumnDesc]): DataFrame = { + options: Map[String, String], columns: util.List[Common.ColumnDesc], loadDataSql: String = "") + : DataFrame = { val fmt = format.toLowerCase if (fmt.equals("hive")) { logger.info(s"load data from hive table $file & $symbolPaths") @@ -335,16 +343,16 @@ object HybridseUtil { var outputDf: DataFrame = null symbolPaths.zipWithIndex.foreach { case (path, index) => if (index == 0) { - outputDf = HybridseUtil.hiveLoad(openmldbSession, path, columns); + outputDf = HybridseUtil.hiveLoad(openmldbSession, path, columns, loadDataSql) } else { - outputDf = outputDf.union(HybridseUtil.hiveLoad(openmldbSession, path, columns)) + outputDf = outputDf.union(HybridseUtil.hiveLoad(openmldbSession, path, columns, loadDataSql)) } } outputDf } else { - var outputDf = HybridseUtil.hiveLoad(openmldbSession, file, columns) + var outputDf = HybridseUtil.hiveLoad(openmldbSession, file, columns, loadDataSql) for (path: String <- symbolPaths) { - outputDf = outputDf.union(HybridseUtil.hiveLoad(openmldbSession, path, columns)) + outputDf = outputDf.union(HybridseUtil.hiveLoad(openmldbSession, path, columns, loadDataSql)) } outputDf } @@ -355,16 +363,18 @@ object HybridseUtil { var outputDf: DataFrame = null symbolPaths.zipWithIndex.foreach { case (path, index) => if (index == 0) { - outputDf = HybridseUtil.autoFileLoad(openmldbSession, path, fmt, options, columns); + outputDf = HybridseUtil.autoFileLoad(openmldbSession, path, fmt, options, columns, loadDataSql) } else { - outputDf = outputDf.union(HybridseUtil.autoFileLoad(openmldbSession, path, fmt, options, columns)) + outputDf = outputDf.union(HybridseUtil.autoFileLoad(openmldbSession, path, fmt, options, columns, + loadDataSql)) } } outputDf } else { - var outputDf = HybridseUtil.autoFileLoad(openmldbSession, file, fmt, options, columns) + var outputDf = HybridseUtil.autoFileLoad(openmldbSession, file, fmt, options, columns, loadDataSql) for (path: String <- symbolPaths) { - outputDf = outputDf.union(HybridseUtil.autoFileLoad(openmldbSession, path, fmt, options, columns)) + outputDf = outputDf.union(HybridseUtil.autoFileLoad(openmldbSession, path, fmt, options, columns, + loadDataSql)) } outputDf } @@ -376,7 +386,7 @@ object HybridseUtil { // 2. spark read may change the df schema to all nullable // So we should fix it. private def autoFileLoad(openmldbSession: OpenmldbSession, file: String, format: String, - options: Map[String, String], columns: util.List[Common.ColumnDesc]): DataFrame = { + options: Map[String, String], columns: util.List[Common.ColumnDesc], loadDataSql: String): DataFrame = { require(format.equals("csv") || format.equals("parquet")) val reader = openmldbSession.getSparkSession.read.options(options) @@ -384,7 +394,13 @@ object HybridseUtil { var df = if (format.equals("parquet")) { // When reading Parquet files, all columns are automatically converted to be nullable for compatibility reasons. // ref https://spark.apache.org/docs/3.2.1/sql-data-sources-parquet.html - val df = reader.format(format).load(file) + val df = if (loadDataSql != null && loadDataSql.nonEmpty) { + reader.format(format).load(file).createOrReplaceTempView("file") + openmldbSession.sparksql(loadDataSql) + } else { + reader.format(format).load(file) + } + require(checkSchemaIgnoreNullable(df.schema, oriSchema), s"schema mismatch(ignore nullable), loaded ${df.schema}!= table $oriSchema, check $file") // reset nullable property @@ -404,6 +420,11 @@ object HybridseUtil { } } + if (loadDataSql != null && loadDataSql.nonEmpty) { + df.createOrReplaceTempView("file") + df = openmldbSession.sparksql(loadDataSql) + } + if (logger.isDebugEnabled()) { logger.debug(s"read dataframe schema: ${df.schema}, count: ${df.count()}") df.show(10) @@ -430,14 +451,19 @@ object HybridseUtil { path.substring(tableStartPos) } - private def hiveLoad(openmldbSession: OpenmldbSession, file: String, columns: util.List[Common.ColumnDesc]): - DataFrame = { + private def hiveLoad(openmldbSession: OpenmldbSession, file: String, columns: util.List[Common.ColumnDesc], + loadDataSql: String = ""): DataFrame = { if (logger.isDebugEnabled()) { logger.debug("session catalog {}", openmldbSession.getSparkSession.sessionState.catalog) openmldbSession.sparksql("show tables").show() } // use sparksql to read hive, no need to try openmldbsql and then fallback to sparksql - val df = openmldbSession.sparksql(s"SELECT * FROM ${hiveDest(file)}") + val df = if (loadDataSql != null && loadDataSql.nonEmpty) { + logger.debug("Try to execute custom SQL for hive: " + loadDataSql) + openmldbSession.sparksql(loadDataSql) + } else { + openmldbSession.sparksql(s"SELECT * FROM ${hiveDest(file)}") + } if (logger.isDebugEnabled()) { logger.debug(s"read dataframe schema: ${df.schema}, count: ${df.count()}") df.show(10) From 07f686274a8b3174bdfb7e300c2e0b5429cfb34e Mon Sep 17 00:00:00 2001 From: aceforeverd Date: Thu, 14 Sep 2023 16:45:00 +0800 Subject: [PATCH 37/63] fix(#3489): rm unnecessary logs (#3495) --- hybridse/src/vm/generator.cc | 3 --- hybridse/src/vm/runner.cc | 3 --- 2 files changed, 6 deletions(-) diff --git a/hybridse/src/vm/generator.cc b/hybridse/src/vm/generator.cc index a0fe03e3a31..2b437ca2602 100644 --- a/hybridse/src/vm/generator.cc +++ b/hybridse/src/vm/generator.cc @@ -282,17 +282,14 @@ Row JoinGenerator::RowLastJoinTable(const Row& left_row, table = right_sort_gen_.Sort(table, true); } if (!table) { - LOG(WARNING) << "Last Join right table is empty"; return Row(left_slices_, left_row, right_slices_, Row()); } auto right_iter = table->GetIterator(); if (!right_iter) { - LOG(WARNING) << "Last Join right table is empty"; return Row(left_slices_, left_row, right_slices_, Row()); } right_iter->SeekToFirst(); if (!right_iter->Valid()) { - LOG(WARNING) << "Last Join right table is empty"; return Row(left_slices_, left_row, right_slices_, Row()); } diff --git a/hybridse/src/vm/runner.cc b/hybridse/src/vm/runner.cc index 3e1ebc72805..28525b8face 100644 --- a/hybridse/src/vm/runner.cc +++ b/hybridse/src/vm/runner.cc @@ -1673,18 +1673,15 @@ const Row Runner::RowLastJoinTable(size_t left_slices, const Row& left_row, ConditionGenerator& cond_gen) { right_table = right_sort.Sort(right_table, true); if (!right_table) { - LOG(WARNING) << "Last Join right table is empty"; return Row(left_slices, left_row, right_slices, Row()); } auto right_iter = right_table->GetIterator(); if (!right_iter) { - DLOG(WARNING) << "Last Join right table is empty"; return Row(left_slices, left_row, right_slices, Row()); } right_iter->SeekToFirst(); if (!right_iter->Valid()) { - LOG(WARNING) << "Last Join right table is empty"; return Row(left_slices, left_row, right_slices, Row()); } From 330d171193b925c4fd63a6df75d51a14572d7ad0 Mon Sep 17 00:00:00 2001 From: aceforeverd Date: Thu, 14 Sep 2023 17:55:42 +0800 Subject: [PATCH 38/63] feat: support const project online (#3376) --- cases/query/const_query.yaml | 42 ++++++-------------- hybridse/include/case/sql_case.h | 8 ++-- hybridse/include/sdk/base.h | 2 +- hybridse/include/vm/physical_op.h | 2 +- hybridse/src/case/sql_case.cc | 53 +++++++++++++++---------- hybridse/src/case/sql_case_test.cc | 4 +- hybridse/src/plan/planner.cc | 7 +++- hybridse/src/testing/engine_test_base.h | 14 ++++++- hybridse/src/vm/runner.cc | 7 ++-- hybridse/src/vm/transform.cc | 9 +++-- src/cmd/sql_cmd_test.cc | 6 ++- src/sdk/sql_cluster_test.cc | 8 ++-- src/sdk/sql_request_row.h | 3 +- src/sdk/sql_sdk_base_test.cc | 44 +++++++++++--------- src/sdk/sql_sdk_test.cc | 8 ++-- src/sdk/sql_standalone_sdk_test.cc | 8 ++-- src/test/base_test.cc | 2 +- 17 files changed, 124 insertions(+), 103 deletions(-) diff --git a/cases/query/const_query.yaml b/cases/query/const_query.yaml index 304f0486073..38bbbeb5e47 100644 --- a/cases/query/const_query.yaml +++ b/cases/query/const_query.yaml @@ -14,10 +14,12 @@ debugs: [] version: 0.5.0 +# FIXME: support request procedure for const project +# requires GetTablet impl for non-table procedure cases: - id: 0 desc: select const number - mode: request-unsupport + mode: procedure-unsupport db: db1 sql: | select 1 as id, 2 as col1, 3.3 as col2; @@ -29,7 +31,7 @@ cases: - [1, 2, 3.3] - id: 1 desc: select str - mode: request-unsupport + mode: procedure-unsupport db: db1 sql: | select 1 as id, "hello_world" as col1; @@ -41,7 +43,7 @@ cases: - [1, "hello_world"] - id: 2 desc: const substr pos len - mode: request-unsupport + mode: procedure-unsupport db: db1 sql: | select 1 as id, substring("hello_world", 3, 6) as col1; @@ -53,7 +55,7 @@ cases: - [1, "llo_wo"] - id: 3 desc: const substr pos - mode: request-unsupport + mode: procedure-unsupport db: db1 sql: | select 1 as id, substring("hello_world", 3) as col1; @@ -65,7 +67,7 @@ cases: - [1, "llo_world"] - id: 4 desc: const concat 1 - mode: request-unsupport + mode: procedure-unsupport db: db1 sql: | select 1 as id, concat("hello", "world", "abc") as col1; @@ -76,13 +78,8 @@ cases: - [1, "helloworldabc"] - id: 5 desc: cast常量 using CAST operator - mode: request-unsupport + mode: procedure-unsupport db: db1 - inputs: - - columns: ["c1 int", "c2 string", "c5 bigint"] - indexs: ["index1:c1:c5"] - rows: - - [1, "2020-05-22 10:43:40", 1] sql: | select CAST (10 as int) as c1, CAST (10 as bigint) as c2, CAST (10 as float) as c3, CAST (10 as double) as c4, CAST (1590115460000 as timestamp) as c5, CAST ("2020-05-20" as date) as c6, CAST (10 as string) as c7; expect: @@ -91,13 +88,8 @@ cases: - [10, 10, 10.0, 10.0, 1590115460000, '2020-05-20', "10"] - id: 6 desc: cast NULL常量 using CAST operator - mode: request-unsupport + mode: procedure-unsupport db: db1 - inputs: - - columns: ["c1 int", "c2 string", "c5 bigint"] - indexs: ["index1:c1:c5"] - rows: - - [1, "2020-05-22 10:43:40", 1] sql: | select CAST (NULL as int) as c1, CAST (NULL as bigint) as c2, CAST (NULL as float) as c3, CAST (NULL as double) as c4, CAST (NULL as timestamp) as c5, CAST (NULL as date) as c6, CAST (NULL as string) as c7; expect: @@ -106,13 +98,8 @@ cases: - [NULL, NULL, NULL, NULL, NULL, NULL, NULL] - id: 7 desc: cast常量 using type() function - mode: request-unsupport + mode: procedure-unsupport db: db1 - inputs: - - columns: ["c1 int", "c2 string", "c5 bigint"] - indexs: ["index1:c1:c5"] - rows: - - [1, "2020-05-22 10:43:40", 1] sql: | select int(10) as c1, bigint(10) as c2, float(10) as c3, double(10) as c4, timestamp(1590115460000) as c5, date("2020-05-20") as c6, string(10) as c7; expect: @@ -121,13 +108,8 @@ cases: - [10, 10, 10.0, 10.0, 1590115460000, '2020-05-20', "10"] - id: 8 desc: cast NULL常量 using type(NULL) function - mode: request-unsupport + mode: procedure-unsupport db: db1 - inputs: - - columns: ["c1 int", "c2 string", "c5 bigint"] - indexs: ["index1:c1:c5"] - rows: - - [1, "2020-05-22 10:43:40", 1] sql: | select int(NULL) as c1, bigint(NULL) as c2, float(NULL) as c3, double(NULL) as c4, timestamp(NULL) as c5, date(NULL) as c6, string(NULL) as c7; expect: @@ -136,7 +118,7 @@ cases: - [NULL, NULL, NULL, NULL, NULL, NULL, NULL] - id: 9 desc: differnt const node type - mode: request-unsupport + mode: procedure-unsupport db: db1 sql: | select true c1, int16(3) c2, 13 c3, 10.0 c4, 'a string' c5, date(timestamp(1590115420000)) c6, timestamp(1590115420000) c7; diff --git a/hybridse/include/case/sql_case.h b/hybridse/include/case/sql_case.h index 0c7756da02c..7cc05bba1d5 100644 --- a/hybridse/include/case/sql_case.h +++ b/hybridse/include/case/sql_case.h @@ -205,10 +205,10 @@ class SqlCase { static std::string GenRand(const std::string& prefix) { return prefix + std::to_string(rand() % 10000000 + 1); // NOLINT } - absl::StatusOr BuildCreateSpSqlFromInput(int32_t input_idx, absl::string_view sql, - const std::set& common_idx); - absl::StatusOr BuildCreateSpSqlFromSchema(const type::TableDef& table, absl::string_view select_sql, - const std::set& common_idx); + absl::StatusOr BuildCreateSpSql(absl::string_view sql, const std::set& common_idx, + std::optional input_idx); + absl::StatusOr BuildCreateSpSql(absl::string_view select_sql, const std::set& common_idx, + std::optional table); friend std::ostream& operator<<(std::ostream& output, const SqlCase& thiz); static bool IS_PERF() { diff --git a/hybridse/include/sdk/base.h b/hybridse/include/sdk/base.h index fcf9adcb0d1..90d8ff75680 100644 --- a/hybridse/include/sdk/base.h +++ b/hybridse/include/sdk/base.h @@ -71,7 +71,7 @@ struct Status { cm.append("] "); cm.append(msg); return cm; - }; + } int code; // msg use prepend and append, it's better to use absl::Cord, but we may directly use msg diff --git a/hybridse/include/vm/physical_op.h b/hybridse/include/vm/physical_op.h index 1fa22650995..c884d0bb7e5 100644 --- a/hybridse/include/vm/physical_op.h +++ b/hybridse/include/vm/physical_op.h @@ -738,7 +738,7 @@ class PhysicalConstProjectNode : public PhysicalOpNode { fn_infos_.push_back(&project_.fn_info()); } virtual ~PhysicalConstProjectNode() {} - virtual void Print(std::ostream &output, const std::string &tab) const; + void Print(std::ostream &output, const std::string &tab) const override; static PhysicalConstProjectNode *CastFrom(PhysicalOpNode *node); const ColumnProjects &project() const { return project_; } diff --git a/hybridse/src/case/sql_case.cc b/hybridse/src/case/sql_case.cc index 3d16b213b5a..c98defb679b 100644 --- a/hybridse/src/case/sql_case.cc +++ b/hybridse/src/case/sql_case.cc @@ -751,6 +751,9 @@ const std::string SqlCase::case_name() const { } bool SqlCase::ExtractInputTableDef(type::TableDef& table, int32_t input_idx) const { + if (inputs_.size() <= input_idx) { + return false; + } return ExtractInputTableDef(inputs_[input_idx], table); } bool SqlCase::ExtractInputTableDef(const TableInfo& input, @@ -1639,35 +1642,41 @@ void InitCases(std::string yaml_path, std::vector& cases, // NOLINT const std::vector& filters) { SqlCase::CreateSqlCasesFromYaml(hybridse::sqlcase::FindSqlCaseBaseDirPath(), yaml_path, cases, filters); } -absl::StatusOr SqlCase::BuildCreateSpSqlFromInput(int32_t input_idx, - absl::string_view select_sql, - const std::set& common_idx) { - type::TableDef table; - if (!ExtractInputTableDef(table, input_idx)) { - return absl::FailedPreconditionError("Fail to extract table schema"); - } +absl::StatusOr SqlCase::BuildCreateSpSql(absl::string_view select_sql, const std::set& common_idx, + std::optional input_idx) { + if (input_idx.has_value()) { + type::TableDef table; + if (!ExtractInputTableDef(table, input_idx.value())) { + return absl::FailedPreconditionError("Fail to extract table schema"); + } - return BuildCreateSpSqlFromSchema(table, select_sql, common_idx); + return BuildCreateSpSql(select_sql, common_idx, &table); + } + std::optional tab = {}; + return BuildCreateSpSql(select_sql, common_idx, tab); } -absl::StatusOr SqlCase::BuildCreateSpSqlFromSchema(const type::TableDef& table, - absl::string_view select_sql, - const std::set& common_idx) { - auto sql_view = absl::StripAsciiWhitespace(select_sql); - std::string query_stmt(sql_view); - if (query_stmt.back() != ';') { +absl::StatusOr SqlCase::BuildCreateSpSql(absl::string_view select_sql, const std::set& common_idx, + std::optional tab) { + auto sql_view = absl::StripAsciiWhitespace(select_sql); + std::string query_stmt(sql_view); + if (query_stmt.back() != ';') { absl::StrAppend(&query_stmt, ";"); } std::string sql = absl::Substitute("CREATE PROCEDURE $0 (\n", sp_name_); - for (int i = 0; i < table.columns_size(); i++) { - auto column = table.columns(i); - if (!common_idx.empty() && common_idx.count(i)) { - absl::StrAppend(&sql, "const "); - } - absl::SubstituteAndAppend(&sql, "$0 $1", column.name(), TypeString(column.type())); - if (i < table.columns_size() - 1) { - absl::StrAppend(&sql, ",\n"); + if (tab.has_value()) { + auto table = tab.value(); + + for (int i = 0; i < table->columns_size(); i++) { + auto column = table->columns(i); + if (!common_idx.empty() && common_idx.count(i)) { + absl::StrAppend(&sql, "const "); + } + absl::SubstituteAndAppend(&sql, "$0 $1", column.name(), TypeString(column.type())); + if (i < table->columns_size() - 1) { + absl::StrAppend(&sql, ",\n"); + } } } absl::SubstituteAndAppend(&sql, ")\nBEGIN\n$0\nEND;", query_stmt); diff --git a/hybridse/src/case/sql_case_test.cc b/hybridse/src/case/sql_case_test.cc index 6cc980253ea..c6603544a3d 100644 --- a/hybridse/src/case/sql_case_test.cc +++ b/hybridse/src/case/sql_case_test.cc @@ -1168,7 +1168,7 @@ TEST_F(SqlCaseTest, BuildCreateSpSqlFromInputTest) { sql_case.sp_name_ = "sp"; std::string sql = " select c1, c2, c3, c4 from t1 "; std::string sp_sql = ""; - auto s = sql_case.BuildCreateSpSqlFromInput(0, sql, {}); + auto s = sql_case.BuildCreateSpSql(sql, {}, 0); ASSERT_TRUE(s.ok()) << s.status(); ASSERT_EQ(R"s(CREATE PROCEDURE sp ( c1 string, @@ -1190,7 +1190,7 @@ END;)s", sql_case.inputs_.push_back(input); std::string sql = "select c1, c2, c3, c4 from t1;"; std::string sp_sql = ""; - auto s = sql_case.BuildCreateSpSqlFromInput(0, sql, {0, 1, 3}); + auto s = sql_case.BuildCreateSpSql(sql, {0, 1, 3}, 0); ASSERT_TRUE(s.ok()) << s.status(); ASSERT_EQ(R"s(CREATE PROCEDURE sp1 ( const c1 string, diff --git a/hybridse/src/plan/planner.cc b/hybridse/src/plan/planner.cc index 3cef58a131f..c0a68e3104e 100644 --- a/hybridse/src/plan/planner.cc +++ b/hybridse/src/plan/planner.cc @@ -490,6 +490,7 @@ absl::StatusOr Planner::IsTable(node::PlanNode *node) { // - SELECT // - JOIN // - WINDOW +// - CONST PROJECT // - UnSupport Ops:: // - CREATE TABLE // - INSERT TABLE @@ -500,8 +501,10 @@ absl::StatusOr Planner::IsTable(node::PlanNode *node) { // - Not Impl // - Order By base::Status Planner::ValidateOnlineServingOp(node::PlanNode *node) { - CHECK_TRUE(nullptr != node, common::kNullInputPointer, - "Fail to validate request table: input node is null") + if (node == nullptr) { + // null is fine, e.g the const project + return {}; + } switch (node->type_) { case node::kPlanTypeProject: { auto project_node = dynamic_cast(node); diff --git a/hybridse/src/testing/engine_test_base.h b/hybridse/src/testing/engine_test_base.h index f9317201f00..e759169f0fd 100644 --- a/hybridse/src/testing/engine_test_base.h +++ b/hybridse/src/testing/engine_test_base.h @@ -271,6 +271,12 @@ class RequestEngineTestRunner : public EngineTestRunner { std::string request_db_name = request_session->GetRequestDbName(); CHECK_TRUE(parameter_rows_.empty(), common::kUnSupport, "Request do not support parameterized query currently") Row parameter = parameter_rows_.empty() ? Row() : parameter_rows_[0]; + if (request_rows_.empty()) { + // send empty request, trigger e.g const project in request mode + CHECK_TRUE(request_name.empty() && request_db_name.empty(), common::kUnsupportSql, + "no request data for request table: <", request_db_name, ".", request_name, ">") + request_rows_.push_back(Row()); + } for (auto in_row : request_rows_) { Row out_row; int run_ret = request_session->Run(in_row, &out_row); @@ -278,7 +284,7 @@ class RequestEngineTestRunner : public EngineTestRunner { return_code_ = ENGINE_TEST_RET_EXECUTION_ERROR; return Status(common::kRunError, "Run request session failed"); } - if (!has_batch_request) { + if (!has_batch_request && !request_name.empty()) { CHECK_TRUE(AddRowIntoTable(request_db_name, request_name, in_row), common::kTablePutFailed, "Fail add row into table ", request_db_name, ".", request_name); } @@ -423,6 +429,12 @@ class BatchRequestEngineTestRunner : public EngineTestRunner { offset += row_num; } } + + if (request_rows_.empty()) { + // batch request rows will empty for const projects + // workaround by add the one empty row + request_rows_.push_back(Row()); + } return Status::OK(); } diff --git a/hybridse/src/vm/runner.cc b/hybridse/src/vm/runner.cc index 28525b8face..a15a2626bf3 100644 --- a/hybridse/src/vm/runner.cc +++ b/hybridse/src/vm/runner.cc @@ -1074,11 +1074,10 @@ std::shared_ptr Runner::BatchRequestRun(RunnerContext& ctx) { return cached; } } - std::shared_ptr outputs = - std::make_shared(); + + std::shared_ptr outputs = std::make_shared(); std::vector> inputs(producers_.size()); - std::vector> batch_inputs( - producers_.size()); + std::vector> batch_inputs(producers_.size()); for (size_t idx = producers_.size(); idx > 0; idx--) { batch_inputs[idx - 1] = producers_[idx - 1]->BatchRequestRun(ctx); } diff --git a/hybridse/src/vm/transform.cc b/hybridse/src/vm/transform.cc index 0e89ccd9291..60549447e42 100644 --- a/hybridse/src/vm/transform.cc +++ b/hybridse/src/vm/transform.cc @@ -2300,7 +2300,9 @@ Status RequestModeTransformer::TransformProjectPlanOp( "Input node or output node is null"); PhysicalOpNode* depend = nullptr; - CHECK_STATUS(TransformPlanOp(node->GetChildren()[0], &depend)); + if (!node->GetChildren().empty() && nullptr != node->GetChildren()[0]) { + CHECK_STATUS(TransformPlanOp(node->GetChildren()[0], &depend)); + } CHECK_STATUS(CompleteProjectList(node, depend)); @@ -2468,8 +2470,6 @@ Status RequestModeTransformer::ValidateRequestTable( return Status::OK(); } case vm::kPhysicalOpConstProject: { - FAIL_STATUS(kPlanError, - "Non-support Const Project in request mode", in->GetTreeString()); break; } default: { @@ -2487,6 +2487,9 @@ Status RequestModeTransformer::TransformProjectOp( node::ProjectListNode* project_list, PhysicalOpNode* depend, bool append_input, PhysicalOpNode** output) { PhysicalOpNode* new_depend = depend; + if (nullptr == depend) { + return CreatePhysicalConstProjectNode(project_list, output); + } if (nullptr != project_list->GetW()) { CHECK_STATUS(TransformWindowOp(depend, project_list->GetW(), &new_depend)); } diff --git a/src/cmd/sql_cmd_test.cc b/src/cmd/sql_cmd_test.cc index bd66e6db4dc..1896ac7c674 100644 --- a/src/cmd/sql_cmd_test.cc +++ b/src/cmd/sql_cmd_test.cc @@ -3740,12 +3740,16 @@ struct DeploymentEnv { }); } + // A bacth request increase deployment cnt by 1 + // yet may greatly impact deploy response time, if the batch size is huge + // maybe it requires a revision void CallDeployProcedureBatch() { hybridse::sdk::Status status; std::shared_ptr rr = std::make_shared(); GetRequestRow(&rr, dp_name_); - auto common_column_indices = std::make_shared(rr->GetSchema()); + auto common_column_indices = std::make_shared(); auto row_batch = std::make_shared(rr->GetSchema(), common_column_indices); + ASSERT_TRUE(row_batch->AddRow(rr)); sr->CallSQLBatchRequestProcedure(db_, dp_name_, row_batch, &status); ASSERT_TRUE(status.IsOK()) << status.msg << "\n" << status.trace; } diff --git a/src/sdk/sql_cluster_test.cc b/src/sdk/sql_cluster_test.cc index f8cccac1832..359ac431573 100644 --- a/src/sdk/sql_cluster_test.cc +++ b/src/sdk/sql_cluster_test.cc @@ -1012,7 +1012,7 @@ TEST_P(SQLSDKBatchRequestQueryTest, SqlSdkDistributeBatchRequestSinglePartitionT TEST_P(SQLSDKBatchRequestQueryTest, SqlSdkDistributeBatchRequestProcedureTest) { auto sql_case = GetParam(); - if (!IsBatchRequestSupportMode(sql_case.mode())) { + if (!IsBatchRequestSupportMode(sql_case.mode()) || "procedure-unsupport" == sql_case.mode()) { LOG(WARNING) << "Unsupport mode: " << sql_case.mode(); return; } @@ -1030,7 +1030,7 @@ TEST_P(SQLSDKBatchRequestQueryTest, SqlSdkDistributeBatchRequestProcedureTest) { TEST_P(SQLSDKQueryTest, SqlSdkDistributeRequestProcedureTest) { auto sql_case = GetParam(); LOG(INFO) << "ID: " << sql_case.id() << ", DESC: " << sql_case.desc(); - if (!IsRequestSupportMode(sql_case.mode())) { + if (!IsRequestSupportMode(sql_case.mode()) || "procedure-unsupport" == sql_case.mode()) { LOG(WARNING) << "Unsupport mode: " << sql_case.mode(); return; } @@ -1041,7 +1041,7 @@ TEST_P(SQLSDKQueryTest, SqlSdkDistributeRequestProcedureTest) { } TEST_P(SQLSDKBatchRequestQueryTest, SqlSdkDistributeBatchRequestProcedureAsyncTest) { auto sql_case = GetParam(); - if (!IsRequestSupportMode(sql_case.mode())) { + if (!IsBatchRequestSupportMode(sql_case.mode()) || "procedure-unsupport" == sql_case.mode()) { LOG(WARNING) << "Unsupport mode: " << sql_case.mode(); return; } @@ -1059,7 +1059,7 @@ TEST_P(SQLSDKBatchRequestQueryTest, SqlSdkDistributeBatchRequestProcedureAsyncTe TEST_P(SQLSDKQueryTest, SqlSdkDistributeRequestProcedureAsyncTest) { auto sql_case = GetParam(); LOG(INFO) << "ID: " << sql_case.id() << ", DESC: " << sql_case.desc(); - if (!IsRequestSupportMode(sql_case.mode())) { + if (!IsRequestSupportMode(sql_case.mode()) || "procedure-unsupport" == sql_case.mode()) { LOG(WARNING) << "Unsupport mode: " << sql_case.mode(); return; } diff --git a/src/sdk/sql_request_row.h b/src/sdk/sql_request_row.h index af16a43da65..e09160f58e4 100644 --- a/src/sdk/sql_request_row.h +++ b/src/sdk/sql_request_row.h @@ -119,6 +119,7 @@ class SQLRequestRowBatch { class ColumnIndicesSet { public: + ColumnIndicesSet() {} explicit ColumnIndicesSet(std::shared_ptr schema) : bound_(schema->GetColumnCnt()) {} bool Empty() const { return common_column_indices_.empty(); } @@ -127,7 +128,7 @@ class ColumnIndicesSet { private: friend class SQLRequestRowBatch; - size_t bound_; + size_t bound_ = 0; std::set common_column_indices_; }; diff --git a/src/sdk/sql_sdk_base_test.cc b/src/sdk/sql_sdk_base_test.cc index 5d715f37fc4..d66ecc8c75f 100644 --- a/src/sdk/sql_sdk_base_test.cc +++ b/src/sdk/sql_sdk_base_test.cc @@ -105,7 +105,7 @@ void SQLSDKTest::CreateProcedure(hybridse::sqlcase::SqlCase& sql_case, // NOLIN std::shared_ptr router, bool is_batch) { DLOG(INFO) << "Create Procedure BEGIN"; hybridse::sdk::Status status; - if (sql_case.inputs()[0].name_.empty()) { + if (!sql_case.inputs().empty() && sql_case.inputs()[0].name_.empty()) { sql_case.set_input_name( hybridse::sqlcase::SqlCase::GenRand("auto_t") + std::to_string(static_cast(time(NULL))), 0); } @@ -126,13 +126,16 @@ void SQLSDKTest::CreateProcedure(hybridse::sqlcase::SqlCase& sql_case, // NOLIN hybridse::type::TableDef batch_request_schema; ASSERT_TRUE(sql_case.ExtractTableDef(sql_case.batch_request().columns_, sql_case.batch_request().indexs_, batch_request_schema)); - auto s = sql_case.BuildCreateSpSqlFromSchema(batch_request_schema, sql, - sql_case.batch_request().common_column_indices_); + auto s = sql_case.BuildCreateSpSql(sql, sql_case.batch_request().common_column_indices_, &batch_request_schema); ASSERT_TRUE(s.ok()) << s.status(); create_sp = s.value(); } else { std::set common_idx; - auto s = sql_case.BuildCreateSpSqlFromInput(0, sql, common_idx); + std::optional idx = {}; + if (!sql_case.inputs().empty()) { + idx = 0; + } + auto s = sql_case.BuildCreateSpSql(sql, common_idx, idx); ASSERT_TRUE(s.ok()) << s.status(); create_sp = s.value(); } @@ -405,33 +408,38 @@ void SQLSDKQueryTest::RequestExecuteSQL(hybridse::sqlcase::SqlCase& sql_case, / hybridse::type::TableDef insert_table; std::vector insert_rows; std::vector inserts; - if (!has_batch_request) { - ASSERT_TRUE(sql_case.ExtractInputTableDef(insert_table, 0)); - ASSERT_TRUE(sql_case.ExtractInputData(insert_rows, 0)); - sql_case.BuildInsertSqlListFromInput(0, &inserts); + if (!sql_case.inputs().empty()) { + if (!has_batch_request) { + ASSERT_TRUE(sql_case.ExtractInputTableDef(insert_table, 0)); + ASSERT_TRUE(sql_case.ExtractInputData(insert_rows, 0)); + sql_case.BuildInsertSqlListFromInput(0, &inserts); + } else { + ASSERT_TRUE(sql_case.ExtractInputTableDef(sql_case.batch_request_, insert_table)); + ASSERT_TRUE(sql_case.ExtractInputData(sql_case.batch_request_, insert_rows)); + } + CheckSchema(insert_table.columns(), *(request_row->GetSchema().get())); + DLOG(INFO) << "Request Row:\n"; + PrintRows(insert_table.columns(), insert_rows); } else { - ASSERT_TRUE(sql_case.ExtractInputTableDef(sql_case.batch_request_, insert_table)); - ASSERT_TRUE(sql_case.ExtractInputData(sql_case.batch_request_, insert_rows)); + // prepare a empty row in case result check for const projects + insert_rows.push_back(hybridse::codec::Row()); } - CheckSchema(insert_table.columns(), *(request_row->GetSchema().get())); - LOG(INFO) << "Request Row:\n"; - PrintRows(insert_table.columns(), insert_rows); hybridse::codec::RowView row_view(insert_table.columns()); std::vector> results; - LOG(INFO) << "Request execute sql start!"; + DLOG(INFO) << "Request execute sql start!"; for (size_t i = 0; i < insert_rows.size(); i++) { row_view.Reset(insert_rows[i].buf()); CovertHybridSERowToRequestRow(&row_view, request_row); std::shared_ptr rs; if (is_procedure) { if (is_asyn) { - LOG(INFO) << "-------asyn procedure----------"; + DLOG(INFO) << "-------asyn procedure----------"; auto future = router->CallProcedure(sql_case.db(), sql_case.sp_name_, 1000, request_row, &status); if (!future || status.code != 0) FAIL() << "sql case expect success == true" << status.msg; rs = future->GetResultSet(&status); } else { - LOG(INFO) << "--------syn procedure----------"; + DLOG(INFO) << "--------syn procedure----------"; rs = router->CallProcedure(sql_case.db(), sql_case.sp_name_, request_row, &status); } } else { @@ -439,8 +447,8 @@ void SQLSDKQueryTest::RequestExecuteSQL(hybridse::sqlcase::SqlCase& sql_case, / } if (!rs || status.code != 0) FAIL() << "sql case expect success == true" << status.msg; results.push_back(rs); - if (!has_batch_request) { - LOG(INFO) << "insert request: \n" << inserts[i]; + if (!has_batch_request && !sql_case.inputs().empty()) { + DLOG(INFO) << "insert request: \n" << inserts[i]; bool ok = router->ExecuteInsert(insert_table.catalog(), inserts[i], &status); ASSERT_TRUE(ok); } diff --git a/src/sdk/sql_sdk_test.cc b/src/sdk/sql_sdk_test.cc index cbb6235ea4c..4b6a67de11f 100644 --- a/src/sdk/sql_sdk_test.cc +++ b/src/sdk/sql_sdk_test.cc @@ -141,7 +141,7 @@ TEST_P(SQLSDKQueryTest, SqlSdkBatchTest) { TEST_P(SQLSDKQueryTest, SqlSdkRequestProcedureTest) { auto sql_case = GetParam(); LOG(INFO) << "ID: " << sql_case.id() << ", DESC: " << sql_case.desc(); - if (!IsRequestSupportMode(sql_case.mode())) { + if (!IsRequestSupportMode(sql_case.mode()) || "procedure-unsupport" == sql_case.mode()) { LOG(WARNING) << "Unsupport mode: " << sql_case.mode(); return; } @@ -153,7 +153,7 @@ TEST_P(SQLSDKQueryTest, SqlSdkRequestProcedureTest) { TEST_P(SQLSDKQueryTest, SqlSdkRequestProcedureAsynTest) { auto sql_case = GetParam(); LOG(INFO) << "ID: " << sql_case.id() << ", DESC: " << sql_case.desc(); - if (!IsRequestSupportMode(sql_case.mode())) { + if (!IsRequestSupportMode(sql_case.mode()) || "procedure-unsupport" == sql_case.mode()) { LOG(WARNING) << "Unsupport mode: " << sql_case.mode(); return; } @@ -193,7 +193,7 @@ TEST_P(SQLSDKBatchRequestQueryTest, SqlSdkBatchRequestTest) { } TEST_P(SQLSDKBatchRequestQueryTest, SqlSdkBatchRequestProcedureTest) { auto sql_case = GetParam(); - if (!IsBatchRequestSupportMode(sql_case.mode())) { + if (!IsBatchRequestSupportMode(sql_case.mode()) || "procedure-unsupport" == sql_case.mode()) { LOG(WARNING) << "Unsupport mode: " << sql_case.mode(); return; } @@ -209,7 +209,7 @@ TEST_P(SQLSDKBatchRequestQueryTest, SqlSdkBatchRequestProcedureTest) { TEST_P(SQLSDKBatchRequestQueryTest, SqlSdkBatchRequestProcedureAsynTest) { auto sql_case = GetParam(); - if (!IsBatchRequestSupportMode(sql_case.mode())) { + if (!IsBatchRequestSupportMode(sql_case.mode()) || "procedure-unsupport" == sql_case.mode()) { LOG(WARNING) << "Unsupport mode: " << sql_case.mode(); return; } diff --git a/src/sdk/sql_standalone_sdk_test.cc b/src/sdk/sql_standalone_sdk_test.cc index ac24fd374df..e61cf1ea76c 100644 --- a/src/sdk/sql_standalone_sdk_test.cc +++ b/src/sdk/sql_standalone_sdk_test.cc @@ -127,7 +127,7 @@ TEST_P(SQLSDKQueryTest, SqlSdkBatchTest) { TEST_P(SQLSDKQueryTest, SqlSdkRequestProcedureTest) { auto sql_case = GetParam(); LOG(INFO) << "ID: " << sql_case.id() << ", DESC: " << sql_case.desc(); - if (!IsRequestSupportMode(sql_case.mode())) { + if (!IsRequestSupportMode(sql_case.mode()) || "procedure-unsupport" == sql_case.mode()) { LOG(WARNING) << "Unsupport mode: " << sql_case.mode(); return; } @@ -139,7 +139,7 @@ TEST_P(SQLSDKQueryTest, SqlSdkRequestProcedureTest) { TEST_P(SQLSDKQueryTest, SqlSdkRequestProcedureAsynTest) { auto sql_case = GetParam(); LOG(INFO) << "ID: " << sql_case.id() << ", DESC: " << sql_case.desc(); - if (!IsRequestSupportMode(sql_case.mode())) { + if (!IsRequestSupportMode(sql_case.mode()) || "procedure-unsupport" == sql_case.mode()) { LOG(WARNING) << "Unsupport mode: " << sql_case.mode(); return; } @@ -164,7 +164,7 @@ TEST_P(SQLSDKBatchRequestQueryTest, SqlSdkBatchRequestTest) { } TEST_P(SQLSDKBatchRequestQueryTest, SqlSdkBatchRequestProcedureTest) { auto sql_case = GetParam(); - if (!IsBatchRequestSupportMode(sql_case.mode())) { + if (!IsBatchRequestSupportMode(sql_case.mode()) || "procedure-unsupport" == sql_case.mode()) { LOG(WARNING) << "Unsupport mode: " << sql_case.mode(); return; } @@ -180,7 +180,7 @@ TEST_P(SQLSDKBatchRequestQueryTest, SqlSdkBatchRequestProcedureTest) { TEST_P(SQLSDKBatchRequestQueryTest, SqlSdkBatchRequestProcedureAsynTest) { auto sql_case = GetParam(); - if (!IsBatchRequestSupportMode(sql_case.mode())) { + if (!IsBatchRequestSupportMode(sql_case.mode()) || "procedure-unsupport" == sql_case.mode()) { LOG(WARNING) << "Unsupport mode: " << sql_case.mode(); return; } diff --git a/src/test/base_test.cc b/src/test/base_test.cc index 26aa3a2713e..0e85ccfb396 100644 --- a/src/test/base_test.cc +++ b/src/test/base_test.cc @@ -215,7 +215,7 @@ void SQLCaseTest::PrintRows(const hybridse::vm::Schema &schema, const std::vecto } } oss << t << std::endl; - LOG(INFO) << "\n" << oss.str() << "\n"; + DLOG(INFO) << "\n" << oss.str() << "\n"; } const std::vector SQLCaseTest::SortRows(const hybridse::vm::Schema &schema, From 1792700a979dfc6505dfa234ad8c39180afc1c06 Mon Sep 17 00:00:00 2001 From: tobe Date: Fri, 15 Sep 2023 11:24:37 +0800 Subject: [PATCH 39/63] fix: refactor taskmanager config and support deleting HDFS files when dropping tables (#3369) --- .../taskmanager/config/TaskManagerConfig.java | 551 +++++++++++++----- .../taskmanager/dao/JobIdGenerator.java | 31 +- .../taskmanager/server/JobResultSaver.java | 6 +- .../taskmanager/server/TaskManagerServer.java | 17 +- .../server/impl/TaskManagerImpl.java | 22 +- .../udf/ExternalFunctionManager.java | 2 +- .../taskmanager/zk/FailoverWatcher.java | 12 +- .../src/main/resources/taskmanager.properties | 1 - .../openmldb/taskmanager/JobInfoManager.scala | 15 +- .../openmldb/taskmanager/LogManager.scala | 4 +- .../taskmanager/k8s/K8sJobManager.scala | 34 +- .../taskmanager/spark/SparkJobManager.scala | 70 +-- .../tracker/YarnJobTrackerThread.scala | 2 +- .../openmldb/taskmanager/util/HdfsUtil.scala | 49 ++ .../taskmanager/util/VersionUtil.scala | 2 +- .../taskmanager/yarn/YarnClientUtil.scala | 16 +- .../server/impl/TestTaskManagerImpl.scala | 16 +- release/sbin/start-taskmanagers.sh | 6 +- 18 files changed, 581 insertions(+), 275 deletions(-) create mode 100644 java/openmldb-taskmanager/src/main/scala/com/_4paradigm/openmldb/taskmanager/util/HdfsUtil.scala diff --git a/java/openmldb-taskmanager/src/main/java/com/_4paradigm/openmldb/taskmanager/config/TaskManagerConfig.java b/java/openmldb-taskmanager/src/main/java/com/_4paradigm/openmldb/taskmanager/config/TaskManagerConfig.java index 3be3bcf39ee..76642ff17d6 100644 --- a/java/openmldb-taskmanager/src/main/java/com/_4paradigm/openmldb/taskmanager/config/TaskManagerConfig.java +++ b/java/openmldb-taskmanager/src/main/java/com/_4paradigm/openmldb/taskmanager/config/TaskManagerConfig.java @@ -32,168 +32,396 @@ public class TaskManagerConfig { private static Logger logger = LoggerFactory.getLogger(TaskManagerConfig.class); - public static String HOST; - public static int PORT; - public static int WORKER_THREAD; - public static int IO_THREAD; - public static int CHANNEL_KEEP_ALIVE_TIME; - public static String ZK_CLUSTER; - public static String ZK_ROOT_PATH; - public static String ZK_TASKMANAGER_PATH; - public static String ZK_MAX_JOB_ID_PATH; - public static int ZK_SESSION_TIMEOUT; - public static int ZK_CONNECTION_TIMEOUT; - public static int ZK_BASE_SLEEP_TIME; - public static int ZK_MAX_CONNECT_WAIT_TIME; - public static int ZK_MAX_RETRIES; - public static String SPARK_MASTER; - public static String SPARK_YARN_JARS; - public static String SPARK_HOME; - public static int PREFETCH_JOBID_NUM; - public static String JOB_LOG_PATH; - public static String EXTERNAL_FUNCTION_DIR; - public static boolean TRACK_UNFINISHED_JOBS; - public static int JOB_TRACKER_INTERVAL; - public static String SPARK_DEFAULT_CONF; - public static String SPARK_EVENTLOG_DIR; - public static int SPARK_YARN_MAXAPPATTEMPTS; - public static String OFFLINE_DATA_PREFIX; - public static String NAMENODE_URI; - public static String BATCHJOB_JAR_PATH; - public static String HADOOP_CONF_DIR; - public static String HADOOP_USER_NAME; - public static boolean ENABLE_HIVE_SUPPORT; - public static long BATCH_JOB_RESULT_MAX_WAIT_TIME; - public static String K8S_HADOOP_CONFIGMAP_NAME; - public static String K8S_MOUNT_LOCAL_PATH; - - private static volatile boolean isParsed = false; + private volatile static TaskManagerConfig instance; + + private volatile static Properties props; + + public static Properties getProps() { + return props; + } + + private static TaskManagerConfig getInstance() throws ConfigException { + if (instance == null) { + instance = new TaskManagerConfig(); + instance.init(); + } + return instance; + } public static void parse() throws ConfigException { - if (!isParsed) { - doParse(); - isParsed = true; + getInstance(); + } + + protected static String getString(String key) { + return props.getProperty(key); + } + + protected static int getInt(String key) { + return Integer.parseInt(getString(key)); + } + + protected static long getLong(String key) { + return Long.parseLong(getString(key)); + } + + protected static boolean getBool(String key) { + return Boolean.parseBoolean(getString(key)); + } + + + public static String getServerHost() { + return getString("server.host"); + } + + public static int getServerPort() { + return getInt("server.port"); + } + + public static int getServerWorkerThreads() { + return getInt("server.worker_threads"); + } + + public static int getServerIoThreads() { + return getInt("server.io_threads"); + } + + public static int getChannelKeepAliveTime() { + return getInt("server.channel_keep_alive_time"); + } + + public static int getZkSessionTimeout() { + return getInt("zookeeper.session_timeout"); + } + + public static String getZkCluster() { + return getString("zookeeper.cluster"); + } + + public static String getZkRootPath() { + return getString("zookeeper.root_path"); + } + + public static int getZkConnectionTimeout() { + return getInt("zookeeper.connection_timeout"); + } + + public static int getZkBaseSleepTime() { + return getInt("zookeeper.base_sleep_time"); + } + + public static int getZkMaxRetries() { + return getInt("zookeeper.max_retries"); + } + + public static int getZkMaxConnectWaitTime() { + return getInt("zookeeper.max_connect_waitTime"); + } + + public static String getSparkMaster() { + return getString("spark.master"); + } + + public static String getSparkYarnJars() { + return getString("spark.yarn.jars"); + } + + public static String getSparkHome() { + return getString("spark.home"); + } + + public static int getPrefetchJobidNum() { + return getInt("prefetch.jobid.num"); + } + + public static String getJobLogPath() { + return getString("job.log.path"); + } + + public static String getExternalFunctionDir() { + return getString("external.function.dir"); + } + + public static boolean getTrackUnfinishedJobs() { + return getBool("track.unfinished.jobs"); + } + + public static int getJobTrackerInterval() { + return getInt("job.tracker.interval"); + } + + public static String getSparkDefaultConf() { + return getString("spark.default.conf"); + } + + public static String getSparkEventlogDir() { + return getString("spark.eventLog.dir"); + } + + public static int getSparkYarnMaxappattempts() { + return getInt("spark.yarn.maxAppAttempts"); + } + + public static String getOfflineDataPrefix() { + return getString("offline.data.prefix"); + } + + public static String getBatchjobJarPath() { + return getString("batchjob.jar.path"); + } + + + public static String getHadoopConfDir() { + return getString("hadoop.conf.dir"); + } + + public static boolean getEnableHiveSupport() { + return getBool("enable.hive.support"); + } + + public static long getBatchJobResultMaxWaitTime() { + return getLong("batch.job.result.max.wait.time"); + } + + + public static String getK8sHadoopConfigmapName() { + return getString("k8s.hadoop.configmap"); + } + + public static String getK8sMountLocalPath() { + return getString("k8s.mount.local.path"); + } + + public static String getHadoopUserName() { + return getString("hadoop.user.name"); + } + + public static String getZkTaskmanagerPath() { + return getZkRootPath() + "/taskmanager"; + } + + public static String getZkMaxJobIdPath() { + return getZkTaskmanagerPath() + "/max_job_id"; + } + + public static boolean isK8s() { + return getSparkMaster().equals("k8s") || getSparkMaster().equals("kubernetes"); + } + + public static boolean isYarnCluster() { + return getSparkMaster().equals("yarn") || getSparkMaster().equals("yarn-cluster"); + } + + public static boolean isYarn() { + return getSparkMaster().startsWith("yarn"); + } + + public static void print() throws ConfigException { + parse(); + + StringBuilder builder = new StringBuilder(); + + for (String key : props.stringPropertyNames()) { + String value = props.getProperty(key); + builder.append(key + " = " + value + "\n"); } + + logger.info("Final TaskManager config: \n" + builder.toString()); } - public static void doParse() throws ConfigException { - Properties prop = new Properties(); + private void init() throws ConfigException { + props = new Properties(); + + // Load local properties file try { - prop.load(TaskManagerConfig.class.getClassLoader().getResourceAsStream("taskmanager.properties")); + props.load(TaskManagerConfig.class.getClassLoader().getResourceAsStream("taskmanager.properties")); } catch (IOException e) { throw new ConfigException(String.format("Fail to load taskmanager.properties, message: ", e.getMessage())); } - HOST = prop.getProperty("server.host", "0.0.0.0"); - PORT = Integer.parseInt(prop.getProperty("server.port", "9902")); - if (PORT < 1 || PORT > 65535) { + // Get properties and check + if (props.getProperty("server.host") == null) { + props.setProperty("server.host", "0.0.0.0"); + } + + if (props.getProperty("server.port") == null) { + props.setProperty("server.port", "9902"); + } + + if (getServerPort() < 1 || getServerPort() > 65535) { throw new ConfigException("server.port", "invalid port, should be in range of 1 through 65535"); } - WORKER_THREAD = Integer.parseInt(prop.getProperty("server.worker_threads", "16")); - IO_THREAD = Integer.parseInt(prop.getProperty("server.io_threads", "4")); - // alive time seconds - CHANNEL_KEEP_ALIVE_TIME = Integer.parseInt(prop.getProperty("server.channel_keep_alive_time", "1800")); - ZK_SESSION_TIMEOUT = Integer.parseInt(prop.getProperty("zookeeper.session_timeout", "5000")); - ZK_CLUSTER = prop.getProperty("zookeeper.cluster", ""); - if (ZK_CLUSTER.isEmpty()) { + if (props.getProperty("server.worker_threads") == null) { + props.setProperty("server.worker_threads", "16"); + } + + if (getServerWorkerThreads() <= 0) { + throw new ConfigException("server.worker_threads", "should be larger than 0"); + } + + if (props.getProperty("server.io_threads") == null) { + props.setProperty("server.io_threads", "4"); + } + + if (getServerIoThreads() <= 0) { + throw new ConfigException("server.io_threads", "should be larger than 0"); + } + + if (props.getProperty("server.channel_keep_alive_time") == null) { + props.setProperty("server.channel_keep_alive_time", "1800"); + } + + if (getChannelKeepAliveTime() <= 0) { + throw new ConfigException("server.channel_keep_alive_time", "should be larger than 0"); + } + + if (props.getProperty("zookeeper.session_timeout") == null) { + props.setProperty("zookeeper.session_timeout", "5000"); + } + + if (getZkSessionTimeout() <= 0) { + throw new ConfigException("zookeeper.session_timeout", "should be larger than 0"); + } + + if (props.getProperty("zookeeper.cluster") == null) { + props.setProperty("", ""); + } + + if (getZkCluster().isEmpty()) { throw new ConfigException("zookeeper.cluster", "should not be empty"); } - ZK_ROOT_PATH = prop.getProperty("zookeeper.root_path", ""); - if (ZK_ROOT_PATH.isEmpty()) { - throw new ConfigException("zookeeper.root_path", "should not be empty"); + if (props.getProperty("zookeeper.connection_timeout") == null) { + props.setProperty("zookeeper.connection_timeout", "5000"); + } + + if (getZkConnectionTimeout() <= 0) { + throw new ConfigException("zookeeper.connection_timeout", "should be larger than 0"); + } + + if (props.getProperty("zookeeper.base_sleep_time") == null) { + props.setProperty("zookeeper.base_sleep_time", "1000"); + } + + if (getZkBaseSleepTime() <= 0) { + throw new ConfigException("zookeeper.base_sleep_time", "should be larger than 0"); + } + + if (props.getProperty("zookeeper.max_retries") == null) { + props.setProperty("zookeeper.max_retries", "10"); + } + + if (getZkMaxRetries() <= 0) { + throw new ConfigException("zookeeper.max_retries", "should be larger than 0"); } - ZK_TASKMANAGER_PATH = ZK_ROOT_PATH + "/taskmanager"; - ZK_MAX_JOB_ID_PATH = ZK_TASKMANAGER_PATH + "/max_job_id"; - ZK_CONNECTION_TIMEOUT = Integer.parseInt(prop.getProperty("zookeeper.connection_timeout", "5000")); - ZK_BASE_SLEEP_TIME = Integer.parseInt(prop.getProperty("zookeeper.base_sleep_time", "1000")); - ZK_MAX_RETRIES = Integer.parseInt(prop.getProperty("zookeeper.max_retries", "10")); - ZK_MAX_CONNECT_WAIT_TIME = Integer.parseInt(prop.getProperty("zookeeper.max_connect_waitTime", "30000")); + if (props.getProperty("zookeeper.max_connect_waitTime") == null) { + props.setProperty("zookeeper.max_connect_waitTime", "30000"); + } + + if (getZkMaxConnectWaitTime() <= 0) { + throw new ConfigException("zookeeper.max_connect_waitTime", "should be larger than 0"); + } - SPARK_MASTER = prop.getProperty("spark.master", "local[*]").toLowerCase(); - if (!SPARK_MASTER.startsWith("local")) { - if (!Arrays.asList("yarn", "yarn-cluster", "yarn-client", "k8s", "kubernetes").contains(SPARK_MASTER)) { + if (props.getProperty("spark.master") == null) { + props.setProperty("spark.master", "local[*]"); + } else { + props.setProperty("spark.master", props.getProperty("spark.master").toLowerCase()); + } + + if (!getSparkMaster().startsWith("local")) { + if (!Arrays.asList("yarn", "yarn-cluster", "yarn-client", "k8s", "kubernetes").contains(getSparkMaster())) { throw new ConfigException("spark.master", "should be local, yarn, yarn-cluster, yarn-client, k8s or kubernetes"); } } - boolean isLocal = SPARK_MASTER.startsWith("local"); - boolean isYarn = SPARK_MASTER.startsWith("yarn"); - boolean isYarnCluster = SPARK_MASTER.equals("yarn") || SPARK_MASTER.equals("yarn-cluster"); - SPARK_YARN_JARS = prop.getProperty("spark.yarn.jars", ""); - if (isLocal && !SPARK_YARN_JARS.isEmpty()) { - logger.warn("Ignore the config of spark.yarn.jars which is invalid for local mode"); + if (props.getProperty("spark.yarn.jars") == null) { + props.setProperty("spark.yarn.jars", ""); + } + + if (isYarn() && !getSparkYarnJars().isEmpty() && getSparkYarnJars().startsWith("file://")) { + throw new ConfigException("spark.yarn.jars", "should not use local filesystem for yarn mode"); } - if (isYarn) { - if (!SPARK_YARN_JARS.isEmpty() && SPARK_YARN_JARS.startsWith("file://")) { - throw new ConfigException("spark.yarn.jars", "should not use local filesystem for yarn mode"); + + + if (props.getProperty("spark.home", "").isEmpty()) { + if (System.getenv("SPARK_HOME") == null) { + throw new ConfigException("spark.home", "should set config 'spark.home' or environment variable 'SPARK_HOME'"); + } else { + logger.info("Use SPARK_HOME from environment variable: " + System.getenv("SPARK_HOME")); + props.setProperty("spark.home", System.getenv("SPARK_HOME")); } } - SPARK_HOME = firstNonEmpty(prop.getProperty("spark.home"), System.getenv("SPARK_HOME")); + String SPARK_HOME = firstNonEmpty(props.getProperty("spark.home"), System.getenv("SPARK_HOME")); // isEmpty checks null and empty - if(isEmpty(SPARK_HOME)) { + if (isEmpty(SPARK_HOME)) { throw new ConfigException("spark.home", "should set config 'spark.home' or environment variable 'SPARK_HOME'"); } + if (SPARK_HOME != null) { + props.setProperty("spark.home", SPARK_HOME); + } // TODO: Check if we can get spark-submit - PREFETCH_JOBID_NUM = Integer.parseInt(prop.getProperty("prefetch.jobid.num", "1")); - if (PREFETCH_JOBID_NUM < 1) { + if (props.getProperty("prefetch.jobid.num") == null) { + props.setProperty("prefetch.jobid.num", "1"); + } + + if (getPrefetchJobidNum() < 1) { throw new ConfigException("prefetch.jobid.num", "should be larger or equal to 1"); } - NAMENODE_URI = prop.getProperty("namenode.uri", ""); - if (!NAMENODE_URI.isEmpty()) { - logger.warn("Config of 'namenode.uri' will be deprecated later"); + if (props.getProperty("job.log.path") == null) { + props.setProperty("job.log.path", "../log/"); } - JOB_LOG_PATH = prop.getProperty("job.log.path", "../log/"); - if (JOB_LOG_PATH.isEmpty()) { - throw new ConfigException("job.log.path", "should not be null"); - } else { - if (JOB_LOG_PATH.startsWith("hdfs") || JOB_LOG_PATH.startsWith("s3")) { - throw new ConfigException("job.log.path", "only support local filesystem"); - } + if (getJobLogPath().startsWith("hdfs") || getJobLogPath().startsWith("s3")) { + throw new ConfigException("job.log.path", "only support local filesystem"); + } - File directory = new File(JOB_LOG_PATH); - if (!directory.exists()) { - logger.info("The log path does not exist, try to create directory: " + JOB_LOG_PATH); - boolean created = directory.mkdirs(); - if (created) { - throw new ConfigException("job.log.path", "fail to create log path"); - } + File jobLogDirectory = new File(getJobLogPath()); + if (!jobLogDirectory.exists()) { + logger.info("The log path does not exist, try to create directory: " + getJobLogPath()); + jobLogDirectory.mkdirs(); + if (!jobLogDirectory.exists()) { + throw new ConfigException("job.log.path", "fail to create log path: " + jobLogDirectory); } } - EXTERNAL_FUNCTION_DIR = prop.getProperty("external.function.dir", "./udf/"); - if (EXTERNAL_FUNCTION_DIR.isEmpty()) { - throw new ConfigException("external.function.dir", "should not be null"); - } else { - File directory = new File(EXTERNAL_FUNCTION_DIR); - if (!directory.exists()) { - logger.info("The external function dir does not exist, try to create directory: " - + EXTERNAL_FUNCTION_DIR); - boolean created = directory.mkdirs(); - if (created) { - logger.warn("Fail to create external function directory: " + EXTERNAL_FUNCTION_DIR); - } + if (props.getProperty("external.function.dir") == null) { + props.setProperty("external.function.dir", "./udf/"); + } + + File externalFunctionDir = new File(getExternalFunctionDir()); + if (!externalFunctionDir.exists()) { + logger.info("The external function dir does not exist, try to create directory: " + + getExternalFunctionDir()); + externalFunctionDir.mkdirs(); + if (!externalFunctionDir.exists()) { + throw new ConfigException("job.log.path", "fail to create external function path: " + externalFunctionDir); } } - TRACK_UNFINISHED_JOBS = Boolean.parseBoolean(prop.getProperty("track.unfinished.jobs", "true")); + if (props.getProperty("track.unfinished.jobs") == null) { + props.setProperty("track.unfinished.jobs", "true"); + } + + if (props.getProperty("job.tracker.interval") == null) { + props.setProperty("job.tracker.interval", "30"); + } + + if (getJobTrackerInterval() <= 0) { + throw new ConfigException("job.tracker.interval", "should be larger than 0"); + } - JOB_TRACKER_INTERVAL = Integer.parseInt(prop.getProperty("job.tracker.interval", "30")); - if (JOB_TRACKER_INTERVAL <= 0) { - throw new ConfigException("job.tracker.interval", "interval should be larger than 0"); + if (props.getProperty("spark.default.conf") == null) { + props.setProperty("spark.default.conf", ""); } - SPARK_DEFAULT_CONF = prop.getProperty("spark.default.conf", ""); - if (!SPARK_DEFAULT_CONF.isEmpty()) { - String[] defaultSparkConfs = TaskManagerConfig.SPARK_DEFAULT_CONF.split(";"); - for (String sparkConfMap: defaultSparkConfs) { + if (!getSparkDefaultConf().isEmpty()) { + String[] defaultSparkConfs = getSparkDefaultConf().split(";"); + for (String sparkConfMap : defaultSparkConfs) { if (!sparkConfMap.isEmpty()) { String[] kv = sparkConfMap.split("="); if (kv.length < 2) { @@ -205,64 +433,85 @@ public static void doParse() throws ConfigException { } } - SPARK_EVENTLOG_DIR = prop.getProperty("spark.eventLog.dir", ""); - if (!SPARK_EVENTLOG_DIR.isEmpty() && isYarn) { - // TODO: Check if we can use local filesystem with yarn-client mode - if (SPARK_EVENTLOG_DIR.startsWith("file://")) { + if (props.getProperty("spark.eventLog.dir") == null) { + props.setProperty("spark.eventLog.dir", ""); + } + + if (!getSparkEventlogDir().isEmpty() && isYarn()) { + if (getSparkEventlogDir().startsWith("file://")) { throw new ConfigException("spark.eventLog.dir", "should not use local filesystem for yarn mode"); } } - SPARK_YARN_MAXAPPATTEMPTS = Integer.parseInt(prop.getProperty("spark.yarn.maxAppAttempts", "1")); - if (SPARK_YARN_MAXAPPATTEMPTS < 1) { - throw new ConfigException("spark.yarn.maxAppAttempts", "should be larger or equal to 1"); + if (props.getProperty("spark.yarn.maxAppAttempts") == null) { + props.setProperty("spark.yarn.maxAppAttempts", "1"); } - OFFLINE_DATA_PREFIX = prop.getProperty("offline.data.prefix", "file:///tmp/openmldb_offline_storage/"); - if (OFFLINE_DATA_PREFIX.isEmpty()) { - throw new ConfigException("offline.data.prefix", "should not be null"); + if (getSparkYarnMaxappattempts() <= 0) { + throw new ConfigException("spark.yarn.maxAppAttempts", "should be larger than 0"); + } + + + if (props.getProperty("offline.data.prefix") == null) { + props.setProperty("offline.data.prefix", "file:///tmp/openmldb_offline_storage/"); + } + + if (getOfflineDataPrefix().isEmpty()) { + throw new ConfigException("offline.data.prefix", "should not be null"); } else { - if (isYarnCluster && OFFLINE_DATA_PREFIX.startsWith("file://") ) { - throw new ConfigException("offline.data.prefix", "should not use local filesystem for yarn mode"); + if (isYarn() || isK8s()) { + if (getOfflineDataPrefix().startsWith("file://")) { + throw new ConfigException("offline.data.prefix", "should not use local filesystem for yarn mode or k8s mode"); + } } } - BATCHJOB_JAR_PATH = prop.getProperty("batchjob.jar.path", ""); - if (BATCHJOB_JAR_PATH.isEmpty()) { - try { - BATCHJOB_JAR_PATH = BatchJobUtil.findLocalBatchJobJar(); - } catch (Exception e) { - throw new ConfigException("batchjob.jar.path", "config is null and fail to load default openmldb-batchjob jar"); + if (props.getProperty("batchjob.jar.path", "").isEmpty()) { + props.setProperty("batchjob.jar.path", BatchJobUtil.findLocalBatchJobJar()); + } + + if (isYarn() && getHadoopConfDir().isEmpty()) { + if (System.getenv("HADOOP_CONF_DIR") == null) { + throw new ConfigException("hadoop.conf.dir", "should set config 'hadoop.conf.dir' or environment variable 'HADOOP_CONF_DIR'"); + } else { + // TODO: Check if we can get core-site.xml + props.setProperty("hadoop.conf.dir", System.getenv("HADOOP_CONF_DIR")); } } // TODO(hw): need default root? - HADOOP_USER_NAME = firstNonEmpty(prop.getProperty("hadoop.user.name"), System.getenv("HADOOP_USER_NAME")); + String HADOOP_USER_NAME = firstNonEmpty(props.getProperty("hadoop.user.name"), System.getenv("HADOOP_USER_NAME")); + if (HADOOP_USER_NAME != null) { + props.setProperty("hadoop.user.name", HADOOP_USER_NAME); + } - HADOOP_CONF_DIR = firstNonEmpty(prop.getProperty("hadoop.conf.dir"), System.getenv("HADOOP_CONF_DIR")); - if (isYarn && isEmpty(HADOOP_CONF_DIR)) { + + String HADOOP_CONF_DIR = firstNonEmpty(props.getProperty("hadoop.conf.dir"), System.getenv("HADOOP_CONF_DIR")); + if (isYarn() && isEmpty(HADOOP_CONF_DIR)) { throw new ConfigException("hadoop.conf.dir", "should set config 'hadoop.conf.dir' or environment variable 'HADOOP_CONF_DIR'"); } - // TODO: Check if we can get core-site.xml + if (HADOOP_CONF_DIR != null) { + props.setProperty("hadoop.conf.dir", HADOOP_CONF_DIR); + } - ENABLE_HIVE_SUPPORT = Boolean.parseBoolean(prop.getProperty("enable.hive.support", "true")); - BATCH_JOB_RESULT_MAX_WAIT_TIME = Long.parseLong(prop.getProperty("batch.job.result.max.wait.time", "600000")); // 10min + if (props.getProperty("enable.hive.support") == null) { + props.setProperty("enable.hive.support", "true"); + } - K8S_HADOOP_CONFIGMAP_NAME = prop.getProperty("k8s.hadoop.configmap", "hadoop-config"); + if (props.getProperty("batch.job.result.max.wait.time") == null) { + props.setProperty("batch.job.result.max.wait.time", "600000"); + } - K8S_MOUNT_LOCAL_PATH = prop.getProperty("k8s.mount.local.path", "/tmp"); - } + if (props.getProperty("k8s.hadoop.configmap") == null) { + props.setProperty("k8s.hadoop.configmap", "hadoop-config"); + } - public static boolean isK8s() throws ConfigException { - parse(); - return SPARK_MASTER.equals("k8s") || SPARK_MASTER.equals("kubernetes"); + if (props.getProperty("k8s.mount.local.path") == null) { + props.setProperty("k8s.mount.local.path", "/tmp"); + } } - public static boolean isYarnCluster() throws ConfigException { - parse(); - return SPARK_MASTER.equals("yarn") || SPARK_MASTER.equals("yarn-cluster"); - } // ref org.apache.spark.launcher.CommandBuilderUtils public static String firstNonEmpty(String... strings) { @@ -273,7 +522,13 @@ public static String firstNonEmpty(String... strings) { } return null; } + + public static boolean isEmpty(String s) { return s == null || s.isEmpty(); } + + + } + diff --git a/java/openmldb-taskmanager/src/main/java/com/_4paradigm/openmldb/taskmanager/dao/JobIdGenerator.java b/java/openmldb-taskmanager/src/main/java/com/_4paradigm/openmldb/taskmanager/dao/JobIdGenerator.java index 2e7ba638703..e9cd0c5014f 100644 --- a/java/openmldb-taskmanager/src/main/java/com/_4paradigm/openmldb/taskmanager/dao/JobIdGenerator.java +++ b/java/openmldb-taskmanager/src/main/java/com/_4paradigm/openmldb/taskmanager/dao/JobIdGenerator.java @@ -28,28 +28,29 @@ public class JobIdGenerator { static { try { zkClient = new ZKClient(ZKConfig.builder() - .cluster(TaskManagerConfig.ZK_CLUSTER) - .namespace(TaskManagerConfig.ZK_ROOT_PATH) - .sessionTimeout(TaskManagerConfig.ZK_SESSION_TIMEOUT) - .baseSleepTime(TaskManagerConfig.ZK_BASE_SLEEP_TIME) - .connectionTimeout(TaskManagerConfig.ZK_CONNECTION_TIMEOUT) - .maxConnectWaitTime(TaskManagerConfig.ZK_MAX_CONNECT_WAIT_TIME) - .maxRetries(TaskManagerConfig.ZK_MAX_RETRIES) + .cluster(TaskManagerConfig.getZkCluster()) + .namespace(TaskManagerConfig.getZkRootPath()) + .sessionTimeout(TaskManagerConfig.getZkSessionTimeout()) + .baseSleepTime(TaskManagerConfig.getZkBaseSleepTime()) + .connectionTimeout(TaskManagerConfig.getZkConnectionTimeout()) + .maxConnectWaitTime(TaskManagerConfig.getZkMaxConnectWaitTime()) + .maxRetries(TaskManagerConfig.getZkMaxRetries()) .build()); zkClient.connect(); + // Initialize zk nodes - zkClient.createNode(TaskManagerConfig.ZK_ROOT_PATH, "".getBytes()); - zkClient.createNode(TaskManagerConfig.ZK_TASKMANAGER_PATH, "".getBytes()); + zkClient.createNode(TaskManagerConfig.getZkRootPath(), "".getBytes()); + zkClient.createNode(TaskManagerConfig.getZkTaskmanagerPath(), "".getBytes()); int lastMaxJobId = 0; - if (zkClient.checkExists(TaskManagerConfig.ZK_MAX_JOB_ID_PATH)) { + if (zkClient.checkExists(TaskManagerConfig.getZkMaxJobIdPath())) { // Get last max job id from zk - lastMaxJobId = Integer.parseInt(zkClient.getNodeValue(TaskManagerConfig.ZK_MAX_JOB_ID_PATH)); + lastMaxJobId = Integer.parseInt(zkClient.getNodeValue(TaskManagerConfig.getZkMaxJobIdPath())); } currentJobId = lastMaxJobId; - maxJobId = lastMaxJobId + TaskManagerConfig.PREFETCH_JOBID_NUM; + maxJobId = lastMaxJobId + TaskManagerConfig.getPrefetchJobidNum(); // set max job id in zk - zkClient.setNodeValue(TaskManagerConfig.ZK_MAX_JOB_ID_PATH, String.valueOf(maxJobId).getBytes()); + zkClient.setNodeValue(TaskManagerConfig.getZkMaxJobIdPath(), String.valueOf(maxJobId).getBytes()); } catch (Exception e) { zkClient = null; @@ -67,8 +68,8 @@ public static int getUniqueId() throws Exception { currentJobId += 1; if (currentJobId > maxJobId) { // Update zk before returning job id - maxJobId += TaskManagerConfig.PREFETCH_JOBID_NUM; - zkClient.setNodeValue(TaskManagerConfig.ZK_MAX_JOB_ID_PATH, String.valueOf(maxJobId).getBytes()); + maxJobId += TaskManagerConfig.getPrefetchJobidNum(); + zkClient.setNodeValue(TaskManagerConfig.getZkMaxJobIdPath(), String.valueOf(maxJobId).getBytes()); } return currentJobId; } diff --git a/java/openmldb-taskmanager/src/main/java/com/_4paradigm/openmldb/taskmanager/server/JobResultSaver.java b/java/openmldb-taskmanager/src/main/java/com/_4paradigm/openmldb/taskmanager/server/JobResultSaver.java index 6f6c77482e5..6bb1f310b2f 100644 --- a/java/openmldb-taskmanager/src/main/java/com/_4paradigm/openmldb/taskmanager/server/JobResultSaver.java +++ b/java/openmldb-taskmanager/src/main/java/com/_4paradigm/openmldb/taskmanager/server/JobResultSaver.java @@ -105,7 +105,7 @@ public boolean saveFile(int resultId, String jsonData) { return true; } // save to /tmp_result// - String savePath = String.format("%s/tmp_result/%d", TaskManagerConfig.JOB_LOG_PATH, resultId); + String savePath = String.format("%s/tmp_result/%d", TaskManagerConfig.getJobLogPath(), resultId); synchronized (this) { File saveP = new File(savePath); if (!saveP.exists()) { @@ -151,7 +151,7 @@ public String readResult(int resultId, long timeoutMs) throws InterruptedExcepti } String output = ""; // all finished, read csv from savePath - String savePath = String.format("%s/tmp_result/%d", TaskManagerConfig.JOB_LOG_PATH, resultId); + String savePath = String.format("%s/tmp_result/%d", TaskManagerConfig.getJobLogPath(), resultId); File saveP = new File(savePath); // If saveP not exists, means no real result saved. But it may use a uncleaned // path, whether read result succeed or not, we should delete it. @@ -225,7 +225,7 @@ public void reset() throws IOException { synchronized (idStatus) { Collections.fill(idStatus, 0); } - String tmpResultDir = String.format("%s/tmp_result", TaskManagerConfig.JOB_LOG_PATH); + String tmpResultDir = String.format("%s/tmp_result", TaskManagerConfig.getJobLogPath()); // delete anyway FileUtils.forceDelete(new File(tmpResultDir)); } diff --git a/java/openmldb-taskmanager/src/main/java/com/_4paradigm/openmldb/taskmanager/server/TaskManagerServer.java b/java/openmldb-taskmanager/src/main/java/com/_4paradigm/openmldb/taskmanager/server/TaskManagerServer.java index 376ea41eee6..0a75c2e37b2 100644 --- a/java/openmldb-taskmanager/src/main/java/com/_4paradigm/openmldb/taskmanager/server/TaskManagerServer.java +++ b/java/openmldb-taskmanager/src/main/java/com/_4paradigm/openmldb/taskmanager/server/TaskManagerServer.java @@ -18,7 +18,6 @@ import com._4paradigm.openmldb.taskmanager.config.ConfigException; import com._4paradigm.openmldb.taskmanager.tracker.JobTrackerService; -import com._4paradigm.openmldb.taskmanager.util.VersionUtil; import com._4paradigm.openmldb.taskmanager.zk.FailoverWatcher; import lombok.extern.slf4j.Slf4j; import com._4paradigm.openmldb.taskmanager.config.TaskManagerConfig; @@ -45,7 +44,7 @@ public class TaskManagerServer { * @throws ConfigException if config file does not exist or some configs are incorrect. */ public TaskManagerServer() throws ConfigException { - TaskManagerConfig.parse(); + TaskManagerConfig.print(); } /** @@ -69,7 +68,7 @@ public void start(Boolean blocking) throws ConfigException, IOException, Interru logger.info("The server runs and prepares for leader election"); if (failoverWatcher.blockUntilActive()) { logger.info("The server becomes active master and prepare to do business logic"); - if (TaskManagerConfig.TRACK_UNFINISHED_JOBS) { + if (TaskManagerConfig.getTrackUnfinishedJobs()) { // Start threads to track unfinished jobs JobTrackerService.startTrackerThreads(); } @@ -97,14 +96,14 @@ public void startRpcServer(Boolean blocking) throws ConfigException, Interrupted RpcServerOptions options = new RpcServerOptions(); options.setReceiveBufferSize(64 * 1024 * 1024); options.setSendBufferSize(64 * 1024 * 1024); - options.setIoThreadNum(TaskManagerConfig.IO_THREAD); - options.setWorkThreadNum(TaskManagerConfig.WORKER_THREAD); - options.setKeepAliveTime(TaskManagerConfig.CHANNEL_KEEP_ALIVE_TIME); - rpcServer = new RpcServer(TaskManagerConfig.PORT, options); + options.setIoThreadNum(TaskManagerConfig.getServerIoThreads()); + options.setWorkThreadNum(TaskManagerConfig.getServerWorkerThreads()); + options.setKeepAliveTime(TaskManagerConfig.getChannelKeepAliveTime()); + rpcServer = new RpcServer(TaskManagerConfig.getServerPort(), options); rpcServer.registerService(new TaskManagerImpl()); rpcServer.start(); - log.info("Start TaskManager on {} with worker thread number {}", TaskManagerConfig.PORT, - TaskManagerConfig.WORKER_THREAD); + log.info("Start TaskManager on {} with worker thread number {}", TaskManagerConfig.getServerPort(), + TaskManagerConfig.getServerWorkerThreads()); if (blocking) { // make server keep running diff --git a/java/openmldb-taskmanager/src/main/java/com/_4paradigm/openmldb/taskmanager/server/impl/TaskManagerImpl.java b/java/openmldb-taskmanager/src/main/java/com/_4paradigm/openmldb/taskmanager/server/impl/TaskManagerImpl.java index 3a06b96b2c2..6fd43d4200c 100644 --- a/java/openmldb-taskmanager/src/main/java/com/_4paradigm/openmldb/taskmanager/server/impl/TaskManagerImpl.java +++ b/java/openmldb-taskmanager/src/main/java/com/_4paradigm/openmldb/taskmanager/server/impl/TaskManagerImpl.java @@ -73,17 +73,17 @@ public TaskManagerImpl() throws InterruptedException, ConfigException { */ private void initExternalFunction() throws InterruptedException { ZKClient zkClient = new ZKClient(ZKConfig.builder() - .cluster(TaskManagerConfig.ZK_CLUSTER) - .namespace(TaskManagerConfig.ZK_ROOT_PATH) - .sessionTimeout(TaskManagerConfig.ZK_SESSION_TIMEOUT) - .baseSleepTime(TaskManagerConfig.ZK_BASE_SLEEP_TIME) - .connectionTimeout(TaskManagerConfig.ZK_CONNECTION_TIMEOUT) - .maxConnectWaitTime(TaskManagerConfig.ZK_MAX_CONNECT_WAIT_TIME) - .maxRetries(TaskManagerConfig.ZK_MAX_RETRIES) + .cluster(TaskManagerConfig.getZkCluster()) + .namespace(TaskManagerConfig.getZkRootPath()) + .sessionTimeout(TaskManagerConfig.getZkSessionTimeout()) + .baseSleepTime(TaskManagerConfig.getZkBaseSleepTime()) + .connectionTimeout(TaskManagerConfig.getZkConnectionTimeout()) + .maxConnectWaitTime(TaskManagerConfig.getZkMaxConnectWaitTime()) + .maxRetries(TaskManagerConfig.getZkMaxRetries()) .build()); zkClient.connect(); - String funPath = TaskManagerConfig.ZK_ROOT_PATH + "/data/function"; + String funPath = TaskManagerConfig.getZkRootPath() + "/data/function"; try { List funNames = zkClient.getChildren(funPath); for (String name : funNames) { @@ -220,7 +220,7 @@ public TaskManager.RunBatchSqlResponse RunBatchSql(TaskManager.RunBatchSqlReques // HOST can't be 0.0.0.0 if distributed or spark is not local confMap.put("spark.openmldb.savejobresult.http", String.format("http://%s:%d/openmldb.taskmanager.TaskManagerServer/SaveJobResult", - TaskManagerConfig.HOST, TaskManagerConfig.PORT)); + TaskManagerConfig.getServerHost(), TaskManagerConfig.getServerPort())); // we can't get spark job id here, so we use JobResultSaver id, != spark job id // if too much running jobs to save result, throw exception int resultId = jobResultSaver.genResultId(); @@ -234,7 +234,7 @@ public TaskManager.RunBatchSqlResponse RunBatchSql(TaskManager.RunBatchSqlReques if (finalJobInfo.isSuccess()) { // wait for all files of result saved and read them, large timeout // TODO: Test for K8S backend - String output = jobResultSaver.readResult(resultId, TaskManagerConfig.BATCH_JOB_RESULT_MAX_WAIT_TIME); + String output = jobResultSaver.readResult(resultId, TaskManagerConfig.getBatchJobResultMaxWaitTime()); return TaskManager.RunBatchSqlResponse.newBuilder().setCode(StatusCode.SUCCESS).setOutput(output) .build(); } else { @@ -253,7 +253,7 @@ public TaskManager.RunBatchSqlResponse RunBatchSql(TaskManager.RunBatchSqlReques // rpc max time is CHANNEL_KEEP_ALIVE_TIME, so we don't need to wait too long private JobInfo busyWaitJobInfo(int jobId, int waitSeconds) throws InterruptedException { long maxWaitEnd = System.currentTimeMillis() - + (waitSeconds == 0 ? TaskManagerConfig.CHANNEL_KEEP_ALIVE_TIME : waitSeconds) * 1000; + + (waitSeconds == 0 ? TaskManagerConfig.getChannelKeepAliveTime() : waitSeconds) * 1000; while (System.currentTimeMillis() < maxWaitEnd) { Option info = JobInfoManager.getJob(jobId); if (info.isEmpty()) { diff --git a/java/openmldb-taskmanager/src/main/java/com/_4paradigm/openmldb/taskmanager/udf/ExternalFunctionManager.java b/java/openmldb-taskmanager/src/main/java/com/_4paradigm/openmldb/taskmanager/udf/ExternalFunctionManager.java index 00bc94e9fb4..2ee03a8742a 100644 --- a/java/openmldb-taskmanager/src/main/java/com/_4paradigm/openmldb/taskmanager/udf/ExternalFunctionManager.java +++ b/java/openmldb-taskmanager/src/main/java/com/_4paradigm/openmldb/taskmanager/udf/ExternalFunctionManager.java @@ -32,7 +32,7 @@ public class ExternalFunctionManager { static private Map nameFileMap = new ConcurrentHashMap<>(); static public String getLibraryFilePath(String libraryFileName) { - return Paths.get(TaskManagerConfig.EXTERNAL_FUNCTION_DIR, libraryFileName).toString(); + return Paths.get(TaskManagerConfig.getExternalFunctionDir(), libraryFileName).toString(); } static public void addFunction(String fnName, String libraryFileName) throws Exception { diff --git a/java/openmldb-taskmanager/src/main/java/com/_4paradigm/openmldb/taskmanager/zk/FailoverWatcher.java b/java/openmldb-taskmanager/src/main/java/com/_4paradigm/openmldb/taskmanager/zk/FailoverWatcher.java index e2e9d8560d9..69c7689bd45 100644 --- a/java/openmldb-taskmanager/src/main/java/com/_4paradigm/openmldb/taskmanager/zk/FailoverWatcher.java +++ b/java/openmldb-taskmanager/src/main/java/com/_4paradigm/openmldb/taskmanager/zk/FailoverWatcher.java @@ -51,13 +51,13 @@ public class FailoverWatcher implements Watcher { */ public FailoverWatcher() throws IOException { - baseZnode = TaskManagerConfig.ZK_ROOT_PATH + "/taskmanager"; + baseZnode = TaskManagerConfig.getZkRootPath() + "/taskmanager"; masterZnode = baseZnode + "/leader"; - zkQuorum = TaskManagerConfig.ZK_CLUSTER; - sessionTimeout = TaskManagerConfig.ZK_SESSION_TIMEOUT; + zkQuorum = TaskManagerConfig.getZkCluster(); + sessionTimeout = TaskManagerConfig.getZkSessionTimeout(); connectRetryTimes = 3; - String serverHost = TaskManagerConfig.HOST; - int serverPort = TaskManagerConfig.PORT; + String serverHost = TaskManagerConfig.getServerHost(); + int serverPort = TaskManagerConfig.getServerPort(); hostPort = new HostPort(serverHost, serverPort); connectZooKeeper(); @@ -91,7 +91,7 @@ protected void connectZooKeeper() throws IOException { */ protected void initZnode() { try { - ZooKeeperUtil.createAndFailSilent(this, TaskManagerConfig.ZK_ROOT_PATH); + ZooKeeperUtil.createAndFailSilent(this, TaskManagerConfig.getZkRootPath()); ZooKeeperUtil.createAndFailSilent(this, baseZnode); } catch (Exception e) { LOG.fatal("Error to create znode " + baseZnode diff --git a/java/openmldb-taskmanager/src/main/resources/taskmanager.properties b/java/openmldb-taskmanager/src/main/resources/taskmanager.properties index ce13bd8be10..e3c41a4b5f8 100644 --- a/java/openmldb-taskmanager/src/main/resources/taskmanager.properties +++ b/java/openmldb-taskmanager/src/main/resources/taskmanager.properties @@ -27,6 +27,5 @@ spark.default.conf= spark.eventLog.dir= spark.yarn.maxAppAttempts=1 batchjob.jar.path= -namenode.uri= offline.data.prefix=file:///tmp/openmldb_offline_storage/ hadoop.conf.dir= diff --git a/java/openmldb-taskmanager/src/main/scala/com/_4paradigm/openmldb/taskmanager/JobInfoManager.scala b/java/openmldb-taskmanager/src/main/scala/com/_4paradigm/openmldb/taskmanager/JobInfoManager.scala index e16bed38cc3..10f958d4472 100644 --- a/java/openmldb-taskmanager/src/main/scala/com/_4paradigm/openmldb/taskmanager/JobInfoManager.scala +++ b/java/openmldb-taskmanager/src/main/scala/com/_4paradigm/openmldb/taskmanager/JobInfoManager.scala @@ -20,6 +20,7 @@ import com._4paradigm.openmldb.sdk.SdkOption import com._4paradigm.openmldb.sdk.impl.SqlClusterExecutor import com._4paradigm.openmldb.taskmanager.config.TaskManagerConfig import com._4paradigm.openmldb.taskmanager.dao.{JobIdGenerator, JobInfo} +import com._4paradigm.openmldb.taskmanager.util.HdfsUtil import com._4paradigm.openmldb.taskmanager.yarn.YarnClientUtil import org.slf4j.LoggerFactory import org.apache.hadoop.fs.{FileSystem, LocalFileSystem, Path} @@ -42,8 +43,8 @@ object JobInfoManager { private val JOB_INFO_TABLE_NAME = "JOB_INFO" private val option = new SdkOption - option.setZkCluster(TaskManagerConfig.ZK_CLUSTER) - option.setZkPath(TaskManagerConfig.ZK_ROOT_PATH) + option.setZkCluster(TaskManagerConfig.getZkCluster) + option.setZkPath(TaskManagerConfig.getZkRootPath) val sqlExecutor = new SqlClusterExecutor(option) sqlExecutor.executeSQL("", "set @@execute_mode='online';") @@ -52,7 +53,7 @@ object JobInfoManager { val startTime = new java.sql.Timestamp(Calendar.getInstance.getTime().getTime()) val initialState = "Submitted" val parameter = if (args != null && args.length>0) args.mkString(",") else "" - val cluster = sparkConf.getOrElse("spark.master", TaskManagerConfig.SPARK_MASTER) + val cluster = sparkConf.getOrElse("spark.master", TaskManagerConfig.getSparkMaster) // TODO: Parse if run in yarn or local val jobInfo = new JobInfo(jobId, jobType, initialState, startTime, null, parameter, cluster, "", "") @@ -210,12 +211,8 @@ object JobInfoManager { FileUtils.deleteDirectory(dir) } else if (filePath.startsWith("hdfs://")) { - val conf = new Configuration(); - // TODO: Get namenode uri from config file - val namenodeUri = TaskManagerConfig.NAMENODE_URI - val hdfs = FileSystem.get(URI.create(s"hdfs://$namenodeUri"), conf) - hdfs.delete(new Path(filePath), true) - + logger.info(s"Try to delete the HDFS path ${filePath}") + HdfsUtil.deleteHdfsDir(filePath) } else { throw new Exception(s"Get unsupported file path: $filePath") } diff --git a/java/openmldb-taskmanager/src/main/scala/com/_4paradigm/openmldb/taskmanager/LogManager.scala b/java/openmldb-taskmanager/src/main/scala/com/_4paradigm/openmldb/taskmanager/LogManager.scala index a700f69145c..2e8fcc7a330 100644 --- a/java/openmldb-taskmanager/src/main/scala/com/_4paradigm/openmldb/taskmanager/LogManager.scala +++ b/java/openmldb-taskmanager/src/main/scala/com/_4paradigm/openmldb/taskmanager/LogManager.scala @@ -26,11 +26,11 @@ object LogManager { private val logger = LoggerFactory.getLogger(this.getClass) def getJobLogFile(id: Int): File = { - Paths.get(TaskManagerConfig.JOB_LOG_PATH, s"job_${id}.log").toFile + Paths.get(TaskManagerConfig.getJobLogPath, s"job_${id}.log").toFile } def getJobErrorLogFile(id: Int): File = { - Paths.get(TaskManagerConfig.JOB_LOG_PATH, s"job_${id}_error.log").toFile + Paths.get(TaskManagerConfig.getJobLogPath, s"job_${id}_error.log").toFile } def getFileContent(inputFile: File): String = { diff --git a/java/openmldb-taskmanager/src/main/scala/com/_4paradigm/openmldb/taskmanager/k8s/K8sJobManager.scala b/java/openmldb-taskmanager/src/main/scala/com/_4paradigm/openmldb/taskmanager/k8s/K8sJobManager.scala index 2393a3833a3..b9985a263b0 100644 --- a/java/openmldb-taskmanager/src/main/scala/com/_4paradigm/openmldb/taskmanager/k8s/K8sJobManager.scala +++ b/java/openmldb-taskmanager/src/main/scala/com/_4paradigm/openmldb/taskmanager/k8s/K8sJobManager.scala @@ -56,7 +56,7 @@ object K8sJobManager { val finalSparkConf: mutable.Map[String, String] = mutable.Map(sparkConf.toSeq: _*) - val defaultSparkConfs = TaskManagerConfig.SPARK_DEFAULT_CONF.split(";") + val defaultSparkConfs = TaskManagerConfig.getSparkDefaultConf.split(";") defaultSparkConfs.map(sparkConf => { if (sparkConf.nonEmpty) { val kvList = sparkConf.split("=") @@ -66,36 +66,36 @@ object K8sJobManager { } }) - if (TaskManagerConfig.SPARK_EVENTLOG_DIR.nonEmpty) { + if (TaskManagerConfig.getSparkEventlogDir.nonEmpty) { finalSparkConf.put("spark.eventLog.enabled", "true") - finalSparkConf.put("spark.eventLog.dir", TaskManagerConfig.SPARK_EVENTLOG_DIR) + finalSparkConf.put("spark.eventLog.dir", TaskManagerConfig.getSparkEventlogDir) } // Set ZooKeeper config for openmldb-batch jobs - if (TaskManagerConfig.ZK_CLUSTER.nonEmpty && TaskManagerConfig.ZK_ROOT_PATH.nonEmpty) { - finalSparkConf.put("spark.openmldb.zk.cluster", TaskManagerConfig.ZK_CLUSTER) - finalSparkConf.put("spark.openmldb.zk.root.path", TaskManagerConfig.ZK_ROOT_PATH) + if (TaskManagerConfig.getZkCluster.nonEmpty && TaskManagerConfig.getZkRootPath.nonEmpty) { + finalSparkConf.put("spark.openmldb.zk.cluster", TaskManagerConfig.getZkCluster) + finalSparkConf.put("spark.openmldb.zk.root.path", TaskManagerConfig.getZkRootPath) } if (defaultDb.nonEmpty) { finalSparkConf.put("spark.openmldb.default.db", defaultDb) } - if (TaskManagerConfig.OFFLINE_DATA_PREFIX.nonEmpty) { - finalSparkConf.put("spark.openmldb.offline.data.prefix", TaskManagerConfig.OFFLINE_DATA_PREFIX) + if (TaskManagerConfig.getOfflineDataPrefix.nonEmpty) { + finalSparkConf.put("spark.openmldb.offline.data.prefix", TaskManagerConfig.getOfflineDataPrefix) } // Set external function dir for offline jobs - val absoluteExternalFunctionDir = if (TaskManagerConfig.EXTERNAL_FUNCTION_DIR.startsWith("/")) { - TaskManagerConfig.EXTERNAL_FUNCTION_DIR + val absoluteExternalFunctionDir = if (TaskManagerConfig.getExternalFunctionDir.startsWith("/")) { + TaskManagerConfig.getExternalFunctionDir } else { // TODO: The current path is incorrect if running in IDE, please set `external.function.dir` with absolute path // Concat to generate absolute path - Paths.get(Paths.get(".").toAbsolutePath.toString, TaskManagerConfig.EXTERNAL_FUNCTION_DIR).toString + Paths.get(Paths.get(".").toAbsolutePath.toString, TaskManagerConfig.getExternalFunctionDir).toString } finalSparkConf.put("spark.openmldb.taskmanager.external.function.dir", absoluteExternalFunctionDir) - if(TaskManagerConfig.ENABLE_HIVE_SUPPORT) { + if(TaskManagerConfig.getEnableHiveSupport) { finalSparkConf.put("spark.sql.catalogImplementation", "hive") } @@ -107,7 +107,7 @@ object K8sJobManager { mainJarFile = "local:///opt/spark/jars/openmldb-batchjob-0.7.2-SNAPSHOT.jar", arguments = args, sparkConf = finalSparkConf.toMap, - mountLocalPath = TaskManagerConfig.K8S_MOUNT_LOCAL_PATH + mountLocalPath = TaskManagerConfig.getK8sMountLocalPath ) manager.submitJob(jobConfig) @@ -170,7 +170,7 @@ class K8sJobManager(val namespace:String = "default", | type: Never | env: | - name: SPARK_USER - | value: ${TaskManagerConfig.HADOOP_USER_NAME} + | value: ${TaskManagerConfig.getHadoopUserName} | volumes: | - name: host-local | hostPath: @@ -178,7 +178,7 @@ class K8sJobManager(val namespace:String = "default", | type: Directory | - name: hadoop-config | configMap: - | name: ${TaskManagerConfig.K8S_HADOOP_CONFIGMAP_NAME} + | name: ${TaskManagerConfig.getK8sHadoopConfigmapName} | driver: | cores: ${jobConfig.driverCores} | memory: "${jobConfig.driverMemory}" @@ -194,7 +194,7 @@ class K8sJobManager(val namespace:String = "default", | - name: HADOOP_CONF_DIR | value: /etc/hadoop/conf | - name: HADOOP_USER_NAME - | value: ${TaskManagerConfig.HADOOP_USER_NAME} + | value: ${TaskManagerConfig.getHadoopUserName} | executor: | cores: ${jobConfig.executorCores} | instances: ${jobConfig.executorNum} @@ -210,7 +210,7 @@ class K8sJobManager(val namespace:String = "default", | - name: HADOOP_CONF_DIR | value: /etc/hadoop/conf | - name: HADOOP_USER_NAME - | value: ${TaskManagerConfig.HADOOP_USER_NAME} + | value: ${TaskManagerConfig.getHadoopUserName} """.stripMargin // Create a CustomResourceDefinitionContext for the SparkApplication diff --git a/java/openmldb-taskmanager/src/main/scala/com/_4paradigm/openmldb/taskmanager/spark/SparkJobManager.scala b/java/openmldb-taskmanager/src/main/scala/com/_4paradigm/openmldb/taskmanager/spark/SparkJobManager.scala index 8d2410fd13a..bc8c5dfebbe 100644 --- a/java/openmldb-taskmanager/src/main/scala/com/_4paradigm/openmldb/taskmanager/spark/SparkJobManager.scala +++ b/java/openmldb-taskmanager/src/main/scala/com/_4paradigm/openmldb/taskmanager/spark/SparkJobManager.scala @@ -36,38 +36,40 @@ object SparkJobManager { * @return the SparkLauncher object */ def createSparkLauncher(mainClass: String): SparkLauncher = { + + + val launcher = new SparkLauncher() + .setAppResource(TaskManagerConfig.getBatchjobJarPath) + .setMainClass(mainClass) + + if (TaskManagerConfig.getSparkHome != null && TaskManagerConfig.getSparkHome.nonEmpty) { + launcher.setSparkHome(TaskManagerConfig.getSparkHome) + } + val env: java.util.Map[String, String] = new java.util.HashMap[String, String] // config may empty, need check - if (!TaskManagerConfig.isEmpty(TaskManagerConfig.HADOOP_CONF_DIR)) { - env.put("HADOOP_CONF_DIR", TaskManagerConfig.HADOOP_CONF_DIR) - } - // env.put("YARN_CONF_DIR", TaskManagerConfig.HADOOP_CONF_DIR) // unused now - if (!TaskManagerConfig.isEmpty(TaskManagerConfig.HADOOP_USER_NAME)){ - env.put("HADOOP_USER_NAME", TaskManagerConfig.HADOOP_USER_NAME) + if (TaskManagerConfig.getHadoopConfDir != null && TaskManagerConfig.getHadoopConfDir.nonEmpty) { + env.put("HADOOP_CONF_DIR", TaskManagerConfig.getHadoopConfDir) } - - val launcher = new SparkLauncher(env) - .setAppResource(TaskManagerConfig.BATCHJOB_JAR_PATH) - .setMainClass(mainClass) - if (!TaskManagerConfig.isEmpty(TaskManagerConfig.SPARK_HOME)) { - launcher.setSparkHome(TaskManagerConfig.SPARK_HOME) + if (TaskManagerConfig.getHadoopUserName != null && TaskManagerConfig.getHadoopUserName.nonEmpty){ + env.put("HADOOP_USER_NAME", TaskManagerConfig.getHadoopUserName) } - if (TaskManagerConfig.SPARK_MASTER.toLowerCase.startsWith("local")) { - launcher.setMaster(TaskManagerConfig.SPARK_MASTER) + if (TaskManagerConfig.getSparkMaster.startsWith("local")) { + launcher.setMaster(TaskManagerConfig.getSparkMaster) } else { - TaskManagerConfig.SPARK_MASTER.toLowerCase match { + TaskManagerConfig.getSparkMaster.toLowerCase match { case "yarn" | "yarn-cluster" => launcher.setMaster("yarn").setDeployMode("cluster") case "yarn-client" => launcher.setMaster("yarn").setDeployMode("client") - case _ => throw new Exception(s"Unsupported Spark master ${TaskManagerConfig.SPARK_MASTER}") + case _ => throw new Exception(s"Unsupported Spark master ${TaskManagerConfig.getSparkMaster}") } } - - if (!TaskManagerConfig.isEmpty(TaskManagerConfig.SPARK_YARN_JARS)) { - launcher.setConf("spark.yarn.jars", TaskManagerConfig.SPARK_YARN_JARS) + + if (TaskManagerConfig.getSparkYarnJars != null && TaskManagerConfig.getSparkYarnJars.nonEmpty) { + launcher.setConf("spark.yarn.jars", TaskManagerConfig.getSparkYarnJars) } launcher @@ -95,17 +97,17 @@ object SparkJobManager { // TODO: Avoid using zh_CN to load openmldb jsdk so - if (TaskManagerConfig.SPARK_EVENTLOG_DIR.nonEmpty) { + if (TaskManagerConfig.getSparkEventlogDir.nonEmpty) { launcher.setConf("spark.eventLog.enabled", "true") - launcher.setConf("spark.eventLog.dir", TaskManagerConfig.SPARK_EVENTLOG_DIR) + launcher.setConf("spark.eventLog.dir", TaskManagerConfig.getSparkEventlogDir) } - if (TaskManagerConfig.SPARK_YARN_MAXAPPATTEMPTS >= 1 ) { - launcher.setConf("spark.yarn.maxAppAttempts", TaskManagerConfig.SPARK_YARN_MAXAPPATTEMPTS.toString) + if (TaskManagerConfig.getSparkYarnMaxappattempts >= 1 ) { + launcher.setConf("spark.yarn.maxAppAttempts", TaskManagerConfig.getSparkYarnMaxappattempts.toString) } // Set default Spark conf by TaskManager configuration file - val defaultSparkConfs = TaskManagerConfig.SPARK_DEFAULT_CONF.split(";") + val defaultSparkConfs = TaskManagerConfig.getSparkDefaultConf.split(";") defaultSparkConfs.map(sparkConf => { if (sparkConf.nonEmpty) { val kvList = sparkConf.split("=") @@ -116,9 +118,9 @@ object SparkJobManager { }) // Set ZooKeeper config for openmldb-batch jobs - if (TaskManagerConfig.ZK_CLUSTER.nonEmpty && TaskManagerConfig.ZK_ROOT_PATH.nonEmpty) { - launcher.setConf("spark.openmldb.zk.cluster", TaskManagerConfig.ZK_CLUSTER) - launcher.setConf("spark.openmldb.zk.root.path", TaskManagerConfig.ZK_ROOT_PATH) + if (TaskManagerConfig.getZkCluster.nonEmpty && TaskManagerConfig.getZkRootPath.nonEmpty) { + launcher.setConf("spark.openmldb.zk.cluster", TaskManagerConfig.getZkCluster) + launcher.setConf("spark.openmldb.zk.root.path", TaskManagerConfig.getZkRootPath) } // Set ad-hoc Spark configuration @@ -126,17 +128,17 @@ object SparkJobManager { launcher.setConf("spark.openmldb.default.db", defaultDb) } - if (TaskManagerConfig.OFFLINE_DATA_PREFIX.nonEmpty) { - launcher.setConf("spark.openmldb.offline.data.prefix", TaskManagerConfig.OFFLINE_DATA_PREFIX) + if (TaskManagerConfig.getOfflineDataPrefix.nonEmpty) { + launcher.setConf("spark.openmldb.offline.data.prefix", TaskManagerConfig.getOfflineDataPrefix) } // Set external function dir for offline jobs - val absoluteExternalFunctionDir = if (TaskManagerConfig.EXTERNAL_FUNCTION_DIR.startsWith("/")) { - TaskManagerConfig.EXTERNAL_FUNCTION_DIR + val absoluteExternalFunctionDir = if (TaskManagerConfig.getExternalFunctionDir.startsWith("/")) { + TaskManagerConfig.getExternalFunctionDir } else { // TODO: The current path is incorrect if running in IDE, please set `external.function.dir` with absolute path // Concat to generate absolute path - Paths.get(Paths.get(".").toAbsolutePath.toString, TaskManagerConfig.EXTERNAL_FUNCTION_DIR).toString + Paths.get(Paths.get(".").toAbsolutePath.toString, TaskManagerConfig.getExternalFunctionDir).toString } launcher.setConf("spark.openmldb.taskmanager.external.function.dir", absoluteExternalFunctionDir) @@ -145,13 +147,13 @@ object SparkJobManager { launcher.setConf(k, v) } - if (TaskManagerConfig.JOB_LOG_PATH.nonEmpty) { + if (TaskManagerConfig.getJobLogPath.nonEmpty) { // Create local file and redirect the log of job into files launcher.redirectOutput(LogManager.getJobLogFile(jobInfo.getId)) launcher.redirectError(LogManager.getJobErrorLogFile(jobInfo.getId)) } - if(TaskManagerConfig.ENABLE_HIVE_SUPPORT) { + if(TaskManagerConfig.getEnableHiveSupport) { launcher.setConf("spark.sql.catalogImplementation", "hive") } diff --git a/java/openmldb-taskmanager/src/main/scala/com/_4paradigm/openmldb/taskmanager/tracker/YarnJobTrackerThread.scala b/java/openmldb-taskmanager/src/main/scala/com/_4paradigm/openmldb/taskmanager/tracker/YarnJobTrackerThread.scala index 7424f82cd84..8e45ee5c935 100644 --- a/java/openmldb-taskmanager/src/main/scala/com/_4paradigm/openmldb/taskmanager/tracker/YarnJobTrackerThread.scala +++ b/java/openmldb-taskmanager/src/main/scala/com/_4paradigm/openmldb/taskmanager/tracker/YarnJobTrackerThread.scala @@ -31,7 +31,7 @@ class YarnJobTrackerThread(job: JobInfo) extends Thread { } // Sleep for interval time - Thread.sleep(TaskManagerConfig.JOB_TRACKER_INTERVAL * 1000) + Thread.sleep(TaskManagerConfig.getJobTrackerInterval * 1000) val currentYarnState = appReport.getYarnApplicationState.toString.toLowerCase() diff --git a/java/openmldb-taskmanager/src/main/scala/com/_4paradigm/openmldb/taskmanager/util/HdfsUtil.scala b/java/openmldb-taskmanager/src/main/scala/com/_4paradigm/openmldb/taskmanager/util/HdfsUtil.scala new file mode 100644 index 00000000000..c4bbb2ef6f2 --- /dev/null +++ b/java/openmldb-taskmanager/src/main/scala/com/_4paradigm/openmldb/taskmanager/util/HdfsUtil.scala @@ -0,0 +1,49 @@ +/* + * Copyright 2021 4Paradigm + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com._4paradigm.openmldb.taskmanager.util + +import com._4paradigm.openmldb.taskmanager.config.TaskManagerConfig +import org.apache.hadoop.conf.Configuration +import org.apache.hadoop.fs.{FileSystem, Path} +import org.slf4j.LoggerFactory + +object HdfsUtil { + + private val logger = LoggerFactory.getLogger(this.getClass) + + def deleteHdfsDir(path: String): Unit = { + + val conf = new Configuration() + conf.addResource(new Path(TaskManagerConfig.getHadoopConfDir, "core-site.xml")) + conf.addResource(new Path(TaskManagerConfig.getHadoopConfDir, "hdfs-site.xml")) + + val fs = FileSystem.get(conf) + + val pathToDelete = new Path(path) + + if (fs.exists(pathToDelete)) { + fs.delete(pathToDelete, true); + logger.info("File deleted successfully: " + path) + } else { + logger.warn("File does not exist: " + path) + } + + fs.close() + + } + +} diff --git a/java/openmldb-taskmanager/src/main/scala/com/_4paradigm/openmldb/taskmanager/util/VersionUtil.scala b/java/openmldb-taskmanager/src/main/scala/com/_4paradigm/openmldb/taskmanager/util/VersionUtil.scala index a416ce9cd0d..93f66b8eba2 100644 --- a/java/openmldb-taskmanager/src/main/scala/com/_4paradigm/openmldb/taskmanager/util/VersionUtil.scala +++ b/java/openmldb-taskmanager/src/main/scala/com/_4paradigm/openmldb/taskmanager/util/VersionUtil.scala @@ -27,7 +27,7 @@ object VersionUtil { private val logger = LoggerFactory.getLogger(this.getClass) def getBatchVersion(): String = { - val sparkJarsPath = Paths.get(TaskManagerConfig.SPARK_HOME, "jars").toString + val sparkJarsPath = Paths.get(TaskManagerConfig.getSparkHome, "jars").toString val batchJarPath = BatchJobUtil.findOpenmldbBatchJar(sparkJarsPath) if (batchJarPath == null) { logger.error("Fail to find batch jar file and the version is unknown") diff --git a/java/openmldb-taskmanager/src/main/scala/com/_4paradigm/openmldb/taskmanager/yarn/YarnClientUtil.scala b/java/openmldb-taskmanager/src/main/scala/com/_4paradigm/openmldb/taskmanager/yarn/YarnClientUtil.scala index ffb7d5eebaf..803bd9ce5bf 100644 --- a/java/openmldb-taskmanager/src/main/scala/com/_4paradigm/openmldb/taskmanager/yarn/YarnClientUtil.scala +++ b/java/openmldb-taskmanager/src/main/scala/com/_4paradigm/openmldb/taskmanager/yarn/YarnClientUtil.scala @@ -31,10 +31,10 @@ object YarnClientUtil { def createYarnClient(): YarnClient = { val config = new Configuration() - config.addResource(new Path(TaskManagerConfig.HADOOP_CONF_DIR, "core-site.xml")) - config.addResource(new Path(TaskManagerConfig.HADOOP_CONF_DIR, "hdfs-site.xml")) - config.addResource(new Path(TaskManagerConfig.HADOOP_CONF_DIR, "yarn-site.xml")) - config.addResource(new Path(TaskManagerConfig.HADOOP_CONF_DIR, "mapred-site.xml")) + config.addResource(new Path(TaskManagerConfig.getHadoopConfDir, "core-site.xml")) + config.addResource(new Path(TaskManagerConfig.getHadoopConfDir, "hdfs-site.xml")) + config.addResource(new Path(TaskManagerConfig.getHadoopConfDir, "yarn-site.xml")) + config.addResource(new Path(TaskManagerConfig.getHadoopConfDir, "mapred-site.xml")) // Create yarn client val yarnClient = YarnClient.createYarnClient() @@ -90,10 +90,10 @@ object YarnClientUtil { val config = new YarnConfiguration() // TODO: Load config file in better way - config.addResource(new Path(TaskManagerConfig.HADOOP_CONF_DIR, "core-site.xml")) - config.addResource(new Path(TaskManagerConfig.HADOOP_CONF_DIR, "hdfs-site.xml")) - config.addResource(new Path(TaskManagerConfig.HADOOP_CONF_DIR, "yarn-site.xml")) - config.addResource(new Path(TaskManagerConfig.HADOOP_CONF_DIR, "mapred-site.xml")) + config.addResource(new Path(TaskManagerConfig.getHadoopConfDir, "core-site.xml")) + config.addResource(new Path(TaskManagerConfig.getHadoopConfDir, "hdfs-site.xml")) + config.addResource(new Path(TaskManagerConfig.getHadoopConfDir, "yarn-site.xml")) + config.addResource(new Path(TaskManagerConfig.getHadoopConfDir, "mapred-site.xml")) val logCliHelper = new LogCLIHelpers logCliHelper.setConf(config) diff --git a/java/openmldb-taskmanager/src/test/scala/com/_4paradigm/openmldb/taskmanager/server/impl/TestTaskManagerImpl.scala b/java/openmldb-taskmanager/src/test/scala/com/_4paradigm/openmldb/taskmanager/server/impl/TestTaskManagerImpl.scala index 311f8166eec..86d85ad2919 100644 --- a/java/openmldb-taskmanager/src/test/scala/com/_4paradigm/openmldb/taskmanager/server/impl/TestTaskManagerImpl.scala +++ b/java/openmldb-taskmanager/src/test/scala/com/_4paradigm/openmldb/taskmanager/server/impl/TestTaskManagerImpl.scala @@ -148,8 +148,8 @@ class TestTaskManagerImpl extends FunSuite { val testDb = "db1" val testTable = "t1" val option = new SdkOption - option.setZkCluster(TaskManagerConfig.ZK_CLUSTER) - option.setZkPath(TaskManagerConfig.ZK_ROOT_PATH) + option.setZkCluster(TaskManagerConfig.getZkCluster) + option.setZkPath(TaskManagerConfig.getZkRootPath) val executor = new SqlClusterExecutor(option) executor.createDB(testDb) executor.executeDDL(testDb, s"drop table $testTable") @@ -191,8 +191,8 @@ class TestTaskManagerImpl extends FunSuite { val testDb = "db1" val testTable = "t1" val option = new SdkOption - option.setZkCluster(TaskManagerConfig.ZK_CLUSTER) - option.setZkPath(TaskManagerConfig.ZK_ROOT_PATH) + option.setZkCluster(TaskManagerConfig.getZkCluster) + option.setZkPath(TaskManagerConfig.getZkRootPath) val executor = new SqlClusterExecutor(option) executor.createDB(testDb) @@ -239,8 +239,8 @@ class TestTaskManagerImpl extends FunSuite { val testDb = "db1" val testTable = "t1" val option = new SdkOption - option.setZkCluster(TaskManagerConfig.ZK_CLUSTER) - option.setZkPath(TaskManagerConfig.ZK_ROOT_PATH) + option.setZkCluster(TaskManagerConfig.getZkCluster) + option.setZkPath(TaskManagerConfig.getZkRootPath) val executor = new SqlClusterExecutor(option) executor.createDB(testDb) @@ -295,8 +295,8 @@ class TestTaskManagerImpl extends FunSuite { val testDb = "db1" val testTable = "t1" val option = new SdkOption - option.setZkCluster(TaskManagerConfig.ZK_CLUSTER) - option.setZkPath(TaskManagerConfig.ZK_ROOT_PATH) + option.setZkCluster(TaskManagerConfig.getZkCluster) + option.setZkPath(TaskManagerConfig.getZkRootPath) val executor = new SqlClusterExecutor(option) executor.createDB(testDb) diff --git a/release/sbin/start-taskmanagers.sh b/release/sbin/start-taskmanagers.sh index e0a55767877..b6873c33089 100755 --- a/release/sbin/start-taskmanagers.sh +++ b/release/sbin/start-taskmanagers.sh @@ -39,6 +39,10 @@ else echo "start taskmanager in $dir with endpoint $host:$port " cmd="cd $dir && SPARK_HOME=${SPARK_HOME} bin/start.sh start taskmanager $*" run_auto "$host" "$cmd" + + # Print the log of taskmanager if fail + #cmd="cd $dir && cat taskmanager/bin/logs/taskmanager.log" + #run_auto "$host" "$cmd" done IFS="$old_IFS" -fi \ No newline at end of file +fi From 1b2f9453a0bc691b1209c69e751b6b1b0bef2dcc Mon Sep 17 00:00:00 2001 From: dl239 Date: Fri, 15 Sep 2023 17:24:52 +0800 Subject: [PATCH 40/63] feat: optimize java sdk (#3445) --- .../examples/toydb/src/sdk/result_set_impl.h | 8 + hybridse/include/sdk/base.h | 1 + hybridse/include/sdk/result_set.h | 6 + .../openmldb/common/codec/ByteBitMap.java | 78 ++ .../common/codec/ClassicRowBuilder.java | 511 ++++++++++++ .../openmldb/common/codec/CodecMetaData.java | 105 +++ .../openmldb/common/codec/CodecUtil.java | 58 ++ .../common/codec/FlexibleRowBuilder.java | 390 +++++++++ .../openmldb/common/codec/RowBuilder.java | 432 +--------- .../openmldb/common/codec/RowView.java | 88 +-- .../openmldb/common/RowCodecTest.java | 741 +++++++++++++++--- .../openmldb/common/codec/TestBitMap.java | 86 ++ .../openmldb/common/codec/TestCodecUtil.java | 10 + java/openmldb-jdbc/pom.xml | 11 +- .../jdbc/CallablePreparedStatement.java | 65 +- .../openmldb/jdbc/DirectResultSet.java | 159 ++++ .../openmldb/jdbc/PreparedStatement.java | 251 +----- .../jdbc/RequestPreparedStatement.java | 9 +- .../openmldb/jdbc/SQLResultSet.java | 223 +----- .../openmldb/jdbc/SQLResultSetMetaData.java | 34 +- .../_4paradigm/openmldb/jdbc/Statement.java | 5 +- .../com/_4paradigm/openmldb/sdk/Common.java | 151 +++- .../openmldb/sdk/ProcedureInfo.java | 11 + .../_4paradigm/openmldb/sdk/QueryFuture.java | 33 +- .../com/_4paradigm/openmldb/sdk/Schema.java | 18 + .../BatchCallablePreparedStatementImpl.java | 173 +++- .../BatchRequestPreparedStatementImpl.java | 2 +- .../sdk/impl/CallableDirectResultSet.java | 36 + .../impl/CallablePreparedStatementImpl.java | 151 +++- .../openmldb/sdk/impl/Deployment.java | 73 ++ .../openmldb/sdk/impl/DeploymentManager.java | 101 +++ .../openmldb/sdk/impl/NativeResultSet.java | 184 +++++ .../sdk/impl/PreparedStatementImpl.java | 251 ++++++ .../openmldb/sdk/impl/SqlClusterExecutor.java | 61 +- .../jdbc/RequestPreparedStatementTest.java | 193 ++++- .../openmldb/jdbc/SQLRouterSmokeTest.java | 103 ++- java/openmldb-spark-connector/pom.xml | 7 +- src/catalog/base.cc | 3 + src/catalog/base.h | 5 + src/catalog/tablet_catalog.cc | 43 +- src/catalog/tablet_catalog.h | 4 +- src/catalog/tablet_catalog_test.cc | 24 + src/client/tablet_client.cc | 85 +- src/client/tablet_client.h | 14 +- src/proto/sql_procedure.proto | 1 + src/sdk/batch_request_result_set_sql.h | 5 + src/sdk/result_set_base.h | 4 + src/sdk/result_set_sql.h | 12 + src/sdk/sql_cluster_router.cc | 134 +++- src/sdk/sql_cluster_router.h | 28 +- src/sdk/sql_router.h | 21 + src/sdk/sql_router_sdk.i | 20 + src/tablet/tablet_impl.cc | 8 +- 53 files changed, 3930 insertions(+), 1300 deletions(-) create mode 100644 java/openmldb-common/src/main/java/com/_4paradigm/openmldb/common/codec/ByteBitMap.java create mode 100644 java/openmldb-common/src/main/java/com/_4paradigm/openmldb/common/codec/ClassicRowBuilder.java create mode 100644 java/openmldb-common/src/main/java/com/_4paradigm/openmldb/common/codec/CodecMetaData.java create mode 100644 java/openmldb-common/src/main/java/com/_4paradigm/openmldb/common/codec/FlexibleRowBuilder.java create mode 100644 java/openmldb-common/src/test/java/com/_4paradigm/openmldb/common/codec/TestBitMap.java create mode 100644 java/openmldb-jdbc/src/main/java/com/_4paradigm/openmldb/jdbc/DirectResultSet.java create mode 100644 java/openmldb-jdbc/src/main/java/com/_4paradigm/openmldb/sdk/impl/CallableDirectResultSet.java create mode 100644 java/openmldb-jdbc/src/main/java/com/_4paradigm/openmldb/sdk/impl/Deployment.java create mode 100644 java/openmldb-jdbc/src/main/java/com/_4paradigm/openmldb/sdk/impl/DeploymentManager.java create mode 100644 java/openmldb-jdbc/src/main/java/com/_4paradigm/openmldb/sdk/impl/NativeResultSet.java diff --git a/hybridse/examples/toydb/src/sdk/result_set_impl.h b/hybridse/examples/toydb/src/sdk/result_set_impl.h index 358da6dfe4a..db9b2185fbd 100644 --- a/hybridse/examples/toydb/src/sdk/result_set_impl.h +++ b/hybridse/examples/toydb/src/sdk/result_set_impl.h @@ -71,6 +71,14 @@ class ResultSetImpl : public ResultSet { inline int32_t Size() { return response_->count(); } + void CopyTo(hybridse::sdk::ByteArrayPtr buf) override { + cntl_->response_attachment().copy_to(reinterpret_cast(buf)); + } + + int32_t GetDataLength() override { + return cntl_->response_attachment().size(); + } + private: inline uint32_t GetRecordSize() { return response_->count(); } diff --git a/hybridse/include/sdk/base.h b/hybridse/include/sdk/base.h index 90d8ff75680..e5da48094f8 100644 --- a/hybridse/include/sdk/base.h +++ b/hybridse/include/sdk/base.h @@ -134,6 +134,7 @@ class ProcedureInfo { virtual ProcedureType GetType() const = 0; virtual const std::string* GetOption(const std::string& key) const = 0; virtual const std::unordered_map* GetOption() const = 0; + virtual int GetRouterCol() const = 0; }; } // namespace sdk diff --git a/hybridse/include/sdk/result_set.h b/hybridse/include/sdk/result_set.h index c36d4bb2f0d..fe42bc9dbd3 100644 --- a/hybridse/include/sdk/result_set.h +++ b/hybridse/include/sdk/result_set.h @@ -25,6 +25,9 @@ namespace hybridse { namespace sdk { + +typedef char* ByteArrayPtr; + struct Date { int32_t year; int32_t month; @@ -235,6 +238,9 @@ class ResultSet { virtual bool IsNULL(int index) = 0; virtual int32_t Size() = 0; + + virtual void CopyTo(hybridse::sdk::ByteArrayPtr buf) = 0; + virtual int32_t GetDataLength() = 0; }; } // namespace sdk diff --git a/java/openmldb-common/src/main/java/com/_4paradigm/openmldb/common/codec/ByteBitMap.java b/java/openmldb-common/src/main/java/com/_4paradigm/openmldb/common/codec/ByteBitMap.java new file mode 100644 index 00000000000..760deb784a3 --- /dev/null +++ b/java/openmldb-common/src/main/java/com/_4paradigm/openmldb/common/codec/ByteBitMap.java @@ -0,0 +1,78 @@ +/* + * Copyright 2021 4Paradigm + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com._4paradigm.openmldb.common.codec; + +public class ByteBitMap { + private int size; + private int length; + private byte[] buf; + + public ByteBitMap(int sizeInBits) { + size = sizeInBits; + length = (sizeInBits >> 3) + ((sizeInBits & 0x07) == 0 ? 0 : 1); + buf = new byte[length]; + } + + public int size() { + return this.size; + } + + public boolean at(int offset) { + int index = indexFor(offset); + return (buf[index] & (1 << (offset & 0x07))) > 0; + } + + public void atPut(int offset, boolean value) { + int index = indexFor(offset); + buf[index] |= (1 << (offset & 0x07)); + } + + public void clear() { + for (int i = 0; i < length; i++) { + buf[i] = 0; + } + } + + public byte[] getBuffer() { + return buf; + } + + public boolean allSetted() { + if ((size & 0x07) == 0) { + for (int i = 0; i < length; i++) { + if (buf[i] != (byte)0xFF) { + return false; + } + } + } else { + for (int i = 0; i < length - 1; i++) { + if (buf[i] != (byte)0xFF) { + return false; + } + } + int val = (1 << (size & 0x07)) - 1; + if (buf[length - 1] != val) { + return false; + } + } + return true; + } + + private int indexFor(int offset) { + return offset >> 3; + } +} diff --git a/java/openmldb-common/src/main/java/com/_4paradigm/openmldb/common/codec/ClassicRowBuilder.java b/java/openmldb-common/src/main/java/com/_4paradigm/openmldb/common/codec/ClassicRowBuilder.java new file mode 100644 index 00000000000..15bb50d0471 --- /dev/null +++ b/java/openmldb-common/src/main/java/com/_4paradigm/openmldb/common/codec/ClassicRowBuilder.java @@ -0,0 +1,511 @@ +/* + * Copyright 2021 4Paradigm + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com._4paradigm.openmldb.common.codec; + +import com._4paradigm.openmldb.proto.Type.DataType; +import com._4paradigm.openmldb.proto.Common.ColumnDesc; +import org.joda.time.DateTime; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.sql.Date; +import java.sql.Timestamp; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class ClassicRowBuilder implements RowBuilder { + + private ByteBuffer buf; + private int size = 0; + private int cnt = 0; + List schema = new ArrayList<>(); + private int strFieldCnt = 0; + private int strFieldStartOffset = 0; + private int strAddrLength = 0; + private int strOffset = 0; + private int schemaVersion = 1; + private List offsetVec = new ArrayList<>(); + private List strIdx = new ArrayList<>(); + private int curPos = 0; + + public ClassicRowBuilder(List schema) throws Exception { + calcSchemaOffset(schema); + } + + public ClassicRowBuilder(List schema, int schemaversion) throws Exception { + this.schemaVersion = schemaversion; + calcSchemaOffset(schema); + } + + private void calcSchemaOffset(List schema) throws Exception { + strFieldStartOffset = CodecUtil.HEADER_LENGTH + CodecUtil.getBitMapSize(schema.size()); + this.schema = schema; + for (int idx = 0; idx < schema.size(); idx++) { + ColumnDesc column = schema.get(idx); + if (column.getDataType() == DataType.kVarchar || column.getDataType() == DataType.kString) { + offsetVec.add(strFieldCnt); + strIdx.add(idx); + strFieldCnt++; + } else { + if (CodecUtil.TYPE_SIZE_MAP.get(column.getDataType()) == null) { + throw new Exception("type is not supported"); + } else { + offsetVec.add(strFieldStartOffset); + strFieldStartOffset += CodecUtil.TYPE_SIZE_MAP.get(column.getDataType()); + } + } + } + } + + public void SetSchemaVersion(int version) { + this.schemaVersion = version; + } + + public int calTotalLength(List row) throws Exception { + if (row.size() != schema.size()) { + throw new Exception("row size is not equal schema size"); + } + int stringLength = 0; + for (Integer idx : strIdx) { + Object obj = row.get(idx); + if (obj == null) { + continue; + } + stringLength += ((String)obj).getBytes(CodecUtil.CHARSET).length; + } + return calTotalLength(stringLength); + } + + public int calTotalLength(int stringLength) throws Exception { + if (schema.size() == 0) { + return 0; + } + long totalLength = strFieldStartOffset + stringLength; + if (totalLength + strFieldCnt <= CodecUtil.UINT8_MAX) { + totalLength += strFieldCnt; + } else if (totalLength + strFieldCnt * 2 <= CodecUtil.UINT16_MAX) { + totalLength += strFieldCnt * 2; + } else if (totalLength + strFieldCnt * 3 <= CodecUtil.UINT24_MAX) { + totalLength += strFieldCnt * 3; + } else if (totalLength + strFieldCnt * 4 <= CodecUtil.UINT32_MAX) { + totalLength += strFieldCnt * 4; + } + if (totalLength > Integer.MAX_VALUE) { + throw new Exception("total length is bigger than integer max value"); + } + return (int) totalLength; + } + + public ByteBuffer setBuffer(ByteBuffer buffer, int size) { + if (buffer == null || size == 0 || + size < strFieldStartOffset + strFieldCnt) { + return null; + } + if (buffer.order() == ByteOrder.BIG_ENDIAN) { + buffer = buffer.order(ByteOrder.LITTLE_ENDIAN); + } + this.size = size; + buffer.put((byte) 1); // FVersion + buffer.put((byte) schemaVersion); // SVersion + buffer.putInt(size); // size + for (int idx = 0; idx < CodecUtil.getBitMapSize(schema.size()); idx++) { + buffer.put((byte)0xFF); + } + this.buf = buffer; + strAddrLength = CodecUtil.getAddrLength(size); + strOffset = strFieldStartOffset + strAddrLength * strFieldCnt; + return this.buf; + } + + private void setField(int index) { + int pos = CodecUtil.HEADER_LENGTH + (index >> 3); + byte bt = buf.get(pos); + buf.put(pos, (byte) (bt & (~(1 << (cnt & 0x07))))); + } + + private boolean check(DataType type) { + return CodecUtil.checkType(schema, cnt, type); + } + + private void setStrOffset(int strPos) { + if (strPos >= strFieldCnt) { + return; + } + int index = strFieldStartOffset + strAddrLength * strPos; + CodecUtil.setStrOffset(buf, index, strOffset, strAddrLength); + } + + @Override + public boolean appendNULL() { + ColumnDesc column = schema.get(cnt); + if (column.getDataType() == DataType.kVarchar || column.getDataType() == DataType.kString) { + int strPos = offsetVec.get(cnt); + setStrOffset(strPos + 1); + } + cnt++; + return true; + } + + @Override + public boolean appendBool(boolean val) { + if (!check(DataType.kBool)) { + return false; + } + setField(cnt); + buf.position(offsetVec.get(cnt)); + if (val) { + buf.put((byte) 1); + } else { + buf.put((byte) 0); + } + cnt++; + return true; + } + + @Override + public boolean appendInt(int val) { + if (!check(DataType.kInt)) { + return false; + } + setField(cnt); + buf.position(offsetVec.get(cnt)); + buf.putInt(val); + cnt++; + return true; + } + + @Override + public boolean appendSmallInt(short val) { + if (!check(DataType.kSmallInt)) { + return false; + } + setField(cnt); + buf.position(offsetVec.get(cnt)); + buf.putShort(val); + cnt++; + return true; + } + + @Override + public boolean appendTimestamp(Timestamp val) { + if (!check(DataType.kTimestamp)) { + return false; + } + setField(cnt); + buf.position(offsetVec.get(cnt)); + buf.putLong(val.getTime()); + cnt++; + return true; + } + + @Override + public boolean appendBigInt(long val) { + if (!check(DataType.kBigInt)) { + return false; + } + setField(cnt); + buf.position(offsetVec.get(cnt)); + buf.putLong(val); + cnt++; + return true; + } + + @Override + public boolean appendFloat(float val) { + if (!check(DataType.kFloat)) { + return false; + } + setField(cnt); + buf.position(offsetVec.get(cnt)); + buf.putFloat(val); + cnt++; + return true; + } + + @Override + public boolean appendDouble(double val) { + if (!check(DataType.kDouble)) { + return false; + } + setField(cnt); + buf.position(offsetVec.get(cnt)); + buf.putDouble(val); + cnt++; + return true; + } + + @Override + public boolean appendDate(Date date) { + int dateInt = CodecUtil.dateToDateInt(date); + buf.position(offsetVec.get(cnt)); + buf.putInt(dateInt); + setField(cnt); + cnt++; + return true; + } + + @Override + public boolean appendString(String val) { + byte[] bytes = val.getBytes(CodecUtil.CHARSET); + int length = bytes.length; + if (val == null || (!check(DataType.kVarchar) && !check(DataType.kString))) { + return false; + } + if (strOffset + length > size) { + return false; + } + int strPos = offsetVec.get(cnt); + if (strPos == 0) { + setStrOffset(strPos); + } + if (length != 0) { + buf.position(strOffset); + buf.put(val.getBytes(CodecUtil.CHARSET), 0, length); + } + strOffset += length; + setStrOffset(strPos + 1); + setField(cnt); + cnt++; + return true; + } + + public static ByteBuffer encode(Object[] row, List schema, int schemaVer) throws Exception { + if (row == null || row.length == 0 || schema == null || schema.size() == 0 || row.length != schema.size()) { + throw new Exception("input error"); + } + int strLength = CodecUtil.calStrLength(row, schema); + ClassicRowBuilder builder = new ClassicRowBuilder(schema, schemaVer); + int size = builder.calTotalLength(strLength); + ByteBuffer buffer = ByteBuffer.allocate(size).order(ByteOrder.LITTLE_ENDIAN); + buffer = builder.setBuffer(buffer, size); + for (int i = 0; i < schema.size(); i++) { + ColumnDesc columnDesc = schema.get(i); + Object column = row[i]; + if (columnDesc.getNotNull() && column == null) { + throw new Exception("col " + columnDesc.getName() + " should not be null"); + } else if (column == null) { + builder.appendNULL(); + continue; + } + boolean ok = false; + switch (columnDesc.getDataType()) { + case kVarchar: + case kString: + ok = builder.appendString((String) column); + break; + case kBool: + ok = builder.appendBool((Boolean) column); + break; + case kSmallInt: + ok = builder.appendSmallInt((Short) column); + break; + case kInt: + ok = builder.appendInt((Integer) column); + break; + case kTimestamp: + if (column instanceof Timestamp) { + ok = builder.appendTimestamp((Timestamp) column); + } else { + ok = false; + } + break; + case kBigInt: + ok = builder.appendBigInt((Long) column); + break; + case kFloat: + ok = builder.appendFloat((Float) column); + break; + case kDate: + ok = builder.appendDate((Date)column); + break; + case kDouble: + ok = builder.appendDouble((Double) column); + break; + default: + throw new Exception("unsupported data type"); + } + if (!ok) { + throw new Exception("append " + columnDesc.getDataType().toString() + " error"); + } + } + return buffer; + } + + public static ByteBuffer encode(Map row, List schema, Map blobKeys, int schemaVersion) throws Exception { + if (row == null || row.size() == 0 || schema == null || schema.size() == 0 || row.size() != schema.size()) { + throw new Exception("input error"); + } + int strLength = CodecUtil.calStrLength(row, schema); + ClassicRowBuilder builder = new ClassicRowBuilder(schema, schemaVersion); + int size = builder.calTotalLength(strLength); + ByteBuffer buffer = ByteBuffer.allocate(size).order(ByteOrder.LITTLE_ENDIAN); + buffer = builder.setBuffer(buffer, size); + for (int i = 0; i < schema.size(); i++) { + ColumnDesc columnDesc = schema.get(i); + Object column = row.get(columnDesc.getName()); + if (columnDesc.getNotNull() + && column == null) { + throw new Exception("col " + columnDesc.getName() + " should not be null"); + } else if (column == null) { + builder.appendNULL(); + continue; + } + boolean ok = false; + switch (columnDesc.getDataType()) { + case kString: + case kVarchar: + ok = builder.appendString((String) column); + break; + case kBool: + ok = builder.appendBool((Boolean) column); + break; + case kSmallInt: + ok = builder.appendSmallInt((Short) column); + break; + case kInt: + ok = builder.appendInt((Integer) column); + break; + case kTimestamp: + if (column instanceof Timestamp) { + ok = builder.appendTimestamp((Timestamp)column); + }else { + ok = false; + } + break; + case kBigInt: + ok = builder.appendBigInt((Long) column); + break; + case kFloat: + ok = builder.appendFloat((Float) column); + break; + case kDate: + ok = builder.appendDate((Date)column); + break; + case kDouble: + ok = builder.appendDouble((Double) column); + break; + default: + throw new Exception("unsupported data type"); + } + if (!ok) { + throw new Exception("append " + columnDesc.getDataType().toString() + " error"); + } + } + return buffer; + } + + @Override + public boolean setNULL(int idx) { + if (idx != curPos) { + return false; + } + curPos++; + return appendNULL(); + } + + @Override + public boolean setBool(int idx, boolean val) { + if (idx != curPos) { + return false; + } + curPos++; + return appendBool(val); + } + + @Override + public boolean setInt(int idx, int val) { + if (idx != curPos) { + return false; + } + curPos++; + return appendInt(val); + } + + @Override + public boolean setSmallInt(int idx, short val) { + if (idx != curPos) { + return false; + } + curPos++; + return appendSmallInt(val); + } + + @Override + public boolean setTimestamp(int idx, Timestamp val) { + if (idx != curPos) { + return false; + } + curPos++; + return appendTimestamp(val); + } + + @Override + public boolean setBigInt(int idx, long val) { + if (idx != curPos) { + return false; + } + curPos++; + return appendBigInt(val); + } + + @Override + public boolean setFloat(int idx, float val){ + if (idx != curPos) { + return false; + } + curPos++; + return appendFloat(val); + } + + @Override + public boolean setDouble(int idx, double val) { + if (idx != curPos) { + return false; + } + curPos++; + return appendDouble(val); + } + + @Override + public boolean setDate(int idx, Date date) { + if (idx != curPos) { + return false; + } + curPos++; + return appendDate(date); + } + + @Override + public boolean setString(int idx, String val) { + if (idx != curPos) { + return false; + } + curPos++; + return appendString(val); + } + + @Override + public boolean build() { + return true; + } + + @Override + public ByteBuffer getValue() { + return buf; + } +} + diff --git a/java/openmldb-common/src/main/java/com/_4paradigm/openmldb/common/codec/CodecMetaData.java b/java/openmldb-common/src/main/java/com/_4paradigm/openmldb/common/codec/CodecMetaData.java new file mode 100644 index 00000000000..68667e92516 --- /dev/null +++ b/java/openmldb-common/src/main/java/com/_4paradigm/openmldb/common/codec/CodecMetaData.java @@ -0,0 +1,105 @@ +/* + * Copyright 2021 4Paradigm + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com._4paradigm.openmldb.common.codec; + +import com._4paradigm.openmldb.proto.Common; +import com._4paradigm.openmldb.proto.Type; + +import java.util.ArrayList; +import java.util.List; + +public class CodecMetaData { + private List schema; + private int schemaVersion = 1; + List offsetList = new ArrayList<>(); + List strIdxList = new ArrayList<>(); + private int strFieldCnt = 0; + private int strFieldStartOffset = 0; + private int baseFieldStartOffset = 0; + + public CodecMetaData(List schema) throws Exception { + this(schema, 1, true); + } + + public CodecMetaData(List schema, boolean addOffsetHeader) throws Exception { + this.schema = schema; + calcSchemaOffset(addOffsetHeader); + } + + public CodecMetaData(List schema, int schemaVersion, boolean addOffsetHeader) throws Exception { + this.schema = schema; + this.schemaVersion = schemaVersion; + calcSchemaOffset(addOffsetHeader); + } + + private void calcSchemaOffset(boolean addOffsetHeader) throws Exception { + if (schema.size() == 0) { + throw new Exception("schema size is zero"); + } + baseFieldStartOffset = CodecUtil.HEADER_LENGTH + CodecUtil.getBitMapSize(schema.size()); + int baseOffset = 0; + for (int idx = 0; idx < schema.size(); idx++) { + Common.ColumnDesc column = schema.get(idx); + if (column.getDataType() == Type.DataType.kVarchar || column.getDataType() == Type.DataType.kString) { + offsetList.add(strFieldCnt); + strFieldCnt++; + strIdxList.add(idx); + } else { + if (CodecUtil.TYPE_SIZE_MAP.get(column.getDataType()) == null) { + throw new Exception("type is not supported"); + } else { + if (addOffsetHeader) { + offsetList.add(baseOffset + baseFieldStartOffset); + } else { + offsetList.add(baseOffset); + } + baseOffset += CodecUtil.TYPE_SIZE_MAP.get(column.getDataType()); + } + } + } + strFieldStartOffset = baseFieldStartOffset + baseOffset; + } + + public List getSchema() { + return schema; + } + + public int getSchemaVersion() { + return schemaVersion; + } + + public List getOffsetList() { + return offsetList; + } + + public int getStrFieldCnt() { + return strFieldCnt; + } + + public int getStrFieldStartOffset() { + return strFieldStartOffset; + } + + public int getBaseFieldStartOffset() { + return baseFieldStartOffset; + } + + public List getStrIdxList() { + return strIdxList; + } + +} diff --git a/java/openmldb-common/src/main/java/com/_4paradigm/openmldb/common/codec/CodecUtil.java b/java/openmldb-common/src/main/java/com/_4paradigm/openmldb/common/codec/CodecUtil.java index 84eef47aec6..468b71e762a 100644 --- a/java/openmldb-common/src/main/java/com/_4paradigm/openmldb/common/codec/CodecUtil.java +++ b/java/openmldb-common/src/main/java/com/_4paradigm/openmldb/common/codec/CodecUtil.java @@ -15,9 +15,12 @@ */ package com._4paradigm.openmldb.common.codec; +import com._4paradigm.openmldb.proto.Common; +import com._4paradigm.openmldb.proto.Type; import com._4paradigm.openmldb.proto.Type.DataType; import com._4paradigm.openmldb.proto.Common.ColumnDesc; +import java.nio.ByteBuffer; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.sql.Date; @@ -158,4 +161,59 @@ public static int dateIntToDays(int dateInt) { return (int)Math.ceil(date.getTime() / 86400000.0); } + public static boolean checkType(List schema, int pos, Type.DataType type) { + if (pos >= schema.size()) { + return false; + } + Common.ColumnDesc column = schema.get(pos); + if (column.getDataType() != type) { + return false; + } + if (column.getDataType() != Type.DataType.kVarchar && column.getDataType() != Type.DataType.kString) { + if (CodecUtil.TYPE_SIZE_MAP.get(column.getDataType()) == null) { + return false; + } + } + return true; + } + + public static void setStrOffset(ByteBuffer buf, int index, int strOffset, int strAddrLength) { + switch (strAddrLength) { + case 1: + buf.put(index, (byte) (strOffset & 0xFF)); + break; + case 2: + buf.putShort(index, (short) (strOffset & 0xFFFF)); + break; + case 3: + buf.put(index, (byte) (strOffset >> 16)); + buf.put(index + 1, (byte) ((strOffset & 0xFF00) >> 8)); + buf.put(index + 2, (byte) (strOffset & 0x00FF)); + break; + default: + buf.putInt(index, strOffset); + + } + } + + public static int getStrOffset(ByteBuffer buf, int offset, int strAddrLength) { + int strOffset = 0; + switch (strAddrLength) { + case 1: + strOffset = buf.get(offset) & 0xFF; + break; + case 2: + strOffset = buf.getShort(offset) & 0xFFFF; + break; + case 3: + strOffset = buf.get(offset) & 0xFF; + strOffset = (strOffset << 8) + (buf.get((offset + 1)) & 0xFF); + strOffset = (strOffset << 8) + (buf.get((offset + 2)) & 0xFF); + break; + default: + strOffset = buf.getInt(offset); + } + return strOffset; + } + } diff --git a/java/openmldb-common/src/main/java/com/_4paradigm/openmldb/common/codec/FlexibleRowBuilder.java b/java/openmldb-common/src/main/java/com/_4paradigm/openmldb/common/codec/FlexibleRowBuilder.java new file mode 100644 index 00000000000..5497237ce20 --- /dev/null +++ b/java/openmldb-common/src/main/java/com/_4paradigm/openmldb/common/codec/FlexibleRowBuilder.java @@ -0,0 +1,390 @@ +/* + * Copyright 2021 4Paradigm + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com._4paradigm.openmldb.common.codec; + +import com._4paradigm.openmldb.proto.Common; +import com._4paradigm.openmldb.proto.Type; +import com.google.common.io.ByteArrayDataOutput; +import com.google.common.io.ByteStreams; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.sql.Date; +import java.sql.Timestamp; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +public class FlexibleRowBuilder implements RowBuilder { + + private CodecMetaData metaData; + private int strFieldStartOffset; + + private int curPos = 0; + private ByteBitMap nullBitmap; + private ByteBitMap settedValue; + private ByteBuffer baseFieldBuf; + private ByteBuffer strAddrBuf; + private int strAddrSize = 1; + private ByteArrayDataOutput stringWriter = ByteStreams.newDataOutput(); + private int settedStrCnt = 0; + private ByteBuffer result; + private int strTotalLen = 0; + private int strAddrLen = 0; + // Cache string values in case of out-of-order insertion + private Map stringValueCache; + private int curStrIdx = 0; + + public FlexibleRowBuilder(List schema) throws Exception { + this(new CodecMetaData(schema, 1, false)); + } + + public FlexibleRowBuilder(CodecMetaData metaData) { + this.metaData = metaData; + nullBitmap = new ByteBitMap(metaData.getSchema().size()); + settedValue = new ByteBitMap(metaData.getSchema().size()); + strFieldStartOffset = metaData.getStrFieldStartOffset(); + baseFieldBuf = ByteBuffer.allocate(strFieldStartOffset - metaData.getBaseFieldStartOffset()).order(ByteOrder.LITTLE_ENDIAN); + strAddrLen = strAddrSize * metaData.getStrFieldCnt(); + strAddrBuf = ByteBuffer.allocate(strAddrLen).order(ByteOrder.LITTLE_ENDIAN); + } + + private boolean checkType(int pos, Type.DataType type) { + return CodecUtil.checkType(metaData.getSchema(), pos, type); + } + + private int getOffset(int idx) { + return metaData.getOffsetList().get(idx); + } + + private void setStrOffset(int strPos) { + if (strPos >= metaData.getStrFieldCnt()) { + return; + } + int curOffset = strFieldStartOffset + strAddrLen + strTotalLen; + int curStrAddrSize = CodecUtil.getAddrLength(curOffset); + if (curStrAddrSize > strAddrSize) { + strAddrBuf = expandStrLenBuf(curStrAddrSize, strPos); + strAddrSize = curStrAddrSize; + curOffset = strFieldStartOffset + strAddrLen + strTotalLen; + } + CodecUtil.setStrOffset(strAddrBuf, strPos * strAddrSize, curOffset, strAddrSize); + } + + private ByteBuffer expandStrLenBuf(int newStrAddrsize, int strPos) { + int newStrAddBufLen = newStrAddrsize * metaData.getStrFieldCnt(); + ByteBuffer newStrAddrBuf = ByteBuffer.allocate(newStrAddBufLen).order(ByteOrder.LITTLE_ENDIAN); + for (int i = 0; i < strPos; i++) { + int strOffset = CodecUtil.getStrOffset(strAddrBuf, i * strAddrSize, strAddrSize); + strOffset += (newStrAddBufLen - strAddrLen); + CodecUtil.setStrOffset(newStrAddrBuf, i * newStrAddrsize, strOffset, newStrAddrsize); + } + strAddrLen = newStrAddBufLen; + return newStrAddrBuf; + } + + public void clear() { + nullBitmap.clear(); + settedValue.clear(); + settedStrCnt = 0; + baseFieldBuf.clear(); + strAddrBuf.clear(); + stringWriter = ByteStreams.newDataOutput(); + curPos = 0; + strTotalLen = 0; + strAddrSize = 1; + strAddrLen = strAddrSize * metaData.getStrFieldCnt(); + result = null; + curStrIdx = 0; + if (stringValueCache != null) { + stringValueCache.clear(); + stringValueCache = null; + } + } + + @Override + public boolean appendNULL() { + if (!setNULL(curPos)) { + return false; + } + curPos++; + return true; + } + + @Override + public boolean appendBool(boolean val) { + if (!setBool(curPos, val)) { + return false; + } + curPos++; + return true; + } + + @Override + public boolean appendInt(int val) { + if (!setInt(curPos, val)) { + return false; + } + curPos++; + return true; + } + + @Override + public boolean appendSmallInt(short val) { + if (!setSmallInt(curPos, val)) { + return false; + } + curPos++; + return true; + } + + @Override + public boolean appendTimestamp(Timestamp val) { + if (!setTimestamp(curPos, val)) { + return false; + } + curPos++; + return true; + } + + @Override + public boolean appendBigInt(long val) { + if (!setBigInt(curPos, val)) { + return false; + } + curPos++; + return true; + } + + @Override + public boolean appendFloat(float val) { + if (!setFloat(curPos, val)) { + return false; + } + curPos++; + return true; + } + + @Override + public boolean appendDouble(double val) { + if (!setDouble(curPos, val)) { + return false; + } + curPos++; + return true; + } + + @Override + public boolean appendDate(Date date) { + if (!setDate(curPos, date)) { + return false; + } + curPos++; + return true; + } + + @Override + public boolean appendString(String val) { + if (!setString(curPos, val)) { + return false; + } + curPos++; + return true; + } + + @Override + public boolean setNULL(int idx) { + if (idx >= metaData.getSchema().size()) { + return false; + } + Type.DataType type = metaData.getSchema().get(idx).getDataType(); + if (type == Type.DataType.kVarchar || type == Type.DataType.kString) { + if (idx != metaData.getStrIdxList().get(curStrIdx)) { + if (stringValueCache == null) { + stringValueCache = new TreeMap<>(); + } + stringValueCache.put(idx, null); + return true; + } else { + int strPos = getOffset(idx); + setStrOffset(strPos + 1); + settedStrCnt++; + curStrIdx++; + } + } + nullBitmap.atPut(idx, true); + settedValue.atPut(idx, true); + return true; + } + + @Override + public boolean setBool(int idx, boolean val) { + if (!checkType(idx, Type.DataType.kBool)) { + return false; + } + settedValue.atPut(idx, true); + if (val) { + baseFieldBuf.put(getOffset(idx), (byte)1); + } else { + baseFieldBuf.put(getOffset(idx), (byte)0); + } + return true; + } + + @Override + public boolean setInt(int idx, int val) { + if (!checkType(idx, Type.DataType.kInt)) { + return false; + } + settedValue.atPut(idx, true); + baseFieldBuf.putInt(getOffset(idx), val); + return true; + } + + @Override + public boolean setSmallInt(int idx, short val) { + if (!checkType(idx, Type.DataType.kSmallInt)) { + return false; + } + settedValue.atPut(idx, true); + baseFieldBuf.putShort(getOffset(idx), val); + return true; + } + + @Override + public boolean setTimestamp(int idx, Timestamp val) { + if (!checkType(idx, Type.DataType.kTimestamp)) { + return false; + } + settedValue.atPut(idx, true); + baseFieldBuf.putLong(getOffset(idx), val.getTime()); + return true; + } + + @Override + public boolean setBigInt(int idx, long val) { + if (!checkType(idx, Type.DataType.kBigInt)) { + return false; + } + settedValue.atPut(idx, true); + baseFieldBuf.putLong(getOffset(idx), val); + return true; + } + + @Override + public boolean setFloat(int idx, float val) { + if (!checkType(idx, Type.DataType.kFloat)) { + return false; + } + settedValue.atPut(idx, true); + baseFieldBuf.putFloat(getOffset(idx), val); + return true; + } + + @Override + public boolean setDouble(int idx, double val) { + if (!checkType(idx, Type.DataType.kDouble)) { + return false; + } + settedValue.atPut(idx, true); + baseFieldBuf.putDouble(getOffset(idx), val); + return true; + } + + @Override + public boolean setDate(int idx, Date val) { + if (!checkType(idx, Type.DataType.kDate)) { + return false; + } + settedValue.atPut(idx, true); + int dateVal = CodecUtil.dateToDateInt(val); + baseFieldBuf.putInt(getOffset(idx), dateVal); + return true; + } + + @Override + public boolean setString(int idx, String val) { + if (!checkType(idx, Type.DataType.kString) && !checkType(idx, Type.DataType.kVarchar)) { + return false; + } + if (settedValue.at(idx)) { + return false; + } + if (curStrIdx >= metaData.getStrIdxList().size()) { + return false; + } + if (idx != metaData.getStrIdxList().get(curStrIdx)) { + if (stringValueCache == null) { + stringValueCache = new TreeMap<>(); + } + stringValueCache.put(idx, val); + } else { + settedValue.atPut(idx, true); + byte[] bytes = val.getBytes(CodecUtil.CHARSET); + stringWriter.write(bytes); + if (settedStrCnt == 0) { + setStrOffset(settedStrCnt); + } + strTotalLen += bytes.length; + settedStrCnt++; + setStrOffset(settedStrCnt); + curStrIdx++; + } + return true; + } + + @Override + public boolean build() { + if (stringValueCache != null) { + for (Map.Entry kv : stringValueCache.entrySet()) { + if (kv.getValue() == null) { + if (!setNULL(kv.getKey())) { + return false; + } + } else { + if (!setString(kv.getKey(), kv.getValue())) { + return false; + } + } + } + } + if (!settedValue.allSetted()) { + return false; + } + int totalSize = strFieldStartOffset + strAddrLen + strTotalLen; + int curStrAddrSize = CodecUtil.getAddrLength(totalSize); + if (curStrAddrSize > strAddrSize) { + strAddrBuf = expandStrLenBuf(curStrAddrSize, settedStrCnt); + strAddrSize = curStrAddrSize; + totalSize = strFieldStartOffset + strAddrLen + strTotalLen; + } + result = ByteBuffer.allocate(totalSize).order(ByteOrder.LITTLE_ENDIAN); + result.put((byte)1); // FVersion + result.put((byte)metaData.getSchemaVersion()); // SVersion + result.putInt(totalSize); // Size + result.put(nullBitmap.getBuffer()); // BitMap + result.put(baseFieldBuf.array()); // Base data type + result.put(strAddrBuf.array()); // String addr + result.put(stringWriter.toByteArray()); // String value + return true; + } + + @Override + public ByteBuffer getValue() { + return result; + } +} diff --git a/java/openmldb-common/src/main/java/com/_4paradigm/openmldb/common/codec/RowBuilder.java b/java/openmldb-common/src/main/java/com/_4paradigm/openmldb/common/codec/RowBuilder.java index f55869d4c1e..5914cf8e68c 100644 --- a/java/openmldb-common/src/main/java/com/_4paradigm/openmldb/common/codec/RowBuilder.java +++ b/java/openmldb-common/src/main/java/com/_4paradigm/openmldb/common/codec/RowBuilder.java @@ -13,415 +13,37 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com._4paradigm.openmldb.common.codec; -import com._4paradigm.openmldb.proto.Type.DataType; -import com._4paradigm.openmldb.proto.Common.ColumnDesc; -import org.joda.time.DateTime; +package com._4paradigm.openmldb.common.codec; import java.nio.ByteBuffer; -import java.nio.ByteOrder; import java.sql.Date; import java.sql.Timestamp; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -public class RowBuilder { - - private ByteBuffer buf; - private int size = 0; - private int cnt = 0; - List schema = new ArrayList<>(); - private int strFieldCnt = 0; - private int strFieldStartOffset = 0; - private int strAddrLength = 0; - private int strOffset = 0; - private int schemaVersion = 1; - private List offsetVec = new ArrayList<>(); - private List strIdx = new ArrayList<>(); - - public RowBuilder(List schema) throws Exception { - calcSchemaOffset(schema); - } - - public RowBuilder(List schema, int schemaversion) throws Exception { - this.schemaVersion = schemaversion; - calcSchemaOffset(schema); - } - - private void calcSchemaOffset(List schema) throws Exception { - strFieldStartOffset = CodecUtil.HEADER_LENGTH + CodecUtil.getBitMapSize(schema.size()); - this.schema = schema; - for (int idx = 0; idx < schema.size(); idx++) { - ColumnDesc column = schema.get(idx); - if (column.getDataType() == DataType.kVarchar || column.getDataType() == DataType.kString) { - offsetVec.add(strFieldCnt); - strIdx.add(idx); - strFieldCnt++; - } else { - if (CodecUtil.TYPE_SIZE_MAP.get(column.getDataType()) == null) { - throw new Exception("type is not supported"); - } else { - offsetVec.add(strFieldStartOffset); - strFieldStartOffset += CodecUtil.TYPE_SIZE_MAP.get(column.getDataType()); - } - } - } - } - - public void SetSchemaVersion(int version) { - this.schemaVersion = version; - } - - public int calTotalLength(List row) throws Exception { - if (row.size() != schema.size()) { - throw new Exception("row size is not equal schema size"); - } - int stringLength = 0; - for (Integer idx : strIdx) { - Object obj = row.get(idx); - if (obj == null) { - continue; - } - stringLength += ((String)obj).getBytes(CodecUtil.CHARSET).length; - } - return calTotalLength(stringLength); - } - - public int calTotalLength(int stringLength) throws Exception { - if (schema.size() == 0) { - return 0; - } - long totalLength = strFieldStartOffset + stringLength; - if (totalLength + strFieldCnt <= CodecUtil.UINT8_MAX) { - totalLength += strFieldCnt; - } else if (totalLength + strFieldCnt * 2 <= CodecUtil.UINT16_MAX) { - totalLength += strFieldCnt * 2; - } else if (totalLength + strFieldCnt * 3 <= CodecUtil.UINT24_MAX) { - totalLength += strFieldCnt * 3; - } else if (totalLength + strFieldCnt * 4 <= CodecUtil.UINT32_MAX) { - totalLength += strFieldCnt * 4; - } - if (totalLength > Integer.MAX_VALUE) { - throw new Exception("total length is bigger than integer max value"); - } - return (int) totalLength; - } - - public ByteBuffer setBuffer(ByteBuffer buffer, int size) { - if (buffer == null || size == 0 || - size < strFieldStartOffset + strFieldCnt) { - return null; - } - if (buffer.order() == ByteOrder.BIG_ENDIAN) { - buffer = buffer.order(ByteOrder.LITTLE_ENDIAN); - } - this.size = size; - buffer.put((byte) 1); // FVersion - buffer.put((byte) schemaVersion); // SVersion - buffer.putInt(size); // size - for (int idx = 0; idx < CodecUtil.getBitMapSize(schema.size()); idx++) { - buffer.put((byte)0xFF); - } - this.buf = buffer; - strAddrLength = CodecUtil.getAddrLength(size); - strOffset = strFieldStartOffset + strAddrLength * strFieldCnt; - return this.buf; - } - - private void setField(int index) { - int pos = CodecUtil.HEADER_LENGTH + (index >> 3); - byte bt = buf.get(pos); - buf.put(pos, (byte) (bt & (~(1 << (cnt & 0x07))))); - } - - private boolean check(DataType type) { - if (cnt >= schema.size()) { - return false; - } - ColumnDesc column = schema.get(cnt); - if (column.getDataType() != type) { - return false; - } - if (column.getDataType() != DataType.kVarchar && column.getDataType() != DataType.kString) { - if (CodecUtil.TYPE_SIZE_MAP.get(column.getDataType()) == null) { - return false; - } - } - return true; - } - private void setStrOffset(int strPos) { - if (strPos >= strFieldCnt) { - return; - } - int index = strFieldStartOffset + strAddrLength * strPos; - buf.position(index); - if (strAddrLength == 1) { - buf.put((byte) (strOffset & 0xFF)); - } else if (strAddrLength == 2) { - buf.putShort((short) (strOffset & 0xFFFF)); - } else if (strAddrLength == 3) { - buf.put((byte) (strOffset >> 16)); - buf.put((byte) ((strOffset & 0xFF00) >> 8)); - buf.put((byte) (strOffset & 0x00FF)); - } else { - buf.putInt(strOffset); - } - } - - public boolean appendNULL() { - ColumnDesc column = schema.get(cnt); - if (column.getDataType() == DataType.kVarchar || column.getDataType() == DataType.kString) { - int strPos = offsetVec.get(cnt); - setStrOffset(strPos + 1); - } - cnt++; - return true; - } - - public boolean appendBool(boolean val) { - if (!check(DataType.kBool)) { - return false; - } - setField(cnt); - buf.position(offsetVec.get(cnt)); - if (val) { - buf.put((byte) 1); - } else { - buf.put((byte) 0); - } - cnt++; - return true; - } - - public boolean appendInt(int val) { - if (!check(DataType.kInt)) { - return false; - } - setField(cnt); - buf.position(offsetVec.get(cnt)); - buf.putInt(val); - cnt++; - return true; - } - - public boolean appendSmallInt(short val) { - if (!check(DataType.kSmallInt)) { - return false; - } - setField(cnt); - buf.position(offsetVec.get(cnt)); - buf.putShort(val); - cnt++; - return true; - } - - public boolean appendTimestamp(long val) { - if (!check(DataType.kTimestamp)) { - return false; - } - setField(cnt); - buf.position(offsetVec.get(cnt)); - buf.putLong(val); - cnt++; - return true; - } - - public boolean appendBigInt(long val) { - if (!check(DataType.kBigInt)) { - return false; - } - setField(cnt); - buf.position(offsetVec.get(cnt)); - buf.putLong(val); - cnt++; - return true; - } - - public boolean appendFloat(float val) { - if (!check(DataType.kFloat)) { - return false; - } - setField(cnt); - buf.position(offsetVec.get(cnt)); - buf.putFloat(val); - cnt++; - return true; - } - - public boolean appendDouble(double val) { - if (!check(DataType.kDouble)) { - return false; - } - setField(cnt); - buf.position(offsetVec.get(cnt)); - buf.putDouble(val); - cnt++; - return true; - } - - public boolean appendDate(Date date) { - int dateInt = CodecUtil.dateToDateInt(date); - buf.position(offsetVec.get(cnt)); - buf.putInt(dateInt); - setField(cnt); - cnt++; - return true; - } - - public boolean appendString(String val) { - byte[] bytes = val.getBytes(CodecUtil.CHARSET); - int length = bytes.length; - if (val == null || (!check(DataType.kVarchar) && !check(DataType.kString))) { - return false; - } - if (strOffset + length > size) { - return false; - } - int strPos = offsetVec.get(cnt); - if (strPos == 0) { - setStrOffset(strPos); - } - if (length != 0) { - buf.position(strOffset); - buf.put(val.getBytes(CodecUtil.CHARSET), 0, length); - } - strOffset += length; - setStrOffset(strPos + 1); - setField(cnt); - cnt++; - return true; - } - - public static ByteBuffer encode(Object[] row, List schema, int schemaVer) throws Exception { - if (row == null || row.length == 0 || schema == null || schema.size() == 0 || row.length != schema.size()) { - throw new Exception("input error"); - } - int strLength = CodecUtil.calStrLength(row, schema); - RowBuilder builder = new RowBuilder(schema, schemaVer); - int size = builder.calTotalLength(strLength); - ByteBuffer buffer = ByteBuffer.allocate(size).order(ByteOrder.LITTLE_ENDIAN); - buffer = builder.setBuffer(buffer, size); - for (int i = 0; i < schema.size(); i++) { - ColumnDesc columnDesc = schema.get(i); - Object column = row[i]; - if (columnDesc.getNotNull() && column == null) { - throw new Exception("col " + columnDesc.getName() + " should not be null"); - } else if (column == null) { - builder.appendNULL(); - continue; - } - boolean ok = false; - switch (columnDesc.getDataType()) { - case kVarchar: - case kString: - ok = builder.appendString((String) column); - break; - case kBool: - ok = builder.appendBool((Boolean) column); - break; - case kSmallInt: - ok = builder.appendSmallInt((Short) column); - break; - case kInt: - ok = builder.appendInt((Integer) column); - break; - case kTimestamp: - if (column instanceof DateTime) { - ok = builder.appendTimestamp(((DateTime) column).getMillis()); - }else if (column instanceof Timestamp) { - ok = builder.appendTimestamp(((Timestamp) column).getTime()); - }else { - ok = builder.appendTimestamp((Long) column); - } - break; - case kBigInt: - ok = builder.appendBigInt((Long) column); - break; - case kFloat: - ok = builder.appendFloat((Float) column); - break; - case kDate: - ok = builder.appendDate((Date)column); - break; - case kDouble: - ok = builder.appendDouble((Double) column); - break; - default: - throw new Exception("unsupported data type"); - } - if (!ok) { - throw new Exception("append " + columnDesc.getDataType().toString() + " error"); - } - } - return buffer; - } - - public static ByteBuffer encode(Map row, List schema, Map blobKeys, int schemaVersion) throws Exception { - if (row == null || row.size() == 0 || schema == null || schema.size() == 0 || row.size() != schema.size()) { - throw new Exception("input error"); - } - int strLength = CodecUtil.calStrLength(row, schema); - RowBuilder builder = new RowBuilder(schema, schemaVersion); - int size = builder.calTotalLength(strLength); - ByteBuffer buffer = ByteBuffer.allocate(size).order(ByteOrder.LITTLE_ENDIAN); - buffer = builder.setBuffer(buffer, size); - for (int i = 0; i < schema.size(); i++) { - ColumnDesc columnDesc = schema.get(i); - Object column = row.get(columnDesc.getName()); - if (columnDesc.getNotNull() - && column == null) { - throw new Exception("col " + columnDesc.getName() + " should not be null"); - } else if (column == null) { - builder.appendNULL(); - continue; - } - boolean ok = false; - switch (columnDesc.getDataType()) { - case kString: - case kVarchar: - ok = builder.appendString((String) column); - break; - case kBool: - ok = builder.appendBool((Boolean) column); - break; - case kSmallInt: - ok = builder.appendSmallInt((Short) column); - break; - case kInt: - ok = builder.appendInt((Integer) column); - break; - case kTimestamp: - if (column instanceof DateTime) { - ok = builder.appendTimestamp(((DateTime) column).getMillis()); - }else if (column instanceof Timestamp) { - ok = builder.appendTimestamp(((Timestamp) column).getTime()); - }else { - ok = builder.appendTimestamp((Long) column); - } - break; - case kBigInt: - ok = builder.appendBigInt((Long) column); - break; - case kFloat: - ok = builder.appendFloat((Float) column); - break; - case kDate: - ok = builder.appendDate((Date)column); - break; - case kDouble: - ok = builder.appendDouble((Double) column); - break; - default: - throw new Exception("unsupported data type"); - } - if (!ok) { - throw new Exception("append " + columnDesc.getDataType().toString() + " error"); - } - } - return buffer; - } +public interface RowBuilder { + + boolean appendNULL(); + boolean appendBool(boolean val); + boolean appendInt(int val); + boolean appendSmallInt(short val); + boolean appendTimestamp(Timestamp val); + boolean appendBigInt(long val); + boolean appendFloat(float val); + boolean appendDouble(double val); + boolean appendDate(Date date); + boolean appendString(String val); + + boolean setNULL(int idx); + boolean setBool(int idx, boolean val); + boolean setInt(int idx, int val); + boolean setSmallInt(int idx, short val); + boolean setTimestamp(int idx, Timestamp val); + boolean setBigInt(int idx, long val); + boolean setFloat(int idx, float val); + boolean setDouble(int idx, double val); + boolean setDate(int idx, Date date); + boolean setString(int idx, String val); + + boolean build(); + ByteBuffer getValue(); } - diff --git a/java/openmldb-common/src/main/java/com/_4paradigm/openmldb/common/codec/RowView.java b/java/openmldb-common/src/main/java/com/_4paradigm/openmldb/common/codec/RowView.java index ad1ff0dce7c..adca2d806ce 100644 --- a/java/openmldb-common/src/main/java/com/_4paradigm/openmldb/common/codec/RowView.java +++ b/java/openmldb-common/src/main/java/com/_4paradigm/openmldb/common/codec/RowView.java @@ -19,52 +19,40 @@ import com._4paradigm.openmldb.proto.Type.DataType; import com._4paradigm.openmldb.proto.Common.ColumnDesc; import org.joda.time.DateTime; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.sql.Date; -import java.util.ArrayList; +import java.sql.Timestamp; import java.util.List; public class RowView { + private CodecMetaData metaData; private final static byte BOOL_FALSE = 0; private ByteBuffer row = null; private int size = 0; - private List schema = new ArrayList<>(); - private int stringFieldCnt = 0; - private int strFieldStartOffset = 0; - private int strAddrLength = 0; - private List offsetVec = new ArrayList<>(); + private List schema; private boolean isValid = false; public RowView(List schema) throws Exception { - this.schema = schema; - this.isValid = true; - if (schema.size() == 0) { - isValid = false; - return; - } - init(); + this(new CodecMetaData(schema, true)); } public RowView(List schema, ByteBuffer row, int size) throws Exception { - this.schema = schema; - this.isValid = true; + this(new CodecMetaData(schema, true)); this.size = size; if (row.order() == ByteOrder.BIG_ENDIAN) { row = row.order(ByteOrder.LITTLE_ENDIAN); } this.row = row; - if (schema.size() == 0) { - isValid = false; - return; - } - if (init()) { - reset(row, size); - } + reset(row, size); + } + + public RowView(CodecMetaData metaData) { + this.metaData = metaData; + this.schema = metaData.getSchema(); + this.isValid = true; } public static int getSchemaVersion(ByteBuffer row) { @@ -75,38 +63,21 @@ public static int getSchemaVersion(ByteBuffer row) { return bt; } - private boolean init() throws Exception { - strFieldStartOffset = CodecUtil.HEADER_LENGTH + CodecUtil.getBitMapSize(schema.size()); - for (int idx = 0; idx < schema.size(); idx++) { - ColumnDesc column = schema.get(idx); - if (column.getDataType() == DataType.kVarchar || column.getDataType() == DataType.kString) { - offsetVec.add(stringFieldCnt); - stringFieldCnt++; - } else { - if (CodecUtil.TYPE_SIZE_MAP.get(column.getDataType()) == null) { - isValid = false; - throw new Exception("type is not supported"); - } else { - offsetVec.add(strFieldStartOffset); - strFieldStartOffset += CodecUtil.TYPE_SIZE_MAP.get(column.getDataType()); - } - } - } - return true; - } public boolean reset(ByteBuffer row, int size) { - if (schema.size() == 0 || row == null || size <= CodecUtil.HEADER_LENGTH || - row.getInt(CodecUtil.VERSION_LENGTH) != size) { + if (schema.size() == 0 || row == null || size <= CodecUtil.HEADER_LENGTH) { isValid = false; return false; } if (row.order() == ByteOrder.BIG_ENDIAN) { row = row.order(ByteOrder.LITTLE_ENDIAN); } + if (row.getInt(CodecUtil.VERSION_LENGTH) != size) { + isValid = false; + return false; + } this.row = row; this.size = size; - strAddrLength = CodecUtil.getAddrLength(size); return true; } @@ -124,7 +95,6 @@ private boolean reset(ByteBuffer row) { isValid = false; return false; } - strAddrLength = CodecUtil.getAddrLength(size); return true; } @@ -168,8 +138,8 @@ public Integer getInt(int idx) throws Exception { return (Integer) getValue(row, idx, DataType.kInt); } - public Long getTimestamp(int idx) throws Exception { - return (Long) getValue(row, idx, DataType.kTimestamp); + public Timestamp getTimestamp(int idx) throws Exception { + return (Timestamp) getValue(row, idx, DataType.kTimestamp); } public Long getBigInt(int idx) throws Exception { @@ -188,6 +158,10 @@ public Double getDouble(int idx) throws Exception { return (Double) getValue(row, idx, DataType.kDouble); } + public Date getDate(int idx) throws Exception { + return (Date) getValue(row, idx, DataType.kDate); + } + public Object getIntegersNum(ByteBuffer row, int idx, DataType type) throws Exception { switch (type) { case kSmallInt: { @@ -235,7 +209,13 @@ public Object getValue(ByteBuffer row, int idx, DataType type) throws Exception } ColumnDesc column = schema.get(idx); if (column.getDataType() != type) { - throw new Exception("data type mismatch"); + if (type == DataType.kString || type == DataType.kVarchar) { + if (column.getDataType() != DataType.kString && column.getDataType() != DataType.kVarchar) { + throw new Exception("data type mismatch"); + } + } else { + throw new Exception("data type mismatch"); + } } int rowSize = getSize(row); if (rowSize <= CodecUtil.HEADER_LENGTH) { @@ -255,7 +235,7 @@ private Object readObject(ByteBuffer buf, int index, DataType dt, int rowSize, if (isNull(buf, index)) { return null; } - int offset = offsetVec.get(index); + int offset = metaData.getOffsetList().get(index); switch (dt) { case kBool: return buf.get(offset) == BOOL_FALSE ? false: true; @@ -270,18 +250,18 @@ private Object readObject(ByteBuffer buf, int index, DataType dt, int rowSize, case kDouble: return buf.getDouble(offset); case kTimestamp: - return new DateTime(buf.getLong(offset)); + return new Timestamp(buf.getLong(offset)); case kDate: int date = buf.getInt(offset); return CodecUtil.dateIntToDate(date); case kVarchar: case kString: int nextStrFieldOffset = 0; - if (offset < stringFieldCnt - 1) { + if (offset < metaData.getStrFieldCnt() - 1) { nextStrFieldOffset = offset + 1; } return getStrField(buf, offset, nextStrFieldOffset, - strFieldStartOffset, localStrAddrLength, rowSize); + metaData.getStrFieldStartOffset(), localStrAddrLength, rowSize); default: throw new Exception("invalid column type" + dt.name()); } diff --git a/java/openmldb-common/src/test/java/com/_4paradigm/openmldb/common/RowCodecTest.java b/java/openmldb-common/src/test/java/com/_4paradigm/openmldb/common/RowCodecTest.java index aaffe7cb19f..ff1c51f06ea 100644 --- a/java/openmldb-common/src/test/java/com/_4paradigm/openmldb/common/RowCodecTest.java +++ b/java/openmldb-common/src/test/java/com/_4paradigm/openmldb/common/RowCodecTest.java @@ -16,47 +16,67 @@ package com._4paradigm.openmldb.common; +import com._4paradigm.openmldb.common.codec.FlexibleRowBuilder; +import com._4paradigm.openmldb.common.codec.RowBuilder; import com._4paradigm.openmldb.proto.Type.DataType; import com._4paradigm.openmldb.proto.Common.ColumnDesc; import com._4paradigm.openmldb.common.codec.RowView; -import com._4paradigm.openmldb.common.codec.RowBuilder; +import com._4paradigm.openmldb.common.codec.ClassicRowBuilder; import org.testng.Assert; +import org.testng.annotations.DataProvider; import org.testng.annotations.Test; import java.nio.ByteBuffer; import java.nio.ByteOrder; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; +import java.sql.Timestamp; +import java.sql.Date; +import java.util.*; public class RowCodecTest { - @Test - public void testNull() { + private List typeList = new ArrayList<>(Arrays.asList( + DataType.kBool, + DataType.kSmallInt, + DataType.kInt, + DataType.kBigInt, + DataType.kFloat, + DataType.kDouble, + DataType.kDate, + DataType.kTimestamp, + DataType.kString, + DataType.kVarchar) + ); + + @DataProvider(name = "builder") + Object[] getData() { + return new Object[] {"classic", "flexible"}; + } + + @Test(dataProvider = "builder") + public void testNull(String builderName) { try { List schema = new ArrayList(); - { - ColumnDesc col1 = ColumnDesc.newBuilder().setName("col1").setDataType(DataType.kSmallInt).build(); - schema.add(col1); - } - { - ColumnDesc col2 = ColumnDesc.newBuilder().setName("col2").setDataType(DataType.kBool).build(); - schema.add(col2); - } - { - ColumnDesc col3 = ColumnDesc.newBuilder().setName("col3").setDataType(DataType.kVarchar).build(); - schema.add(col3); - } - RowBuilder builder = new RowBuilder(schema); - int size = builder.calTotalLength(9); - ByteBuffer buffer = ByteBuffer.allocate(size).order(ByteOrder.LITTLE_ENDIAN); - buffer = builder.setBuffer(buffer, size); + schema.add(ColumnDesc.newBuilder().setName("col1").setDataType(DataType.kSmallInt).build()); + schema.add(ColumnDesc.newBuilder().setName("col2").setDataType(DataType.kBool).build()); + schema.add(ColumnDesc.newBuilder().setName("col3").setDataType(DataType.kVarchar).build()); + RowBuilder builder; + if (builderName.equals("classic")) { + ClassicRowBuilder cBuilder = new ClassicRowBuilder(schema); + int size = cBuilder.calTotalLength(9); + ByteBuffer buffer = ByteBuffer.allocate(size).order(ByteOrder.LITTLE_ENDIAN); + cBuilder.setBuffer(buffer, size); + builder = cBuilder; + } else { + builder = new FlexibleRowBuilder(schema); + } Assert.assertTrue(builder.appendNULL()); Assert.assertTrue(builder.appendBool(false)); Assert.assertTrue(builder.appendString("123456789")); - - RowView rowView = new RowView(schema, buffer, size); + Assert.assertTrue(builder.build()); + ByteBuffer buffer = builder.getValue(); + RowView rowView = new RowView(schema, buffer, buffer.capacity()); Assert.assertTrue(rowView.isNull(0)); + Assert.assertFalse(rowView.isNull(1)); Assert.assertEquals(rowView.getBool(1), new Boolean(false)); Assert.assertEquals(rowView.getString(2), "123456789"); @@ -64,45 +84,40 @@ public void testNull() { Object value = rowView2.getValue(buffer, 2, DataType.kVarchar); Assert.assertEquals((String) value, "123456789"); } catch (Exception e) { + e.printStackTrace(); Assert.assertTrue(false); } } - @Test - public void testNormal() { + @Test(dataProvider = "builder") + public void testNormal(String builderName) { try { List schema = new ArrayList(); - { - ColumnDesc col1 = ColumnDesc.newBuilder().setName("col1").setDataType(DataType.kInt).build(); - schema.add(col1); - } - { - ColumnDesc col2 = ColumnDesc.newBuilder().setName("col2").setDataType(DataType.kSmallInt).build(); - schema.add(col2); - } - { - ColumnDesc col3 = ColumnDesc.newBuilder().setName("col3").setDataType(DataType.kFloat).build(); - schema.add(col3); - } - { - ColumnDesc col4 = ColumnDesc.newBuilder().setName("col4").setDataType(DataType.kDouble).build(); - schema.add(col4); - } - { - ColumnDesc col5 = ColumnDesc.newBuilder().setName("col5").setDataType(DataType.kBigInt).build(); - schema.add(col5); - } - RowBuilder builder = new RowBuilder(schema); - int size = builder.calTotalLength(1); - ByteBuffer buffer = ByteBuffer.allocate(size).order(ByteOrder.LITTLE_ENDIAN); - buffer = builder.setBuffer(buffer, size); + schema.add(ColumnDesc.newBuilder().setName("col1").setDataType(DataType.kInt).build()); + schema.add(ColumnDesc.newBuilder().setName("col2").setDataType(DataType.kSmallInt).build()); + schema.add(ColumnDesc.newBuilder().setName("col3").setDataType(DataType.kFloat).build()); + schema.add(ColumnDesc.newBuilder().setName("col4").setDataType(DataType.kDouble).build()); + schema.add(ColumnDesc.newBuilder().setName("col5").setDataType(DataType.kBigInt).build()); + RowBuilder builder; + if (builderName.equals("classic")) { + ClassicRowBuilder cBuilder = new ClassicRowBuilder(schema); + int size = cBuilder.calTotalLength(1); + ByteBuffer buffer = ByteBuffer.allocate(size).order(ByteOrder.LITTLE_ENDIAN); + buffer = cBuilder.setBuffer(buffer, size); + cBuilder.setBuffer(buffer, size); + builder = cBuilder; + } else { + builder = new FlexibleRowBuilder(schema); + } Assert.assertTrue(builder.appendInt(1)); Assert.assertTrue(builder.appendSmallInt((short) 2)); Assert.assertTrue(builder.appendFloat(3.1f)); Assert.assertTrue(builder.appendDouble(4.1)); Assert.assertTrue(builder.appendBigInt(5)); + builder.build(); - RowView rowView = new RowView(schema, buffer, size); + ByteBuffer buffer = builder.getValue(); + RowView rowView = new RowView(schema, buffer, buffer.capacity()); Assert.assertEquals(rowView.getInt(0), new Integer(1)); Assert.assertEquals(rowView.getSmallInt(1), new Short((short) 2)); Assert.assertEquals(rowView.getFloat(2), 3.1f); @@ -113,8 +128,8 @@ public void testNormal() { } } - @Test - public void testEncode() { + @Test(dataProvider = "builder") + public void testEncode(String builderName) { try { List schema = new ArrayList(); for (int i = 0; i < 10; i++) { @@ -129,10 +144,16 @@ public void testEncode() { } schema.add(col.build()); } - RowBuilder builder = new RowBuilder(schema); - int size = builder.calTotalLength(30); - ByteBuffer buffer = ByteBuffer.allocate(size).order(ByteOrder.LITTLE_ENDIAN); - buffer = builder.setBuffer(buffer, size); + RowBuilder builder; + if (builderName.equals("classic")) { + ClassicRowBuilder cBuilder = new ClassicRowBuilder(schema); + int size = cBuilder.calTotalLength(30); + ByteBuffer buffer = ByteBuffer.allocate(size).order(ByteOrder.LITTLE_ENDIAN); + cBuilder.setBuffer(buffer, size); + builder = cBuilder; + } else { + builder = new FlexibleRowBuilder(schema); + } for (int i = 0; i < 10; i++) { if (i % 3 == 0) { @@ -145,8 +166,10 @@ public void testEncode() { } } Assert.assertFalse(builder.appendSmallInt((short) 1)); + Assert.assertTrue(builder.build()); + ByteBuffer buffer = builder.getValue(); - RowView rowView = new RowView(schema, buffer, size); + RowView rowView = new RowView(schema, buffer, buffer.capacity()); for (int i = 0; i < 10; i++) { if (i % 3 == 0) { Assert.assertEquals(rowView.getSmallInt(i), new Short((short) i)); @@ -164,12 +187,13 @@ public void testEncode() { Assert.assertTrue(true); } } catch (Exception e) { + e.printStackTrace(); Assert.assertTrue(false); } } - @Test - public void testAppendNull() { + @Test(dataProvider = "builder") + public void testAppendNull(String builderName) { try { List schema = new ArrayList(); for (int i = 0; i < 20; i++) { @@ -184,10 +208,17 @@ public void testAppendNull() { } schema.add(col.build()); } - RowBuilder builder = new RowBuilder(schema); - int size = builder.calTotalLength(30); - ByteBuffer buffer = ByteBuffer.allocate(size).order(ByteOrder.LITTLE_ENDIAN); - buffer = builder.setBuffer(buffer, size); + RowBuilder builder; + if (builderName.equals("classic")) { + ClassicRowBuilder cBuilder = new ClassicRowBuilder(schema); + int size = cBuilder.calTotalLength(30); + ByteBuffer buffer = ByteBuffer.allocate(size).order(ByteOrder.LITTLE_ENDIAN); + buffer = cBuilder.setBuffer(buffer, size); + cBuilder.setBuffer(buffer, size); + builder = cBuilder; + } else { + builder = new FlexibleRowBuilder(schema); + } for (int i = 0; i < 20; i++) { if (i % 2 == 0) { @@ -204,8 +235,10 @@ public void testAppendNull() { } } Assert.assertFalse(builder.appendSmallInt((short) 1)); + Assert.assertTrue(builder.build()); + ByteBuffer buffer = builder.getValue(); - RowView rowView = new RowView(schema, buffer, size); + RowView rowView = new RowView(schema, buffer, buffer.capacity()); for (int i = 0; i < 20; i++) { if (i % 2 == 0) { Assert.assertTrue(rowView.isNull(i)); @@ -238,8 +271,8 @@ public void testAppendNull() { } } - @Test - public void testAppendNullAndEmpty() { + @Test(dataProvider = "builder") + public void testAppendNullAndEmpty(String builderName) { try { List schema = new ArrayList(); for (int i = 0; i < 20; i++) { @@ -252,10 +285,17 @@ public void testAppendNullAndEmpty() { } schema.add(col.build()); } - RowBuilder builder = new RowBuilder(schema); - int size = builder.calTotalLength(30); - ByteBuffer buffer = ByteBuffer.allocate(size).order(ByteOrder.LITTLE_ENDIAN); - buffer = builder.setBuffer(buffer, size); + RowBuilder builder; + if (builderName.equals("classic")) { + ClassicRowBuilder cBuilder = new ClassicRowBuilder(schema); + int size = cBuilder.calTotalLength(30); + ByteBuffer buffer = ByteBuffer.allocate(size).order(ByteOrder.LITTLE_ENDIAN); + cBuilder.setBuffer(buffer, size); + builder = cBuilder; + } else { + builder = new FlexibleRowBuilder(schema); + } + for (int i = 0; i < 20; i++) { if (i % 2 == 0) { @@ -276,8 +316,9 @@ public void testAppendNullAndEmpty() { } } Assert.assertFalse(builder.appendSmallInt((short) 1)); - - RowView rowView = new RowView(schema, buffer, size); + Assert.assertTrue(builder.build()); + ByteBuffer buffer = builder.getValue(); + RowView rowView = new RowView(schema, buffer, buffer.capacity()); for (int i = 0; i < 20; i++) { if (i % 2 == 0) { if (i % 3 == 0) { @@ -309,8 +350,8 @@ public void testAppendNullAndEmpty() { } } - @Test - public void testManyCol() { + @Test(dataProvider = "builder") + public void testManyCol(String builderName) { int[] arr = {10, 20, 50, 100, 1000, 10000, 100000}; try { for (int colNum : arr) { @@ -335,35 +376,43 @@ public void testManyCol() { schema.add(col.build()); } } - RowBuilder builder = new RowBuilder(schema); - int size = builder.calTotalLength(10 * colNum); - ByteBuffer buffer = ByteBuffer.allocate(size).order(ByteOrder.LITTLE_ENDIAN); - buffer = builder.setBuffer(buffer, size); + RowBuilder builder; + if (builderName.equals("classic")) { + ClassicRowBuilder cBuilder = new ClassicRowBuilder(schema); + int size = cBuilder.calTotalLength(10 * colNum); + ByteBuffer buffer = ByteBuffer.allocate(size).order(ByteOrder.LITTLE_ENDIAN); + cBuilder.setBuffer(buffer, size); + builder = cBuilder; + } else { + builder = new FlexibleRowBuilder(schema); + } long base = 1000000000l; long ts = 1576811755000l; for (int idx = 0; idx < colNum; idx++) { - String s = String.join("", Collections.nCopies(10, String.valueOf((base + idx) % 10))); - Assert.assertTrue(builder.appendString(s)); + Assert.assertTrue(builder.appendString(String.valueOf(base + idx))); Assert.assertTrue(builder.appendBigInt(ts + idx)); Assert.assertTrue(builder.appendDouble(1.3)); } + Assert.assertTrue(builder.build()); + ByteBuffer buffer = builder.getValue(); - RowView rowView = new RowView(schema, buffer, size); + RowView rowView = new RowView(schema, buffer, buffer.capacity()); for (int idx = 0; idx < colNum; idx++) { - String s = String.join("", Collections.nCopies(10, String.valueOf((base + idx) % 10))); + String s = String.valueOf(base + idx); Assert.assertEquals(rowView.getString(3 * idx), s); Assert.assertEquals(rowView.getBigInt(3 * idx + 1), new Long(ts + idx)); Assert.assertEquals(rowView.getDouble(3 * idx + 2), 1.3); } } } catch (Exception e) { + e.printStackTrace(); Assert.assertTrue(false); } } - @Test - public void testNotAppendString() { + @Test(dataProvider = "builder") + public void testNotAppendString(String builderName) { try { List schema = new ArrayList(); for (int i = 0; i < 10; i++) { @@ -372,10 +421,16 @@ public void testNotAppendString() { col.setDataType(DataType.kVarchar); schema.add(col.build()); } - RowBuilder builder = new RowBuilder(schema); - int size = builder.calTotalLength(100); - ByteBuffer buffer = ByteBuffer.allocate(size).order(ByteOrder.LITTLE_ENDIAN); - buffer = builder.setBuffer(buffer, size); + RowBuilder builder; + if (builderName.equals("classic")) { + ClassicRowBuilder cBuilder = new ClassicRowBuilder(schema); + int size = cBuilder.calTotalLength(100); + ByteBuffer buffer = ByteBuffer.allocate(size).order(ByteOrder.LITTLE_ENDIAN); + cBuilder.setBuffer(buffer, size); + builder = cBuilder; + } else { + builder = new FlexibleRowBuilder(schema); + } for (int i = 0; i < 7; i++) { if (i == 0) { @@ -386,17 +441,128 @@ public void testNotAppendString() { } } Assert.assertFalse(builder.appendSmallInt((short) 1)); + if (builderName.equals("classic")) { + Assert.assertTrue(builder.build()); + ByteBuffer buffer = builder.getValue(); + RowView rowView = new RowView(schema, buffer, buffer.capacity()); + for (int i = 0; i < 10; i++) { + if (i == 0) { + Assert.assertTrue(rowView.isNull(i)); + } else if (i < 7) { + String s = String.join("", Collections.nCopies(10, String.valueOf(i % 10))); + Assert.assertEquals(rowView.getString(i), s); + } else { + Assert.assertTrue(rowView.isNull(i)); + } + } + } else { + Assert.assertFalse(builder.build()); + } + } catch (Exception e) { + e.printStackTrace(); + Assert.assertTrue(false); + } + } + + @Test(dataProvider = "builder") + public void testEncodeRow(String builderName) { + try { + List schema = new ArrayList(); + for (int i = 0; i < 10; i++) { + ColumnDesc.Builder col = ColumnDesc.newBuilder(); + col.setName("col" + String.valueOf(i)); + if (i % 2 == 0) { + col.setDataType(DataType.kBigInt); + } else { + col.setDataType(DataType.kVarchar); + } + schema.add(col.build()); + } + List row = new ArrayList<>(); + for (int i = 0; i < 10; i++) { + if (i % 2 == 0) { + row.add(Long.valueOf(i)); + } else { + row.add(new String("aaa") + String.valueOf(i)); + } + } + RowBuilder builder; + if (builderName.equals("classic")) { + ClassicRowBuilder cBuilder = new ClassicRowBuilder(schema); + int size = cBuilder.calTotalLength(row); + ByteBuffer buffer = ByteBuffer.allocate(size).order(ByteOrder.LITTLE_ENDIAN); + cBuilder.setBuffer(buffer, size); + builder = cBuilder; + } else { + builder = new FlexibleRowBuilder(schema); + } + + for (int i = 0; i < 10; i++) { + if (i % 2 == 0) { + Assert.assertTrue(builder.appendBigInt((Long)row.get(i))); + } else { + Assert.assertTrue(builder.appendString((String)row.get(i))); + } + } + Assert.assertFalse(builder.appendSmallInt((short) 1)); + Assert.assertTrue(builder.build()); + ByteBuffer buffer = builder.getValue(); - RowView rowView = new RowView(schema, buffer, size); + RowView rowView = new RowView(schema, buffer, buffer.capacity()); for (int i = 0; i < 10; i++) { - if (i == 0) { - Assert.assertTrue(rowView.isNull(i)); - } else if (i < 7){ - String s = String.join("", Collections.nCopies(10, String.valueOf(i % 10))); - Assert.assertEquals(rowView.getString(i), s); + if (i % 2 == 0) { + Assert.assertTrue(rowView.getBigInt(i) == i); } else { + Assert.assertEquals(new String("aaa") + String.valueOf(i), rowView.getString(i)); + } + } + } catch (Exception e) { + Assert.fail(); + } + } + + @Test(dataProvider = "builder") + public void testAppendNull2(String builderName) { + try { + List schema = new ArrayList(); + for (int i = 0; i < 2; i++) { + ColumnDesc.Builder col = ColumnDesc.newBuilder(); + col.setName("col" + i); + col.setDataType(DataType.kVarchar); + schema.add(col.build()); + } + RowBuilder builder; + if (builderName.equals("classic")) { + ClassicRowBuilder cBuilder = new ClassicRowBuilder(schema); + int size = cBuilder.calTotalLength(10); + ByteBuffer buffer = ByteBuffer.allocate(size).order(ByteOrder.LITTLE_ENDIAN); + buffer = cBuilder.setBuffer(buffer, size); + cBuilder.setBuffer(buffer, size); + builder = cBuilder; + } else { + builder = new FlexibleRowBuilder(schema); + } + + for (int i = 0; i < 2; i++) { + if (i == 0) { + Assert.assertTrue(builder.appendNULL()); + continue; + } + String s = String.join("", Collections.nCopies(10, String.valueOf(i))); + Assert.assertTrue(builder.appendString(s)); + } + Assert.assertTrue(builder.build()); + ByteBuffer buffer = builder.getValue(); + + RowView rowView = new RowView(schema, buffer, buffer.capacity()); + for (int i = 0; i < 2; i++) { + if (i == 0) { Assert.assertTrue(rowView.isNull(i)); + Assert.assertEquals(rowView.getString(i), null); + continue; } + String s = String.join("", Collections.nCopies(10, String.valueOf(i))); + Assert.assertEquals(rowView.getString(i), s); } } catch (Exception e) { e.printStackTrace(); @@ -404,8 +570,222 @@ public void testNotAppendString() { } } - @Test - public void testEncodeRow() { + @DataProvider(name = "columnNum") + Object[] getColNum() { + return new Object[] {1, 3, 5, 10, 20, 30, 50, 100, 200, 300, 500, 1000, 2000, 3000, 5000, 10000, 20000, 100000}; + } + + public String genRandomString(int len) { + Random r = new Random(); + char[] arr = new char[len]; + for (int i = 0; i < len; i++) { + arr[i] = (char)(32 + (int)(94 * r.nextDouble())); + } + return new String(arr); + } + + Object[] genData(List schema) { + Random r = new Random(); + Object[] data = new Object[schema.size()]; + for (int idx = 0; idx < schema.size(); idx++) { + if (r.nextInt() % 5 == 0) { + data[idx] = null; + continue; + } + DataType type = schema.get(idx).getDataType(); + if (type == DataType.kBool) { + data[idx] = r.nextInt() % 2 == 0 ? true : false; + } else if (type == DataType.kSmallInt) { + data[idx] = (short)r.nextInt(10000); + } else if (type == DataType.kInt) { + data[idx] = r.nextInt(10000); + } else if (type == DataType.kBigInt) { + data[idx] = (long)r.nextInt(10000); + } else if (type == DataType.kFloat) { + data[idx] = r.nextFloat(); + } else if (type == DataType.kDouble) { + data[idx] = r.nextDouble(); + } else if (type == DataType.kDate) { + data[idx] = new java.sql.Date(r.nextInt(8000), r.nextInt(11), r.nextInt(25)); + } else if (type == DataType.kTimestamp) { + data[idx] = new Timestamp(System.currentTimeMillis()); + } else if (type == DataType.kVarchar || type == DataType.kString) { + data[idx] = r.nextInt() % 3 == 0 ? "" : genRandomString(r.nextInt(10)); + } + } + return data; + } + + void setData(RowBuilder builder, int idx, DataType type, Object obj) { + if (obj == null) { + builder.setNULL(idx); + return; + } + if (type == DataType.kBool) { + builder.setBool(idx, (boolean)obj); + } else if (type == DataType.kSmallInt) { + builder.setSmallInt(idx, (short)obj); + } else if (type == DataType.kInt) { + builder.setInt(idx, (int)obj); + } else if (type == DataType.kBigInt) { + builder.setBigInt(idx, (long)obj); + } else if (type == DataType.kFloat) { + builder.setFloat(idx, (float)obj); + } else if (type == DataType.kDouble) { + builder.setDouble(idx, (double)obj); + } else if (type == DataType.kDate) { + builder.setDate(idx, (java.sql.Date)obj); + } else if (type == DataType.kTimestamp) { + builder.setTimestamp(idx, (Timestamp) obj); + } else if (type == DataType.kString || type == DataType.kVarchar) { + builder.setString(idx, (String)obj); + } + } + + void checkData(RowView rowView, int idx, DataType type, Object exp) throws Exception { + if (exp == null) { + Assert.assertTrue(rowView.isNull(idx)); + return; + } else { + Assert.assertFalse(rowView.isNull(idx)); + } + if (type == DataType.kBool) { + Assert.assertEquals(rowView.getBool(idx), exp); + } else if (type == DataType.kSmallInt) { + Assert.assertEquals(rowView.getSmallInt(idx), exp); + } else if (type == DataType.kInt) { + Assert.assertEquals(rowView.getInt(idx), exp); + } else if (type == DataType.kBigInt) { + Assert.assertEquals(rowView.getBigInt(idx), exp); + } else if (type == DataType.kFloat) { + Assert.assertEquals(rowView.getFloat(idx), exp); + } else if (type == DataType.kDouble) { + Assert.assertEquals(rowView.getDouble(idx), exp); + } else if (type == DataType.kDate) { + Assert.assertEquals(rowView.getDate(idx), exp); + } else if (type == DataType.kTimestamp) { + Assert.assertEquals(rowView.getTimestamp(idx), exp); + } else if (type == DataType.kString || type == DataType.kVarchar) { + Assert.assertEquals(rowView.getString(idx), (String)exp); + } else { + Assert.fail(); + } + } + + @Test(dataProvider = "columnNum") + public void testDisorderPutBase(int columnNum) { + Random r = new Random(); + List schema = new ArrayList(); + List idx = new ArrayList<>(); + for (int i = 0; i < columnNum; i++) { + ColumnDesc.Builder col = ColumnDesc.newBuilder(); + col.setName("col" + i); + while (true) { + DataType type = typeList.get(r.nextInt(typeList.size())); + if (type != DataType.kString && type != DataType.kVarchar) { + col.setDataType(type); + break; + } + } + schema.add(col.build()); + idx.add(i); + } + Collections.shuffle(idx); + Object[] data = genData(schema); + try { + FlexibleRowBuilder builder = new FlexibleRowBuilder(schema); + for (Integer i : idx) { + setData(builder, i, schema.get(i).getDataType(), data[i]); + } + Assert.assertTrue(builder.build()); + ByteBuffer buffer = builder.getValue(); + RowView rowView = new RowView(schema, buffer, buffer.capacity()); + for (Integer i : idx) { + checkData(rowView, i, schema.get(i).getDataType(), data[i]); + } + } catch (Exception e) { + e.printStackTrace(); + Assert.fail(); + } + } + + @Test(dataProvider = "columnNum") + public void testDisorderPut(int columnNum) { + Random r = new Random(); + List schema = new ArrayList(); + List idx = new ArrayList<>(); + for (int i = 0; i < columnNum; i++) { + ColumnDesc.Builder col = ColumnDesc.newBuilder(); + col.setName("col" + i); + DataType type = typeList.get(r.nextInt(typeList.size())); + col.setDataType(type); + schema.add(col.build()); + idx.add(i); + } + Collections.shuffle(idx); + Object[] data = genData(schema); + try { + FlexibleRowBuilder builder = new FlexibleRowBuilder(schema); + for (Integer i : idx) { + setData(builder, i, schema.get(i).getDataType(), data[i]); + } + Assert.assertTrue(builder.build()); + ByteBuffer buffer = builder.getValue(); + RowView rowView = new RowView(schema, buffer, buffer.capacity()); + for (Integer i : idx) { + try { + checkData(rowView, i, schema.get(i).getDataType(), data[i]); + } catch (Exception e) { + e.printStackTrace(); + Assert.fail(); + } + } + } catch (Exception e) { + e.printStackTrace(); + Assert.fail(); + } + } + + @Test(dataProvider = "columnNum") + public void testDisorderStringOnly(int columnNum) { + Random r = new Random(); + List schema = new ArrayList(); + List idx = new ArrayList<>(); + String[] data = new String[columnNum]; + for (int i = 0; i < columnNum; i++) { + ColumnDesc.Builder col = ColumnDesc.newBuilder(); + col.setName("col" + i); + col.setDataType(DataType.kString); + schema.add(col.build()); + idx.add(i); + data[i] = genRandomString(r.nextInt(1000)); + } + Collections.shuffle(idx); + + try { + FlexibleRowBuilder builder = new FlexibleRowBuilder(schema); + for (Integer i : idx) { + setData(builder, i, schema.get(i).getDataType(), data[i]); + } + Assert.assertTrue(builder.build()); + ByteBuffer buffer = builder.getValue(); + RowView rowView = new RowView(schema, buffer, buffer.capacity()); + for (Integer i : idx) { + try { + checkData(rowView, i, schema.get(i).getDataType(), data[i]); + } catch (Exception e) { + e.printStackTrace(); + Assert.fail(); + } + } + } catch (Exception e) { + e.printStackTrace(); + Assert.fail(); + } + } + + @Test(dataProvider = "builder") + public void testSetMultiTimes(String builderName) { try { List schema = new ArrayList(); for (int i = 0; i < 10; i++) { @@ -426,21 +806,35 @@ public void testEncodeRow() { row.add(new String("aaa") + String.valueOf(i)); } } - RowBuilder builder = new RowBuilder(schema); - int size = builder.calTotalLength(row); - ByteBuffer buffer = ByteBuffer.allocate(size).order(ByteOrder.LITTLE_ENDIAN); - buffer = builder.setBuffer(buffer, size); + RowBuilder builder; + if (builderName.equals("classic")) { + ClassicRowBuilder cBuilder = new ClassicRowBuilder(schema); + int size = cBuilder.calTotalLength(row); + ByteBuffer buffer = ByteBuffer.allocate(size).order(ByteOrder.LITTLE_ENDIAN); + cBuilder.setBuffer(buffer, size); + builder = cBuilder; + } else { + builder = new FlexibleRowBuilder(schema); + } for (int i = 0; i < 10; i++) { if (i % 2 == 0) { - Assert.assertTrue(builder.appendBigInt((Long)row.get(i))); + Assert.assertTrue(builder.setBigInt(i, (Long)row.get(i))); + if (builder instanceof ClassicRowBuilder) { + Assert.assertFalse(builder.setBigInt(i, (Long)row.get(i))); + } else { + Assert.assertTrue(builder.setBigInt(i, (Long)row.get(i))); + } } else { - Assert.assertTrue(builder.appendString((String)row.get(i))); + Assert.assertTrue(builder.setString(i, (String)row.get(i))); + Assert.assertFalse(builder.setString(i, (String)row.get(i))); } } Assert.assertFalse(builder.appendSmallInt((short) 1)); + Assert.assertTrue(builder.build()); + ByteBuffer buffer = builder.getValue(); - RowView rowView = new RowView(schema, buffer, size); + RowView rowView = new RowView(schema, buffer, buffer.capacity()); for (int i = 0; i < 10; i++) { if (i % 2 == 0) { Assert.assertTrue(rowView.getBigInt(i) == i); @@ -449,9 +843,148 @@ public void testEncodeRow() { } } } catch (Exception e) { + e.printStackTrace(); Assert.fail(); } } + @Test + public void testSpecialCase() { + List types = new ArrayList<>(Arrays.asList( + /* 0 */ DataType.kTimestamp, + /* 1 */ DataType.kBool, + /* 2 */ DataType.kSmallInt, + /* 3 */ DataType.kInt, + /* 4 */ DataType.kVarchar, + /* 5 */ DataType.kBool, + /* 6 */ DataType.kTimestamp, + /* 7 */ DataType.kFloat, + /* 8 */ DataType.kInt, + /* 9 */ DataType.kInt, + /* 10 */ DataType.kString, + /* 11 */ DataType.kVarchar, + /* 12 */ DataType.kInt, + /* 13 */ DataType.kTimestamp, + /* 14 */ DataType.kBool, + /* 15 */ DataType.kInt, + /* 16 */ DataType.kInt, + /* 17 */ DataType.kDouble, + /* 18 */ DataType.kInt, + /* 19 */ DataType.kDate, + /* 20 */ DataType.kDate, + /* 21 */ DataType.kSmallInt, + /* 22 */ DataType.kString, + /* 23 */ DataType.kDouble, + /* 24 */ DataType.kBigInt, + /* 25 */ DataType.kBigInt, + /* 26 */ DataType.kVarchar, + /* 27 */ DataType.kSmallInt, + /* 28 */ DataType.kBigInt, + /* 29 */ DataType.kDate, + /* 30 */ DataType.kDate, + /* 31 */ DataType.kDouble, + /* 32 */ DataType.kString, + /* 33 */ DataType.kFloat, + /* 34 */ DataType.kBool, + /* 35 */ DataType.kTimestamp, + /* 36 */ DataType.kBool, + /* 37 */ DataType.kFloat, + /* 38 */ DataType.kVarchar, + /* 39 */ DataType.kInt, + /* 40 */ DataType.kBool, + /* 41 */ DataType.kFloat, + /* 42 */ DataType.kInt, + /* 43 */ DataType.kDate, + /* 44 */ DataType.kTimestamp, + /* 45 */ DataType.kBigInt, + /* 46 */ DataType.kSmallInt, + /* 47 */ DataType.kInt, + /* 48 */ DataType.kVarchar, + /* 49 */ DataType.kBool + )); + List schema = new ArrayList(); + List idx = new ArrayList<>(Arrays.asList( + 36, 3, 31, 8, 27, 10, 49, 44, 23, 20, 40, 28, 25, 19, 29, 24, 11, 21, 13, 30, 46, 26, 41, 1, 17, + 5, 45, 37, 43, 0, 38, 33, 22, 35, 39, 12, 9, 16, 48, 6, 7, 34, 47, 42, 4, 15, 14, 2, 32, 18 + )); + for (int i = 0; i < types.size(); i++) { + ColumnDesc.Builder col = ColumnDesc.newBuilder(); + col.setName("col" + i); + DataType type = types.get(i); + col.setDataType(type); + schema.add(col.build()); + } + Object[] data = { + /* 0 */ new Timestamp(System.currentTimeMillis()), + /* 1 */ true, + /* 2 */ null, + /* 3 */ null, + /* 4 */ "val10417", + /* 5 */ false, + /* 6 */ new Timestamp(System.currentTimeMillis()), + /* 7 */ 0.759f, + /* 8 */ 9053, + /* 9 */ null, + /* 10 */ "val10564", + /* 11 */ "", + /* 12 */ 5870, + /* 13 */ new Timestamp(System.currentTimeMillis()), + /* 14 */ false, + /* 15 */ 3155, + /* 16 */ 3022, + /* 17 */ 0.78d, + /* 18 */ 8100, + /* 19 */ null, + /* 20 */ new Date(2542, 3, 13), + /* 21 */ (short)8712, + /* 22 */ "val10187", + /* 23 */ 0.39d, + /* 24 */ (long)1941, + /* 25 */ 357l, + /* 26 */ "val10139", + /* 27 */ (short)376, + /* 28 */ null, + /* 29 */ new Date(3041, 3, 8), + /* 30 */ new Date(2782, 6, 22), + /* 31 */ 0.22d, + /* 32 */ "val10117", + /* 33 */ 0.19f, + /* 34 */ false, + /* 35 */ new Timestamp(System.currentTimeMillis()), + /* 36 */ false, + /* 37 */ 0.49f, + /* 38 */ "", + /* 39 */ 1117, + /* 40 */ false, + /* 41 */ 0.61f, + /* 42 */ 489, + /* 43 */ new Date(5282, 8, 8), + /* 44 */ null, + /* 45 */ null, + /* 46 */ null, + /* 47 */ null, + /* 48 */ "val10738", + /* 49 */ false + }; + try { + FlexibleRowBuilder builder = new FlexibleRowBuilder(schema); + for (Integer i : idx) { + setData(builder, i, schema.get(i).getDataType(), data[i]); + } + Assert.assertTrue(builder.build()); + ByteBuffer buffer = builder.getValue(); + RowView rowView = new RowView(schema, buffer, buffer.capacity()); + for (Integer i : idx) { + try { + checkData(rowView, i, schema.get(i).getDataType(), data[i]); + } catch (Exception e) { + Assert.fail(); + } + } + } catch (Exception e) { + e.printStackTrace(); + Assert.fail(); + } + } } diff --git a/java/openmldb-common/src/test/java/com/_4paradigm/openmldb/common/codec/TestBitMap.java b/java/openmldb-common/src/test/java/com/_4paradigm/openmldb/common/codec/TestBitMap.java new file mode 100644 index 00000000000..3836d757fa3 --- /dev/null +++ b/java/openmldb-common/src/test/java/com/_4paradigm/openmldb/common/codec/TestBitMap.java @@ -0,0 +1,86 @@ +/* + * Copyright 2021 4Paradigm + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com._4paradigm.openmldb.common.codec; + +import org.testng.Assert; +import org.testng.annotations.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class TestBitMap { + @Test + public void testValue() { + ByteBitMap bitmap = new ByteBitMap(10); + for (int i = 0; i < 10; i++) { + Assert.assertFalse(bitmap.at(i)); + } + for (int i = 0; i < 10; i++) { + bitmap.atPut(i, true); + Assert.assertTrue(bitmap.at(i)); + } + bitmap.clear(); + List v = Arrays.asList(1, 4, 7); + for (int i : v) { + bitmap.atPut(i, true); + } + for (int i = 0; i < 10; i++) { + if (v.contains(i)) { + Assert.assertTrue(bitmap.at(i)); + } else { + Assert.assertFalse(bitmap.at(i)); + } + } + } + + @Test + public void testClear() { + ByteBitMap bitmap = new ByteBitMap(10); + bitmap.clear(); + for (int i = 0; i < 10; i++) { + Assert.assertFalse(bitmap.at(i)); + } + } + + @Test + public void testSize() { + Assert.assertEquals(new ByteBitMap(1).getBuffer().length, 1); + Assert.assertEquals(new ByteBitMap(7).getBuffer().length, 1); + Assert.assertEquals(new ByteBitMap(8).getBuffer().length, 1); + Assert.assertEquals(new ByteBitMap(9).getBuffer().length, 2); + Assert.assertEquals(new ByteBitMap(15).getBuffer().length, 2); + Assert.assertEquals(new ByteBitMap(16).getBuffer().length, 2); + Assert.assertEquals(new ByteBitMap(17).getBuffer().length, 3); + Assert.assertEquals(new ByteBitMap(100).getBuffer().length, 13); + } + + @Test + public void testSetted() { + for (int i = 1; i < 1000; i++) { + ByteBitMap bitmap = new ByteBitMap(i + 1); + Assert.assertFalse(bitmap.allSetted()); + bitmap.atPut(i % 5, true); + Assert.assertFalse(bitmap.allSetted()); + for (int j = 0; j < bitmap.size(); j++) { + bitmap.atPut(j, true); + } + Assert.assertTrue(bitmap.allSetted()); + } + + } +} diff --git a/java/openmldb-common/src/test/java/com/_4paradigm/openmldb/common/codec/TestCodecUtil.java b/java/openmldb-common/src/test/java/com/_4paradigm/openmldb/common/codec/TestCodecUtil.java index 0d2269a8a4b..f2a62ca7a1d 100644 --- a/java/openmldb-common/src/test/java/com/_4paradigm/openmldb/common/codec/TestCodecUtil.java +++ b/java/openmldb-common/src/test/java/com/_4paradigm/openmldb/common/codec/TestCodecUtil.java @@ -19,6 +19,7 @@ import org.testng.Assert; import org.testng.annotations.Test; +import java.nio.ByteBuffer; import java.sql.Date; public class TestCodecUtil { @@ -79,5 +80,14 @@ public void testFromDaysToDays() { Assert.assertEquals(actualDays, expectDays); } + @Test + public void testSetStrOffset() { + for (int i = 1; i < 1000000; i++) { + int addLen = CodecUtil.getAddrLength(i); + ByteBuffer buf = ByteBuffer.allocate(addLen); + CodecUtil.setStrOffset(buf, 0, i, addLen); + Assert.assertEquals(i, CodecUtil.getStrOffset(buf, 0, addLen)); + } + } } diff --git a/java/openmldb-jdbc/pom.xml b/java/openmldb-jdbc/pom.xml index 0b2b8af9df0..3978579b373 100644 --- a/java/openmldb-jdbc/pom.xml +++ b/java/openmldb-jdbc/pom.xml @@ -38,6 +38,11 @@ openmldb-native ${variant.native.version} + + com.4paradigm.openmldb + openmldb-common + ${project.parent.version} + @@ -51,6 +56,11 @@ log4j-core 2.17.2 + + org.xerial.snappy + snappy-java + 1.1.7.2 + @@ -59,7 +69,6 @@ 6.14.3 test - diff --git a/java/openmldb-jdbc/src/main/java/com/_4paradigm/openmldb/jdbc/CallablePreparedStatement.java b/java/openmldb-jdbc/src/main/java/com/_4paradigm/openmldb/jdbc/CallablePreparedStatement.java index 0e67e93125d..b63afee098b 100644 --- a/java/openmldb-jdbc/src/main/java/com/_4paradigm/openmldb/jdbc/CallablePreparedStatement.java +++ b/java/openmldb-jdbc/src/main/java/com/_4paradigm/openmldb/jdbc/CallablePreparedStatement.java @@ -17,65 +17,34 @@ package com._4paradigm.openmldb.jdbc; import com._4paradigm.openmldb.SQLRouter; -import com._4paradigm.openmldb.Status; +import com._4paradigm.openmldb.common.codec.FlexibleRowBuilder; import com._4paradigm.openmldb.sdk.QueryFuture; +import com._4paradigm.openmldb.sdk.impl.Deployment; +import java.sql.ResultSetMetaData; import java.sql.SQLException; -import java.util.ArrayList; import java.util.concurrent.TimeUnit; -public class CallablePreparedStatement extends RequestPreparedStatement { - protected String spName; - private com._4paradigm.openmldb.ProcedureInfo procedureInfo; +public abstract class CallablePreparedStatement extends PreparedStatement { + protected Deployment deployment; + protected FlexibleRowBuilder rowBuilder; + protected String db; + protected String deploymentName; - public CallablePreparedStatement(String db, String spName, SQLRouter router) throws SQLException { - if (db == null) throw new SQLException("db is null"); + public CallablePreparedStatement(Deployment deployment, SQLRouter router) throws SQLException { if (router == null) throw new SQLException("router is null"); - if (spName == null) throw new SQLException("spName is null"); - - this.db = db; this.router = router; - this.spName = spName; - - Status status = new Status(); - procedureInfo = router.ShowProcedure(db, spName, status); - if (procedureInfo == null || status.getCode() != 0) { - String msg = status.ToString(); - status.delete(); - throw new SQLException("show procedure failed, msg: " + msg); - } - this.currentSql = procedureInfo.GetSql(); - this.currentRow = router.GetRequestRow(db, procedureInfo.GetSql(), status); - if (status.getCode() != 0 || this.currentRow == null) { - String msg = status.ToString(); - status.delete(); - throw new SQLException("getRequestRow failed!, msg: " + msg); - } - status.delete(); - this.currentSchema = procedureInfo.GetInputSchema(); - if (this.currentSchema == null) { - throw new SQLException("inputSchema is null"); - } - int cnt = this.currentSchema.GetColumnCnt(); - this.currentDatas = new ArrayList<>(cnt); - this.hasSet = new ArrayList<>(cnt); - for (int i = 0; i < cnt; i++) { - this.hasSet.add(false); - currentDatas.add(null); - } - } - - @Override - public void close() throws SQLException { - super.close(); - this.spName = null; - if (this.procedureInfo != null) { - procedureInfo.delete(); - procedureInfo = null; - } + this.deployment = deployment; + db = deployment.getDatabase(); + deploymentName = deployment.getName(); } public QueryFuture executeQueryAsync(long timeOut, TimeUnit unit) throws SQLException { throw new SQLException("current do not support this method"); } + + @Override + public ResultSetMetaData getMetaData() { + return new SQLResultSetMetaData(deployment.getInputSchema()); + } } diff --git a/java/openmldb-jdbc/src/main/java/com/_4paradigm/openmldb/jdbc/DirectResultSet.java b/java/openmldb-jdbc/src/main/java/com/_4paradigm/openmldb/jdbc/DirectResultSet.java new file mode 100644 index 00000000000..a3d9497f78d --- /dev/null +++ b/java/openmldb-jdbc/src/main/java/com/_4paradigm/openmldb/jdbc/DirectResultSet.java @@ -0,0 +1,159 @@ +/* + * Copyright 2021 4Paradigm + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com._4paradigm.openmldb.jdbc; + +import com._4paradigm.openmldb.common.codec.RowView; +import com._4paradigm.openmldb.sdk.Schema; + +import java.nio.ByteBuffer; +import java.sql.*; + +public abstract class DirectResultSet extends SQLResultSet { + + protected ByteBuffer buf; + protected RowView rowView; + + public DirectResultSet(ByteBuffer buf, int totalRows, Schema schema) { + this.buf = buf; + this.totalRows = totalRows; + this.schema = schema; + } + + @Override + public void close() throws SQLException { + closed = true; + } + + @Override + public String getString(int i) throws SQLException { + int realIdx = i - 1; + try { + return rowView.getString(realIdx); + } catch (Exception e) { + throw new SQLException(e.getMessage()); + } + } + + @Override + public boolean getBoolean(int i) throws SQLException { + int realIdx = i - 1; + if (rowView.isNull(realIdx)) { + return false; + } + try { + return rowView.getBool(realIdx); + } catch (Exception e) { + throw new SQLException(e.getMessage()); + } + } + + @Override + public short getShort(int i) throws SQLException { + int realIdx = i - 1; + if (rowView.isNull(realIdx)) { + return 0; + } + try { + return rowView.getSmallInt(realIdx); + } catch (Exception e) { + throw new SQLException(e.getMessage()); + } + } + + @Override + public int getInt(int i) throws SQLException { + int realIdx = i - 1; + if (rowView.isNull(realIdx)) { + return 0; + } + try { + return rowView.getInt(realIdx); + } catch (Exception e) { + throw new SQLException(e.getMessage()); + } + } + + @Override + public long getLong(int i) throws SQLException { + int realIdx = i - 1; + if (rowView.isNull(realIdx)) { + return 0; + } + try { + return rowView.getBigInt(realIdx); + } catch (Exception e) { + throw new SQLException(e.getMessage()); + } + } + + @Override + public float getFloat(int i) throws SQLException { + int realIdx = i - 1; + if (rowView.isNull(realIdx)) { + return 0.0f; + } + try { + return rowView.getFloat(realIdx); + } catch (Exception e) { + throw new SQLException(e.getMessage()); + } + } + + @Override + public double getDouble(int i) throws SQLException { + int realIdx = i - 1; + if (rowView.isNull(realIdx)) { + return 0.0; + } + try { + return rowView.getDouble(realIdx); + } catch (Exception e) { + throw new SQLException(e.getMessage()); + } + } + + @Override + public Date getDate(int i) throws SQLException { + int realIdx = i - 1; + try { + return rowView.getDate(realIdx); + } catch (Exception e) { + throw new SQLException(e.getMessage()); + } + } + + + @Override + public Timestamp getTimestamp(int i) throws SQLException { + int realIdx = i - 1; + try { + return rowView.getTimestamp(realIdx); + } catch (Exception e) { + throw new SQLException(e.getMessage()); + } + } + + @Override + public String getNString(int i) throws SQLException { + int realIdx = i - 1; + try { + return rowView.getString(realIdx); + } catch (Exception e) { + throw new SQLException(e.getMessage()); + } + } +} diff --git a/java/openmldb-jdbc/src/main/java/com/_4paradigm/openmldb/jdbc/PreparedStatement.java b/java/openmldb-jdbc/src/main/java/com/_4paradigm/openmldb/jdbc/PreparedStatement.java index 44aa969e6d0..0d38f460384 100644 --- a/java/openmldb-jdbc/src/main/java/com/_4paradigm/openmldb/jdbc/PreparedStatement.java +++ b/java/openmldb-jdbc/src/main/java/com/_4paradigm/openmldb/jdbc/PreparedStatement.java @@ -17,50 +17,21 @@ package com._4paradigm.openmldb.jdbc; import com._4paradigm.openmldb.*; -import com._4paradigm.openmldb.sdk.impl.Util; import java.io.InputStream; import java.io.Reader; import java.math.BigDecimal; import java.net.URL; -import java.nio.charset.Charset; -import java.nio.charset.StandardCharsets; import java.sql.Date; import java.sql.ResultSet; import java.sql.*; import java.util.*; -public class PreparedStatement implements java.sql.PreparedStatement { - public static final Charset CHARSET = StandardCharsets.UTF_8; - protected String db; - protected String currentSql; +public abstract class PreparedStatement implements java.sql.PreparedStatement { protected SQLRouter router; - protected SQLRequestRow currentRow; - protected Schema currentSchema; - protected TreeMap types; - protected TreeMap orgTypes; - protected TreeMap currentDatas; protected boolean closed = false; protected boolean closeOnComplete = false; - protected Map stringsLen = new HashMap<>(); - private void checkNull() throws SQLException { - if (db == null) { - throw new SQLException("db is null"); - } - if (currentSql == null) { - throw new SQLException("sql is null"); - } - if (router == null) { - throw new SQLException("SQLRouter is null"); - } - if (currentDatas == null) { - throw new SQLException("currentDatas is null"); - } - if (types == null) { - throw new SQLException("currentDatas is null"); - } - } protected void checkClosed() throws SQLException { if (closed) { @@ -74,123 +45,24 @@ protected void checkExecutorClosed() throws SQLException { } } - void checkIdx(int i) throws SQLException { - checkClosed(); - checkNull(); - if (i <= 0) { - throw new SQLException("index out of array"); - } - if (currentDatas.containsKey(i)) { - throw new SQLException("index duplicate, index: " + i + " already exist"); - } - } - - @Override - public SQLResultSet executeQuery() throws SQLException { - checkClosed(); - checkExecutorClosed(); - dataBuild(); - Status status = new Status(); - com._4paradigm.openmldb.ResultSet resultSet = router.ExecuteSQLParameterized(db, currentSql, currentRow, status); - if (resultSet == null || status.getCode() != 0) { - String msg = status.ToString(); - status.delete(); - if (resultSet != null) { - resultSet.delete(); - } - throw new SQLException("execute sql fail, msg: " + msg); - } - status.delete(); - SQLResultSet rs = new SQLResultSet(resultSet); - if (closeOnComplete) { - closed = true; - } - return rs; - } - @Override @Deprecated public int executeUpdate() throws SQLException { throw new SQLException("current do not support this method"); } - @Override - public void setNull(int parameterIndex, int sqlType) throws SQLException { - setNull(parameterIndex, Util.sqlTypeToDataType(sqlType)); - } - - private void setNull(int i, DataType type) throws SQLException { - checkIdx(i); - types.put(i, type); - currentDatas.put(i, null); - } - - @Override - public void setBoolean(int i, boolean b) throws SQLException { - checkIdx(i); - types.put(i, DataType.kTypeBool); - currentDatas.put(i, b); - } - @Override @Deprecated public void setByte(int i, byte b) throws SQLException { throw new SQLException("current do not support this method"); } - @Override - public void setShort(int i, short i1) throws SQLException { - checkIdx(i); - types.put(i, DataType.kTypeInt16); - currentDatas.put(i, i1); - } - - @Override - public void setInt(int i, int i1) throws SQLException { - checkIdx(i); - types.put(i, DataType.kTypeInt32); - currentDatas.put(i, i1); - } - - @Override - public void setLong(int i, long l) throws SQLException { - checkIdx(i); - types.put(i, DataType.kTypeInt64); - currentDatas.put(i, l); - } - - @Override - public void setFloat(int i, float v) throws SQLException { - checkIdx(i); - types.put(i, DataType.kTypeFloat); - currentDatas.put(i, v); - } - - @Override - public void setDouble(int i, double v) throws SQLException { - checkIdx(i); - types.put(i, DataType.kTypeDouble); - currentDatas.put(i, v); - } - @Override @Deprecated public void setBigDecimal(int i, BigDecimal bigDecimal) throws SQLException { throw new SQLException("current do not support this type"); } - @Override - public void setString(int i, String s) throws SQLException { - checkIdx(i); - if (s == null) { - setNull(i, DataType.kTypeString); - return; - } - types.put(i, DataType.kTypeString); - byte[] bytes = s.getBytes(CHARSET); - stringsLen.put(i, bytes.length); - currentDatas.put(i, bytes); - } @Override @Deprecated @@ -198,35 +70,12 @@ public void setBytes(int i, byte[] bytes) throws SQLException { throw new SQLException("current do not support this type"); } - @Override - public void setDate(int i, Date date) throws SQLException { - checkIdx(i); - if (date == null) { - setNull(i, DataType.kTypeDate); - return; - } - types.put(i, DataType.kTypeDate); - currentDatas.put(i, date); - } - @Override @Deprecated public void setTime(int i, Time time) throws SQLException { throw new SQLException("current do not support this type"); } - @Override - public void setTimestamp(int i, Timestamp timestamp) throws SQLException { - checkIdx(i); - if (timestamp == null) { - setNull(i, DataType.kTypeTimestamp); - return; - } - types.put(i, DataType.kTypeTimestamp); - long ts = timestamp.getTime(); - currentDatas.put(i, ts); - } - @Override @Deprecated public void setAsciiStream(int i, InputStream inputStream, int i1) throws SQLException { @@ -245,89 +94,12 @@ public void setBinaryStream(int i, InputStream inputStream, int i1) throws SQLEx throw new SQLException("current do not support this type"); } - @Override - public void clearParameters() { - currentDatas.clear(); - types.clear(); - stringsLen.clear(); - } - @Override @Deprecated public void setObject(int i, Object o, int i1) throws SQLException { throw new SQLException("current do not support this method"); } - protected void dataBuild() throws SQLException { - if (types == null) { - throw new SQLException("fail to build data when data types is null"); - } - // types has been updated - if (null == this.currentRow || orgTypes != types) { - if (types.firstKey() != 1 || types.lastKey() != types.size()) { - throw new SQLException("data not enough, indexes are " + currentDatas.keySet()); - } - ColumnTypes columnTypes = new ColumnTypes(); - for (int i = 0; i < types.size(); i++) { - columnTypes.AddColumnType(types.get(i + 1)); - } - this.currentRow = SQLRequestRow.CreateSQLRequestRowFromColumnTypes(columnTypes); - if (this.currentRow == null) { - throw new SQLException("fail to create sql request row from column types"); - } - this.currentSchema = this.currentRow.GetSchema(); - this.orgTypes = this.types; - } - if (this.currentSchema == null) { - throw new SQLException("fail to build data with null schema"); - } - int strLen = 0; - for (Map.Entry entry : stringsLen.entrySet()) { - strLen += entry.getValue(); - } - boolean ok = this.currentRow.Init(strLen); - if (!ok) { - throw new SQLException("build data row failed"); - } - for (int i = 0; i < this.currentSchema.GetColumnCnt(); i++) { - DataType dataType = this.currentSchema.GetColumnType(i); - Object data = this.currentDatas.get(i + 1); - if (data == null) { - ok = this.currentRow.AppendNULL(); - } else { - if (DataType.kTypeBool.equals(dataType)) { - ok = this.currentRow.AppendBool((boolean) data); - } else if (DataType.kTypeDate.equals(dataType)) { - Date date = (Date) data; - ok = this.currentRow.AppendDate(date.getYear() + 1900, date.getMonth() + 1, date.getDate()); - } else if (DataType.kTypeDouble.equals(dataType)) { - ok = this.currentRow.AppendDouble((double) data); - } else if (DataType.kTypeFloat.equals(dataType)) { - ok = this.currentRow.AppendFloat((float) data); - } else if (DataType.kTypeInt16.equals(dataType)) { - ok = this.currentRow.AppendInt16((short) data); - } else if (DataType.kTypeInt32.equals(dataType)) { - ok = this.currentRow.AppendInt32((int) data); - } else if (DataType.kTypeInt64.equals(dataType)) { - ok = this.currentRow.AppendInt64((long) data); - } else if (DataType.kTypeString.equals(dataType)) { - byte[] bdata = (byte[]) data; - ok = this.currentRow.AppendString(bdata, bdata.length); - } else if (DataType.kTypeTimestamp.equals(dataType)) { - ok = this.currentRow.AppendTimestamp((long) data); - } else { - throw new SQLException("unkown data type " + dataType.toString()); - } - } - if (!ok) { - throw new SQLException("append data failed, idx is " + i); - } - } - if (!this.currentRow.Build()) { - throw new SQLException("build request row failed"); - } - clearParameters(); - } @Override @Deprecated @@ -377,13 +149,6 @@ public void setArray(int i, Array array) throws SQLException { throw new SQLException("current do not support this method"); } - @Override - public ResultSetMetaData getMetaData() throws SQLException { - checkClosed(); - checkNull(); - return new SQLResultSetMetaData(this.currentSchema); - } - @Override @Deprecated public void setDate(int i, Date date, Calendar calendar) throws SQLException { @@ -548,21 +313,7 @@ public int executeUpdate(String s) throws SQLException { @Override public void close() throws SQLException { - this.db = null; - this.currentSql = null; this.router = null; - if (this.currentSchema != null) { - this.currentSchema.delete(); - this.currentSchema = null; - } - this.currentDatas = null; - this.types = null; - this.orgTypes = null; - this.stringsLen = null; - if (this.currentRow != null) { - this.currentRow.delete(); - this.currentRow = null; - } this.closed = true; } diff --git a/java/openmldb-jdbc/src/main/java/com/_4paradigm/openmldb/jdbc/RequestPreparedStatement.java b/java/openmldb-jdbc/src/main/java/com/_4paradigm/openmldb/jdbc/RequestPreparedStatement.java index 336ca1dd409..dc1d09d13ba 100644 --- a/java/openmldb-jdbc/src/main/java/com/_4paradigm/openmldb/jdbc/RequestPreparedStatement.java +++ b/java/openmldb-jdbc/src/main/java/com/_4paradigm/openmldb/jdbc/RequestPreparedStatement.java @@ -17,6 +17,8 @@ package com._4paradigm.openmldb.jdbc; import com._4paradigm.openmldb.*; +import com._4paradigm.openmldb.sdk.Common; +import com._4paradigm.openmldb.sdk.impl.NativeResultSet; import java.io.InputStream; import java.io.Reader; @@ -49,9 +51,6 @@ private void checkNull() throws SQLException { if (db == null) { throw new SQLException("db is null"); } - if (currentSql == null) { - throw new SQLException("sql is null"); - } if (router == null) { throw new SQLException("SQLRouter is null"); } @@ -114,7 +113,7 @@ public SQLResultSet executeQuery() throws SQLException { throw new SQLException("execute sql fail, msg: " + msg); } status.delete(); - SQLResultSet rs = new SQLResultSet(resultSet); + SQLResultSet rs = new NativeResultSet(resultSet); if (closeOnComplete) { closed = true; } @@ -399,7 +398,7 @@ public void setArray(int i, Array array) throws SQLException { public ResultSetMetaData getMetaData() throws SQLException { checkClosed(); checkNull(); - return new SQLResultSetMetaData(this.currentSchema); + return new SQLResultSetMetaData(Common.convertSchema(this.currentSchema)); } @Override diff --git a/java/openmldb-jdbc/src/main/java/com/_4paradigm/openmldb/jdbc/SQLResultSet.java b/java/openmldb-jdbc/src/main/java/com/_4paradigm/openmldb/jdbc/SQLResultSet.java index ab5d44e3070..69f19d027ad 100644 --- a/java/openmldb-jdbc/src/main/java/com/_4paradigm/openmldb/jdbc/SQLResultSet.java +++ b/java/openmldb-jdbc/src/main/java/com/_4paradigm/openmldb/jdbc/SQLResultSet.java @@ -1,24 +1,6 @@ -/* - * Copyright 2021 4Paradigm - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package com._4paradigm.openmldb.jdbc; -import com._4paradigm.openmldb.DataType; -import com._4paradigm.openmldb.QueryFuture; -import com._4paradigm.openmldb.Schema; +import com._4paradigm.openmldb.sdk.Schema; import java.io.InputStream; import java.io.Reader; @@ -28,92 +10,24 @@ import java.util.Calendar; import java.util.Map; -public class SQLResultSet implements ResultSet { - private com._4paradigm.openmldb.ResultSet resultSet; - private boolean closed = false; - private int rowNum = 0; - private QueryFuture queryFuture; - private Schema schema; - - public SQLResultSet(com._4paradigm.openmldb.ResultSet resultSet) { - this.resultSet = resultSet; - if (resultSet != null) { - this.schema = resultSet.GetSchema(); - } - } - - public SQLResultSet(com._4paradigm.openmldb.ResultSet resultSet, QueryFuture future) { - this.resultSet = resultSet; - this.queryFuture = future; - if (resultSet != null) { - this.schema = resultSet.GetSchema(); - } - } - - private void check(int i, DataType type) throws SQLException { - checkClosed(); - checkResultSetNull(); - checkIdx(i); - checkDataType(i, type); - } - - private void checkClosed() throws SQLException { - if (closed) { - throw new SQLException("resultset closed"); - } - } - - private void checkIdx(int i) throws SQLException { - if (i <= 0) { - throw new SQLException("index underflow"); - } - if (i > schema.GetColumnCnt()) { - throw new SQLException("index overflow"); - } - } - - private void checkResultSetNull() throws SQLException{ - if (this.resultSet == null) { - throw new SQLException("resultset is null"); - } - } - - private void checkDataType(int i, DataType type) throws SQLException { - if (schema.GetColumnType(i - 1) != type) { - throw new SQLException(String.format("data type not match, get %s and expect %s", - schema.GetColumnType(i - 1), type)); - } - } +public abstract class SQLResultSet implements ResultSet { + protected boolean closed = false; + protected int rowNum = 0; + protected int totalRows = 0; + protected Schema schema; public Schema GetInternalSchema() { return schema; } @Override - public boolean next() throws SQLException { - checkClosed(); - checkResultSetNull(); - if (this.resultSet.Next()) { - this.rowNum++; - return true; - } else { - return false; - } + public SQLResultSetMetaData getMetaData() throws SQLException { + return new SQLResultSetMetaData(schema); } @Override - public void close() throws SQLException { - if (schema != null) { - schema.delete(); - schema = null; - } - this.resultSet.delete(); - this.resultSet = null; - if (queryFuture != null) { - queryFuture.delete(); - queryFuture = null; - } - this.closed = true; + public boolean isClosed() throws SQLException { + return closed; } @Override @@ -122,75 +36,6 @@ public boolean wasNull() throws SQLException { throw new SQLException("current do not support this method"); } - @Override - public String getString(int i) throws SQLException { - check(i, DataType.kTypeString); - if (this.resultSet.IsNULL(i - 1)) { - return null; - } - return this.resultSet.GetStringUnsafe(i - 1); - } - - @Override - public boolean getBoolean(int i) throws SQLException { - check(i, DataType.kTypeBool); - if (this.resultSet.IsNULL(i - 1)) { - return false; - } - return this.resultSet.GetBoolUnsafe(i - 1); - } - - @Override - @Deprecated - public byte getByte(int i) throws SQLException { - throw new SQLException("current do not support this method"); - } - - @Override - public short getShort(int i) throws SQLException { - check(i, DataType.kTypeInt16); - if (this.resultSet.IsNULL(i - 1)) { - return 0; - } - return this.resultSet.GetInt16Unsafe(i - 1); - } - - @Override - public int getInt(int i) throws SQLException { - check(i, DataType.kTypeInt32); - if (this.resultSet.IsNULL(i - 1)) { - return 0; - } - return resultSet.GetInt32Unsafe(i - 1); - } - - @Override - public long getLong(int i) throws SQLException { - check(i, DataType.kTypeInt64); - if (this.resultSet.IsNULL(i - 1)) { - return 0; - } - return this.resultSet.GetInt64Unsafe(i - 1); - } - - @Override - public float getFloat(int i) throws SQLException { - check(i, DataType.kTypeFloat); - if (this.resultSet.IsNULL(i - 1)) { - return 0.0f; - } - return this.resultSet.GetFloatUnsafe(i - 1); - } - - @Override - public double getDouble(int i) throws SQLException { - check(i, DataType.kTypeDouble); - if (this.resultSet.IsNULL(i - 1)) { - return 0.0; - } - return resultSet.GetDoubleUnsafe(i - 1); - } - @Override @Deprecated public BigDecimal getBigDecimal(int i, int i1) throws SQLException { @@ -203,31 +48,12 @@ public byte[] getBytes(int i) throws SQLException { throw new SQLException("current do not support this method"); } - @Override - public Date getDate(int i) throws SQLException { - check(i, DataType.kTypeDate); - if (this.resultSet.IsNULL(i - 1)) { - return null; - } - com._4paradigm.openmldb.Date date = this.resultSet.GetStructDateUnsafe(i - 1); - return new Date(date.getYear() - 1900, date.getMonth() - 1, date.getDay()); - } - @Override @Deprecated public Time getTime(int i) throws SQLException { throw new SQLException("current do not support this method"); } - @Override - public Timestamp getTimestamp(int i) throws SQLException { - check(i, DataType.kTypeTimestamp); - if (this.resultSet.IsNULL(i - 1)) { - return null; - } - return new Timestamp(this.resultSet.GetTimeUnsafe(i -1)); - } - @Override @Deprecated public InputStream getAsciiStream(int i) throws SQLException { @@ -365,10 +191,9 @@ public String getCursorName() throws SQLException { } @Override - public SQLResultSetMetaData getMetaData() throws SQLException { - checkClosed(); - checkResultSetNull(); - return new SQLResultSetMetaData(schema); + @Deprecated + public byte getByte(int i) throws SQLException { + throw new SQLException("current do not support this method"); } @Override @@ -463,8 +288,6 @@ public boolean last() throws SQLException { @Override public int getRow() throws SQLException { - checkClosed(); - checkResultSetNull(); return this.rowNum; } @@ -507,9 +330,7 @@ public void setFetchSize(int i) throws SQLException { @Override @Deprecated public int getFetchSize() throws SQLException { - checkClosed(); - checkResultSetNull(); - return this.resultSet.Size(); + return totalRows; } @Override @@ -1004,11 +825,6 @@ public int getHoldability() throws SQLException { throw new SQLException("current do not support this method"); } - @Override - public boolean isClosed() throws SQLException { - return closed; - } - @Override @Deprecated public void updateNString(int i, String s) throws SQLException { @@ -1069,17 +885,6 @@ public void updateSQLXML(String s, SQLXML sqlxml) throws SQLException { throw new SQLException("current do not support this method"); } - @Override - public String getNString(int i) throws SQLException { - checkClosed(); - checkResultSetNull(); - checkIdx(i); - if (this.resultSet.IsNULL(i - 1)) { - return null; - } - return this.resultSet.GetAsStringUnsafe(i - 1); - } - @Override @Deprecated public String getNString(String s) throws SQLException { diff --git a/java/openmldb-jdbc/src/main/java/com/_4paradigm/openmldb/jdbc/SQLResultSetMetaData.java b/java/openmldb-jdbc/src/main/java/com/_4paradigm/openmldb/jdbc/SQLResultSetMetaData.java index f44ca49c5b8..6ff6f1029dd 100644 --- a/java/openmldb-jdbc/src/main/java/com/_4paradigm/openmldb/jdbc/SQLResultSetMetaData.java +++ b/java/openmldb-jdbc/src/main/java/com/_4paradigm/openmldb/jdbc/SQLResultSetMetaData.java @@ -16,10 +16,7 @@ package com._4paradigm.openmldb.jdbc; -import com._4paradigm.openmldb.DataType; -import com._4paradigm.openmldb.Schema; -import com._4paradigm.openmldb.sdk.Common; - +import com._4paradigm.openmldb.sdk.Schema; import java.sql.ResultSetMetaData; import java.sql.SQLException; @@ -31,30 +28,18 @@ public SQLResultSetMetaData(Schema schema) { this.schema = schema; } - private void checkSchemaNull() throws SQLException { - if (schema == null) { - throw new SQLException("schema is null"); - } - } - - private void checkIdx(int i) throws SQLException { + private void check(int i) throws SQLException { if (i <= 0) { throw new SQLException("index underflow"); } - if (i > schema.GetColumnCnt()) { + if (i > schema.size()) { throw new SQLException("index overflow"); } } - public void check(int i) throws SQLException { - checkIdx(i); - checkSchemaNull(); - } - @Override public int getColumnCount() throws SQLException { - checkSchemaNull(); - return schema.GetColumnCnt(); + return schema.size(); } @Override @@ -84,10 +69,10 @@ public boolean isCurrency(int i) throws SQLException { @Override public int isNullable(int i) throws SQLException { check(i); - if (schema.IsColumnNotNull(i - 1)) { - return columnNoNulls; - } else { + if (schema.isNullable(i - 1)) { return columnNullable; + } else { + return columnNoNulls; } } @@ -112,7 +97,7 @@ public String getColumnLabel(int i) throws SQLException { @Override public String getColumnName(int i) throws SQLException { check(i); - return schema.GetColumnName(i - 1); + return schema.getColumnName(i - 1); } @Override @@ -148,8 +133,7 @@ public String getCatalogName(int i) throws SQLException { @Override public int getColumnType(int i) throws SQLException { check(i); - DataType dataType = schema.GetColumnType(i - 1); - return Common.type2SqlType(dataType); + return schema.getColumnType(i - 1); } @Override diff --git a/java/openmldb-jdbc/src/main/java/com/_4paradigm/openmldb/jdbc/Statement.java b/java/openmldb-jdbc/src/main/java/com/_4paradigm/openmldb/jdbc/Statement.java index 79aeca51555..e4706bc4f9c 100644 --- a/java/openmldb-jdbc/src/main/java/com/_4paradigm/openmldb/jdbc/Statement.java +++ b/java/openmldb-jdbc/src/main/java/com/_4paradigm/openmldb/jdbc/Statement.java @@ -1,6 +1,7 @@ package com._4paradigm.openmldb.jdbc; import com._4paradigm.openmldb.SQLRouter; import com._4paradigm.openmldb.Status; +import com._4paradigm.openmldb.sdk.impl.NativeResultSet; import java.sql.*; @@ -36,7 +37,7 @@ public ResultSet getResultSet() throws SQLException { if (resultSet == null) { throw new SQLException("no result set"); } - return new SQLResultSet(resultSet); + return new NativeResultSet(resultSet); } // TODO(hw): why return sqlresultset? @@ -51,7 +52,7 @@ public SQLResultSet executeQuery(String sql) throws SQLException { throw new SQLException("executeSQL fail: " + msg); } status.delete(); - return new SQLResultSet(resultSet); + return new NativeResultSet(resultSet); } @Override diff --git a/java/openmldb-jdbc/src/main/java/com/_4paradigm/openmldb/sdk/Common.java b/java/openmldb-jdbc/src/main/java/com/_4paradigm/openmldb/sdk/Common.java index 2476635267b..0c57cf26a5a 100644 --- a/java/openmldb-jdbc/src/main/java/com/_4paradigm/openmldb/sdk/Common.java +++ b/java/openmldb-jdbc/src/main/java/com/_4paradigm/openmldb/sdk/Common.java @@ -17,35 +17,85 @@ package com._4paradigm.openmldb.sdk; import com._4paradigm.openmldb.DataType; +import com._4paradigm.openmldb.proto.Type; import java.sql.SQLException; import java.sql.Types; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; public class Common { + private static Map proto2SqlTypeMap = new HashMap() { + { + put(Type.DataType.kBool, Types.BOOLEAN); + put(Type.DataType.kSmallInt, Types.SMALLINT); + put(Type.DataType.kInt, Types.INTEGER); + put(Type.DataType.kBigInt, Types.BIGINT); + put(Type.DataType.kFloat, Types.FLOAT); + put(Type.DataType.kDouble, Types.DOUBLE); + put(Type.DataType.kDate, Types.DATE); + put(Type.DataType.kTimestamp, Types.TIMESTAMP); + put(Type.DataType.kString, Types.VARCHAR); + put(Type.DataType.kVarchar, Types.VARCHAR); + } + }; + + private static Map dataType2SqlTypeMap = new HashMap() { + { + put(DataType.kTypeBool, Types.BOOLEAN); + put(DataType.kTypeInt16, Types.SMALLINT); + put(DataType.kTypeInt32, Types.INTEGER); + put(DataType.kTypeInt64, Types.BIGINT); + put(DataType.kTypeFloat, Types.FLOAT); + put(DataType.kTypeDouble, Types.DOUBLE); + put(DataType.kTypeDate, Types.DATE); + put(DataType.kTypeTimestamp, Types.TIMESTAMP); + put(DataType.kTypeString, Types.VARCHAR); + } + }; + + public static int type2SqlType(DataType dataType) throws SQLException { - if (dataType == DataType.kTypeBool) { - return Types.BOOLEAN; - } else if (dataType == DataType.kTypeInt16) { - return Types.SMALLINT; - } else if (dataType == DataType.kTypeInt32) { - return Types.INTEGER; - } else if (dataType == DataType.kTypeInt64) { - return Types.BIGINT; - } else if (dataType == DataType.kTypeFloat) { - return Types.FLOAT; - } else if (dataType == DataType.kTypeDouble) { - return Types.DOUBLE; - } else if (dataType == DataType.kTypeString) { - return Types.VARCHAR; - } else if (dataType == DataType.kTypeDate) { - return Types.DATE; - } else if (dataType == DataType.kTypeTimestamp) { - return Types.TIMESTAMP; - } else { + Integer type = dataType2SqlTypeMap.get(dataType); + if (type == null) { throw new SQLException("Unexpected value: " + dataType.toString()); } + return type; + } + + public static int type2SqlType(com._4paradigm.openmldb.proto.Type.DataType dataType) throws SQLException { + Integer type = proto2SqlTypeMap.get(dataType); + if (type == null) { + throw new SQLException("Unexpected value: " + dataType.name()); + } + return type; + } + + public static com._4paradigm.openmldb.proto.Type.DataType sqlType2ProtoType(int sqlType) throws SQLException { + switch (sqlType) { + case Types.BOOLEAN: + return Type.DataType.kBool; + case Types.SMALLINT: + return Type.DataType.kSmallInt; + case Types.INTEGER: + return Type.DataType.kInt; + case Types.BIGINT: + return Type.DataType.kBigInt; + case Types.FLOAT: + return Type.DataType.kFloat; + case Types.DOUBLE: + return Type.DataType.kDouble; + case Types.VARCHAR: + return Type.DataType.kString; + case Types.TIMESTAMP: + return Type.DataType.kTimestamp; + case Types.DATE: + return Type.DataType.kDate; + default: + throw new SQLException("Unexpected value: " + sqlType); + } } public static com._4paradigm.openmldb.sdk.Schema convertSchema(com._4paradigm.openmldb.Schema schema) throws SQLException { @@ -64,6 +114,33 @@ public static com._4paradigm.openmldb.sdk.Schema convertSchema(com._4paradigm.op return new com._4paradigm.openmldb.sdk.Schema(columnList); } + public static com._4paradigm.openmldb.sdk.Schema convertSchema(List pbSchema) throws SQLException { + if (pbSchema.isEmpty()) { + throw new SQLException("schema is empty"); + } + List columnList = new ArrayList<>(); + for (com._4paradigm.openmldb.proto.Common.ColumnDesc col : pbSchema) { + Column column = new Column(); + column.setColumnName(col.getName()); + column.setSqlType(type2SqlType(col.getDataType())); + column.setNotNull(col.getNotNull()); + columnList.add(column); + } + return new com._4paradigm.openmldb.sdk.Schema(columnList); + } + + public static List convert2ProtoSchema(com._4paradigm.openmldb.sdk.Schema schema) throws SQLException { + List columnList = new ArrayList<>(); + for (Column column : schema.getColumnList()) { + com._4paradigm.openmldb.proto.Common.ColumnDesc.Builder builder = com._4paradigm.openmldb.proto.Common.ColumnDesc.newBuilder(); + builder.setName(column.getColumnName()) + .setDataType(sqlType2ProtoType(column.getSqlType())) + .setNotNull(column.isNotNull()); + columnList.add(builder.build()); + } + return columnList; + } + public static DataType sqlTypeToDataType(int sqlType) throws SQLException { switch (sqlType) { case Types.BOOLEAN: @@ -88,4 +165,40 @@ public static DataType sqlTypeToDataType(int sqlType) throws SQLException { throw new SQLException("Unexpected Values: " + sqlType); } } + + public static ProcedureInfo convertProcedureInfo(com._4paradigm.openmldb.ProcedureInfo procedureInfo) throws SQLException { + ProcedureInfo spInfo = new ProcedureInfo(); + spInfo.setDbName(procedureInfo.GetDbName()); + spInfo.setProName(procedureInfo.GetSpName()); + spInfo.setSql(procedureInfo.GetSql()); + spInfo.setInputSchema(convertSchema(procedureInfo.GetInputSchema())); + spInfo.setOutputSchema(convertSchema(procedureInfo.GetOutputSchema())); + spInfo.setMainTable(procedureInfo.GetMainTable()); + spInfo.setInputTables(procedureInfo.GetTables()); + spInfo.setInputDbs(procedureInfo.GetDbs()); + spInfo.setRouterCol(procedureInfo.GetRouterCol()); + return spInfo; + } + + public static ProcedureInfo convertProcedureInfo(com._4paradigm.openmldb.proto.SQLProcedure.ProcedureInfo procedureInfo) throws SQLException { + ProcedureInfo spInfo = new ProcedureInfo(); + spInfo.setDbName(procedureInfo.getDbName()); + spInfo.setProName(procedureInfo.getSpName()); + spInfo.setSql(procedureInfo.getSql()); + spInfo.setInputSchema(Common.convertSchema(procedureInfo.getInputSchemaList())); + spInfo.setOutputSchema(Common.convertSchema(procedureInfo.getOutputSchemaList())); + spInfo.setMainTable(procedureInfo.getMainTable()); + List tables = new ArrayList<>(); + List dbs = new ArrayList<>(); + for (com._4paradigm.openmldb.proto.Common.DbTableNamePair pair : procedureInfo.getTablesList()) { + tables.add(pair.getTableName()); + dbs.add(pair.getDbName()); + } + spInfo.setInputTables(tables); + spInfo.setInputDbs(dbs); + if (procedureInfo.getRouterColCount() > 0) { + spInfo.setRouterCol(procedureInfo.getRouterCol(0)); + } + return spInfo; + } } diff --git a/java/openmldb-jdbc/src/main/java/com/_4paradigm/openmldb/sdk/ProcedureInfo.java b/java/openmldb-jdbc/src/main/java/com/_4paradigm/openmldb/sdk/ProcedureInfo.java index 7795952a84c..309c8532a9e 100644 --- a/java/openmldb-jdbc/src/main/java/com/_4paradigm/openmldb/sdk/ProcedureInfo.java +++ b/java/openmldb-jdbc/src/main/java/com/_4paradigm/openmldb/sdk/ProcedureInfo.java @@ -27,8 +27,11 @@ public class ProcedureInfo { private Schema outputSchema; private String mainTable; private List inputTables = new ArrayList<>(); + private List inputDbs= new ArrayList<>(); + private int routerCol = -1; + public ProcedureInfo() { } @@ -95,4 +98,12 @@ public String getMainTable() { public void setMainTable(String mainTable) { this.mainTable = mainTable; } + + public void setRouterCol(int routerCol) { + this.routerCol = routerCol; + } + + public int getRouterCol() { + return routerCol; + } } diff --git a/java/openmldb-jdbc/src/main/java/com/_4paradigm/openmldb/sdk/QueryFuture.java b/java/openmldb-jdbc/src/main/java/com/_4paradigm/openmldb/sdk/QueryFuture.java index 374dce9758e..12bbd1ab8d9 100644 --- a/java/openmldb-jdbc/src/main/java/com/_4paradigm/openmldb/sdk/QueryFuture.java +++ b/java/openmldb-jdbc/src/main/java/com/_4paradigm/openmldb/sdk/QueryFuture.java @@ -17,10 +17,13 @@ package com._4paradigm.openmldb.sdk; import com._4paradigm.openmldb.Status; -import com._4paradigm.openmldb.jdbc.SQLResultSet; +import com._4paradigm.openmldb.common.codec.CodecMetaData; +import com._4paradigm.openmldb.sdk.impl.CallableDirectResultSet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; @@ -29,9 +32,13 @@ public class QueryFuture implements Future{ private static final Logger logger = LoggerFactory.getLogger(QueryFuture.class); com._4paradigm.openmldb.QueryFuture queryFuture; + Schema schema; + CodecMetaData metaData; - public QueryFuture(com._4paradigm.openmldb.QueryFuture queryFuture) { + public QueryFuture(com._4paradigm.openmldb.QueryFuture queryFuture, Schema schema, CodecMetaData metaData) { this.queryFuture = queryFuture; + this.schema = schema; + this.metaData = metaData; } @Override @@ -48,23 +55,37 @@ public boolean isCancelled() { @Override public boolean isDone() { - return queryFuture.IsDone(); + if (queryFuture != null) { + return queryFuture.IsDone(); + } + return true; } @Override public java.sql.ResultSet get() throws InterruptedException, ExecutionException { + if (queryFuture == null) { + throw new ExecutionException(new SqlException("queryFuture is null")); + } Status status = new Status(); com._4paradigm.openmldb.ResultSet resultSet = queryFuture.GetResultSet(status); if (status.getCode() != 0 || resultSet == null) { String msg = status.ToString(); status.delete(); - status = null; + if (resultSet != null) { + resultSet.delete(); + } logger.error("call procedure failed: {}", msg); throw new ExecutionException(new SqlException("call procedure failed: " + msg)); } status.delete(); - status = null; - return new SQLResultSet(resultSet, queryFuture); + int totalRows = resultSet.Size(); + int dataLength = resultSet.GetDataLength(); + ByteBuffer dataBuf = ByteBuffer.allocate(dataLength).order(ByteOrder.LITTLE_ENDIAN); + resultSet.CopyTo(dataBuf.array()); + resultSet.delete(); + queryFuture.delete(); + queryFuture = null; + return new CallableDirectResultSet(dataBuf, totalRows, schema, metaData); } /** diff --git a/java/openmldb-jdbc/src/main/java/com/_4paradigm/openmldb/sdk/Schema.java b/java/openmldb-jdbc/src/main/java/com/_4paradigm/openmldb/sdk/Schema.java index 1c23ece9cae..f708adf2b74 100644 --- a/java/openmldb-jdbc/src/main/java/com/_4paradigm/openmldb/sdk/Schema.java +++ b/java/openmldb-jdbc/src/main/java/com/_4paradigm/openmldb/sdk/Schema.java @@ -24,9 +24,11 @@ public class Schema { private List columnList; + private int size; public Schema(List columnList) { this.columnList = columnList; + this.size = columnList.size(); } public List getColumnList() { @@ -46,4 +48,20 @@ public String toString() { } }).collect(Collectors.joining(",")); } + + public int size() { + return size; + } + + public String getColumnName(int idx) { + return columnList.get(idx).getColumnName(); + } + + public int getColumnType(int idx) { + return columnList.get(idx).getSqlType(); + } + + public boolean isNullable(int idx) { + return !columnList.get(idx).isNotNull(); + } } diff --git a/java/openmldb-jdbc/src/main/java/com/_4paradigm/openmldb/sdk/impl/BatchCallablePreparedStatementImpl.java b/java/openmldb-jdbc/src/main/java/com/_4paradigm/openmldb/sdk/impl/BatchCallablePreparedStatementImpl.java index ffbd8cf283a..e1b1c8b21e9 100644 --- a/java/openmldb-jdbc/src/main/java/com/_4paradigm/openmldb/sdk/impl/BatchCallablePreparedStatementImpl.java +++ b/java/openmldb-jdbc/src/main/java/com/_4paradigm/openmldb/sdk/impl/BatchCallablePreparedStatementImpl.java @@ -18,35 +18,52 @@ import com._4paradigm.openmldb.*; +import com._4paradigm.openmldb.common.codec.FlexibleRowBuilder; import com._4paradigm.openmldb.jdbc.CallablePreparedStatement; import com._4paradigm.openmldb.jdbc.SQLResultSet; import com._4paradigm.openmldb.sdk.QueryFuture; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; import java.sql.SQLException; +import java.sql.Timestamp; +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.TimeUnit; public class BatchCallablePreparedStatementImpl extends CallablePreparedStatement { - private ColumnIndicesSet commonColumnIndices; - private SQLRequestRowBatch currentRowBatch; - - public BatchCallablePreparedStatementImpl(String db, String spName, SQLRouter router) throws SQLException { - super(db, spName, router); - this.commonColumnIndices = new ColumnIndicesSet(this.currentSchema); - for (int i = 0; i < this.currentSchema.GetColumnCnt(); i++) { - if (this.currentSchema.IsConstant(i)) { - this.commonColumnIndices.AddCommonColumnIdx(i); - } + + private List datas = new ArrayList<>(); + private ByteBuffer meta; + private ByteBuffer result; + private int totalSize = 0; + + public BatchCallablePreparedStatementImpl(Deployment deployment, SQLRouter router) throws SQLException { + super(deployment, router); + rowBuilder = new FlexibleRowBuilder(deployment.getInputMetaData()); + } + + private void build() throws SQLException { + if (datas.isEmpty()) { + throw new SQLException("no data"); + } + meta = ByteBuffer.allocate(4 * (datas.size() + 1)).order(ByteOrder.LITTLE_ENDIAN); + meta.putInt(0); // reserved for common slice + result = ByteBuffer.allocate(totalSize); + for (ByteBuffer buf : datas) { + meta.putInt(buf.capacity()); + result.put(buf.array()); } - this.currentRowBatch = new SQLRequestRowBatch(this.currentSchema, this.commonColumnIndices); } @Override public SQLResultSet executeQuery() throws SQLException { checkClosed(); checkExecutorClosed(); + build(); Status status = new Status(); - com._4paradigm.openmldb.ResultSet resultSet = router.ExecuteSQLBatchRequest( - db, currentSql, currentRowBatch, status); + com._4paradigm.openmldb.ResultSet resultSet = router.CallSQLBatchRequestProcedure( + db, deploymentName, meta.array(), meta.capacity(), result.array(), result.capacity(), status); if (status.getCode() != 0 || resultSet == null) { String msg = status.ToString(); status.delete(); @@ -56,10 +73,16 @@ public SQLResultSet executeQuery() throws SQLException { throw new SQLException("execute sql fail: " + msg); } status.delete(); - SQLResultSet rs = new SQLResultSet(resultSet); + int totalRows = resultSet.Size(); + int dataLength = resultSet.GetDataLength(); + ByteBuffer dataBuf = ByteBuffer.allocate(dataLength).order(ByteOrder.LITTLE_ENDIAN); + resultSet.CopyTo(dataBuf.array()); + resultSet.delete(); + SQLResultSet rs = new CallableDirectResultSet(dataBuf, totalRows, deployment.getOutputSchema(), deployment.getOutputMetaData()); if (closeOnComplete) { closed = true; } + clearParameters(); return rs; } @@ -67,41 +90,37 @@ public SQLResultSet executeQuery() throws SQLException { public QueryFuture executeQueryAsync(long timeOut, TimeUnit unit) throws SQLException { checkClosed(); checkExecutorClosed(); + build(); Status status = new Status(); - com._4paradigm.openmldb.QueryFuture queryFuture = router.CallSQLBatchRequestProcedure(db, spName, unit.toMillis(timeOut), currentRowBatch, status); + com._4paradigm.openmldb.QueryFuture queryFuture = router.CallSQLBatchRequestProcedure(db, deploymentName, unit.toMillis(timeOut), + meta.array(), meta.capacity(), result.array(), result.capacity(), status); if (status.getCode() != 0 || queryFuture == null) { String msg = status.ToString(); status.delete(); if (queryFuture != null) { queryFuture.delete(); } - throw new SQLException("call procedure fail, msg: " + msg); + throw new SQLException("call deployment failed, msg: " + msg); } status.delete(); - return new QueryFuture(queryFuture); + clearParameters(); + return new QueryFuture(queryFuture, deployment.getOutputSchema(), deployment.getOutputMetaData()); } @Override public void addBatch() throws SQLException { - dataBuild(); - if (!this.currentRow.OK()) { - throw new RuntimeException("not ok row"); + if (!rowBuilder.build()) { + throw new SQLException("failed to encode data"); } - currentRowBatch.AddRow(this.currentRow); - this.currentRow.delete(); - Status status = new Status(); - this.currentRow = router.GetRequestRow(db, currentSql, status); - if (status.getCode() != 0 || this.currentRow == null) { - String msg = status.ToString(); - status.delete(); - throw new SQLException("getRequestRow failed!, msg: " + msg); - } - status.delete(); + ByteBuffer buf = rowBuilder.getValue(); + datas.add(buf); + totalSize += buf.capacity(); + rowBuilder.clear(); } @Override public void clearBatch() throws SQLException { - currentRowBatch.Clear(); + clearParameters(); } @Override @@ -110,15 +129,91 @@ public int[] executeBatch() throws SQLException { } @Override - public void close() throws SQLException { - super.close(); - if (commonColumnIndices != null) { - commonColumnIndices.delete(); - commonColumnIndices = null; + public void clearParameters() { + datas.clear(); + rowBuilder.clear(); + result = null; + meta = null; + totalSize = 0; + } + + @Override + public void setNull(int i, int i1) throws SQLException { + int realIdx = i - 1; + if (!rowBuilder.setNULL(realIdx)) { + throw new SQLException("set null failed. idx is " + i); + } + } + + @Override + public void setBoolean(int i, boolean b) throws SQLException { + int realIdx = i - 1; + if (!rowBuilder.setBool(realIdx, b)) { + throw new SQLException("set bool failed. idx is " + i); + } + } + + @Override + public void setShort(int i, short i1) throws SQLException { + int realIdx = i - 1; + if (!rowBuilder.setSmallInt(realIdx, i1)) { + throw new SQLException("set short failed. idx is " + i); } - if (currentRowBatch != null) { - currentRowBatch.delete(); - currentRowBatch = null; + } + + @Override + public void setInt(int i, int i1) throws SQLException { + int realIdx = i - 1; + if (!rowBuilder.setInt(realIdx, i1)) { + throw new SQLException("set int failed. idx is " + i); + } + } + + @Override + public void setLong(int i, long l) throws SQLException { + int realIdx = i - 1; + if (!rowBuilder.setBigInt(realIdx, l)) { + throw new SQLException("set long failed. idx is " + i); + } + } + + @Override + public void setFloat(int i, float v) throws SQLException { + int realIdx = i - 1; + if (!rowBuilder.setFloat(realIdx, v)) { + throw new SQLException("set float failed. idx is " + i); + } + } + + @Override + public void setDouble(int i, double v) throws SQLException { + int realIdx = i - 1; + if (!rowBuilder.setDouble(realIdx, v)) { + throw new SQLException("set double failed. idx is " + i); + } + } + + @Override + public void setDate(int i, java.sql.Date date) throws SQLException { + int realIdx = i - 1; + if (!rowBuilder.setDate(realIdx, date)) { + throw new SQLException("set date failed. idx is " + i); + } + } + + @Override + public void setTimestamp(int i, Timestamp timestamp) throws SQLException { + int realIdx = i - 1; + if (!rowBuilder.setTimestamp(realIdx, timestamp)) { + throw new SQLException("set timestamp failed. idx is " + i); + } + } + + @Override + public void setString(int i, String s) throws SQLException { + int realIdx = i - 1; + if (!rowBuilder.setString(realIdx, s)) { + throw new SQLException("set string failed. idx is " + i); } } } diff --git a/java/openmldb-jdbc/src/main/java/com/_4paradigm/openmldb/sdk/impl/BatchRequestPreparedStatementImpl.java b/java/openmldb-jdbc/src/main/java/com/_4paradigm/openmldb/sdk/impl/BatchRequestPreparedStatementImpl.java index ae6060326b0..5f3ae1f696e 100644 --- a/java/openmldb-jdbc/src/main/java/com/_4paradigm/openmldb/sdk/impl/BatchRequestPreparedStatementImpl.java +++ b/java/openmldb-jdbc/src/main/java/com/_4paradigm/openmldb/sdk/impl/BatchRequestPreparedStatementImpl.java @@ -63,7 +63,7 @@ public SQLResultSet executeQuery() throws SQLException { throw new SQLException("execute sql fail: " + msg); } status.delete(); - SQLResultSet rs = new SQLResultSet(resultSet); + SQLResultSet rs = new NativeResultSet(resultSet); if (closeOnComplete) { closed = true; } diff --git a/java/openmldb-jdbc/src/main/java/com/_4paradigm/openmldb/sdk/impl/CallableDirectResultSet.java b/java/openmldb-jdbc/src/main/java/com/_4paradigm/openmldb/sdk/impl/CallableDirectResultSet.java new file mode 100644 index 00000000000..03d691b2170 --- /dev/null +++ b/java/openmldb-jdbc/src/main/java/com/_4paradigm/openmldb/sdk/impl/CallableDirectResultSet.java @@ -0,0 +1,36 @@ +package com._4paradigm.openmldb.sdk.impl; + +import com._4paradigm.openmldb.common.codec.CodecMetaData; +import com._4paradigm.openmldb.common.codec.RowView; +import com._4paradigm.openmldb.jdbc.DirectResultSet; +import com._4paradigm.openmldb.sdk.Schema; + +import java.nio.ByteBuffer; +import java.sql.SQLException; + +public class CallableDirectResultSet extends DirectResultSet { + private int position = 0; + + public CallableDirectResultSet(ByteBuffer buf, int totalRows, Schema schema, CodecMetaData metaData) { + super(buf, totalRows, schema); + rowView = new RowView(metaData); + } + + @Override + public boolean next() throws SQLException { + if (closed) { + return false; + } + if (rowNum < totalRows && position < buf.capacity()) { + buf.position(position); + int rowLength = buf.getInt(position + 2); + position += rowLength; + if (position > buf.capacity()) { + return false; + } + rowNum++; + return rowView.reset(buf.slice(), rowLength); + } + return false; + } +} diff --git a/java/openmldb-jdbc/src/main/java/com/_4paradigm/openmldb/sdk/impl/CallablePreparedStatementImpl.java b/java/openmldb-jdbc/src/main/java/com/_4paradigm/openmldb/sdk/impl/CallablePreparedStatementImpl.java index c5cbaca4075..6178100b77b 100644 --- a/java/openmldb-jdbc/src/main/java/com/_4paradigm/openmldb/sdk/impl/CallablePreparedStatementImpl.java +++ b/java/openmldb-jdbc/src/main/java/com/_4paradigm/openmldb/sdk/impl/CallablePreparedStatementImpl.java @@ -18,26 +18,39 @@ import com._4paradigm.openmldb.SQLRouter; import com._4paradigm.openmldb.Status; +import com._4paradigm.openmldb.common.codec.CodecUtil; +import com._4paradigm.openmldb.common.codec.FlexibleRowBuilder; import com._4paradigm.openmldb.jdbc.CallablePreparedStatement; -import com._4paradigm.openmldb.jdbc.SQLResultSet; import com._4paradigm.openmldb.sdk.QueryFuture; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.sql.ResultSet; import java.sql.SQLException; +import java.sql.Timestamp; import java.util.concurrent.TimeUnit; public class CallablePreparedStatementImpl extends CallablePreparedStatement { + private int routerCol; + private String routerValue = ""; - public CallablePreparedStatementImpl(String db, String spName, SQLRouter router) throws SQLException { - super(db, spName, router); + public CallablePreparedStatementImpl(Deployment deployment, SQLRouter router) throws SQLException { + super(deployment, router); + rowBuilder = new FlexibleRowBuilder(deployment.getInputMetaData()); + routerCol = deployment.getRouterCol(); } @Override - public SQLResultSet executeQuery() throws SQLException { + public ResultSet executeQuery() throws SQLException { checkClosed(); checkExecutorClosed(); - dataBuild(); + if (!rowBuilder.build()) { + throw new SQLException("failed to encode data"); + } + ByteBuffer buf = rowBuilder.getValue(); Status status = new Status(); - com._4paradigm.openmldb.ResultSet resultSet = router.CallProcedure(db, spName, currentRow, status); + com._4paradigm.openmldb.ResultSet resultSet = router.CallProcedure(db, deploymentName, + buf.array(), buf.capacity(), routerValue, status); if (status.getCode() != 0 || resultSet == null) { String msg = status.ToString(); status.delete(); @@ -47,7 +60,13 @@ public SQLResultSet executeQuery() throws SQLException { throw new SQLException("call procedure fail, msg: " + msg); } status.delete(); - SQLResultSet rs = new SQLResultSet(resultSet); + int totalRows = resultSet.Size(); + int dataLength = resultSet.GetDataLength(); + ByteBuffer dataBuf = ByteBuffer.allocate(dataLength).order(ByteOrder.LITTLE_ENDIAN); + resultSet.CopyTo(dataBuf.array()); + resultSet.delete(); + ResultSet rs = new CallableDirectResultSet(dataBuf, totalRows, deployment.getOutputSchema(), deployment.getOutputMetaData()); + clearParameters(); if (closeOnComplete) { closed = true; } @@ -58,9 +77,13 @@ public SQLResultSet executeQuery() throws SQLException { public QueryFuture executeQueryAsync(long timeOut, TimeUnit unit) throws SQLException { checkClosed(); checkExecutorClosed(); - dataBuild(); + if (!rowBuilder.build()) { + throw new SQLException("failed to encode data"); + } + ByteBuffer buf = rowBuilder.getValue(); Status status = new Status(); - com._4paradigm.openmldb.QueryFuture queryFuture = router.CallProcedure(db, spName, unit.toMillis(timeOut), currentRow, status); + com._4paradigm.openmldb.QueryFuture queryFuture = router.CallProcedure(db, deploymentName, + unit.toMillis(timeOut), buf.array(), buf.capacity(), routerValue, status); if (status.getCode() != 0 || queryFuture == null) { String msg = status.ToString(); status.delete(); @@ -70,7 +93,115 @@ public QueryFuture executeQueryAsync(long timeOut, TimeUnit unit) throws SQLExce throw new SQLException("call procedure fail, msg: " + msg); } status.delete(); - return new QueryFuture(queryFuture); + clearParameters(); + return new QueryFuture(queryFuture, deployment.getOutputSchema(), deployment.getOutputMetaData()); + } + + @Override + public void clearParameters() { + rowBuilder.clear(); + routerValue = ""; + } + + @Override + public void setNull(int i, int i1) throws SQLException { + int realIdx = i - 1; + if (!rowBuilder.setNULL(realIdx)) { + throw new SQLException("set null failed. idx is " + i); + } + } + + @Override + public void setBoolean(int i, boolean b) throws SQLException { + int realIdx = i - 1; + if (realIdx == routerCol) { + routerValue = String.valueOf(b); + } + if (!rowBuilder.setBool(realIdx, b)) { + throw new SQLException("set bool failed. idx is " + i); + } + } + + @Override + public void setShort(int i, short i1) throws SQLException { + int realIdx = i - 1; + if (realIdx == routerCol) { + routerValue = String.valueOf(i1); + } + if (!rowBuilder.setSmallInt(realIdx, i1)) { + throw new SQLException("set short failed. idx is " + i); + } + } + + @Override + public void setInt(int i, int i1) throws SQLException { + int realIdx = i - 1; + if (realIdx == routerCol) { + routerValue = String.valueOf(i1); + } + if (!rowBuilder.setInt(realIdx, i1)) { + throw new SQLException("set int failed. idx is " + i); + } + } + + @Override + public void setLong(int i, long l) throws SQLException { + int realIdx = i - 1; + if (realIdx == routerCol) { + routerValue = String.valueOf(l); + } + if (!rowBuilder.setBigInt(realIdx, l)) { + throw new SQLException("set long failed. idx is " + i); + } + } + + @Override + public void setFloat(int i, float v) throws SQLException { + int realIdx = i - 1; + if (!rowBuilder.setFloat(realIdx, v)) { + throw new SQLException("set float failed. idx is " + i); + } + } + + @Override + public void setDouble(int i, double v) throws SQLException { + int realIdx = i - 1; + if (!rowBuilder.setDouble(realIdx, v)) { + throw new SQLException("set double failed. idx is " + i); + } + } + + @Override + public void setDate(int i, java.sql.Date date) throws SQLException { + int realIdx = i - 1; + if (realIdx == routerCol) { + routerValue = String.valueOf(CodecUtil.dateToDateInt(date)); + } + if (!rowBuilder.setDate(realIdx, date)) { + throw new SQLException("set date failed. idx is " + i); + } + } + + @Override + public void setTimestamp(int i, Timestamp timestamp) throws SQLException { + int realIdx = i - 1; + if (realIdx == routerCol) { + routerValue = String.valueOf(timestamp.getTime()); + } + if (!rowBuilder.setTimestamp(realIdx, timestamp)) { + throw new SQLException("set timestamp failed. idx is " + i); + } + } + + @Override + public void setString(int i, String s) throws SQLException { + int realIdx = i - 1; + if (realIdx == routerCol) { + routerValue = s; + } + if (!rowBuilder.setString(realIdx, s)) { + throw new SQLException("set string failed. idx is " + i); + } } } diff --git a/java/openmldb-jdbc/src/main/java/com/_4paradigm/openmldb/sdk/impl/Deployment.java b/java/openmldb-jdbc/src/main/java/com/_4paradigm/openmldb/sdk/impl/Deployment.java new file mode 100644 index 00000000000..4f25496b5d7 --- /dev/null +++ b/java/openmldb-jdbc/src/main/java/com/_4paradigm/openmldb/sdk/impl/Deployment.java @@ -0,0 +1,73 @@ +/* + * Copyright 2021 4Paradigm + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com._4paradigm.openmldb.sdk.impl; + +import com._4paradigm.openmldb.common.codec.CodecMetaData; +import com._4paradigm.openmldb.sdk.Common; +import com._4paradigm.openmldb.sdk.Schema; +import com._4paradigm.openmldb.sdk.ProcedureInfo; + +public class Deployment { + + private CodecMetaData inputMetaData; + private CodecMetaData outputMetaData; + private ProcedureInfo proInfo; + + public Deployment(com._4paradigm.openmldb.proto.SQLProcedure.ProcedureInfo info) throws Exception { + proInfo = Common.convertProcedureInfo(info); + inputMetaData = new CodecMetaData(info.getInputSchemaList(), false); + outputMetaData = new CodecMetaData(info.getOutputSchemaList(), true); + } + + public Deployment(ProcedureInfo procedureInfo) throws Exception { + proInfo = procedureInfo; + inputMetaData = new CodecMetaData(Common.convert2ProtoSchema(procedureInfo.getInputSchema()), false); + outputMetaData = new CodecMetaData(Common.convert2ProtoSchema(procedureInfo.getOutputSchema()), true); + } + + public CodecMetaData getInputMetaData() { + return inputMetaData; + } + + public CodecMetaData getOutputMetaData() { + return outputMetaData; + } + + public int getRouterCol() { + return proInfo.getRouterCol(); + } + + public String getDatabase() { + return proInfo.getDbName(); + } + + public String getName() { + return proInfo.getProName(); + } + + public String getSQL() { + return proInfo.getSql(); + } + + public Schema getInputSchema() { + return proInfo.getInputSchema(); + } + + public Schema getOutputSchema() { + return proInfo.getOutputSchema(); + } +} diff --git a/java/openmldb-jdbc/src/main/java/com/_4paradigm/openmldb/sdk/impl/DeploymentManager.java b/java/openmldb-jdbc/src/main/java/com/_4paradigm/openmldb/sdk/impl/DeploymentManager.java new file mode 100644 index 00000000000..9dc73868f5e --- /dev/null +++ b/java/openmldb-jdbc/src/main/java/com/_4paradigm/openmldb/sdk/impl/DeploymentManager.java @@ -0,0 +1,101 @@ +/* + * Copyright 2021 4Paradigm + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com._4paradigm.openmldb.sdk.impl; + +import com._4paradigm.openmldb.common.zk.ZKClient; +import com._4paradigm.openmldb.sdk.SqlException; +import org.apache.curator.framework.recipes.cache.NodeCache; +import org.apache.curator.framework.recipes.cache.NodeCacheListener; +import com._4paradigm.openmldb.proto.SQLProcedure; +import com._4paradigm.openmldb.proto.Type; +import org.xerial.snappy.Snappy; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + +public class DeploymentManager { + + private Map, Deployment> deployments = new ConcurrentHashMap<>(); + private ZKClient zkClient; + private NodeCache nodeCache; + private String spPath; + + public DeploymentManager(ZKClient zkClient) throws SqlException { + this.zkClient = zkClient; + if (zkClient != null) { + spPath = zkClient.getConfig().getNamespace() + "/store_procedure/db_sp_data"; + nodeCache = new NodeCache(zkClient.getClient(), zkClient.getConfig().getNamespace() + "/table/notify"); + try { + parseAllDeployment(); + nodeCache.start(); + nodeCache.getListenable().addListener(new NodeCacheListener() { + @Override + public void nodeChanged() throws Exception { + parseAllDeployment(); + } + }); + } catch (Exception e) { + throw new SqlException("start NodeCache failed. " + e.getMessage()); + } + } + } + + public void parseAllDeployment() throws Exception { + if (!zkClient.checkExists(spPath)) { + return; + } + List children = zkClient.getChildren(spPath); + Set> curDeployments = new HashSet<>(); + for (String path : children) { + byte[] bytes = zkClient.getClient().getData().forPath(spPath + "/" + path); + byte[] data = Snappy.uncompress(bytes); + SQLProcedure.ProcedureInfo procedureInfo = SQLProcedure.ProcedureInfo.parseFrom(data); + Deployment deployment = getDeployment(procedureInfo.getDbName(), procedureInfo.getSpName()); + if (deployment != null) { + if (deployment.getSQL().equals(procedureInfo.getSql())) { + continue; + } + } + deployment = new Deployment(procedureInfo); + AbstractMap.SimpleImmutableEntry key = + new AbstractMap.SimpleImmutableEntry<>(procedureInfo.getDbName(), procedureInfo.getSpName()); + addDeployment(key, deployment); + curDeployments.add(key); + } + if (deployments.size() > children.size()) { + Iterator> iterator = deployments.keySet().iterator(); + while (iterator.hasNext()) { + AbstractMap.SimpleImmutableEntry key = iterator.next(); + if (!curDeployments.contains(key)) { + iterator.remove(); + } + } + } + } + + public Deployment getDeployment(String db, String name) { + return deployments.get(new AbstractMap.SimpleImmutableEntry<>(db, name)); + } + + public void addDeployment(String db, String name, Deployment deployment) { + addDeployment(new AbstractMap.SimpleImmutableEntry<>(db, name), deployment); + } + + public void addDeployment(AbstractMap.SimpleImmutableEntry key, Deployment deployment) { + deployments.put(key, deployment); + } +} diff --git a/java/openmldb-jdbc/src/main/java/com/_4paradigm/openmldb/sdk/impl/NativeResultSet.java b/java/openmldb-jdbc/src/main/java/com/_4paradigm/openmldb/sdk/impl/NativeResultSet.java new file mode 100644 index 00000000000..482ce4db72c --- /dev/null +++ b/java/openmldb-jdbc/src/main/java/com/_4paradigm/openmldb/sdk/impl/NativeResultSet.java @@ -0,0 +1,184 @@ +/* + * Copyright 2021 4Paradigm + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com._4paradigm.openmldb.sdk.impl; + +import com._4paradigm.openmldb.jdbc.SQLResultSet; +import com._4paradigm.openmldb.sdk.Common; +import java.sql.*; + +public class NativeResultSet extends SQLResultSet { + private com._4paradigm.openmldb.ResultSet resultSet; + + public NativeResultSet(com._4paradigm.openmldb.ResultSet resultSet) { + this.resultSet = resultSet; + if (resultSet != null) { + totalRows = resultSet.Size(); + try { + this.schema = Common.convertSchema(resultSet.GetSchema()); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + private void check(int i, int type) throws SQLException { + checkIdx(i); + checkDataType(i, type); + } + + private void checkClosed() throws SQLException { + if (closed) { + throw new SQLException("resultset closed"); + } + } + + private void checkIdx(int i) throws SQLException { + if (i <= 0) { + throw new SQLException("index underflow"); + } + if (i > schema.size()) { + throw new SQLException("index overflow"); + } + } + + private void checkResultSetNull() throws SQLException{ + if (this.resultSet == null) { + throw new SQLException("resultset is null"); + } + } + + private void checkDataType(int i, int type) throws SQLException { + if (schema.getColumnType(i - 1) != type) { + throw new SQLException(String.format("data type not match, get %d and expect %d", + schema.getColumnType(i - 1), type)); + } + } + + @Override + public boolean next() throws SQLException { + checkClosed(); + checkResultSetNull(); + if (this.resultSet.Next()) { + this.rowNum++; + return true; + } else { + return false; + } + } + + @Override + public void close() throws SQLException { + if (resultSet != null) { + resultSet.delete(); + resultSet = null; + } + closed = true; + } + + @Override + public String getString(int i) throws SQLException { + check(i, Types.VARCHAR); + if (this.resultSet.IsNULL(i - 1)) { + return null; + } + return this.resultSet.GetStringUnsafe(i - 1); + } + + @Override + public boolean getBoolean(int i) throws SQLException { + check(i, Types.BOOLEAN); + if (this.resultSet.IsNULL(i - 1)) { + return false; + } + return this.resultSet.GetBoolUnsafe(i - 1); + } + + @Override + public short getShort(int i) throws SQLException { + check(i, Types.SMALLINT); + if (this.resultSet.IsNULL(i - 1)) { + return 0; + } + return this.resultSet.GetInt16Unsafe(i - 1); + } + + @Override + public int getInt(int i) throws SQLException { + check(i, Types.INTEGER); + if (this.resultSet.IsNULL(i - 1)) { + return 0; + } + return resultSet.GetInt32Unsafe(i - 1); + } + + @Override + public long getLong(int i) throws SQLException { + check(i, Types.BIGINT); + if (this.resultSet.IsNULL(i - 1)) { + return 0; + } + return this.resultSet.GetInt64Unsafe(i - 1); + } + + @Override + public float getFloat(int i) throws SQLException { + check(i, Types.FLOAT); + if (this.resultSet.IsNULL(i - 1)) { + return 0.0f; + } + return this.resultSet.GetFloatUnsafe(i - 1); + } + + @Override + public double getDouble(int i) throws SQLException { + check(i, Types.DOUBLE); + if (this.resultSet.IsNULL(i - 1)) { + return 0.0; + } + return resultSet.GetDoubleUnsafe(i - 1); + } + + @Override + public Date getDate(int i) throws SQLException { + check(i, Types.DATE); + if (this.resultSet.IsNULL(i - 1)) { + return null; + } + com._4paradigm.openmldb.Date date = this.resultSet.GetStructDateUnsafe(i - 1); + return new Date(date.getYear() - 1900, date.getMonth() - 1, date.getDay()); + } + + @Override + public Timestamp getTimestamp(int i) throws SQLException { + check(i, Types.TIMESTAMP); + if (this.resultSet.IsNULL(i - 1)) { + return null; + } + return new Timestamp(this.resultSet.GetTimeUnsafe(i -1)); + } + + @Override + public String getNString(int i) throws SQLException { + checkClosed(); + checkResultSetNull(); + checkIdx(i); + if (this.resultSet.IsNULL(i - 1)) { + return null; + } + return this.resultSet.GetAsStringUnsafe(i - 1); + } +} diff --git a/java/openmldb-jdbc/src/main/java/com/_4paradigm/openmldb/sdk/impl/PreparedStatementImpl.java b/java/openmldb-jdbc/src/main/java/com/_4paradigm/openmldb/sdk/impl/PreparedStatementImpl.java index 02da5e5de69..a7303312839 100644 --- a/java/openmldb-jdbc/src/main/java/com/_4paradigm/openmldb/sdk/impl/PreparedStatementImpl.java +++ b/java/openmldb-jdbc/src/main/java/com/_4paradigm/openmldb/sdk/impl/PreparedStatementImpl.java @@ -16,14 +16,33 @@ package com._4paradigm.openmldb.sdk.impl; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.sql.ResultSetMetaData; import java.sql.SQLException; +import java.util.HashMap; +import java.util.Map; import java.util.TreeMap; import com._4paradigm.openmldb.DataType; import com._4paradigm.openmldb.SQLRouter; import com._4paradigm.openmldb.jdbc.PreparedStatement; +import com._4paradigm.openmldb.*; +import com._4paradigm.openmldb.jdbc.SQLResultSet; +import com._4paradigm.openmldb.jdbc.SQLResultSetMetaData; +import com._4paradigm.openmldb.sdk.Common; public class PreparedStatementImpl extends PreparedStatement { + private String db; + private String currentSql; + private SQLRequestRow currentRow; + private Schema currentSchema; + private TreeMap types; + private TreeMap orgTypes; + private TreeMap currentDatas; + private Map stringsLen = new HashMap<>(); + public static final Charset CHARSET = StandardCharsets.UTF_8; + public PreparedStatementImpl(String db, String sql, SQLRouter router) throws SQLException { if (db == null) throw new SQLException("db is null"); @@ -35,4 +54,236 @@ public PreparedStatementImpl(String db, String sql, SQLRouter router) throws SQL this.currentDatas = new TreeMap(); this.types = new TreeMap(); } + + private void checkNull() throws SQLException { + if (db == null) { + throw new SQLException("db is null"); + } + if (currentSql == null) { + throw new SQLException("sql is null"); + } + if (router == null) { + throw new SQLException("SQLRouter is null"); + } + if (currentDatas == null) { + throw new SQLException("currentDatas is null"); + } + if (types == null) { + throw new SQLException("currentDatas is null"); + } + } + + void checkIdx(int i) throws SQLException { + checkClosed(); + checkNull(); + if (i <= 0) { + throw new SQLException("index out of array"); + } + if (currentDatas.containsKey(i)) { + throw new SQLException("index duplicate, index: " + i + " already exist"); + } + } + + @Override + public SQLResultSet executeQuery() throws SQLException { + checkClosed(); + checkExecutorClosed(); + dataBuild(); + Status status = new Status(); + com._4paradigm.openmldb.ResultSet resultSet = router.ExecuteSQLParameterized(db, currentSql, currentRow, status); + if (resultSet == null || status.getCode() != 0) { + String msg = status.ToString(); + status.delete(); + if (resultSet != null) { + resultSet.delete(); + } + throw new SQLException("execute sql fail, msg: " + msg); + } + status.delete(); + SQLResultSet rs = new NativeResultSet(resultSet); + if (closeOnComplete) { + closed = true; + } + return rs; + } + + @Override + @Deprecated + public int executeUpdate() throws SQLException { + throw new SQLException("current do not support this method"); + } + + @Override + public void setNull(int parameterIndex, int sqlType) throws SQLException { + setNull(parameterIndex, Util.sqlTypeToDataType(sqlType)); + } + + private void setNull(int i, DataType type) throws SQLException { + checkIdx(i); + types.put(i, type); + currentDatas.put(i, null); + } + + @Override + public void setBoolean(int i, boolean b) throws SQLException { + checkIdx(i); + types.put(i, DataType.kTypeBool); + currentDatas.put(i, b); + } + + @Override + public void setShort(int i, short i1) throws SQLException { + checkIdx(i); + types.put(i, DataType.kTypeInt16); + currentDatas.put(i, i1); + } + + @Override + public void setInt(int i, int i1) throws SQLException { + checkIdx(i); + types.put(i, DataType.kTypeInt32); + currentDatas.put(i, i1); + } + + @Override + public void setLong(int i, long l) throws SQLException { + checkIdx(i); + types.put(i, DataType.kTypeInt64); + currentDatas.put(i, l); + } + + @Override + public void setFloat(int i, float v) throws SQLException { + checkIdx(i); + types.put(i, DataType.kTypeFloat); + currentDatas.put(i, v); + } + + @Override + public void setDouble(int i, double v) throws SQLException { + checkIdx(i); + types.put(i, DataType.kTypeDouble); + currentDatas.put(i, v); + } + + @Override + public void setString(int i, String s) throws SQLException { + checkIdx(i); + if (s == null) { + setNull(i, DataType.kTypeString); + return; + } + types.put(i, DataType.kTypeString); + byte[] bytes = s.getBytes(CHARSET); + stringsLen.put(i, bytes.length); + currentDatas.put(i, bytes); + } + + @Override + public void setDate(int i, java.sql.Date date) throws SQLException { + checkIdx(i); + if (date == null) { + setNull(i, DataType.kTypeDate); + return; + } + types.put(i, DataType.kTypeDate); + currentDatas.put(i, date); + } + + @Override + public void setTimestamp(int i, java.sql.Timestamp timestamp) throws SQLException { + checkIdx(i); + if (timestamp == null) { + setNull(i, DataType.kTypeTimestamp); + return; + } + types.put(i, DataType.kTypeTimestamp); + long ts = timestamp.getTime(); + currentDatas.put(i, ts); + } + + @Override + public void clearParameters() { + currentDatas.clear(); + types.clear(); + stringsLen.clear(); + } + + protected void dataBuild() throws SQLException { + if (types == null) { + throw new SQLException("fail to build data when data types is null"); + } + // types has been updated + if (null == this.currentRow || orgTypes != types) { + if (types.firstKey() != 1 || types.lastKey() != types.size()) { + throw new SQLException("data not enough, indexes are " + currentDatas.keySet()); + } + ColumnTypes columnTypes = new ColumnTypes(); + for (int i = 0; i < types.size(); i++) { + columnTypes.AddColumnType(types.get(i + 1)); + } + this.currentRow = SQLRequestRow.CreateSQLRequestRowFromColumnTypes(columnTypes); + if (this.currentRow == null) { + throw new SQLException("fail to create sql request row from column types"); + } + this.currentSchema = this.currentRow.GetSchema(); + this.orgTypes = this.types; + } + if (this.currentSchema == null) { + throw new SQLException("fail to build data with null schema"); + } + int strLen = 0; + for (Map.Entry entry : stringsLen.entrySet()) { + strLen += entry.getValue(); + } + boolean ok = this.currentRow.Init(strLen); + if (!ok) { + throw new SQLException("build data row failed"); + } + for (int i = 0; i < this.currentSchema.GetColumnCnt(); i++) { + DataType dataType = this.currentSchema.GetColumnType(i); + Object data = this.currentDatas.get(i + 1); + if (data == null) { + ok = this.currentRow.AppendNULL(); + } else { + if (DataType.kTypeBool.equals(dataType)) { + ok = this.currentRow.AppendBool((boolean) data); + } else if (DataType.kTypeDate.equals(dataType)) { + java.sql.Date date = (java.sql.Date) data; + ok = this.currentRow.AppendDate(date.getYear() + 1900, date.getMonth() + 1, date.getDate()); + } else if (DataType.kTypeDouble.equals(dataType)) { + ok = this.currentRow.AppendDouble((double) data); + } else if (DataType.kTypeFloat.equals(dataType)) { + ok = this.currentRow.AppendFloat((float) data); + } else if (DataType.kTypeInt16.equals(dataType)) { + ok = this.currentRow.AppendInt16((short) data); + } else if (DataType.kTypeInt32.equals(dataType)) { + ok = this.currentRow.AppendInt32((int) data); + } else if (DataType.kTypeInt64.equals(dataType)) { + ok = this.currentRow.AppendInt64((long) data); + } else if (DataType.kTypeString.equals(dataType)) { + byte[] bdata = (byte[]) data; + ok = this.currentRow.AppendString(bdata, bdata.length); + } else if (DataType.kTypeTimestamp.equals(dataType)) { + ok = this.currentRow.AppendTimestamp((long) data); + } else { + throw new SQLException("unkown data type " + dataType.toString()); + } + } + if (!ok) { + throw new SQLException("append data failed, idx is " + i); + } + } + if (!this.currentRow.Build()) { + throw new SQLException("build request row failed"); + } + clearParameters(); + } + + @Override + public ResultSetMetaData getMetaData() throws SQLException { + checkClosed(); + checkNull(); + return new SQLResultSetMetaData(Common.convertSchema(this.currentSchema)); + } } diff --git a/java/openmldb-jdbc/src/main/java/com/_4paradigm/openmldb/sdk/impl/SqlClusterExecutor.java b/java/openmldb-jdbc/src/main/java/com/_4paradigm/openmldb/sdk/impl/SqlClusterExecutor.java index 42d4a760171..7d32ac092af 100644 --- a/java/openmldb-jdbc/src/main/java/com/_4paradigm/openmldb/sdk/impl/SqlClusterExecutor.java +++ b/java/openmldb-jdbc/src/main/java/com/_4paradigm/openmldb/sdk/impl/SqlClusterExecutor.java @@ -36,15 +36,11 @@ import com._4paradigm.openmldb.VectorString; import com._4paradigm.openmldb.common.LibraryLoader; import com._4paradigm.openmldb.common.Pair; +import com._4paradigm.openmldb.common.zk.ZKClient; +import com._4paradigm.openmldb.common.zk.ZKConfig; import com._4paradigm.openmldb.jdbc.CallablePreparedStatement; -import com._4paradigm.openmldb.jdbc.SQLResultSet; import com._4paradigm.openmldb.proto.NS; -import com._4paradigm.openmldb.sdk.Column; -import com._4paradigm.openmldb.sdk.Common; -import com._4paradigm.openmldb.sdk.Schema; -import com._4paradigm.openmldb.sdk.SdkOption; -import com._4paradigm.openmldb.sdk.SqlException; -import com._4paradigm.openmldb.sdk.SqlExecutor; +import com._4paradigm.openmldb.sdk.*; import com._4paradigm.openmldb.sql_router_sdk; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -64,6 +60,8 @@ public class SqlClusterExecutor implements SqlExecutor { private static final AtomicBoolean initialized = new AtomicBoolean(false); private SQLRouter sqlRouter; + private DeploymentManager deploymentManager; + private ZKClient zkClient; public SqlClusterExecutor(SdkOption option, String libraryPath) throws SqlException { initJavaSdkLibrary(libraryPath); @@ -72,6 +70,18 @@ public SqlClusterExecutor(SdkOption option, String libraryPath) throws SqlExcept SQLRouterOptions sqlOpt = option.buildSQLRouterOptions(); this.sqlRouter = sql_router_sdk.NewClusterSQLRouter(sqlOpt); sqlOpt.delete(); + zkClient = new ZKClient(ZKConfig.builder() + .cluster(option.getZkCluster()) + .namespace(option.getZkPath()) + .sessionTimeout((int)option.getSessionTimeout()) + .build()); + try { + if (!zkClient.connect()) { + throw new SqlException("zk client connect failed."); + } + } catch (Exception e) { + throw new SqlException("init zk client failed. " + e.getMessage()); + } } else { StandaloneOptions sqlOpt = option.buildStandaloneOptions(); this.sqlRouter = sql_router_sdk.NewStandaloneSQLRouter(sqlOpt); @@ -80,6 +90,7 @@ public SqlClusterExecutor(SdkOption option, String libraryPath) throws SqlExcept if (sqlRouter == null) { throw new SqlException("fail to create sql executor"); } + deploymentManager = new DeploymentManager(zkClient); } public SqlClusterExecutor(SdkOption option) throws SqlException { @@ -151,7 +162,7 @@ public java.sql.ResultSet executeSQL(String db, String sql) { logger.error("executeSQL failed: {}", status.ToString()); } status.delete(); - return new SQLResultSet(rs); + return new NativeResultSet(rs); } @Override @@ -199,13 +210,33 @@ public PreparedStatement getBatchRequestPreparedStmt(String db, String sql, @Override public CallablePreparedStatement getCallablePreparedStmt(String db, String deploymentName) throws SQLException { - return new CallablePreparedStatementImpl(db, deploymentName, this.sqlRouter); + Deployment deployment = deploymentManager.getDeployment(db, deploymentName); + if (deployment == null) { + try { + ProcedureInfo procedureInfo = showProcedure(db, deploymentName); + deployment = new Deployment(procedureInfo); + deploymentManager.addDeployment(db, deploymentName, deployment); + } catch (Exception e) { + throw new SQLException("deployment does not exist. db name " + db + " deployment name " + deploymentName); + } + } + return new CallablePreparedStatementImpl(deployment, this.sqlRouter); } @Override public CallablePreparedStatement getCallablePreparedStmtBatch(String db, String deploymentName) throws SQLException { - return new BatchCallablePreparedStatementImpl(db, deploymentName, this.sqlRouter); + Deployment deployment = deploymentManager.getDeployment(db, deploymentName); + if (deployment == null) { + try { + ProcedureInfo procedureInfo = showProcedure(db, deploymentName); + deployment = new Deployment(procedureInfo); + deploymentManager.addDeployment(db, deploymentName, deployment); + } catch (Exception e) { + throw new SQLException("deployment does not exist. db name " + db + " deployment name " + deploymentName); + } + } + return new BatchCallablePreparedStatementImpl(deployment, this.sqlRouter); } @Override @@ -284,15 +315,7 @@ public com._4paradigm.openmldb.sdk.ProcedureInfo showProcedure(String dbName, St throw new SQLException("ShowProcedure failed: " + msg); } status.delete(); - com._4paradigm.openmldb.sdk.ProcedureInfo spInfo = new com._4paradigm.openmldb.sdk.ProcedureInfo(); - spInfo.setDbName(procedureInfo.GetDbName()); - spInfo.setProName(procedureInfo.GetSpName()); - spInfo.setSql(procedureInfo.GetSql()); - spInfo.setInputSchema(Common.convertSchema(procedureInfo.GetInputSchema())); - spInfo.setOutputSchema(Common.convertSchema(procedureInfo.GetOutputSchema())); - spInfo.setMainTable(procedureInfo.GetMainTable()); - spInfo.setInputTables(procedureInfo.GetTables()); - spInfo.setInputDbs(procedureInfo.GetDbs()); + com._4paradigm.openmldb.sdk.ProcedureInfo spInfo = Common.convertProcedureInfo(procedureInfo); procedureInfo.delete(); return spInfo; } diff --git a/java/openmldb-jdbc/src/test/java/com/_4paradigm/openmldb/jdbc/RequestPreparedStatementTest.java b/java/openmldb-jdbc/src/test/java/com/_4paradigm/openmldb/jdbc/RequestPreparedStatementTest.java index f761138cb49..dc520b74221 100644 --- a/java/openmldb-jdbc/src/test/java/com/_4paradigm/openmldb/jdbc/RequestPreparedStatementTest.java +++ b/java/openmldb-jdbc/src/test/java/com/_4paradigm/openmldb/jdbc/RequestPreparedStatementTest.java @@ -20,14 +20,12 @@ import com._4paradigm.openmldb.sdk.SqlExecutor; import com._4paradigm.openmldb.sdk.impl.SqlClusterExecutor; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.Date; -import java.sql.Timestamp; +import java.sql.*; import org.testng.Assert; import org.testng.annotations.Test; +import java.sql.PreparedStatement; import java.util.ArrayList; import java.util.List; import java.util.Random; @@ -129,6 +127,96 @@ public void testRequest() { } } + @Test + public void testDeploymentRequest() { + java.sql.Statement state = executor.getStatement(); + String dbname = "db" + random.nextInt(100000); + String deploymentName = "dp_test1"; + try { + state.execute("drop database if exists " + dbname + ";"); + state.execute("create database " + dbname + ";"); + state.execute("use " + dbname + ";"); + String createTableSql = "create table trans(c1 string,\n" + + " c3 int,\n" + + " c4 bigint,\n" + + " c5 float,\n" + + " c6 double,\n" + + " c7 timestamp,\n" + + " c8 date,\n" + + " index(key=c1, ts=c7));"; + state.execute(createTableSql); + String selectSql = "SELECT c1, c3, sum(c4) OVER w1 as w1_c4_sum FROM trans WINDOW w1 AS " + + "(PARTITION BY trans.c1 ORDER BY trans.c7 ROWS BETWEEN 2 PRECEDING AND CURRENT ROW);"; + String deploySql = "DEPLOY " + deploymentName + " " + selectSql; + state.execute(deploySql); + } catch (SQLException e) { + e.printStackTrace(); + Assert.fail(); + } + String insertSql = "insert into trans values(\"aa\",23,33,1.4,2.4,1590738993000,\"2020-05-04\");"; + PreparedStatement pstmt = null; + try { + pstmt = executor.getInsertPreparedStmt(dbname, insertSql); + Assert.assertTrue(pstmt.execute()); + } catch (Exception e) { + e.printStackTrace(); + Assert.fail(); + } finally { + if (pstmt != null) { + try { + pstmt.close(); + } catch (Exception throwables) { + throwables.printStackTrace(); + } + } + } + + ResultSet resultSet = null; + try { + Thread.sleep(1000); + pstmt = executor.getCallablePreparedStmt(dbname, deploymentName); + + pstmt.setString(1, "bb"); + pstmt.setInt(2, 24); + pstmt.setLong(3, 34l); + pstmt.setFloat(4, 1.5f); + pstmt.setDouble(5, 2.5); + pstmt.setTimestamp(6, new Timestamp(1590738994000l)); + pstmt.setDate(7, Date.valueOf("2020-05-05")); + + resultSet = pstmt.executeQuery(); + + Assert.assertEquals(resultSet.getMetaData().getColumnCount(), 3); + Assert.assertTrue(resultSet.next()); + Assert.assertEquals(resultSet.getString(1), "bb"); + Assert.assertEquals(resultSet.getInt(2), 24); + Assert.assertEquals(resultSet.getLong(3), 34); + Assert.assertFalse(resultSet.next()); + + state.execute("drop deployment " + deploymentName + ";"); + String drop = "drop table trans;"; + boolean ok = executor.executeDDL(dbname, drop); + Assert.assertTrue(ok); + ok = executor.dropDB(dbname); + Assert.assertTrue(ok); + } catch (Exception e) { + e.printStackTrace(); + Assert.fail(); + } finally { + try { + state.close(); + if (resultSet != null) { + resultSet.close(); + } + if (pstmt != null) { + pstmt.close(); + } + } catch (Exception throwables) { + throwables.printStackTrace(); + } + } + } + @Test public void testBatchRequest() { String dbname = "db" + random.nextInt(100000); @@ -213,4 +301,101 @@ public void testBatchRequest() { } } } + + @Test + public void testDeploymentBatchRequest() { + java.sql.Statement state = executor.getStatement(); + String dbname = "db" + random.nextInt(100000); + String deploymentName = "dp_test1"; + try { + state.execute("drop database if exists " + dbname + ";"); + state.execute("create database " + dbname + ";"); + state.execute("use " + dbname + ";"); + String createTableSql = "create table trans(c1 string,\n" + + " c3 int,\n" + + " c4 bigint,\n" + + " c5 float,\n" + + " c6 double,\n" + + " c7 timestamp,\n" + + " c8 date,\n" + + " index(key=c1, ts=c7));"; + state.execute(createTableSql); + String selectSql = "SELECT c1, c3, sum(c4) OVER w1 as w1_c4_sum FROM trans WINDOW w1 AS " + + "(PARTITION BY trans.c1 ORDER BY trans.c7 ROWS BETWEEN 2 PRECEDING AND CURRENT ROW);"; + String deploySql = "DEPLOY " + deploymentName + " " + selectSql; + state.execute(deploySql); + } catch (SQLException e) { + e.printStackTrace(); + Assert.fail(); + } + String insertSql = "insert into trans values(\"aa\",23,33,1.4,2.4,1590738993000,\"2020-05-04\");"; + PreparedStatement pstmt = null; + try { + pstmt = executor.getInsertPreparedStmt(dbname, insertSql); + Assert.assertTrue(pstmt.execute()); + } catch (Exception e) { + e.printStackTrace(); + Assert.fail(); + } finally { + if (pstmt != null) { + try { + pstmt.close(); + } catch (Exception throwables) { + throwables.printStackTrace(); + } + } + } + + ResultSet resultSet = null; + try { + Thread.sleep(1000); + pstmt = executor.getCallablePreparedStmtBatch(dbname, deploymentName); + + int batchSize = 5; + for (int idx = 0; idx < batchSize; idx++) { + pstmt.setString(1, "bb"); + pstmt.setInt(2, 24); + pstmt.setLong(3, 34l); + pstmt.setFloat(4, 1.5f); + pstmt.setDouble(5, 2.5); + pstmt.setTimestamp(6, new Timestamp(1590738994000l + idx)); + pstmt.setDate(7, Date.valueOf("2020-05-05")); + pstmt.addBatch(); + } + + resultSet = pstmt.executeQuery(); + + Assert.assertEquals(resultSet.getMetaData().getColumnCount(), 3); + int resultNum = 0; + while (resultSet.next()) { + Assert.assertEquals(resultSet.getString(1), "bb"); + Assert.assertEquals(resultSet.getInt(2), 24); + Assert.assertEquals(resultSet.getLong(3), 34); + resultNum++; + } + Assert.assertEquals(resultNum, batchSize); + + state.execute("drop deployment " + deploymentName + ";"); + String drop = "drop table trans;"; + boolean ok = executor.executeDDL(dbname, drop); + Assert.assertTrue(ok); + ok = executor.dropDB(dbname); + Assert.assertTrue(ok); + } catch (Exception e) { + e.printStackTrace(); + Assert.fail(); + } finally { + try { + state.close(); + if (resultSet != null) { + resultSet.close(); + } + if (pstmt != null) { + pstmt.close(); + } + } catch (Exception throwables) { + throwables.printStackTrace(); + } + } + } } diff --git a/java/openmldb-jdbc/src/test/java/com/_4paradigm/openmldb/jdbc/SQLRouterSmokeTest.java b/java/openmldb-jdbc/src/test/java/com/_4paradigm/openmldb/jdbc/SQLRouterSmokeTest.java index 61945b0740d..b8f54bfa5ca 100644 --- a/java/openmldb-jdbc/src/test/java/com/_4paradigm/openmldb/jdbc/SQLRouterSmokeTest.java +++ b/java/openmldb-jdbc/src/test/java/com/_4paradigm/openmldb/jdbc/SQLRouterSmokeTest.java @@ -128,12 +128,11 @@ public void testSmoke(SqlExecutor router) { // select String select1 = "select * from tsql1010;"; - com._4paradigm.openmldb.jdbc.SQLResultSet rs1 = (com._4paradigm.openmldb.jdbc.SQLResultSet) router - .executeSQL(dbname, select1); + SQLResultSet rs1 = (SQLResultSet) router .executeSQL(dbname, select1); - Assert.assertEquals(2, rs1.GetInternalSchema().GetColumnCnt()); - Assert.assertEquals("kTypeInt64", rs1.GetInternalSchema().GetColumnType(0).toString()); - Assert.assertEquals("kTypeString", rs1.GetInternalSchema().GetColumnType(1).toString()); + Assert.assertEquals(2, rs1.GetInternalSchema().getColumnList().size()); + Assert.assertEquals(Types.BIGINT, rs1.GetInternalSchema().getColumnType(0)); + Assert.assertEquals(Types.VARCHAR, rs1.GetInternalSchema().getColumnType(1)); List col1Insert = new ArrayList<>(); List col2Insert = new ArrayList<>(); @@ -150,10 +149,9 @@ public void testSmoke(SqlExecutor router) { rs1.close(); String select2 = "select col1 from tsql1010;"; - com._4paradigm.openmldb.jdbc.SQLResultSet rs2 = (com._4paradigm.openmldb.jdbc.SQLResultSet) router - .executeSQL(dbname, select2); - Assert.assertEquals(1, rs2.GetInternalSchema().GetColumnCnt()); - Assert.assertEquals("kTypeInt64", rs2.GetInternalSchema().GetColumnType(0).toString()); + SQLResultSet rs2 = (SQLResultSet) router .executeSQL(dbname, select2); + Assert.assertEquals(1, rs2.GetInternalSchema().size()); + Assert.assertEquals(Types.BIGINT, rs2.GetInternalSchema().getColumnType(0)); List col1InsertRes = new ArrayList<>(); while (rs2.next()) { @@ -165,10 +163,9 @@ public void testSmoke(SqlExecutor router) { rs2.close(); String select3 = "select col2 from tsql1010;"; - com._4paradigm.openmldb.jdbc.SQLResultSet rs3 = (com._4paradigm.openmldb.jdbc.SQLResultSet) router - .executeSQL(dbname, select3); - Assert.assertEquals(1, rs3.GetInternalSchema().GetColumnCnt()); - Assert.assertEquals("kTypeString", rs3.GetInternalSchema().GetColumnType(0).toString()); + SQLResultSet rs3 = (SQLResultSet) router .executeSQL(dbname, select3); + Assert.assertEquals(1, rs3.GetInternalSchema().size()); + Assert.assertEquals(Types.VARCHAR, rs3.GetInternalSchema().getColumnType(0)); List col2InsertRes = new ArrayList<>(); while (rs3.next()) { @@ -185,11 +182,10 @@ public void testSmoke(SqlExecutor router) { { query_statement.setString(1, "hi"); query_statement.setLong(2, 1003); - com._4paradigm.openmldb.jdbc.SQLResultSet rs4 = (com._4paradigm.openmldb.jdbc.SQLResultSet) query_statement - .executeQuery(); - Assert.assertEquals(2, rs4.GetInternalSchema().GetColumnCnt()); - Assert.assertEquals("kTypeInt64", rs4.GetInternalSchema().GetColumnType(0).toString()); - Assert.assertEquals("kTypeString", rs4.GetInternalSchema().GetColumnType(1).toString()); + SQLResultSet rs4 = (SQLResultSet) query_statement .executeQuery(); + Assert.assertEquals(2, rs4.GetInternalSchema().size()); + Assert.assertEquals(Types.BIGINT, rs4.GetInternalSchema().getColumnType(0)); + Assert.assertEquals(Types.VARCHAR, rs4.GetInternalSchema().getColumnType(1)); Assert.assertTrue(rs4.next()); Assert.assertEquals(1002, rs4.getLong(1)); Assert.assertEquals("hi", rs4.getString(2)); @@ -200,11 +196,10 @@ public void testSmoke(SqlExecutor router) { { query_statement.setString(1, "hi"); query_statement.setLong(2, 1002); - com._4paradigm.openmldb.jdbc.SQLResultSet rs4 = (com._4paradigm.openmldb.jdbc.SQLResultSet) query_statement - .executeQuery(); - Assert.assertEquals(2, rs4.GetInternalSchema().GetColumnCnt()); - Assert.assertEquals("kTypeInt64", rs4.GetInternalSchema().GetColumnType(0).toString()); - Assert.assertEquals("kTypeString", rs4.GetInternalSchema().GetColumnType(1).toString()); + SQLResultSet rs4 = (SQLResultSet) query_statement .executeQuery(); + Assert.assertEquals(2, rs4.GetInternalSchema().size()); + Assert.assertEquals(Types.BIGINT, rs4.GetInternalSchema().getColumnType(0)); + Assert.assertEquals(Types.VARCHAR, rs4.GetInternalSchema().getColumnType(1)); Assert.assertFalse(rs4.next()); rs4.close(); } @@ -212,11 +207,10 @@ public void testSmoke(SqlExecutor router) { { query_statement.setString(1, "world"); query_statement.setLong(2, 1003); - com._4paradigm.openmldb.jdbc.SQLResultSet rs4 = (com._4paradigm.openmldb.jdbc.SQLResultSet) query_statement - .executeQuery(); - Assert.assertEquals(2, rs4.GetInternalSchema().GetColumnCnt()); - Assert.assertEquals("kTypeInt64", rs4.GetInternalSchema().GetColumnType(0).toString()); - Assert.assertEquals("kTypeString", rs4.GetInternalSchema().GetColumnType(1).toString()); + SQLResultSet rs4 = (SQLResultSet) query_statement .executeQuery(); + Assert.assertEquals(2, rs4.GetInternalSchema().size()); + Assert.assertEquals(Types.BIGINT, rs4.GetInternalSchema().getColumnType(0)); + Assert.assertEquals(Types.VARCHAR, rs4.GetInternalSchema().getColumnType(1)); Assert.assertTrue(rs4.next()); Assert.assertEquals(1001, rs4.getLong(1)); Assert.assertEquals("world", rs4.getString(2)); @@ -227,11 +221,10 @@ public void testSmoke(SqlExecutor router) { { query_statement.setString(1, "hello"); query_statement.setLong(2, 1003); - com._4paradigm.openmldb.jdbc.SQLResultSet rs4 = (com._4paradigm.openmldb.jdbc.SQLResultSet) query_statement - .executeQuery(); - Assert.assertEquals(2, rs4.GetInternalSchema().GetColumnCnt()); - Assert.assertEquals("kTypeInt64", rs4.GetInternalSchema().GetColumnType(0).toString()); - Assert.assertEquals("kTypeString", rs4.GetInternalSchema().GetColumnType(1).toString()); + SQLResultSet rs4 = (SQLResultSet) query_statement .executeQuery(); + Assert.assertEquals(2, rs4.GetInternalSchema().size()); + Assert.assertEquals(Types.BIGINT, rs4.GetInternalSchema().getColumnType(0)); + Assert.assertEquals(Types.VARCHAR, rs4.GetInternalSchema().getColumnType(1)); Assert.assertTrue(rs4.next()); Assert.assertEquals(1000, rs4.getLong(1)); Assert.assertEquals("hello", rs4.getString(2)); @@ -242,11 +235,10 @@ public void testSmoke(SqlExecutor router) { { query_statement.setString(1, "word"); query_statement.setLong(2, 1003); - com._4paradigm.openmldb.jdbc.SQLResultSet rs4 = (com._4paradigm.openmldb.jdbc.SQLResultSet) query_statement - .executeQuery(); - Assert.assertEquals(2, rs4.GetInternalSchema().GetColumnCnt()); - Assert.assertEquals("kTypeInt64", rs4.GetInternalSchema().GetColumnType(0).toString()); - Assert.assertEquals("kTypeString", rs4.GetInternalSchema().GetColumnType(1).toString()); + SQLResultSet rs4 = (SQLResultSet) query_statement .executeQuery(); + Assert.assertEquals(2, rs4.GetInternalSchema().size()); + Assert.assertEquals(Types.BIGINT, rs4.GetInternalSchema().getColumnType(0)); + Assert.assertEquals(Types.VARCHAR, rs4.GetInternalSchema().getColumnType(1)); Assert.assertFalse(rs4.next()); rs4.close(); } @@ -284,8 +276,7 @@ public void testParameterizedQueryFail(SqlExecutor router) { // missing 2nd parameter { query_statement.setString(1, "hi"); - com._4paradigm.openmldb.jdbc.SQLResultSet rs4 = (com._4paradigm.openmldb.jdbc.SQLResultSet) query_statement - .executeQuery(); + SQLResultSet rs4 = (SQLResultSet) query_statement .executeQuery(); Assert.fail("executeQuery is expected to throw exception"); rs4.close(); } @@ -426,14 +417,13 @@ public void testInsertPreparedState(SqlExecutor router) { Assert.assertTrue(ok); // select String select1 = "select * from tsql1010;"; - com._4paradigm.openmldb.jdbc.SQLResultSet rs1 = (com._4paradigm.openmldb.jdbc.SQLResultSet) router - .executeSQL(dbname, select1); - Assert.assertEquals(5, rs1.GetInternalSchema().GetColumnCnt()); - Assert.assertEquals("kTypeInt64", rs1.GetInternalSchema().GetColumnType(0).toString()); - Assert.assertEquals("kTypeDate", rs1.GetInternalSchema().GetColumnType(1).toString()); - Assert.assertEquals("kTypeString", rs1.GetInternalSchema().GetColumnType(2).toString()); - Assert.assertEquals("kTypeString", rs1.GetInternalSchema().GetColumnType(3).toString()); - Assert.assertEquals("kTypeInt32", rs1.GetInternalSchema().GetColumnType(4).toString()); + SQLResultSet rs1 = (SQLResultSet) router .executeSQL(dbname, select1); + Assert.assertEquals(5, rs1.GetInternalSchema().size()); + Assert.assertEquals(Types.BIGINT, rs1.GetInternalSchema().getColumnType(0)); + Assert.assertEquals(Types.DATE, rs1.GetInternalSchema().getColumnType(1)); + Assert.assertEquals(Types.VARCHAR, rs1.GetInternalSchema().getColumnType(2)); + Assert.assertEquals(Types.VARCHAR, rs1.GetInternalSchema().getColumnType(3)); + Assert.assertEquals(Types.INTEGER, rs1.GetInternalSchema().getColumnType(4)); while (rs1.next()) { int idx = rs1.getInt(5); int suffix = idx - 1; @@ -451,10 +441,9 @@ public void testInsertPreparedState(SqlExecutor router) { rs1.close(); String select2 = "select col1 from tsql1010;"; - com._4paradigm.openmldb.jdbc.SQLResultSet rs2 = (com._4paradigm.openmldb.jdbc.SQLResultSet) router - .executeSQL(dbname, select2); - Assert.assertEquals(1, rs2.GetInternalSchema().GetColumnCnt()); - Assert.assertEquals("kTypeInt64", rs2.GetInternalSchema().GetColumnType(0).toString()); + SQLResultSet rs2 = (SQLResultSet) router .executeSQL(dbname, select2); + Assert.assertEquals(1, rs2.GetInternalSchema().size()); + Assert.assertEquals(Types.BIGINT, rs2.GetInternalSchema().getColumnType(0)); rs2.close(); // drop table String drop = "drop table tsql1010;"; @@ -536,9 +525,8 @@ public void testInsertPreparedStateBatch(SqlExecutor router) { impl.executeBatch(); Assert.assertTrue(ok); String select1 = "select * from tsql1010;"; - com._4paradigm.openmldb.jdbc.SQLResultSet rs1 = (com._4paradigm.openmldb.jdbc.SQLResultSet) router - .executeSQL(dbname, select1); - Assert.assertEquals(6, rs1.GetInternalSchema().GetColumnCnt()); + SQLResultSet rs1 = (SQLResultSet) router .executeSQL(dbname, select1); + Assert.assertEquals(6, rs1.GetInternalSchema().size()); rs1.close(); i++; PreparedStatement impl2 = router.getInsertPreparedStmt(dbname, (String) batchData[i][0]); @@ -583,9 +571,8 @@ public void testInsertPreparedStateBatch(SqlExecutor router) { Assert.assertEquals(result, expected); String select2 = "select * from tsql1010;"; - com._4paradigm.openmldb.jdbc.SQLResultSet rs2 = (com._4paradigm.openmldb.jdbc.SQLResultSet) router - .executeSQL(dbname, select1); - Assert.assertEquals(6, rs2.GetInternalSchema().GetColumnCnt()); + SQLResultSet rs2 = (SQLResultSet) router .executeSQL(dbname, select1); + Assert.assertEquals(6, rs2.GetInternalSchema().size()); int recordCnt = 0; while (rs2.next()) { recordCnt++; diff --git a/java/openmldb-spark-connector/pom.xml b/java/openmldb-spark-connector/pom.xml index f8d2e31d9f2..3f79972c60a 100644 --- a/java/openmldb-spark-connector/pom.xml +++ b/java/openmldb-spark-connector/pom.xml @@ -57,8 +57,13 @@ spark-sql_${scala.binary.version} ${spark.version} ${spark.dependencyScope} + + + com.google.protobuf + protobuf-java + + - com.4paradigm.openmldb openmldb-jdbc diff --git a/src/catalog/base.cc b/src/catalog/base.cc index 7aec1102cb8..2320025fe40 100644 --- a/src/catalog/base.cc +++ b/src/catalog/base.cc @@ -45,6 +45,9 @@ ProcedureInfoImpl::ProcedureInfoImpl(const ::openmldb::api::ProcedureInfo& proce for (const auto& op : procedure.options()) { options_[op.name()] = op.value().value(); } + if (procedure.router_col_size() > 0) { + router_col_ = procedure.router_col(0); + } } } // namespace catalog diff --git a/src/catalog/base.h b/src/catalog/base.h index 87fd8da65e5..631e3fd5bf8 100644 --- a/src/catalog/base.h +++ b/src/catalog/base.h @@ -63,6 +63,10 @@ class ProcedureInfoImpl : public hybridse::sdk::ProcedureInfo { return &options_; } + int GetRouterCol() const override { + return router_col_; + } + private: std::string db_name_; std::string sp_name_; @@ -75,6 +79,7 @@ class ProcedureInfoImpl : public hybridse::sdk::ProcedureInfo { std::string main_db_; ::hybridse::sdk::ProcedureType type_; std::unordered_map options_; + int router_col_ = -1; }; } // namespace catalog diff --git a/src/catalog/tablet_catalog.cc b/src/catalog/tablet_catalog.cc index 7f470703b6c..a9e74ff7061 100644 --- a/src/catalog/tablet_catalog.cc +++ b/src/catalog/tablet_catalog.cc @@ -359,15 +359,24 @@ bool TabletCatalog::AddTable(const ::openmldb::api::TableMeta& meta, } const std::string& table_name = meta.name(); auto it = db_it->second.find(table_name); - if (it == db_it->second.end()) { + if (it != db_it->second.end()) { + if (it->second->GetTid() < static_cast(meta.tid())) { + db_it->second.erase(it); + } else if (it->second->GetTid() > static_cast(meta.tid())) { + LOG(WARNING) << "current tid " << it->second->GetTid() << " is greater than new table info tid " + << meta.tid(); + return false; + } else { + handler = it->second; + } + } + if (!handler) { handler = std::make_shared(meta, local_tablet_); if (!handler->Init(client_manager_)) { LOG(WARNING) << "tablet handler init failed"; return false; } db_it->second.emplace(table_name, handler); - } else { - handler = it->second; } handler->AddTable(table); return true; @@ -383,7 +392,7 @@ bool TabletCatalog::AddDB(const ::hybridse::type::Database& db) { return true; } -bool TabletCatalog::DeleteTable(const std::string& db, const std::string& table_name, uint32_t pid) { +bool TabletCatalog::DeleteTable(const std::string& db, const std::string& table_name, uint32_t tid, uint32_t pid) { std::lock_guard<::openmldb::base::SpinMutex> spin_lock(mu_); auto db_it = tables_.find(db); if (db_it == tables_.end()) { @@ -393,7 +402,11 @@ bool TabletCatalog::DeleteTable(const std::string& db, const std::string& table_ if (it == db_it->second.end()) { return false; } - LOG(INFO) << "delete table from catalog. db " << db << ", name " << table_name << ", pid " << pid; + if (it->second->GetTid() > tid) { + LOG(WARNING) << "delete failed. current tid " << it->second->GetTid() << " is greater than " << tid; + return false; + } + LOG(INFO) << "delete table from catalog. db " << db << " name " << table_name << " tid " << tid << " pid " << pid; if (it->second->DeleteTable(pid) < 1) { db_it->second.erase(it); } @@ -450,6 +463,9 @@ bool TabletCatalog::UpdateTableMeta(const ::openmldb::api::TableMeta& meta) { if (it == db_it->second.end()) { LOG(WARNING) << "table " << table_name << " does not exist in db " << db_name; return false; + } else if (it->second->GetTid() != static_cast(meta.tid())) { + LOG(WARNING) << "tid is not match. tid " << it->second->GetTid() << " new tid " << meta.tid(); + return false; } else { handler = it->second; } @@ -469,16 +485,25 @@ bool TabletCatalog::UpdateTableInfo(const ::openmldb::nameserver::TableInfo& tab db_it = result.first; } auto it = db_it->second.find(table_name); - if (it == db_it->second.end()) { + if (it != db_it->second.end()) { + if (table_info.tid() > it->second->GetTid()) { + db_it->second.erase(it); + } else if (table_info.tid() < it->second->GetTid()) { + LOG(INFO) << "current tid " << it->second->GetTid() << " is greater than new table info tid " + << table_info.tid(); + return false; + } else { + handler = it->second; + } + } + if (!handler) { handler = std::make_shared(table_info, local_tablet_); if (!handler->Init(client_manager_)) { LOG(WARNING) << "tablet handler init failed"; return false; } db_it->second.emplace(table_name, handler); - LOG(INFO) << "add table " << table_name << "to db " << db_name; - } else { - handler = it->second; + LOG(INFO) << "add table " << table_name << "to db " << db_name << " tid " << table_info.tid(); } if (bool updated = false; !handler->Update(table_info, client_manager_, &updated)) { return false; diff --git a/src/catalog/tablet_catalog.h b/src/catalog/tablet_catalog.h index 6b0365f6f51..7d834147591 100644 --- a/src/catalog/tablet_catalog.h +++ b/src/catalog/tablet_catalog.h @@ -174,7 +174,7 @@ class TabletTableHandler : public ::hybridse::vm::TableHandler, std::shared_ptr<::hybridse::vm::Tablet> GetTablet(const std::string &index_name, const std::vector &pks) override; - inline int32_t GetTid() { return table_st_.GetTid(); } + inline uint32_t GetTid() { return table_st_.GetTid(); } void AddTable(std::shared_ptr<::openmldb::storage::Table> table); @@ -233,7 +233,7 @@ class TabletCatalog : public ::hybridse::vm::Catalog { bool IndexSupport() override; - bool DeleteTable(const std::string &db, const std::string &table_name, uint32_t pid); + bool DeleteTable(const std::string &db, const std::string &table_name, uint32_t tid, uint32_t pid); bool DeleteDB(const std::string &db); diff --git a/src/catalog/tablet_catalog_test.cc b/src/catalog/tablet_catalog_test.cc index 1f134028c60..81b5c8c6ebc 100644 --- a/src/catalog/tablet_catalog_test.cc +++ b/src/catalog/tablet_catalog_test.cc @@ -314,6 +314,30 @@ TEST_F(TabletCatalogTest, segment_handler_test) { } } +TEST_F(TabletCatalogTest, add_drop_test) { + TestArgs args = PrepareTable("t1"); + std::shared_ptr catalog(new TabletCatalog()); + ASSERT_TRUE(catalog->Init()); + ::openmldb::api::TableMeta meta = args.meta[0]; + meta.set_tid(2); + ASSERT_TRUE(catalog->AddTable(meta, args.tables[0])); + uint32_t tid = meta.tid(); + uint32_t pid = meta.pid(); + std::string db = meta.db(); + std::string name = meta.name(); + ASSERT_FALSE(catalog->DeleteTable(db, name, tid - 1, pid)); + ::openmldb::api::TableMeta new_meta = meta; + new_meta.set_tid(1); + ASSERT_FALSE(catalog->UpdateTableMeta(new_meta)); + ::openmldb::nameserver::TableInfo table_info; + table_info.set_db(db); + table_info.set_tid(1); + table_info.set_name(name); + bool index_updated = false; + ASSERT_FALSE(catalog->UpdateTableInfo(table_info, &index_updated)); + ASSERT_TRUE(catalog->DeleteTable(db, name, tid, pid)); +} + TEST_F(TabletCatalogTest, segment_handler_pk_not_exist_test) { TestArgs args = PrepareTable("t1"); auto handler = std::shared_ptr( diff --git a/src/client/tablet_client.cc b/src/client/tablet_client.cc index 3ed9eb596b5..9357b23e29a 100644 --- a/src/client/tablet_client.cc +++ b/src/client/tablet_client.cc @@ -1111,7 +1111,7 @@ bool TabletClient::Scan(const ::openmldb::api::ScanRequest& request, brpc::Contr return true; } -bool TabletClient::CallProcedure(const std::string& db, const std::string& sp_name, const std::string& row, +bool TabletClient::CallProcedure(const std::string& db, const std::string& sp_name, const base::Slice& row, brpc::Controller* cntl, openmldb::api::QueryResponse* response, bool is_debug, uint64_t timeout_ms) { if (cntl == NULL || response == NULL) return false; @@ -1182,6 +1182,87 @@ bool TabletClient::CallSQLBatchRequestProcedure(const std::string& db, const std return true; } +bool static ParseBatchRequestMeta(const base::Slice& meta, const base::Slice& data, + ::openmldb::api::SQLBatchRequestQueryRequest* request) { + uint64_t total_len = 0; + const int32_t* buf = reinterpret_cast(meta.data()); + int32_t cnt = meta.size() / sizeof(int32_t); + for (int32_t idx = 0; idx < cnt; idx++) { + // the first field is for common_slice + if (idx == 0) { + if (buf[idx] == 0) { + request->set_common_slices(0); + } else { + request->set_common_slices(1); + request->add_row_sizes(buf[idx]); + } + } else { + request->add_row_sizes(buf[idx]); + } + total_len += buf[idx]; + } + if (total_len != data.size()) { + return false; + } + return true; +} + +base::Status TabletClient::CallSQLBatchRequestProcedure(const std::string& db, const std::string& sp_name, + const base::Slice& meta, const base::Slice& data, + bool is_debug, uint64_t timeout_ms, + brpc::Controller* cntl, openmldb::api::SQLBatchRequestQueryResponse* response) { + ::openmldb::api::SQLBatchRequestQueryRequest request; + request.set_sp_name(sp_name); + request.set_is_procedure(true); + request.set_db(db); + request.set_is_debug(is_debug); + request.set_common_slices(0); + request.set_non_common_slices(1); + cntl->set_timeout_ms(timeout_ms); + if (!ParseBatchRequestMeta(meta, data, &request)) { + return {base::ReturnCode::kError, "parse meta data failed"}; + } + auto& io_buf = cntl->request_attachment(); + if (io_buf.append(data.data(), data.size()) != 0) { + return {base::ReturnCode::kError, "append to iobuf error"}; + } + bool ok = client_.SendRequest(&::openmldb::api::TabletServer_Stub::SQLBatchRequestQuery, cntl, &request, response); + if (!ok || response->code() != ::openmldb::base::kOk) { + LOG(WARNING) << "fail to query tablet"; + return {base::ReturnCode::kError, "fail to query tablet. " + response->msg()}; + } + return {}; +} + +base::Status TabletClient::CallSQLBatchRequestProcedure(const std::string& db, const std::string& sp_name, + const base::Slice& meta, const base::Slice& data, + bool is_debug, uint64_t timeout_ms, + openmldb::RpcCallback* callback) { + if (callback == nullptr) { + return {base::ReturnCode::kError, "callback is null"}; + } + ::openmldb::api::SQLBatchRequestQueryRequest request; + request.set_sp_name(sp_name); + request.set_is_procedure(true); + request.set_db(db); + request.set_is_debug(is_debug); + request.set_common_slices(0); + request.set_non_common_slices(1); + if (!ParseBatchRequestMeta(meta, data, &request)) { + return {base::ReturnCode::kError, "parse meta data failed"}; + } + auto& io_buf = callback->GetController()->request_attachment(); + if (io_buf.append(data.data(), data.size()) != 0) { + return {base::ReturnCode::kError, "append to iobuf error"}; + } + callback->GetController()->set_timeout_ms(timeout_ms); + if (!client_.SendRequest(&::openmldb::api::TabletServer_Stub::SQLBatchRequestQuery, + callback->GetController().get(), &request, callback->GetResponse().get(), callback)) { + return {base::ReturnCode::kError, "stub is null"}; + } + return {}; +} + bool TabletClient::DropProcedure(const std::string& db_name, const std::string& sp_name) { ::openmldb::api::DropProcedureRequest request; ::openmldb::api::GeneralResponse response; @@ -1195,7 +1276,7 @@ bool TabletClient::DropProcedure(const std::string& db_name, const std::string& return true; } -bool TabletClient::CallProcedure(const std::string& db, const std::string& sp_name, const std::string& row, +bool TabletClient::CallProcedure(const std::string& db, const std::string& sp_name, const base::Slice& row, uint64_t timeout_ms, bool is_debug, openmldb::RpcCallback* callback) { if (callback == nullptr) { diff --git a/src/client/tablet_client.h b/src/client/tablet_client.h index 23a1c5df879..f955040a157 100644 --- a/src/client/tablet_client.h +++ b/src/client/tablet_client.h @@ -220,7 +220,7 @@ class TabletClient : public Client { base::Status CreateProcedure(const openmldb::api::CreateProcedureRequest& sp_request); - bool CallProcedure(const std::string& db, const std::string& sp_name, const std::string& row, + bool CallProcedure(const std::string& db, const std::string& sp_name, const base::Slice& row, brpc::Controller* cntl, openmldb::api::QueryResponse* response, bool is_debug, uint64_t timeout_ms); @@ -229,6 +229,11 @@ class TabletClient : public Client { openmldb::api::SQLBatchRequestQueryResponse* response, bool is_debug, uint64_t timeout_ms); + base::Status CallSQLBatchRequestProcedure(const std::string& db, const std::string& sp_name, + const base::Slice& meta, const base::Slice& data, + bool is_debug, uint64_t timeout_ms, + brpc::Controller* cntl, openmldb::api::SQLBatchRequestQueryResponse* response); + bool DropProcedure(const std::string& db_name, const std::string& sp_name); bool Refresh(uint32_t tid); @@ -243,7 +248,7 @@ class TabletClient : public Client { bool DropFunction(const ::openmldb::common::ExternalFun& fun, std::string* msg); - bool CallProcedure(const std::string& db, const std::string& sp_name, const std::string& row, uint64_t timeout_ms, + bool CallProcedure(const std::string& db, const std::string& sp_name, const base::Slice& row, uint64_t timeout_ms, bool is_debug, openmldb::RpcCallback* callback); bool CallSQLBatchRequestProcedure(const std::string& db, const std::string& sp_name, @@ -251,6 +256,11 @@ class TabletClient : public Client { uint64_t timeout_ms, openmldb::RpcCallback* callback); + base::Status CallSQLBatchRequestProcedure(const std::string& db, const std::string& sp_name, + const base::Slice& meta, const base::Slice& data, + bool is_debug, uint64_t timeout_ms, + openmldb::RpcCallback* callback); + bool CreateAggregator(const ::openmldb::api::TableMeta& base_table_meta, uint32_t aggr_tid, uint32_t aggr_pid, uint32_t index_pos, const ::openmldb::base::LongWindowInfo& window_info); diff --git a/src/proto/sql_procedure.proto b/src/proto/sql_procedure.proto index be76ff95fff..29cdf8b6a24 100755 --- a/src/proto/sql_procedure.proto +++ b/src/proto/sql_procedure.proto @@ -36,6 +36,7 @@ message ProcedureInfo { repeated openmldb.common.DbTableNamePair tables = 8; // dependent tables optional openmldb.type.ProcedureType type = 9 [default = kReqProcedure]; repeated google.protobuf.Option options = 10; + repeated int32 router_col = 11; } message CreateProcedureRequest { diff --git a/src/sdk/batch_request_result_set_sql.h b/src/sdk/batch_request_result_set_sql.h index e6a038fff62..dabebdae3f7 100644 --- a/src/sdk/batch_request_result_set_sql.h +++ b/src/sdk/batch_request_result_set_sql.h @@ -72,6 +72,11 @@ class SQLBatchRequestResultSet : public ::hybridse::sdk::ResultSet { inline int32_t Size() { return response_->count(); } + int32_t GetDataLength() override { return cntl_->response_attachment().size(); } + void CopyTo(hybridse::sdk::ByteArrayPtr buf) override { + cntl_->response_attachment().copy_to(reinterpret_cast(buf)); + } + private: inline uint32_t GetRecordSize() { return response_->count(); } diff --git a/src/sdk/result_set_base.h b/src/sdk/result_set_base.h index 69991901bb0..9a29d5ba9c5 100644 --- a/src/sdk/result_set_base.h +++ b/src/sdk/result_set_base.h @@ -64,6 +64,10 @@ class ResultSetBase { inline int32_t Size() { return count_; } + int32_t GetDataLength() { return io_buf_->size(); } + + void CopyTo(void* buf) { io_buf_->copy_to(buf); } + private: const butil::IOBuf* io_buf_; uint32_t count_; diff --git a/src/sdk/result_set_sql.h b/src/sdk/result_set_sql.h index ee38e41f2e0..92ed418dc6f 100644 --- a/src/sdk/result_set_sql.h +++ b/src/sdk/result_set_sql.h @@ -96,6 +96,12 @@ class ResultSetSQL : public ::hybridse::sdk::ResultSet { int32_t Size() override { return result_set_base_->Size(); } + void CopyTo(hybridse::sdk::ByteArrayPtr buf) override { + return result_set_base_->CopyTo(reinterpret_cast(buf)); + } + + int32_t GetDataLength() override { return result_set_base_->GetDataLength(); } + private: ::hybridse::vm::Schema schema_; uint32_t record_cnt_; @@ -211,6 +217,9 @@ class MultipleResultSetSQL : public ::hybridse::sdk::ResultSet { return total_size; } + int32_t GetDataLength() override { return 0; } + void CopyTo(hybridse::sdk::ByteArrayPtr buf) override { } + private: std::vector> result_set_list_; uint32_t result_set_idx_; @@ -261,6 +270,9 @@ class ReadableResultSetSQL : public ::hybridse::sdk::ResultSet { const bool GetAsString(uint32_t idx, std::string& val) override; + int32_t GetDataLength() override { return rs_->GetDataLength(); } + void CopyTo(hybridse::sdk::ByteArrayPtr buf) override { rs_->CopyTo(buf); } + private: std::shared_ptr<::hybridse::sdk::ResultSet> rs_; }; diff --git a/src/sdk/sql_cluster_router.cc b/src/sdk/sql_cluster_router.cc index 85eeaa31761..296cd3d5755 100644 --- a/src/sdk/sql_cluster_router.cc +++ b/src/sdk/sql_cluster_router.cc @@ -1145,17 +1145,21 @@ std::shared_ptr SQLClusterRouter::GetTableReader() { } std::shared_ptr SQLClusterRouter::GetTablet(const std::string& db, - const std::string& sp_name, - hybridse::sdk::Status* status) { + const std::string& sp_name, const std::string& router_col, hybridse::sdk::Status* status) { RET_IF_NULL_AND_WARN(status, "output status is nullptr"); - std::shared_ptr sp_info = cluster_sdk_->GetProcedureInfo(db, sp_name, &status->msg); + auto sp_info = cluster_sdk_->GetProcedureInfo(db, sp_name, &status->msg); if (!sp_info) { CODE_PREPEND_AND_WARN(status, StatusCode::kCmdError, "procedure not found"); return nullptr; } const std::string& table = sp_info->GetMainTable(); const std::string& db_name = sp_info->GetMainDb().empty() ? db : sp_info->GetMainDb(); - auto tablet = cluster_sdk_->GetTablet(db_name, table); + std::shared_ptr<::openmldb::catalog::TabletAccessor> tablet; + if (router_col.empty()) { + tablet = cluster_sdk_->GetTablet(db_name, table); + } else { + tablet = cluster_sdk_->GetTablet(db_name, table, router_col); + } if (!tablet) { SET_STATUS_AND_WARN(status, StatusCode::kCmdError, "fail to get tablet, table " + db_name + "." + table); return nullptr; @@ -1463,29 +1467,43 @@ std::shared_ptr SQLClusterRouter::Explain(const std::string& db, co } std::shared_ptr SQLClusterRouter::CallProcedure(const std::string& db, - const std::string& sp_name, - std::shared_ptr row, - hybridse::sdk::Status* status) { - RET_IF_NULL_AND_WARN(status, "output status is nullptr"); + const std::string& sp_name, std::shared_ptr row, hybridse::sdk::Status* status) { if (!row || !row->OK()) { SET_STATUS_AND_WARN(status, StatusCode::kCmdError, "make sure the request row is built before execute sql"); return nullptr; } - auto tablet = GetTablet(db, sp_name, status); + return CallProcedure(db, sp_name, base::Slice(row->GetRow()), "", status); +} + +std::shared_ptr SQLClusterRouter::CallProcedure(const std::string& db, + const std::string& sp_name, hybridse::sdk::ByteArrayPtr buf, int len, + const std::string& router_col, hybridse::sdk::Status* status) { + if (buf == nullptr || len == 0) { + SET_STATUS_AND_WARN(status, StatusCode::kCmdError, "invalid request row data"); + return nullptr; + } + return CallProcedure(db, sp_name, base::Slice(buf, len), router_col, status); +} + +std::shared_ptr SQLClusterRouter::CallProcedure(const std::string& db, + const std::string& sp_name, const base::Slice& row, + const std::string& router_col, hybridse::sdk::Status* status) { + RET_IF_NULL_AND_WARN(status, "output status is nullptr"); + auto tablet = GetTablet(db, sp_name, router_col, status); if (!tablet) { + SET_STATUS_AND_WARN(status, StatusCode::kCmdError, "cannot get tablet"); return nullptr; } auto cntl = std::make_shared<::brpc::Controller>(); auto response = std::make_shared<::openmldb::api::QueryResponse>(); - bool ok = tablet->CallProcedure(db, sp_name, row->GetRow(), cntl.get(), response.get(), options_->enable_debug, - options_->request_timeout); + bool ok = tablet->CallProcedure(db, sp_name, row, cntl.get(), response.get(), + options_->enable_debug, options_->request_timeout); if (!ok || response->code() != ::openmldb::base::kOk) { RPC_STATUS_AND_WARN(status, cntl, response, "CallProcedure failed"); return nullptr; } - auto rs = ResultSetSQL::MakeResultSet(response, cntl, status); - return rs; + return ResultSetSQL::MakeResultSet(response, cntl, status); } std::shared_ptr SQLClusterRouter::CallSQLBatchRequestProcedure( @@ -1496,7 +1514,7 @@ std::shared_ptr SQLClusterRouter::CallSQLBatchRequestP SET_STATUS_AND_WARN(status, StatusCode::kNullInputPointer, "row_batch is nullptr"); return nullptr; } - auto tablet = GetTablet(db, sp_name, status); + auto tablet = GetTablet(db, sp_name, "", status); if (!tablet) { return nullptr; } @@ -1517,6 +1535,38 @@ std::shared_ptr SQLClusterRouter::CallSQLBatchRequestP return rs; } +std::shared_ptr SQLClusterRouter::CallSQLBatchRequestProcedure( + const std::string& db, const std::string& sp_name, + hybridse::sdk::ByteArrayPtr meta, int meta_len, + hybridse::sdk::ByteArrayPtr buf, int len, + hybridse::sdk::Status* status) { + RET_IF_NULL_AND_WARN(status, "output status is nullptr"); + if (meta == nullptr || meta_len == 0 || buf == nullptr || len == 0) { + SET_STATUS_AND_WARN(status, StatusCode::kNullInputPointer, "input data is null"); + return nullptr; + } + auto tablet = GetTablet(db, sp_name, "", status); + if (!tablet) { + return nullptr; + } + + auto cntl = std::make_shared<::brpc::Controller>(); + auto response = std::make_shared<::openmldb::api::SQLBatchRequestQueryResponse>(); + auto ret = tablet->CallSQLBatchRequestProcedure(db, sp_name, base::Slice(meta, meta_len), + base::Slice(buf, len), + options_->enable_debug, options_->request_timeout, cntl.get(), response.get()); + if (!ret.OK()) { + RPC_STATUS_AND_WARN(status, cntl, response, "CallSQLBatchRequestProcedure failed" + ret.GetMsg()); + return nullptr; + } + auto rs = std::make_shared<::openmldb::sdk::SQLBatchRequestResultSet>(response, cntl); + if (!rs->Init()) { + SET_STATUS_AND_WARN(status, StatusCode::kCmdError, "SQLBatchRequestResultSet init failed"); + return nullptr; + } + return rs; +} + std::shared_ptr SQLClusterRouter::ShowProcedure(const std::string& db, const std::string& sp_name, hybridse::sdk::Status* status) { @@ -2159,22 +2209,37 @@ std::shared_ptr SQLClusterRouter::CallProcedure(cons int64_t timeout_ms, std::shared_ptr row, hybridse::sdk::Status* status) { - RET_IF_NULL_AND_WARN(status, "output status is nullptr"); if (!row || !row->OK()) { SET_STATUS_AND_WARN(status, StatusCode::kCmdError, "make sure the request row is built before execute sql"); return {}; } - auto tablet = GetTablet(db, sp_name, status); + return CallProcedure(db, sp_name, timeout_ms, base::Slice(row->GetRow()), "", status); +} + +std::shared_ptr SQLClusterRouter::CallProcedure(const std::string& db, + const std::string& sp_name, int64_t timeout_ms, hybridse::sdk::ByteArrayPtr buf, int len, + const std::string& router_col, hybridse::sdk::Status* status) { + if (buf == nullptr || len == 0) { + SET_STATUS_AND_WARN(status, StatusCode::kCmdError, "invalid request row data"); + return nullptr; + } + return CallProcedure(db, sp_name, timeout_ms, base::Slice(buf, len), router_col, status); +} + +std::shared_ptr SQLClusterRouter::CallProcedure(const std::string& db, + const std::string& sp_name, int64_t timeout_ms, const base::Slice& row, + const std::string& router_col, hybridse::sdk::Status* status) { + RET_IF_NULL_AND_WARN(status, "output status is nullptr"); + auto tablet = GetTablet(db, sp_name, router_col, status); if (!tablet) { return {}; } - std::shared_ptr response = std::make_shared(); std::shared_ptr cntl = std::make_shared(); auto* callback = new openmldb::RpcCallback(response, cntl); std::shared_ptr future = std::make_shared(callback); - bool ok = tablet->CallProcedure(db, sp_name, row->GetRow(), timeout_ms, options_->enable_debug, callback); + bool ok = tablet->CallProcedure(db, sp_name, row, timeout_ms, options_->enable_debug, callback); if (!ok) { // async rpc SET_STATUS_AND_WARN(status, StatusCode::kConnError, "CallProcedure failed(stub is null)"); @@ -2191,7 +2256,7 @@ std::shared_ptr SQLClusterRouter::CallSQLBatchReques // todo return nullptr; } - auto tablet = GetTablet(db, sp_name, status); + auto tablet = GetTablet(db, sp_name, "", status); if (!tablet) { return nullptr; } @@ -2213,6 +2278,32 @@ std::shared_ptr SQLClusterRouter::CallSQLBatchReques return future; } +std::shared_ptr SQLClusterRouter::CallSQLBatchRequestProcedure( + const std::string& db, const std::string& sp_name, int64_t timeout_ms, + hybridse::sdk::ByteArrayPtr meta, int meta_len, + hybridse::sdk::ByteArrayPtr buf, int len, hybridse::sdk::Status* status) { + RET_IF_NULL_AND_WARN(status, "output status is nullptr"); + if (meta == nullptr || meta_len == 0 || buf == nullptr || len == 0) { + SET_STATUS_AND_WARN(status, StatusCode::kNullInputPointer, "input data is null"); + return nullptr; + } + auto tablet = GetTablet(db, sp_name, "", status); + if (!tablet) { + return nullptr; + } + std::shared_ptr cntl = std::make_shared(); + auto response = std::make_shared(); + auto callback = new openmldb::RpcCallback(response, cntl); + auto future = std::make_shared(callback); + auto ret = tablet->CallSQLBatchRequestProcedure(db, sp_name, base::Slice(meta, meta_len), + base::Slice(buf, len), options_->enable_debug, timeout_ms, callback); + if (!ret.OK()) { + SET_STATUS_AND_WARN(status, StatusCode::kConnError, "CallSQLBatchRequestProcedure failed " + ret.GetMsg()); + return nullptr; + } + return future; +} + std::shared_ptr SQLClusterRouter::GetTableSchema(const std::string& db, const std::string& table_name) { auto table_info = cluster_sdk_->GetTableInfo(db, table_name); @@ -3462,7 +3553,7 @@ hybridse::sdk::Status SQLClusterRouter::HandleDeploy(const std::string& db, db_table->set_db_name(table.first); db_table->set_table_name(table.second); } - + const auto& router_col = explain_output.router.GetRouterCol(); std::stringstream str_stream; str_stream << "CREATE PROCEDURE " << deploy_node->Name() << " ("; for (int idx = 0; idx < input_schema->size(); idx++) { @@ -3475,6 +3566,9 @@ hybridse::sdk::Status SQLClusterRouter::HandleDeploy(const std::string& db, if (idx != input_schema->size() - 1) { str_stream << ", "; } + if (col.name() == router_col) { + sp_info.add_router_col(idx); + } } str_stream << ") BEGIN " << select_sql << " END;"; diff --git a/src/sdk/sql_cluster_router.h b/src/sdk/sql_cluster_router.h index ae72a32edbb..033bda8d090 100644 --- a/src/sdk/sql_cluster_router.h +++ b/src/sdk/sql_cluster_router.h @@ -152,10 +152,19 @@ class SQLClusterRouter : public SQLRouter { std::shared_ptr row, hybridse::sdk::Status* status) override; + std::shared_ptr CallProcedure(const std::string& db, const std::string& sp_name, + hybridse::sdk::ByteArrayPtr buf, int len, const std::string& router_col, + hybridse::sdk::Status* status) override; + std::shared_ptr CallSQLBatchRequestProcedure( const std::string& db, const std::string& sp_name, std::shared_ptr row_batch, hybridse::sdk::Status* status) override; + std::shared_ptr CallSQLBatchRequestProcedure( + const std::string& db, const std::string& sp_name, hybridse::sdk::ByteArrayPtr meta, int meta_len, + hybridse::sdk::ByteArrayPtr buf, int len, + hybridse::sdk::Status* status) override; + std::shared_ptr ShowProcedure(const std::string& db, const std::string& sp_name, hybridse::sdk::Status* status) override; @@ -168,10 +177,20 @@ class SQLClusterRouter : public SQLRouter { int64_t timeout_ms, std::shared_ptr row, hybridse::sdk::Status* status) override; + std::shared_ptr CallProcedure(const std::string& db, const std::string& sp_name, + int64_t timeout_ms, hybridse::sdk::ByteArrayPtr buf, int len, + const std::string& router_col, hybridse::sdk::Status* status) override; + std::shared_ptr CallSQLBatchRequestProcedure( const std::string& db, const std::string& sp_name, int64_t timeout_ms, std::shared_ptr row_batch, hybridse::sdk::Status* status) override; + std::shared_ptr CallSQLBatchRequestProcedure( + const std::string& db, const std::string& sp_name, int64_t timeout_ms, + hybridse::sdk::ByteArrayPtr meta, int meta_len, + hybridse::sdk::ByteArrayPtr buf, int len, + hybridse::sdk::Status* status) override; + std::shared_ptr<::openmldb::client::TabletClient> GetTabletClient(const std::string& db, const std::string& sql, ::hybridse::vm::EngineMode engine_mode, const std::shared_ptr& row, @@ -300,7 +319,7 @@ class SQLClusterRouter : public SQLRouter { inline bool CheckSQLSyntax(const std::string& sql); std::shared_ptr GetTablet(const std::string& db, const std::string& sp_name, - hybridse::sdk::Status* status); + const std::string& router_col, hybridse::sdk::Status* status); bool ExtractDBTypes(const std::shared_ptr& schema, std::vector* parameter_types); @@ -386,6 +405,13 @@ class SQLClusterRouter : public SQLRouter { const nameserver::TablePartition& partition_info, uint32_t replica_num, const TableStatusMap& statuses, std::string* msg); + std::shared_ptr CallProcedure(const std::string& db, const std::string& sp_name, + const base::Slice& row, const std::string& router_col, hybridse::sdk::Status* status); + + std::shared_ptr CallProcedure(const std::string& db, const std::string& sp_name, + int64_t timeout_ms, const base::Slice& row, + const std::string& router_col, hybridse::sdk::Status* status); + private: std::shared_ptr options_; std::string db_; diff --git a/src/sdk/sql_router.h b/src/sdk/sql_router.h index 356e8b80df3..aa12b6dff56 100644 --- a/src/sdk/sql_router.h +++ b/src/sdk/sql_router.h @@ -37,6 +37,8 @@ namespace openmldb { namespace sdk { +typedef char* ByteArrayPtr; + struct BasicRouterOptions { virtual ~BasicRouterOptions() = default; bool enable_debug = false; @@ -163,10 +165,19 @@ class SQLRouter { std::shared_ptr row, hybridse::sdk::Status* status) = 0; + virtual std::shared_ptr CallProcedure(const std::string& db, const std::string& sp_name, + hybridse::sdk::ByteArrayPtr buf, int len, const std::string& router_col, + hybridse::sdk::Status* status) = 0; + virtual std::shared_ptr CallSQLBatchRequestProcedure( const std::string& db, const std::string& sp_name, std::shared_ptr row_batch, hybridse::sdk::Status* status) = 0; + virtual std::shared_ptr CallSQLBatchRequestProcedure( + const std::string& db, const std::string& sp_name, hybridse::sdk::ByteArrayPtr meta, int meta_len, + hybridse::sdk::ByteArrayPtr buf, int len, + hybridse::sdk::Status* status) = 0; + virtual std::shared_ptr ShowProcedure(const std::string& db, const std::string& sp_name, hybridse::sdk::Status* status) = 0; @@ -176,10 +187,20 @@ class SQLRouter { std::shared_ptr row, hybridse::sdk::Status* status) = 0; + virtual std::shared_ptr CallProcedure(const std::string& db, const std::string& sp_name, + int64_t timeout_ms, hybridse::sdk::ByteArrayPtr buf, int len, + const std::string& router_col, hybridse::sdk::Status* status) = 0; + virtual std::shared_ptr CallSQLBatchRequestProcedure( const std::string& db, const std::string& sp_name, int64_t timeout_ms, std::shared_ptr row_batch, hybridse::sdk::Status* status) = 0; + virtual std::shared_ptr CallSQLBatchRequestProcedure( + const std::string& db, const std::string& sp_name, int64_t timeout_ms, + hybridse::sdk::ByteArrayPtr meta, int meta_len, + hybridse::sdk::ByteArrayPtr buf, int len, + hybridse::sdk::Status* status) = 0; + virtual std::shared_ptr GetTableSchema(const std::string& db, const std::string& table_name) = 0; diff --git a/src/sdk/sql_router_sdk.i b/src/sdk/sql_router_sdk.i index bf4d7a52f98..1146aeba42e 100644 --- a/src/sdk/sql_router_sdk.i +++ b/src/sdk/sql_router_sdk.i @@ -28,6 +28,26 @@ // Enable protobuf interfaces %include "swig_library/java/protobuf.i" %protobuf(openmldb::nameserver::TableInfo, com._4paradigm.openmldb.proto.NS.TableInfo); + +// refer https://github.com/swig/swig/blob/master/Lib/java/various.i + +%typemap(jni) hybridse::sdk::ByteArrayPtr "jbyteArray" +%typemap(jtype) hybridse::sdk::ByteArrayPtr "byte[]" +%typemap(jstype) hybridse::sdk::ByteArrayPtr "byte[]" +%typemap(in) hybridse::sdk::ByteArrayPtr { + $1 = (hybridse::sdk::ByteArrayPtr) JCALL2(GetByteArrayElements, jenv, $input, 0); +} + +%typemap(argout) hybridse::sdk::ByteArrayPtr { + JCALL3(ReleaseByteArrayElements, jenv, $input, (jbyte *) $1, 0); +} + +%typemap(javain) hybridse::sdk::ByteArrayPtr "$javainput" +%typemap(javaout) hybridse::sdk::ByteArrayPtr "{ return $jnicall; }" + +/* Prevent default freearg typemap from being used */ +%typemap(freearg) hybridse::sdk::ByteArrayPtr "" + #endif %shared_ptr(hybridse::sdk::ResultSet); diff --git a/src/tablet/tablet_impl.cc b/src/tablet/tablet_impl.cc index 6fb002edbd3..f30f1f8b74b 100644 --- a/src/tablet/tablet_impl.cc +++ b/src/tablet/tablet_impl.cc @@ -2054,7 +2054,7 @@ void TabletImpl::ChangeRole(RpcController* controller, const ::openmldb::api::Ch } PDLOG(INFO, "change to follower. tid[%u] pid[%u]", tid, pid); if (!table->GetDB().empty()) { - catalog_->DeleteTable(table->GetDB(), table->GetName(), pid); + catalog_->DeleteTable(table->GetDB(), table->GetName(), tid, pid); } } response->set_code(::openmldb::base::ReturnCode::kOk); @@ -3304,7 +3304,6 @@ int32_t TabletImpl::DeleteTableInternal(uint32_t tid, uint32_t pid, std::shared_ptr replicator = GetReplicator(tid, pid); { std::lock_guard spin_lock(spin_mutex_); - engine_->ClearCacheLocked(table->GetTableMeta()->db()); tables_[tid].erase(pid); replicators_[tid].erase(pid); snapshots_[tid].erase(pid); @@ -3318,12 +3317,13 @@ int32_t TabletImpl::DeleteTableInternal(uint32_t tid, uint32_t pid, snapshots_.erase(tid); } } + engine_->ClearCacheLocked(""); if (replicator) { replicator->DelAllReplicateNode(); PDLOG(INFO, "drop replicator for tid %u, pid %u", tid, pid); } if (!table->GetDB().empty()) { - catalog_->DeleteTable(table->GetDB(), table->GetName(), pid); + catalog_->DeleteTable(table->GetDB(), table->GetName(), tid, pid); } // delete related aggregator uint32_t base_tid = table->GetTableMeta()->base_table_tid(); @@ -3927,7 +3927,7 @@ int TabletImpl::CreateTableInternal(const ::openmldb::api::TableMeta* table_meta } else { LOG(WARNING) << "fail to add table " << table_meta->name() << " to catalog with db " << table_meta->db(); } - engine_->ClearCacheLocked(table_meta->db()); + engine_->ClearCacheLocked(""); // we always refresh the aggr catalog in case zk notification arrives later than the `deploy` sql if (boost::iequals(table_meta->db(), openmldb::nameserver::PRE_AGG_DB)) { From b685e648e4467d86ce1ad8233b9428fb50313e09 Mon Sep 17 00:00:00 2001 From: dl239 Date: Fri, 15 Sep 2023 17:25:16 +0800 Subject: [PATCH 41/63] docs: upgrade 0.8.3 (#3496) --- CHANGELOG.md | 38 ++++++++++- demo/java_quickstart/demo/pom.xml | 2 +- demo/predict-taxi-trip-duration/README.md | 4 +- .../README.md | 2 +- docs/en/deploy/compile.md | 8 +-- docs/en/deploy/install_deploy.md | 68 +++++++++---------- docs/en/quickstart/openmldb_quickstart.md | 2 +- docs/en/quickstart/sdk/java_sdk.md | 10 +-- docs/en/reference/ip_tips.md | 6 +- docs/en/use_case/JD_recommendation_en.md | 2 +- docs/en/use_case/airflow_provider_demo.md | 2 +- .../en/use_case/dolphinscheduler_task_demo.md | 2 +- docs/en/use_case/kafka_connector_demo.md | 2 +- docs/en/use_case/lightgbm_demo.md | 2 +- docs/en/use_case/pulsar_connector_demo.md | 2 +- docs/en/use_case/talkingdata_demo.md | 2 +- docs/zh/deploy/compile.md | 8 +-- docs/zh/deploy/install_deploy.md | 68 +++++++++---------- .../deploy_integration/OpenMLDB_Byzer_taxi.md | 2 +- .../airflow_provider_demo.md | 2 +- .../dolphinscheduler_task_demo.md | 2 +- .../kafka_connector_demo.md | 2 +- .../pulsar_connector_demo.md | 2 +- docs/zh/quickstart/openmldb_quickstart.md | 2 +- docs/zh/quickstart/sdk/java_sdk.md | 10 +-- docs/zh/reference/ip_tips.md | 12 ++-- docs/zh/tutorial/standalone_use.md | 2 +- docs/zh/use_case/JD_recommendation.md | 2 +- docs/zh/use_case/talkingdata_demo.md | 2 +- .../use_case/taxi_tour_duration_prediction.md | 2 +- release/conf/openmldb-env.sh | 2 +- steps/upgrade_docs_version.sh | 2 +- 32 files changed, 156 insertions(+), 120 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b5e0cb3fe8..96615004cee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,40 @@ # Changelog +## [0.8.3] - 2023-09-15 + +### Features +- Optimize the performance of Java SDK (#3445 @dl239) +- Optimize the writing performance and significantly reduce the memory consumption of the Spark connector (#3443 @vagetablechicken) +- Support loading data from HIVE with customized SQLs (#3380 @tobegit3hub) +- Improve the output message for SDK and CLI (#3384 @vagetablechicken, #3434 #3494 @dl239) +- Support new built-in functions `json_array_length` and `get_json_object` (#3414 #3429 @aceforeverd) +- Add new options `RANGE_BIAS` and `ROWS_BIAS` for the `DEPLOYMENT` statement (#3456 @vagetablechicken) +- Support `const` project in online mode (#3376 @aceforeverd) +- Support `SHOW DEPLOYMENT` and `DROP DEPLOYMENT` with a database name (#3353 @emo-coder) +- Support inheriting environment variables for Spark (#3450 @vagetablechicken) +- Support deleting HDFS files when dropping tables (#3369 @tobegit3hub) +- Enhance the diagnostic tool (#3330 @zhangziheng01233) +- Enhance the operation tool (#3455 @dl239) +- Use the timeout value set by an user only if that is greater than the default value (#3484 @vagetablechicken) +- Remove the sync tool from the demo docker image (#3390 @dl239) +- Improve the documents (#3383 #3392 #3410 @vagetablechicken, #3175 #3447 ##3463 @TanZiYen, #3436 @aceforeverd, #3451 @wangerry, #3453 #3462 #3498 @dl239) + +### Bug Fixes +- `CREATE TABLE LIKE HIVE` returns success even if a database is not found (#3379 @emo-coder) +- If an error occurred when executing `DROP FUNCTION`, the function cannot be deleted again. (#3362 @vagetablechicken, #3441 @dl239) +- The results of `SHOW JOBS` are not sorted by `id` (#3371 @emo-coder) +- NameServer will crash if creating system tables fails. (#3432 @dl239) +- `CREATE INDEX` may fail if the previous `CREATE INDEX` command on the same table has not finished. (#3393 @dl239) +- The result of `SELECT` on the deleted index column is empty (#3426 @dl239) +- Other minor bug fixes (#3391 #3408 @vagetablechicken, #3386 #3427 #3459 @dl239, #3367 #3495 @aceforeverd) + +### Code Refactoring +#3397 @emo-coder, #3411 @vagetablechicken, #3435 @aceforeverd, #3473 @lqy222 + +### Breaking Changes +- The return type of `GetInternalSchema` in `SQLResultSet` changes from native Schema to `com._4paradigm.openmldb.sdk.Schema` #3445 +- Remove the deprecated TaskManager configuration `namenode.uri` #3369 + ## [0.8.2] - 2023-07-20 ### Features @@ -16,7 +51,7 @@ - The bool type is not properly packed in APIServer. (#3366 @vagetablechicken) - The table can be created successfully when there are duplicated indexs. (#3306 @dl239) -### Breaking Changes: +### Breaking Changes - The field `Offline_deep_copy` will be replaced by `Offline_symbolic_paths` in the result of `SHOW TABLE STATUS` #3349. ## [0.8.1] - 2023-06-28 @@ -618,6 +653,7 @@ Removed - openmldb-0.2.0-linux.tar.gz targets on x86_64 - aarch64 artifacts consider experimental +[0.8.3]: https://github.com/4paradigm/OpenMLDB/compare/v0.8.2...v0.8.3 [0.8.2]: https://github.com/4paradigm/OpenMLDB/compare/v0.8.1...v0.8.2 [0.8.1]: https://github.com/4paradigm/OpenMLDB/compare/v0.8.0...v0.8.1 [0.8.0]: https://github.com/4paradigm/OpenMLDB/compare/v0.7.3...v0.8.0 diff --git a/demo/java_quickstart/demo/pom.xml b/demo/java_quickstart/demo/pom.xml index 8b56bc1ce31..d69691970e7 100644 --- a/demo/java_quickstart/demo/pom.xml +++ b/demo/java_quickstart/demo/pom.xml @@ -29,7 +29,7 @@ com.4paradigm.openmldb openmldb-jdbc - 0.8.2 + 0.8.3 org.testng diff --git a/demo/predict-taxi-trip-duration/README.md b/demo/predict-taxi-trip-duration/README.md index 3a2bc6b9dee..bd44778c2a3 100644 --- a/demo/predict-taxi-trip-duration/README.md +++ b/demo/predict-taxi-trip-duration/README.md @@ -28,7 +28,7 @@ w2 as (PARTITION BY passenger_count ORDER BY pickup_datetime ROWS_RANGE BETWEEN **Start docker** ``` -docker run -it 4pdosc/openmldb:0.8.2 bash +docker run -it 4pdosc/openmldb:0.8.3 bash ``` **Initialize environment** ```bash @@ -138,7 +138,7 @@ python3 predict.py **Start docker** ```bash -docker run -it 4pdosc/openmldb:0.8.2 bash +docker run -it 4pdosc/openmldb:0.8.3 bash ``` **Initialize environment** diff --git a/demo/talkingdata-adtracking-fraud-detection/README.md b/demo/talkingdata-adtracking-fraud-detection/README.md index 46a17cb770c..5fedb578266 100644 --- a/demo/talkingdata-adtracking-fraud-detection/README.md +++ b/demo/talkingdata-adtracking-fraud-detection/README.md @@ -15,7 +15,7 @@ We recommend you to use docker to run the demo. OpenMLDB and dependencies have b **Start docker** ``` -docker run -it 4pdosc/openmldb:0.8.2 bash +docker run -it 4pdosc/openmldb:0.8.3 bash ``` #### Run locally diff --git a/docs/en/deploy/compile.md b/docs/en/deploy/compile.md index 66c7a55d1ab..a20c921b4ac 100644 --- a/docs/en/deploy/compile.md +++ b/docs/en/deploy/compile.md @@ -7,7 +7,7 @@ This section describes the steps to compile and use OpenMLDB inside its official docker image [hybridsql](https://hub.docker.com/r/4pdosc/hybridsql). The docker image has packed required tools and dependencies, so there is no need to set them up separately. To compile without the official docker image, refer to the section [Detailed Instructions for Build](#detailed-instructions-for-build) below. -Keep in mind that you should always use the same version of both compile image and [OpenMLDB version](https://github.com/4paradigm/OpenMLDB/releases). This section demonstrates compiling for [OpenMLDB v0.8.2](https://github.com/4paradigm/OpenMLDB/releases/tag/v0.8.2) under `hybridsql:0.8.2` ,If you prefer to compile on the latest code in `main` branch, pull `hybridsql:latest` image instead. +Keep in mind that you should always use the same version of both compile image and [OpenMLDB version](https://github.com/4paradigm/OpenMLDB/releases). This section demonstrates compiling for [OpenMLDB v0.8.3](https://github.com/4paradigm/OpenMLDB/releases/tag/v0.8.3) under `hybridsql:0.8.3` ,If you prefer to compile on the latest code in `main` branch, pull `hybridsql:latest` image instead. 1. Pull the docker image @@ -21,11 +21,11 @@ Keep in mind that you should always use the same version of both compile image a docker run -it 4pdosc/hybridsql:0.8 bash ``` -3. Download the OpenMLDB source code inside the docker container, and setting the branch into v0.8.2 +3. Download the OpenMLDB source code inside the docker container, and setting the branch into v0.8.3 ```bash cd ~ - git clone -b v0.8.2 https://github.com/4paradigm/OpenMLDB.git + git clone -b v0.8.3 https://github.com/4paradigm/OpenMLDB.git ``` 4. Compile OpenMLDB @@ -151,7 +151,7 @@ The built jar packages are in the `target` path of each submodule. If you want t 1. Downloading the pre-built OpenMLDB Spark distribution: ```bash -wget https://github.com/4paradigm/spark/releases/download/v3.2.1-openmldb0.8.2/spark-3.2.1-bin-openmldbspark.tgz +wget https://github.com/4paradigm/spark/releases/download/v3.2.1-openmldb0.8.3/spark-3.2.1-bin-openmldbspark.tgz ``` Alternatively, you can also download the source code and compile from scratch: diff --git a/docs/en/deploy/install_deploy.md b/docs/en/deploy/install_deploy.md index 66d1a9c987b..cdaf06a5d6a 100644 --- a/docs/en/deploy/install_deploy.md +++ b/docs/en/deploy/install_deploy.md @@ -52,17 +52,17 @@ If your operating system is not mentioned above or if you want to compile from s ### Linux Platform Compatibility pre-test -Due to the variations among Linux platforms, the distribution package may not be entirely compatible with your machine. Therefore, it's recommended to conduct a preliminary compatibility test. Download the pre-compiled package `openmldb-0.8.2-linux.tar.gz`, and execute: +Due to the variations among Linux platforms, the distribution package may not be entirely compatible with your machine. Therefore, it's recommended to conduct a preliminary compatibility test. Download the pre-compiled package `openmldb-0.8.3-linux.tar.gz`, and execute: ``` -tar -zxvf openmldb-0.8.2-linux.tar.gz -./openmldb-0.8.2-linux/bin/openmldb --version +tar -zxvf openmldb-0.8.3-linux.tar.gz +./openmldb-0.8.3-linux/bin/openmldb --version ``` The result should display the version number of the program, as shown below: ``` -openmldb version 0.8.2-xxxx +openmldb version 0.8.3-xxxx Debug build (NDEBUG not #defined) ``` @@ -177,9 +177,9 @@ DataCollector and SyncTool currently do not support one-click deployment. Please ### Download OpenMLDB ``` -wget https://github.com/4paradigm/OpenMLDB/releases/download/v0.8.2/openmldb-0.8.2-linux.tar.gz -tar -zxvf openmldb-0.8.2-linux.tar.gz -cd openmldb-0.8.2-linux +wget https://github.com/4paradigm/OpenMLDB/releases/download/v0.8.3/openmldb-0.8.3-linux.tar.gz +tar -zxvf openmldb-0.8.3-linux.tar.gz +cd openmldb-0.8.3-linux ``` ### Environment Configuration @@ -188,7 +188,7 @@ The environment variables are defined in `conf/openmldb-env.sh`, as shown in the | Environment Variable | Default Value | Note | | --------------------------------- | ------------------------------------------------------- | ------------------------------------------------------------ | -| OPENMLDB_VERSION | 0.8.2 | OpenMLDB version | +| OPENMLDB_VERSION | 0.8.3 | OpenMLDB version | | OPENMLDB_MODE | standalone | standalone or cluster | | OPENMLDB_HOME | root directory of the release folder | openmldb root directory | | SPARK_HOME | $OPENMLDB_HOME/spark | openmldb spark root directory,If the directory does not exist, it will be downloaded automatically.| @@ -361,10 +361,10 @@ Note that at least two TabletServer need to be deployed, otherwise errors may oc **1. Download the OpenMLDB deployment package** ``` -wget https://github.com/4paradigm/OpenMLDB/releases/download/v0.8.2/openmldb-0.8.2-linux.tar.gz -tar -zxvf openmldb-0.8.2-linux.tar.gz -mv openmldb-0.8.2-linux openmldb-tablet-0.8.2 -cd openmldb-tablet-0.8.2 +wget https://github.com/4paradigm/OpenMLDB/releases/download/v0.8.3/openmldb-0.8.3-linux.tar.gz +tar -zxvf openmldb-0.8.3-linux.tar.gz +mv openmldb-0.8.3-linux openmldb-tablet-0.8.3 +cd openmldb-tablet-0.8.3 ``` **2. Modify the configuration file `conf/tablet.flags`** @@ -427,12 +427,12 @@ For clustered versions, the number of TabletServers must be 2 or more. If there' To start the next TabletServer on a different machine, simply repeat the aforementioned steps on that machine. If starting the next TabletServer on the same machine, ensure it's in a different directory, and do not reuse a directory where the TabletServer is already running. -For instance, you can decompress the package again (avoid using a directory where TabletServer is already running, as files generated after startup may be affected), and name the directory `openmldb-tablet-0.8.2-2`. +For instance, you can decompress the package again (avoid using a directory where TabletServer is already running, as files generated after startup may be affected), and name the directory `openmldb-tablet-0.8.3-2`. ``` -tar -zxvf openmldb-0.8.2-linux.tar.gz -mv openmldb-0.8.2-linux openmldb-tablet-0.8.2-2 -cd openmldb-tablet-0.8.2-2 +tar -zxvf openmldb-0.8.3-linux.tar.gz +mv openmldb-0.8.3-linux openmldb-tablet-0.8.3-2 +cd openmldb-tablet-0.8.3-2 ``` Modify the configuration again and start the TabletServer. Note that if all TabletServers are on the same machine, use different port numbers to avoid "Fail to listen" error in the log (`logs/tablet.WARNING`). @@ -450,10 +450,10 @@ Please ensure that all TabletServer have been successfully started before deploy **1. Download the OpenMLDB deployment package** ```` -wget https://github.com/4paradigm/OpenMLDB/releases/download/v0.8.2/openmldb-0.8.2-linux.tar.gz -tar -zxvf openmldb-0.8.2-linux.tar.gz -mv openmldb-0.8.2-linux openmldb-ns-0.8.2 -cd openmldb-ns-0.8.2 +wget https://github.com/4paradigm/OpenMLDB/releases/download/v0.8.3/openmldb-0.8.3-linux.tar.gz +tar -zxvf openmldb-0.8.3-linux.tar.gz +mv openmldb-0.8.3-linux openmldb-ns-0.8.3 +cd openmldb-ns-0.8.3 ```` **2. Modify the configuration file conf/nameserver.flags** @@ -498,12 +498,12 @@ You can have only one NameServer, but if you need high availability, you can dep To start the next NameServer on another machine, simply repeat the above steps on that machine. If starting the next NameServer on the same machine, ensure it's in a different directory and do not reuse the directory where NameServer has already been started. -For instance, you can decompress the package again (avoid using the directory where NameServer is already running, as files generated after startup may be affected) and name the directory `openmldb-ns-0.8.2-2`. +For instance, you can decompress the package again (avoid using the directory where NameServer is already running, as files generated after startup may be affected) and name the directory `openmldb-ns-0.8.3-2`. ``` -tar -zxvf openmldb-0.8.2-linux.tar.gz -mv openmldb-0.8.2-linux openmldb-ns-0.8.2-2 -cd openmldb-ns-0.8.2-2 +tar -zxvf openmldb-0.8.3-linux.tar.gz +mv openmldb-0.8.3-linux openmldb-ns-0.8.3-2 +cd openmldb-ns-0.8.3-2 ``` Then modify the configuration and start. @@ -544,10 +544,10 @@ Before running APIServer, ensure that the TabletServer and NameServer processes **1. Download the OpenMLDB deployment package** ``` -wget https://github.com/4paradigm/OpenMLDB/releases/download/v0.8.2/openmldb-0.8.2-linux.tar.gz -tar -zxvf openmldb-0.8.2-linux.tar.gz -mv openmldb-0.8.2-linux openmldb-apiserver-0.8.2 -cd openmldb-apiserver-0.8.2 +wget https://github.com/4paradigm/OpenMLDB/releases/download/v0.8.3/openmldb-0.8.3-linux.tar.gz +tar -zxvf openmldb-0.8.3-linux.tar.gz +mv openmldb-0.8.3-linux openmldb-apiserver-0.8.3 +cd openmldb-apiserver-0.8.3 ``` **2. Modify the configuration file conf/apiserver.flags** @@ -607,18 +607,18 @@ You can have only one TaskManager, but if you require high availability, you can Spark distribution: ```shell -wget https://github.com/4paradigm/spark/releases/download/v3.2.1-openmldb0.8.2/spark-3.2.1-bin-openmldbspark.tgz -# Image address (China):http://43.138.115.238/download/v0.8.2/spark-3.2.1-bin-openmldbspark.tgz +wget https://github.com/4paradigm/spark/releases/download/v3.2.1-openmldb0.8.3/spark-3.2.1-bin-openmldbspark.tgz +# Image address (China):http://43.138.115.238/download/v0.8.3/spark-3.2.1-bin-openmldbspark.tgz tar -zxvf spark-3.2.1-bin-openmldbspark.tgz export SPARK_HOME=`pwd`/spark-3.2.1-bin-openmldbspark/ ``` OpenMLDB deployment package: ``` -wget https://github.com/4paradigm/OpenMLDB/releases/download/v0.8.2/openmldb-0.8.2-linux.tar.gz -tar -zxvf openmldb-0.8.2-linux.tar.gz -mv openmldb-0.8.2-linux openmldb-taskmanager-0.8.2 -cd openmldb-taskmanager-0.8.2 +wget https://github.com/4paradigm/OpenMLDB/releases/download/v0.8.3/openmldb-0.8.3-linux.tar.gz +tar -zxvf openmldb-0.8.3-linux.tar.gz +mv openmldb-0.8.3-linux openmldb-taskmanager-0.8.3 +cd openmldb-taskmanager-0.8.3 ``` **2. Modify the configuration file conf/taskmanager.properties** diff --git a/docs/en/quickstart/openmldb_quickstart.md b/docs/en/quickstart/openmldb_quickstart.md index 944778abec4..57c3c0d2e75 100644 --- a/docs/en/quickstart/openmldb_quickstart.md +++ b/docs/en/quickstart/openmldb_quickstart.md @@ -19,7 +19,7 @@ This article is developed and deployed based on OpenMLDB CLI, and it is necessar Execute the following command in the command line to pull the OpenMLDB image and start the Docker container: ```bash -docker run -it 4pdosc/openmldb:0.8.2 bash +docker run -it 4pdosc/openmldb:0.8.3 bash ``` ``` {note} diff --git a/docs/en/quickstart/sdk/java_sdk.md b/docs/en/quickstart/sdk/java_sdk.md index 629c715d5e6..a74f4c98f3c 100644 --- a/docs/en/quickstart/sdk/java_sdk.md +++ b/docs/en/quickstart/sdk/java_sdk.md @@ -10,12 +10,12 @@ com.4paradigm.openmldb openmldb-jdbc - 0.7.2 + 0.8.3 com.4paradigm.openmldb openmldb-native - 0.7.2 + 0.8.3 ``` @@ -27,16 +27,16 @@ com.4paradigm.openmldb openmldb-jdbc - 0.7.2 + 0.8.3 com.4paradigm.openmldb openmldb-native - 0.7.2-macos + 0.8.3-macos ``` -Note: Since the openmldb-native package contains the C++ static library compiled for OpenMLDB, it is defaults to the Linux static library. For macOS, the version of openmldb-native should be changed to `0.7.2-macos`, while the version of openmldb-jdbc should remain unchanged. +Note: Since the openmldb-native package contains the C++ static library compiled for OpenMLDB, it is defaults to the Linux static library. For macOS, the version of openmldb-native should be changed to `0.8.3-macos`, while the version of openmldb-jdbc should remain unchanged. The macOS version of openmldb-native only supports macOS 12. To run it on macOS 11 or macOS 10.15, the openmldb-native package needs to be compiled from source code on the corresponding OS. For detailed compilation methods, please refer to [Concurrent Compilation of Java SDK](https://openmldb.ai/docs/zh/main/deploy/compile.html#java-sdk). diff --git a/docs/en/reference/ip_tips.md b/docs/en/reference/ip_tips.md index e8a7001792f..2fc5b1c8805 100644 --- a/docs/en/reference/ip_tips.md +++ b/docs/en/reference/ip_tips.md @@ -38,12 +38,12 @@ Expose the port through `-p` when starting the container, and the client can acc The stand-alone version needs to expose the ports of three components (nameserver, tabletserver, apiserver): ``` -docker run -p 6527:6527 -p 9921:9921 -p 8080:8080 -it 4pdosc/openmldb:0.8.2 bash +docker run -p 6527:6527 -p 9921:9921 -p 8080:8080 -it 4pdosc/openmldb:0.8.3 bash ``` The cluster version needs to expose the zk port and the ports of all components: ``` -docker run -p 2181:2181 -p 7527:7527 -p 10921:10921 -p 10922:10922 -p 8080:8080 -p 9902:9902 -it 4pdosc/openmldb:0.8.2 bash +docker run -p 2181:2181 -p 7527:7527 -p 10921:10921 -p 10922:10922 -p 8080:8080 -p 9902:9902 -it 4pdosc/openmldb:0.8.3 bash ``` ```{tip} @@ -57,7 +57,7 @@ If the OpenMLDB service process is distributed, the "port number is occupied" ap #### Host Network Or more conveniently, use host networking without port isolation, for example: ``` -docker run --network host -it 4pdosc/openmldb:0.8.2 bash +docker run --network host -it 4pdosc/openmldb:0.8.3 bash ``` But in this case, it is easy to find that the port is occupied by other processes in the host. If occupancy occurs, change the port number carefully. diff --git a/docs/en/use_case/JD_recommendation_en.md b/docs/en/use_case/JD_recommendation_en.md index 219026010e7..3a3a7df6f0a 100644 --- a/docs/en/use_case/JD_recommendation_en.md +++ b/docs/en/use_case/JD_recommendation_en.md @@ -52,7 +52,7 @@ Oneflow-serving:https://github.com/Oneflow-Inc/serving/tree/ce5d667468b6b3ba66 Pull the OpenMLDB docker image and run. ```bash -docker run -dit --name=openmldb --network=host -v $demodir:/work/oneflow_demo 4pdosc/openmldb:0.8.2 bash +docker run -dit --name=openmldb --network=host -v $demodir:/work/oneflow_demo 4pdosc/openmldb:0.8.3 bash docker exec -it openmldb bash ``` diff --git a/docs/en/use_case/airflow_provider_demo.md b/docs/en/use_case/airflow_provider_demo.md index 2e5b59efd34..bf430b7cce2 100644 --- a/docs/en/use_case/airflow_provider_demo.md +++ b/docs/en/use_case/airflow_provider_demo.md @@ -34,7 +34,7 @@ For the newest version, please visit [GitHub example_dags](https://github.com/4p - Please project the previously downloaded files to the path `/work/airflow/dags`, where Airflow will access for the DAG. ``` -docker run -p 8080:8080 -v `pwd`/airflow_demo_files:/work/airflow/dags -it 4pdosc/openmldb:0.8.2 bash +docker run -p 8080:8080 -v `pwd`/airflow_demo_files:/work/airflow/dags -it 4pdosc/openmldb:0.8.3 bash ``` #### 0.3 Download and Install the Airflow and the Airflow OpenMLDB Provider diff --git a/docs/en/use_case/dolphinscheduler_task_demo.md b/docs/en/use_case/dolphinscheduler_task_demo.md index c3666b6c8aa..8f3d9b51e97 100644 --- a/docs/en/use_case/dolphinscheduler_task_demo.md +++ b/docs/en/use_case/dolphinscheduler_task_demo.md @@ -33,7 +33,7 @@ In addition to the feature engineering done by OpenMLDB, the prediction also req The demo can run on MacOS or Linux, the OpenMLDB docker image is recommended. We'll start OpenMLDB and DolphinScheduler in the same container, expose the DolphinScheduler web port: ``` -docker run -it -p 12345:12345 4pdosc/openmldb:0.8.2 bash +docker run -it -p 12345:12345 4pdosc/openmldb:0.8.3 bash ``` ```{attention} diff --git a/docs/en/use_case/kafka_connector_demo.md b/docs/en/use_case/kafka_connector_demo.md index b088d24d085..be6c17e9fae 100644 --- a/docs/en/use_case/kafka_connector_demo.md +++ b/docs/en/use_case/kafka_connector_demo.md @@ -22,7 +22,7 @@ For OpenMLDB Kafka Connector implementation, please refer to [extensions/kafka-c This article will start the OpenMLDB in docker container, so there is no need to download the OpenMLDB separately. Moreover, Kafka and connector can be started in the same container. We recommend that you save the three downloaded packages to the same directory. Let's assume that the packages are in the `/work/kafka` directory. ``` -docker run -it -v `pwd`:/work/kafka --name openmldb 4pdosc/openmldb:0.8.2 bash +docker run -it -v `pwd`:/work/kafka --name openmldb 4pdosc/openmldb:0.8.3 bash ``` ### Steps diff --git a/docs/en/use_case/lightgbm_demo.md b/docs/en/use_case/lightgbm_demo.md index 28132af3829..f4e602373a6 100644 --- a/docs/en/use_case/lightgbm_demo.md +++ b/docs/en/use_case/lightgbm_demo.md @@ -13,7 +13,7 @@ Note that: (1) this case is based on the OpenMLDB cluster version for tutorial d - Pull the OpenMLDB docker image and run the corresponding container: ```bash -docker run -it 4pdosc/openmldb:0.8.2 bash +docker run -it 4pdosc/openmldb:0.8.3 bash ``` The image is preinstalled with OpenMLDB and preset with all scripts, third-party libraries, open-source tools and training data required for this case. diff --git a/docs/en/use_case/pulsar_connector_demo.md b/docs/en/use_case/pulsar_connector_demo.md index 161bee81eb8..194195da3fd 100644 --- a/docs/en/use_case/pulsar_connector_demo.md +++ b/docs/en/use_case/pulsar_connector_demo.md @@ -29,7 +29,7 @@ Only OpenMLDB cluster mode can be the sink dist, and only write to online storag We recommend that you use ‘host network’ to run docker. And bind volume ‘files’ too. The sql scripts are in it. ``` -docker run -dit --network host -v `pwd`/files:/work/pulsar_files --name openmldb 4pdosc/openmldb:0.8.2 bash +docker run -dit --network host -v `pwd`/files:/work/pulsar_files --name openmldb 4pdosc/openmldb:0.8.3 bash docker exec -it openmldb bash ``` ```{note} diff --git a/docs/en/use_case/talkingdata_demo.md b/docs/en/use_case/talkingdata_demo.md index 42408324a15..4c0370d375f 100644 --- a/docs/en/use_case/talkingdata_demo.md +++ b/docs/en/use_case/talkingdata_demo.md @@ -13,7 +13,7 @@ It is recommended to run this demo in Docker. Please make sure that OpenMLDB and **Start the OpenMLDB Docker Image** ``` -docker run -it 4pdosc/openmldb:0.8.2 bash +docker run -it 4pdosc/openmldb:0.8.3 bash ``` #### 1.1.2 Run Locally diff --git a/docs/zh/deploy/compile.md b/docs/zh/deploy/compile.md index c717f784ac4..aec38f6a5a3 100644 --- a/docs/zh/deploy/compile.md +++ b/docs/zh/deploy/compile.md @@ -4,7 +4,7 @@ 此节介绍在官方编译镜像 [hybridsql](https://hub.docker.com/r/4pdosc/hybridsql) 中编译 OpenMLDB,主要可以用于在容器内试用和开发目的。镜像内置了编译所需要的工具和依赖,因此不需要额外的步骤单独配置它们。关于基于非 docker 的编译使用方式,请参照下面的 [从源码全量编译](#从源码全量编译) 章节。 -对于编译镜像的版本,需要注意拉取的镜像版本和 [OpenMLDB 发布版本](https://github.com/4paradigm/OpenMLDB/releases)保持一致。以下例子演示了在 `hybridsql:0.8.2` 镜像版本上编译 [OpenMLDB v0.8.2](https://github.com/4paradigm/OpenMLDB/releases/tag/v0.8.2) 的代码,如果要编译最新 `main` 分支的代码,则需要拉取 `hybridsql:latest` 版本镜像。 +对于编译镜像的版本,需要注意拉取的镜像版本和 [OpenMLDB 发布版本](https://github.com/4paradigm/OpenMLDB/releases)保持一致。以下例子演示了在 `hybridsql:0.8.3` 镜像版本上编译 [OpenMLDB v0.8.3](https://github.com/4paradigm/OpenMLDB/releases/tag/v0.8.3) 的代码,如果要编译最新 `main` 分支的代码,则需要拉取 `hybridsql:latest` 版本镜像。 1. 下载 docker 镜像 ```bash @@ -16,10 +16,10 @@ docker run -it 4pdosc/hybridsql:0.8 bash ``` -3. 在 docker 容器内, 克隆 OpenMLDB, 并切换分支到 v0.8.2 +3. 在 docker 容器内, 克隆 OpenMLDB, 并切换分支到 v0.8.3 ```bash cd ~ - git clone -b v0.8.2 https://github.com/4paradigm/OpenMLDB.git + git clone -b v0.8.3 https://github.com/4paradigm/OpenMLDB.git ``` 4. 在 docker 容器内编译 OpenMLDB @@ -141,7 +141,7 @@ make SQL_JAVASDK_ENABLE=ON NPROC=4 1. 下载预编译的OpenMLDB Spark发行版。 ```bash -wget https://github.com/4paradigm/spark/releases/download/v3.2.1-openmldb0.8.2/spark-3.2.1-bin-openmldbspark.tgz +wget https://github.com/4paradigm/spark/releases/download/v3.2.1-openmldb0.8.3/spark-3.2.1-bin-openmldbspark.tgz ``` 或者下载源代码并从头开始编译。 diff --git a/docs/zh/deploy/install_deploy.md b/docs/zh/deploy/install_deploy.md index aa730c6189d..d060cce3b01 100644 --- a/docs/zh/deploy/install_deploy.md +++ b/docs/zh/deploy/install_deploy.md @@ -47,17 +47,17 @@ strings /lib64/libc.so.6 | grep ^GLIBC_ ### Linux 平台预测试 -由于 Linux 平台的多样性,发布包可能在你的机器上不兼容,请先通过简单的运行测试。比如,下载预编译包 `openmldb-0.8.2-linux.tar.gz` 以后,运行: +由于 Linux 平台的多样性,发布包可能在你的机器上不兼容,请先通过简单的运行测试。比如,下载预编译包 `openmldb-0.8.3-linux.tar.gz` 以后,运行: ``` -tar -zxvf openmldb-0.8.2-linux.tar.gz -./openmldb-0.8.2-linux/bin/openmldb --version +tar -zxvf openmldb-0.8.3-linux.tar.gz +./openmldb-0.8.3-linux/bin/openmldb --version ``` 结果应显示该程序的版本号,类似 ``` -openmldb version 0.8.2-xxxx +openmldb version 0.8.3-xxxx Debug build (NDEBUG not #defined) ``` @@ -171,9 +171,9 @@ DataCollector和SyncTool暂不支持一键部署。请参考手动部署方式 ### 下载OpenMLDB发行版 ``` -wget https://github.com/4paradigm/OpenMLDB/releases/download/v0.8.2/openmldb-0.8.2-linux.tar.gz -tar -zxvf openmldb-0.8.2-linux.tar.gz -cd openmldb-0.8.2-linux +wget https://github.com/4paradigm/OpenMLDB/releases/download/v0.8.3/openmldb-0.8.3-linux.tar.gz +tar -zxvf openmldb-0.8.3-linux.tar.gz +cd openmldb-0.8.3-linux ``` ### 环境配置 @@ -181,7 +181,7 @@ cd openmldb-0.8.2-linux | 环境变量 | 默认值 | 定义 | |-----------------------------------|------------------------------------|-------------------------------------------------------------------------| -| OPENMLDB_VERSION | 0.8.2 | OpenMLDB版本 | +| OPENMLDB_VERSION | 0.8.3 | OpenMLDB版本 | | OPENMLDB_MODE | standalone | standalone或者cluster | | OPENMLDB_HOME | 当前发行版的根目录 | openmldb发行版根目录 | | SPARK_HOME | $OPENMLDB_HOME/spark | openmldb spark发行版根目录,如果该目录不存在,自动从网上下载 | @@ -348,10 +348,10 @@ bash bin/zkCli.sh -server 172.27.128.33:7181 **1. 下载OpenMLDB部署包** ``` -wget https://github.com/4paradigm/OpenMLDB/releases/download/v0.8.2/openmldb-0.8.2-linux.tar.gz -tar -zxvf openmldb-0.8.2-linux.tar.gz -mv openmldb-0.8.2-linux openmldb-tablet-0.8.2 -cd openmldb-tablet-0.8.2 +wget https://github.com/4paradigm/OpenMLDB/releases/download/v0.8.3/openmldb-0.8.3-linux.tar.gz +tar -zxvf openmldb-0.8.3-linux.tar.gz +mv openmldb-0.8.3-linux openmldb-tablet-0.8.3 +cd openmldb-tablet-0.8.3 ``` **2. 修改配置文件`conf/tablet.flags`** ```bash @@ -402,12 +402,12 @@ Start tablet success 在另一台机器启动下一个TabletServer只需在该机器上重复以上步骤。如果是在同一个机器上启动下一个TabletServer,请保证是在另一个目录中,不要重复使用已经启动过TabletServer的目录。 -比如,可以再次解压压缩包(不要cp已经启动过TabletServer的目录,启动后的生成文件会造成影响),并命名目录为`openmldb-tablet-0.8.2-2`。 +比如,可以再次解压压缩包(不要cp已经启动过TabletServer的目录,启动后的生成文件会造成影响),并命名目录为`openmldb-tablet-0.8.3-2`。 ``` -tar -zxvf openmldb-0.8.2-linux.tar.gz -mv openmldb-0.8.2-linux openmldb-tablet-0.8.2-2 -cd openmldb-tablet-0.8.2-2 +tar -zxvf openmldb-0.8.3-linux.tar.gz +mv openmldb-0.8.3-linux openmldb-tablet-0.8.3-2 +cd openmldb-tablet-0.8.3-2 ``` 再修改配置并启动。注意,TabletServer如果都在同一台机器上,请使用不同端口号,否则日志(logs/tablet.WARNING)中将会有"Fail to listen"信息。 @@ -421,10 +421,10 @@ cd openmldb-tablet-0.8.2-2 ``` **1. 下载OpenMLDB部署包** ```` -wget https://github.com/4paradigm/OpenMLDB/releases/download/v0.8.2/openmldb-0.8.2-linux.tar.gz -tar -zxvf openmldb-0.8.2-linux.tar.gz -mv openmldb-0.8.2-linux openmldb-ns-0.8.2 -cd openmldb-ns-0.8.2 +wget https://github.com/4paradigm/OpenMLDB/releases/download/v0.8.3/openmldb-0.8.3-linux.tar.gz +tar -zxvf openmldb-0.8.3-linux.tar.gz +mv openmldb-0.8.3-linux openmldb-ns-0.8.3 +cd openmldb-ns-0.8.3 ```` **2. 修改配置文件conf/nameserver.flags** ```bash @@ -462,12 +462,12 @@ NameServer 可以只存在一台,如果你需要高可用性,可以部署多 在另一台机器启动下一个 NameServer 只需在该机器上重复以上步骤。如果是在同一个机器上启动下一个 NameServer,请保证是在另一个目录中,不要重复使用已经启动过 namserver 的目录。 -比如,可以再次解压压缩包(不要cp已经启动过 namserver 的目录,启动后的生成文件会造成影响),并命名目录为`openmldb-ns-0.8.2-2`。 +比如,可以再次解压压缩包(不要cp已经启动过 namserver 的目录,启动后的生成文件会造成影响),并命名目录为`openmldb-ns-0.8.3-2`。 ``` -tar -zxvf openmldb-0.8.2-linux.tar.gz -mv openmldb-0.8.2-linux openmldb-ns-0.8.2-2 -cd openmldb-ns-0.8.2-2 +tar -zxvf openmldb-0.8.3-linux.tar.gz +mv openmldb-0.8.3-linux openmldb-ns-0.8.3-2 +cd openmldb-ns-0.8.3-2 ``` 然后再修改配置并启动。 @@ -505,10 +505,10 @@ APIServer负责接收http请求,转发给OpenMLDB集群并返回结果。它 **1. 下载OpenMLDB部署包** ``` -wget https://github.com/4paradigm/OpenMLDB/releases/download/v0.8.2/openmldb-0.8.2-linux.tar.gz -tar -zxvf openmldb-0.8.2-linux.tar.gz -mv openmldb-0.8.2-linux openmldb-apiserver-0.8.2 -cd openmldb-apiserver-0.8.2 +wget https://github.com/4paradigm/OpenMLDB/releases/download/v0.8.3/openmldb-0.8.3-linux.tar.gz +tar -zxvf openmldb-0.8.3-linux.tar.gz +mv openmldb-0.8.3-linux openmldb-apiserver-0.8.3 +cd openmldb-apiserver-0.8.3 ``` **2. 修改配置文件conf/apiserver.flags** @@ -563,18 +563,18 @@ TaskManager 可以只存在一台,如果你需要高可用性,可以部署 Spark发行版: ```shell -wget https://github.com/4paradigm/spark/releases/download/v3.2.1-openmldb0.8.2/spark-3.2.1-bin-openmldbspark.tgz -# 中国镜像地址:http://43.138.115.238/download/v0.8.2/spark-3.2.1-bin-openmldbspark.tgz +wget https://github.com/4paradigm/spark/releases/download/v3.2.1-openmldb0.8.3/spark-3.2.1-bin-openmldbspark.tgz +# 中国镜像地址:http://43.138.115.238/download/v0.8.3/spark-3.2.1-bin-openmldbspark.tgz tar -zxvf spark-3.2.1-bin-openmldbspark.tgz export SPARK_HOME=`pwd`/spark-3.2.1-bin-openmldbspark/ ``` OpenMLDB部署包: ``` -wget https://github.com/4paradigm/OpenMLDB/releases/download/v0.8.2/openmldb-0.8.2-linux.tar.gz -tar -zxvf openmldb-0.8.2-linux.tar.gz -mv openmldb-0.8.2-linux openmldb-taskmanager-0.8.2 -cd openmldb-taskmanager-0.8.2 +wget https://github.com/4paradigm/OpenMLDB/releases/download/v0.8.3/openmldb-0.8.3-linux.tar.gz +tar -zxvf openmldb-0.8.3-linux.tar.gz +mv openmldb-0.8.3-linux openmldb-taskmanager-0.8.3 +cd openmldb-taskmanager-0.8.3 ``` **2. 修改配置文件conf/taskmanager.properties** diff --git a/docs/zh/integration/deploy_integration/OpenMLDB_Byzer_taxi.md b/docs/zh/integration/deploy_integration/OpenMLDB_Byzer_taxi.md index 1d43938f04e..926c079469d 100644 --- a/docs/zh/integration/deploy_integration/OpenMLDB_Byzer_taxi.md +++ b/docs/zh/integration/deploy_integration/OpenMLDB_Byzer_taxi.md @@ -13,7 +13,7 @@ 执行命令如下: ``` -docker run --network host -dit --name openmldb -v /mlsql/admin/:/byzermnt 4pdosc/openmldb:0.8.2 bash +docker run --network host -dit --name openmldb -v /mlsql/admin/:/byzermnt 4pdosc/openmldb:0.8.3 bash docker exec -it openmldb bash /work/init.sh echo "create database db1;" | /work/openmldb/bin/openmldb --zk_cluster=127.0.0.1:2181 --zk_root_path=/openmldb --role=sql_client diff --git a/docs/zh/integration/deploy_integration/airflow_provider_demo.md b/docs/zh/integration/deploy_integration/airflow_provider_demo.md index ee812d5e428..5e8a77df979 100644 --- a/docs/zh/integration/deploy_integration/airflow_provider_demo.md +++ b/docs/zh/integration/deploy_integration/airflow_provider_demo.md @@ -35,7 +35,7 @@ ls airflow_demo_files 登录Airflow Web需要对外端口,所以此处暴露容器的端口。并且直接将上一步下载的文件映射到`/work/airflow/dags`,接下来Airflow将加载此文件夹的DAG。 ``` -docker run -p 8080:8080 -v `pwd`/airflow_demo_files:/work/airflow_demo_files -it 4pdosc/openmldb:0.8.2 bash +docker run -p 8080:8080 -v `pwd`/airflow_demo_files:/work/airflow_demo_files -it 4pdosc/openmldb:0.8.3 bash ``` #### 0.3 下载安装Airflow与Airflow OpenMLDB Provider diff --git a/docs/zh/integration/deploy_integration/dolphinscheduler_task_demo.md b/docs/zh/integration/deploy_integration/dolphinscheduler_task_demo.md index ac869925505..da484e5dad7 100644 --- a/docs/zh/integration/deploy_integration/dolphinscheduler_task_demo.md +++ b/docs/zh/integration/deploy_integration/dolphinscheduler_task_demo.md @@ -31,7 +31,7 @@ OpenMLDB 希望能达成开发即上线的目标,让开发回归本质,而 测试可以在macOS或Linux上运行,推荐在我们提供的 OpenMLDB 镜像内进行演示测试。我们将在这个容器中启动OpenMLDB和DolphinScheduler,暴露DolphinScheduler的web端口: ``` -docker run -it -p 12345:12345 4pdosc/openmldb:0.8.2 bash +docker run -it -p 12345:12345 4pdosc/openmldb:0.8.3 bash ``` ```{attention} DolphinScheduler 需要配置租户,是操作系统的用户,并且该用户需要有 sudo 权限。所以推荐在 OpenMLDB 容器内下载并启动 DolphinScheduler。否则,请准备有sudo权限的操作系统用户。 diff --git a/docs/zh/integration/online_datasources/kafka_connector_demo.md b/docs/zh/integration/online_datasources/kafka_connector_demo.md index e0cbdba9d6e..7dffd7be109 100644 --- a/docs/zh/integration/online_datasources/kafka_connector_demo.md +++ b/docs/zh/integration/online_datasources/kafka_connector_demo.md @@ -21,7 +21,7 @@ OpenMLDB Kafka Connector实现见[extensions/kafka-connect-jdbc](https://github. 我们推荐你将下载的三个文件包都绑定到文件目录`kafka`。当然,也可以在启动容器后,再进行文件包的下载。我们假设文件包都在`/work/kafka`目录中。 ``` -docker run -it -v `pwd`:/work/kafka 4pdosc/openmldb:0.8.2 bash +docker run -it -v `pwd`:/work/kafka 4pdosc/openmldb:0.8.3 bash ``` ### 注意事项 diff --git a/docs/zh/integration/online_datasources/pulsar_connector_demo.md b/docs/zh/integration/online_datasources/pulsar_connector_demo.md index 267e16614f4..7277f039ee9 100644 --- a/docs/zh/integration/online_datasources/pulsar_connector_demo.md +++ b/docs/zh/integration/online_datasources/pulsar_connector_demo.md @@ -35,7 +35,7 @@ Apache Pulsar是一个云原生的,分布式消息流平台。它可以作为O ``` 我们更推荐你使用‘host network’模式运行docker,以及绑定文件目录‘files’,sql脚本在该目录中。 ``` -docker run -dit --network host -v `pwd`/files:/work/pulsar_files --name openmldb 4pdosc/openmldb:0.8.2 bash +docker run -dit --network host -v `pwd`/files:/work/pulsar_files --name openmldb 4pdosc/openmldb:0.8.3 bash docker exec -it openmldb bash ``` diff --git a/docs/zh/quickstart/openmldb_quickstart.md b/docs/zh/quickstart/openmldb_quickstart.md index 3d252f1fc3b..6a0191b09f1 100644 --- a/docs/zh/quickstart/openmldb_quickstart.md +++ b/docs/zh/quickstart/openmldb_quickstart.md @@ -19,7 +19,7 @@ OpenMLDB 的主要使用场景为作为机器学习的实时特征平台。其 在命令行执行以下命令拉取 OpenMLDB 镜像,并启动 Docker 容器: ```bash -docker run -it 4pdosc/openmldb:0.8.2 bash +docker run -it 4pdosc/openmldb:0.8.3 bash ``` ```{note} diff --git a/docs/zh/quickstart/sdk/java_sdk.md b/docs/zh/quickstart/sdk/java_sdk.md index 3f8534518b4..966d50db785 100644 --- a/docs/zh/quickstart/sdk/java_sdk.md +++ b/docs/zh/quickstart/sdk/java_sdk.md @@ -12,12 +12,12 @@ Java SDK中,JDBC Statement的默认执行模式为在线,SqlClusterExecutor com.4paradigm.openmldb openmldb-jdbc - 0.8.2 + 0.8.3 com.4paradigm.openmldb openmldb-native - 0.8.2 + 0.8.3 ``` @@ -29,16 +29,16 @@ Java SDK中,JDBC Statement的默认执行模式为在线,SqlClusterExecutor com.4paradigm.openmldb openmldb-jdbc - 0.8.2 + 0.8.3 com.4paradigm.openmldb openmldb-native - 0.8.2-macos + 0.8.3-macos ``` -注意:由于 openmldb-native 中包含了 OpenMLDB 编译的 C++ 静态库,默认是 Linux 静态库,macOS 上需将上述 openmldb-native 的 version 改成 `0.8.2-macos`,openmldb-jdbc 的版本保持不变。 +注意:由于 openmldb-native 中包含了 OpenMLDB 编译的 C++ 静态库,默认是 Linux 静态库,macOS 上需将上述 openmldb-native 的 version 改成 `0.8.3-macos`,openmldb-jdbc 的版本保持不变。 openmldb-native 的 macOS 版本只支持 macOS 12,如需在 macOS 11 或 macOS 10.15上运行,需在相应 OS 上源码编译 openmldb-native 包,详细编译方法见[并发编译 Java SDK](https://openmldb.ai/docs/zh/main/deploy/compile.html#java-sdk)。使用自编译的 openmldb-native 包,推荐使用`mvn install`安装到本地仓库,然后在 pom 中引用本地仓库的 openmldb-native 包,不建议用`scope=system`的方式引用。 diff --git a/docs/zh/reference/ip_tips.md b/docs/zh/reference/ip_tips.md index 846bd520088..fad3d3e0944 100644 --- a/docs/zh/reference/ip_tips.md +++ b/docs/zh/reference/ip_tips.md @@ -52,15 +52,15 @@ curl http:///dbs/foo -X POST -d'{"mode":"online", "sql":"show component - 暴露端口,也需要修改apiserver的endpoint改为`0.0.0.0`。这样可以使用127.0.0.1或是公网ip访问到 APIServer。 单机版: ``` - docker run -p 8080:8080 -it 4pdosc/openmldb:0.8.2 bash + docker run -p 8080:8080 -it 4pdosc/openmldb:0.8.3 bash ``` 集群版: ``` - docker run -p 9080:9080 -it 4pdosc/openmldb:0.8.2 bash + docker run -p 9080:9080 -it 4pdosc/openmldb:0.8.3 bash ``` - 使用host网络,可以不用修改endpoint配置。缺点是容易引起端口冲突。 ``` - docker run --network host -it 4pdosc/openmldb:0.8.2 bash + docker run --network host -it 4pdosc/openmldb:0.8.3 bash ``` 如果是跨主机访问容器 onebox 中的 APIServer,可以**任选一种**下面的方式: @@ -126,17 +126,17 @@ cd /work/openmldb/conf/ && ls | grep -v _ | xargs sed -i s/0.0.0.0//g && cd 单机版需要暴露三个组件(nameserver,tabletserver,APIServer)的端口: ``` -docker run -p 6527:6527 -p 9921:9921 -p 8080:8080 -it 4pdosc/openmldb:0.8.2 bash +docker run -p 6527:6527 -p 9921:9921 -p 8080:8080 -it 4pdosc/openmldb:0.8.3 bash ``` 集群版需要暴露zk端口与所有组件的端口: ``` -docker run -p 2181:2181 -p 7527:7527 -p 10921:10921 -p 10922:10922 -p 8080:8080 -p 9902:9902 -it 4pdosc/openmldb:0.8.2 bash +docker run -p 2181:2181 -p 7527:7527 -p 10921:10921 -p 10922:10922 -p 8080:8080 -p 9902:9902 -it 4pdosc/openmldb:0.8.3 bash ``` - 使用host网络,可以不用修改 endpoint 配置。如果有端口冲突,请修改 server 的端口配置。 ``` -docker run --network host -it 4pdosc/openmldb:0.8.2 bash +docker run --network host -it 4pdosc/openmldb:0.8.3 bash ``` 如果是跨主机使用 CLI/SDK 访问问容器onebox,只能通过`--network host`,并更改所有endpoint为公网IP,才能顺利访问。 diff --git a/docs/zh/tutorial/standalone_use.md b/docs/zh/tutorial/standalone_use.md index 4cc7eee9bea..df27c8307de 100644 --- a/docs/zh/tutorial/standalone_use.md +++ b/docs/zh/tutorial/standalone_use.md @@ -11,7 +11,7 @@ 执行以下命令拉取 OpenMLDB 镜像,并启动 Docker 容器: ```bash -docker run -it 4pdosc/openmldb:0.8.2 bash +docker run -it 4pdosc/openmldb:0.8.3 bash ``` 成功启动容器以后,本教程中的后续命令默认均在容器内执行。 diff --git a/docs/zh/use_case/JD_recommendation.md b/docs/zh/use_case/JD_recommendation.md index 143666d58ec..d4035be912a 100644 --- a/docs/zh/use_case/JD_recommendation.md +++ b/docs/zh/use_case/JD_recommendation.md @@ -74,7 +74,7 @@ docker pull oneflowinc/oneflow-serving:nightly 由于 OpenMLDB 集群需要和其他组件网络通信,我们直接使用 host 网络。本例将在容器中使用已下载的脚本,所以请将数据脚本所在目录 `demodir` 映射为容器中的目录: ```bash -docker run -dit --name=openmldb --network=host -v $demodir:/work/oneflow_demo 4pdosc/openmldb:0.8.2 bash +docker run -dit --name=openmldb --network=host -v $demodir:/work/oneflow_demo 4pdosc/openmldb:0.8.3 bash docker exec -it openmldb bash ``` diff --git a/docs/zh/use_case/talkingdata_demo.md b/docs/zh/use_case/talkingdata_demo.md index 1baff5ea2e2..c47bc9a652a 100755 --- a/docs/zh/use_case/talkingdata_demo.md +++ b/docs/zh/use_case/talkingdata_demo.md @@ -16,7 +16,7 @@ **启动 Docker** ``` -docker run -it 4pdosc/openmldb:0.8.2 bash +docker run -it 4pdosc/openmldb:0.8.3 bash ``` #### 1.1.2 在本地运行 diff --git a/docs/zh/use_case/taxi_tour_duration_prediction.md b/docs/zh/use_case/taxi_tour_duration_prediction.md index 21d2c50fee6..faaff3bf922 100644 --- a/docs/zh/use_case/taxi_tour_duration_prediction.md +++ b/docs/zh/use_case/taxi_tour_duration_prediction.md @@ -15,7 +15,7 @@ 在命令行执行以下命令拉取 OpenMLDB 镜像,并启动 Docker 容器: ```bash -docker run -it 4pdosc/openmldb:0.8.2 bash +docker run -it 4pdosc/openmldb:0.8.3 bash ``` 该镜像预装了OpenMLDB,并预置了本案例所需要的所有脚本、三方库、开源工具以及训练数据。 diff --git a/release/conf/openmldb-env.sh b/release/conf/openmldb-env.sh index d2967bdb777..c86d84aebd1 100644 --- a/release/conf/openmldb-env.sh +++ b/release/conf/openmldb-env.sh @@ -1,5 +1,5 @@ #! /usr/bin/env bash -export OPENMLDB_VERSION=0.8.2 +export OPENMLDB_VERSION=0.8.3 # openmldb mode: standalone / cluster export OPENMLDB_MODE=${OPENMLDB_MODE:=standalone} # tablet port diff --git a/steps/upgrade_docs_version.sh b/steps/upgrade_docs_version.sh index 7199962e667..e73dfdf0b4b 100644 --- a/steps/upgrade_docs_version.sh +++ b/steps/upgrade_docs_version.sh @@ -62,8 +62,8 @@ do upgrade_docker "$file" done -upgrade_java_sdk "docs/en/quickstart/java_sdk.md" upgrade_java_sdk "docs/zh/quickstart/sdk/java_sdk.md" +upgrade_java_sdk "docs/en/quickstart/sdk/java_sdk.md" cd demo/java_quickstart/demo || exit mvn versions:use-dep-version -Dincludes=com.4paradigm.openmldb:openmldb-jdbc -DdepVersion="${VERSION}" -DforceVersion=true cd - || exit From c0234c658e42b456cef8accd2072d4f23e2276fc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 19 Sep 2023 10:34:20 +0800 Subject: [PATCH 42/63] build(deps-dev): bump certifi from 2022.12.7 to 2023.7.22 in /docs (#3508) Bumps [certifi](https://github.com/certifi/python-certifi) from 2022.12.7 to 2023.7.22. - [Commits](https://github.com/certifi/python-certifi/compare/2022.12.07...2023.07.22) --- updated-dependencies: - dependency-name: certifi dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- docs/poetry.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/poetry.lock b/docs/poetry.lock index 01f5d11fa68..b2e0c52c7c0 100644 --- a/docs/poetry.lock +++ b/docs/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand. [[package]] name = "alabaster" @@ -45,13 +45,13 @@ lxml = ["lxml"] [[package]] name = "certifi" -version = "2022.12.7" +version = "2023.7.22" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" files = [ - {file = "certifi-2022.12.7-py3-none-any.whl", hash = "sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18"}, - {file = "certifi-2022.12.7.tar.gz", hash = "sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3"}, + {file = "certifi-2023.7.22-py3-none-any.whl", hash = "sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9"}, + {file = "certifi-2023.7.22.tar.gz", hash = "sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082"}, ] [[package]] From 9190ecfb473171b9194d92a308c9b6202599a7eb Mon Sep 17 00:00:00 2001 From: aceforeverd Date: Mon, 25 Sep 2023 13:01:41 +0800 Subject: [PATCH 43/63] fix(#3941): support window union multiple join in request mode (#3493) --- cases/query/window_query.yaml | 68 +++++++++++++++++++ hybridse/include/vm/schemas_context.h | 7 +- .../physical/group_and_sort_optimized.cc | 13 ++-- hybridse/src/vm/runner.cc | 1 + hybridse/src/vm/schemas_context.cc | 40 ++++++++++- 5 files changed, 121 insertions(+), 8 deletions(-) diff --git a/cases/query/window_query.yaml b/cases/query/window_query.yaml index 24ac38afe4f..84365be97f7 100644 --- a/cases/query/window_query.yaml +++ b/cases/query/window_query.yaml @@ -833,3 +833,71 @@ cases: 200, 1, 1, 1 300, 0, 0, 0 400, 1, 0, 0 + + - id: 23 + sql: | + select + gp_id, + count(gp_id) over w as cnt, + -- t2 matches and t3 not matches + count_where(gp_id, not is_null(lcond) and is_null(cond)) over w as feat1, + from (select id as gp_id, 0 as lcond, 0 as cond, cast(90000 as timestamp) as ts from request) + window w as ( + union (select t1.gp_id, t2.cond as lcond, t3.cond as cond, t1.ts from + t1 last join t2 on t1.gp_id = t2.account + last join t3 on t1.cond = t3.cond) + partition by gp_id order by ts + rows between unbounded preceding and current row + exclude current_row instance_not_in_window + ) + inputs: + - name: request + columns: ["id int"] + indexs: ['idx:id'] + data: | + 100 + 200 + 300 + 400 + - name: t1 + columns: + - gp_id int + - cond int + - ts timestamp + indexs: + - idx2:gp_id:ts + data: | + 100, 201, 10000 + 100, 201, 10000 + 200, 203, 10000 + 400, 204, 10000 + 400, 205, 10000 + - name: t2 + columns: + - account int + - cond int + - ts timestamp + indexs: ["idx1:account:ts"] + data: | + 100, 201, 1000 + 200, 203, 4000 + 400, 209, 4000 + - name: t3 + columns: + - cond int + - ts timestamp + indexs: ["idx3:cond:ts"] + data: | + 201, 1000 + 208, 1000 + expect: + columns: + - gp_id int + - cnt int64 + - feat1 int64 + order: gp_id + data: | + 100, 2, 0 + 200, 1, 1 + 300, 0, 0 + 400, 2, 2 diff --git a/hybridse/include/vm/schemas_context.h b/hybridse/include/vm/schemas_context.h index 43731f076cc..1541c64201d 100644 --- a/hybridse/include/vm/schemas_context.h +++ b/hybridse/include/vm/schemas_context.h @@ -58,7 +58,8 @@ class SchemaSource { size_t size() const; void Clear(); - std::string ToString() const; + std::string DebugString() const; + friend std::ostream& operator<<(std::ostream& os, const SchemaSource& sc) { return os << sc.DebugString(); } private: bool CheckSourceSetIndex(size_t idx) const; @@ -246,6 +247,10 @@ class SchemasContext { void BuildTrivial(const std::vector& schemas); void BuildTrivial(const std::string& default_db, const std::vector& tables); + std::string DebugString() const; + + friend std::ostream& operator<<(std::ostream& os, const SchemasContext& sc) { return os << sc.DebugString(); } + private: bool IsColumnAmbiguous(const std::string& column_name) const; diff --git a/hybridse/src/passes/physical/group_and_sort_optimized.cc b/hybridse/src/passes/physical/group_and_sort_optimized.cc index 2c10529fcc7..287919d9406 100644 --- a/hybridse/src/passes/physical/group_and_sort_optimized.cc +++ b/hybridse/src/passes/physical/group_and_sort_optimized.cc @@ -393,8 +393,8 @@ bool GroupAndSortOptimized::KeysOptimized(const SchemasContext* root_schemas_ctx return false; } PhysicalSimpleProjectNode* new_simple_op = nullptr; - Status status = plan_ctx_->CreateOp( - &new_simple_op, new_depend, simple_project->project()); + Status status = + plan_ctx_->CreateOp(&new_simple_op, new_depend, simple_project->project()); if (!status.isOK()) { LOG(WARNING) << "Fail to create simple project op: " << status; return false; @@ -442,11 +442,16 @@ bool GroupAndSortOptimized::KeysOptimized(const SchemasContext* root_schemas_ctx index_key, right_key, sort, &new_depend)) { return false; } - if (!ResetProducer(plan_ctx_, request_join, 0, new_depend)) { + PhysicalRequestJoinNode* new_join = nullptr; + auto s = plan_ctx_->CreateOp(&new_join, new_depend, request_join->GetProducer(1), + request_join->join(), + request_join->output_right_only()); + if (!s.isOK()) { + LOG(WARNING) << "Fail to create new request join op: " << s; return false; } - *new_in = request_join; + *new_in = new_join; return true; } return false; diff --git a/hybridse/src/vm/runner.cc b/hybridse/src/vm/runner.cc index a15a2626bf3..2072715c613 100644 --- a/hybridse/src/vm/runner.cc +++ b/hybridse/src/vm/runner.cc @@ -44,6 +44,7 @@ static bool IsPartitionProvider(vm::PhysicalOpNode* n) { switch (n->GetOpType()) { case kPhysicalOpSimpleProject: case kPhysicalOpRename: + case kPhysicalOpRequestJoin: return IsPartitionProvider(n->GetProducer(0)); case kPhysicalOpDataProvider: return dynamic_cast(n)->provider_type_ == kProviderTypePartition; diff --git a/hybridse/src/vm/schemas_context.cc b/hybridse/src/vm/schemas_context.cc index 9bff83940bb..214da20caa5 100644 --- a/hybridse/src/vm/schemas_context.cc +++ b/hybridse/src/vm/schemas_context.cc @@ -15,7 +15,10 @@ */ #include "vm/schemas_context.h" + #include + +#include "absl/strings/str_join.h" #include "passes/physical/physical_pass.h" #include "vm/physical_op.h" @@ -121,14 +124,18 @@ size_t SchemaSource::size() const { return schema_ == nullptr ? 0 : schema_->size(); } -std::string SchemaSource::ToString() const { +// output: {db}.{table}[ {name}:{type}:{id}, ... ] +std::string SchemaSource::DebugString() const { std::stringstream ss; + ss << source_db_ << "." << source_name_ << "["; for (size_t i = 0; i < column_ids_.size(); ++i) { + ss << schema_->Get(i).name() << ":" << node::TypeName(schema_->Get(i).type()) << ":"; ss << "#" << std::to_string(column_ids_[i]); if (i < column_ids_.size() - 1) { ss << ", "; } } + ss << "]"; return ss.str(); } @@ -173,7 +180,7 @@ void SchemasContext::Merge(size_t child_idx, const SchemasContext* child) { db_name = source->GetSourceDB(); } std::string rel_name = child->GetName(); - if (rel_name.empty()&& !source->GetSourceName().empty()) { + if (rel_name.empty() && !source->GetSourceName().empty()) { rel_name = source->GetSourceName(); } new_source->SetSourceDBAndTableName(db_name, rel_name); @@ -751,7 +758,34 @@ void SchemasContext::BuildTrivial( this->Build(); } -RowParser::RowParser(const SchemasContext* schema_ctx) : schema_ctx_(schema_ctx) { +std::string SchemasContext::DebugString() const { + std::stringstream ss; + ss << absl::StrCat("{", root_db_name_, ",", root_relation_name_, ",", default_db_name_, ", ", + absl::StrJoin(schema_sources_, ",", [](std::string* out, const SchemaSource* source) { + absl::StrAppend(out, source->DebugString()); + })); + ss << ", id_map={" + << absl::StrJoin(column_id_map_, ",", [](std::string* out, decltype(column_id_map_)::const_reference e) { + absl::StrAppend(out, e.first, "=(", e.second.first, ",", e.second.second, ")"); + }) << "}, "; + ss << "name_map={" + << absl::StrJoin(column_name_map_, ",", + [](std::string* out, decltype(column_name_map_)::const_reference e) { + absl::StrAppend( + out, e.first, "=[", + absl::StrJoin(e.second, ",", + [](std::string* out, decltype(e.second)::const_reference ref) { + absl::StrAppend(out, "(", ref.first, ",", ref.second, ")"); + }), + "]"); + }) + << "}"; + ss << "}"; + return ss.str(); +} + +RowParser::RowParser(const SchemasContext* schema_ctx) + : schema_ctx_(schema_ctx) { for (size_t i = 0; i < schema_ctx_->GetSchemaSourceSize(); ++i) { auto source = schema_ctx_->GetSchemaSource(i); row_view_list_.push_back(codec::RowView(*source->GetSchema())); From 116fbf57af24c70d24588f31c695b4d2a30c28a0 Mon Sep 17 00:00:00 2001 From: aceforeverd Date: Sat, 7 Oct 2023 22:26:30 +0800 Subject: [PATCH 44/63] feat(udf): support `datediff` dates before 1900 (#3499) --- hybridse/src/codegen/udf_ir_builder_test.cc | 42 +++++++- hybridse/src/udf/default_udf_library.cc | 28 +++--- hybridse/src/udf/udf.cc | 106 +++++++++++++++----- hybridse/src/udf/udf.h | 10 +- 4 files changed, 134 insertions(+), 52 deletions(-) diff --git a/hybridse/src/codegen/udf_ir_builder_test.cc b/hybridse/src/codegen/udf_ir_builder_test.cc index 13a82a1a925..6cd82be7859 100644 --- a/hybridse/src/codegen/udf_ir_builder_test.cc +++ b/hybridse/src/codegen/udf_ir_builder_test.cc @@ -1078,14 +1078,21 @@ TEST_F(UdfIRBuilderTest, DateDiff) { CheckUdf, Nullable, Nullable>(func_name, 44924, "2022-12-31", "1900-01-01"); CheckUdf, Nullable, Nullable>(func_name, 50, "20220620", "2022-05-01 11:11:11"); - CheckUdf, Nullable, Nullable>(func_name, 0, "2022-05-01", "20220501"); + CheckUdf, Nullable, Nullable>(func_name, 0, + "2022-05-01", "20220501"); CheckUdf, Nullable, Nullable>(func_name, nullptr, "2022-02-29", "20220501"); - CheckUdf, Nullable, Nullable>(func_name, nullptr, "1899-05-20", - "2020-05-20"); + CheckUdf, Nullable, Nullable>(func_name, 9, "1899-05-20", "1899-05-11"); CheckUdf, Nullable, Nullable>(func_name, nullptr, "2022-05-40", "2020-05-20"); - CheckUdf, Nullable, Nullable>(func_name, nullptr, "2020-05-20", - "1899-05-20"); + CheckUdf, Nullable, Nullable>(func_name, -30, "1199-10-12", "1199-11-11"); + // rfc3399 full format + CheckUdf, Nullable, Nullable>( + func_name, 20, "2000-01-01t00:12:00.1+08:00", "1999-12-12T12:12:12+08:00"); + CheckUdf, Nullable, Nullable>( + func_name, 19, "2000-01-01t00:12:00.1+08:00", "1999-12-12T20:12:12Z"); + CheckUdf, Nullable, Nullable>( + func_name, 20, "2000-01-01t06:12:00.1+08:00", "1999-12-12T12:12:12Z"); + CheckUdf, Nullable, Nullable>(func_name, nullptr, nullptr, "20220501"); CheckUdf, Nullable, Nullable>(func_name, nullptr, "2022-05-01", nullptr); CheckUdf, Nullable, Nullable>(func_name, nullptr, nullptr, nullptr); @@ -1093,6 +1100,8 @@ TEST_F(UdfIRBuilderTest, DateDiff) { // mix types CheckUdf, Nullable, Nullable>(func_name, -19, "2022-05-01", Date(2022, 5, 20)); CheckUdf, Nullable, Nullable>(func_name, 19, Date(2022, 5, 20), "2022-05-01"); + CheckUdf, Nullable, Nullable>(func_name, 3, Date(1900, 1, 1), "1899-12-29"); + CheckUdf, Nullable, Nullable>(func_name, -3, "1899-12-29", Date(1900, 1, 1)); CheckUdf, Nullable, Nullable>(func_name, nullptr, nullptr, "2022-05-01"); CheckUdf, Nullable, Nullable>(func_name, nullptr, Date(2022, 5, 20), nullptr); CheckUdf, Nullable, Nullable>(func_name, nullptr, nullptr, nullptr); @@ -1101,6 +1110,29 @@ TEST_F(UdfIRBuilderTest, DateDiff) { CheckUdf, Nullable, Nullable>(func_name, nullptr, nullptr, nullptr); } +TEST_F(UdfIRBuilderTest, DateDiffNull) { + auto func_name = "datediff"; + + // out-of-range format + CheckUdf, Nullable, Nullable>(func_name, nullptr, "1900-01-00", + "1999-12-12T12:12:12Z"); + CheckUdf, Nullable, Nullable>(func_name, nullptr, "1977-13-01", + "1999-12-12T12:12:12Z"); + CheckUdf, Nullable, Nullable>(func_name, nullptr, "19771232", + "1999-12-12T12:12:12Z"); + CheckUdf, Nullable, Nullable>(func_name, nullptr, "1999-12-12T25:12:12Z", + "1999-12-12T12:12:12Z"); + CheckUdf, Nullable, Nullable>(func_name, nullptr, "1999-12-12T12:66:12Z", + "1999-12-12T12:12:12Z"); + CheckUdf, Nullable, Nullable>(func_name, nullptr, "1999-12-12T12:00:61Z", + "1999-12-12T12:12:12Z"); + + // invalid format + CheckUdf, Nullable, Nullable>(func_name, nullptr, "1999-12-12T12:12:12Z", + "202 2-12-2 9"); + CheckUdf, Nullable, Nullable>(func_name, nullptr, "1999-12-12T12:12:12Z", + "12:30:30"); +} class UdfIRCastTest : public ::testing::TestWithParam>> {}; diff --git a/hybridse/src/udf/default_udf_library.cc b/hybridse/src/udf/default_udf_library.cc index f2c9bc1afd8..8b98212fffb 100644 --- a/hybridse/src/udf/default_udf_library.cc +++ b/hybridse/src/udf/default_udf_library.cc @@ -2591,16 +2591,21 @@ void DefaultUdfLibrary::InitTimeAndDateUdf() { }); RegisterExternal("datediff") - .args(reinterpret_cast(static_cast(v1::date_diff))) - .return_by_arg(true) - .returns>() + .args(static_cast(v1::date_diff)) .doc(R"( @brief days difference from date1 to date2 Supported date string style: - yyyy-mm-dd - yyyymmdd - - yyyy-mm-dd hh:mm:ss + - yyyy-mm-dd HH:MM:SS + - yyyy-mm-ddTHH:MM:SS.fff+HH:MM (RFC3399 format) + + Dates from string are transformed into the same time zone (which is currently always UTC+8) before differentiation, + dates from date type by default is at UTC+8, you may see a +1/-1 difference if the two date string have different time zones. + + Hint: since openmldb date type limits range from year 1900, to datadiff from/to a date before + 1900, pass it as string. Example: @@ -2614,20 +2619,11 @@ void DefaultUdfLibrary::InitTimeAndDateUdf() { @endcode @since 0.7.0)"); RegisterExternal("datediff") - .args( - reinterpret_cast(static_cast(v1::date_diff))) - .return_by_arg(true) - .returns>(); + .args(static_cast(v1::date_diff)); RegisterExternal("datediff") - .args( - reinterpret_cast(static_cast(v1::date_diff))) - .return_by_arg(true) - .returns>(); + .args(static_cast(v1::date_diff)); RegisterExternal("datediff") - .args( - reinterpret_cast(static_cast(v1::date_diff))) - .return_by_arg(true) - .returns>(); + .args(static_cast(v1::date_diff)); RegisterExternal("unix_timestamp") .args(reinterpret_cast(static_cast(v1::date_to_unix_timestamp))) diff --git a/hybridse/src/udf/udf.cc b/hybridse/src/udf/udf.cc index c32a58b8adb..2ec7033472f 100644 --- a/hybridse/src/udf/udf.cc +++ b/hybridse/src/udf/udf.cc @@ -16,7 +16,6 @@ #include "udf/udf.h" -#include #include #include @@ -28,6 +27,7 @@ #include "absl/strings/ascii.h" #include "absl/strings/str_replace.h" #include "absl/time/civil_time.h" +#include "absl/time/time.h" #include "base/iterator.h" #include "boost/date_time.hpp" #include "boost/date_time/gregorian/parsers.hpp" @@ -37,7 +37,7 @@ #include "codec/row.h" #include "codec/type_codec.h" #include "codegen/fn_ir_builder.h" -#include "farmhash.h" // NOLINT +#include "farmhash.h" #include "node/node_manager.h" #include "node/sql_node.h" #include "re2/re2.h" @@ -57,6 +57,20 @@ using openmldb::base::StringRef; using openmldb::base::Timestamp; using openmldb::base::Date; +// strftime()-like formatting options with extensions +// ref absl::FormatTime +static constexpr char DATE_FMT_YMD_1[] = "%E4Y-%m-%d"; +static constexpr char DATE_FMT_YMD_2[] = "%E4Y%m%d"; +static constexpr char DATE_FMT_YMDHMS[] = "%E4Y-%m-%d %H:%M:%S"; +static constexpr char DATE_FMT_RF3399_FULL[] = "%Y-%m-%d%ET%H:%M:%E*S%Ez"; + +// TODO(chenjing): 时区统一配置 +static constexpr int32_t TZ = 8; +static const absl::TimeZone DEFAULT_TZ = absl::FixedTimeZone(TZ * 60 * 60); +static constexpr time_t TZ_OFFSET = TZ * 3600000; +static constexpr int MAX_ALLOC_SIZE = 2 * 1024 * 1024; // 2M +bthread_key_t B_THREAD_LOCAL_MEM_POOL_KEY; + void hex(StringRef *str, StringRef *output) { std::ostringstream ss; for (uint32_t i=0; i < str->size_; i++) { @@ -104,12 +118,6 @@ void unhex(StringRef *str, StringRef *output, bool* is_null) { } } -// TODO(chenjing): 时区统一配置 -constexpr int32_t TZ = 8; -constexpr time_t TZ_OFFSET = TZ * 3600000; -constexpr int MAX_ALLOC_SIZE = 2 * 1024 * 1024; // 2M -bthread_key_t B_THREAD_LOCAL_MEM_POOL_KEY; - void trivial_fun() {} void dayofyear(int64_t ts, int32_t* out, bool* is_null) { @@ -818,7 +826,26 @@ void string_to_date(StringRef *str, Date *output, return; } -void date_diff(Date *date1, Date *date2, int *diff, bool *is_null) { +absl::StatusOr string_to_time(absl::string_view ref) { + absl::string_view fmt = DATE_FMT_RF3399_FULL; + if (19 == ref.size()) { + fmt = DATE_FMT_YMDHMS; + } else if (10 == ref.size()) { + fmt = DATE_FMT_YMD_1; + } else if (8 == ref.size()) { + fmt = DATE_FMT_YMD_2; + } + absl::Time tm; + std::string err; + bool ret = absl::ParseTime(fmt, ref, &tm, &err); + + if (!ret) { + return absl::InvalidArgumentError(err); + } + return tm; +} + +void date_diff(Date *date1, Date *date2, int32_t *diff, bool *is_null) { if (date1 == nullptr || date2 == nullptr || date1->date_ <= 0 || date2->date_ <= 0) { *is_null = true; return; @@ -838,36 +865,61 @@ void date_diff(Date *date1, Date *date2, int *diff, bool *is_null) { *is_null = false; } -void date_diff(StringRef *date1, StringRef *date2, int *diff, bool *is_null) { - Date d1; - string_to_date(date1, &d1, is_null); - if (*is_null) { +void date_diff(StringRef *date1, StringRef *date2, int32_t *diff, bool *is_null) { + auto t1 = string_to_time(absl::string_view(date1->data_, date1->size_)); + if (!t1.ok()) { + *is_null = true; return; } - Date d2; - string_to_date(date2, &d2, is_null); - if (*is_null) { + auto t2 = string_to_time(absl::string_view(date2->data_, date2->size_)); + if (!t2.ok()) { + *is_null = true; return; } - date_diff(&d1, &d2, diff, is_null); + + auto d1 = absl::ToCivilDay(t1.value(), DEFAULT_TZ); + auto d2 = absl::ToCivilDay(t2.value(), DEFAULT_TZ); + + *diff = d1 - d2; + *is_null = false; } -void date_diff(StringRef *date1, Date *date2, int *diff, bool *is_null) { - Date d1; - string_to_date(date1, &d1, is_null); - if (*is_null) { +void date_diff(StringRef *date1, Date *date2, int32_t *diff, bool *is_null) { + auto t1 = string_to_time(absl::string_view(date1->data_, date1->size_)); + if (!t1.ok()) { + *is_null = true; return; } - date_diff(&d1, date2, diff, is_null); + auto d1 = absl::ToCivilDay(t1.value(), DEFAULT_TZ); + + int32_t year, month, day; + if (!Date::Decode(date2->date_, &year, &month, &day)) { + *is_null = true; + return; + } + auto d2 = absl::CivilDay(year, month, day); + + *diff = d1 - d2; + *is_null = false; } -void date_diff(Date *date1, StringRef *date2, int *diff, bool *is_null) { - Date d2; - string_to_date(date2, &d2, is_null); - if (*is_null) { +void date_diff(Date *date1, StringRef *date2, int32_t *diff, bool *is_null) { + auto t2 = string_to_time(absl::string_view(date2->data_, date2->size_)); + if (!t2.ok()) { + *is_null = true; return; } - date_diff(date1, &d2, diff, is_null); + auto d2 = absl::ToCivilDay(t2.value(), DEFAULT_TZ); + + int32_t year, month, day; + if (!Date::Decode(date1->date_, &year, &month, &day)) { + *is_null = true; + return; + } + auto d1 = absl::CivilDay(year, month, day); + + *diff = d1 - d2; + *is_null = false; } // cast string to timestamp with yyyy-mm-dd or YYYY-mm-dd HH:MM:SS diff --git a/hybridse/src/udf/udf.h b/hybridse/src/udf/udf.h index c2f2d2dc6f0..a761e99f88b 100644 --- a/hybridse/src/udf/udf.h +++ b/hybridse/src/udf/udf.h @@ -367,10 +367,10 @@ void timestamp_to_date(Timestamp *timestamp, Date *output, bool *is_null); void date_to_string(Date *date, StringRef *output); -void date_diff(Date *date1, Date *date2, int *diff, bool *is_null); -void date_diff(StringRef *date1, StringRef *date2, int *diff, bool *is_null); -void date_diff(StringRef *date1, Date *date2, int *diff, bool *is_null); -void date_diff(Date *date1, StringRef *date2, int *diff, bool *is_null); +void date_diff(Date *date1, Date *date2, int32_t *diff, bool *is_null); +void date_diff(StringRef *date1, StringRef *date2, int32_t *diff, bool *is_null); +void date_diff(StringRef *date1, Date *date2, int32_t *diff, bool *is_null); +void date_diff(Date *date1, StringRef *date2, int32_t *diff, bool *is_null); void like(StringRef *name, StringRef *pattern, StringRef *escape, bool *out, bool *is_null); @@ -384,6 +384,8 @@ void regexp_like(StringRef *name, StringRef *pattern, bool *out, bool *is_null); void date_to_timestamp(Date *date, Timestamp *output, bool *is_null); void string_to_date(StringRef *str, Date *output, bool *is_null); +absl::StatusOr string_to_time(absl::string_view str); + void string_to_timestamp(StringRef *str, Timestamp *output, bool *is_null); void date_to_unix_timestamp(Date *date, int64_t *output, bool *is_null); void string_to_unix_timestamp(StringRef *str, int64_t *output, bool *is_null); From bcc0b1f4d633ea6c0c4ab9da182048913c1672c5 Mon Sep 17 00:00:00 2001 From: dl239 Date: Sun, 8 Oct 2023 10:26:53 +0800 Subject: [PATCH 45/63] docs: update version (#3517) --- CMakeLists.txt | 2 +- demo/Dockerfile | 2 +- java/hybridse-native/pom.xml | 2 +- java/hybridse-proto/pom.xml | 2 +- java/hybridse-sdk/pom.xml | 2 +- java/openmldb-batch/pom.xml | 2 +- java/openmldb-batchjob/pom.xml | 2 +- java/openmldb-common/pom.xml | 2 +- java/openmldb-jdbc/pom.xml | 2 +- java/openmldb-native/pom.xml | 2 +- java/openmldb-spark-connector/pom.xml | 2 +- java/openmldb-synctool/pom.xml | 2 +- java/openmldb-taskmanager/pom.xml | 2 +- java/pom.xml | 4 ++-- python/openmldb_sdk/setup.py | 2 +- python/openmldb_tool/setup.py | 2 +- test/integration-test/openmldb-test-python/install.sh | 2 +- 17 files changed, 18 insertions(+), 18 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a9f10095c38..21066a3c505 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,7 +36,7 @@ endif() message (STATUS "CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}") set(OPENMLDB_VERSION_MAJOR 0) set(OPENMLDB_VERSION_MINOR 8) -set(OPENMLDB_VERSION_BUG 2) +set(OPENMLDB_VERSION_BUG 3) function(get_commitid CODE_DIR COMMIT_ID) find_package(Git REQUIRED) diff --git a/demo/Dockerfile b/demo/Dockerfile index e6495931d35..6dc38d46c2b 100644 --- a/demo/Dockerfile +++ b/demo/Dockerfile @@ -25,7 +25,7 @@ COPY *_dist.yml /work/ ENV LANG=en_US.UTF-8 ENV SPARK_HOME=/work/openmldb/spark-3.2.1-bin-openmldbspark -ARG OPENMLDB_VERSION=0.8.2 +ARG OPENMLDB_VERSION=0.8.3 ENV OPENMLDB_VERSION="${OPENMLDB_VERSION}" RUN if [ "${USE_ADD_WHL}" = "true" ] ; then \ diff --git a/java/hybridse-native/pom.xml b/java/hybridse-native/pom.xml index e347b945b6c..629da3b0282 100644 --- a/java/hybridse-native/pom.xml +++ b/java/hybridse-native/pom.xml @@ -5,7 +5,7 @@ openmldb-parent com.4paradigm.openmldb - 0.8.2-SNAPSHOT + 0.8.3-SNAPSHOT ../pom.xml 4.0.0 diff --git a/java/hybridse-proto/pom.xml b/java/hybridse-proto/pom.xml index 83b28bdd95f..a78a0da61cf 100644 --- a/java/hybridse-proto/pom.xml +++ b/java/hybridse-proto/pom.xml @@ -4,7 +4,7 @@ openmldb-parent com.4paradigm.openmldb - 0.8.2-SNAPSHOT + 0.8.3-SNAPSHOT ../pom.xml 4.0.0 diff --git a/java/hybridse-sdk/pom.xml b/java/hybridse-sdk/pom.xml index e103c064280..ee37ccdf60f 100644 --- a/java/hybridse-sdk/pom.xml +++ b/java/hybridse-sdk/pom.xml @@ -6,7 +6,7 @@ openmldb-parent com.4paradigm.openmldb - 0.8.2-SNAPSHOT + 0.8.3-SNAPSHOT ../pom.xml 4.0.0 diff --git a/java/openmldb-batch/pom.xml b/java/openmldb-batch/pom.xml index a2a93ad4d3a..f2da581860d 100644 --- a/java/openmldb-batch/pom.xml +++ b/java/openmldb-batch/pom.xml @@ -7,7 +7,7 @@ openmldb-parent com.4paradigm.openmldb - 0.8.2-SNAPSHOT + 0.8.3-SNAPSHOT openmldb-batch diff --git a/java/openmldb-batchjob/pom.xml b/java/openmldb-batchjob/pom.xml index fddac8ffb82..408cb79e16d 100644 --- a/java/openmldb-batchjob/pom.xml +++ b/java/openmldb-batchjob/pom.xml @@ -7,7 +7,7 @@ openmldb-parent com.4paradigm.openmldb - 0.8.2-SNAPSHOT + 0.8.3-SNAPSHOT openmldb-batchjob diff --git a/java/openmldb-common/pom.xml b/java/openmldb-common/pom.xml index 8bd3e054cce..d42db9b7d54 100644 --- a/java/openmldb-common/pom.xml +++ b/java/openmldb-common/pom.xml @@ -5,7 +5,7 @@ openmldb-parent com.4paradigm.openmldb - 0.8.2-SNAPSHOT + 0.8.3-SNAPSHOT 4.0.0 openmldb-common diff --git a/java/openmldb-jdbc/pom.xml b/java/openmldb-jdbc/pom.xml index 3978579b373..d98f248d811 100644 --- a/java/openmldb-jdbc/pom.xml +++ b/java/openmldb-jdbc/pom.xml @@ -5,7 +5,7 @@ openmldb-parent com.4paradigm.openmldb - 0.8.2-SNAPSHOT + 0.8.3-SNAPSHOT ../pom.xml 4.0.0 diff --git a/java/openmldb-native/pom.xml b/java/openmldb-native/pom.xml index 8bd8a8399f3..b9ad048d640 100644 --- a/java/openmldb-native/pom.xml +++ b/java/openmldb-native/pom.xml @@ -5,7 +5,7 @@ openmldb-parent com.4paradigm.openmldb - 0.8.2-SNAPSHOT + 0.8.3-SNAPSHOT ../pom.xml 4.0.0 diff --git a/java/openmldb-spark-connector/pom.xml b/java/openmldb-spark-connector/pom.xml index 3f79972c60a..7431aae760f 100644 --- a/java/openmldb-spark-connector/pom.xml +++ b/java/openmldb-spark-connector/pom.xml @@ -6,7 +6,7 @@ openmldb-parent com.4paradigm.openmldb - 0.8.2-SNAPSHOT + 0.8.3-SNAPSHOT openmldb-spark-connector diff --git a/java/openmldb-synctool/pom.xml b/java/openmldb-synctool/pom.xml index a877d921eac..360f26e26be 100644 --- a/java/openmldb-synctool/pom.xml +++ b/java/openmldb-synctool/pom.xml @@ -6,7 +6,7 @@ openmldb-parent com.4paradigm.openmldb - 0.8.2-SNAPSHOT + 0.8.3-SNAPSHOT openmldb-synctool openmldb-synctool diff --git a/java/openmldb-taskmanager/pom.xml b/java/openmldb-taskmanager/pom.xml index 56aeef8b7c3..e983c4027e2 100644 --- a/java/openmldb-taskmanager/pom.xml +++ b/java/openmldb-taskmanager/pom.xml @@ -6,7 +6,7 @@ openmldb-parent com.4paradigm.openmldb - 0.8.2-SNAPSHOT + 0.8.3-SNAPSHOT openmldb-taskmanager openmldb-taskmanager diff --git a/java/pom.xml b/java/pom.xml index 3a46ab4d5c1..00c893a4201 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -7,7 +7,7 @@ openmldb-parent pom openmldb - 0.8.2-SNAPSHOT + 0.8.3-SNAPSHOT hybridse-sdk hybridse-native @@ -65,7 +65,7 @@ - 0.8.2-SNAPSHOT + 0.8.3-SNAPSHOT error 2.9.0 diff --git a/python/openmldb_sdk/setup.py b/python/openmldb_sdk/setup.py index 528250e1822..c96e2ae899b 100644 --- a/python/openmldb_sdk/setup.py +++ b/python/openmldb_sdk/setup.py @@ -18,7 +18,7 @@ setup( name='openmldb', - version='0.8.2a0', + version='0.8.3a0', author='OpenMLDB Team', author_email=' ', url='https://github.com/4paradigm/OpenMLDB', diff --git a/python/openmldb_tool/setup.py b/python/openmldb_tool/setup.py index 097b3c229c8..7b9a8dcf27f 100644 --- a/python/openmldb_tool/setup.py +++ b/python/openmldb_tool/setup.py @@ -18,7 +18,7 @@ setup( name="openmldb-tool", - version="0.8.2a0", + version='0.8.3a0', author="OpenMLDB Team", author_email=" ", url="https://github.com/4paradigm/OpenMLDB", diff --git a/test/integration-test/openmldb-test-python/install.sh b/test/integration-test/openmldb-test-python/install.sh index 9b16e934807..1f2babc873c 100644 --- a/test/integration-test/openmldb-test-python/install.sh +++ b/test/integration-test/openmldb-test-python/install.sh @@ -17,7 +17,7 @@ set -eE -x CURRENT_DIR=$(dirname "$0") -SPARK_VERSION=0.8.2 +SPARK_VERSION=0.8.3 pushd "${CURRENT_DIR}" cp -r ../../../openmldb ./ sed -i"" -e "s/OPENMLDB_MODE:=standalone/OPENMLDB_MODE:=cluster/g" openmldb/conf/openmldb-env.sh From 2224a6cef3e43be632367a1ad4803e94792c6064 Mon Sep 17 00:00:00 2001 From: HuangWei Date: Wed, 11 Oct 2023 16:07:41 +0800 Subject: [PATCH 46/63] build: thirdparty parallel compile option & fix centos6 build (#3492) * build: fix centos6 build * fix * fix * fix --- .github/workflows/other-os-build.yml | 46 +++++++++++++++++++--------- Makefile | 4 +-- docs/zh/deploy/compile.md | 22 ++++++++++--- steps/centos6_build.sh | 17 +++++++--- 4 files changed, 64 insertions(+), 25 deletions(-) diff --git a/.github/workflows/other-os-build.yml b/.github/workflows/other-os-build.yml index fcc99fb674b..aa63c3cc19a 100644 --- a/.github/workflows/other-os-build.yml +++ b/.github/workflows/other-os-build.yml @@ -78,41 +78,60 @@ jobs: shell: bash run: | cd /root/OpenMLDB + # centos6_build.sh will try build zetasql even cache hit, just ignore the failure IN_WORKFLOW=true bash steps/centos6_build.sh # bazel bin export PATH=$PATH:`pwd` source /opt/rh/devtoolset-8/enable if [[ "${USE_DEPS_CACHE}" != "true" ]]; then - echo "build thirdparty" - make thirdparty CMAKE_INSTALL_PREFIX=${OPENMLDB_PREFIX} BUILD_BUNDLE=ON NPROC=8 + echo "build thirdparty, make opt is better than nproc?" + make thirdparty CMAKE_INSTALL_PREFIX=${OPENMLDB_PREFIX} BUILD_BUNDLE=ON THIRD_PARTY_CMAKE_FLAGS=-DMAKEOPTS=-j8 + # 5.8G ./.deps, avail 8G rm -rf .deps/build # GitHub runner disk space is limited fi echo "build" + # 1.4G ./.deps, avail 13G + + # will failed if openmldb_sdk is on cmake -S . -B `pwd`/build -DCMAKE_PREFIX_PATH=`pwd`/.deps/usr -DCMAKE_BUILD_TYPE=RelWithDebInfo \ -DSQL_PYSDK_ENABLE=${SQL_PYSDK_ENABLE} -DSQL_JAVASDK_ENABLE=OFF \ -DTESTING_ENABLE=OFF -DCMAKE_INSTALL_PREFIX=${OPENMLDB_PREFIX} \ -DHYBRIDSE_TESTING_ENABLE=OFF -DEXAMPLES_ENABLE=OFF -DEXAMPLES_TESTING_ENABLE=OFF - cmake --build build --target install -- -j2 - # clean up to save disk space(~11G), don't know which is relative, build again in next step - rm -rf build + # target openmldb 6.7G ./build(no py/java), avail 5.2G + # openmldb+cp_python_sdk_so 7.7G ./build(has py), python just ~180M + # target 'install' cost more, preinstall/fast won't build all, so use install/fast if needed + # or https://cmake.org/cmake/help/latest/variable/CMAKE_SKIP_INSTALL_ALL_DEPENDENCY.html + cmake --build build --target openmldb cp_python_sdk_so -- -j2 + du -h --max-depth=1 + df -h + # if target above cost too much disk, make java build failed, try to rm build cache + # don't rm cache now cuz build java from emtpy will cost 20min + # rm build/hybridse build/src -rf if [[ "${SQL_JAVASDK_ENABLE}" == "ON" ]]; then echo "build java sdk" cmake -S . -B `pwd`/build -DCMAKE_PREFIX_PATH=`pwd`/.deps/usr -DCMAKE_BUILD_TYPE=RelWithDebInfo \ -DSQL_PYSDK_ENABLE=OFF -DSQL_JAVASDK_ENABLE=ON \ -DTESTING_ENABLE=OFF -DCMAKE_INSTALL_PREFIX=${OPENMLDB_PREFIX} \ -DHYBRIDSE_TESTING_ENABLE=OFF -DEXAMPLES_ENABLE=OFF -DEXAMPLES_TESTING_ENABLE=OFF - cmake --build build --target sql_javasdk_package -- -j2 + # if build the whole java, 7.6G ./build, 5.7G ./java, avail 331M + # so split it and build native only + # 7.6G ./build, 1.8G ./java, avail 5.2G + cmake --build build --target cp_native_so -- -j2 + du -h --max-depth=1 + df -h + rm build/hybridse build/src -rf + cd java + ./mvnw -pl openmldb-native clean package -DskipTests=true -Dscalatest.skip=true -Dwagon.skip=true -Dmaven.test.skip=true --batch-mode fi - - - name: package - run: | - tar czf ${{ env.OPENMLDB_PREFIX }}.tar.gz ${{ env.OPENMLDB_PREFIX }}/ + rm build/hybridse build/src -rf + du -h --max-depth=1 + df -h - name: upload binary uses: actions/upload-artifact@v2 with: - path: openmldb-*.tar.gz - name: binary-package + path: build/bin/openmldb + name: binary - name: upload java native if: ${{ env.SQL_JAVASDK_ENABLE == 'ON' }} @@ -127,8 +146,7 @@ jobs: with: name: python-whl path: | - python/openmldb_sdk/dist/openmldb*.whl - + python/openmldb_sdk/dist/openmldb*.whl # TODO(hw): upload cxx sdk # macos no need to build thirdparty, but binary/os needs to be built on each os diff --git a/Makefile b/Makefile index 74274755b4b..697b12923af 100644 --- a/Makefile +++ b/Makefile @@ -154,7 +154,7 @@ thirdparty-fast: if [ "$$new_zetasql_version" != "$(ZETASQL_VERSION)" ] ; then \ echo "[deps]: thirdparty up-to-date. reinstall zetasql from $(ZETASQL_VERSION) to $$new_zetasql_version"; \ $(MAKE) thirdparty-configure; \ - $(CMAKE_PRG) --build $(THIRD_PARTY_BUILD_DIR) -j $(NPROC) --target zetasql; \ + $(CMAKE_PRG) --build $(THIRD_PARTY_BUILD_DIR) --target zetasql; \ else \ echo "[deps]: all up-to-date. zetasql already installed with version: $(ZETASQL_VERSION)"; \ fi; \ @@ -166,7 +166,7 @@ thirdparty-fast: # third party compiled code install to 'OpenMLDB/.deps/usr', source code install to 'OpenMLDB/thirdsrc' thirdparty: thirdparty-configure - $(CMAKE_PRG) --build $(THIRD_PARTY_BUILD_DIR) -j $(NPROC) + $(CMAKE_PRG) --build $(THIRD_PARTY_BUILD_DIR) thirdparty-configure: $(CMAKE_PRG) -S third-party -B $(THIRD_PARTY_BUILD_DIR) -DSRC_INSTALL_DIR=$(THIRD_PARTY_SRC_DIR) -DDEPS_INSTALL_DIR=$(THIRD_PARTY_DIR) $(THIRD_PARTY_CMAKE_FLAGS) diff --git a/docs/zh/deploy/compile.md b/docs/zh/deploy/compile.md index aec38f6a5a3..896009ca2ce 100644 --- a/docs/zh/deploy/compile.md +++ b/docs/zh/deploy/compile.md @@ -110,7 +110,7 @@ make CMAKE_BUILD_TYPE=Debug - CMAKE_EXTRA_FLAGS: 传递给 cmake 的额外参数 - 默认: ‘’ + 默认: '' - BUILD_BUNDLED: 从源码编译 thirdparty 依赖,而不是下载预编译包 @@ -124,6 +124,9 @@ make CMAKE_BUILD_TYPE=Debug 默认: all +- THIRD_PARTY_CMAKE_FLAGS: 编译thirdparty时可以配置额外参数。例如,配置每个thirdparty项目并发编译,`THIRD_PARTY_CMAKE_FLAGS=-DMAKEOPTS=-j8`。thirdparty不受NPROC影响,thirdparty的多项目将会串行执行。 + 默认:'' + ### 并发编译Java SDK ``` @@ -185,14 +188,25 @@ docker run -it -v`pwd`:/root/OpenMLDB ghcr.io/4paradigm/centos6_gcc7_hybridsql b ```bash cd OpenMLDB bash steps/centos6_build.sh +# THIRD_PARTY_CMAKE_FLAGS=-DMAKEOPTS=-j8 bash steps/centos6_build.sh # run fast when build single project # OPENMLDB_SOURCE=true bash steps/centos6_build.sh -# SQL_JAVASDK_ENABLE=ON SQL_PYSDK_ENABLE=ON NRPOC=8 bash steps/centos6_build.sh +# SQL_JAVASDK_ENABLE=ON SQL_PYSDK_ENABLE=ON NPROC=8 bash steps/centos6_build.sh # NPROC will build openmldb in parallel, thirdparty should use THIRD_PARTY_CMAKE_FLAGS ``` +本地2.20GHz CPU,SSD硬盘,32线程编译三方库与OpenMLDB主体,耗时参考: +`THIRD_PARTY_CMAKE_FLAGS=-DMAKEOPTS=-j32 SQL_JAVASDK_ENABLE=ON SQL_PYSDK_ENABLE=ON NPROC=32 bash steps/centos6_build.sh` +- thirdparty(不包括下载src时间)~40m:zetasql打patch 13m,所有thirdparty编译30m +- OpenMLDB 本体,包括python和java native,~12min + #### 云编译 -Fork OpenMLDB仓库后,可以使用在`Actions`中触发workflow `Other OS Build`,编译产出在`Actions`的`Artifacts`中。workflow 配置 `os name`为`centos6`, -如果不需要Java或Python SDK,可配置`java sdk enable`或`python sdk enable`为`OFF`,节约编译时间。 +Fork OpenMLDB仓库后,可以使用在`Actions`中触发workflow `Other OS Build`,编译产出在`Actions`的`Artifacts`中。workflow 配置方式: +- 不要更换`Use workflow from`为某个tag,可以是其他分支。 +- 选择`os name`为`centos6`。 +- 如果不是编译main分支,在`The branch, tag or SHA to checkout, otherwise use the branch`中填写想要的分支名、Tag(e.g. v0.8.2)或SHA。 +- 编译产出在触发后的runs界面中,参考[成功产出的runs链接](https://github.com/4paradigm/OpenMLDB/actions/runs/6044951902)。 + - 一定会产出openmldb binary文件。 + - 如果不需要Java或Python SDK,可配置`java sdk enable`或`python sdk enable`为`OFF`,节约编译时间。 此编译流程需要从源码编译thirdparty,且资源较少,无法开启较高的并发编译。因此编译时间较长,大约需要3h5m(2h thirdparty+1h OpenMLDB)。workflow会缓存thirdparty的编译产出,因此第二次编译会快很多(1h15m OpenMLDB)。 diff --git a/steps/centos6_build.sh b/steps/centos6_build.sh index a5614b10693..b5cee80838f 100644 --- a/steps/centos6_build.sh +++ b/steps/centos6_build.sh @@ -19,6 +19,7 @@ set +x # self build or workflow IN_WORKFLOW=${IN_WORKFLOW:-"false"} +USE_DEPS_CACHE=${USE_DEPS_CACHE:-"false"} # if download from openmldb.ai OPENMLDB_SOURCE=${OPENMLDB_SOURCE:-"false"} @@ -36,6 +37,13 @@ function tool_install() { chmod +x bazel } +if [ "$USE_DEPS_CACHE" == "true" ]; then + echo "use deps cache, exit" + exit 0 +else + echo "not use deps cache, install tools and build deps" +fi + if [ "$IN_WORKFLOW" == "true" ]; then echo "in workflow" else @@ -56,13 +64,12 @@ echo "add patch in fetch cmake" sed -i'' '34s/WITH_TOOLS=OFF$/WITH_TOOLS=OFF -DWITH_CORE_TOOLS=OFF/' third-party/cmake/FetchRocksDB.cmake # if BUILD_BUNDLED=OFF will download pre-built thirdparty, not good -# so we use cmake to build zetasql only -# it's hard to avoid add zetasql patch twice, so we check the dir to avoid -if [ -d ".deps/build/src/zetasql" ]; then - echo "zetasql already exists, skip add patch, if you want, rm .deps/build/src/zetasql* -rf" +# so we use cmake to build zetasql only and add patch to it +# it's hard to avoid add zetasql patch twice, so we check the stamp to avoid +if [ -e ".deps/build/src/zetasql-stamp/zetasql-build" ]; then + echo "zetasql already exists, skip add patch, if you want, rm .deps/build/src/zetasql-stamp/zetasql-build or whole .deps/build/src/zetasql*" else echo "modify in .deps needs a make first, download&build zetasql first(build will fail)" - # sed -i'' '31s/${BUILD_BUNDLED}/ON/' third-party/CMakeLists.txt cmake -S third-party -B "$(pwd)"/.deps -DSRC_INSTALL_DIR="$(pwd)"/thirdsrc -DDEPS_INSTALL_DIR="$(pwd)"/.deps/usr -DBUILD_BUNDLED=ON cmake --build "$(pwd)"/.deps --target zetasql echo "add patch in .deps zetasql" From 1386632befd9a49191965df9dbc2cc5d649ac852 Mon Sep 17 00:00:00 2001 From: HuangWei Date: Wed, 11 Oct 2023 16:12:50 +0800 Subject: [PATCH 47/63] feat: check table status when CLI login (#3506) * feat: check table status when CLI login * fix --- src/cmd/sql_cmd.h | 49 +++++++++++++++++++++++++++++++++-- src/sdk/sql_cluster_router.cc | 2 +- 2 files changed, 48 insertions(+), 3 deletions(-) diff --git a/src/cmd/sql_cmd.h b/src/cmd/sql_cmd.h index 5cf1b99cd0c..2d941c65a35 100644 --- a/src/cmd/sql_cmd.h +++ b/src/cmd/sql_cmd.h @@ -141,14 +141,59 @@ std::string ExecFetch(const std::string& sql) { return ss.str(); } -void HandleSQL(const std::string& sql) { - std::cout << ExecFetch(sql); +void HandleSQL(const std::string& sql) { std::cout << ExecFetch(sql); } + +std::string SafeGetString(std::shared_ptr rs, int idx) { + std::string tmp; + if (!rs->GetString(idx, &tmp)) { + LOG(WARNING) << "fail to get string in col " << idx; + return ""; + } + return tmp; +} + +bool CheckAllTableStatus() { + hybridse::sdk::Status status; + auto rs = sr->ExecuteSQL("show table status like '%'", &status); + bool is_ok = true; + if (status.IsOK()) { + // ref GetTableStatusSchema, just use idx to get column + while (rs->Next()) { + auto table = SafeGetString(rs, 1); + auto db = SafeGetString(rs, 2); + auto pu = SafeGetString(rs, 8); + auto warnings = SafeGetString(rs, 13); + std::string msg = absl::StrCat("table ", db, ".", table, " is broken, `show table status` to check detail"); + bool is_broken = false; + if (pu != "0") { + is_broken = true; + msg.append(", unalive partition: ").append(pu); + } + if (!warnings.empty()) { + is_broken = true; + msg.append(", warning preview: ").append(warnings.substr(0, 100)); + } + if (is_broken) { + is_ok = false; + std::cout << "ERROR: " << msg << std::endl; + } + } + } else { + std::cout << "ERROR: fail to get all table status, " << status.ToString() << std::endl; + is_ok = false; + } + return is_ok; } // cluster mode if zk_cluster is not empty, otherwise standalone mode void Shell() { DCHECK(cs); DCHECK(sr); + // before all, check all table status + if (!CheckAllTableStatus()) { + std::cout << "HINT: Use `openmldb_tool inspect` to get full report." << std::endl; + } + // If use FLAGS_cmd, non-interactive. No Logo and make sure router interactive is false if (!FLAGS_cmd.empty()) { std::string db = FLAGS_database; diff --git a/src/sdk/sql_cluster_router.cc b/src/sdk/sql_cluster_router.cc index 296cd3d5755..eea62b508ff 100644 --- a/src/sdk/sql_cluster_router.cc +++ b/src/sdk/sql_cluster_router.cc @@ -4279,7 +4279,7 @@ static const std::initializer_list GetTableStatusSchema() { // 1. memory: binlog + snapshot // 2. SSD/HDD: binlog + rocksdb data (sst files), wal files and checkpoints are not included // - Partition: partition number -// - partition_unalive: partition number that is unalive +// - Partition_unalive: partition number that is unalive // - Replica: replica number // - Offline_path: data path for offline data // - Offline_format: format for offline data From 34377414f89eb475eef6f2ea3b62c062300c7e0c Mon Sep 17 00:00:00 2001 From: HuangWei Date: Wed, 11 Oct 2023 17:21:59 +0800 Subject: [PATCH 48/63] fix: add deploy bias on demo and docs (#3520) --- demo/byzer-taxi/openmldb_byzer_taxi.bznb | 2 +- .../demo/src/main/java/com/openmldb/demo/App.java | 2 +- demo/jd-recommendation/sql_scripts/deploy.sql | 2 +- demo/predict-taxi-trip-duration/README.md | 4 ++-- demo/predict-taxi-trip-duration/script/taxi.sql | 2 +- .../train_and_serve.ipynb | 2 +- .../train_and_serve.py | 3 ++- docs/en/use_case/lightgbm_demo.md | 2 +- .../deploy_integration/OpenMLDB_Byzer_taxi.md | 2 +- .../openmldb_sql/deployment_manage/DEPLOY_STATEMENT.md | 10 ++++++++-- docs/zh/quickstart/sdk/java_sdk.md | 2 +- docs/zh/use_case/JD_recommendation.md | 2 +- docs/zh/use_case/taxi_tour_duration_prediction.md | 6 +++++- 13 files changed, 26 insertions(+), 15 deletions(-) diff --git a/demo/byzer-taxi/openmldb_byzer_taxi.bznb b/demo/byzer-taxi/openmldb_byzer_taxi.bznb index dc1c925cb0f..b4835f7cc85 100644 --- a/demo/byzer-taxi/openmldb_byzer_taxi.bznb +++ b/demo/byzer-taxi/openmldb_byzer_taxi.bznb @@ -64,7 +64,7 @@ "job_id" : null }, { "id" : "240", - "content" : "run command as FeatureStoreExt.`` where\r\nzkAddress=\"127.0.0.1:2181\"\r\nand zkPath=\"/openmldb\"\r\nand `sql-0`='''\r\nSET @@execute_mode='online';\r\n'''\r\nand `sql-1`='''\r\nDEPLOY d1 SELECT trip_duration, passenger_count,\r\nsum(pickup_latitude) OVER w AS vendor_sum_pl,\r\nmax(pickup_latitude) OVER w AS vendor_max_pl,\r\nmin(pickup_latitude) OVER w AS vendor_min_pl,\r\navg(pickup_latitude) OVER w AS vendor_avg_pl,\r\nsum(pickup_latitude) OVER w2 AS pc_sum_pl,\r\nmax(pickup_latitude) OVER w2 AS pc_max_pl,\r\nmin(pickup_latitude) OVER w2 AS pc_min_pl,\r\navg(pickup_latitude) OVER w2 AS pc_avg_pl ,\r\ncount(vendor_id) OVER w2 AS pc_cnt,\r\ncount(vendor_id) OVER w AS vendor_cnt\r\nFROM t1 \r\nWINDOW w AS (PARTITION BY vendor_id ORDER BY pickup_datetime ROWS_RANGE BETWEEN 1d PRECEDING AND CURRENT ROW),\r\nw2 AS (PARTITION BY passenger_count ORDER BY pickup_datetime ROWS_RANGE BETWEEN 1d PRECEDING AND CURRENT ROW);\r\n'''\r\nand db=\"db1\"\r\nand action=\"ddl\";", + "content" : "run command as FeatureStoreExt.`` where\r\nzkAddress=\"127.0.0.1:2181\"\r\nand zkPath=\"/openmldb\"\r\nand `sql-0`='''\r\nSET @@execute_mode='online';\r\n'''\r\nand `sql-1`='''\r\nDEPLOY d1 OPTIONS(RANGE_BIAS="inf", ROWS_BIAS="inf") SELECT trip_duration, passenger_count,\r\nsum(pickup_latitude) OVER w AS vendor_sum_pl,\r\nmax(pickup_latitude) OVER w AS vendor_max_pl,\r\nmin(pickup_latitude) OVER w AS vendor_min_pl,\r\navg(pickup_latitude) OVER w AS vendor_avg_pl,\r\nsum(pickup_latitude) OVER w2 AS pc_sum_pl,\r\nmax(pickup_latitude) OVER w2 AS pc_max_pl,\r\nmin(pickup_latitude) OVER w2 AS pc_min_pl,\r\navg(pickup_latitude) OVER w2 AS pc_avg_pl ,\r\ncount(vendor_id) OVER w2 AS pc_cnt,\r\ncount(vendor_id) OVER w AS vendor_cnt\r\nFROM t1 \r\nWINDOW w AS (PARTITION BY vendor_id ORDER BY pickup_datetime ROWS_RANGE BETWEEN 1d PRECEDING AND CURRENT ROW),\r\nw2 AS (PARTITION BY passenger_count ORDER BY pickup_datetime ROWS_RANGE BETWEEN 1d PRECEDING AND CURRENT ROW);\r\n'''\r\nand db=\"db1\"\r\nand action=\"ddl\";", "job_id" : null }, { "id" : "241", diff --git a/demo/java_quickstart/demo/src/main/java/com/openmldb/demo/App.java b/demo/java_quickstart/demo/src/main/java/com/openmldb/demo/App.java index 2923832d3b8..cbe363f4359 100644 --- a/demo/java_quickstart/demo/src/main/java/com/openmldb/demo/App.java +++ b/demo/java_quickstart/demo/src/main/java/com/openmldb/demo/App.java @@ -146,7 +146,7 @@ private void createDeployment() { "(PARTITION BY %s.c1 ORDER BY %s.c7 ROWS_RANGE BETWEEN 2d PRECEDING AND CURRENT ROW);", table, table, table); // 上线一个Deployment - String deploySql = String.format("DEPLOY %s %s", deploymentName, selectSql); + String deploySql = String.format("DEPLOY %s OPTIONS(RANGE_BIAS='inf', ROWS_BIAS='inf') %s", deploymentName, selectSql); // set return null rs, don't check the returned value, it's false state.execute(deploySql); } catch (Exception e) { diff --git a/demo/jd-recommendation/sql_scripts/deploy.sql b/demo/jd-recommendation/sql_scripts/deploy.sql index 7cb2121e869..e37408b6396 100644 --- a/demo/jd-recommendation/sql_scripts/deploy.sql +++ b/demo/jd-recommendation/sql_scripts/deploy.sql @@ -1,6 +1,6 @@ USE JD_db; SET @@execute_mode='online'; -DEPLOY demo select * from +DEPLOY demo OPTIONS(RANGE_BIAS='inf', ROWS_BIAS='inf') select * from ( select `reqId` as reqId_1, diff --git a/demo/predict-taxi-trip-duration/README.md b/demo/predict-taxi-trip-duration/README.md index bd44778c2a3..a35f2eb9363 100644 --- a/demo/predict-taxi-trip-duration/README.md +++ b/demo/predict-taxi-trip-duration/README.md @@ -85,7 +85,7 @@ python3 train.py /tmp/feature_data /tmp/model.txt # The below commands are executed in the CLI > USE demo_db; > SET @@execute_mode='online'; -> DEPLOY demo SELECT trip_duration, passenger_count, +> DEPLOY demo OPTIONS(RANGE_BIAS="inf", ROWS_BIAS="inf") SELECT trip_duration, passenger_count, sum(pickup_latitude) OVER w AS vendor_sum_pl, max(pickup_latitude) OVER w AS vendor_max_pl, min(pickup_latitude) OVER w AS vendor_min_pl, @@ -193,7 +193,7 @@ python3 train.py /tmp/feature.csv /tmp/model.txt ```sql # The below commands are executed in the CLI > USE demo_db; -> DEPLOY demo SELECT trip_duration, passenger_count, +> DEPLOY demo OPTIONS(RANGE_BIAS="inf", ROWS_BIAS="inf") SELECT trip_duration, passenger_count, sum(pickup_latitude) OVER w AS vendor_sum_pl, max(pickup_latitude) OVER w AS vendor_max_pl, min(pickup_latitude) OVER w AS vendor_min_pl, diff --git a/demo/predict-taxi-trip-duration/script/taxi.sql b/demo/predict-taxi-trip-duration/script/taxi.sql index bbdd219b2e5..8ade33df870 100644 --- a/demo/predict-taxi-trip-duration/script/taxi.sql +++ b/demo/predict-taxi-trip-duration/script/taxi.sql @@ -22,7 +22,7 @@ w2 AS (PARTITION BY passenger_count ORDER BY pickup_datetime ROWS_RANGE BETWEEN OPTIONS(mode='overwrite'); SET @@execute_mode='online'; -DEPLOY demo SELECT trip_duration, passenger_count, +DEPLOY demo OPTIONS(RANGE_BIAS="inf", ROWS_BIAS="inf") SELECT trip_duration, passenger_count, sum(pickup_latitude) OVER w AS vendor_sum_pl, max(pickup_latitude) OVER w AS vendor_max_pl, min(pickup_latitude) OVER w AS vendor_min_pl, diff --git a/demo/talkingdata-adtracking-fraud-detection/train_and_serve.ipynb b/demo/talkingdata-adtracking-fraud-detection/train_and_serve.ipynb index 6a7c71ff412..b3b01306588 100644 --- a/demo/talkingdata-adtracking-fraud-detection/train_and_serve.ipynb +++ b/demo/talkingdata-adtracking-fraud-detection/train_and_serve.ipynb @@ -187,7 +187,7 @@ "outputs": [], "source": [ "deploy_name='d1'\n", - "%sql DEPLOY $deploy_name $sql_part;" + "%sql DEPLOY $deploy_name OPTIONS(RANGE_BIAS=\"inf\", ROWS_BIAS=\"inf\") $sql_part;" ] }, { diff --git a/demo/talkingdata-adtracking-fraud-detection/train_and_serve.py b/demo/talkingdata-adtracking-fraud-detection/train_and_serve.py index 9cdd93d2074..a592edfdb0e 100644 --- a/demo/talkingdata-adtracking-fraud-detection/train_and_serve.py +++ b/demo/talkingdata-adtracking-fraud-detection/train_and_serve.py @@ -166,7 +166,8 @@ def nothrow_execute(sql): connection.execute("SET @@execute_mode='online';") connection.execute(f'USE {DB_NAME}') nothrow_execute(f'DROP DEPLOYMENT {DEPLOY_NAME}') -deploy_sql = f"""DEPLOY {DEPLOY_NAME} {sql_part}""" +# to avoid data expired by abs ttl, set inf +deploy_sql = f"""DEPLOY {DEPLOY_NAME} OPTIONS(RANGE_BIAS="inf", ROWS_BIAS="inf") {sql_part}""" print(deploy_sql) connection.execute(deploy_sql) print('Import data to online') diff --git a/docs/en/use_case/lightgbm_demo.md b/docs/en/use_case/lightgbm_demo.md index f4e602373a6..80a3d98ba98 100644 --- a/docs/en/use_case/lightgbm_demo.md +++ b/docs/en/use_case/lightgbm_demo.md @@ -152,7 +152,7 @@ Assuming that the model produced by the features designed in Section 2.3 in the ```sql > USE demo_db; > SET @@execute_mode='online'; -> DEPLOY demo SELECT trip_duration, passenger_count, +> DEPLOY demo OPTIONS(RANGE_BIAS='inf', ROWS_BIAS='inf') SELECT trip_duration, passenger_count, sum(pickup_latitude) OVER w AS vendor_sum_pl, max(pickup_latitude) OVER w AS vendor_max_pl, min(pickup_latitude) OVER w AS vendor_min_pl, diff --git a/docs/zh/integration/deploy_integration/OpenMLDB_Byzer_taxi.md b/docs/zh/integration/deploy_integration/OpenMLDB_Byzer_taxi.md index 926c079469d..9250d593341 100644 --- a/docs/zh/integration/deploy_integration/OpenMLDB_Byzer_taxi.md +++ b/docs/zh/integration/deploy_integration/OpenMLDB_Byzer_taxi.md @@ -232,7 +232,7 @@ and `sql-0`=''' SET @@execute_mode='online'; ''' and `sql-1`=''' -DEPLOY d1 SELECT trip_duration, passenger_count, +DEPLOY d1 OPTIONS(RANGE_BIAS='inf', ROWS_BIAS='inf') SELECT trip_duration, passenger_count, sum(pickup_latitude) OVER w AS vendor_sum_pl, max(pickup_latitude) OVER w AS vendor_max_pl, min(pickup_latitude) OVER w AS vendor_min_pl, diff --git a/docs/zh/openmldb_sql/deployment_manage/DEPLOY_STATEMENT.md b/docs/zh/openmldb_sql/deployment_manage/DEPLOY_STATEMENT.md index 4f94e228357..41b3e1141e8 100644 --- a/docs/zh/openmldb_sql/deployment_manage/DEPLOY_STATEMENT.md +++ b/docs/zh/openmldb_sql/deployment_manage/DEPLOY_STATEMENT.md @@ -175,9 +175,9 @@ deploy demo options(SYNC="false") SELECT t1.col1, t2.col2, sum(col4) OVER w1 as WINDOW w1 AS (PARTITION BY t1.col2 ORDER BY t1.col3 ROWS BETWEEN 2 PRECEDING AND CURRENT ROW); ``` -#### 设置偏移 +#### 设置偏移BIAS -如果你并不希望数据根据deploy的索引淘汰,或者希望晚一点淘汰,可以在deploy时设置偏移,常用于数据时间戳并不实时的情况、测试等情况。如果deploy后的索引ttl为abs 3h,但是数据的时间戳是3h前的(以系统时间为基准),那么这条数据就会被淘汰,无法参与计算。设置一定时间或永久的偏移,则可以让数据更久的停留在在线表中。 +如果你并不希望数据根据deploy的索引淘汰,或者希望晚一点淘汰,可以在deploy时设置偏移BIAS,常用于数据时间戳并不实时的情况、测试等情况。如果deploy后的索引ttl为abs 3h,但是数据的时间戳是3h前的(以系统时间为基准),那么这条数据就会被淘汰,无法参与计算。设置一定时间或永久的偏移,则可以让数据更久的停留在在线表中。 时间偏移,单位可以是`s`、`m`、`h`、`d`,也可以是整数,单位为`ms`,也可以是`inf`,表示永不淘汰;如果是行数偏移,可以是整数,单位是`row`,也可以是`inf`,表示永不淘汰。两种偏移中,0均表示不偏移。 @@ -185,6 +185,12 @@ deploy demo options(SYNC="false") SELECT t1.col1, t2.col2, sum(col4) OVER w1 as 而时间偏移的单位是`min`,我们会在内部将其转换为`min`,并且取上界。比如,新索引ttl是abs 2min,加上偏移20s,结果是`2min + ub(20s) = 3min`,然后和旧索引1min取上界,最终索引ttl是`max(1min, 3min) = 3min`。 +**Example** +```sql +DEPLOY demo OPTIONS(RANGE_BIAS="inf", ROWS_BIAS="inf") SELECT t1.col1, t2.col2, sum(col4) OVER w1 as w1_col4_sum FROM t1 LAST JOIN t2 ORDER BY t2.col3 ON t1.col2 = t2.col2 + WINDOW w1 AS (PARTITION BY t1.col2 ORDER BY t1.col3 ROWS BETWEEN 2 PRECEDING AND CURRENT ROW); +``` + ## 相关SQL [USE DATABASE](../ddl/USE_DATABASE_STATEMENT.md) diff --git a/docs/zh/quickstart/sdk/java_sdk.md b/docs/zh/quickstart/sdk/java_sdk.md index 966d50db785..166b44adb8e 100644 --- a/docs/zh/quickstart/sdk/java_sdk.md +++ b/docs/zh/quickstart/sdk/java_sdk.md @@ -403,7 +403,7 @@ try { "(PARTITION BY %s.c1 ORDER BY %s.c7 ROWS_RANGE BETWEEN 2d PRECEDING AND CURRENT ROW);", table, table, table); // 上线一个Deployment - String deploySql = String.format("DEPLOY %s %s", deploymentName, selectSql); + String deploySql = String.format("DEPLOY %s OPTIONS(RANGE_BIAS='inf', ROWS_BIAS='inf') %s", deploymentName, selectSql); // set return null rs, don't check the returned value, it's false state.execute(deploySql); } catch (Exception e) { diff --git a/docs/zh/use_case/JD_recommendation.md b/docs/zh/use_case/JD_recommendation.md index d4035be912a..cb4ce603059 100644 --- a/docs/zh/use_case/JD_recommendation.md +++ b/docs/zh/use_case/JD_recommendation.md @@ -393,7 +393,7 @@ bash train_deepfm.sh $demodir/feature_preprocess/out ```sql -- OpenMLDB CLI USE JD_db; - DEPLOY demo ; + DEPLOY demo OPTIONS(RANGE_BIAS='inf', ROWS_BIAS='inf') ; ``` 也可以在 Docker 容器内直接运行部署脚本: diff --git a/docs/zh/use_case/taxi_tour_duration_prediction.md b/docs/zh/use_case/taxi_tour_duration_prediction.md index faaff3bf922..dfb84de28da 100644 --- a/docs/zh/use_case/taxi_tour_duration_prediction.md +++ b/docs/zh/use_case/taxi_tour_duration_prediction.md @@ -151,7 +151,7 @@ w2 AS (PARTITION BY passenger_count ORDER BY pickup_datetime ROWS_RANGE BETWEEN --OpenMLDB CLI USE demo_db; SET @@execute_mode='online'; - DEPLOY demo SELECT trip_duration, passenger_count, + DEPLOY demo OPTIONS(RANGE_BIAS='inf', ROWS_BIAS='inf') SELECT trip_duration, passenger_count, sum(pickup_latitude) OVER w AS vendor_sum_pl, max(pickup_latitude) OVER w AS vendor_max_pl, min(pickup_latitude) OVER w AS vendor_min_pl, @@ -167,6 +167,10 @@ w2 AS (PARTITION BY passenger_count ORDER BY pickup_datetime ROWS_RANGE BETWEEN w2 AS (PARTITION BY passenger_count ORDER BY pickup_datetime ROWS_RANGE BETWEEN 1d PRECEDING AND CURRENT ROW); ``` +```{note} +此处DEPLOY包含BIAS OPTIONS,是因为导入在线存储的数据文件不会更新,对于当前时间来讲,可能会超过DEPLOY后的表索引的时间TTL,导致表淘汰掉这些数据。时间淘汰,只看每个索引的ts列和ttl,只要数据中该列的值<(当前时间-abs_ttl),在该索引上就会被淘汰,与其他因素无关,各个索引也互相不影响。如果你的数据不是实时产生的新timestamp,也需要考虑带上BIAS OPTIONS。 +``` + ### 步骤 7:导入在线数据 首先,请切换到**在线**执行模式。接着在在线模式下,导入样例数据 `/work/taxi-trip/data/taxi_tour_table_train_simple.csv` 作为在线数据,用于在线特征计算。 From 72a49d8b4d872016c54f5d837d3ad1a991aaee32 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 12 Oct 2023 10:04:24 +0800 Subject: [PATCH 49/63] docs(udf): upgrade udf list (#3541) Co-authored-by: aceforeverd --- .../reference/sql/functions_and_operators/Files/udfs_8h.md | 7 ++++++- .../openmldb_sql/functions_and_operators/Files/udfs_8h.md | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/docs/en/reference/sql/functions_and_operators/Files/udfs_8h.md b/docs/en/reference/sql/functions_and_operators/Files/udfs_8h.md index ac96c6bfc3f..d1696b6c764 100644 --- a/docs/en/reference/sql/functions_and_operators/Files/udfs_8h.md +++ b/docs/en/reference/sql/functions_and_operators/Files/udfs_8h.md @@ -1178,7 +1178,12 @@ Supported date string style: * yyyy-mm-dd * yyyymmdd -* yyyy-mm-dd hh:mm:ss +* yyyy-mm-dd HH:MM:SS +* yyyy-mm-ddTHH:MM:SS.fff+HH:MM (RFC3399 format) + +Dates from string are transformed into the same time zone (which is currently always UTC+8) before differentiation, dates from date type by default is at UTC+8, you may see a +1/-1 difference if the two date string have different time zones. + +Hint: since openmldb date type limits range from year 1900, to datadiff from/to a date before 1900, pass it as string. Example: diff --git a/docs/zh/openmldb_sql/functions_and_operators/Files/udfs_8h.md b/docs/zh/openmldb_sql/functions_and_operators/Files/udfs_8h.md index ac96c6bfc3f..d1696b6c764 100644 --- a/docs/zh/openmldb_sql/functions_and_operators/Files/udfs_8h.md +++ b/docs/zh/openmldb_sql/functions_and_operators/Files/udfs_8h.md @@ -1178,7 +1178,12 @@ Supported date string style: * yyyy-mm-dd * yyyymmdd -* yyyy-mm-dd hh:mm:ss +* yyyy-mm-dd HH:MM:SS +* yyyy-mm-ddTHH:MM:SS.fff+HH:MM (RFC3399 format) + +Dates from string are transformed into the same time zone (which is currently always UTC+8) before differentiation, dates from date type by default is at UTC+8, you may see a +1/-1 difference if the two date string have different time zones. + +Hint: since openmldb date type limits range from year 1900, to datadiff from/to a date before 1900, pass it as string. Example: From 15a7d46dd5704421c037d5590d3bee96683df93f Mon Sep 17 00:00:00 2001 From: TanZiYen <104113819+TanZiYen@users.noreply.github.com> Date: Thu, 12 Oct 2023 13:42:33 +0800 Subject: [PATCH 50/63] docs: change_for_airflow_provider_demo_of_integration_folder (#3467) --- .../airflow_provider_demo.md | 145 ++++++++++++++++++ .../images/add_connection.png | Bin 0 -> 32687 bytes .../deploy_integration/images/airflow_dag.png | Bin 0 -> 303427 bytes .../images/airflow_login.png | Bin 0 -> 42118 bytes .../images/airflow_test_result.png | Bin 0 -> 25328 bytes .../deploy_integration/images/connection.png | Bin 0 -> 157110 bytes .../images/connection_display.png | Bin 0 -> 21200 bytes .../images/connection_settings.png | Bin 0 -> 188000 bytes .../deploy_integration/images/dag_code.png | Bin 0 -> 51039 bytes .../deploy_integration/images/dag_home.png | Bin 0 -> 64185 bytes .../deploy_integration/images/dag_run.png | Bin 0 -> 789458 bytes .../airflow_provider_demo.md | 24 +-- 12 files changed, 157 insertions(+), 12 deletions(-) create mode 100644 docs/en/integration/deploy_integration/airflow_provider_demo.md create mode 100644 docs/en/integration/deploy_integration/images/add_connection.png create mode 100644 docs/en/integration/deploy_integration/images/airflow_dag.png create mode 100644 docs/en/integration/deploy_integration/images/airflow_login.png create mode 100644 docs/en/integration/deploy_integration/images/airflow_test_result.png create mode 100644 docs/en/integration/deploy_integration/images/connection.png create mode 100644 docs/en/integration/deploy_integration/images/connection_display.png create mode 100644 docs/en/integration/deploy_integration/images/connection_settings.png create mode 100644 docs/en/integration/deploy_integration/images/dag_code.png create mode 100644 docs/en/integration/deploy_integration/images/dag_home.png create mode 100644 docs/en/integration/deploy_integration/images/dag_run.png diff --git a/docs/en/integration/deploy_integration/airflow_provider_demo.md b/docs/en/integration/deploy_integration/airflow_provider_demo.md new file mode 100644 index 00000000000..3f741cf4c61 --- /dev/null +++ b/docs/en/integration/deploy_integration/airflow_provider_demo.md @@ -0,0 +1,145 @@ +# Airflow +We provide [Airflow OpenMLDB Provider](https://github.com/4paradigm/OpenMLDB/tree/main/extensions/airflow-provider-openmldb), which facilitates the integration of OpenMLDB with Airflow DAG. + +This specific case will undergo training and execution with Airflow's [TalkingData](https://chat.openai.com/talkingdata_demo). + +## TalkingData DAG + +To implement this workflow in Airflow, a DAG (Directed Acyclic Graph) file needs to be written. Here we use an example DAG file in [example_openmldb_complex.py](https://github.com/4paradigm/OpenMLDB/blob/main/extensions/airflow-provider-openmldb/openmldb_provider/example_dags/example_openmldb_complex.py). + +![airflow dag](images/airflow_dag.png) + +The diagram above illustrates the work process in DAG. It begins by creating a table, followed by offline data loading, feature extraction, and model training. If the model trained performs well (AUC >= 99.0), the workflow proceeds to execute deploy SQL and model serving online. Otherwise, a failure report is generated. + +In the following demonstration, you can directly import this DAG and run in Airflow. + + +## Demonstration + +We import the above DAG to perform feature computation and deployment for the TalkingData Demo, then perform real-time inference using the predict server of TalkingData Demo. + +### Preparation + +#### Download DAG + +Along with the DAG files, training scripts are also required. For convenience, we provide the [code package](https://openmldb.ai/download/airflow_demo/airflow_demo_files.tar.gz) for direct download. If you prefer to use the latest version, you can obtain it from [github example_dags](https://github.com/4paradigm/OpenMLDB/tree/main/extensions/airflow-provider-openmldb/openmldb_provider/example_dags). + +``` +wget https://openmldb.ai/download/airflow_demo/airflow_demo_files.tar.gz +tar zxf airflow_demo_files.tar.gz +ls airflow_demo_files +``` +#### Start Docker Image + +For smooth function, we recommend starting OpenMLDB using the docker image and installing Airflow within the docker container. + +Since Airflow Web requires an external port for login, the container's port must be exposed. Then map the downloaded file from the previous step to the `/work/airflow/dags` directory. This step is crucial for Airflow to load the DAGs from this folder correctly. + +``` +docker run -p 8080:8080 -v `pwd`/airflow_demo_files:/work/airflow_demo_files -it 4pdosc/openmldb:0.8.0 bash +``` + +#### Download and Install Airflow and Airflow OpenMLDB Provider +In the docker container, execute: +``` +pip3 install airflow-provider-openmldb +``` +Airflow will be downloaded as a dependency. + +#### Source Data and DAG Preparation +Copy the sample data file, named `/tmp/train_sample.csv`, to the tmp directory. Airflow DAG files and training scripts used in the DAG must also be copied to the Airflow directory. + +``` +cp /work/airflow_demo_files/train_sample.csv /tmp/ +mkdir -p /work/airflow/dags +cp /work/airflow_demo_files/example_openmldb_complex.py /work/airflow_demo_files/xgboost_train_sample.py /work/airflow/dags +``` + +### Step 1: Start OpenMLDB and Airflow +The command provided below will initiate the OpenMLDB cluster, enabling support for predict server and Airflow standalone. +``` +/work/init.sh +python3 /work/airflow_demo_files/predict_server.py --no-init > predict.log 2>&1 & +export AIRFLOW_HOME=/work/airflow +cd $AIRFLOW_HOME +airflow standalone +``` + +Airflow standalone will show username and password as shown below. + +![airflow login](images/airflow_login.png) + +In Airflow Web interface at `http://localhost:8080`, enter username and password. + +```{caution} +`airflow standalone` is a front-end program that exits with Airflow. You can exit Airflow after DAG completion to run [Step 3-Testing](#3-Testing), or place the Airflow process in the background. +``` + +### Step 2: Running DAG + +To check the status of the DAG "example_openmldb_complex" in Airflow Web, click on the DAG and select the `Code` tab, as shown below. + +![dag home](images/dag_home.png) + +In this code, you will notice the usage of `openmldb_conn_id`, as depicted in the following figure. The DAG doesn't directly employ the address of OpenMLDB; instead, it uses a connection, so you need to create a new connection with the same name. + +![dag code](images/dag_code.png) + +#### Create Connection +Click on connections in the Admin tab. +![connection](images/connection.png) + +Add the connection. +![add connection](images/add_connection.png) + +The Airflow OpenMLDB Provider is linked to the OpenMLDB API Server. Therefore, you should provide the address of the OpenMLDB API Server in this configuration, rather than the Zookeeper address. + +![connection settings](images/connection_settings.png) + +The completed connection is shown in the figure below. +![display](images/connection_display.png) + +#### Running DAG +Run the DAG to complete the training of the model, SQL deployment, and model deployment. A successful operation will yield results similar to the figure below. +![dag run](images/dag_run.png) + +### Step 3: Test + +If Airflow is currently running in the foreground within the container, you may exit the process now. The upcoming tests will not be dependent on Airflow. + +#### Online Data Import +The SQL and model deployment have been successfully executed in the Airflow DAG. However, there is currently no data in the online storage, necessitating an online data import. + +``` +curl -X POST http://127.0.0.1:9080/dbs/example_db -d'{"mode":"online", "sql":"load data infile \"file:///tmp/train_sample.csv\" into table example_table options(mode=\"append\");"}' +``` + +This import process is asynchronous, but since the data volume is small, it will be completed quickly. You can monitor the status of the import operations by using the `SHOW JOBS` command. +``` +curl -X POST http://127.0.0.1:9080/dbs/example_db -d'{"mode":"online", "sql":"show jobs"}' +``` + +#### Prediction +Execute the prediction script to make a prediction using the newly deployed SQL and model. +``` +python3 /work/airflow_demo_files/predict.py +``` +The result is as shown. +![result](images/airflow_test_result.png) + + +### Non-Interactive Testing + +Check if DAG has been successfully loaded: +``` +airflow dags list | grep openmldb +``` +Add required connection: +``` +airflow connections add openmldb_conn_id --conn-uri http://127.0.0.1:9080 +airflow connections list --conn-id openmldb_conn_id +``` +DAG test: +``` +airflow dags test example_openmldb_complex 2022-08-25 +``` diff --git a/docs/en/integration/deploy_integration/images/add_connection.png b/docs/en/integration/deploy_integration/images/add_connection.png new file mode 100644 index 0000000000000000000000000000000000000000..50cd41d16ff69f9168bfdec667be80e7b5818648 GIT binary patch literal 32687 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

8yVr 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 literal 0 HcmV?d00001 diff --git a/docs/en/integration/deploy_integration/images/airflow_dag.png b/docs/en/integration/deploy_integration/images/airflow_dag.png new file mode 100644 index 0000000000000000000000000000000000000000..ad2bd6193e2a046d1b51ad513776bd5c3d97d7f4 GIT binary patch literal 303427 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 z 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 literal 0 HcmV?d00001 diff --git a/docs/en/integration/deploy_integration/images/airflow_login.png b/docs/en/integration/deploy_integration/images/airflow_login.png new file mode 100644 index 0000000000000000000000000000000000000000..03d58db49a92faa3034f64371845e2ad444c5e99 GIT binary patch literal 42118 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

d$FkykOrvKFEj4Vtg~L$x$hx1eR7mDv*Ln2@ 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 SB?@4G? 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 literal 0 HcmV?d00001 diff --git a/docs/en/integration/deploy_integration/images/connection.png b/docs/en/integration/deploy_integration/images/connection.png new file mode 100644 index 0000000000000000000000000000000000000000..d0383aef2dc1abd0d7d0c5220316f284de56b666 GIT binary patch literal 157110 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 literal 0 HcmV?d00001 diff --git a/docs/en/integration/deploy_integration/images/connection_display.png b/docs/en/integration/deploy_integration/images/connection_display.png new file mode 100644 index 0000000000000000000000000000000000000000..05726e821a440cb2c6fcfdfeabadb8450996f850 GIT binary patch literal 21200 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

nCUj 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 literal 0 HcmV?d00001 diff --git a/docs/en/integration/deploy_integration/images/dag_code.png b/docs/en/integration/deploy_integration/images/dag_code.png new file mode 100644 index 0000000000000000000000000000000000000000..86f2289a0a53713d2c58816cfc550378cd574310 GIT binary patch literal 51039 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%0A}py*HDY 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 literal 0 HcmV?d00001 diff --git a/docs/en/integration/deploy_integration/images/dag_home.png b/docs/en/integration/deploy_integration/images/dag_home.png new file mode 100644 index 0000000000000000000000000000000000000000..00a6ed33c539a51a6eca0f1e83c17320240af508 GIT binary patch literal 64185 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 D&TSAD literal 0 HcmV?d00001 diff --git a/docs/en/integration/deploy_integration/images/dag_run.png b/docs/en/integration/deploy_integration/images/dag_run.png new file mode 100644 index 0000000000000000000000000000000000000000..d072e4f879261c799b998873092774f7d3aa6808 GIT binary patch literal 789458 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

g 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

  • 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 z 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%0AB}bY(2rQ3pu5#e_BS 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

    eB_71?!HQY_q!|3gzKhU4; z%uwrq)YcS-z$_OHMLVmIfRv*;8%ixAeymRXGaX`Vk{?G3AB^0bH|PE>3tE)LsRAnX zqs`7ibQ@NDUb?a|a_FY(nLRtPJ~)62{kn#c&$h#OF9j2Sv^mtG1~=TGOaJ-YZWzvq zyNUKhCna0%T2|B4G|`9-aD0mL26#_%!S%z;58Z2z(GQZfR`*t%;kbcvy;q`dYL|$- zoXv=wOK*g~317*vB#+^4%0hN)mPVDu+O;=K#RO$1%ufkynBy0nN*vV0?(gpMm%@X? zH%%7e9C8m0_ZElYehE$!9Sg-t4CdTa`+;7(TW>30>n4R-97;<)`$XKfX}vMN_R<}c z$DjNN>mB+b9ac4Hl{&iV%O7t3p>p&RYMn4ieb!U{o{Zo)P%_S9?QZNK0Y~@&cXx&J zS~S)}ZYauS{nKAvnW^Cp*435zXb<{TNGN%Bm1m{OSs%?QyRq*E^_PtbrHMI*++P-B z?Y(VAHKsWvz~D0qjOSyfN4jpIm0%6=uLRas0hnIdQZh^Fd?{zktb~C9SjO=1u0edY zDaAa(LPn)4LNgwvs@cB{4#bZE>zdjqdNr0kV2HQgJ5g;cML_|30j0XYMov%dp78r^ zF&K91$iv2FJYB4Sl~QN>X!Gqc?(I3H#cXryLbczH;jVD0+&ctfQqphZDeI||0YRE` zLT{nk-tnSc*Yfr&XBo8ahe!~bc%ut%>A?jOvcpd;4m#vMht|)FqV9hg#^5p#d-wye zFfj(#jeAJ+?r0;Lke>ePw^AsF>iQ=dCR)?1g@e>xDng zN5$@N1_*@9%=Atwj|Q9B+HwqLxqzK1*;U)zp4ErC4dR93?DNFPw>OylU=oKqUKA%x z#|u7{XZVrL;m{5Y{<+&Kw;ekY*bhkD#NDWydTK|kCn8pxNAq(w-H@huOYjfBRmlSQJ^RGkvD2lDms+%? z(pYx(9ud4p9t+^vjvRm&PxjD;c?lo^|6S zt*75ry@bm&{8>l!q1cQUjUMoW)%fm_XEdB2EdC8V^?Z(^(N>>NZ&`6epWw{B4L`p} z{&qcc=R;p%G@k1+J~ZgV?^wl!%r6r@Ek7D=vBpy`K7|4OlT*KcKZJF2h}F-2MrOu5 zvUU;(6@7utaSwP~#JH)`?FoG?{5LHS%Wy!t#5qYpXv(v0Nszq;67u4Ka9 zJ%*S)UTAt23Gd3>Mcg*G)I7_q80ebWC6lix*3gauwnt*hmCE4-Jww7ZhYZjuEC+7;NVWc8N3nvFYc7+(_*sczC zqMt+|DfsLJB7yypgGt)pNYL~qReVJeMu)uR6@1|aritQ# zpIzHjJUr`WXMnx5v{0@{Kq`5C$_+etT=B9WRDSD!O-gRNJX^R+<5@-^7XtA+YZ^Nu zG0g4jANT|^>AS2843c0I^Ul_bz+xgMJfetUZ@wpzS+~VFoi}Us+=5#h$woi`@MHGJ z%KRKbw)cA(u_lz|_+cKIJ^;8qJ;L)vbVmWPlUn#2EO83i1V0t-c&m3)q`{~LF8~IJi=bOrFp75 zQ#m3eNF+Plyn3;ev{S^c;~rPr1MJ!gr7|C5WSgbowRi~~i-$bb-bd#3D#P1gQ6{ML zP+!yRhCG}~ybo!TQ@JOoUZ$r&q(B19xF+OE0`~o(5JMJ0-AfAJHD8z3^8_W{KJNtULW#M@h>ayBB(1h>;Mk2XyoBA+*cb_NWO;E_50zg|EI?^KrpCR1->w6H#S>1BleaI&EV^u zQaEyED4#$e4lrK)U!$OZ6w9Y+^mpMk^Poxz(>QVBPTp`Z2?}K%;lf$6K(i3U<$~2T zoWyV{(Uu@^ZU(SiHZ++lB%71U4)DKQu|6Viw?^{f08`~!d%i9TIZ6w0f3pK?JG_xB zK{LLLu%GQ?9M7wEHPS-X!0}eCmyigo_%VC?1xmKS9XN=yFJW>=CZA(5mgqyR*)m>W zp_vhpyD(#SbhKiA2%AY4o?p?HA$wlCz6g^VGpt&WpzE>Jnpy-P$m)Od`plui^A&Su zXf)n0KZpnYwsGez;?T%Z}?we8jnNld~Xez(B*9TyL0mXGW!emqM;y$G^8 zOHoi3e}PG>b_6P|T|Xs-_#vtreWkg-Q~FsV-Q0p|4Z@-0`y8}JGTCrd4u6w?vs*xb z4AEjAq$urAY2(dgwotwS_XFzeY%7c0eA^`vsKaST2G$@G@VHO9-&*%?9-|c42JTaQ z-dn6%n=v`=;K2mu98(5;I<9gVmOhA@+)OilDCn4XByQqS5!!zXAt0zPQJEQLY1Q^8Z-F4s;(+OZHmG__ z8O$q0z<5$Ee?I$3A}_Ya~ptIE^mW_MEHjf ztVLW0@-DdX7AAZzHQ4!LriH?o@9fcTXmcVgM0zzk&5wF{AQmfpSF~anB9mO@TvmVa z*~8^;-_b?d zIL0In9R00$nJBi}r{PKe#iUJC!JE}4J0PWq$E)u#JRyOd^+UkPQ_Ncz3v|JX{YnXXo;@dVk7}S_O}eBG z$|A(Wk$Mo^Q*3Ox`lz}>L_!kd7W>q5wIevv6y=g`c)3x6MMiz8kTXbnlx{uRzS-LG z-XkH1VKEB6uFGv>_c=B&|6us1Gee$S`$F1isGY)6-21~WA&+yoR#0@iq{-IE1x_P> ztP+#Run>U~&2)1lsWkXbcc}I& z9=}!k75O`xs=%-_i=iaHiu3lqRma3v=d3ZuB~mysvVVN>7gJ*0xGsS%t${K}>aq_| zzTdiOSd$&qrKAzn5-Al0HCarg1aRk*xndMOst3uco;AttHD?xkl%5lR&3}gQ-X@+H zAK?OOzyh&shOO9VE-Vo7w?~uU!OLdND`U2(o8Tg)SNzDIPD-043L@QV0I!w2-!I_~ z6%850k#NI)tR-@!UU2DDmdYRB`^@~b|HasQM#K4sU%xX(i!MZGhy)3u6O1uPkO)I0 zKfM#Z_cDwKk{AThdjyf_gy`T8N2Pa|5jzzg2S;)k(_k)1`YunTpIo@oL#Y1p1RB6=r1a zE6)C3evO@TTbptot?~yn>gNO0o$~kB@R{)M?NWbo-b@Yo2mR;3W6gCk%$VZbq_-~g ztn(cH9kIRg`{2`QR~I;hU2mEU5x=J4wyHgt-aX?!Go>M?DeWx(c6Met~e%}F28NJU>`!msW#KZ zT<4>xQmYKLM9%EM4?*IkLTV-gn)#5f7Q8a&s-X_s_*sFDmUEtGhPQedgdi6_x-~i< zWU_zrb#JCc{6fyXqTu)cvDc?J^k~CfR>p?==S72!?z6|uXkSXcXdnH0u@vuovIN?( z%Yz%ftGS^Ysb;^age)`5-F$7Uj0jC?J2npz+Tg?1$nD<5p`+vz79YuV1q9v#AZXh5 zNu369)!N>x`{E?eQ$L(_#jNn1&QoI(67?K&SZ8_c(JrBb76mHwz;HwT@ zR+#a@sRzl|?-_|V?1ZpUgs3g4O(DSF)b%KQS5RFrW0@SMUz9%wsWG(&5d)#}DvTDI z6WtPhdUAIB>juESs(DZl(Y*zr0Z!RPCA^p&ddjWfBH8xNj0;IqN5lIfQTlkCif@Ca zO+#Zt_!)18IZ(5dzFT1L7ZNRvHMF|a6a9#J>qxCKtV=}=zU7y*NNQ!Y=f%LN&Y_`7 z>Y^)rs-s#|Gzqle{9U9FUQFN_ntSH6m{QL8{RpyX6x)(y0Yw2CPj9NTc(Zpf8hFs0 zd_A^AptEw7G0&kDD?m`3m+&`wW2ldGQ7?S~2~VuhY3VlU+Y|ZY?<>B(vwLo-XlMNW zL)Sx;3SiYxPV|vCwwQ97cBy+y_+C1d8f5jh3W~hb0VT#bGpzqT_gPM^>VxQKZ0{v0 z?AJ%M^K_1y&K4;RhRYv(d6APIS(yt|%Zu(*ivHRgjZO01=uH%zPZ_GezR13jr4n-m z-9MkKZN5jC{D`ByIi$6~(6`$dUYxB&f>ptG#Um^aa&$n6)%M>6*#kSQtl;4G_sujo%S} zc0O59BWSKtGZDW!I^T;v&-GH=Q3cJi4yM;uZ}cGKM#-2mX2QIX-XyGW@!KM4Igj1y z()G#Gdo`jGtyQc>TLmPeovy$Z68(ip397P*GZmnFrZU41M*Mx+c#q4k-|A1;l&1f? zz?l1#w5(&`pmQBO72SInW+jdXAIO#IA=9{L^2d$mY72bjT<>;=`%wr`xox>eHJqbc z=n2@!4lWpG$YiS@3*$;vB|Z^II+`+4@YxXlu_BK6UdW zBx(0}Zs*;}KB#lv8|}fi_k;&@`WxN-3t;r>a=i!sQ{$7v7aS!d<8udQQ>5plWT`-{9kFoM!>P=8e z@k^`vk0>hRQE<|h&!-(GAR9SNRukq0gLx;qNfb7N)F4U3m|zgF#2DAExsbY#rAjf0&!Zc5Vi%zR*IL&?L5z|2#D(XU zidvD#xNX3T*Wjx)#cN*JzU?+I?h{g`>HW?~Z=ELK>+;>Mw%A}?p4X8d>8C5ESz-!% zYo-jLNRBAo3F6&5h}$yjB5wMub!)D+ul z=4J3vLN+nobM0^3X=;NAy)s^vRI)+r%~i_{ExJ)6w}IqNo$5$cGAy?R-%4w_NVk5T zzC_^b43#s;qHyskabFn?$8E|Wk9pSudg?(4*G6oBAEze=!Q*aO_fm@H0peHzH!W=s z&7)@6{#!>`%s`wE)dC0O=5VvSW%=OiJ=!s&gXYWtd{wQ^$t{&)d<1!P@^g|Xd*Y&h zmgBvZplJBPj08zMN6KQRETnpmblFJNRxo83+98 zdDoGmoH$dJ{qPl@zB1QEH>S$Asl9Jeb=l3@4eBJa`i@IFXxcNjIg40ei=*ftBrGWs*=#ap zn3O};)#e(E?OPtfR`A-t^G*BRrB)H_7E>jpr0AQvRWPzQ>YQGdlMsB-XGV-{m&-J} z0uHbBr~!p24|(iuvo*=UB#B@k<2^zooD!gLa7)f9q|R%Jjjf{Q-`hTfHNP8{ox~<& z6aJ)yX?3PFwbHtl(zj5AEGi9UKlP-D{Yda@Z??%SZ`GgLwJ>e?7DR6y5zw7eY1h&o zNwkO&$BF)3>wQ2&^2x(+zCaHV`)|J4Uw-q$niHk`dt1xe?`FiW{24CleD)lW0WyL5 zwBocybHqVA)D7ShCBnec_m-=<8!Gx@d%H8b#m(<|N-rO(jPB4r{BE|e zr|)5UO@&ymKcdfZ0ls?wYdkDq83Aq*MB^eA3GfOnFD)FR9m-UWNgI>$lv#eZ^EA=)l+1svY7U5NUFJX3{QE@6zTVFM7Gj*;z~xcG z^QBkzFE~{;*)Z0Te(H6pTD}FBXpQIh-5W6PWsGp^_fky~rM{cF;+ctLqPlT|B$44E zsp#D|tvLbAgp7LUu;q6O3`=!^{K3|AirvVEo4QcrEn>HH_fmd@%`}+tPB|ock&b{M z3qbh9L7~0+$-kS)7@3$^Uo)AoZ`qS;Uq{x>*=C_;Z`?lkV`q?}m^=SM zb|W*|x-Si;6?}HN6uvJvh?m%Oddc`;*Jr%eW$9JR^+cIfRt<*@_UyZFFe4*^5h15= z1-ar{xTbgHUn*0>e%{efX*}be{FIbhgE(;Rx(o&z@-sQMuT)eLamY~ykWZgIX7cL~ zk4uv6mY&@tbp3c36U(CK?#%Tb@o!dx^RO!WN@z1^3IOa}Z_@GC&N8NLsPUlQY)Azt zco$xf=u;e?lHCcpnhN1OaH_d9FE^dD=ejtU?L1RjN`7^~HhYqLP+E#rzSfWZb$|$w zEJHk0AMxt_W@Y5BTUNv@q-Wp@l^&FRp31mvCkyp8z2|kFc@V@83v1rRD}02w(bT$l zr{YI(@2U1Qc6Djh%PJj=B}AOdB)L%X-I0ouSFQEU#inEIX=9u|&c>buaMWVhhP7sh zXian3{}Cf5XA^BkMfSPqSH_u)P;>8;-#AX+txGy$;V?`2=<*D!-_3l&H|jZh+rF7z z`)ifbp#{KrM6;RXFazDk2?bN{C4QLzgHN5w&z&BbcqEyFhc)c2y{M= zR%jt?h_tZ`NBbsqM~SH(qRdbY3tNm4#?cP%$w{b<|G0@E$=Kq{EKy!j@(K|3Y{2s- zRg*TBb?M;X)|)|jSF(>vv0%2lQZ8q&lNVo0_G6T`jSiZUpJcS4LM^}gC~A$t3un08 zQzyy!z8Oqwtp>X2C$$_*u73_C&3nnNcKStC?wmfPRgZx^@=?B%1oOK@B-M? zXjRq7F?zsDwl1li7@#S8D@?>(6}IAz(p_r(Sa7R_{#s#78J~p$mEjS;SRN#;w8?c+ zDX|by`-**kS!7BZ65aa7)sD?x1UUcEW4Xfw@cq3_saZ?7PCaMXw3W5FOw&n8ZBYm@ z{F4z&TD>J@#k3`xb_N#>$O6fMM0cx=t3|=M{#6sn*@d~YJ^rNkL>w~47dQY8Iu@cm z>Zv-*64!t3n#}f&*l)Tez+ptd{!;3jo#-fCo=zd4K(g_xxa2Hj+ZXJ6CgDEu=qZ8x z+}FHuHvU;?mO`C@dX0-DT`FES1hJQY4P@-xxIC9sczgIfe?(38)Jd+D_C4JzUYJrW zHvE`} z^M6`R9rym)$k<$P;a=lBfI}f{1p7C2%%0J-n3&~L4e-GTxp47w^G`;n+xIl#rU(Up zcCn@m(?&O9xLeuCHTJ0s(SKu(**WPZN4pc+VLwQFZU;P{)$4B1T*@~Y-2N)lCl^kP z-#z2}!l%deC)p9Q3W_suwgi&-?H31fusRJtg?ewYAs%FP zg<+Yes{)~X@8dTq$NRmv;YvWHg+{E;rkJqy<9|CmpL>6{ffvYQNjON|be|+Vd2fXx zXgG-|NJRAmzPk0FoltZC>O!wpPCQtMM4Pqm*}o#FUKTD#uK`|Z z0dn5xk0%ltZA8wAQ4lc~Z)V*3eX0JtCcXNW{2=sefDGd@_xbBtJy!Nn!NU!9*Cx)z zdE$_|hZn-@dSSJmcBjo``e8JRtv&?-Jg<}Wv$;PkjkTPOjJS{xSaYOrhvQ307RvwV z={BY-IDnsBlpbULisAH@Zj{=h2An&?8r`^@yg9lVXjz&2H%T*v^q)oZFof6LiAdOT zh#_n>v3Dn_ELeXwmmf+i{V{|s>r(X&pX;S)OuX(YX8sDYr}xY7c3Y&;p3TF^ zdO)WjhtfX{$dTmP%aM)F^9)AQ&O0Uz`H`Bn zpEIZxR%zAi(XX@-fM1q^UXxTirma3iaL#+lPs$4{UEWGZ8t6UBKHDb(ToD|0h#=k^ zw9#T4Z*YF`H9mM3WQpuh79?}=kC&_hGMnFEDzhuF5Fi5#)kS`aT_ZVyteLq*a+@pN zJKKkUcf7rIb=25+S@x00N>~G(#f@@@yz>414qmPKcFU)&-ULt?&~0Sv2(Nvu5YA!T z=y&I;gI0?J1ORvGyEeOTp8!biJl4^|UCY41qyoTz;;r9N$$27)cS5>{@BGk!Wh;KD zpdJ)aB(&c2EoLfQcw75gw*1a3cO`xSc<>AGg}C;er~OHRz2=X6mPehm!IN#%W!LG1 z)laG9kzkIskM7-KHHN|`Kh9jdtLmhDk$^jvJT-q!(_69=T8vIW z8y>ft=LG+Qi^YVcAJ@UsIa)}Gta4YigECdcV(WBDJ1W0_;X!B%drPi)0K)_|`Qb~* z2qNY9IBukH&1M{e>@QI@jdomxR^PM|ao?U$;P3j0xkeNxUuW;8lBL64DFtF97mXf) zVTY$DM(zR%JGr^x%qqC~p1aK?>yOI4p<17kpJ#;uMSv)<)*OV{up4W%LP_h%zlbP9 z!f8p%e~J)AOR-7@x_yPYqLlrK7a`7iYFh#h%CjB<9V%sivaTJp+$HvW_jZ2c%PoW@ zb@oqdN8x3KE}LNKEG`Am7h2OJ%!?uL?G9-tn1hb~>a3eLR$9b;cCO#RTE#L0UYSD1URtGYY-Qg#Y`*i9T53F;%1*lGZt$uXGXqPi9{c!XhF2hS z)~x&e6Wd3Zk$%e2(C>56vXN&BUurg*-1HPw;tX<5$;7)J4x!gHw(}Yl;Q0$kqtKdx z5FicEFaN|n*&UtX6^Q>rOM+_I%z#)Qy}xK@fHP_LXMBFLaa+Q?k!rAJ;-?j! zlx-U||H?Y5P{j%sHD>5hRfNrPe$ZCIs!JO?7*1i`$70^BMs@+q``pJhfJP?y@>BN7 z%1nV=B$f&#Q0du_q*x7crmSv)ar!oGV{}?JKzEyWGu7_^N*f6X-J!(X*scFfdGQVx zc+7s0RJhPh6dQH4b{n_UT+|Z|aT5AXIGsB%F249mOCE$H3L+QkUaM`^$Ts~Gm!GMt zhSc3)JFbB?{Coq|Tlv3H(*@Ft6o_e8H1` znjC0ConWQ|^uni6C-|tRkL2AkVL)EqFy{He=)FxT*RrO!8$fm~(p9IrErIlpYS5$9 za$~P!H#@>lf5ZLB^ViC8a%{t&T);gP(XrRJ8#fm`JoT*r3=7-7blNA%{<`% zc~9;GruM1iAY(p&BN5Gx5fr0uY%HW;By(_~lO;Xgx!G$S*Z8!Gm2t0LOLnQ`vP1H}hpQ zIy4jnA|s`t;$V3y`v3Ysk)38-ezVcm#q`tuddDR{i5B2N!1YC0yJt5z$>O>dKR68}qor9L(o!;-f3a#Z8qAy@`G1sb zAdo9zab(jm%u*Ws;y>$yr6OX|f#b!*m`)_l1LiwK8q~?&_K8J}*wpeG2fzN_$N>0v z*pGRR4|^23V#gh6HdaUZshi>Zwl8Y*fR&pv{KSK$cXX3S8*oqRYD)xLaqsNy{H7{(ky(b&up>x|Mx(%c4ZmqhR4 zIzyqc!P&lN*>|WIl9IBbbV9{G)6=3oA9n^JSxJY_ERpXB7UxzOe%txU4V(=`HGhKh z=Wdp-_w)Rss*QPhbp&~%;dV$pCj^G1Nbx2E!AfiGMcafTsaZt+Rqs;&yGwjv0a0Sa zJWzce@mw`igl(O~3w_Q9uk01=`xwcupso$sxOoDK47Bc#;M?n9X)kX_KidygWC`wl z%4Ya`Df^igt2is+G+*RUv;)>tu1SN;z3<*(3(LZ`XbxLg%@)|UE%wh%%VuqIoihEZ z=DC%{k09*W38 z7@An&FKS&D9*NRxV`Kbi5xBUnUUrtYnZ5Z25C0;suHX6W+rG6#A75MpSwHNFT@R8l za_}0UibR4=It*9of1EFL3uw2hu7m5^L0ewx>Yr77VL5;d!CG+R*f zW$ZtZmY?Jar0V32o&Ba=rCaYlxyL-LxHBsHYc6ew58`kEfCD9&mbvk-Z99ZZwXYd)Rw-0#5+I?GsY;fv;6AVYgdqA^#}2b%Jl`K zxNIg@G4sL&o2Mv`e>HpEuLB>Qg|Qg1@`T3vVK4A9Ejo*Rm=D`uLzxyggg9r`izaOI zN{h@UWSPE>>v1J^{`UErB|Ae3K&^=+(*ec3>XWmJ1`_jTzkGiPpe@hB9*zEQ3mGD!ZU!qcOL zUM$i&&bBCXyT!R@aa$;_p%y@!Ty`i&@^lz=OodJcK_vEBWU}P0-}GcQP8{ri;Yx82 zGX<32yqjfB$toUZIKmJ7GKd$0xmvNS;8aGghQ5m+cITE#k9%-sWA;mCT~A#;8F0VN z;`e7*$5Lq%Ez=3uz;RNnw#KBvIYYp#>xt|ugHTrt&oa5k@0#m?9yXwB)`Nor0ywZh z(?NsN!o5<^|E~zCE0Www4xu>&{51Tq^sJsSWwIjJ!06~lb1D_^DbT||<=JIxTV$_B zuFY*h<}pwQYh9fI(dulVn}G!Uvpefrx^iLt*Hf@zuw76BTri^&IB24U3W)p#a72D< zATjO6b^Mx2s)rLn4PF^v`9VGJX)GR4xEU%gT4_GmjLO* zyQ0cLmE=DN{6jY{q8A!{vGX<;L&wa6%yb|Af%Na4^NZt;`pg7}#b;{zkFS)06n_Pd z`-Lx9o^!!CL|OO;echP6!o z`>&F?v^ha>&k;o7yHHV)4zXj-v6t)j8?7y?wu8Wvx3XmA1EvH@L^zc#j;f>pAqn$I z3_CJmcB{_4MHfq(S6E(rud%bcdqv|n2boHA0<$(JZ2?lnOPDin5ptNc2=41Xrj(W0 zDuDZGc{{nNCm&PcUjZ~4_gRhnU+R%w;r9a%Cc8wuJ_J&#F;Q{^;<`srErGp(3qLes zZZmoVdl+vIY8K2|LZLX#w}V3ecRcA2tBRNpzM;phr@D-1I~p^JbLs}nz~s~ooMNWlY!8xaoRXCT<9%aN#p$>jcdbH+pM4>~2 z`i=AWY-L*cTWr+T%jP(>_y?e)Etr{v%eRAY+(yTtFLciaT66&5FodQyuVLfP9D{%u ziT}WUZbh5sIN)J{Tzc1$_&IKD=&P@9Pz4rn{u&knhg?{nyj^?1nR|t>w)Hpn0`7w9 z*sX#f_gO-IlQiYJ9#z^oNx<{ffIUGEh^V>+tMM#h$cw`pJHVibS~Danqu)~~hrK*U ztln=#-^k`f(N9rAL1%+#jvc-&3eY`1i1o4lbbu|;OPidK02Jbf1453Q zQ@3XhBWhj2{mUWf@|CHE1M7?PevSXcmkP_yY@m@@Dv?}0UH;5}3q9lVt44GWCdgPq z&P!akQVAPJc3Op#epyXhn$G{=0RtvHl24uTW-yjgh^y-KPiEqnQPsamghey|GxqBQ zk|BeoK?ubVf(2H)GY757Vd!Uo{KCfEkYRm_MMjRjxSIU#i>uN=MQ|HM;2hIHy$Zwn zPg%somYJ~D_}Z7_Ey*7H=0;!wzB&|MXpa*JgZn-{`V5*|0!~ov+2o>7MB8d zQvt(#e6?SdQY!9hzq7RTWwm78E-2tXzK>|8N*wsOEqJr}JG6r6&$8>ysq0RM6!$mg z1C&BcQ+LuLr3W^Rvx@a4t2;}3@M%@{gGFmra+YZ%n&oX%IWyAzv;z>y#Qh|zWIj71 zY8qX_AMFfro2SzXxSjpYVo@Gs*8V2* z5!=3!0$i(7TJ+r-8m1DeYUGIJE>djUbV6xSEdgX4u}?bx(5!c)uQxYcT0Na*7#^MU z7=Fqs;*9zZL4To)_*Ho)e3EXlCXV$HKW{w#%J$K}vhF;YMckz!-s|g|hK9i}?S5@^ zFJf3evcOsTI73Fb6;(C7ZN@J~VuRmovl<^Jh$YcyVV^f=+Xih@%b{M6b`QONmo#EY zysGebGsRu!ny8Hy6pu%`#BF67oYDy+Sx{?os;Nu7bJx;J5x}@VNdnRuF4E>&R1pUI z^F-yoc9^ zZ#q5uul;s-;PSrzD26P@X~=h!Q3*+KweQlIFTt5Hak>k_STcdX66~xQF)O;JRR*K% zx9kl!jYhh?#l(jPcYR%?rxLnC{jB6X`LdKOAEcvv(>9QduFv^4$fgB`W@h*VsmhR% zenWk;*0Wxa<(C28Aj$k-7|c7%@w}dX-vX@S3wmE8>Gk&wzH1Q!=Kc`|w(%c;-wu(( z`3rzr<+GJGTE*#GqBb$rJ%IWwtNFHDZ!aQheDa-qRZY`lW9q$uqtUnZIX$+ii}^`*}WfiHln zE}yhSnh+0Or5$i7y&9#8zgbwC;_o86@)_B!3H3Bp0C4rUWp!aj;+maEb>Z zzaDuSVf-E-znhGRAd9JrkiU+xU#0JHg1%U2Y-n_S$oR*(%Pd@=0P zflDw%TO|NtG#n{7#IZYfCUY+B zmf+R-*n6`hX^XFK22?5WJ|v(5SP;62Yz6kSp7%vrW4x&CmbE&IbmQ)DKQ(PWWG^Qb zKB~Wq{MrbtJ0v@A^~jvf^5ij%_T&5ytaKFx#zhm~d_|7jNz{zl`yEY`wzWtaKU1~! zENK^N!cF3fJzPPGAgp1L;z4J*M|8Gdmpqbo36fy)Va2Jn6RMm9q$gWEuQ%at|&1J}C?L+WjsGTkU=F8^p$HBr6 z2g~9`OyISk|1by0CNd(92A?a#-%%TX%rsIPK$F>;>55-ZM_K~hY?ngta!g_u+d~Jl zvDnEIKFkw`+Jf}Vw`};uKiZr`Zrsb6a!LF9!@D6zd9i6XKVmTetxi$|oj?WIlS9C} z7!N)5xTGh{>oc*m)bq*iVrcGT$>T?!vT|wj_P)qQT7W!&s}g%o>I8fW`d7%cnZ3!I zeLEugZ}7kCkQ<@{aMx$5$SoYWuaqbO6XnHE>xmD)9^icfPd;e52|jZ`KI+_cm_owV zUpxsqeD5=LqHr>9#>~Xjd-;SmWmC743(w>iuLUA%WNlJY`|=3vh{;>$5ZzA(dVRb3 zZ%3WB7v#L9OzY~lX#=xIZs93JA1vAlu7$};l=&Q0xBtZcIg|Uf_cON8g{(+u@GGr3`r-_K|BjtA6LU*l7YtD{Pi_xM`k z_5z=2Dj$2q=@9p`211I!f zIv<<CLSB_`W$-ugu*;fxBWA-v$&9?Ed5{!LNmHrqE1tA%JKn0yeS?iHQsoA~+D( z9(WBWK~JiV>ab5Vx;Hje4@~=G9(SO~l`bFNyjI@pWxmv`n5~~>zr%Yy1}fc-vc9cY z*ueh5qn%E~xSPT|I*1rZy) z3e#P5_yU_BAq?dlZbh+b5cv_6P8z}zr))ym#d|T9C17}H)a4O?C%O*(G0F0jC)Gby zTB_z31M4>{z_;y3;JR5-peQ@DJLTxuie(dt1O9$T$+StY4e$gP@{9OYsoDH8-KNv zAT>UKavK`{K`uFf}cL&Q>cy;ph-x@zc1uB7g2PK2@FpZG_h!F=?&=R(wyg5zB zoAw=*){#5Ug9Ce@1|kDMD2tJE4DhLwBe32tcj)7~O&wul`}su@3+O*(`8+mWlow{E zihsG3<^EhB{u7IyNFsfz&K(vk&GLV&foV!)B584%Jl2f8r*X&P{m~`|2RDOF=ZLi6 zCDWrJVy}+A*yJlJ6&Nq<7L0X!@}gh~`@eqG+O0pZ_EbA(lttIJM0C8MJI$OQb5AiN zRyO5^^#Q`F6`2bILVdnl|0P+{#5+Hhv}0aYhTS2(nG?j)=~Zu>1R!Z_P#;&q-0xF4 z7#T!-mVI=ycx9{pSQ@7q9W26>^AX)W5twR@la`^B0Q+djL$hT&^FdYk0w{SWvVrL-eqWL{7cBnnWP;M~3M z8`oZ5?>t~_rspALoA>mYJ%b%NV?@bbS4$G!q3w|6j~%J#N6bf0D4eAfk^L>{hl^x5!6Z+z`i;HYOca`ZPtU`8Ow@UxLTkuf%-eGRHE#6m%R-RNREc z@7j#N{pTkdF|3JiaDE6QSGvMDiz5qX!8bzTl#n+$38<2XRrCU9Z^#5*1;zon$^n`_ zq+frlTt07+yv0DjG9Z=T2H2zeaPaz<{wr-E+|MfC_~%or77NSL^kTTJtSWb&d=YbwU|piB!hjS&r<9m` zv$^8fr$+t)1Ms60DJwJsdToo~%b~!3z;n;18og^#?i-=~ti6JS5U4w^h3c^}xBMk?g6Mpq|os~aPZAzBNSRU2qk6|XSxz^f9c z@7rnkT;_C$pU(XR$BA74j4niTJO8Y&q)`uSra{7qvH)d3f!sUYQEDT5z&HT%qo5{G zOxbMPYB{5wM0I=$cT$i&l5SI`s~AMaBpEAVT3Q&Oa4NZQwdOkL$#d5SZ`;7wzgPusQE*yok0MV>_9Oya&s!q^-hTHRcJ^LO-`~b z3)(gPK}xzhY38A-9bYPp6# zpKF94=uJU4xAw4q&BCp@X~Dk_wc%vxkuuAskhcX?S+9En26q1jA}CS_(<4IRTm~{` zB=^Zi1(ih3~R4aeC zW`Bb!^7@P zM{n5WbZw~}p-S9S6~cv-)j4Nr8vErqUH@%j4?V!WRZk!{Fw{bgQ* zrL!U`{-_lHpUrVn8bZ|?a;oPKef<7%e#UcS z0jZ|K^^oNWi^;t0mk{w~Y7tynm%h(6A)JT>a4i-rsY03ObZpu2rwje_Ri6x}tVcCU zjwa^1V1(=T9A;J5~)t(uAci+MTt zjEfaZLDzZCpkj>yrdZ?Fe@F7fZDpI?;1h++PF$o|pkRf7?*R4|LWhVn6-!g=vnFX} zJ2G>tV$h#;g($JmED#lqZ zO{#lTcL&JD0~WH5L!8XfJJcL&f!>w5tgb9Cy6>5aJ)Q-;>W+0>ot2n8q~W0kQG#d; zH};^QFIN7y@8dhF^1KuDA>i!nlE5ULv zv^|T$U^T1^zeTj)=f{V7`#oy(=(#&S*Lz@*ZaTVqLkJSdo(hlowldNrJDk|h-n@eKcW#4IU9&`CE9xyR88D7OBR}NmGH6UAu^|~7M8*E6gW1o9yn)s>{NLy_| zY?=~%PG|gQ>zO0ukm5~ZyxP&rp%t0Djr?lfHCbA@^9PE{Y+U`A!}yW;>{mCFuH6yM zc_CO>1lQK;bMs!xwHoI+PZn;{yDH3Verup!BYnSO{KoI@FO|dSJeG?trK?vh^&IH4 zqmo497X2-xKU!2d{$atqjEZ+ugNXAYha$yLuf9ku^!j76g5m^y`DbHRet>{*SK!rT zG$xHQpZRxB^ROmAhn&}+!21z4%i=iGKW@#J7b3x?Yi|=YBWAxQ*Vqh;a7DEE4gEkV zb{6?A1~)WtKpC}(mwAb|L$di=V%Oeoze*aiUY`Q{n|Zok)*yK;Aq-TVl5BbuK)hl&xY6CPRWu{I8fd4Ia{1HUs8~fUgcBfoPy5B zDszj-tZlq8`nS{~vb0;?9;P={l$kL`M8s;_t} z)la6(qLPdK&@Z7!Av>wI6phozeOZI;>?fU#(FmHItfs&_r`{YqyJbBp(2#vZcx8zPvBYn_-kk^iuyx$-rBX{rtwT8`U;C|5=sfX^har zfYQxaJk#Pz>_a(trrg^;wPVr}8vOlchhw6u0^xDcm$w(Cz`&6>sLxZD)W0GDBS8^c zuQl3}oXm`V$`a>HoQ@w@S|3?Lh#dP0bZf`EqXtHp zs@>NrM1A#7Vu+U5DUh~!45HjRkVu(ORyI>I3vr=czmYy8?gh{v=5 z@gYWQmaC(&#i_|sX7lX9Z!JAuq4&lLW|R6KflL^%OmkDsbZ=c0y|0K6XQ$!#C?wD?r`TSJ?Go^^^=*f}ZtQcvGj+s22 zaJ?TZ1*TaVDBAXh8GDPF)zgbgUKK>+th`rr#v$tRM2tre42aTZSOpPMe7=2?B|9K{ z;y6SD`84^u^U>TihV}H;gfew}my_a6adyu0K*ZhHoHD{@V%lz0mxNS#&Z%M|7Wpp) z^rg+=SM~3}B4t?X!pQ&40*JQiZKF*0Cvq<3sv(LFAVs}$K5|z%$V% znKmx(3#~gJgEj>=OFPG_$P_<8_OulA9E=N@w+*k9|2T5#S;p%Q+TA-Q7G*?Cp9kZlzs^tkp&Mvn><@QhAvsO^n?Q4{`5qx z*!_Q6DbD#l(3Ifx<&IY^Co`vK_+tyj|M6Pfl*!V)eH$8A=A}PqY&q>*cHER<78`f! zR*P#-+%HtT)DgbTmadZgVZM#4?4_R^Wzcu}2t_1MnIeSGph7vMdHEZ-Me>hPz>}iF z>|RO&i?!7-rj@1f{>QWPHTd}G8*qmJ^?JAY?H*`1gp^jpbOANe9_7C?c?5@%4|E3< z0mM+xDmK5;JKi*3ogA44Ee5dUG~U|BcJgAilGX0!WQCEPk#tuyp;m4Y0fI$cCCKlm zwz8c%fxPD1IV{lq3-7G{R3e>6=F5}Aj_|DWoLY& zg+)PElHBYnAQ1*CQq+ST0PLG7iV9)_EP49wgE_=*xjylo*BwnNO%#>9ReKP&gE@*B z#pz0h`}?CEhQ&LU`Cgv>+vSqS>HFIDd9XGs4jxUm+#Q>icp`o=@&rqxD`iSB8(y*=`A(7zyv15{Q9Y6^2$49%#*>UuPu|#2T4dBabkv!dNvYX;;AFHOp?s8pm1* z_{Jhw+kVNdvhUFN+u&|Iwv3D-UKb5RTQz15RXAE_=xCYMkWCxWMcE-o5R$snJc{!nQjFhqNEulO{5GjyIP_h!huf6MyfHQZha##=PJ~mb9TR z$B9!EB9fN#$zn*_>9<(ZzNS@MuP@TpL7O@$YSnG_jyW~H_vc)n>uo0^@2-ayK1T{$ zJ&c7P6gQb5&I=VUUQEQrd=nAAS0rREQVqQKZ1;N&uM&J=#MEj*fgo70Zky4;jtDpX zbY1>dOO|Zez`*Ru@cNQkB+-B(Te*Cu7FR&57TN+wOh+qAumNy(f^%M)3)a_1Q!?a- z^>CPznBaF}re4~b#Kd5_TEkW{YrY%#-6vd|-FS!o^LOny^HuJ$8Zq-atnhmPpkM2GSwu z4*6<#Zf}9xmv(rxf}DZAlD{HsDGIuctp2q;_p7sxqX{}qwIw1&l*8`!g8nMW8*nox z05^WE*%D2z6dZkybsqe%G>fpX;5m_9YD)EgY#>ns^ucPWUJn3quFHHdMX(3P%+FFN zA@`^=Q{-{E2cUlini4~7aVHWUAZjwznMJ| z9w{qh=EUuQuUA?G5HHoPApxN57$NE5l%aOPBHQhc|>fRra)S?z^k0+c7cdBLqJjsh3HazB}QUs)usmV$4k0#LrezT^TBkF zk3;eL%{orY_rBb!wbw(DpF_|~C+u5#O|HvIX?xOQ@u{NImzR&_DG`zUmp?<6SKuM| zF$aE+zn=4gD^0ekU9p@RsoOV2l-B?|VC%`A32c9T=D47px#hFvkO-`7KwieJsu7^V zJlYBC-?4>Wm|MSQyZVost0(C5JWk{JSP02F`v4_MUV0$MX|5l&F$(U5Y{%_4RZA&s zx6yJ94!lSR?S7j~-W*!t7co=<7W8MNvuZF?m&E1^a!N6E8B^2I9h~mZ7!8Fh;#X)T z`2GKQo$r>lFmVQ!bXtL7IR|e5HMSvvHR+dqEydXXZtwd@TybletjXIOXL-LMw!MAE zA88)5y%m(iz(+@?U~Tvk_YTjm7sWNAPnJWk57D7y-vmW`%9^en7S>**@S9$Jpb3$~ ze}-UGynZ;6sL}|J0^#3!x`1Av#J&^*-wzkARaf>dkN=+M&{CNxR315O3IEI|Tv++MRxpxSsMy`4d?CJAlyUKLKqCrT*%)?&PD z{9S_)b?K=c^oc`^oLb(pukPK1(HFtOJBYB_pfqC36+|ng7B5pMhbJ|>8#@kMAJ5feY6%Qw-Cm+?a-WX)X#(J8;q7e z=Y3M2k*IWA@e*AA}t4;-{UEn(=@=#~g`DY>ph&>i01_*%S% zuGQkl$*jfZn6_*1<}FW=5a}=3{=f5?{?CG?nV78ckiOGQRd&BD!?M z4Zd2j>jZq^_Zw(u-M?(#IjTz~B>^NAAAXz&WK>_;?TV!p!fX1kSt>+^&+*HMff*mS+NJPg;^^? zlS8iu4Wy!Gx!(PiyTWernTpTxqs{J9WK4Zq_YNuSeJoFV)Aj4Q@|ePjDxIStE-%>~ zgY9N2#|zQY2`XcJJX) zjJhCIjgm*o32_Up3RZlJ1m2~{1bIHosJt(ya500r4e}I?(Um>DT`}d}l)55H057+G zbKikfA!lU`a!C-Q4nY~>&Afus$)U9fnaR5^)D|#hE0@RhCZU&2!vcM#40g+(v@8jx z-_+UM4WXmfS&RvJNa15ArF~_gj!3B_ZHTF)Yp+0dg!PolXRAUfNs2q@ip=FPWCD=!u^bDH_yJ<~N*2Ol9Ky(boYN%M!lqh6-Vt-s8| zw26Lr@xAXcK6(5tE5w%(3e^W}GdAmue=eQYNDfani18H4-&_hEV zQvXa*YFBv!1RaLR5^aLE7Pixr!pKv~M#U99@R6+=F>A#+vb4h=TWfg_ag(kq3<8Ly z)eElX-J>}uJy|e27HAnIG23{@T0diOC7a|b8Z{F;t14~Gv7Ny3*)B6lA&!5B*)FOx zwF*HGR^;6-k!sUj8G_QVQO}211i!<9mK54SOZ-Hux55LJTWo#K)7i^5Sr28O0=b7X zt05HS<(&K7-|b=;%^8^j%NEW9yj%hYV$Ao9eH>+B2^h$;a+NpyOiLQFx{VCAPmNW{{;rMIv(sWyyjG&rD?EAc*Sd5CGk?Uv}+Aja#nQ?*gs)_BN#YIzhSW%&#E5 z0ycpMeqCzXx<1&yk11^% z2@mc{sRYYC!XLBLtPq-vlTE85MqUZPx4p=}6z`XS<^IU*RQBY13=#sG7D>)l>(g@L zpp3J@5eL1anDvp{ssb=lTaiXl=QKpT))$4Ml@amfjrGG0sO@jFSqCqCy8zHRX^=ZB zCgZ=lTQa1_IfgDW{QcFi{w(=d0e|~jff__V+ncgnSD-hR-e~{{!;VEsDU{a%KmP#3 zlbrzN>kKF(=WDxcRhJ3i@DMd_Tu&=3C~{)5jAToD1y`iK;l(Q`vM@F`OVJip4{pJE zocaNByVmv*`~DLK*4ZFL=-DS8hD~v#zFD(bl?Z0Fxhv zs|%XTjNhnY;%M)=2-F!SH;U4njq43|rS@%s+3{pg)xP*TIxZ)+6#xCbo8PxMT zqD;5yMLa`6X}r(qzV2I{y|fL{9!&gUXCfn}E|(G7F=Nu+OdmVzQj^1!jP zXVXxUTK!6QrmtjQ5{DeBW@X22`9Uku(!gq(w);KV_=68xuAmPWJulj%5P1E~r%2|G z_RKJHhB6p(ilz{bkx7)ogM-c^_ng2%rCWt^o8->>X`}Nu=jF1N(7$N`?9aIxVpG?x z&NySPzn9Z!Z2|!1N35u*!#nm}3g_wlU#X}t0%mNg>qzTrYZmAAj|9@HXkP2K-+x+Z zROky``NMb8lx`+8@b}EW$qh0^Ct@zxvx)1iXP|gRIF2>F_aX)Zlzw%wq>#%Z^}R&a zHD%|eih?wvDL5PV5cPAFxWUs`o^(C|OPW;32m2__RwAD}mtMtSX`%-<)*|0)2 zzkq;&srp>1j?IsTYcrvJ<}DaIX+;yJirfwrfmOCxd8VdMvH(M#X2gT>^|LOD^J0i) zxts4tyO{2B8KysIdTKzJ?-5jz?!Sr3{69X^wkPDPHZ4>q$Bqi%E(r}fi~N=6W$^O> z`*r1GCF9Shm=QMk+d=LNhn6I}zi{^KT!8jdDk{g#XW&Mfi}Jsw+6{lVug)3!*T_o#zP|I;DrQN~#8ssT`h+Nx!m`02Zz)Qu3BUIQgG<`5QIvDnI% zJ@Ibm`C^t$zqME}qx5>c%Vzfd27#2X!6Ggbqo1QfOhMMtkNw8OM$}tV&Y;#yXkzM> z$w9vIMyDWIf8%v9M3peJo!d`_G0+w|Zwu@$6#kP;_}wUlSJ~&*?D>@8?V1s_yYv*= zsd9Jjo6`_A88vVnic#?biW$8~t=c2xp_-t**w$#wUk- zea`sx*F8Gpl6j2J+!(EDAvXGR2BzaFiN`K6e+!~<%^K~UtXMNl9 z#*fB{+Ku|X8R+1hB)hMo;}A_wDAVN_>~o z3%q0>6Fo)-x&M~jX=kUQjGfA~yyeBbA}Q&L zh5qXom?iNetf|LHM~XlvdWi0{vpnPU>No1P=(gsW6*ilU=G|j9L+e3Z2ic+dUa{bpg-A7!j3~c0rB!YHbU!u_wyF&;oQ)6k!yNo}Ox%Rcy|v0F=^3fx zMFfdE`0mtbENLL6<&2yU9spFM3Re?>E~>~rwSH67ka>!d%xzwk`3p<>@05`PoND3y z(8q~(@OSeJQiOaXwxuX^*pq(`KkO&R*g1z135$&pUPjG4reM^quQuIhQsAiCR6WLmjQJ;C z>u^3TaPXBmM5G(jDghYUyc$D$Vjp!zGG=KB31#z8aJxFq(!cVuTwOP?1_74;;j%!$ z+B0xiswftdE9=aF?m!+&= z0eP`Y3D=8UL0RVB0rgUr8(+6^LI|xj(K$iayq&5l))t@Fr`AiD#s;GZzlm8B+c5 zJ5C#EL^Fm_&m~zTLaOOn5%=Fasvxu?xO9KU98c@d+TYEnFxhf88hJZc)_$QvZUyK$GuZ3}aQ$*7)XD>S#mIW${V@xb`%w2iu8T zOJvICF_+A#+X#7bm+@;3aY=UGm4*CjowYEPe?V4?_S9~5tv~SJ8haelvHnur3SMhy z!gsi$6n;CNg##KVTT){sgC(#7k4CK>E%8Fexmr=sIT4DE4O3wJn2hyF?*QR7#xkG~ zPi!n4^yj1hv3-Q51(}g@7|<;Nd>IFp#{4R9f{4Y?8p0ao=yMlHM($~awgu;<+?RhG z$KFcEb*yLM(aUK@mR1?EH(!+H-m> zZ`p^mcAK=GkMJkuEAXQLFXDqnpKm3DmatEE%nFZs+2X=mKNb?&qvY0lQPcKPuXl-t z0|Y!>nNGPA8FeWXRSEtcoi4uswWep-GcEYSkEI_TM4uF_6bQL~bG_eQ#Zvp`HvXs7 zbm7eBcMlOr>%>DIoPEA!ILcJpc>PloAPu~W{q){%;S0ByLr(%160>1`c;x;#H#gwq z$3E!$2ophEC7d^F^1f@Xw#8^N+1M3t=G|61x5N`c`72epf$W9TYiG~Hf zw=8&L;MUI=+w(gpW=c{TM8PlB+EAg*M03FSerK76#y){`#1fz03U&E07?M6gHW>W3 z%idXY0S7*Jr1Ypu*r-gODsaZ!J1yG)Nhpr5x72RfIT3i|TaGqoJD4=v!S{mcm`Jj4 zPvfdK*ipVDB#|^SOh4^>o7|n`1!k)443ELN?~YExR)D6BvLB|NGT85(1YLLE7+u91 z&Q2Q62CRMyBY)hZE8iaB+Ip@c&n{{FVWknl0%c?Q?xbY@pfSVS{!#p#7ru9%;zi zhPR&n+D>oE)il`%j$bdGZS>O5UNdK_w^k2079ra59i3SXm-lF`sCZz0o*cop-}?GZ z<-ZMxyA^A+)T4fIvdb+m=YNhKpOF%GS<1uXH*B=q8tv%*{HOVL+oQc7d-5T`GbgVo zZ~9x)oWxYKp{3N!)fKp!bSGR7J=KPNcUpL}Ws0??Vl%D|^7egK0x~i+9~`ypPx;Vf z4ql@XLO!-*-n8_PA&PrfLo8}WUi6ObC9$x+VAH|(&NvLAOuHsOOJ@;nhq^fm;@{s5 z955>DtJ06U!4*-hpG4iU_#wgxa|eUL>oiu>{_}Rj(0~R8=}a~BMp_5}-N{d`y~cxgZ#D0X>QM37VBAc~ zrRV~+e@#d**PaM7;DOnf-&y}c;t6le##Bgc2t(DP0X=num}+lV+;@>M;^kC3{VHHkfk^xi_`Wr|2`6&^jFsl~)OJvyI%u1f5H4+^5+z=Dp zfq7&oo3XvBN^`c}Dx9?4+9HWGiF?!a44z6=;ngQ*Ivc4WatRE3-TBIb=pXKr2oqxd zis|N`f)|sGgMmtW(2>G51^QbUMd8{XR4sLG!q{#PI;)sZY&L7(V*j<5!u|qAAkeLD z4vW=4U6ecKLRXr{difW=*!M$ba15FNkW>UPQ_Y!b;MCqU707(nK{y)%{a3oSHQc*2 z6Kpa#7A8Ny#;Q2&xC#>^^03hFGs=)IE8HVBC4C$9Eo$S8kBRvnk(G)&vf}YCzwOAk z_P*ku6&7S@25Q*A5qtYHgI07aFc?uyr{{#nA)#R71JGgjKa?(v!su=D)AmKDHh95> zAaexQ?bw$)p4UTHuBbMhXC0v@Hf#6ZOP=WX$`P$hHI2F2%>1Li$&q<9yqg#6Yphs5 z*Th(EJYzfR{4tL-mWGyVdH1Me?{6_51a|ylc2hf;lk{}64Oo`^TnuaQT{4wGc-)|5 zgh9j;8i&56`2tSf3bI+P1@@LGU%YS0*U7Nt7_7TJq7Zc> zr-0zbu05XfHp_EY`5N7Gw8$*G4$A4tAsA zdpI%G>>G5PvDdzwhmLne4m%r5PHQ@&zCOB*wKJy)t!$LXk`nS|JEEKxHixI_Os8`gB}3B0*ZpkgKIF%RUpM@_I5UNt9j(6 zW3Ki2XtpBsUt!LdOJIKQedM*t>H1t7|CQHeXSrAREmzG2E)P|geOqdyVVd>Dx1t!P zLXwE`m1w>>Uc9>4Q@B|>Z7L^vaFpd5*nF{00`{f2%{m3J=8lYV84HNzDl)NA2lXhN zvl-&0XGY0+(=wB&>c=BBu(Y2nfP;8)!DvW|39VU+71w(W!KDN%&^5RP**Tc*j zCs`Dc7bkhFHZD;TuL)Wo9GBbe-JU4JsR=`VV5_`Pugd7+_?qOLivr%Xcpv#FCT{dy z{3`<#+YyLX;?7fWX^lI1(;ZX68sCiq@$2BGn+>iY^wna}Y0F-vQmju9esKTxQLC^= zz9%<5sLA1CP=ush8XY99l+fD{Q9JcXU5nL1E)1HrjWMLnOkDl@$JGqNYr$$b+&S}9 zmq!KtAspb&X2(?#C>_3`aEfzU zd4lGCEGXjf@(`z!ZQJ{0%|1HZ{x>Asa?lwrsJE~-ZjK1a9}p@eh_lpe!(;Mb;pAEv zr;s7`M40KC-F3gGqrz#?B`yp{DGLU>6_ZGf+TjR2bwuMlt4d1;+o-{$BeabPUESTVNX-67|bwsK$pS;2!nGG8g(&YGKRaP~Z6;W2 z@-;H(JrKVz zWDg?>1mEeOzpr?kY?F&`9KJbpy>YH@s2E0^HZPV^(9{j(xk||jji^~Toi{8EHSXbU zm@KiQsT5QvvXi^>zOsW}k^^HFz*x;6$y34`mW}l0p7KXGdg#$K-{wTuw%Ngb|NJ@M zRXX&f5_m0*6X7n5a>$cv^LbufFvP#Iu`?01(C8{LYJPrUU*R z{&w5wj&FmP84|WA*29TC%v6fvr(H>?O&KrU!T3?KR3f0}wITNFE2cP(LzVTAs)>n8 zJiu(Kn=sj9hYQYhYf@Hh*oYEio8i*KQ#2C)`9-0h5Cn`#q_iB&j$vHm2@aP2(w_1B*H-#jGQ^=vS)&o=k75hPN4 z3v(o1p(nZ&9*QxTj|o{gy{Ra%E_!E{K&JYsk0)ebT3WVnlcuh3nhaiC?AjOwr6_2# z$nll>!`>=IV()6xl+UYv)U;nA&I%|#E0M#H&^1nJVO&w#quWQL31IgLimT;#Wp^_r zsx*C9yeBeydDH=ed!2i8@H%^R9^8XEu@uJHoUUD#33u+U1TkvD3bMC8p-x~CRN0!P zCRP@?S!T_a7zd0)0>w-P{ARpf%yl)-(Dt<7wn6-V`IY^zb!X5zJ!X-A@rbc8sC=hx z<>b3K(b;Z0I~ky>0gL{3aoB{qAfcVL@>EPp0cDOv=*$e74(vz6 zjU=_rU0`GrRjOf*rovd<^@l46jRcF3vVXW-E0$H!1>iH*B6&q+!5q_) zCXF*xROjAhvDP%pd$}Y_4m>I{3;Wb7G-EZYXxBs++4RM2 zlVGJylpR}Tf8GfF8tkN&runG-sG!}p$B|uhuL!a-eg<#GQ(PrH+bs+1yg0~-zk64; z<^3LrZF-oeZ2JUoH!Ah;?U%QF+8Y)Ioxeq#_p^m})*LADHKU-v`maQU_f}k)3>lXb zActFK+A3|wj6Q##^8%hiAMSq`!wS5ht>MD+b8yM06BOK!yZ_h@H^nr?=$m0C=1Fg) zcF(j^fR>iy*ngiQk2Q~Ssoz9)9x!nRZ8oSw9)vz@rI>gs1arF4mrfn4Q+~17^^N5e zCOKbQ!jJwcV1Rhgquj)qf$fu`q7dB7NDh1d4w;$m*KkJ6JgoUwaqg#{|0ZB9ei-zy0smL|m@zx9J~_#_ zYYlT4K5Xutz`A*=I<*r+%%x%?xH zi|CaQT8=ta*WcGhY6sJhCg{N9EJMR~f)IE}(-yCSKbdQfC8}f^iv^@IPO6fOWa#;zIMCd-Qcu#Q{?bS|E$h6igY9&BeaoBs)dO9=@c^;AeR6U;@BfOX;Ozk3U)fq}3N; z15w*vKmT^$IXwl#o=7sy)igeul?_lI;foo+_fDsTV;WpiF52wDh?fOoW==~TePIPt-=vezYp#yW>|KW>`gd8 zqUK;anz+D;mB~6Eo)sBL2Hq?kDS;s~Fxu#KiNs#cmu`u0sj_|jeh>ALN$SZ!Qp(<)muU?J6uMMzoUY_-l&QEAe>G4Ws+EOWDikrzgxUQ;_^V8UbaZtZj^C+CF-P6xlMrEDJF)YMT6 zazTNeFMK13Y!zNGb;jUoAO<7!My%C8<}YN^T*)(yguZ znlULi^DDq@Y&6Id_jbV!5cZ7NFgo>6GT=-*8DcRn2p*1=E$lZ#z=dc+G zsJagigtJ?JQJvoJue=?;wenza2hF340Ge*38Cc12nT?0T8ENJ#SD&b$7(s;@%$2@( z9JkU7*k6aDzotjU+g2g4WuM3Nfbn;2lw66p7{3f#hP>rrB`|2XKrL$$Dx2Eir+@NJYOP zq%EMj1+d)+A&%V4UI4W2M;Sq!(U+dVE|jo-chB#*->=f>m!l(u)&S6UJ7j5!t;e;rll8V9i7(crR+`3b+lUf^(#+CEx&Ydt(9>s+&M5j zBou#`WX`e?b)K0dcvkkSV15GTVG^zJni!WV9KvDn0_BD0pvBWquE54Ezl!51v-*39TA`fd+0#~5r&2<5#GDn-LLO;;YXj&5r{NPXhnN=`oEKIB!+G_gx zno`fV>Fih|A{f*JOUpzi%feMVoZ%7z1~apbJcaX^%AEtMIW!>7EOOM0;z2!+jx+6p ziU8e{%K*|apl8`Ja#8qL49xzI!=oTXnQR6PaXr`xn4l zSnSj1wB31#esHv+c!z6~34`4j*N^XzG3+v#ATI26l?ei3!qVJIZVD*h{LnOQa=&HS zFnvCaeSgM8>K~=RP|x!o*XxPm(-XbaBN57q5F3k;oxp-zRsZ!+-$Vyq6@SxDn3v~N zSA8_Srg=11%h93tfxVISbH+$58a%>b>CRnG<|@C1$>7D+Qm^pP* zm5~ze88=F6cCfUPN;Mos879UDIzhV5Sx)1hA)|MO1-)WUc-ta`ouL}3^j zMKnt#@qu2^l#~9M9fvUYq{&|Cx{*R#vb>T2n*)tg)U)>K5eiDh;(uUamA7*6+!DNI zS!sWHS5~Gqa+G+|Bd5N6{{Er||F>hFV~y~C#O=E&fa3(E&6(COQL_abYhqMx4;4S> zA_;6heLWtwSo^Wp)mkdFQ~nY>Pagc1e`B3p8M+qpu~_NM3TEEIAa8puR-Jv_&Sp)$ z2#{;|Fr-bp-ckF{(N9AHBJc=xI8on`GdKI)F=^}bQ|F>+T%rcOsBd!HBYFKE7rFLy zXW$=~*!psTg?R&|i@zzu53e7D*$Br;dWv8J{q0=qiH$gz=~m`panNXg;D-&c_yTG4@}Tw zn&&!N$jT~)LMGP{DP}-EYITZuGSx-LL>G^~erzOYrQh9q3w7dPG4}OMNW~}iM)9zS z?#5}F|7;$<+0@p1$jBNpF{=GR`h|q=aO0Pr+n6LU+#%DDllj*I%oDaqzS`F+7OXpRT*g?%ZPqa4KIj$5;D?& z^fR?GF`n%&uRNpylXCt*7m$3OuafF!VpK+#EfOdDcCQ8LA$LkUOuXrLvt?pWYKg$C z&=dj6)eHx4Au81BwFkBqqFfUu>r%1=LxY4-b%xU*xaiOo?>8C>pZ&Wqnc#Dp z6)*g{Nmu0rjGdDt+dj8$6Z`MM(F&twp)qq8eE^;XKJtJBQ!Lu`(tk>VUnIwMe_5AEnT9508m3#>Gjd#{BUM=*6ozNAs%|iBd(`fXwK-Ob-ls8Ttg8EvI3vQDy zl|!?Zt;jEqvZ<|@P_T-Y`&x+F?jzgQn@U%IzRNExcpv<7#e}I|i?__FAje|_P9dyc z=M{W}+1*$k{jNu|vWDB(`xl@4x;^Z$vUWon=`70Qde<@rq@$qpUlPcFlg+;jlw4XH zIQX!B+R@LF`X4U^TGAisb>1##R(!}(oB+Zc)kV1_u_9y*PPI@6b$f)dSo!p=` zje4PMeSzb##dt)NhR|0r224R~>H(>J>A-Ak1k>(TU_XnE{-t%I=q`irSOL>2`ITQ;(~DYGi*g7aLe$lk{lIAnMhC zVxRScI_uf__efEj%^{h7*KK{e!Q>2+_;Z$YAQqm?Fp>~$@#iZ(>f zx5Sv$Pptok4SG^|fm>Xs@ZICI2w?YU?wSyr8Xcx^LX6N5n0S}3YvvZ?hNiBCg_MwN z{kGzM{e7G>y^mdx*O}t(`{>%&_*Jgim!x_&+Un;+Gkx@(zf%0Vv?*tSIxr4i=tIA8 zE(i+I$NMsh8a>1(LbGzre(KVjA2p|(N3SP8S|!$@eiYc-GbA|~g)c3$w)$B9tsKB; zqy4`BQwmoFEXqo;QV0;w@R|B@KU(;(GFf(`3Hlt_WN~MrfI1MN9bebT{Tl~QnyQO>9KqUGV+Rq^kJ|CVYMK%ug z@Ds#|l^X#lmtT@78%CvcYW}5}dZOsVp2Q~|HcB9$HLod(fBY0jJkb5eu znL7Hekw186&EOivl;_7=9j)7#Tl&Xy*Ct^05zSYM@*jy|Hh)QA3{t<@mVPe387=7| z`b0;ZT)A^^(I~XZ<0i+kzt%-n5w&<*uGep*0XA-;)`rqnJVqQs0DQ7duwOD?Ihvw{ zQb=td6aCP4Wy|4VAvZR?e>OL?{X)xWnVJ~h!++f=@K2wU9St6IhSDq5a+inL z>rCg@I0P3T2=(91X{MjoQ&dTdbPjLjympQn`u(b%i;dXpM8k!MXWTiG6dhq9!8rfQ zf1b2{RFsgfxKV4F%DM3eAM3OCFrmKM-t)#i>_+w`IfFSL(LlF9_IR~0$dGV2$H#Oi zaBcyOX2V($o(amwy!ml1q(ay4T0kLTKC(!&E9CxC$(%-J%B$dyA48IqO0U2Dd=NJT z)}@z<%xI-S)!P)cQ7iKV!3hUM7!;FFindqO@X)o`4^_a4h%eXgPL%a#vjzyr48CZe z;H0@glz5H9?dq3r7?dpg*e_Qa$uY^YhHYg7CKh<=U_NI0il0Ym!K07wz|_2X^_VNS zB;p6!Z1I>~}$vhpZMO#{$?`!*S`EO{ST%xW*BXq7!^c@{a)*R+kr z`*Ar}qz78;GQ9va`l@uN>1jkCDr>%Jz00ZiSS4RRwRi8(&xHLbZX$qLu$sej1DDms z*CO+2EvO%P==^0;0QNvDl%|8~DPzn=T^9P!^6$e(^%A*%8UykLY2q@CLFEm)Kw9~$ zfe~?ouoT7v-l^;~{JloqQ#B^jq+w;PI|pJ^v|D)*0r@&tLb636jaw75?6H*3An-KG zf@^ufuNmmEU37IGKD^PtMD@wm?I!IcKz1!n?-#MP7$fhjwMh81vRghMUjL>0#gakv z4Zm)0_~hc0`Z~YMzw~z7p9``4tyep*K^^Yhh!z)Blc=KCjkT@WYP7{ zb)J5HP3!4~EpYPIV$8!gx<-tJNu$E${)}Wl)0D_UQpg7|L$G_!r}#UkJBVK+iq#IW z1Fn;LRLZF-udW_{W^^tft6Oc24V>Y*`1^lZ00-7x)rap_%)uhN|7qi{Ic|H_3`)1Aa=%&h~y1NbI4Vl9G^Y;!! zn5vHvM(=m?W5v+1@}go*9@?kThGMpBq9PNv4QHae~^ z7Y^4p)4dM&`sqb5^(^2%lX*VF^YZ_0hBjuBpWE^?mkVmsI58UO% z@3zlg_w>YcpKt(YpW-lYVIe)8wVkDK6qCBlM+W_7ano=Ac=qjb)c0ERoF8fTc^o-r zG&W2ibISm7gSa<+&bhyI>M*bD!95qq?a{7SZdr|Iz+TeVq!lra&Ac;mUhFE(gW8kJ zr$VrcuhGQp;C)wHzMcm%~7w12p_^S6%#{;(PKIIrOTjNi40+v&G2+t29DksCF! zev61hLeWU&N~~z-U7x%T=~KP0k!I{9itMD$mG^zn1tsRb*YBpXnuigWn}z5>m_hpB z;hSv-)oic(yV~Jv34%X~4@j8FJIGyMPL=U{=LGt(RKbK8-=CyZ7JOfEIDG5(PXBih z-AZpZ-!sSU5WOoo{}3USbnIY@KN;WQi2|eg$-x!L=Y=It*Czk|1P1pzXIrtR#MEh8 zkvM7?=(-^QtLw`mdv!M7*n{$_7YUfjDfnIpMJkKTJjMGFM?A1nCk;B!Lhb7-Qb@Qg zyrwkY@^Kv$b_WvwmCy~*gXO%9+9`Nb4oz?lN_GZD_4WLWdOXoEyLW!uD9tso-svB! zV$QO@rM|eKbJ09KH|s4651Pw%STs0HJ!*w$xsXkJx|mATivWJWrtjSsIv%E?$%NOc z8K%d?d!~c`&H?H1{Rv^)sRR{9F%ngcF5N{pPp2!~xrBA)^3L@BJPgFrH;EB-K~krx z*nJc;A%;z+%^-Vg`_~t4$D$SrEhX--e2q6{tgD5K&Eog}PTCw3Y7}uF&%rt(^_YI{ z`V&R3w(N4Q>;sp27HjOMYVK=p|CdulyLohC0=Uz^lq#nx()kZ}qys}e z3+~W|OLXPwh`(DfwSbeCRP9HIMx;4GQ%#@sN@2S_TynJLd7 zjov#bvPL$0Vby)v09n1s2htFez0Adj4@hIjuzzSNs&Nl*ixP!gy9HT8=*L{pyEP1` zN<-JhojT=m)L*({e;tqEq1_h? zw)5a@lx_MToA)iMWumaHwKp{(rzfUT#Vuw7iMZdXms&LeKMBtN1JlE8I_<^RARWta z!p4R#{5X;FQxucox4VPlJceb;&4|Te@0~(IT+xfpUGQvdH&a_S)RW2L;A9t_ z%wg&~Zhl0CZ>}RSBG@A=+T8vD%jgHpc-NPC`!2V{ZkSu#Y(W!x)ZKZ!wkLx>5OOE- z+8!NJX%!s-HNo8-|FZaa5gT2{0}}P0!N6PBrr~bJSwvr1aac;ud;9}4Gr0H;DpD(L zyPh$=L~fh4ywfDL%ZrThIDm&D647|ospDygysU9a&>(Bb8&>s>=-iFh&lE>Z5Yoxz z^+gplm#7WnW&Ws0L9u2TnwHq->Imivos0|Xq}DNl*~9W`@x7RnZ*7M$vUZlteOrb6 z3un5U$|Rpt1Uw83U;Yvdtb=Ff={LwnQMU1DnbbO|{K4F9M>}@(7{MjY8SPKjl8y+(cSuKs37_xlBV(I1x#(EvHqJr6oS~m-*VWSZ|d3y`=*Od`c*FV-)MvBfyjCgOKxJ+fE(E(?@!xZy9X7}3vm}0r?@atLB;o`GBiVJ7Khg7=$-dA&d{$-46G;%Oj9h%x1Z~^cVW5tu1hy` zD5k}~UnXK+%%cCqrHT4QFd2{tZr|`!NxY)3H<2r`h~KUt>q66@<=>ix@lI;wBUQ}G zarY37?~d~d+~5QFkn*s1TtN)S=i-AI{_x}AyZu~`LhdAA#__-2F?6?`{N7Nu3|$;5 zpg!m(doRA5k#|3y@SWo8_;J$J_RpjXM>BQA_eG!l-53NIzFw53Q}U42Cj8apEWN;$ z_$5r~^I^?98y325vPTn+vr}~uy;`7g&e0Z@-SM#-W!N2QSQ~R`)j?4j9p#uBY#cf!$keuwvG(i~b317L1t1ciy(sWr(X)LGApq{S794_Dfq4{;uL-{iLenU)f^@tf2{; zIoo;i_AE9DGf|&A9?tA!+g7`tq8$g{?0YVCGyL0Rq{*gCjc1(R zyi#ot`&C_Ukc;dT_$4fo$B)vyTYhlrF$=#agP%?q4k}!2P0cnQc}r@@U+nmMbRo)6 zqEf}fnvMjgyQ%mA|H3s?)ABD z)bUqHva{2#_r-auhw2x?uEx(KJxKelXEp`PBHs$VdE;2J^**m-+ulTE{Z%DC{JW0w zlxkCIrGL&4Jn3I~(AD<(e$Wx`Ijl8#tTV)@B@Q~w)r8s}Iik(k!N9r01{-E-92+B- z^kMm6+x-a&$;H6qbyGyG_7LSRmb07baK=5x^xeFYkgE6S#B@?=hXdwiGq2}?y-``n zw5Ig0*Ux4jlycilY;>R&sQ(XNZy6I;8?}wLP)hOQ4#nMV7~HkEYq8?)UcqH>x8g2? z3=S>sGPoBQoWWhcekVD}n>Xh<$=b=j|Lk8oS!-SQCF5M83DG3c%C*#o>DTnyE+cK$ zDV8$(p^(Oc)t;FsOX-D)yuu1&{Zt492-r zw}-fDbkD_B&qT(-QtXC)VTNeb_6)(Y0sKj|g-vX@j5=Cr+{Q-b#qtV~^?1%SQZET( zp;-v}pL$hdsn-D1>SQ})&85c0OWK@Wry9-2FcLY_R!|cnIVkKQY&LuJ?J+ECcWYeW zfpjnMe!VycZK?B2@-}NR5|=@&Q^ltC>ewE6))AiB?8!LTQ&>6<>LPXQoTusD2%gD) z*`%bc9MnGY#}-RHAjaP=%}oAmNTCtT@u$=2>Vpu=ANBlpm7-H8r(SawO|!7_;B#TG z;mIm?>7;=nROFhoq1G>3u5IfjSs+ZV2k$++Lwa?XKE=Be8)shlM4x!&u zJ;ln{Mpu4#y|1Z(6Pg9^H8RWC;A#0p?`Q0l@Ei7Q ztv+T={{`Vc>#XHN-TMZbTCl2UAoK~wAO@XKKh1i*6?^Hr5AS~U6wW>Fe%fvJLoX+D z$)y}_-EH*}5sohm_exFdo!{aT1)Eyxxm97Zg?S#CjSK@Ib-&fT1#XerG}n~krkMvdM^jgxrk=LpJQ%QX=QzFY{Nd6ETYcE23$dUro+VAk%O z8?~kv^>C?h_eqqQsFm+Tlp2j#8d&Z-`s5xEe{B_JAXBQmOzUs2FHSN*j_1DdZoE_; zCS*}g8*9I%%rt$`P|5CoNf}rZ7%#0AwGX;z$m4t6Qhim%)RMDWp!Ki~kp5-Px&ZFZ zy9u=uOhu!N7lL}S^-BwYGA91uT-3$9M zwBI7$H--f>h_%EGslI!eDUNdO{?{OL9>1xrE_CBC!KwTH;dcCwuF$xuO7KUaMiKu4 zB=-?pDv=!a-ncLUFY>tpp$Tz-MQ{cA&-ISLYS+ilmn$1~&0KKQD=oAW0P1v2o%Z!5 zqoh|%>(q&*py(y|tC@&6ODKPu(C9HDJv z0NW4|ve&k!80iHLSbs9Pg`dJc-Ws&`kdJWvSj+YD&1T$|wX#SPV2W`4`IGFKP`s`T z2p!;OI?1ZOC!$m_B;k3oU_Fb_f5;w$45)|$Ii7i@47I=K#z&s#qt~3vhs3=ktdGq0 zbDJDbG>FDrF;)nGbi}+m@i%I_X4#uw?<#kVgfK;3H*YgJ{@ur%|6ot#i6UX5xZpzZ zzv+t!H#(FE9G?unnAtUb?KkCpWwh@7_dIw9-$CvgXPbP=4!lcu}9)U*&`V&jq(%!~EZxbfJSqMb(d; z16QxN-mm&quj=jnowh+Q(PDNlSCcPQ_uwwde|bcYvGv=wQD(tQV; zSuDR4g0nxVcE9G3NpJ9)9oV=P(_(M1lKMfWULQc$h2GFB7K zb!92XHu(JU!6-(k_AvG|9JqNtE&L-eJgYy$>{L^iQ<@;2c5rwbEZ^%JiY&|B@tQO(TNF`F_*S=sx zf56?-PLdKOeDx~j*9ZLs)$Gdn8a2K0$TDtjre3Hm^Y7{1a6A0@f#E{AtF()HRbr0g zzazKtP=0((q_fSxLHM_7k-_kEF|c^V)n ze_DXoq4mH}L}&E2=KBB)r;~H2-MVy1Osb&-8tzt^V@e^Nrm6AIhrD0FAsb`zy3-J` zfX!&+&~BPjK_uY&RQillrgL1k{mZ6q_v|UhMAPH$-gsQl)6DMus4Aamk=#a#pbxrx zyD<%eIzKo4`d{7w=D{JYmyk9?rCpQMg%f&*suk4$T+daKDt{yF!i{j0^l$%J0Uqzt zyREC`?IXr3b>+qDF@^=f>=1N8R^ z!;vS+)>Ks{w~_8v^^AYG__1@jJq0Z_b<Cn}K8Q$(NO1+o5+e&8juRrYuKb^ij?!G=9y;j~E!riWRUZ1_k4}xC;FWY#l5jfMr+;?;m++99jFkD}LG%-O6o|(W4J_4|5h5cnNmG=OWX+A5%pl;) zvr9M-tgLok7wDb=>22(vn~1#>irIC&I=^UNiU^X4`d%wyRxPhd|HQbNPE}+JoS^9y zU8so*e%TAI8B}_asIA>f7y+~pgfnN9Jsj$SQUZ*)eGl%`N3rur@=}Nkr`yQ<00U52 zG}9Euvn5B)$TbYJqL&FSMIff4Y4D}v@d>2*y!S-6)+DgTSiT8RiuC2?B)SRUNdyWz zpl#t0Ij#!p-Mnq|hY1imXPE{)PL3~7235+L1kW7@dVku`^`eKGZ=Nf(>e$kB5Rn3; z`+bRNxKwPqFSv6*sWrxW9`f!}6YEXq{3`0O*O& zv3LQrw%dV*BUUfDPz?^`z<0rdm7cVK0jpt!35L z({Glz3HBzfJd5$@z41xUxb^wYo4iJ$1$&W~=O(hEIek~kBJYCmqM!C3!lk_SG(H~N z+IzoIl`DbqCF~`X`WpbqnYqZ&S41u41hcUca+aBRPx*|=aoDA({*eX}$!T)FS;})n z$(rx$`o)}1Rda=4f~`tB1(p>OOHI3c?iDT%_Qc}d;Y!AYZh(ZO;nZUeMOXnlGHx~^ z!~9-aB~XDJiJY<>|rLy==0bEs(uSbng27R0<~gUen0539d(9 zl-zWXez1gooP7-k=A61KW|(8%K+{q`xGniZmy~}I4f*3;$HUE$bE8!HiGe8q^$Svm!5-98``>)Z@%(#ONU1^GYza4Mj0QN1!<)gZ6qtd;W$)JLPSBMk(u4gaJ z0OXjuW7SYV%LE@QYmRcdR{LSO;HupuNp3~!J_s7*({ zyXr=<>ebMOMPRT*>ZS3VJ2OKGfsv^=xV($}aB$aL24%Rm@)V9r+NL z?ot@%mE4ZtAlV)AfktIfTii6s1V?^Dohlmx#a|Hr5OXA1_f@}FZ`TCl^`bN4RE~_Z zu-i8$5+7uYPHy7wG8=sw=fAA)>95qTdU$?f=tp}I8uZJp#^dBZBw(90Ze@NgSgF0j zuC$FUHerpUYa_t#NunM0+9mk9yD)y#-0iqN>TBFD8wV52Y~=+9yrO9}=A9eWc@_gO zFn?aD{N<7{=S;!Ymhq?M!d%$+(j~F!-~%qE^s+HZMg@y+YkOXZl#%X_2&>PNKIh#1 zGlmx6FgZZRFntnN)xR?WRjX#xi8L92;%N#b%{qU<_GW8U+vzpT4D|2?{$=% zAfkmj-_ts-ST@~)*RmW=9giTrn~u2Nf&U-d%^5b%*}F-(#RI2$6yc8uRbp^T2R;t5 zR-2$3cfmG3v6&N^!R60zN|?u43WfJozVGX%vGC!kcpMNYC^Ahww^bChpf&&Qy*~I2 zr+jp1xLIb@?v^;~hhNFs%`70MXYdzdd|R-T*^>oW<$*dBDHgzjpPb#rZ8zcXt>0(k z7&+HdZxNj;Lvl}Vn-8bb?qlYZrJqL z2=I#hD*acr^;X>~Q1Y>CK{#7geG~GkO&{(hqZ5$1;X-WGj`niN-FK8PS-XR;yQTt< z|96m7j(hC{KqYB0KmO?~6IaS-P9)Ks+)4g1b0pwrd06{$zOcM@<_-Ro(!MY?5HQz% zUYDL}5iZAEs!VRqCpTiHr}20tOc}OMLSbPmm9gn-9Ojy0_Y=)QFCEwPcyxLM@SFIY zDK6)$x0GN-7qn0b>!v>$)!Zf-+{1(h1t`O#Z{6WSH;P!=qC!svORrk(#9b>+T?q}p z&%Kx3H3R$ath}3NwtFy2YhA)L?)4VGZi-?Z+F~8E7k<)g^ck=B2aw-9C%5);YERdE zFR;OcpIZJvbMV-yr=|#Z18bk7`M${;Uczsu`*oz6`b)PS{P+| zKVk-|Ar3KGWLmBs2bPI7G3>ba8egwW*r&IMl18Ew9jvVXcw{Q%L~F?0RNR_-vJ@vx zVar|%=9^N};pzIi{M-(u*p5bAMT3XAySNZxhI4%#L?7lA?BnBTx;)N%b-mPRpANb= z^~$W>=#C}d1nqojE`EIfoYRRt5eGo(0Y91A;0%|yC9~`;rCrJGqn>C`8E44;sbU!T z{bLv6Of;(g&u1a$x9MtV_wOR-ExmQr>gH{n_b@@uh&9&i%^k z-o_C)(oJ%~ngmn+6z0;xxocNA)50F}-otv7+AR1ZGuWygX4NE^8h#;oBdNow!LrSv zAVD)Es>6oRv57=h8*D!&9jL zkGI@JJt4qMUe5PwM@E=kpFKPF<2O94U{AyTd zA?zqr#6d3@z-v~*7h1HQVt)SlK%LpVmBgO>qaQhh$MCddo+$9G}4^NZNeqh(O+ zXTu+j3{mog%XJ|8C)i&pclN!PKse*waH-t5YHrZ-6Kv-;vvlPFJz0R*?k;yU9XKN+ zKEAwSSc`biZQplt@R)BoG3g=J+7arJ5GKSy1-y5 zr=yevb9^a2?zwt$rPR9SXl=vHd_$`)+87f z_TUHd8(0}0%qJ2>5IAIP7&thv{lDu3<*n-eY7(f9_+Jk~ zM99prmXItrFm;0DIq+>fcG%_)pYINeJyzYTz8>(v>5Hvv?aHYs#${|#+*L<&xZIp5 z$@}C^y;+2>Xf$pX7}2= zHmm!2<+Uh>wu!U)rGGe{^O14;S`Y$#?SL{RG(HS*B~$r&W?#-KbclwzWeB9?-g^Q{ zNUh)Q51Km(@fVu*dFd*P-e2*+4@=~R4zGtx{e*KUutwP>00r{d?~~K+e*5UI(;>U* z_*Niw@W4a+*AwMbF|%~V?u&dm81j>5+~KqZJ1+PZ*hHlhdcSm+rXq9eL1c%cM7EV0 zp2x{jSq*Fdz6md<==ClcJU`bP@o3r3+R@~w*zjUn_IWm==a4DLI%W)*})AM z7A*kdh?s%XRxjfo1v#|sk&OX%#ls-)J7wiYmKliG$x;%{^=LKW_#R>2uYM`a9Dg z2=g~20Y2dTMd3tS5RQ#+@UORffEvTCUYbXynZl}f(IAOP6morj`(FyaG1tz!7`q#iOT74R`s~ z$RWPkURnn~ZSUSXciDHPYF-)&N|S9a(%plS(0ldwl8=4>9su!7;CMQUy+f9hG$TU5 zEZ2)*eQkJvqM%1H6lo|EUC49 z-fHmd1kajOPrlqm|2;8*4E+xWG#?@XS5s<(s>({=iZv~<8SihDPiNudw_Uf2an`R| zSuUbUg+5DEDTB20gbf_JlAjM&feUqdSLloUy3<(;kr{)uAVZ^Eo7;{ z4-eEx=ppa1Cus&8{BbawGxF8u1`@`r>I-5I&P+>dSwP0%EL+TdPo|SAyuC`vCM6>! z+I#j@CE)mo%$GNOwF?uuTF?~3nBU?4<}rgri5*RCgfiRc&-XdZ4NCZd8ANDpCav90 zqu9Z!LezuoJ#D8H2cqZy45T`j=bt6J>z3xdF|!R^Z}$AmsxB0&Rm}p!trnb(EK^*m z_Vm_z^C)udPInr1i@hjQ7xau+7~H=0m7F|EjnW?fmXB&CqmB)rd&km$DP#}Gw<_dL zR!??}wzdm_x*dFqbtW4!WiRxo0BJnbloJU(Z@NUpE~Qb;gm09)vLQBNBH*|=L+U_7XKSJvb)Fk}KE1zv2a>0oi%S#L$3u$UwL_QjuvxiLe1sxIx*&buEAv zqAR%dK!P0uMd-7b4wp+-r+BLI=Y$Yjtmdt-!?nPLJb7rm-@B~Ia_s{g4@@qSyRELa z69S(jq=SVOf5mj7J!6miY>9gx-;t-pePl#Mtm0~-`aj9F*E@?Pw6mFZ_3EVTK|4%XN-KD22Lg4Bv= zrueZKbrBMuS{KCn&qnr698BOs6C2U}cXgxb$y2ee4h~6*4Zi-BO-g?P16K==U zP_kijxYwOV9C^@-PP>9#DP;SW!4IK~!gQb~rhbW7F|$|=Gu)5WVOlv8bBxW|Gr4aL zH1(Ooj!V&*yS8tR0I*xB8)kVB?k-r>8OXBt9xOaXy`4Znr;2rxsVV4gJhXHsiJ4LA z7qSk{u4e?7(w{|Wpcm(Iir_cHBXXW#5&5rv9dMr5SaK2eT48p9L74?ptGV(k2D=rm zWd5}NT4-2R31))o+I4!l)ggfUyhS0V7Q~ummeS$X`_G2u0Z{ zZuNxTq+}lk=VCcBds2*SnNiQssJGBx(}= z6uVV?TCTw2F~)ksMiQ>D!ql;`s><(6iH z;Ib+(VQIDKMJt1F!cWw|uwD|(7DG2b7Gx`YG@)hQk+1E^()>A;Dv@oe6FXYXp85W_ z!#$pYn$FsNHiWwrlgr*8A0JCd z@R~Yn3!yqz(ek{LHE2nHheTFt_%}KO*V+d3Vpxs3wlcCO;~Ig3dqT7PId+nmLTNYF89SokaN2shxZ`zPN~hS#Kbx+IM6} zn`H@qtN&hD$9|i}B%4-IQxmDJywyhX7{sle>N@1z!sXA+cH&C1XLNTA2Nm&aKInkhH{uBx1gXR+1f}XJZJ3_txkgC}{oWcu@8Y zz}igM(U)0mXDrk;nrOpppf#%){TN85PMdt=`tW0vC|pJUrnpePLngRsr)@U>g583) z;PHL?==oMx_xJ{5{jSk-V7`BL*WVDD^3{<-Rqs2l@WYiPN3BYcqPe}N&L2`0T^*-^ z)T%bmE_SD}qRnD+H9Z^lH=-S}lWF@xv3YkY<)_l2b1h)7)LukuZxJhbbGuo|B-&ev ztw(WxDmJwYZ#FhON7WYkYh5RFRaxmSyqW8f+cLMQ(->TolaqsRF%G=LII}MMGo??l zLIb(E(_D7DC52#IRZ^fzt%|MwiqK=&8|D;)Qtb{f&YK504GRqQ=`-C ztp9uu<;oVlfyG?)6Xp%MM)V_$%^J3=V58X^d>qROGN=0H`Gz3(>%@Hn>1Y=j)C*QD zZp_-#Ftg00LTXU-dnsP#UIY$Ws9|R1w0N6{1k!*k#v_2sigA|-xRnaYoUPJB-PRSIC`)vnPTUX6c7(RMQ&}0Lr{sqPGg5b=FK&}?O&$x5^2Yu7DaVjb( zp2C;97^9fs=0BYtk_p>i%09uCe=w=6ys|zf_Pvaoa-*as}@}Ph@e|U z{Nxoo;@XNz;TppUW+AXZsU5QlQ)BCulY}9S#TT2(4Shs+Z$gh&hykJ zWlQg28QD3QK@Gh+8$w9!6a8`B%9e?sUn82B<3>i zYb#dBctXcjwLAZ8dRHS0F>t$W*z1=liMn1$zS7)n#(skE1cDJV*KTn|+3EEy6219s zxg=K?Q`L*vpo-_=j~eb5nT)}XfmpKK!|A%hYL4SH(aeyEE>FcF?D<`HIHO{)m`DIr zIIw`?b=?=RbdWJl!VTyyBT^v>dHqOx9H@H z(IROWJPn8EeH~ZiBkPjD;pAFL+Gc1^hd+fy{GPP(I5veNq~5fb9*S)QrB}&ol zm@y$EaP?1fZYl1l;IqrB=GyOd?kykuiwQ~5og=_ilqAmU{{^JTi%W>1qU-jLE1=_I zPuXu__b!&VUeJa%8VBB+#C_X&M9CvJLfm{eVNrIiyii|hTN@FOZGm$1^QIURdLOg= zb&q4%RaL63Il6(uAOK_9aOpcfy}VoPmP(&>iC1hQjof_z>w+YcYOTEDb}qM#a&_Hk zOXBtiQnfr3mQWI8u5}%pu2ePG4-^LeY}z!T{Ptoxc?Tq=~ad};E*+WTgV27v8N!b=I+apKLlWiCQ5QX zXw^G*Yg|FWZm$)z^$%dQao>1kKyD2fBy4Q#TTX1uW2HVlP}peqg*Z`QlP%a_qfgYNgsM2QWlt zvNhxw~|X8u1g*7_ktd_af{3x)_Zo$Hz|^l zkgi}=mNuudyIJM*_E1bZN;L>YW&qk5@5lSmlKs zWu>f+?CtesXGjkDfKzR%(O=wcw7aD?rO=Y*fsOh6sbBI_~HlIr#VI z5{<_63BC1^YP@Y}lvO=_n+3GAF+e;`w}{(u`~X@k>v57vV=2rh;=u)V`^P4qunUxk zY#)o1S?E1ATB(^*rO{@Wzk+J6|3;I!BCHdjxoPJdw&qv^q6eD_IN2LDL^G=d zroy+;=Z->xV;)@qc&!B)&jA)5GNPU-LLb{2Oz^(UoPFsqh13#>CXc&U{Uy>)mn__@ zSkR5}#)y-GPlS1rCGQBK3-KHO)vmo9W5My;Ti#cxh;T{*yO)ggeZ*gJLmo&_*G1SV za4-~oh`A`HNQeRCA){NIT(8cVE64xT7$%6GVk7NcQWHv500qH*uLsCTKK@I`t;9W* z%J!c?6!~i;>KBQD>G=@Px0e7MBf_gz<*+Yz=NKGKCn_O)oJx84K((>)5QQ$ZdhV+0 zpj{^4(MEsR9&%D!%)8J%koRzworAfZ2aT6;U1(yY`s|#~kkWJ@J{Hw>WWE>%b{`4f z0*|pkq3?SFv}G)Vh4qL_+vb(s8rm)*YZ67o-HOZYJs4?7(Fie@i4f(ewByLtr{xET zD2va@>pm5!YJt{vCVXSyH7EE)`JL$7f)j_u5bV`c?`6QP_f7t`yV!DAl_=|hCY-@| zWM57z)=QW*m?lE$J`qZq!Eb8k!oj_?u4qX$a+?k2=lHNTgX`1hU4gMkC=hUvMzLCYvGs9*Ue8sUvqHtJ%V0fMlp(J+) zQTo!X3IE3y{QqpnEy}-g&f{b_2~wlp9r0NiH-C;8L}`v*P_$FBRX0LY!LfAhFl;e( z1ta@XXp%+iYtfK{pGqvRxYjK-3C2wS)(c0tk zvH!`%T#Yu0xzLz%lswfumWx2ikY&pKUctKR~hweMPaC{OE%RTIYXkLPI7ItI79jgtUrs3ljt0t0Z zpzk}xgLIE4C4V4BBhHGYs-s+5u%JfGsQG8SVMypcvyKihNdd%g{dqOm<%_KL=uaO= zB+yI)lbiRh(3mzg#))+ZyK*%z1;3=<(2zhR5w>1o4lTg`y+0Qyy*Aug&FZ^1N4wO& zm0wCn#3u(I!sDUto)wa35j+R^lvR&v-J_|agz=$0%Wp|(~$lMf!FKWBe z6O?w$`q2JFvhsZ!QD1@PwCda<`8R~KX3Az@Gu9BzXlN^FI50eTC$(3k@HrqFr=KDc zB3H>d5wReblXjrrbtN6MmjEfg;*_=kEW~)8h_@UaEwZI58{2QqtusY6jxp&FhS9zU z7SC5R>1=mM`3*BogaJg;T#2qDDPKnt%!sqp6}<`(GxpI?sQay*k~Q^QrlbxLJ6&*l zh!ZaI3o&?VW=P4Ga!Hwd`$8{W=PP-+9ujknboZ(>n7FxS9}?tnZhy9LKp-ytJP>LT zS)AJ~?vtK>LF4hT8Lrky44Z~Wjl1ZJjwq;iRLh$Do1#Mb+oOveKv?-Z! zv8!ued7VK{sges3vS>ok-$-_%!z|1ln8Oe#1b}8zhqgBlR<_T1mx37Fg}c)YpPv@= zD%J8mVYiaAJ>fc}^0>$mrkJeacgxctZKJec6atkQaWS`Seh+im>RR0*wd=~+`ok}( z$*)ei5qN+%u=4lA)egy#d-Bk2<^F=GCvzBl$2=bsa|HKnAlmOb>eLswM=z0gWQ(%n zpY4Tl(lx1|g$(Rno_C7PMP6FP(#))(vpUNZE%@7%y-gq%kp}!uP)?)+r$k_x5i{%{vM>B7>bj6#K=I^ z$MtTJ*#kQ|(vpdWKz-*fABmV?GO7+5c_cPLI{kIW2pBa~3`@abWU2i~L8|aNC|TUY zDS?lBbDdgD8K6G$&c7{tiXZs1u8}`A4A~)CByswWzs6Qok*rP!8}M(+Y9rQnqjY*$ znKuBYKJ0n_v22~OmnN~nA%)KX(|Bn2&LDZO3S#oR1|X?QCuJKZE$A^{{xf)*uveD9 z4`dGflfXdWZlz7^FH^I@2yz|l4PjE)C__i{$FT_5)FV+#O|g%0X==mIzLIeui&bGx-L5eD5_~#G;W`;&523!-is`jUdvf1ur@rf&GvZ~}a-mmf7W?~n3lP$Vh4ji6<}LosS_Vgx%n zadoiWUq_3H9JLiWO_aGwLvG!=JYznI-~f9VqK+LnBSgwfRiTQ@P5Vqg*mf1OB3?*P z7aPsgB(L&S)e`SWU^;9fm*}zgaB$#*6^>Tepq;(X5#4k45w7kfeCv%=+IX&!U5MkZ@k&`DfN_DD9+(*h5YH0nxUI1Z^cF7JKBjy^1MXMJy1gOAscDHi1F}l%^#sY$f z{tq4NZBl61cvG+Sk7~sN1Y#enpt36AOH4SPt~dy+Jy%S|%-W;=(M`z!Eo=R%UMfUu zgq)_Eb4EiiepWPk7-1}Hhg!hims-~A5cZUqP!Tx7pA(wK+G-lX_oa2^)&5Q&#}mlm zG>P7N05(NAKJZgR?6&^VuZU?d?MT*gXED;45#YmIesN>0I|zb;Et{q2_Gsc}%ivX1 z!&ut4=JVt+OvC1AHP|B@VeMBkM+LO+?Cp3E-rjzwr(=?lH-I#%aGVz~ToTg_@nAk6 zbrm!PPuk-(wZi6|tc%?gW3JS2=Nj(D5%}8gL-`ov-R)=J(!&l|==KUP6tVskFGebp z(f0k;`BTFxSpCVbhgpzK18av7=U%2H1>N&r=Ww*)oU9i`jh>sT>bk{Rs3J&6W{!-Vs%QnL|6OrYsun@Ajjk3_JA>Zk+(Co@hTT#cwuLYsK=aFPAxk5G#NgtnL_)MiU<|eGtoEcj!;H008K=O zojbL|(<*hyQfGZF3h{G!+AWzXlK^@l=p(DuLqHB9*C5@SLZoYQ&80>;b1Mcj$-Y@C zyoJgZVfyn=xW4N0ND1F%S6NoNezDqiTnzby6)7E4kS`7R(vRCB26o6>nHX2fU;YGv z3wKq5z*&=r5Y<6cZCicOUS@mVOe5|N*2XdvmJ0&e3`X7PeNk}(06R;L8M)heszqi} zqDj|lnd-{9>B%b$KLaNjbxSC-fa}H(g9^Wm#tiOUjqe(p?6an)0pPHxhh4HCc{-+E zE*C)@std+XmJOq^wl#*l1?}uzG3ZjWM{`HtE13DqC4KI%qSgxNw?*FAXl=LcsDLJp zBER*LXAq#fxC-PJRiHEU5}Yk%jvL>lz^ANB-u$~~@{}G4fqgf*P@qCIjvl*94| z?8i;2{)MwM5FQis6DoHG{5Y%oW;a#j#fqvSzO=TSc5F_}tr)>3?jCYh+-6 zt`z+fof3RiQ|z9KFW^*B@}p%XFubo~Gb|@+ropc$9vXQ`ALE$jHn+h=0|DdzYysNj z>95SR9rMZ-(=m$X4z497b3g@~_dhN?bJ3A?7JiM!*1$jGMzt*S%_bqB60@&KycNtR zIOlG&2u!nlpnZo8MQ|0CO@CMHWhMD(T~YOVk4kr8&dM`3F_#@2&DkR~C_qd=e;BK$ zE>O|PvLUCaKhdfZHG@ON6i8t`gY0>TKY4Rh3`D~z!I3PK3@9C`@sU;}Mp(v1PH1^q zzQh};!yS7DvTE(oR2-=wfF#06?6oBe>FTA*Immm=$RSfwP_hNGAvT4j|w&`(L81&HdjUAiFA zVD8<4(ls{*^4x@zlc%%0h3X$}w5${MOX|MPTbcXXN~Q4<=lcY`!wmJC!;W@1jySEW z_@&B5S2#h`OviE2>tr0wrIN5+GabZ0TWd$Svt5Q~dWf_$=Vok#G|D$~sAvDX)ADfN zy@#MP)-+agXRma@(WKae7;C1Oz~(vJ<<- zRok#pxH_%!TuoZ=V}ayk>gMtEBHP1M0>uZ3iIE`%2B*D1$@6jld*)}{uhqH2<`0LV znr`g<^`n8nkEN;T+oDsT+QgFSa^^4U0}7_5l=khPa6ZzzF$q+<+$CWD^;cP!t3SBD z^?{Cf=Wm8)LyxfFE-gVN^EUsDa=M(N%qG=&b`Y6(v6Asq?{Jgt)^2y0sI@4X(pB2* z+5AeG9V)MnE~$@ctvhkm#%I~XO5f)tOV<+3MyZP3^KLhM$uQUpR(oUu%146V$wm5^Z z&+$>_y3XWWeTpM>;{VWY8dW(MPbG$?xt)aXr=aSYy3Sz38Yws18Mi-)LdnIMWl=Ip z{qFhoX*2`Z#2PXR*SVb=3{6Z_VkRYNof53*$JHI*OF|3uCO!FzeTVYx&8$_k3N9aV zlkXJS@naU$(*DuNZWOGSjuS^wliPo;bBV2|D*Pjap9(W?m`BZjP$}=8GvwnMgfsII z>@{f+t$XTP*2WtS>Q1kWmRaJdU<6J8&C$?x)bz{@BuEm>z30$aQVl;>QS$S!Rtl9@ zm+Wy)_-a4;`xC$^XH21G&Wevyslp3^bcNA#Rd9z1zB-5^a@0jz#&iQB#;7Xp=2!iS z)DA9b89NLNW&vyJl!Tqj8cLQ7ZOakw#>Tep`YkD0>VU(rB5BErN}B5{v)ba%(cEbo zG(3&-UvKPm;zD1rU5g8Yn@;m+c6bhSuAJjlAr@3cnrS~xJ5&jJ)Gji6oz6m- zxyi;vy&6RN0HYo?t8LdjCv~Cm3)C?4D@T!$y7LH*PMbMZiE)=w0t;`fwP~RX9us8N zu_QmV&*$@z3OTU1$}qUvxvhpuY=W?C){a+2Q`G80xEJX>A!$}DvAD=2IH$HBswYoz zgNYVARBhm0?M}O1VQr&94#McKwwLyzj#Lk=HWu{w)x6EGewI1hqTZ*CmvW_ekz65I zcLb~|5Mhkq$8J3wdKs@v%!9QK`x};v32Mz(u|+viPywxp*xAiF1dyJ6jm4YL%QQ+< zx>pBAneG`kH8Q>Z(p|eYEk=~~!{!LuORFij$Oz{gd6=_~f zwP2GBL)IyO>98Bi&jJ|Jm5P;q&J|pv92Kyt)%ysfaUTy)VvFQW#YxfO_^Sh&6DQ^& zkR|XCmBHmRZQ2Fy=~5c*l7Rd!CR@YI3qJ37lNju=8Q(6Qq5kmBM8@6(vB@C@M0~^1 zV680XSJ>>X<&3pB!8$n$TaEtgjs1fRy#{bTgSRy4l8cl&5;)R{BrMQZsXr zQ~9cV6oMa#-6(C!cGQBhN{HYE=-?1B6Z&7j#{WDzWxU~e&}?`}Ba9t^LKhKcfMWxo zqy5}^$}ILhV-@{y_%;ywhuW=4O;pDl*oMv*!!{LiBys#XiMbNh`K(zMN3r0r!<1s!o#L$E^e!Z3-K!Tz`D&wjPs zu*8?3V{Hu(B6jjsa`|q%z4n_cemyJPb~tU*Tv}iwzhyiAciTBj zM`2|rNJ*_v|NO9370hZKcBhvyIJUp*SV>qLhUOsv4H`85^Wp!n_8w48b=%souks3r z6hWkgCQYOXNUw@W2L+`UX(Ba1q&HEdNC^ni2`UIuL+=nEKR)_zcKFk#$YhAH#_XT)|&HK&wTb;bLJtV?x^%e6Jn?bvdC^XduK|aauy)vz?p|} zLkbxu=CyZa`?&DxMly$=2bxmZ$^(J~KA$cxleiAIez{R?xq4($gZWH$Gv4+k+EgC2 zQjpK0RdLFcI8fPAV(KcWsUK1hcwO+&pum5d*`9XLe(HYN$@7lmIfQwsR<-i0Er>{> z{uF4&5mmRcQ71vmT8_R?0dktCz)4k2|Jb4rtFJ(Kq%~`ij4$kRADWK+c@4$P2uB<`h zQ~K^#lEPeI?l#c+o<0n3I#AVMg)(IZ*&rrIa?J)%O<3#5N1m!)N4Ivz-x(26< z5kSKmF)g2qGT+`r*m3Fu_2zi*$9-IT5oGpq;5~IFA!L^|zG{7vP+CE>YX~#ocLY^k za4+N>&9#XqiTy=W!}6C5z#N>Wk@DyLtFkw)deV8{p7AD*F3DI5OOa0H|3g5)!5Q0wE)aL#&c}?^#CPS73dHdx9t^I#YZ@hDRJFP)?z22`wPoU@ zs<)+<55H3cSN+NFh(&V(v*I4_yz9x$j}_jM942q@N~;{!u# z+akF@=oFKl{D*#x1&K8_wKvAV?Ve|P}G$T zzdNNc=^Zl#vS)bmUqSKTKhHA~xvL4l3tp6b{kXN*I`%ft6kzfaUy)M^Fb(MbZd_{D zTV~m3En;1_<7iy*cKqkJQj&F#FXJWSa5a$ zOp59RF-WYL&*0K_!Kv3@nz>9UzOs)uwj0nekJ2D3N{`?&L1%dy2tL$!Fy))lT9cZf zm#kds#@6dIKHCHmPhhxRJ7$kxuO~%U6jWI09eYLhJJf~Tfu@S#S#)_L-i$tn^Jkna zf4Y?}B=nq5t;<{^!*AI8m+?j?(7osF@sefAi%jHZ@40Dab&Mgqbl7TxvEzn^zk1Kn zR+l_v$T&B5>;Blt3X6-Zal<#%EOydD0@+}7og8wCyEUgRPWoo1uU!838@psRf^CWh z8%_V&!JCQ1mDuQ@A`SV8rXvmNf(MnVck&HJGgpc!jO?sQ1;vEQ3Cy>^v4NP{q4Pev z_jvYNB(N(aTV!-!bAYP6Of$3~**GbaL!o;5eqCIO^?AND@V*2hC&iSrDc=)2h>{`C zmSZq)|L);iufD~IX~(_oFdBu_(iW+gkCF}37SJ!jjQR8n*%N-ybbNZ+N2L_tbg$-l zE_^in@O7?#sB=uLtvaTmj~czY^hvdR#FZ>v`HCzZvC34*!Ls5L{M_&Nj2tgsFMmqropEfLpI-6f1g8v12?+s^_>D` z-@cp)3s02^>j%UmLFj=zH(Dbz+h6S-3OvC#BUv~FN5V;i+Fsr-kNR!L(~eh}JR}=_ zHerE#5%WteR5g*fd_)iTqdxm7=ZT80W8>;SU_i4Hd-L6w{91hvyge{QhG=86z|dP6 z1xju*{E^N*4&5s?wj!Vn7@3h1Y$M!$&uQyrE0{|GdG$`CbN-kB!>Uo$XGoOxM&|v} z?Ag4#<-m1eA&8rT1YR(?hovYcAwU!qF1fFfQvhVs9zs^!up^>mAU~Qkk&Xp7e zJj+mG+g7;#4p(YEUBi!|(2YnfAqmG&qMJGjHGiOhyViH zG{KWQZ(?9W0S@qF0Va^7tbzitzrX*x_B&@F5K$0npu{Zd!e1n)kf@+pug$E)yeS*j z^AhobDR*d7T)RtiREj&(o$zO3D}gT9a-w{$ywRLuV-k`icLP1%Pj-1QV|$@npCaXIOT;S*i)EISQp^{;oj_QP z0MP1O;xS$HQfEhEIq^kPRFI{CSk4*VP2Mge&xOAcgZ@yV|DTKuy4;aP%^;7QQJEA&sJ9R3_&5W(B`O z#*ZcvFXc&9ui{jZ*^_&`7ZdK*s1@x1RR6GufZ1f3)cIzr5QwIykg?_VPqL!h&?bv^ zZ^ql7XJ2G2C|Y6GD{lxGq|_=OZO|498aa%QdrXP5p(lFRcFqNS%xKLAAp9O*jvr+i z+zz^F#l)oI)7ZcDzE4;n)KpxBH_ltiPN^E#C-y5BzXm8#bmwZo1=$X7NGM&cG82yOOYrA-qJdS=2-2gfXB z!d5H$;0@o3jjLjGy2y`4Z4(Ld#b%VKOO6xsm?%cxNFL@3NOaIRQ;UjMl znBTXlu2U~g+wZ(%Dw8AGJ9=Qa(ua5P8^aQQlTJIMS@5W!YNG8bPw@Z6rkg9%E~7@% zAy=9t)`LF40DI{>?8}wa)kw@c+gYuR26P$HQ!-uT_A}Cg#ICnP)VD3K7TY?eSe_-z z+FP&Z@oX|}Fv?_d$mrwpD|jy|FL*_7>UrfLZ97dmEbp20fLCnIJd%%I8?N?62o-=; zSO%m@Y+w5QOmdU-)cgUSs4Hv|#+07Pk5$-ijkUC4lk^_qSNz2@9C2yf4W=-R&}#X* z>A~E?g_X0uAgN5P)l_^AsSpx~>%lllBe4=%-nD@T7X5a^wJ4ZvV$7Gp z%pNK?50X~MZ4v#$Ii(FAIpkCNqCLK$BG&7jBhC;<1>1u!XV4qDTXlrB9D&X<7<+uW z^cYtp%4?$m2Qc#P zVUOhh%^#RnFL{a8>`e9ZLVthft^X#$7iH#tp2c-nTMpN4)>OgSLpc*}s$F9g8rpcf zn380xR=`Z*INuq?T-jfWwVb>!y(t1ZLfY;q`4?a4`R5F+Zp=N5uRR{=FHNgLvu|W> zoOo2)x&##!%!YkOV0Wr=d;f@-e%Lhp40X-aq#W-5FjCX{OGZT2lE;!zb3!4rNbE?Pi1g|XQY-jwpFP>Op z=R(PciPEhvi5gEnTsfeF!{P|c^xSg&sL-$;Zkftn4Zo5UOOm8@-Bw^~rJXTQ1p2g^0O7^d9&4_D zo6T#;lh)T#IeAg9KkD`cPz9`uDR8P$Yr@Qh%=Y!9;`<04^x^-}f#8x3gz^#`d=QMw zYXN_0?*LG&i2rmZrz?$?vWnJv94np}QQdf>#N|gGf)(X5sBmR&i}9yHUxMpUI*|&wGl$!-^gG zW8@DLH~(-b%{D@8;0SDs3#@(agALODwA`Y`ysx50aBS7 z^tmQ&5W5rVH|Bs-TXcDR0At!VD){5Yu9wRByHuo3(+jRM&m3}pNNdaK(TM4-gPcsl zVx*49ZS6M`$dxGIWt8jw`O%=Mk|?C~hGAGtt#chYMytv(z7F`I&v~R&?P=Ob0F#qT zR*aU&WcK#dD-e^1)8}8@9&^{zrhZz-M~Ua1i;qt~WlpoHP@Ac4L5XjSb7pC6G?9x} zKSyvgIP0D`m()f}i+8>G-~P)EkAJhN|2Qms`YOd@#DUR;+XKiQ5^Duyf9IDfpRvFZ5;kSExv%Fa!zzL1G{ida5Z}ee5OTHs?_8 z{@BZty2IX-XQ>?Zg6Ayd5CDg3kp;N6&n4{==q9vM*5T?YQx!-qV`(AFrG&}uGK>62 zeoF$Up51n>xv3!?BM@IZ;KQ26u@JBcJ%2qAz*124KW^mtcd_7~+x$N_e*j5VlpA`^ zz)5yYf@UQASe^<^s$Ygu|Ebvg;lrC$;#JqvK^f=m?NHBHzco3Ra{{sb(~bXle%|f3 zh`6+-Qr-H;FUzZ6#kFj!OrTvVF7MLhX69Rj|03|=glJ9RnwQ5V!}pIK1t-2+on&!F z)>;1Pij#!qW4X$n*Z+egcB$R|=gDb~-vYNvhNa#goBTib(tHy&p(z1qhAG=WTE?HA z{y)y2gcIbWo#%GrxBoZeN{~TnphTWK1(sDkL@YAFMQ`TYKlM#J=zlSN{;mD;M$P}a zb?-nR%`tJ+P8>1zJHWa0I+p7M!BIXcu2C&@`p=qMko=6gjL6){>Pu%3l3M5Zm~_) zg7a=;v9j7pQX@aG58A%Dn5mcrMTAW)tYt;GNyq{-&da99#*|m5lVXBW5I!BH)%`pf z$k@JTf&-3n*>2739+33(CzJjC+G|BCqVoC%xA*^m`oDqyF2NxEpJc4({3lxc+XsID z(aEtFLDuyGmC%>anGyTt{e_B<-E&ljEDl(?<_{ zva`6jVpm@7sD=YXDoq7U95&|4o4#c&3??>(SpTU5xc|khfTR_oD`mh3FnI^JICEw9 ziW-_rU6_S5_-$cN>}y-uj3r5(usY3xG#y5vUrnUOb2C zycb(H<&}XYy0!Y`l^%WXAC1-4*Bb!nIjWP^Lzw?;H{cKgt8HjBj*OR}a%8tuFImSAnb6G5$Tb`{Go@)0bU;I)XKq9}yxQs@^DnDW?8DId6TJ@a`R)S3NUk z!v0KWUgxEu|IG8PehNqU$WeM$nlEWN zBubPg&b~AIVE#Uc*3cly0$f?(*fLuUoa=tRt7QKH^|-1siyg#c5p?8S9<*!TA1p}% z`FL%fMTVk#4;(>@+Z$gdER7F5sPZ`>B+bHIg#GmQvx813$U0F_P(G}_3v5ZYT=$Za zm6D2wFVv4S2utQQy?xq7N*xC0QOPZ{g-ZFBjc)Cn$nY1>N^7KErosI7oaQBVLc0;e zNiJQfq-{dWCK;0FS}YmxuXi=K!d?l@A$-ZPILHScd{ln%jUTF{8nyQtx=qf>@OO=Q z&F*Q`88b~7F;CWaxDJqjox-!kTb@0=uKd+2XGr_duJlCjd4b?Ml7xB9xR0oxj{*vKzIU-$%6G z{`nn-Jmc?)@lD+%d4|$gFL9gWdRa)Wr!-+fSD6*0?n&~)0ry^zXr5MzrvMwODeFqR z)q9h;@T6ypZV5lGDbw8zK#$x}8A;;din{xP{!F--mg^a#kyKYEZOF{Nf3{O;=Hv9e z{Yy7o#uB+M_Rs}c&>;SbiRHj;CoWCtOP+WHwb<>6g>3yoUPK8WKKH^`%ZuO3l=_fN ze9w(24!-_?nQ}NeIjb}NexXN9S8S#bi$>JVI-jlGXHCc-wbz{rHrBs)w|4Dl_9tvT zJzc~^qMm7g>~x7(PpkHwF54&Mz2Cy}vwUng=+Z8)ujIRX=9Q0&)tZ_-ay5wgB$F#M zM96{k5Js`8pl!(@PghPyQF zuFUG4WHjh7pR=(|T z5E>6pW%Ym_YW<-q2E?oNBe=UmLaiV4i*AayqMx8jP32h3z33XTK9 z`f?QgZD>z!qeqG~1XB=dT?ggwk^S2l`P`Fl+5v(t+x!andxf*J|3j`MZ8wG>Z{LFK z+(GDg%KP&Ww^~clvB(GHBRI+nmOgqa%Z>?#BKoEzfR%m=dQvoycI&hWt+=;`+pGWw zgLWQw-RVe7B28EN{0Iq;=f@XjQ!-oA$rS0m*`_;RohO>oN*c}4f`1~VudYh)5U@8* zv?3H7tEfpK+CJcN-8YI-hQ7nJhWmR@f+X1$^ayMydD-A=*PEe=H7b^1*$&kR&Pt)T z%Y2b^4YrJF5&L&db;EYXY2!z~OlB~TLJiIyTM>Iub+|;zo=LIb`I(?{X7UwntCtz_ zDM@y@p>!0v34M^mCOW<7Y!Qh6*0!uvT8V_-(OTX1J0h1E%|D2+*zb5Y zRnfQ)q5LjlF8u*G!b<9@h-E;IK{{e2Xyq-vcXGs7q*yS?ujfNn#E((cQ%CR1Ucb9N zjJ|-P5IpG&N0mX?B-k=SELV#m=fxyg)S~g6-JRD2Q9kXV z^wzA6lE+|#%^W)@5;ScDih0e)TAwK^^~sFbYux9V48@{k__akpv}^eAe2nMa<+$D{ zcM4e<$v2)W4?BxPcV-)r9PtCo&7G8rDKyfgeWbos@cN+k~|N-6XNc^{x*5 zPF1a#-L>5ksidW)u|yVj0Y3+GWtY0536@Dj3&xOPsGHJ2PEAy6Sn3j^`43g)_d9+s zwK)$3!>gJ%L?8LP`+iq*Y`ncnw`Z4{EHfAy6K(v&mN%6Q z*?mfUE6Jp{n%&du}1sCZOJzO-+m)}0kU^jN-0?-roh-Nwohug{73NvOR-Y;ugte}2z1?9PCfQF}{wJ+#PNYZ)T)Y z&5aVBEyOEboZ(BPMhGR4jy=EV1E^BQ1;O{FM{Te%>9@RE7mWipJT8?|OQeIfq$0l= zh?1Zi9=UnL^WT~mZ1z3eAI6mQ^T>e*3w(%TcvBJ@UB=gK1wV8Ve=LHUYkqhIZ1}0@ zBS{L`h(oY)M#i&(6<*_3HleT>AF$m3gx2I`j1N74D2082^fvIJ1zfg|K=Rit1y`Qz zRr_x3Lr}`eLXQ-VN;wvTS%$I8UGb>-SJhI!XW3HH(hd-XpjrQmQG@3~6rXwvp2bKL zvJ_Yg65AUvGhZX6X5~}zoqQ^LzI}hD|G$=0{Tqk`PyLPtrtYGBSp@&;qZ9qh-&dbI zFQpJ=uqVmXXQc?;a_{7nu8k*AeQ3W zKZVF?x$LaD$*-+*r)SOTdTk|-uf@rH5V-l+OF0qa+nt2emZXE^vv5x+05aQ%&YHM? zfx#!Ovl-q_u^V6D)|!zBCiVM2VlL%GvW8cCZ@Jpk6tWZ2En~{c3YI;F$$J&v@&u@P za!fyGITE4M4V?D5B}20hAF%F2%tuw%n|N}yF@SwiPj+UNYoIe^lq>w++{|0Lou(8q zpVq!jPgMh3OLy~7PbnxV9~$T$mVRL0x3fdPu$QTRAAfgU642oiky;<|z=_{;Hsn__ z+)@WFe3@tdqf2Wt34(>U!g;%XG*zeCc$(&N%+E%V2w%((tlO5AH{(L}Tp*JSfaTyG zi(KHUD$HHM_pDG;+R;7T?D~K)u`+iN=8If^*lOxtAbvrzl}5{xwTJYX2}`KbVr}3o zOd#_y_q3a8ufb{sy-QTLp``0;Q`Nz2f2q02MPUW3v3dNKvl-%?$G2fWg1g%*m8T5T z^()?cT0}ET{!>1-GaYZmbvf-AzZcwvp~Srb!)DH5vpsW17cpTVa;7mcS&Nqz{WFF4 zS>4eD@A9Nt{u&rqf(hIVC_no4%e1>5b#Z>uSDH@jb#h$C`a*>Gz0!i8oNfxM=Qit> z#DGP^AjPMTI5&e7yA)BXqmmAz8-rbpn|Q;fKRgGc4~efIUO20^|_sdm0$;CH5rKZmrhXb62@Lz+L7Li3>@KjumO=>sgGi_uksvR3TH?BLLalI&xoA^T7Aw%wxPPS8Y3skbziFOE(P8 z4r@b_@jUSqYx_lI*Iq6vMeDG5&hIHE8*`wy@*HAK}m9R;d4U*zL+PgVNrb zt?s1SRuh`jJTOG33v3U((H`O_|8(t1{1NN&ZcUNJa%RR*l4gRVSGyT^CquPu>oiMP z$ap7oz90l;+3QT*fn70^F0C7HSRU(f341*{UwV#6 zRg#L+V^cV`nf;JK;RdiV|2Xq^rhLF8Ku*emA`{R(5zBm2$6Dlr)E$;c?AC-{Ur$1= zYQhYa*J#JSweH3%oHYj>1=x zgdm8`-B&{0k(0&%iaf`ywX-1pVf#PLmfWt0-%fyF-W|-hvr7D6;U$VmS*`v>vC)lm z)@snDj334tLlF0toK!now%_T9Mf5r9fEOozS;ti$S$QdOL#vlZyuL;3W*lUo{KL#{ zwrj*M)ld2PR)D$dR$M!YPFtGN>F2sgpVw0@(kIJsyiXM|@fF6*YTJ7K%(LEVM#Yi` zFP7X>=y0`Zs8Lji-%O1{wrod8K9V2iyr4l}Eh!-N_LYQ8%X<5RZtq?yzBQw%lF#6o zu(K4uSXwCs_jpVSVL8qHXr{3w+pu+mbt{VRkj(}An^;wmzzSXj+!Y&NzL1iR_gvu& zk6-Aa6H2>=mh7A!Ij7vQo<+UFD}qG*d^PvKq3o)d`dZs!KFZWfva>kIuRib;SXTLd z{`s1Kc3AKFuF@~mW~4V~`Tdw$>b{(Z=gW@Us~8`KA*y&kpYQA|IS&RXd%( zbk2xZNLm8SBodb8{^o!G{U_iF(yQbFF#_z!8x-ln?fj!DWLJ-LD@W zaQE1XQiMoyFDiyZlGkRZkl9CWAS}+1X5QT)K_ScZCYQ;w+l}c(PKxI0=gQK8afu9_ zRCc}%lJi#MYEU zx)_$4^@gAUe5oa>6f)*)Q4LRQ$9Bbe(zLnln)Zk8RfLw)e3Ut3U-JYtFMU1=iC7ha zv}=d=+M6~g#B+RS0b{Xw>Ex7)?M@^*v64tYZ!u$x^}<4Yzz?~&a(0pPtGfJB3H?P$ z18}K0Db_$Dbp_W!o~`WDty+k~w-P}6SSw5M1hMlm<9lHj$7hbTfApFXae~BRddoJY zS)f&FPsK17neWP7aNtcX4w z12(W+48wl1%~Ot5sdYe{q>y{>?xtIhx@FwEu1&d%$v~hT)TfAb1m<&5g<%83<+R?3 zVv6;@v)!S7_PAE^d%ru2A7SJAnUvL}`Fk$2m8rM;gAp;#jTt?AyHDZ|2sgIxAMGDr zqU<{{4`76i^H@-`EAdOIkgrAhH@>WkyEMfXD@|~+LMIG?Q3~pa15x#o@7{X_n4MkU zd5e;f^sM+W_tA5D3FvcS+0VMam+=17&%)Ld^Nl0udx0@ZY@haHLGGP4Q$-oDqPa5& zvO0J1+_Sy9BIqYn5ON@ueSzPa!6eyg7;)nY*Uc3lB}6_Z6b6&+VvD~!R8i%*=~ZOj zsE;qg4P|LvyP?g-#ukqJ>o)QHY@};${#gglv5$xsL^|b*(H9>_-$iPMQjS^gFrG!g zSm1`7UoNZhqoL)SeNYw^Z-b|c^L#4q7K_QQE{prdw(`C!Wkip47#sY38uAwQ3naW- z0-s1fsFgbGmjK;l;15Ug;@ebh{4qVOnX)$<+*l-|HIjt38nHu5@nk8Uw^$0CvvgpZ#W;JwGh)GGLdaOmpNxujoM3+2JfW z^J1?;s6P_`%Q>O^D+ytqMSSamV~7ja66e$peE-i}0F*6z2nC8)S$#F;s)rR%S0Bh; zNPMQU@Siw(2;K^!t9@u^y*VYl_59Gn!PbC45+ z5e>rA_s_>%q~GzxPgK{6HBODaV&;~3;Hukn{k2{7qar7Rr5yhX5A$*#C@ zQqBUPuV&jmA?Y;{%W`*Kw=vy2?ue)0VlZdCP!8j4`}S!pZ*{2o@Ml>=)7b;c#qNDH zJrcQz6Q~QjD$n|HMqvMBuUXb4t*C;0BJzBEMX$bUR^wu8s(2h(=BQNPk!2@v!gf@B zG1oLpTYgHhaLo31Lk|*eL0-?f?R^t6MekzmkLa#1xx_R+HJjZX41w!iscC8%AXgAM z&C7*M`kk%k=H?hQZoKq^ysIYcO2_=L8%rD3sf_8}XH41@Eft9Hcg)~H4;K^$NUl#h zOWDLO%hz!-)8Cw97!WUd7{aT1_7*OlUnn2U-%$Ei>Zy0(zibCMN1PwY2$K6`Qu z@m`GB+dSFZNW*L%e)P`~b*ws?+m_xW6sE1q<1NM7f(u{TLWaZW42sjZ+iAXs6lc=wIp3F^4gJ-jR3O-vud=w zeh*BMUo9lXqyr&)H9%e2(=;kBrBf1+wilDPDHM~-*&yi@6ErdprN6TuAeE-iEa(^z zX6O6z|MZ}Lnh-xp$P&q%E1BRDx9R6;(13isk8Wm2D_56I)2tWn%CO19j>fZ7kk2kF zUGYus<~m`C%ulCFtOL0?Ox*GZ^Y=(kK*!1IGwQ5nI6Va%e;cWL$1Gp+3mM1K?qV$0 zj!Nc@*KI-7e8bzu;u^a=2>#a^a``ra8y2crSxP?hrfipGa`MnjmarJKHeX<-I0`J<-91RW74uM>-@s& zU!dWi4Z9sI^XMyS7|#>(V76acY0KhjZ>YDM&PJiz7o!#zqa%kX37F4L#*Y80rgW64 zrr~D9yN>vpq}mFbz{4z7bin!497HW@#}`@O;b;+fcevUalg-94MjBLY=6&@qo7j+t z!rtUNgc71NjY0wI8h$$Rm}O1udAkL+UGB&O>NnFgz@Di6HPDJLB5tmw-`DVOD5rJc z64TuHc@GTR(@5z*f6Wi*4QE2p+$l1e`mmC5M^awGT-ZtIbh+z`f)q75SHdJ^ogMS_ z`LcvZU!1A2+#YiNt2!w03=_t?NkY*faDxV`hQ-s45-;nWlsyMq9f{q;mb?HQ!}|kT zRW-qMS=3+{znxv)6S^UnLzMD8*@G-q?ewyxo!7Yby=*1YMC1gMj@AQN<%+EEm;9m- z;o1;o9gR!7@@@%Pun#MG&$l~NPx@2A?hAP22PW@cN&q62PCUJBSO@=V{{adZ<#)`N z1l^L8m%JfF*7T5?W~Sb+o4x5Obbb_0x!>n|`t|P$6MQH*Mfiq-Y5UXL2c@no8oJ#5 z9h+Z>eLzO!v?xqV&CLS}w`5Y|<_36`8kLSz{c{7+w=@u)V{m~J6Bhl&9 zF6h|qfHV;3zr=JBf09#XjT?kTRzzf2Cqf)P!4_j}cqg6>*?zv+=;F=nWd#nN!juUB z&Bor)WbpuS7jyWFHe(UC$CG+@jyUMjyoP20M8o7zCP>IYJ3~B=Bx>}RD& zVU#>80(pUIy;I-wB-)NRD^&19l7diradUbdM(kesKwpRw$UnI*-l^tWJ>Aur^x0>M zp07eEc!DeoMQaMh?0VboiqcSWvBn7NigLpSB_7y@ZsjzO`g8Q5+^EG zp^R$wY5EHvSmQNjK%|7-yh(%EqkxUP+!Nd}Ff(1@(BD(GWzaET?{-pXR4%}rQSB`? z6<4<$?$p|E#~UGYjD17vbqf-~D<1_r2qJq_0uMg7AsHw+t)KcP7D&x!pxOTIZ&52Z z%0PHL+bYY=hUOBBXRK*t=w(BZ5}}7YInyC-wb{qVDYT0D~a!~|D^l|1hJ zH>N^>BT3$?T9UH7x=IvXU)5cPvh4#4CKBoDeG`L}4}n+m-F` zS!2Li)XECyoPQ0OWLY+bI7u2g_^CN0ITubj_|Guwf#p2yIhmWRUU}N5#tkBJYhQP6 z@Rl#h*cWP8L-Pb!7M2mcyD3GIq8C_989D z&#PN#%@_$Oct6=X%9RepQ(e3n9VWN9*vL#x|HxVtYAy9d&Z*{>J_rnRM{CwQUYwET zx{1)lUt^elazmc?Y|M8^hsg(ddShYZTsZGSb%>`{$uab%U|W!P>^P(DEAB?2OvkKv znu%VmH28eWWOOub#N+EpTr8%vxj8}BvYWY;?Vapq9q{NPXL^G|MCHPQt8G7#3={xd z-_4pwKbE&mYU<6%S;+(y0#JQ1W1nj(_jqvtI-%%IF)OW7`8pBL`ouXYyUnA+T_v-r zaQU))AK-`QrhJZgYK;!0$oj#;KgbO##SUexbHCC{z9%l_lSELI5j^G~L?f}J_YT&r z?Km?Xw4gGz;I&R3)-dKXDL=L6T*Wq?zPUD;XtYF~()K8fF=Z`g+Ss5&duS;0b3p%5 zvG_Ty!k9qe`hy%B?M!C+DW$np8_Oa{o6FlFT0qr#vB`hUvi~wv(>f%D<+LPp%-ksI_WtCnW6neNkk~t8B^N^YeIdPBN6_rqSoOA9qpybr zn4;`1X^1LiQ%P{h+3ZP^g!Mex`P)X^5p*s{mt8lF_4C;p{-ed|M=70L&K?***a71R zacNS!5^_s7$~lDLyXM^-8^x-%JDHeF0+vGp{j}Wtt>eLmaUbH5FW?k+oY;Z%wH8$6;%dQY|MwkKSJIu$YK-hvJ!k>GS`JjllP>ZhzUM zMb7vFU0_^vvw-xty%l-<&@9;pL@Y^|hWd9Z$TFq;`9zmM(>@PRb?}bQ z>*)eQS{BZDqfL+QB$fBmnu>r9^P_x&Z9GXrQJ2P6JdFzVb-TKk3wrn3>9Xl9EbRPe zLd#po7JMppyTLl^hMQ`0(gE#^$-Y;!O9^#|w%RVbt(ORI(%7FucJsYG#8@_Ip{EgD z=TtSq;&FmHu`d#lMt)=+(Je``O()~t-W{6Gi*ZmGNR!W zn$V`j?Kq^P!CD*_IQ^nyeNZBd2RlV5N!1ATU*S7Iv|uqu7WgBhB8&dE&)HRNl~_Y5 zSvrLTF$icrBQOj_cNio4-x)?11&HiOi(m)p+EhKpVuvLN z`_g~?O*YM8Qqz;rdDgJ-nkzbP;H*=bSPpAA6#=zhe_>W&^%}`IcP;a5B==bUVqIro zk5GK5(U0|f9C7?v(TO=quW*$P@0vOL*}Acu%@QL3^y!)9A=LX>j|c7f!A|_*mu@4V zxDDubBib35hTV~dtXZ5h_^DJzAY=tWRq*OHswrPh1yE^N&?sdP6NB)i!pjJM)Vdn` zW@_VMBBWYck_n)vAtX{#%>%U^me@MlQ7A%J_kq>+v2do&og2F?kryY-x! z3co0Xv*NzG|5r;(E+<~?=9A8j0WqoISKs0XoD4r)dE|TdDW{tHbbRxwN0(|f6+Jb} z92O-p(53*Pns$Gz_@)9;e?uN?g_$Q34tw*b=FQ;)nhGL&{ z$=;o4fEY(vy}QM1aoS49-9b%x%cbNML7-?AT#j1q_&t5j$V(##DijLJ9Z_Tss_Gciq8Q?&bQ@35(9^r15FTT}cDCJJQuJ40Ta1{CLXf^4dk z5BVQ20XxKfzmH*qH-u9ogligfeagi*bKTokPnEaB{nVav`hT^x5?_&s%qf1fhUT-Y z{Baq9RJ=uU72+?xy~YC#o@Pw$RF*;*T!)ApQ-j9-Bt z_S9?Ec~K@JBQ8`dfKI8Tlp^$?h#HRX(xO*-#Mx|d51nH^dmIQjZog4SsY;j83(^^ub>`d~0(UByy zaKdF>EHvqEn$MDGzt*c7sV?o)c3|K^T0C9CPj&fZqpZQK`yiK4zSJLE#__dg&WE|t zE~|1?IdfZ1B_^Atg!O!`Zr4Q;le^YerA^Jee?>VWo7ytRdrg8v?|HNAL|k&;iCYE?sf=&LY2_c!+&P~2`Lf68u=q=l(qv6#YQ z&Pf4brpNwOBe6DbzBjtb>Ve2@`zEJ$E3GUs)0!Az->>3g?u6Slvo!euH{9(`UDGB5 zfoiqWJl?*JGCw7dL(~bGLiOXm$?*YcWWTNYmK4X?aSP?z-jbwqS4QBnP;I=nS{NoU zhQfKP2yvRWH{r6Nme{8x;`lfEQWx`%ppxwU)cE8n>kCo#Z>Qk4M{C|UzS`7P!!FSA zZMy|lqyZhb$im4Z96Op$+l96pSLJq67C5d`y@#Cg*lWN4mdok8$ol>0XUbz@|PffxK^_!xuLY=v& zKSjc@uPm-wle{Ta-O!x2X>-aDPA;>Ig1Sl-$1*z|K7Jn~R!rU};{PJ2cPmYkauYC7p2$pl2m2%jCh6+$Hkm=KTnPTI0u0wLK)%U<}yB zw%#04P$?;=`oZ+$oM`MJTCSV-?Vc3UIr3kXzkd=B*{dWqK#SA&A7mCU98bEIW_@a5 zEU%)@ow1aHM3nD^*dw!=y?##UwX%blBwojRY?hh@IMVJL!G&ZwO{Pfw;=qC; zn;so*p&gyYE64zm+Ws@sVN3qfx>>^VGfwd^xVZCt!MQX)%yG?kBMVu=j7_E zR-u5iqON%Dy{54RmzT19vx(7bS!wc)>!kxW0kEG&1v`Gk&@EF*R{!R-GV7SR`xn^o zjB#TGe7nv@&>7{PG3I?{zm7DR7+~le_tiRnARqBaLE1_J`F71D7X_lL^^lS64D+Rc z9t~wZp_A(vw>a+#6{bC&&^SHkuj?o>(LvTd0q<=+>VM^g03X#k%c1U8JGwMhU0h0a z?EVph2fq$y%Z}w#>~bMtjBx%uAFKHJh;v`9_#pDRT3EOx=Ph00T7di2h<8z)rvn$$ z1L($}25DlW;5Uj_E|T#37Ox+3ykDfm?^2%7&?w;czym~&d?)rt6eD~gkAJceghORW z>4&H+CemA1IiU}a3w&@I50?j^*lwt%S>VN|^v}CI3(pp_Ehc@%!VEl1ggPv6D;AI@ z_L5kp!=`mDU@Fw=HJF0*6NHF&UH9(SY-L#4$CPdzq>bf?3GB$^z&@1314|C}AfNDGTKWtl!@6J_MVOD$CyH#Il}a@ZWmF6m%Z_-^oJofsZToSdZ$_fKfKE%i7{jEQS98Vg{M z)_s#U*)EGfgs*EUxSxA(QO54U+B2_3-+FUtSUmiO&i?I>bdXHz(}(`ErV@wK z^dZqm(ejh&@&O$akUMsPW_mARFU<^h4_iLBaII_C;s>};*p3TFAK5E?o!}e{e*Q0` zex&xuHs(t?Mwc_CZ528rh@GA6oERsu!gPUxr9%b97hhSQ8cVjbqK#pggs5Duw9oRH zx*WMPFHg(O3V&!gGS9ugWdzDE9d{#F*q2mWLba!hS&0?DDW@|AwWw!8=5<&g{g(M+ zr_;ChOMmF}+i?^%-Cq!p?qphmmM>N|tBq${Ebs(4_=Ee*oR>xI%+wb>$c4C0iHD=? z%(htUrWkSYKPdH$mll6jhvhqZBsEp`txuyUjp`N^YF~hUIpe=8l-Ui7Pr5i& zy&=I;-@AUGVApT-tjH{gVhmSwe#j**y%veCG(G?4OO$^?=|4hLpd{=T%OYyJXYz8c!3xCh@S|W$FERvouCvsnCsBEYEXcwHQ~Wx4+0Wbl%vI zwXnFiH7qNIL?G{Ktc64y1PxM3fPCt*EwCKpT;_qBCUdDeM$1AsX?=C{u)fSsB|4xp z;QK$}*QA}3Mji`pr>0n2@0|q)tBFVbe{{WfINNRb_kXvvD7BSZF@mb1s7=zMh?Tpy zwzgPps??}WB(?X7S~YUFlnym(@6Bx$tzAUS3KAm{f+zj{j^}uu-*NQ$(?9;W^106I zyx#Bk>%6G{nkpc1ia&<-MlTyTE{&&9?->`si5?T<%1wj6R*8|$4jJ9`)0*F23zwFF z&D{>3*1q_oHflf@1`LXwY}sqPe8Jh+X|B^?DcnnRQIOi-mI$agBgBldfL193j{-aA z_X|^C9aDy^$E3P*B$a)?#NZkJrK-ByBSA4-f)1rmF~t#elrCW#LK^q5!O6RdU7xK) zSI1=&HKn*8_BM1f^9q;^Tudl$VU3eR7d&%U9qzvL_RD*^$CL4!wA#$Yrf`zzHsdFZ zg(iD|I<8Ndmix3@kZJ;v!8{ln_$Fk)B))U%hy(U@WrjVW`rppMv!JMIpRs@bq^~Z$ze_VSO731~N+ftsq^#WY5MgI` z{(b#gH*IUpMbb0u^_qe&imeRJ+V<)g72Ow(d^N^z5G^9X&W=7|S7yAtAIl2yM<4eS zm@35`*{(11&xkzWxKJokz<#qp=Y|#Ay}g`K#MA%8OUTfyaQb%!2`pn`cQ^E&MIJ0A zgqt`T`~$3_WA*Jwvac-S)4f~eIz4$a66^;qUi*Puvc<1&0ii%aIbDV=F)r<=rEh{lG!LMZ(;=Pl&y85}%yc#=dwrMc zX{C_2cw7=rhv|LyXa>kVlGfASS~S8JpZk>8(?Mx@p{~Av7@{Z{6OmA>@a@m*ly_b$McM3d6)PxXl;B$f;ZXo2O{BZ$t zFXrhHwXp3YF6hOHvuey)uCuLaPD=wwJjSMx)d`esoTsmdA>Dm`pi(^a#6DrY1B^{0 z*urRH7l|v@>jkzOco{Q#LWxw|>A*#vF$H~giujX~Ji z-pY5vgTOYzvwC??v5x%87s(*c3uACLizjkg|F&clMMs$1*+i(mX1`zOB4s=IdK)O= zx-7F9jn!PFj94wwG&VC^0wS>M@Jw&2@yT54w-l+U1nu;ChAjssuu3<*%vs#}0I_Qm zFOg%7LzXU0UiV46t>{b*9Jo){~#R1%VxCF!dn9t=2mQKPxYb zz^By{a=b{H*91#DGXaRM(E_YCHb!POqS}}(*J)Kot6vj9$^ejP_g?DKL5fh6=h{s1 zT)?HguOkvBdl4SqH_1@X4~~n$*)pU^*ko6SeWSl5uu+|k)ZfrO#oy+?J=;Cg&|PTV z$LnXoG_p3$qnY1!e$oa`H``9C3A7D4Gj2ODR`Q4o{?rL?1*?;m1s2J@Z(Z(#;@B8= zAm!8uDNXdn?K-t-$1Zy^3uS?wE3Z6x|Dy5P44oTLsF5TjI*B<7vJRchGiX&4P!Az( zB*3W4CInp8e#IGecHe7s?FT+=5=5A|UXen*R=uy6iCkm?O;Bu45Yuqm7M~7YzV@zl z`m+PF$Ez4^m38-y!apen!dL0%L&|r@V81@((j=O04$gix+3+h6oaBNs=U-v3Ty!Nx zRM}PgMY=CIF}5ak9O5w(Xi=7Q9sUi2PyiQZa(rtk?6%aqxJCHDA>u zIa}rY!|ki??s}tpUhS&@fwe^FRb1(fqX)V5I^VFUwI`!} zy&AH`%ob%L{ARIHtZ(%CVCxGb27Lkf5(92in+hA>1wqjKJo)MAt-^e7XpCz3pvn0k zotMB59QA7UVm%slKy<1 zv1JeiN?lpHs((>bs_$F{^;8kNa``gT+YH-4?6P6xbjiRQOTGNj8D2W*L|`1mq?-*Q7jfMK4?SCflFk;-Pf$uZjJw^i*6ccznv4dRG63B< zoh?6|ZSFmS7&+1LX6VTwm$%7#D~6KjXVE@NazT(M;_G3U5jwDhfi(IZP&2LTFfPl? zt*28J;{6F&dVUN$-!kc3R&ByA<|UBBL(&;9PeDU}nOCQ{70PQ!JSXOCryy3uPna^j zENYKl&_vnpIHOGJK?ztp9kbDQN{nFLcQS8Lz9iS%!zjgb`R2UDTS}f9+kr9l^~1y6 z^V2p=oD-?%jA(%tpHs7RbK)IYn^|ILs;Hef5pu<|DE2qat7>Ikm! zcU!^3v~+o~oa5(^xH!9aERI_s?#|t1Q89KpMm_q-!(MjQD^_~+RnDe=-M4UIcVd)| zT7jQFp#l`r1mI zvbFz@X+^-^fY!3!-rH**V^_FR4h^OCq?;y1pi5!E23p0&izlUO-!@>hW;3QQoYYV; zUoxs|0K%W{_{-W|38mV_9!I?!jq&}@WCywLC_HSde8$(1@q96Hs6_Br3LBNIxmBf% zyQRi9IQ{OP)*x?Jm6KI9H6kp>lE}u53&tH;-bp~&Rydt^#5>=SvCP@`!8%RtP3K(s zvPjj{^OZVNsH_|>{==HZw-<)LY9dE03kRKpf>41CFw)UDx#QmxHzpCR1Mrg}XQ$N- zH7?t}{Rs1*9vK3tJkPd2224$B`D{^s)nj?wP~~K5prJbxAZ9Oo_+Q{L_I(Ej)|sG| z)py8@`xp};kt29-XSL)lk>Qe;_x$K0pLEoMM}^i8ub@W9oqehF+j*D=?z7WL1#>lX z{&nTFku3MYt<2{U(Dao;HdUxW*SVn>IxvPV{aK_%)#G^quVc<2EhZKN z-~gL^l#H0n7(|R){dl^w1v^Z5)GML5JXL1jqA_Kxr6{FtD(H*->Sb6D9Js$mDW4Un zB|4D=`ku{c@Wv(+z(7^#_JhMDoCx-kMJ0bk zNe$wa)b(bJx_4*n*&%3qHed^=drAbH6BhtySb$j`v0%AJ;SDcyX$>JV?TTAf$Ap&F z5|fN9Y8m6>;W;!T!)J7se&;!g2jWl=&8UmGn`oa&bxseN{ z+sKiDizSiJqZdqesi=>mx) z75A0Y9+2lBsOwnbXB@xfm3ydt?j2Ct8G1lvU5D`v|6tye8s?hQHicmjuWP*iRMZ#Zu#=ZTb zvh1q9B4Xr-JVp*Z-k(=GC4pBJ@863SkxISCD7#;vxZxV!&%crhG|*#tNoU2`8Oe4` z%m?`^>GSF^W9z5$(T5|W8{pI*_4;*tKOYe>MHoo|FUA8PCsAsWf zKd9llRj*@|@tl#Z68#G*HYS@NdBD>jSApIZPa zk-4P-p1U6BzK!7*lvktOgg%r_(Dmzp-o<`<@a=wB>XMvO$K@T*Rf*hEOM-=Py8xES^!S zoiz}9u>_QOj8aqZ;jfhjFFSOMmz|nqNElmJ1Y}|2$X5emIB9&jvkAgo6%cq&>&;_q zgmR#KBcADuIVEbn6NRk-@{UD~;$+JH7PNNzk{+v9{>y!dOy#pwOe>P9CtSt!0tT!R zozR_|RF-RA?b3UfX{PNwNdnKXbbmF#oLCp%y zH4v|_`u|7>4s`ad-|I<;{{gBRP(@ZYeOAMaMEZ?r00>jqr`y`iq564qy7PO~HbJEBQ@L8^9yS-ekmapwYhzfIgaMCV!9pi^FuguMQWe^=& zd7~H0W82-$fKi6W@8m%+-Z+pZ)q7+(VNsZ3&z`OWHc5)>J7UPAbb5w(+H<}g#n9`x zk&B)V@|zG_{Rop%_)d{7C@KR$Zd!KADe%@oRxyq(HhMI4$0wa$$5v#NUD8G8nLAgw?|9!+Lk?$RE~=je zcpXIzzE&AAbT2seLS@v(`w+X$r5FX$L<~cxK`DY)27h*fI^RwAz2X>q=bj|-0`2vc z2rbH<$jzQ@{s)r!MGp)iwPY?uhI?G#DmG#o%g-_;)hG*;Sgt5poH-3L**)@Lt9s}A zcIS_zE~Ur6JQ-1Z3-w{X?>QFE)EyXa(OqWiQ|#q6ayOIRr!-ZHHh8D;Hx@U4j+m0@ z=tqJ#MpQGyV0rZceT8!X@quzMQQ=E>y&(*ZP;SZ22d*3@S#~&?HEaXtn z2qvD>A?t?UJ2+0ezz8_u#^U_RLE!4HIrP+FA$L7vL@jnbZU2FJX=_XA`J5Dx0bF z-I6F^$XF6UsbsV`T)s2^2BPQvDQ%G3nh8ti>QXcCh&GzcH7)}{jA1U;b%*ESb(PcsDj0j;U)s_yv=$J-2; z(tVZd2r(*Z_Xz1J$$D9y*mV&e2YAB!nDa_L?@5Pe?eq{O{JNk-skHn4pzv=Sa^_@3 z3as8>%=D6z3fj*ZH97Bjw!59)g3UVZ&^^xk|J2&x%xp>GxQq{_w@m3I4vKJ+JhH+` zr=s_Va5;bQT0fFSDGyMvH8~Fpw}r+N(9c!P|EA-2xNfby4`m-kY|- ziWxxpc9g≤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

    &d0tA^(r6=jeKb7A%3>0rE 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�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
    MjJ5@h{!;E821PxIr5 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

    $%(hPTPZ~t;S>UE}# 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 z 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 Date: Thu, 12 Oct 2023 13:44:37 +0800 Subject: [PATCH 51/63] docs: change_for_dolphinscheduler_task_demo_of_integration_folder (#3468) --- .../dolphinscheduler_task_demo.md | 211 ++++++++++++++++++ .../images/ds_bind_status.png | Bin 0 -> 15414 bytes .../images/ds_bind_tenant.png | Bin 0 -> 54562 bytes .../images/ds_create_project.png | Bin 0 -> 21787 bytes .../images/ds_create_tenant.png | Bin 0 -> 16426 bytes .../images/ds_import_workflow.png | Bin 0 -> 46103 bytes .../deploy_integration/images/ds_predict.png | Bin 0 -> 107490 bytes .../deploy_integration/images/ds_project.png | Bin 0 -> 28988 bytes .../deploy_integration/images/ds_run.png | Bin 0 -> 31524 bytes .../images/ds_run_status.png | Bin 0 -> 85761 bytes .../images/ds_set_tenant.png | Bin 0 -> 29224 bytes .../deploy_integration/images/ds_switch.png | Bin 0 -> 89579 bytes .../images/ds_switch_right.png | Bin 0 -> 106556 bytes .../images/ds_workflow_detail.png | Bin 0 -> 93731 bytes .../images/ds_workflow_list.png | Bin 0 -> 40847 bytes .../deploy_integration/images/ecosystem.png | Bin 0 -> 173017 bytes .../deploy_integration/images/task_func.png | Bin 0 -> 196506 bytes .../dolphinscheduler_task_demo.md | 8 +- 18 files changed, 215 insertions(+), 4 deletions(-) create mode 100644 docs/en/integration/deploy_integration/dolphinscheduler_task_demo.md create mode 100644 docs/en/integration/deploy_integration/images/ds_bind_status.png create mode 100644 docs/en/integration/deploy_integration/images/ds_bind_tenant.png create mode 100644 docs/en/integration/deploy_integration/images/ds_create_project.png create mode 100644 docs/en/integration/deploy_integration/images/ds_create_tenant.png create mode 100644 docs/en/integration/deploy_integration/images/ds_import_workflow.png create mode 100644 docs/en/integration/deploy_integration/images/ds_predict.png create mode 100644 docs/en/integration/deploy_integration/images/ds_project.png create mode 100644 docs/en/integration/deploy_integration/images/ds_run.png create mode 100644 docs/en/integration/deploy_integration/images/ds_run_status.png create mode 100644 docs/en/integration/deploy_integration/images/ds_set_tenant.png create mode 100644 docs/en/integration/deploy_integration/images/ds_switch.png create mode 100644 docs/en/integration/deploy_integration/images/ds_switch_right.png create mode 100644 docs/en/integration/deploy_integration/images/ds_workflow_detail.png create mode 100644 docs/en/integration/deploy_integration/images/ds_workflow_list.png create mode 100644 docs/en/integration/deploy_integration/images/ecosystem.png create mode 100644 docs/en/integration/deploy_integration/images/task_func.png diff --git a/docs/en/integration/deploy_integration/dolphinscheduler_task_demo.md b/docs/en/integration/deploy_integration/dolphinscheduler_task_demo.md new file mode 100644 index 00000000000..143e8b82dbc --- /dev/null +++ b/docs/en/integration/deploy_integration/dolphinscheduler_task_demo.md @@ -0,0 +1,211 @@ +# DolphinScheduler + +## Introduction +In the whole process of machine learning from development to deployment, tasks including data processing, feature development, and model training demand significant time and effort. To streamline the development and deployment of AI models and simplify the overall machine-learning process, we introduce the DolphinScheduler OpenMLDB Task. It seamlessly integrates the capabilities of the feature platform into DolphinScheduler's workflow, effectively bridging feature engineering with scheduling, resulting in a comprehensive end-to-end MLOps workflow. In this article, we present a concise introduction and practical demonstration of the procedures for using the DolphinScheduler OpenMLDB Task. + +```{seealso} +For detailed information on the OpenMLDB Task, please refer to the [DolphinScheduler OpenMLDB Task Official Documentation](https://dolphinscheduler.apache.org/zh-cn/docs/3.1.5/guide/task/openmldb). +``` + +## Scenarios and Functions +### Why Develop DolphinScheduler OpenMLDB Task + +![eco](images/ecosystem.png) + +As an open-source machine learning database providing a comprehensive solution for production-level data and feature development, the key to enhancing OpenMLDB's usability and reducing usage barriers lies in upstream and downstream connectivity. As depicted in the diagram above, the ability to access the data source allows seamless data flow from DataOps into OpenMLDB. Then the generated features by OpenMLDB need to smoothly integrate with ModelOps for training. To alleviate the significant workload resulting from manual integration by developers using OpenMLDB, we have also developed the function for OpenMLDB integration into Deployment and Monitoring. In this article, we introduce the framework for integrating OpenMLDB into the DolphinScheduler workflow. The DolphinScheduler OpenMLDB Task simplifies the usage of OpenMLDB. In the meantime, OpenMLDB tasks are efficiently managed by Workflow, enabling greater automation. + +### What Can DolphinScheduler OpenMLDB Task Do + +OpenMLDB aims to expedite development launch, enabling developers to focus on the essence of their work rather than expending excessive effort on engineering implementation. By writing OpenMLDB Tasks, we can fulfill the offline import, feature extraction, SQL deployment, and online import requirements of OpenMLDB. Furthermore, we can also implement complete training and online processes using OpenMLDB in DolphinScheduler. + +![task func](images/task_func.png) + +For instance, the most straightforward user operation process we envision, as illustrated in the diagram above, involves steps 1-4: offline data import, offline feature extraction, SQL deployment, and online data import. All of these steps can be achieved by utilizing the DolphinScheduler OpenMLDB Task. + +In addition to SQL execution in OpenMLDB, real-time prediction also requires model deployment. Therefore in the following sections, we will demonstrate how to utilize the DolphinScheduler OpenMLDB Task to coordinate a comprehensive machine learning training and online deployment process, based on the TalkingData adtracking fraud detection challenge from Kaggle. Further information about the TalkingData competition can be found at [talkingdata-adtracking-fraud-detection](https://www.kaggle.com/competitions/talkingdata-adtracking-fraud-detection/discussion). + +## Demonstration +### Environment Configuration + +**Run OpenMLDB Docker Image** + +The test can be executed on macOS or Linux, and we recommend running this demo within the provided OpenMLDB docker image. In this setup, both OpenMLDB and DolphinScheduler will be launched inside the container, with the port of DolphinScheduler exposed. +``` +docker run -it -p 12345:12345 4pdosc/openmldb:0.8.0 bash +``` +```{attention} +For proper configuration of DolphinScheduler, the tenant should be set up as a user of the operating system, and this user must have sudo permissions. It is advised to download and initiate DolphinScheduler within the OpenMLDB container. Otherwise, please ensure that the user has sudo permissions. +``` + +As our current docker image does not have sudo installed, and DolphinScheduler requires sudo for running workflows, please install sudo in the container first: +``` +apt update && apt install sudo +``` + +The DolphinScheduler runs the task using sh, but in the docker, sh is `dash` as default. Thus modify it to `bash` with the following command: +``` +dpkg-reconfigure dash +``` +Enter `no`. + +**Data Preparation** + +The workflow loads data from `/tmp/train_Sample.csv ` to OpenMLDB. Thus, first download the source data to this address: +``` +curl -SLo /tmp/train_sample.csv https://openmldb.ai/download/dolphinschduler-task/train_sample.csv +``` + +**Run OpenMLDB Cluster and Predict Server** + +Run the following command in the container to start a OpenMLDB cluster: +``` +/work/init.sh +``` + +We will run a workflow that includes data import, offline training, and model deployment. The deployment of the model is done by sending the model address to the predict server. Let's begin by downloading and running the predict server in the background: +``` +cd /work +curl -SLo predict_server.py https://openmldb.ai/download/dolphinschduler-task/predict_server.py +python3 predict_server.py --no-init > predict.log 2>&1 & +``` +```{tip} +If an error occurred in the 'Online Prediction Test', please check `/work/predict.log`. +``` + +**Download and Run DolphinScheduler** + +Please note that DolphinScheduler supports OpenMLDB Task versions 3.1.3 and above. In this article, we will be using version 3.1.5, which can be downloaded from the [Official Website](https://dolphinscheduler.apache.org/zh-cn/download/3.1.5) or from a mirrored website. + +To start the DolphinScheduler standalone, follow the steps outlined in the [Official Documentation](https://dolphinscheduler.apache.org/zh-cn/docs/3.1.5/guide/installation/standalone) for more information. + +``` +# Official +curl -SLO https://dlcdn.apache.org/dolphinscheduler/3.1.5/apache-dolphinscheduler-3.1.5-bin.tar.gz +# Image curl -SLO http://openmldb.ai/download/dolphinschduler-task/apache-dolphinscheduler-dev-3.1.5-bin.tar.gz +tar -xvzf apache-dolphinscheduler-*-bin.tar.gz +cd apache-dolphinscheduler-*-bin +sed -i s#/opt/soft/python#/usr/bin/python3#g bin/env/dolphinscheduler_env.sh +./bin/dolphinscheduler-daemon.sh start standalone-server +``` + +```{hint} +In the official release version of DolphinScheduler, there is an issue with OpenMLDB Task in versions older than 3.1.3, which cannot be used directly. If you are using an older version, you can contact us to obtain a corresponding version. This problem has been resolved in versions 3.1.3 and later, making them suitable for use with the official release version. + +In other versions of DolphinScheduler, there may be a change in `bin/env/dolphinscheduler_env.sh`. If `PYTHON_HOME` does not exist in `bin/env/dolphinscheduler_env.sh`, additional configuration is required. You can modify it using the command `echo "export PYTHON_HOME=/usr/bin/python3" >>bin/env/dolphinscheduler_env.sh`. +``` + +To access the system UI, open your browser and go to the address http://localhost:12345/dolphinscheduler/ui (the default configuration allows cross-host access, but you need to ensure a smooth IP connection). The default username and password are admin/dolphinscheduler123. + +```{note} +The DolphinScheduler worker server requires the OpenMLDB Python SDK. For the DolphinScheduler standalone worker, you only need to install the OpenMLDB Python SDK locally. We have already installed it in our OpenMLDB docker image. If you are in a different environment, please install the openmldb SDK using the command `pip3 install openmldb`. +``` + +**Download Workflow Configuration** + +Workflow can be manually created, but for the purpose of simplifying the demonstration, we have provided a JSON workflow file directly, which you can download from the following link: [Click to Download](http://openmldb.ai/download/dolphinschduler-task/workflow_openmldb_demo.json). You can upload this file directly to the DolphinScheduler environment and make simple modifications (as shown in the demonstration below) to complete the entire workflow. + +Please note that the download will not be saved within the container but to the browser host you are using. The upload will be done on the web page later. + +### Run Demo + +#### Step 1: Initial Configuration + +To create a tenant in DolphinScheduler web, navigate to the tenant management interface, and fill in the required fields. Make sure to fill in **user with sudo permission**. You can use the default settings for the queue. You can use root in the docker container. + +![create tenant](images/ds_create_tenant.png) + +Bind the tenant to the user again. For simplicity, we directly bind to the admin user. Enter User Management page and click Edit Admin User. + +![bind tenant](images/ds_bind_tenant.png) + +After binding, the user status is similar as shown below. +![bind status](images/ds_bind_status.png) + +#### Step 2: Create Workflow +In DolphinScheduler, you need to create a project first, and then create a workflow within that project. + +To begin, create a test project. As shown in the following figure, click on "Create Project" and enter the project name. + +![create project](images/ds_create_project.png) + +![project](images/ds_project.png) + +Once inside the project page, you can import the downloaded workflow file. In the workflow definition tab, click on "Import Workflow". + +![import workflow](images/ds_import_workflow.png) + +After importing, the workflow table will show as follows. + +![workflow list](images/ds_workflow_list.png) + +Click on the workflow name to view the detailed content of the workflow, as shown in the following figure. + +![workflow detail](images/ds_workflow_detail.png) + +**Note**: A minor modification is required here since the task ID will change after importing the workflow. Specifically, the upstream and downstream IDs in the switch task will not exist and need to be manually modified. + +![switch](images/ds_switch.png) + +As depicted in the above figure, there are non-existent IDs in the settings of the switch task. Please modify the "branch flow" and "pre-check conditions" for successful and failed workflows to match the tasks of the current workflow. + +The correct results are shown in the following figure: + +![right](images/ds_switch_right.png) + +Once the modifications are completed, save the workflow directly. The default tenant in the imported workflow is "default," which is also **executable**. If you want to specify your own tenant, please select the tenant when saving the workflow, as shown in the following figure. + +![set tenant](images/ds_set_tenant.png) + +#### Step 3: Deploy Online Workflow + +After saving the workflow, it needs to be launched before running. Once it goes online, the run button will be activated. As illustrated in the following figure. + +![run](images/ds_run.png) + +After clicking "Run," wait for the workflow to complete. You can view the details of the workflow operation in the Workflow Instance page, as shown in the following figure. +![run status](images/ds_run_status.png) + +To demonstrate the process of a successful product launch, validation was not actually validated but returned a successful validation and flowed into the deploy branch. After running the deploy branch and successfully deploying SQL and subsequent tasks, the predict server receives the latest model. + +```{note} +If the `Failed` appears on the workflow instance, please click on the instance name and go to the detailed page to see which task execution error occurred. Double-click on the task and click on "View Log" in the upper right corner to view detailed error information. + +`load offline data`, `feature extraction`, and `load online` may display successful task execution in the DolphinScheduler, but actual task execution fails in OpenMLDB. This may lead to errors in the `train` task, where there is no source feature data to concatenate (Traceback `pd.concat`). + +When such problems occur, please query the true status of each task in OpenMLDB and run it directly using the command: `echo "show jobs;" | /work/openmldb/bin/openmldb --zk_cluster=127.0.1:2181 --zk_root_path=/openmldb --role=SQL_client`. If the status of a task is `FAILED`, please query the log of that task. The method can be found in [Task Log](../../quickstart/beginninger_mustread.md#offline). +``` + +#### Step 4: Test Online Prediction +The predict server also provides online prediction services, through `curl/predict`. You can construct a real-time request and send it to the predict server. +``` +curl -X POST 127.0.0.1:8881/predict -d '{"ip": 114904, + "app": 11, + "device": 1, + "os": 15, + "channel": 319, + "click_time": 1509960088000, + "is_attributed": 0}' +``` +The return result is as follows: + +![predict](images/ds_predict.png) + +#### Note + +If the workflow is run repeatedly, the `deploy SQL` task may fail because the deployment `demo` already exists. Please delete the deployment in the docker container before running the workflow again: +``` +/work/openmldb/bin/openmldb --zk_cluster=127.0.0.1:2181 --zk_root_path=/openmldb --role=sql_client --database=demo_db --interactive=false --cmd="drop deployment demo;" +``` + +You can confirm whether the deployment has been deleted by using the following command: +``` +/work/openmldb/bin/openmldb --zk_cluster=127.0.0.1:2181 --zk_root_path=/openmldb --role=sql_client --database=demo_db --interactive=false --cmd="show deployment demo;" +``` + +Restart the DolphinScheduler server (Note that restarting this will clear the metadata and require reconfiguring the environment and creating workflows): +``` +./bin/dolphinscheduler-daemon.sh stop standalone-server +./bin/dolphinscheduler-daemon.sh start standalone-server +``` + +If you want to preserve metadata, please refer to [Pseudo Cluster Deployment](https://dolphinscheduler.apache.org/zh-cn/docs/3.1.5/guide/installation/pseudo-cluster) to configure the database. diff --git a/docs/en/integration/deploy_integration/images/ds_bind_status.png b/docs/en/integration/deploy_integration/images/ds_bind_status.png new file mode 100644 index 0000000000000000000000000000000000000000..42ebeea6c90af5b47bb85252dad3a0ad3ed38503 GIT binary patch literal 15414 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 F{~MujqE!F@ literal 0 HcmV?d00001 diff --git a/docs/en/integration/deploy_integration/images/ds_bind_tenant.png b/docs/en/integration/deploy_integration/images/ds_bind_tenant.png new file mode 100644 index 0000000000000000000000000000000000000000..74ef857d6a81e398bad381397050e2f43edbd50a GIT binary patch literal 54562 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

    ZgFJ`bP9Ks!xBH~6@k}yB_#->+b&R0%d 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 literal 0 HcmV?d00001 diff --git a/docs/en/integration/deploy_integration/images/ds_create_project.png b/docs/en/integration/deploy_integration/images/ds_create_project.png new file mode 100644 index 0000000000000000000000000000000000000000..37920851ad39949cb661476d6560bedf85e5309d GIT binary patch literal 21787 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 literal 0 HcmV?d00001 diff --git a/docs/en/integration/deploy_integration/images/ds_import_workflow.png b/docs/en/integration/deploy_integration/images/ds_import_workflow.png new file mode 100644 index 0000000000000000000000000000000000000000..2d6e257143de0f8b475b46243f57435c05805761 GIT binary patch literal 46103 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 bF>s=gU7cK(k!n5gDwciPhFVYeSYG^ZcmhHH literal 0 HcmV?d00001 diff --git a/docs/en/integration/deploy_integration/images/ds_predict.png b/docs/en/integration/deploy_integration/images/ds_predict.png new file mode 100644 index 0000000000000000000000000000000000000000..7d2dba0f1615c95070729ee124fec3f7fcccb338 GIT binary patch literal 107490 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 QIF2IFEbPonce<{9 literal 0 HcmV?d00001 diff --git a/docs/en/integration/deploy_integration/images/ds_project.png b/docs/en/integration/deploy_integration/images/ds_project.png new file mode 100644 index 0000000000000000000000000000000000000000..e24ea8876b3d699717048b7ac8cd06096a40e574 GIT binary patch literal 28988 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 y(4N}J2;R8(L*q= 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 z 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 literal 0 HcmV?d00001 diff --git a/docs/en/integration/deploy_integration/images/ds_switch.png b/docs/en/integration/deploy_integration/images/ds_switch.png new file mode 100644 index 0000000000000000000000000000000000000000..75dbb327c07b4d6e69c6501661b556ac90dc77a8 GIT binary patch literal 89579 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 z z!RL_D?4W@f(Y)P_Js;q;RJ}@NhUIegW%&N;|8^D`rD_O-HM@6jDPk#7bt;nq0%UWo z$r`2m36SrgZbsj}X_T}h9@bigTemLi>Fr8PJT)$Gf^=zJX-r>S9m{CbP?MSAdL;@skkU=|OPm zm(LW)3uS0?Wh{D27~HXKp~Bhp&WCcj(t~`at!q61Qi$oK-Hd(C zi|vI&H<^*ho6R41lUma-A#=(fd=WPZzG}1D<{=~P1wPOD7tmL82ToFoEnPb0uUL%47o)_|lal2rYjv<+BNjGQ2 zKmykF)p_m4_q-~%Rp_`*{{=(|W_zG;+<&tRU@Jh2he- zK-=>~G23Uz-XVr;9dJZ%LM7=+Q#OT~BY$dc;}u*59b(MItE^A+R&0x{y*{yb{#QWdZ@%W|r$Ynw;x} zM*g2KI1v$2A9c5WGA#wPT%X14?PSPmiwEa5*DpTPOj90i6AanUNdt7&{>y$~)GU+9 zZ}u{pv}CIEANoIvow4yiaJioF{YT@r0p(fa|i`DwF%s&l@|h zLZnZ8HiR@Gkpr}?AZL{XMByzH4N$`PY-=l<)JxdZito=pKH&0%edLdo;<7mYgp*S zJ)_cKl*B`}cX`0@~~Q zt-p{Qin6~f*U>318*Jp?vuhio4YSPtR|4C~9^k5f9{S>k`sT_>xZnn2z1@MvS}z@G zeTndglJcl;r4i~EpRG&eOzcE(`tQmI!8Qg{hmCjH>RowTreAhO&nHlnn)|tkZmJ~3#zqS@p zh{V}!Y@YM;!yRLgOQ;cZ60SmrKgT#TGYce6r*)TeU_+8(nt7M_cax55cmrX=y88sXY?v`WfhcI0t~RDmfGlfW z`AxA-Ec<9z#%RPLx^puxzuW$NLLoX+%2-x5T$K|z$O{-AUQ?YGZE)GI!-}eM*n5D? zz5n>p51ZwzRi9^M;*!o*`h zv6;&J94s%Nl!CfqDKk_RnCD*G%4;o=0`~b-Bx$^Voqla7i z+2m(7b~9RyE%Cr7jT4T5kmEDp`dTWF+1&91=(*)0E;pYRt)A7cY;Qb_m{Ik7M1mny z-wAR*PA@`VpO_ejL)9$tUDK%c=6|@iFRhPpZGPQa0+|_FH~YbKMx$~!m}r1~(jc@Y z<=?T20D-lbwot@+aHgh&kr$UN)=YR{r3^GnOFf&bnCIb7OivkWZn(|qv{b6u&VV8t zQ*9np4b$1)j7(4wf})Ls>H571{a8t)TdRwVgzo~XB?PE7Z^#jQ;2ukJoTZY{qqK|g zhf2FvP>>q1+YkGGYNbxKb*(`HTLfyNAmZVjP+J!VPOdW|;B6DDqGY?4Ik~ZlCZaZ? zLF7`yev_ZT(soFVJE-gP0J= zUh6{ACS=AZPYV5>e#68n8+j$&MC<{!`_!TDTX#Qb8`qnC#X)EQael(olU&&kw62Wh zO$y`+MA`%n8+yrQumrW9N#%>EbeT=grV%hXxqXHFHI_4wn4eyiSN;BraTUjoi$|aI z_COEgYq%T-bOr>853)7OrBY&e0DLEkS@xrL$?`Eo9>8@jb9e>*uV0J#_T&$%=S#o@ z#6dsLhd`wrTwhg}X(=Ufd-{dl+dkgQ^}<}h>DSxzhn;kiygXJJ5%jg5VXMg-*X?01v2%cF*x_`&hy=1PYzOi=&UMD3xk0_3byWieu?gT2jEze|fE zKR8L9u)>?92Ef#H_JiROMesdh6EzG$B8BuEPKtdS%PH3x2$D%dWK(A34W; zo<7z3`*<%JpvULZSguRxR)+IG@Nv$YjiS1fqWcegyZ-^GzF*uoHw4nr2&3UMct%j> z&Y64a65JpJQ0_mHtOqQRqrQo%Sqj&eS!RNmImdApHfX(8%yoe{1YQ(E!;P!oOt2gE zbvOrzj&OWAG4DhjM)@)X;!9J7fi&VSLJIM6KX(vk^vk5X54Waw*wZG_|#vVWKN9=gPgPE434bpB~VYM=WM5(V-KM zmWTmbjMwP4!b!q;fuagT2~Rc_Wx33^Kr@W=izK*31|C3so|#~zWoAdtY?Z1ir((jJ z%S2b=klj{kRG6y8H$}n86vrh8I$Y31M>dc9#v_3cbwDdie4mz1ys9LI7~L~P9QO*G zhoDN8@7!+f&2}XXD=u#0(fJREF;=*sd5roz5z)*-*3~oZM9Xh<0`PL1%8{q;VKLO| zP6XhrCU;f8P~Jt25v=L{?$}G{u;LZ$_wMboufxeYWBXZqP!5GLU=Ov=Z*HKn@|`HX zqPqq&WB~)k^m{!n-iS&xzWgUUX78hVLd^0o58Q}?Xp5*!z-!a*pWgWy=0&Wt$%E-NyVuioU%TuK#u|um{GiMxfAnKvqvjcFPK|+s-j%2Tq z4J3M3@>ZkFtw%Wo1r>e2F3hl}Z0q*3v}*g59$BeQ)M)P=zKgx+Dwec`fSU+-lEQ-zbxax294khCS-WWM*=;y5B1O|+Q-j83{UNwOus zs|41#qQO~sv};|o>uu)I7oqui4DGw5rNaK7$e(~T=kF7-6vQm#(oO8mj zP%gi;vHiu|>S8bS*UG09eQO4DkE@OxenFE+b7Kj z9%dMX;~!B!(DW4Joiwn3yu7ZbcJZbqQjm24!48M*P^hep{OtOZXw8ao20HB-sX#M( z9cI5I3m>^mIUY`@tO_;NV0wpx_=(GvV#3|?sq*(Z<8xd4|e!2<^m5@FBU29jbHH0Pc{ z8gQ>Nwj>N^5%K;oF`x|S92#MrJx2x2`cmAT55YhKO!?%Zwo9_8cCp_uw3(0H)>mr2 zUDqd&wWu2akK9JW!1si(EF8bIE6uV_kHK}02Vbw^mk(v;c%zE2=nkTfsJbU9=>;}i zg%mYG2ad9DM}t8%d$v~b?%kOR-pybARy=NN4wwB`J{mEJarj@QWcv(LBuB?AmG(g==IH66;u^KLj&%xD?(ZAz4RDOJaSx{$57zzx#Z0Q?l`LW zB?wzGfz_q4o|YP1vBC6)$A@t~|7G!AF7Mj7Y-o52R8Z})P@dS~w*1n`*&uuF7!W9- z@Sl6R&@yC|YS!UI@RV`Owfy}Co`P0dTjp$lIk(06O@X8fZC|wdul<&P)r&faVePND zbv}X57@q_f-0|blV;=){(xK6BH*+E-cjL&*J1?QVesNOz5COtrW2?(m5ol>?A%=uN zDi6jOTpZ%((OI{12KUVeuQMnlMjiTj-xa6QF z2^~AMy|SoB4-Y5W!1g#4^j7J+%Q>7kalqWFFB?9`?rQP}K`FS%muRuvwEXf2!Pa!8 zd1u4Km=03o_1$DQEZ#kUX`|0()9TU{n7qK~?ja_0UJ7Y?{o(6zRZj_gl{PJ#h*e2}$^OLn=v_Z($4~I`5=>p9>JDvVnS{f$=aek}MyOU{>W;!~ z)e7xEv7c>|IZ%se5+&Yha`)0AYX9QNMJP*<0X^6ooOr4o(}8+A89SO(DT2;V3V6MV zMoVJKDHj?zcv^oJNcrA_@v*2#3K=J~MKr*~Lcw#NP)~xtMUVB0JQ@>m;xYDQ4~9wV z)x42<7x(_G=?EXId^j%Fbssw+ILBj$bp?TLfe0KA9bz)30<-w3W*)bdG z634t@by?!;*biTx!h%l5ApE$&i1f~zR+H~{N$*s?T1dJMsSr{=LT1D&8~Ou%nB^wx z>NNK*apZJFaq(x0+j2f!zb{Hl_YkrKMZlnKel5_qIgUo9!;{{>GMb`qWj(B~lBKI} zjk?<{<-5fwLoXLHZb&w9-Y)|WmjMKl@mkN+-eRxl6)XzK=Ww=@uP#mw4sNdI$BR+L z*OkG<4SVwzjo*D&L6W#GCl~Nr6tS$ZM(;r7+EB^jP1W0gb|L(F=rWnzrCSl|yOo%J zlv3sB-z!zrY;qUd2XSQ|z0iSug2eQj--h9KX22a`4rc^Q4CnhVZw{ul$Pgp|5qqX| zb9J-KVOBtJK1dl5W8HL$Nm%7Vf8=2HTenAGX}QRGLTr~Vf&Xl|{;laO zY|tqGtb38dT^{uEr4(j>;Obnf4xAu{wh*8lFN)jA3QoqupZNc5sCmV9na(&8H6_ z&;~Q{7ye62D+Qbah*QKfFyrBS&w-esZ^FEz2*BM-E|@z~8C4iBAMqP~V+cbzUQO0z zlQ-SB=Kz@g9kgK@BGw;TOa(BSq)b+@^T5hI6dZ@!FY^=Xm#?2beA$;?E{lns1YgO% zW0~}<#G;h=Zwe{o#<2DprK;f{C)h4DJb(Svf3u%H-2$3BT$?CuqcM4KWD?D;DnoBQ z6rQc-Un*MQ*E9Dig5gs)1_*ueBc}3<4 z8@3=Mwmx9oeJNV#)zStoKTbhU&p@fH{!}o8Y3%Ua5<)pd#!x@ex`y9_Ypg9KJWd>+ zNtA|Y1Z++cc_7+|TIu#K;rEHc=T9A)@k%EKal&7N8rBaDqz`mr%2#kDKtVWhBYhIC zOw1-4YJbm*vfJ>D;Lqr*p-GSX{gWC9O22*yUA=Z~-^+Kn=1M~bXO|uN8=5drNwc*+ z@l*0BJVMAjdl5EqxS6H34dd1X(jfY~Id$cw+Z*#K`aIk`PW4wn6<>7Ea&s1CSQwZ1 zf$GN9>MPpCUkzh>1xP$l3!|Vsdi|Q!S)5Ur#PhkYPCj#@)mVkp`4bQdVE=+TQd@iL z61iCbgGlj!Yy^(PU&Te>Oa#`+-BGcaJ835%>3WK|Nmov^ztS~WxLSLM{Q0wEqqY>G}7tJ)%;+X zqyEH;Ker}1kBenycircFr)rg1&XRfi*zwridvYIZMf}}1k=yqCOv^~FX;)Ym<8!M0 z<=;tmIrgl(h|V2o4U1Z?8Vm1mtX^DQetP1+=-%gZ|3&xK{#V{|^}n3o|F^%KMsB-( z7yF@olw{IAMOrjo=jtm7X_Qi`G6&v;D(7yq^wI4diYE@T&;=auzwA>oA4bTcG`}dH zX{HxIv}~46>05NRUiQdWUh!^jlHi2}&TSoA-=qp%ZvVkIuS*1Qg*qs1w0;yCvvYCd zGBnJq+wd&mlg-N7W@EeSiKO+b6i*?JGJGbj!v9* zdfW34&Mws>hFbWvY_3u2DBPR`W3bE(9G=;4*c zL_1jp1@gv`nY!EN_u9hW|TF{9XM2?4#%Z9mIas>Hp)u?3!@&8Ac}xMMXtnA)-lrcXW5m{;;^K#||7j zu;ig?Ni-C*NlxZnTr%oTzId&inM+b(z_!<78;C*nf;FNdm(jE zXf+dMV51|`4jZK554qW8=-*gjq_TmG&Kf?C@qBPd?wjFG(%Zy zt{@u;s5C3lOpp1hKaxndS{8s=y_EjN+w93K@kgYUdsyy)~fYKp6N_}M^cVBO|f z;rCHtD=JJHN3RdenYcAN@@?UNj>PZgVJh^4dgDqDj zFWh#z8}U+e=T7+1HN#oqx`sZ7#7TlPBpA99Q3m_JJav4-V`J~YutU6Wo$+DcnY;V; zd+}(m*nkpsmun zaSP*J^2WyOQl3)qoIhRDT-MRm`|H#+d-^mP?6JFCJL`CPORLKZ&C*yHd@sM{6X3^; z$Wjn|QMW%1{<~#w@hznr=RuC(Srt>}J2F08=KR@j`zraL#!kXS3)PGkazG58C3WZo zbLQK(m#wW?!Qb2T(g-sP&d5}y$hZO4@FNks?Eg?rWprTh^Or7rMLeL95%fKca8-f~ zc{u3GW9Tu8{b*&~)HpG#q&YxXk%@_MXO`8(`T--8k65-_p9M!;I)nEZND+ASm)6ap z>p$P#WD1(HOxOL^8gTTAL#{X`TTid9hsNPJM+G0vQ*eVfx#aLY81Ox9Fk+$dt?BF( z?-l?FRy@W$K|U7mn09hJy=jKge&fLnZkvL67d5WU8LrKWmoXWUN@uEO4yVk%W!xNm zy$I52f&Aud2{Eh1DPMw^bU(*iJ%fpuHA~69O@4kh1iUjjdqEj}hrkxK>RDrlp&)68 z<$gs&Q`2#K@2+yU63?B50hw3pIyx$knkV;hEp}A@^w6a#7TFqzAQ(ff zT_)1fd7K;1w3{vRtme<|me7KSW<6T)nRS(TL`vthF43o(oQ{iQ#T<6PTmcaVY;QE(x&YtY94JvrK{~R3wCnCssV~GuO z!!MmVGM>JH@MdXD-&}wCo-aUZB_>>SWJF`hD}-uzxLvw*8OMA9?wR90FlG%+O~SQ0fP ztC@QK+S3>c=F5>fKjMADD=UF#ojv*cocgKLZqK*)#CEp2l}q7 zuZeNB`2>mR+N-L^>#EZ7@LO6S$o7~s?T{@o4$vjRku!$U*e0bHAuzSJ=dlgVy; zRBzMLC?PGXs$yd4U$QMFcE9kd`cV3}Z`1J-T_wzcYlo2~qqkO8Q2~KrO=qJVoTP2v z&3cn3wxAXvwV(QgT1Y{`wk(lsc_AU814C$p@LMVj4K+D~!n`)J=~jp<0TV*sWhCsu zAaA&TK8$c!=I6O~>y`OF7#B^tQazBI+XKU8<55||5 zElpDf-ed4BI=BkN!+PF>c~X5`WYG7w5=JNW4W3b^k_i1%4V!)cd0h%rSsTT zghp?EZwVDJJ>9id+$5K=K9S7nHd7Zv;a>#ph5X#FCzsz6#Y)|BMW`ji&RMexc21oF zo!-AeOB33{cH706W;AH3bElYk+59H$f6^l2g^*Gy^{*{5(cxO_OiW(EkZrmki^nim z0mju{g1KUZ%(S;SpdW)&uZX&1wz25k6f+hMH@-!)6dJ}@ zY(?`i8;8)L{5fo8mGJj~yZf>(QSBmz60)aoYGYg@2wht&>PQ9uG&Z`muZP8zeP}Rt zm~YVinx3w3OZEAZT0IQNT<-J6D1V}l|2Jzixu@N_>NvBQzn9nLjg8uJLJtk_|6^%J zR{($FgJNQ0n(d5LjuNxPK&w2?%jjsnH`vQIyV@ZhR(+!Hs~TXHwFjuu_xep3LO%o z+viElRwa&VpU2D<;rzi#$tye3^s7MZys6&x18eUG4lE*3CPpr!ZotYcw*GZZgpz{R zh;j4*^TEA@0NdDjKju#${AwB<4fzofhbUHXeB4xsP7csw3*kVCyVl$fJq#CKoEB5t znt0`ejI=bSW7p*5K7 zd*vaEZ*{@2%~Xf`hWpoV>DJxd7cR!$&`KzS9}xipre9&GQ@T}+C-sR1F~?d-{Fn(z zpLCa>HYFq{#?IW(&42I~R0keD?TN^iNY{nG7hUzYj1+KhG&VWu|(s$_rSg$9FrReH_ov}`^!|D^?QL0p zR3l3%m}wjzG0=G#&iJZt>_cY=DpB>9OB2c%eSmduU}~yMT;M&{F}ed4v_7t@>uwv< zjCog#RoGE0MTF!JhTIp6pKBNLlaZHv2{ioyG&|6n@6f|Wm;$m9h;$Vd8#^zP7QTk3 zdtpJK-As|`wovrqQ2`{3;o;#panX~2j_%yKOrL(F30u&wE2@p$?9sHe-6*C$c+x_V+oRrttq0qhLWYKRaudb z?n_Jn?#L#8fa`(Vai&lE>8+|L-iQ`!z3Jjn^IJD>UXUs}9yixGIW#u5t?mRk%xP2{ zI!bP8@)3*QvPlz`I>9@E0%i%B5uB7fiTgq{05U9;jlzO@%JH<8r~VrB|t9Uej6&JwHFV zwdliA`ba9}yInH3xCLRl?`nt1h=_T}eXG5Y8^&SR9A;OWq_)7F-zw4yEA3KFXPq+l zhQ>WwH1$i2zQL2qHvolRZr8o?-{mbZ2!jJLTdndN6`X*U^z#0{0}*CKOq|id@Z~IT zm-cnZJ{uoNCRoETcK|oBi+s%T{rmSpcfwI&S8<~xFc?dqrlsZT{rem}7JHymL{AXK zC&tgOq(nPsf10H#?dE4`UZo)OPE46RwW~q!rE1p;9amExlI`(z@3WBJGHRkNFfzi7 zYChn8SFX~!1m@<2NsuunpE%DpG(rf^bT-iZa zB<0!e&`XM=p2x8EM9%g@g}=0})>opCAv6}y>CK^PH(Ug{A4dJnZC&&)dY(Y4iOfu7 z#hu^8)ReiE+CoQ{EgbaurPlj;6Z|up&1!G~C`rhr|9_y>NM16~pJ! z;qyn19CaKWPd|6-ekGi?N*NYgk>C3C&kk0yu(NMZX(|bYl&IaS!ASP8l-Q=Y(}u_q z|1Ykqc3TKjH@D{6gX$QpJ43@^xYE9u6&AadGxh8lA0Nl$J(t`|icXy8v-V0CEV+uS zayFgH@4~D>pM0^a3JSb<4iRP<;yfzy_Q%enT|!S!51jNXBuI!Jd1QjC0_226L?GG# zgOP|6kjl7TTcrvjm*YFq4}GW7nOB|R(k=0GVE`~;blpy@0pvorwt`e%*fT`BcN-3> z;FuVRZa(!m!-MvD#D@4brTDiZD@0?4@Z0Z_=$2U&CSh}c9!y0G!cRGWR z9lAUQei&yj4l%-=U0d7ZF;Z4jXZ_ay!-s3z9-7U}diU5RrK=0k#pS-mMEa+dM!LOm zl>qTauVn2)y9F=)?{g;3oNgCZKSqh6aRI^fsd@!@6t`$_UxwVbyQ+2XBwbwQdru6f z#I8b(X1iRuAsVR|II`v8#Dtqdt)9Mqn$GmSk`8xKhIoX&PoG|bMi5Ligh3vi56}mt zMF`xRQEiR`vMx?`OP`Kza7b)YH-7)?VSG;!w4a zSto^a($KKXauFe4<*0d18OX6)LivqZ&v^{7Xoa()O|1>E9TNX46(`QO(*9$Mxds4} z;76HQD(PsfLOMG%Gz7tdXThUwCf}n6OSE{%UcKDjJ?~If#*iTk4Ksw?z{$wrjOeit zqB!AIsEP9kFY{OLu9TcCKNuuxYTK{zQK4AH>(;IW81!;e(?EAcB8=&kVxhxJTIfz_N2jxpnU?t$$9^hV%At!ZBO*A2po;hT%EW1J{U_ zAlaR}2lsg%iaU<;_tDt~G5SdEv9aI& zEIRsCLejJu!W>u}aA5@*@YU4maHG1oG^)<{{_R^2AW6~;Y93u6_uk`mF=u^2xzW7& zK~t|LX5$c;fXxLJWhbZL;@-8U>J4wqt3;6bD)ellS$dk?xblY&WV6Ha?qAf`1MEkU zA64E-;`wW-+}7Hh?=Atj95{bonbw0AExI8j?d5&@nwluN&V zs(rSalFT#lQxdaN{q1jne`^DO(!R{t#mvUG`feOUGDMf>cyZWhgtNxvjyJk(e!moZ zU_94T_CBW|SMC?ThNPw&_gXdw!Ya-5k>f-6YPlXSVXN$2{iM{n*Fxyz$&)|0Nst1b z9eT!mV2{OA_XRA?(%;0GVVmw~H%4a0w#~Pm7d4R+525apn!0hH(j*sHS78z7JOdj$ zyU)&x$-!oATB*;1gX>c4iZu{Ad`Yv$U)K5cgyQz?7nCGkdv;_!j4oMq!YNGDGQ~&G zQ%1r0ynHEg)vAP>Z>N?6#+{79S)-!p>4a}zKp~Qgi^!q^*Z;f_@9Esu8D~g@1{kJ| zQ6d_12{pJ7(o9S2$i^goU<~;3u=~2-YfzbiRJHd`XmQI5D1a+NcxpO4)P?9ssu5g}+g z5a^y2QdPwnCm&NRMScHSC_}pZbz>E+j4@1`K6#3B;LCNcpQH0P@7^u`PTz*GwclKx z5ZUyu$h$wU*lHv^S9xnOqHB!``m??!P^(k91J24Der#+(DpG3E3~9>4ela2hch4T+4$VtsEP_1ptF~wy_MZw zxR`7YxN_p6ipuEgiRW`2EqlESC7EQ|?ssXW(kal*HIIjF7dP)K4MGfRA|Nt6OlxCH z&w60b3B9{}cWoEzFg9HXT#rzc?CbpjM!vH0Q^SSFbOfbrtl4sX;?JCaJ_kEHGY!eJ zhzNNAq4w&puVf65{@lt9T@W!OX`mtuvWMdgfi(>cl_~D4B5JO-v=F0>hpo)&ONjO8 zkHf=y5mztRc6G9X40XOg_$npkVMsgIeWTf4>9f+O^=Rv}ixN`@FlR}0+94A4s@bpz zGrD@r3FbS+&OYewwy__3A1(X=uTcpxjn{RxZ64dZ!A2bLjoB2%y#{pH#n@vLTD_nDp_s zr>FloCmVm%zxZIT=N77pnCfvTxI=l~S_(%=GT%fEW2wh|gDu;BTywnD7GlISmx+b8 zfb1B9GsQ%81i@OIq1EUoLB?6PrzO3TOcX;=xh1{x7SP05w7jP#2C4yhL9@@Ys>L@G zHO08^-vbB(fe$4sZriUHVlGiEcO#KN6*X3{k1$ahePi7qUBo&-T_++SfDFYMS}`X+ zhCt~iZ^pp~;AD0$#%s|GOe>x}PPdmu{LIjPE>$xVwv0lJZ%Xn?%|+aQX}owee%|sf zyxKK*2N|Ap0D_R*C=@Oq{XBqc6UlF|%hiLpmkx%M6CoWBN$|kHWy~TehZ2gikR5=u z-DG1}HqRp>r165p**bw`_b%tkCn@B6AdX8lUNj@r zSxNsB(_l6+KBfoN zSoxEOv>-EcS#9fj_gcf^8y$#s+%Iy`WSjhJUGPl`?F3g3hP8M?xJ!f6aqZtTg$uG=UTCE7yxX4HKJ}&E3 zh?<*^Vh9H|11SL-^$^*_1X)(nC6*3u5BFYq4$yfHfBKsmrvXE#6 z;t*?8G4FXa8^XZ5qdzK3zxb-Y?K!6>3hI-XwRjO?Z%o{ktsWE9k1B!*XvOm+E$#4H z_v+@)%JgGZ+x{xKNWbvyZ9RkHNM=m@_{3kh^@db~vk| z`tn|J#}5AWtc-YQ^~#E<6;xEJI~Lc109Qneb9bwEDebRe!o`R-8&S$Z=Yx=@Aua{3 z55XCf;&S@>Y$!_k;mrH9GhBj997chrQFsz>`jEeWh+_qXt!pO^UUlKd-m;pVfg9mm z&w~7%rZLSfM7tJ3^3CsShi$;WADWoJ>SeW*j*r)f?EHlchz&t3k9P4KnWCWWX8)Mh$-*n3L`ba=aRQV|gz49R3#b<`jEOZ z7|%{|aA-Cqci`O<4VjNV$c|PUPSU=LseV^RSyo*=n75nZ9cI4uv#Y($ND#Vta^App zem(~%J2Vw!!uf)beG8l254KN&iXd;5vAaRH9IKg-1*keVyxji1&gdINtItC}EK7!~ zsGtslOhhsR!{owHwG3uk+`*4r7WqE{=Qr=ZKF)PsfS}3H+?1Jpw7EyD$0C&QiNd*! zr~wU*U839P>M?0dM{j<|4P?F$u#*Y7b-~txfHq?_S^}Req-v{kWAKKgxG0h)h;6Za z;JCsOBEl2gh&4?qMksjR39C%bq;meXnDeeWFA;m=$;1RFeAq25=lTXo(d4N^M$Ank z+G8E4z9pGWXgF+S{?}H_8)lQp-vV4CJX4dZ3utHtj+i3_B54`7gR)E7&Q1V$IItML zSPzmF^h8@|_m$qb>622dKk2Qy;e)vK+G^noR(Mx(wJ!pavcVi8+~)kP#D)f=mW zU+rz4MMV`6HAq_E@Lm`A54@tvo9}SPn-v3NDmiySIS;e8=oZD0x78TDFyON)(By^S z*eB~KzTGS4$15@OaF5w8CxCu`_S&WClxab)^oQ4GK;zuQ?o|FOPLFbOiIx#2V1Ymgse_1q^QkOIonMhXYjSG! zl?@&4;ipf}4$*IlVhbYhkgX&NoxN6>Sovb_1z+MyVhCg@{*u)(;ig%deQAHvH~j?6 zJbs^@zRy`tuW@gZqc2nm5$J7=HAgGMt?iGcXl3nOKzeu|9S#amwoP;9X39}eOtTrz zlo6^r^v3mq2j(L)uQ%`SHsC9s=})<-ruML~ryIAvYx|J{c#0?!&CJXud`f)(<-^m7 z^WC9pyYWy!f4w+a|8_g6#FNX~NRR_!HrJ^61OTk|k ze>ZEvtoxpkq?A51W|+TIwsR9rn(2J83%HEo0qqV?N}=N>F*G)Ro0&=L<7g$oM8>04mJiVheg{>_6Tof55XBFqUt{R#e>UM2xuZVCqnQnwo z^iOA}7p{*eUbbmh&I5a<27ccmcpKQ2);&81c}yN8YM{#aaV=_5rFBd0T_mrLsDz)p zea90Z)-TsN#o`W_Fs3wJl9LmFmKzPQ@bCzX$PJwgagCKn#TYyMg+l1V%aY=%c&I_S zX)0*gAc96eP2=~AQ(3W=bR)aCAE3B{C-UjjC%ih`2vZKCsN8yyvZGOt0Y2bk+2#-D z1(XciU|>z~JMTh@tgNhqo>!QI_Lzl4AaneZ7LcnA73TD+!iwp2bqy&=@y?Zt0qyO0 zi8ElxLiqSTEh(6KbK@Yn_#C%+vT9i9%P|8OXMmyvm*`Q`=-bbqr$TLsuEh%JQPcx8 ze%GI>h!z`tEGPn=8BND(zfDK${0sUsB5{J30DEUwbN?@Yx~8?#;59I+1@LWMYgUyY3Dy*#6nwm-UtdcqRT+?|6(h{`oLb_sF&-jUd)S-lDL`~(a?lUdUx=jz(EJ#xrOG#E~rxMyuy;;2VC`6bRp zT8UPsboB^8v~Xs}V1DWk!>G8pSFsVU9%U?UVF=vWFZ+L*IIS$)0_+ocJ0Z zPED5THR$2^cRsA(&3@Wbtl-z~-An)>tD@)~P99~zfveLnM|W6CQ%#M&A~5M*mxeyL zkJ9f+{*S4A@}5P%e@{&9(5@s{^=-Lh+;tcTbw>yIk`6WY!opqLg2I^Xw0op3 zfAwWl(ADJuQ-w=!`T&Gd23;LuF>l}D*cd@2mGkC7e*T=m6;B#$%fZ67zxd}Bfza6Z z#+hKFf4W~V#HLz|F=(_z>ITa38kvnvZaG6LcrRcfl-9i++T$e`2g!2qyo)X`*em!4 zIPwDHyS}%Q`*jREp&CNjHSf+u6~BAtZF-1aio5Ctn0oPl^nL;8=S7`kB3?97r`+ zuDrZsx9wX^4I3NVY(~^W(-2@DwRLr};2m&sqFuTRKzRF>EYOgyUC+Jr*KyP|9UY3c zwko{4)7v`*KWd6r_7tP8q5~SU_cl0JRHs~Br4@oA6o5lY*i8_dig#9~Sx06?l}&b5!fAMCc2DHCj$r z3f9y$w3N(^TtZeOOlaMkyX`k)l9iw#m@)ij$BI-w22aEe0KFbQES!YqM@2r_j(#C0 z1qB3D4d9%?my+9$yi{}OanG->t-Yke&f-1onLR;4k$23d8in6#iUMs}T7N=nOV)Kx z%iP?+e5q-6Pd_GunHVHjE{ffG|D0PZ9az#yd_ows90s1Dc#Bhl(^~~NMTgzSLzutL zbjz%;5;FbAyWiS(u-qKI+4|XG2X>>fq4`hx>&_VKeA4r8w3n;~e_tcrV(mh%$dSmo z`K~+{4meD8q5{FXLs2J*v$wL73GZ z<2ni>sfc|^`O5SDH__W>4h+^j3tXt40!b0w6FKaS-=d|pHb@+xQ#3$coqhQ{zCPk9D|< zih^G$U~Y~IkM+r$zmoe7V&;78*nZ#Q9#jTkSjcU<&vV-*8^nvlQc{~k$HQq}L@$(@TKYVK?K8DvH zK0F6yPggfD>nLb}VT6rRkhU|hV*>ZId7ttehsV2aaV(jo*}-X_B)1d%gPAID{KYyQ z>r7|GaMYbXOvP2V>-B;ZU>@nUI2D5sLZPJVv}TK6^Ch=o_Zw`sPkXW#9#CS8F?a9C?9t=nXKwqIaiU`aQozfuNT-ri4^l}scsY{{D|<;8oq zHyhXNa?Ku!_3Jc_{v7kjEHN{LlP7;HxK$-_wPYWrW9!SqMv%=%LMxfveH#zKr&Y;n z-IPZvE;Hii&o9?pr2mV;VbMQ-k6-E01N;wm@JhO=&ewn<75-k2&POipsEq8HrKr9{ z%e1yVL&078H@`nG{rvee!2-K@#Wol`rs}gb>zw-6nlD zJzuI2=qF}hX*sqHX2!|t!LY9(a0uLxWQ#zrZ}R133+1AFDQhuE#W4)x!Ip>jODd0O zXtYu>UbX6YV=zZodt3B8%zblD%EHy|w0*8Fb{?2SV{{e@;LpW{85B|=#)j#w-(kBB zA{Fd`O+)YrkXM9fIIO2wzOJq96b6<4XScv*`sB%j4h~Cjg+aJnLjwl%qIcduuo6`? z9kFr|{ddIxEK$>&-;Rdsdh(0j|L-DYGLEo43f}t%;?Y z2-;P{YuC|Z|4vM{>KA+IrDcXmaKB>n-441(UB=)qPEcL0VdUS?*i1|5Zk6q5ak(l? z>>!L4Xj{-@8;f}t zLv=Gmvt9^tRH5|GyL@EfN{x-s{6}}D#}m^K0Q>bFb*Ds`z7E{>M`vF%Br)>LUmCpA zrY5vK63H6oVBPw6X^r+jv~W_h6Zy!^{it|1q>-7M_uk&OTg-nZg$+8S^Pco!9v@vk zNqwCf0d@UWbGS^r=wQ>nwfyBNH3K}k9R63vO-MiFyD8bOi zFx|G2IWi~584O*o(%x&=O=x{eiJCSh=EdCHoNJGJ`?zt-r%l>C$cy##-A_&Uhj1W? z=X-fMJExgd=ei1gZ2dW13m%8e%+oHEU0|vyUAWNWP1> zP@bx)tg5opW4n zZKg)oRn|8ma=HJGRd#3D+z+eFFplu*vEjQ^pMCDP$*7WFCfMr)ZJ(`knf}l3|0gl* z2`hqsU;h{Get0dpXX8!s0wK>VJzRl+v~T+{JOxQcICN)ZjZ52?(c5C^LQr4cF(Rn+R8W*{ z1uNC^eW(DTis+fm#B>_ZG|F@@Swl*3{A>>x6UiRp$KPy+$6by^hefk#dtUo^$*2vj zudJzs1$v56L6{z2`wP7l`yN0NKGoG0lDZzQY$%zr@9ZBmHcSogT4A5gIB8}k5gvZOYm@eJCGOq+|Hs0M|NCX}KVARv z-*4l8SZ4$2%#>@XA%9zYJ0wEb(E%EHW}RaVMu!A&%PO-2e`k=MPYOx`u!|Wg8DbylG~g*#WlNXu8FvOSE7P;(&b?5bo3XLHm!8uA1wM#3YP5qAW^QF6dq;+)N`~ zQvMQ@RxWlC5xe)H4K+P%@{9A9t`(qbf2%BKm*5`y1D9g) zS;Muij(1MnwnlISY~_tZ13FB4<5WXDlv8!z=Ck-c*(e#=>XL2?A#}OAl>A*7;B+T zWCcNXbvl2#S)TQJ;p!6jlf20ZAo&o8SIN!?Q7U0|4wMuRJ6vj5Tk~ADkxAH%zd>hJ zU5$yMz{v0z3`=*SeDEA^|FW6Sr21C1jO8&%){PrKTp=tKUcEf`q&u<&hu1+FnMjaM zm;cd)o{M<-7oJ_d*C)HcdbAJDn@9F+kG#`~>yk<^&$lvluQu7;xOMY@)!MO3M{Gu` zLTZ;ST_P(#T%hd8vT1DqZKitOZ_*ZsSb*l-l zLDAwg8QOuNDv@po^bGRi)4#$W3L#?qKQUwQmYA4W?|2L7IY)&36U-A3Tl4+?-1G}K73fXm`{6@gK%e!-eaCd{++0@%sY?crPUck6 z4)v64)6a6~|7^g^>^cPiIf{t62a~ijgX8J@sz)Cg1WV5SDE2A}Jr;LNa=IlNMo%VW z0VyfBC-PQUP#_okH0#yC(~L}gx^47U`z0f~G5J zMMi&9a+gbJ+5WGaK=1O8cDl1xCw;!j^575uHwdxfh4o zbva-}!{WG@1iH}!RL2N7R0h8VE3w9TP!0&^ceMJ%^$3Uv{JOEy>~>g?&EIxo5?IAxOVohHn6|fx@=iOO63^a*fo4>VpEN&WQ9h72=Vb8%oQruA))M*V{bbq2 zCqgQ?r<5X+r z1h3{u`qoqZc`Rv^>b5cg>suX*l{vSX3sXtR9yZ=!v_I)O)V{gt0ha#&1VBYF@I??F z#=?6lF!4v*nAGgqIUzcjNr%$P+jK$FszD%rt7mbAv9tPI{O~=?>Ekjn9TxrF6 zVC4eR8*h1G8AVta%4|$VCf}$nGWs&#-o0^OX0^uxsNy%$`ncXP9vVYH)`zrK^8w1Acj)Mu;Lbq@nHw2J7rK}`@1ufEi_ z(9xPoct+7~72lR(GN}vM%VYoyk-_Eo`e11mCaQbq=mdRa53T-`mw5x{hK>%H#znK< zZqo3k=(wt^)tk5vw z)SO<<(Qq7gyJf z7WVsm6}6iB+pPKHW_@6-k4{Coj)J=V1Fhr*gPvo0{V?JYFj=^jK*#C6bt|sUQpP9? z*bjh+@y%c-ACnw4{iyv*>o~LF2)UexxmVi)7wB|YyLpE@*1+JIxJx+6l@LYA>@XWUFuPs}IKjc(qyQR_M zR9%$oe=bDX<%?UHpf7D}`U>;YiH!_{y2i%RcU7Udp?H>Y?;JfYyubisfh+HB88faT zq^Kw1f1sn&Dax!&OcGla0{NVp63C0aKt!F$CDll-PO*`pPRCr1*xAU?kV~$N*$~cA z&P$Q&{y2Xl>z?^wdJ#;xiJoOAZt%>b(JyIfAfvXXT;E~GBK@@9y7%A-V#}Xc(8`?1 zGK;eNpnea_RzFn})f(_yIVx6V<-5WaY6*WI@B*SIdQnH|=-xM}sR2NP_%RRxSQm_? zfw5*aOa)6}>hM0{!<&SJu}8&s{+v+KH_q*|!VCg1+|n;*y%EwP=JbU#1DwIaE(eCV zG)`fZ@@oHnE-|O=!tp*DsU{d-90hrmj|y&)Wj-Pu_e>Aqny7-wg3u#up-#<;$1yI* z$^5>MZF8VX09)!}V+&m*K4O)p7VhybOKjjjzy3=Ihp zcyGRNPC=n?-1_w7j|k`lN5+0#11b(bx83$8j_ufsJ9yW_ZwSeqIU|;Rcft^cQDIIK zV)*y_s~wU!HQK*eZqsi{ctvC{V{V156ug+_XWcvhJkXP8KirE>5*>OQq+NthdXjK^ zHc^cIn3l%zGg#bxRAw!%mywO7ArY#6*7m5b}UzY+VpceBlFh>PBFI; z2eJg6bYGbcTQx(63kC=2b?zJl^4pP51xCeg*zF;FG4LZmXgpIEsW}bPQFX@(Ta&r}Z(F z92V&u^(8T+5^Su=Y!r)`L>9xKSvg}J4*rD2GZ0dYHJMX$qNBsX?<7c*VkW#|M}9(u zj^W~ia{)&-sb120*!Mo_AkMK0p4>+`%aU&`-JZx=vA~J-cr_>IBy-K8bsu=@9ot;H z!j!j_o;g!OQG4lH@1yC5ZN`!CgmT^-ruCmgO#z3R)TQh%&_1Y`n3&|p>DW!Ql(MSlu2# zs5@|}tJ?q9O&0r~g`~znuq_iadT-+iT$YN6h^XdB$__lszNx_5&CLrozWO19PHle8 zeFN_pEel{6ct>2tR2|>|@%lCrc!yjb^yB1A1WG=s7zd7ZIM5u9$1W=*kd)C_7>)YB@m~xxR_xfbE4mQ_*bS`~f91;D=WvoNxD%vMaN8)z2B60oa zI%&TZRu|yV&qjoaKT@&fdM8i6ymG56XGx022m40H^h>v*zUiDv8zRL3uNl|F*9&SJ z8Zdb;9U1Ok1(x~MhZTho9E$e^RV+o2Y(7B#O;96~c(e!)TU5ufOjf@&wuik?Z>umDFg8UyNdywL&+|)d_Xlsl`%1VA$qZnKK~z} zWw5vsFO|wghL;cV|B8wpoRo=u`_>(>z5S0-nU&R9Y~b6A?JWtOkab&i0 z!=4IVZ2pi6X`!-Y54HQq*a3SNXnd+fIv;mhxas9GLcTmQV!Gz>B2000uV;w1P2&~V zitX%^TKEZOuPDGwV+`_%ZaUmz7!{b1CVB(2W2Bz&Qj#>p`S6U?dRd}+tZ z6m>2zKA0>osulBL>=P3&(!i%Q`>0qvM|V;+@=bsvqG8oj2f2%SrkuLq6cP3DqmLV| zkms`1pAGam=g-G-(PJ%{-Vn76R)-hA4bm;Lbe+{gl|cUZ5gRa9*rN-_qlhoxIut1n zS|?qFSGtr#dB5vcg_Ud9Vj!7cf2IQCWHRjJKUeJ5cg2@(;%GgRd;hUPgytk=F&{AA zf=NrpIUDh|_HDTnsJWNgX;pUll};@yyEr!gJAf5|Oy)lT4{ET>yK4=BlWRrVMzpduc_L4T3A2j?rbInQ)znlo1~o`UtBzqDoV4t~maC(az)@z5)Z2 zuPu2lpi7TbhDdn>D3-RV_jwT*h{BpC)op|P4`~W$dzto}78e~8oLk}{+?JEH*yUH| zRc44S6?66~DZ7pPwb63qR%j+q$(wM%eox_+QB&pq$`CQ5f(5CS4jW?biD68q1q63c zk3eLJA&2Tkfeei+kn%1qEv-Fx8GwQj1ljPqr4Gbq{;Sx+Qf|&L#gjU*S0!aZ3KHabZ_BMFkZZJx_t z$?RjN_eKRK3EdY?2=U~v)?K<8iLrTVYN{T8J*YewUsh|FaKD*jC-WpIjYaHz_^gQzpp8S1D51BlPp%jHB%EnC1MkTlx z48mOb-6V->;Abxh>}uS zGItC<2^ouTcI-dlDAcTN+jxu>RE%swi;Qi+xn}76pQhnXO7H=*sbn-@9tMQNvF69g zktIMpNT{m&0h3qa)nEMj|1^~8itEn%1B34tD?a-3kG+fk|F4kv|Jv9mf%ct*uMD<} ze~m#nT;WZV9#LvILR#tcb5q6EDjdo^4X+`z#lXn^c-yeQ^}AdEXNf4)qthT9;W=45 z?BVbG!AsMcjw!K+xL4w1@jt}DH0xz z?mcwC2iys=GKrGt(^T<|ZIj7%8*2u`JH&yiNk`H|)&$-7bC5h{(!00~a!<4AFgwH2pL;7TaL7IB9I``TGpJ*o=rZti$TryyXJMEZD=)NH5Y*_nK z>$8W(>=cE+#eXREsJy7yEcAor-V8y7UJ4IVzya;!8J!a)e1TvCKmv##2xF33M3*W3DK?Qqq>RId-J%3@wcx9?@#EnJ$LA;z0{yL) z{_9-hE(l>udaS7CNB*pMu|9Sk8ehCa`0`a%RmmRmmz`EcB?1{RPzFq@$L_AYE`E$J z5IR7p#vma=D}rts21&Pswene*T-@ARaKBM{qkqG@^7vW#=OTq=Vn4&74dMn22v(|| z5})+Kh28klNzQu!T#1RKIX5`U>fE`hVJ+rs9v`T#e1mEn+C>ft39aHQzaLjw-{&}l zKw<}n3(ZPzvZ#kC6mCRtgLLHNXLo9#0c~yNFk0Z-2^S>a#wOZZb4ruqdrycB?-cv3 zg>1f@@_QW}%6)E3T?b>jsMDUy?u@vaI20n9tR8mt{Ea_ATuK*T1$w`$ zYo}du)$yS{u5JAJ4kmA6{vz)F@>$3hgYCJLJ0ACuRxox(rRMy%)G=HRP?r#{g-xd} zwOd_s_bsM1!M#J#NfdvcgvadX3NTtaX`;sAQ7fp0(Tw^3j<;q|LL=ykw=Gs)q1b&Mog#91($lMJsSH-!A zG13RRKTnVI*x@q;wQ6>o|CT_0cG+yy7x)#U1%b8u`5L}gf=B!D4OvoL{&VY(VbaES zzz3}KJ@=7!ewc$}==6AQE#htg$~Y+Q^o^nxI~DcJBks@JD(fPQ(Kg+WFg0-Uc3O6p z#!UCw=hB;Lm`8!3Shj`f^mjh0`QG1~v%g<1F2ZnVNFxF33Cg@Edb1g72N-MR=vt~| z7z9hnm=y56{O51JYb$iWubUWcSS`7{-lR_+nZbKU%Q62Ld&%Qu=j@#n9{Y2N=N->>ZspnkIpYxh3O;G%&J?YkA*L=5c z9`A`HFudz~JzrHX()^ioE@enyR*6->`-9<0>m}ue2*D%$3|Wi z8@K^Ln;3&rLI)1-rQGTAdYb37)SW+Hhj)Y#X{UwCTri#|D^baq@qc$;@rKxe|C+`1 z_f0&d7G=6Cf9NNkPejokG*`jyyvK{ml=<0xeAp<92Y;^iGh%}3*Zi=`SYZn#*ukfT zC1aAd!8gOMH#xdED5@Wi@tyyy_S56|wyUl()Sl6L1^ky!%fc*p*Iz%N3QlSnR`p~= z*lK>aOpN)Tb94Vq%t@i90N=&y9}H;0GZtab6$Nbo`-@e>t>f~ZOp_4?1I1W-M2_~q zFX;uWm(723bMFkTf{46SNC{k|zuZr;-ed&-6l)mxKAJ5jS1zf1&*Lq_pu&6H>i*B; z_ymumB#X)+Vhuu`vT%C5lo0L5$FtFSAKpam-B_12^i}l{6O7F8PT>vs`-jSUw87{P z!Yk|$AO!=W!7~k0p@oG7n!|*<4j!>1SSnk&O-@bvz+Q(~lj9iV60d$m>#y6G$i&3N zP`j2qJq|O7%a<>MiX;8w8APBc*^lz_mhnB6{brcjhLOqX-FL7)5Mq(68PIzezS+cU z>7T=;jmgD5uVZ4i#MUDlXxr=LWPif4lG0uJ*)yC?(3}#I+CqK4-4?)B5`;(j$;&*y zLsaVTA2mY65)dy(Je(&jxqab0f`XIU6GnYVuYHH)5`8{<&C|YlRz|QS1%8I|62}q( z0%gDPzep1Sd8g6ok3(hu(S<0B0(}i|{BPfeQvtL@TZ)1I8xo=-)uScg{{T;M(u0#q zh}X|+8|!P2ihE?=ThFOrRqVAD1wI1m#gB$hf9{I>xsZDgyP}RmqVVX!L6CZ0r~h>W zrwlF>8U--9AGeQ7zx@%#+j7IwG8O?LKxA;Q@#aITn(7}G8>=*QF?=`_4NMa4dzkXj zvoN>cSIuAWW$7y4kd~AZ7%DhDFb)YuC4vVB;~WMG#1FwkN>x=Al$l}rQ*&z*68(Al z(MdqY&52oGd}9bhU>&^$ae^(ZV^8=teKJ*@3r9S}Bp(S|hksw2&z56Ff;+0({ZC`h zaVRLjS;0BTMW^s+FQV?RnOyIiUBMZQ!x+2__|CEK@%_06_fH%;33)DI^QOJ}zeX;USVl=x3yKO-&N(D_V zEj$9zIJ9wu;Nr0Ou?jK`be_KIOD*P0ExxR@W96Vbqst1mSL9ED;`OA;nDJLVLeq%X z?XN3vnQ(#D7v$#8X=yOZcr{#Gs}@;Itm#5Ba(TnQo-}$i3R``$&MNYW_I|N2e*hI5 zVdx+o>47AGe_vq_Q1V%S#&v^m=#8|z9)Ddl4Qcqb(d#(9ZOdZJ%bNnCM_uqS+zGRTcT?_ac|=U%U8UKCAx^Y)1cId6NG37yl32x&H5Z=~h%l{?@<@ z=Z}n4k|m*uMwk(-F4Rp1sTxR65SwtsToSmofMJOh7hQ#Fygy#r0J*f?_Q)Ru<0sh6T&}B z^GrilmqqgX?r&qcHks$b1wLrZL3uO=fU?l}XKlVuny01ooX+Uv=os2qTNYUz$Xg*4 zNf?v#dC-B&D7moMy*Q4jMojz|#BQ=YoDTh!1?JV+{B=%%t-ksqeXUU< zuL$l$+j7W6s2(n7i9@a8j%frFrQ7SW5i8Lt`;G+d%;zAr3|2>A?49bXVF^nYVkFbh z)F@!t)p2tE=TT1HA-4@u5D2KblXu_6#?DS|Om6U7bhPv(rrI>~5S|{y_*1c7a>5ak z_ueEw>90t=K-7m~n2^_l2b)%Ro$JtwtJ`}^O`L>m*)wyIbg%D2=WPO z30CAB&ddONm1AvG+mqn%NLbE=a;ov)Za9k`1ON(Bp5UQJSU#b(fr5e2GS91fXowc! zYHhxM77VTr8edI@t9(x(je-_CCDBD1h9%0=t=qR+$lRDY7-ne)A5{+nxmZow> zM+wv1moDCaY38~GNUR{TfdM%}diFg-JF!F@ZBh0_UTD^`IHo~Ft_DTgI3`*PW1Xk3 zG#Vg@1jQ`JzjSvfT=${V#0}<2UPs@groI=t!i39}}g$!DCL+E=8ziaq_N zioAKP;lzOu<7D+0`D^3bAtWh7#m?N)RIBpH8tA38VI5Z zZK90Dm?$Bn=3x3oSW>LfNPdH#J|Kv(?)*qzw}JSUF%Fiigw5tOtP3PZ#DLc^wYc3Q z-a=$#P04mzpz*i5k?7HFOTUdK(3-^RitI&kGLPw=T$$f>ShXxM0sGe_ZkBapeex-@ zU|{xSrYDlH{8jl%n)4T~tdFqfmBh+8=;TLi+PTxmdEHy@5F6V=OSjQbgqfKZm$lXIBQX8T=M>js z3Y^y{Q&>0p^X=`ZpS^rpdN@eCGGV_U#jOeiaCAfccgX)}QeM{6Ntcb4%21J@$kj!8 zFI&VLYz#s_Ss0s`?Hiw%wds(kQEi@ zUdoGpm)v?fbGIngD{eXLVUT0v*)g`#F8WS~EZuEl7M7VK8RM*QSL$A>PS^NsMNWno zxL(Nh(eW^)CK@oH1_ztW9{I-0X|+OCB{OhEjc{9EGK$cW8Vy6S(Iw zrE;-|*#8=d1*>3^ev@o8OquoTO1$8r(Yl%X974yy6ui~K zH-X5c;1f23{OwOld%?u_*;pM>d7qGANLXP`z1bnQ|KFZs@NJOZVJujTg^@%{RRB59 z4GUA_t_}zL+%DJ2db@l}mAUobJ@?Z4b6IEsyiZ`Ch?TZwzVuGH&^S%Naqg&ic9yS( zUFoPLRyTv7F%_Nn^#a@F3>_Zmh``U^$taSHuvJA`!)XS%o}!D`?zTJ|c}_veX^6ee z9AiKg|DYgw(Y?sQg1OsDqbAxE49W3rmmBW~%UJU8cs@KUG?P~Yq6?KdpAs#~Zp@(w z?~iofDk5{ScXny9%gs5zTL3zkE-9y_U(I=dxWIKni-dTBoVmIR;+L0gt}d@`5`Ade z#u*ofGh6*u=Lxy>)`AErC$fym^=(KjxQ;jOI`@!O^`d5FVTyn&l5anmw+mx}Q$fT? zg6B^k<|YkuNK61XzLF+a$zAjPFot6o%0A@@8h2vu;4fD{_hN%zqoYfmtP?QmcnIkv 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 F;a^0eM*#o; literal 0 HcmV?d00001 diff --git a/docs/en/integration/deploy_integration/images/ds_switch_right.png b/docs/en/integration/deploy_integration/images/ds_switch_right.png new file mode 100644 index 0000000000000000000000000000000000000000..7fbbab6963d115fe2517c3fbab0a6b27318f860c GIT binary patch literal 106556 zcmce;WmuJK)HMnQib^O*hp4nP(xKAQNJ$DvcXuc$DWHUOH%K=qjkI)k#{%g-bM5y# z@A-Fro$Ks5p>v^90zUQ1{jxnZRn%|`n*zkZouL{}Ug)=x2+gM#k`9tf@*#nkZ&kYdX<%?-& zh^v@P8yBUU0w|g|e&`CAF}&=)J%+D-iD$4q(VU2PJo&Sd-|Xn!>%fYpaA()__RBke ziLrY_mrfsEO_+A!GyU0nTWzEqELeEyFI8_aL6#R#Bj1fL#r;%RBAT;n;;pK(^5ffE zNxa(@hP#r+E;cb=ZmY_E&U@MIAUnr%@2Xvgqw&_2dzCTzu$(96SC^fQnI8*3i!c)X z^z9vg)!w}{>~gVGS8hbdVX~S$zm@RHXW z3W_~53d+U@6cnBq6cl``_!@aW_yL-(q?jH*gy)6m~9wqV*s)WMx zUDzHQ7e!S&1s%sHRyLLf#%B6Y?3}IipZvF9Tp{J}G(%^eV7sp3@_$~vgGEC9>Z{B3 zeMO4SEVs$&;iHtOR4)wgV!wmh$BoV)LSY zd`0&J)d(d(IK}t21}kgbVpIF6V+;u&NvHIKL2KTw3@2aFWfEf3uCF7s4>OF+ z-d96%%4m{RO+|^i%1+BNgPOHXL!p+MhdU>nVuQ*~j^2UT%ZKcVjLGyLC;!o|jSL#z z|5DOqIUfCl;I!2Kt$9{&1xb1JAqwTU&y5t3yUc&P6TjaoXeC&gAUVm%q5~IuKmcqWiiGfMMW>0aX-6kT{fYKHM6AGiz2l=dhCdH|Mxo=K21mRSHwz6*0s~wUdm~&)7JT1jZI9N*6kJ+7rh6J>S)f7ocI*s4Ns|~ zvpeoS!oe9>Z(xfSMP6$5k&=?Jkx_DCLt^r;Cl{BO14iF0?UOj-LL#$d4t?09#UVG@?hG~%= zYh1vW&<3OP{DQp2gp4kyozvC5y~7lhBHV-3@s4?$>ah}i`uF4SeSG~AUs};HFf3<8 zNALFj)i+`%^1~pW&~KJeIiIYtC-yw+($3D!eOj)<AmktR_51hEpE^Rd8arzPdRsH~amIt0 zHoJ2a9(#G(di|SwPJB{vJcmt3=B44`#bNPN8fORFwlWM9$Stm_8Y!6lwP{)RDXECu z^Q0LeE-CFhqJ?*Tg^1i5FZC}evGKzoonX8;q1xi$rQVl`fR8YKm$hJB+viG5%we&# zZkU*noBJbHMdP<-5Z)tV;=$#AS68UQqM~0D5_%fk&$;dCl7@z~R_huwGpR7icv=$q zUDp#v;3?eNmifuB{ISUAVS$rrPR>s?znN0Q;dL9eQ~#w?Ihj*X@QjY`-;K)TukrE0 zjg5kLFiGY*sgtPqUc`U8d8bT07k0X&gp~x-Fj_sguy6%F2NpM9ncg3hRA6%^EiEk| zIJnM;Qx%@I<#;^j-Kkr#3&tHRGTnoANaq#+v#%j$F0fstIIpRV+8*i@yN+# z6%}#C!pQW-OZ0K^9`9{xUq>W+pJeRh5c5}5!Q{^gvzWfI$afTsjDGZneMdWj3@^V6UR*i0vjLYV>%yGqu?Y6Vs&T^Fo4>@9BzmljW1dUM z${NDf-9o!%H@W%>8K{{xlyd=JzGM~_hMt|dR9Mb-`k*iNy6Y?5ew8uP^S>-A(`fz>dgIql z50FO7AR{F`JNHk`A+hS!?>p@5L$>GJl`IAa*_iVpR#wb{RjZ6wCs~4}_M6Hx?`wi@ zJ5J5$3km7zJu9leLOVS@HRSiJIQQarJ^C6RZr!H;_(MlOMKC#MXh6VSlrl92mVtqb zg>bREckeFsaLzXAmg};{ehK|Tl{|i5; zI{nMRTzzRdqj8p=C5bE!sbZT^UV39cQy!4<#q`8+mTuJ_69{^2aTlz@rHt=$snry2 z*zt&ll5{p>T?a6YdUWb*AN(?ce53TEb?yo}*cH>^ANo%l$&oW63-x&%d1%d)y0&=fXW2*}IJlSU119(KoZ zMsM>v{B+oke&Kq||JU`{U?@90q4(|r0?`-4q9-mT&b7z_r|F+eVtQHGHy@u{j}{jD zcp}w`v}j3h5J1?l#5td&kh3Ohd!jwqlC@LBwJl1#S%BJncDPx6e&;y+D|TO>=aKD7 zPr{;RV`}PWEWxvwZ{KjKg2_iy3jK=0um-bavkEJs^kUnMvn`)cQTZLr)KB{Ip`t=g z6K2*Ov(imf-|$*mS_x0`yjMEj-LKQk?8tgw(*-dT)2-kw1y!)aj zX?MfAOT2xnJJ96&fP^`@%l`9?o6aXH{D82O{JlyUtIj2WV5{WetTxN(o)c};gI8HdHKvJB*4?t+bGC$Gh#V5 zmSVc`>(foA-B0J}Jd%Auxw&*&9?MEch*?=xCxXgZ4`Ot5becdv)56$xe{zqOuA&#O z(*~GbN2G4txRE~Nhditg0b+%caR1q7dSTErQ7$YZVzZjy$ZWmf!^R;8b>^?aJLe@s z!9^y|Q+h%|LcXKE_S4QVas!B^>Az0~rlz{pEgPEyu>^H{3Xyyk~J6}A1_s!QQc*`#}e14I{F`e zDcr2^5-J+CK8)w)+uth9*Q)$lc zL_7%v=@7kjk<_$|j4R`YNm&3weDCi(p`bu5P?mIdzFpLC#M2d39|W)gGEJKiyUo^^ zHHi#0V-`-_v<#%?PoF*!gorw9js2cPyjZ3d^oaS{FNbJ=hd#$5X)!5&gpV(2CiMa` zI1e$Wd8Xy8z>JsPaBg1*(@E}8M`FFJT2uHx8!fl>4924EWi|t5tGQn?{ufXyu=!tC z9?8e^n^Fa7yFHBS5nUfO;9j51!h$+sK_o+u`NMtTR+3vu#KZ(d$7koct8<><$dJ&` zoddM=;^Oe8)TfwRf*pSyqpg=Z@;GLYm<)@ zsQ2N>@*_)Utm`3(A13J=4YT~R|DgS9r%8Fq*7UKne&-_-6O*;!Jbcb+yPKnZjd-Qw zQ#w4ug#;mJjJ0NHjL|_VQFVU?(%MhXXUU&kD+*Lj+2Dj1v}k1{Mxoj8TBk^KAeGMX&h*ge=sWG%S9*Fk@q%twPKIa|wHvXg4z4?Sp4#|f5x2$X z6$%%P-jq{UY2O$&g`)Uj&I=WCii3*`;B@ZE3Ue`~;i6vkPePDN)^_I^i%emkF3*pn zkxh(n*l+==n2U?0MZ+l-+5tk=$Vj~Qcws^JOHh#c@kCahfZp~56F|#WgHjgDo#9)a z<`yS|FF85iDJZ;^mw(tEN^aOSV!k!oNS-(R7BN%5K1rRR*UkpH7t8ZM^Xjwr```45AF)>$C$OSr(sbcoiO%gu4fcI=gYuHwGbxV%{ z^Y@M<5(cI^33+p%d4CEHcK`TsYrkRT$jPc!gC?-);&jx3$O`3Pb28eW%ne0QP*C7v zS179fobmGPn39esFf~>8#?706KM5Y7$t3(~H)8(45qB38v(1cEhc%X&DCFey6q0)f z0CO&@-6u>hJi_-D+a3`Te(73%O3xPLetrZSDL&iao}H8P8<0^h89YT!e*T-hVOIAE zc4+H%tN6{PD-s~k;MY2rqkHD0AUpQu_{GAr?`gu0_$%ivA2(hy@FYZ~E-aKXCM6{y zNK+NGwfpwErP@Na-O%s=M#P2xE^(Y)?oW9J^(bX@Q#>Fn%d)YdJMm=@Z6tNE#U&cV zoBq+(w4}KvDKeU%XnujBw70i6WqYg%Rn4c|bhJcol~K9qeKUTZ%3!6Xc3~`MbS*+{ zC?Q%7f^&`K;_~9;H@{=#1if!wQKFEvGy@bxOIiln>f~+d1Xf?c>vegTj3eDo!?s96S)feP7YE-yfKn zt$v|czV0o;D(W{Wzu2M{Y_q6b)H|AwHB5T;lrMhLqP|>6c3|*(R8&M-uY~lT(ROw+=(Ki(#{`ScxSf+KhWXPQ&I(CLpd*;BkxQ%3X7Dv4 zqI;xZa_pxyj%2|gaT_ zBZ~$%4>cUdAIxZ9p^j6RobINDRjuySGTXE62lf~&7F0%;xKBC+i$24;+(z%k%vpF%KCfrI+L$LyzPRWF+-=lRuGkb$>j) zLk<|~R$CZ(yFOcH2;R49s?@rcySjO#Lkryvh2=^SE7;R=0=2v|Av~_Q>a;M%|)V# zM&inLB(J3`3gF3cwmu-OJ0jWG}_Ghzc3D~K#gB2DIGh`;Xi+-Am7M+_5uvS4uMXb@oT|z>F zVxi21s0@$J{63qV+B7%*<%kjc!CQ&>l{kw9LD`GwFo9E_^z`SDw8jfZ{Rh{fsaM$> zXeS>Wn(Ceo!eg;+Io!M0||!NfAk{!KY#uVOvtzY zsgI)tsO=jsO_8lOm)pr*=mAbn&+TjCyOwp^LleHKC|Wfijt=q`a5np6ESB~OtZq9- zu^KP<$Or{jjQ;8BdJkLzK;y!A{-Ce@a%AoYva0!bWLE=1@Te(e`N1mMt|2E?T)Mit z1Z*yfwU>>jN0Vj~WwG-@{>5{>ym!~qqyHlj88wWJ38`h`f9V!8yBw^^AnpUy>FMd| z=UH|$tmmo)q}e=`xBB&aLMz|^)r$p5CqA;mstX$_s%LmXzF5j`9mJLm62``~gIUr5 zNiBI-X%~p&evH+a6W2N-C;g`w6aayQNSHFP?yo94n>Kch8vhe7Et6Jz! zX_ks-4}X30j(CPO^1hx7c zxJvB7PtTIq4q^>{~r zy~J@nAu3Ronp5k)Jn0FsGyYTOCfp7rJ^l2|3 zL_=xb?SBm%4@4}!n>!J4oNBen<5rPYTwFXkH8oLbX$-i2)wIPCIhR-a;_qz-Hj^Qm zp^^8d73Q-tjScSg&3Z=LwEN?R+EypKIsowhD5+>k#7Mb9YrOENp8>UfFMkm@b{3Bd zr>ninMr7LpF_G0}ur=9NvCV5_Vgmav;p6X%`hhjJYO~N67AX>v=)XN`WLH{FI#QHZ z@Qjl)tgTH{G?+j}PHuHtC`9yGvmch;#mOGLI|wo|o7W#OlX{kJ#BlA`YYRZ0{ zAM55Q$QfGOT}zDy-T<#ZR%MLy`0-;D4GujtKy*9%n4Ez@LEJ7|G(ZqaCGvgw`7`QQ zGLSt#Hi)3|+`NOyRb=`v%u}Fu4$8jg_Dgn7PUsx3TR?TN;sUrdBTC?fdJ~OV?~L8= z0pS}Pg{MF-ZA~^NU7z<@%Hv{G&e+@^)plfnt|vPBg$|S>?8lE=r8%|w=6-uGd4M?4 zLYbm|BUDGo?-3bMZ=c7RUQ<(3&4q%wg~iG~Q^Q<;iqOHvaF9eKl?#2*O{rM+aQL!V zzY7n^akd4MiD+nO)S7QiN54M$Vy391-||!E)~#C~%GG$cB;|1<9^q7bT$RI)Lk097 zH_J?BPC6v!z$PK-)^ud}c(^gVHBle8s_hiMn&`YySJQS6LO!(~$nzPeO$IIZOM9XX z;woSh{4fds>K94H^}L+S=OCEqTgJrrKA;FsN7T0;3xPE#(8W zo8j{ECqH`9)6)SleFmli$Ph?-o#mq%%}{XswTh0>*tof?zeTHjC{{f=_PlD2P|>EO zrTr2d`~W(S>dT|a7~5f`Rku)NDsa@Sf?VC2jT@If2>RMiilvVx*ExaB(VfFEy zV?cji4mtsy9~ZD1uNn4>8b9T4nO&V#dhP5i2R6)i)GQ{K_Z5+u^5?AfD$C!4#7>I-vE^; zsi5$e8_~7xpjSFM-r&y5)L7yJjTzLZZUF-W4G!<{XQDri2dR)Cqb-DZyv~^|O*90_ z%1IFj;?-+~iKeZWT}4e>7y{k1w4Cq}0Lk+f>f`|V4{C*|Zv344C0q@G$_iyd(Upg{YdFQ;N za#U;Oqh$%#zWx_|afKCDV$tlERg9=PAZDBbV?_X64+!V;#JxJHZhKOSQu8RVGl6xv%eOtOZ6v}|C1^M z_LFnz9BLDgDn~b4U$I9s8strAB^Tz&`Gsc%+^x7s#Hr_5 zskv!=m_DQH5>V}y1Fv#ih>$;Ku|ZF?*;H9WTu1jbbK!-pr)nhu0)nX=INq<|9TjG#neWh?Cv2=`8sQcv0!Fx4Y8T{Lw3jzRXCNXb$|)+!D$9T1KmA(H3j3|8>^tw2%gad7 zw0WtUl92q1Sy(Pk!@_KiQ+?}xo=_@#oY4Z%F`KA@nzq_FRs8{VU|?X)ZdHIp41nxO z;h9Wm5u5}{dio$(7FLHX)xriB%!dyjCNSnfZRK~xgF?C6%PaZ5)_|dSfQ9cO&HpY2 zIwd7HVPt0upyv8_$%GHksALnnK@@EPw5M8cARXNfP{`QW7$VuJ*6M2I+QawvhY!2S zuczH}kZl_oP$I=P!-g08Nn!7lltcih;805eJB85i{}mO>S79*;myDKISNE660v<|1 zL*b2<1!G_;HZ=X;Xd}@v(ARAQ&yNtR^~W=Y(|#C3ilcC(clY<72?&H99N1aZFXI7Z z@3)`{2GBn-L<<^7RQcvesm*d039rMqvokb-v$bbYOxmGIN#tw>QmcNOMS4vqdnh1; zKBuPk&(5YfJKpq%+#u_DNvvLBCQ>w7YW4d@MBm@Z=QK3fezQ<_!AF`ii4N z%uXF&Ej7r6Anis(oGhj_8lIKq@SP3~#&>k;bmkRV{~MwuJ00(XwpZ5ClO4GTmRr0y zRsg;(+}!sL51T)7q|n@3Utgbcy0m{lVzrX0@d*`m$`OI}lbzFcZjK@~^{G7W+B9#7 z;N_k?5z)q^64srn%L?_MY78F@^%or+1$=*`4$Q5tc3qejWjG?(5u0hEp<6p$lyI>F zalln)H9UM4oUkn#Y?fr4Q&yU_?~&;|CpSMm`DgNU4ea`@+qYSP|6b{-KMOw3$;r_l z-QjndB73SdO*Ab$&!BH1cf6G5#fy)NnLYXHm5HA}-`LpL z}G@_Bzjx8&&~X;B>D$Fu2Xaz5U{ zAn90>Peb|s_pbsw5eB^69cQZ)J^0tOj7W+K2WYTWMVxLUc2>PMJB4?yd;cO-kf6#S za=k7v2U)vj^#eDyugB{F`kdHI^V|yxB;Yi7kdl(}?fdtEEZHR6ADmiBN^iUVboBl) z*cxL=z%*R9OZdzB0#7mvLHxkwv$yx&{(j`f0 z>Fkg=yD(?3ArpnF^oxi4`Cn@O%Y0v!X$wvuV9{}Xt@YZGRmJk)3BsY4+xn?LV8j$i zU#FSG_4R_luvewu8cuuk`0*F5qO697#P3Okz}e^By-?f60Qy$Km@qf@(L*vn;F3|k zD=CriIR$KNm;f5tv$h6`D7$Mn6?8|a;*gsy4-E=+LYTa6F9%D&c z7J$$e4g;j-+dTi|`SW!BwG~Io|HoV5!=<8)#l?=V6Ndz+NXpO$d;0DF*N%<2cHPIBzBz?oDcKOJ&DZD=uh9sx&MVWgV01 zkW}Jj22Jv?G4uum4_w%ZGHS8bQ%{p_LDR2WK4EN9@}ZL3s(Q+a&&Kf?ETJ9o?J^<^ zo&?Ou@qnZxEUL))RD`IK(vKf6*k8W4=83PGvQP~BAFg~_L>{+H4X#Z_u8rg2A+TmQ zYa!Hr{pb4m+)p+0p%IP+hiVpr!x(odBixMTowvx{xwfUF%N1)~xBEL~2Hem?l1E+x zPS!%9ZEXe{CdBgW_ZmD5AomA8oxO(L6t0w4nj*vEh)Xy-qk=~F_it1qcK*iCt(9a} zTnUUs#4oBeEiNwHK~gg+C#tS~2)|Pko0|(j@kd;N>I>x`L=0#pQ|tJ;35$uLQeSD$qiA1We(bZy*^4i{3;< z^+=|Hb(3TI^e}za<=4Dj4KPbCz=;&Y8?C=pKAPvhaD+f+nJbU>PLs0N~ zN5K7me>-Bo{s9-zWB@LV%GDszg9r!j9~c_)ncSwLpm>-z;3R*f4^p&IcDq3-gkR11 z5QHAADQB*!$=wI{-ca)!`ND0?yAoQ_1TI`wD(6vBQQ?KP19!@@ysQt$5y}ortklf1 zn%|Eqd8NbP$q|#6M&%Pb0wT!!D05kgZcNQB`N5(mYK_3!S>I_+sjG|6Ks1Pnws~Yq zCv-D#&e{zSDKp;Q9I^b+8i-|VWVWDveZdjKm3l%+xg0-Q$xE`d zv=s6EyNI~>UH9{J9BZ=kqU`MFZ1hMa*yUIzP30|9x4|HHfoZX7$Dco$$v?5-5rLXF z&dIrjhL&i}pEdYpa$8{Y=y3=d;PsUUtAG3Z`&}N~24Oa0rj~PlUT4+qiY)Aoe>1Ar z%!gO5QC>gu^Mj}AB0;%W*j$x#GkbA)cF4P$a*FBi{kiFBNvZ6dL+#oyz3-4#Ju|_B z)iQxs*4F6G)cZ6H^whwTr?=b!!-K%8EnajeLjPBayqd@OL=#vHwn%>{$bAhPTz{p+ zzdge3%o_IR&pkde+2~0nT#Sbg=K;<+9Mk^SCJxUxi)H8MKYjYNt`_SyN?ebOg#{T9 zCR8uW@ny0kZ-fp)lV-KF#scOZ;|f(KBjW!d(!_Oi9_s5aBRc{5qWJ0QEQij#yu8J6 ze7sWoF69?^^fFmEA);XEdqPVaOFmT}Fpwr@U+n?Zo1~Vfw=^-QPO&Pu0O8EJxpfB< zvVjzf2cE5=<+Ji~d%p9FeY5Er(!9fiOJZP2R}yRSSWJdG7)P~%nE?SG6S!=yEeFVs zF7=q6o}LEXF}*;CF08JOuGi$|mN;zlK;Mh;_U&6Mo&=ONJGG|f=4!xL)iy^j4qV)` zHm|{oFkWF!%s3h=6x!f^X4S{<$l}!h3ll*q;N}Ci2q^~{Ow3x+=t*DYyw(rQ5%KZe z3HL3K$kK4iL&usW?0Vlil(etqBWvkg=w#JZ&1nLox~69H9deJ3T&1LU@7{S&ZoiRd zhigOk*CizA_7z};Lv;~&6Qk;n1(G~!0w*SFqD(V$*A85Op9B}lsz26LSXfwfmpM>H zB?r^Ts-*@DhxUh1b)d?Dl9wWcW{s#nj+o)a*t@irKvpq``}Tn}|2|{quy*hNa({)z z^ppiJN_lxXsD7;WDj#3vyR$t)*rub8}sau<%dV*y4J?koHbZ4FraaO1`k^boSG0ZD1m6V(j zm&_SZXEK}mh3#~}Q8?U>frD*>F#4*2`Gq*MSOav$Vep8Q7R zeY~qn#I=H|;?VqwQu*OUUapZviPh;c8_$-Ql2U_4Wn%mFAvqnL8j-!bo10OEDi43? zj|sIz_bX@U)(A0cFHO(ijTh_TS64eL0BixpsxL@GvkGOF zFOQ?&DJphYdR&@5efEsUEG;)Tu>JZ{8(b*?`=&|Nz~sP}EuE347ncan3|SY1RmGa^y4A3aX`>3dECScMB{j=Ba_4ziI&z@ zhhqx&ELa%p1z|>q+Q`hz70bGM=nKGq(KrTn)WB3``wd*6NzkCx>goi~WR1yVdSy3Z zEiE$3x>G87`S1rM{j;?V)ALI?SxL*kK6yh?fL@{lbgTf6%d?2cxGqVi`X;2d1`Zw2 zlfTSfu8f|XpC?sZqg@?^wU0Hp-+{}RPbgY6tc#u^F?oD}{4a3jM2T*_M|#6AbH#w9fM0-0 zhM*6c=GyoJSn1#vA0LMzQURN9SxL^JV3O%JV1u*uD|me{06cm0^l`ER?>oUq@Qjlw zOdru8X=e7|rdm;B`(jFN&5vm9_f2s2f2zI!7HZ4E)!%P|Lnb;1#EKTfB==8)^G_HU zzSrV0e4MH(9vU9q-^eRMcE6C8ch|_TyCs8?OgV+S=dJ$DoOhkBrO5)JnVykRVl7SyB@5?_Y_Gs|gq(9c#$JkKDc({-1%o z>XkV^n(^eNz*4rkf)2bzM$;iBX42j|-Gt7%Pb@yS59s9T-Qewn<*SE*dR2m8$922p>rxh|7q}IM#cqAZm*OO4xWN1F7WGDY!oyMl2>os_|!t8kc1exh{ngq zw{L`kantGo+(Vdmz-6+WbbZ1L&R&L1EP`K_*9eu1btX@}uBZZ_;Y!@wWk8`~MV#?E zZc{KZVO37{Al(qiH2C`UYm^6M>_6?ViJ}-y(J5r&x#lsr%p7>Z(U)0Ph6~o*w8nFc zNGjLyoUz4J!9rH(ugCehlV_({WkPZaCa7MaTA| zJ=E;%!LyfFSn#uAok1$li9u)bIzHLl*r>xu#i5ols=TTmtL22ULI+*R_3@@Qi|fea znH1(`KTHZ13bOjweYJ=NS>yMF)y~c{Ue5#tliJCyIcpuZMe~M{6*HJ{(CKuU667o} zk{<1h2LY$b&HY7N;GeA@7P%jYr_4{h#5}Ln<7Es@JLG%+C?#39n^(GwGQFUCe%^u( z%h%c2dGQZFj2XlP1Y$T~zS(J|lUmholpk!?$RPpH$p`XOm=GMHWZX?)@%}k(mY#9A z0LBpvaG|`G$3=QK_Q|ika&zzUk~l z3?U&HPJDFp^Yb%vO^bU@?HdW}nJeArqd3^uYX$T3R$Sn_0LC~hOtzMZ3S{yhp>ze; zIMB<`ma3_5po2N_{#hs(=)rwfrdBXyW|Z-t8S>k=ZwiWv!cf5k1R7e}+OUa$Lfe%9p&!4y8JSG(R@ZnYMGN}x8Mut8x01xr--YR~4et3uh z)>9}JpL-0zvFmf0>%w+M?rvqK~O!EMIh1VgbVIOPN{skR9;a^LV{K% zIXNRR}qufVQHmoRegci^rkr=D2$SaE>{!F z2c)QiH1InDtwu+m*Kfk^Bj$&Lk0a?M%mofy(jAih{rlND1r#(i0lPn~Og2q&3UjI8 zY{B3}jDR_v{ox@};gwUpn>Fl>e)qwvJR>mPY?rjP#D4o$=C%W}mhFnh_|81(lPABD zkd`9w$027gI63=>+kn0YJD`MwF)dF*sIMdAlNb4y*k6DCWQHn*cC;sQ&RWuZWdknz2W^*`f%M&m3;R8w}we&(gRR8zyCA4NF5Lc$Td0_c{lQjrvR~+pD zLH~}2CWI0;d0%$;vs1faLjZ<0{}dZI=Q?@?UR!99VP-|jr-`!K|M})H@AJCIaIF7# zcZ@)YtctrR6koAW5`Cx|Gx{I^)uD=si+4EtmO{H8@mB%yGauS5`xEyE_wMO-uMO0l z@0=^DD819rxENl|O#TW(60oa;ybdq01gyx1hli0MDq%-fNhy@PZ)vPp50HCdjSYwU z+0nXHH`a_N`R1r)a!Sf4;IwQOF-G;w>KTzP;Fm8^nUg&Q;gs^`lar9Ntg)ogXlVH~ z&!=Z*Mpd{8DJfx+@d;bVFv0?EuJlpUa_3AR=MESx?bPBG+i!9Jg+31Iylqj#M&0MM zpiU4_DkmEUjw=V(LBC5K-K@=De#-dUYf&Sn$DA{Y%#0(CbMEJO{?h8I9y38eXv0sY ziR9!bpzCd1B2rVEMw&8Y{{D5r!97sgRnX87mgz-DNq+$D0*wj9VIgH@DsY4R_~Dn7 zH0$h30^Sj*ZeTz{$Hb&!kZ!}d36-%FB;C5}t2^5k6NfJR0NWQsCoMsP+?E0p5iG)o z@mfT9f_v)pw;i2%o$dtNmtY>gPm%R9eyGf0_3C6%whFosFhPD+0%zf7#r8~`gO^Xs zEsN??s=@@$%IR)1!@;pJDE^+k=Uvf}k&&Z?zp8=Vj@EV{*~^`4asGmg$dNl>-MB&KvZe!4X$<%Ny{p~Y+4w`z$$6`i>p4^{4}J9W zBhp{%Cnj5-K-U8$I$v`-%IdV6>@aCltzkA^69RS&7!zv94L4vWAmDSt=G5>c1rQ55 zga`)}B@5o)sq%(mU3NZxb=;YS)PV*w zrROu_ES46-R7wEx%pAWY!eC)xnX+oOhhu}X=iu$8zm)hi4Zb>K!HgbTBd{%R&8)&4fgSEULsVq zJi^1D`6V5fhXP6dZ|Mt2`+o-qc@NmZr*v|1GN;F(SFBcFuY)U+0;b#41zjLc(7&J` zNFWC1=cV(8K}l~<{&aJ!-jM0>+z@C8)xyS&Sj%18 zScpPkc+I+}&RK&EVFI3Yux76RGc+$(qh+K`X-N6h(IE~luf^F;Zht>t5Ri--E^PQQ z><=N5z}l067dr5v#faIZC`;_n&bpfs(KqX+mNEeCY@lSIv6;zJ03RUOxHXKpYRlt- zah-rgmn)9bG9@$95B!@lo0S(#;1&ml#TQ6%XzF8nR`zS|Kt099$A2d;|N70F8?d?4 zHe~$2DeOm|zk%gEu37NMh$FG*`YPehu2;htWyTrU?`bk(Nl#Krm!t?zy^bju<>Ob8BEk%!}OwvuSiUfL26ps?(u$BaZWgl2_!ln zaW~0&zPjcxDM&o(AM#kU>T2wI+of8(@C*n)@c`LZA{lU-H8ngl9)`4P2~qW~Y)C?F zrrvdNbyX6)93mpETeF_nzMSajmIRjW*&dwyc~SHn7@%MzjdQ zSR_<5$a1`qH_0!jqNuq0Ct_6fbe#^YtHs4EK0ZEk)|Cf*vwg`sxf7<`2190a zo@DVl|HB2PL?mnUAvw8!W0K&*@P{!{qDTiBhZ)VV((n>K?gw_FFCih6G&DamOdGFH zk7rO~c=YeR%aU6E=kDK(+!q))1EK8UW71{!047?7jD|ve0!KjeqI#JG zB;}D37Y{hL%!j6CH~iVwOf5cK3Py^Gi?xRba`JOiOG? z&cMAr0|IUu&#H_cSQ=%}h>>%*BsOKf#3CXLp0XKtR%0~*Sx;MJm6iEyY~*4h02gAz zIM9I6KR*m}PJYJd$eJ28gj__Bl26;Bby{wsOuKr8l1#K281fn2893P`7&Mpi=#boH zdiqjS;fi5SEWlf7iyd@DB}zud(CqBB9f1UkRhXKs^b{n|KMDBr=PkIvsxST#!g%Ea zZF22<_wJv9%O2$V(82Ys{?tvIYNTDgYsNhc1||TwG=Ep=)m{5a0Zs&&M4r}v%k*R5 zCkHDEH497lI2h`!NWhYP>(&Vy%ob)!#Y*YExlwL!zh%i^0XYmY7U7gmRY&+UF_ai^@kzyTeE+s23~KRsTya>Rvb)S{9SVAZi-55hRd5po zq|yLlpSk{UTRUINd)NetN4pcbBgVFiehl&o}!A+8Dth*U{Tui#z*TDnR{bt0*#KynIcMFT>4}BC88G5GxK~Yp zRA#GuumrDedBwySp(T!}Lr#B~SD=5Q<>cfPt%;DP!jUhq=Y|=HkPzwBy6dK! z3ivG!5FeGp3c)zyfEaIXxIBju0Sbn4eED$g+awr=htSf2KLpaa=201RTogQX5&35> zfB+o)Z`zt7l4=HTg!ZUc0L_FMN+uZEY~epbW(-&jIWhvq@8w&!?JwgYf;&2Unzt=t zQA<)&BLJ`LL3a*Gh(%}N#GyW8!XzP{KCgA7{^<4VPnsNeP1%WHSOV3Ghjb`@gqRju zzRpN$c{$aN-`@UC+lQPm-S&)@1-(F7$X*%)QQsI}@|P9PWQ_Z=*=KdrToiWBmxDtS zi*%qKcahlu#;$?^?xeJknws8CiYum*v$KeA-+)=9&M`_briM`{a`Gg$nVVqNwXA|s z4{>pEE=GWQ^VIW39e`vVAep39M9osHA8!Tz=+o0I_~hp><=NE~Zmtv1z6kEqPh(?a z`;3Xsv?fMI&-G?KY;0}Ov9JuU%J-`bW;84}SG-U!*9WN+fDMItfLWc(i3uJK%ke+O zdhHTYQcF>BmJ20anXc+>U^@UG*&Ju%DZj6XNZt9&c>)@Vi+3W6UYpfus&9Ix$1oj_ zn`)QU@Tit&O#KF`yEu@~^!0&wK%ZtDN;ey(1q zX(i4)+S52fjO%;r4h{*4@JHcW%gc|SQ&P=2R8Ea8(jmuZ*r4q-qXR zY+X!^Q^a)D(-tH$cc49+oBJTOQn*RKZpVW_e42(a>{92Op=UKNTMl=Sc1`W~pT|EU znRkHia_`;=fqn)a>$HjrJmA^Bml`@|V+^&;TfuC6G}xV60I?}5N&!s+W|Qt}6OR3& zYCm947{39^HZZR~dj09sNlfk9ULnn`GP1O;l0<0;FP48VmMisNTzDb1xWU^9Dx_`+#njNXSB0ZF7hb@A#-V$b;r2x!nk2_dIE#Qik!}SHvdk?0N$8Y zIJd?RV6+Ti_DU*oUEQZhHonN?^ezCn-&yv-ZWdaM%6S+CGVT113>(bpbO7OU-erA_ zIoak{ObYt>Qw|N2q`xpZBb6roSmt;JMh>`AG`osZX<&Zb^(Jai>*tK1uLc86yptIE z$>n1!z=y1j@}wu1pT^`84oxzUm$Y%#N_6iGlf5rQ*JihD_FRDG@e-S7DbL4iN(3 z-L-6?!jwgim-ijALWe)OdvNDR-KjAa4Q|&X?$MUcKfNoh(OOiz=eMA(04MbyhvwHB zjQEKoADEszLAfL}sFqhVbWF8(AXznT*H*b6enHpSDKS-UL?P&LB6e{=Ex0o8@6o|f zIVCJ5#ZF0?0qN5Q>fzwB2k3$Dmt+`#U`N7j^DQ?wN^2XYOe-ga-aB(+`bXX$W~Eo? zIs>1hCT6r8O4`gEKb8OVz|JMeDmF4pAIc{h=tw_Q>Xoaid(PZbEAXqC-~kKQM8T#_ zZCw%}3?Uwz_4>c04g5S`rf22hz286p0RgNX1O&2M&CY%zCJ%h5^)^AS{!9bQIARuDEKoF1|M3NCj1qB5p zqvV`}5(NR3Euey^s3?knWXT{Pk|n2?BuO$zPCeHCpWAi2sz2Q7Tity>Txwf&>Xg2` zYpuEFe8L#N$HF3J@emGvE2 zH};1UM~@xj*TWt}$FBlmK8U47uwzdXM4g7typ(={7qM$NAcuU81@c2vb z4P~@tTsQp72%qQg-v&9iKj@+#`H^->tG3nK_j$EGkaBK+?y>rFP}Y z?q|=mdj1UmzJMyy4l0_7>2D22O7VAc1VbK7O6-n@5O+nncGR;ELofbh}THz!*>8R33m zhKat@TCl9oM#y1=-YR!O^U2NdLU?-@NxbedF9oVnKO_!+`sm?k8W+$$F_U6Y7MPdj zzy@tZUul5dSdw_eKmWL>Rp{Ng@tueTRm$ZE<#^x9a&?TWYwr3Vhi?lNUIE&|iK_Df z!rHN4x}87+Aj(-#H;*O1sQdnaQ7ERx{*(8n*T_7juGcd2E-I?_e0MrSmGD0fhq%yP zAz$nX>9(pEygS(+17 zv=E6vhQg`s&gm5Bymz_5u;NGcpEZR})L|2kT4m+F)2wLS8s<~vBjU73RM0ADQ!-h& zEIA@bZ0FO;I=`}WayAc=$5dMEhbQw%|88`$#N5vpJ$~L1f`mye=7;{=P!f^h!?~!g zrB#M@4lH}roML{D9@%pjdQO~dt?x3@hnaxrr(C%bTzngyo0yRgXRb6}c_!w#OOcWe z#HWV(`csOYg^Rl`<9dypP;|vRf-MC#5I%!-q4}0kun`+6-*wr||MZ1+DJJ6YD11W$K-q^pB zvE?{G4C;%>IJpupnxth>6r`qBb_#DgqJfM0_Ul1P-@b?WLLjL`JpDw(>mNTQ$6s=@ zo-8nDAm2_ckr22tJG<+p+mIHKq(bdJmmFK+IF~$e`Er`&&r3+g6%|X_+@DJ5s7)O| zui{JAc&CmdswTSjM_*~fpLE#gW&$`YI|9(tT0K}1&Yh6==Kh~Qf7HmsGQWvif);|b z`)!g=75r$%Jq7Fh{wi0WJP4L5T;b3wTwn=#r&#x}(Q60kIV*PZxW@bSo~v{zf70p@ z%$xjr`=HBh?jjuP^m;v!ZcNzXQw>!ackeDiSGpEBDmbHl67E5}^@l1`bJ}3e2lky#HM)N#x{}RaJ8VdjZtB`;p9X_HEXXO0sS~w1{I1MPCo+9N0=5 zo?!g#x^`iZS|N|wr+dY%Zf7B>LJJ;_#H0*y?Zi%1B_-V=hV+Mz?{=yg88HRFxt-Q- zBcVMcE%u_uOmxECCPr@J1y=JEdduT9l2B!wriHW{_W}Z-0|yQuQgW;<-C1xcy(5xE z0aBUHi3PRgB}cV{PmNPImE&ie{JHB3?VPtPbfGd7A~l@>XW$ zVut_{N4ng$blK>Ri3I_U(|F&szrx3`sDRW^#9+w==j3_3a)4g#=3*%ii_Jwyi8MR- zW5nUOR>!a%bf$fvfuOPqWfvG+7Z)Bd5@yg_x2lnTOE`^7EOvQbuX@DpOZINwWnAoL z6n}?a+}*h=hH34&78*|OT_jE8ShYa4)>)xX=9WSBx!u`au|2$ri53V(^}uA#dKk%^tj$6LG~w>_zC;7TxP6X_pcj zQ!}+7b-=&zLkR`xrt9>*?I@#-NK%xYJedIsi2diD6o!1=x~?31!kI1=n@YJ)NN7t$ zr4pzZ^EC4lCaRLL&3E5@&f5yjSEYp&t4~>3xtpoq=sBnuE6+aI3yVoe+3Bn1oW*HS z0^-aTF>kqmg2KNNgIP5k=MD;Ff$bIJG@1a{nOWQ&caVggMl$jZc&bwXrnmIORB%^NbVH(%|ahALq zSvC0PgCk#j!|ekpA#8fz6mMP()6I8#{NlxVgF1;=EC^Aw7H8DSq;nCdX z>oYPR6X9c4Rc8`?zL54byIVl!W!Tvi%}=Fubv@Gd!3|~yq@)(|+^w+!ghj&j)7sI0 zhIMN<3(LKVfpX|2XIxN@#^*Xj^S}G#rw!TaA}m}BwAYcFpOGWn>`FSGve@#_1Fl0O zrM-N!@ zPZc1iLYLpB54ldx#%x52iHgp`);c{HHD)6jTOH@kA2OL25fl_8(E1=e>N9 z7(ov5I4Vl@s=dg^kNH7i!GGve(&zV#JbhB~&2j9bHK2tXB_>R$hcD=^A}+L6s81%q zR&rg!FCIbi=WKQbHPxq2HPBE-l!Ctfq{9eGXWx}n8Ch9X)zoM~EW+==x>9ku5$&9P z&dN$RDlMMi4Vjv3?d9(6xpSsF76CKLjZID0zNV$+^8}(EcqvW}-Hr3vwqybiHoowm zl!)OAe&2Yfx68^op)vhvQiK}r2%1m_x+J!P{Xk-=yNXza ziOK0(*L(gKwmDLAM%yX3J$h7cmv-~24xu64LbGH;s+ZtZ?zij5*P`Tk56TZ%rl%VR*pJ|6D5uG1B+CSaLYnHB!peyL%_#P?1 z6{A>nT^{G<$!y(9uDvM#C+4o;jU+K$mzjfddij(g%|G#1vNsF39lpgizI%Isk^R@K zNLRY+`Gm&Sme$rZ&tWw%Ef8rwxx|t9o^Kj;N14?wdzty_G!oBwz1Ft2AC(aad99x< zvoC;ZL(q`CH!I||zKzRH2}RQmo2Ft}&?c3h6iDY%K3`Y52jTX|&mRsPJNBv3Q&Up| z7HR?OOa7T}O4vjDX>KDOl3JH;ML8SlhW2kS%U8wQT3e&q+HRo#f&73M|E~1m1qd=V zb!$oaY^aBT`xO)-Z7=w268JqVqqhkO?i^*S^J9nm%EfIex9t^(YLu3Fm(z}nh^Dz& z14{P+8;SZ+hlz6Dp`oGr`Wrn~>zT*nEo(=rs|S)CrMiCqj)D1>{P)lhO6Y&`AFDG_ zq_qcr|9$|qcC}>Pk7)4X$)g8pCd%Jfo9QSoqjNopozTL1YbU-vEMitqkJk~aI;4?2 z7{S3@J8ZVrex2iOIU#g%ODT<95okP-J|1;O$#^&}`F*A%Wr!H_)2C}-`wiDBFHV1y1=;~_hsdwth-1@KB=kcyZ6Q_#60tW#Jp;JUp9^aUwCFF6+0-pc;(VnBujDT z_h`zDqz)W+ou7|nXSKCO8kf+0F-0?8zu(Um;n5Xs?ehL8UA(RaqR*qH2G;@-h{W7K zWOAsblZB{gb>;&*f#YZOZb#dr+a8`b-5B4#d#^9{OkI`>+RcG$*R!nqWI@|pB*l);9tIzf$bNU|XUc2EJpGF8$23z6hrc+{ zSh9pX<$sZx;#Q+=7Hi4NpV8{dd6ff70PHJodo{t2sFPcbfghbvf?on!LbCb6+4JY$fvpNV^6J6_ zfgU5VshdqWrdjupwIuiWKz(-bpg$RT!SnYm7GjWGJ*Y7mMqA^v*6P89#6OFq_48_-%y}!_my3>wsx&pLs-@5|tCv=+ z5pUK!s$c&~0a0QMBCDO^1^2kT7k{JCc8po@Hy zB~RIC;hN^Br~Un>cQZ!d{eh((_*K|i?OlV}-yXS(DQM$NJ|1j37VZYzb@lIzSg2}Mv%ltc5rV`b% z9n?F_JTKoE<|i#Ul5K3j`7eb{g+PQj_qfNQnfLzUvkPb#xh^LV^a=^Dr41Ky^{W zVKM9&>b`ikqD9KR`j_#PL53FLJb)At1>wPgOPXKJTPGup-4htvSBgYke;N^ejE)RF zyFX1wCOvxttE0t2b@Q(fU3K{CZP6b%@AbjT(vnktqn)=pZ%vpml%pzyj-%lei8@B@ zFr3K)vsET8-opd^{Wns2o&MFXV`sJYUUfIAd09FAx)dNN;PqZibUN2}bzMM7Dq7rn zl47xUdVc<(5Z|NV*;LPvFIvY5P;6VfSXs@K( zKa4tmf1$T-Tpis&I%Nv`Fz^*o05ZV}i$@)P>G8ZYFdY7tQ8-~0RcX=uZeQLQ=-(slCJM(m^x*^+6*VFyOG|z?-+L(Z zYkK@;h?OEHpHdV+us&i(~L9aU2l05wMU@{bz z55mJu0koJ1etI;x1aJ6_tb%u9KrxX-;qIAT3p=T5p5z(3(lmZlj*Ije-8NAiNZT$W7 z)UG&g0w@m@>uQS?1Ao*B_Qq|lu@XjkNcHOL+rXg7D;tjUZqR-q^yE|Wx1YIRV5)9Q z;h`Ss3HRMjH&glZ5_<1O3!k;nYT1^gqPXy2uN+Fla4*BJKe(!sCV#XinHz@^ z4?IeFT@h=eb>4k1b0-ac)E753H4z5@l-(kTqv+#`n4@aUoN3wER$WF1um7m69SW(t z4mz-`N{<}B@>}_h-;6K4R#AGrqwLMMjv7CzY2; znoNUlE;`J3whoZfW_4b?b7v>OjL(*qwjDkC^JC)OJ7wxj0PyXXl44hGutUQj@zb&V zQq7gWqvx%xXpwaW2iqY4fc57_Qe?}FCI9QQH*V}PiFo#?UdY3vBk}4n&if&#Qz7ci z&sPEKP&#RR(k=%~5+(J2(hhavZ1yv|q|UUmFi~om)qTP7;z@*!@S21m@hiC_@>Iuj zey?!2X{Ncq`OQLSZFXmptu(kGdnIfh5e;}4e=jppXlOvAi^F$mX5dD8Zmq}! zOvrh1FYFHiv`KC0nL94U3O|c*J$&b`RXNCH=0l(7tQbT&Lf3xxanDrgt#hN@lDEEa z1(t)XZb^W0hpKa)fRODHURGh$&nmyk22~C}T~7PqA2pTpU$)mn-n_r06e`HhtR)`F zD2jqu&0i4f-xhu$=`kW?oxdKo*pbCtI0gP-R*%y$_|Zs?*oILiQd3lv1vzvpELF$# z3hpx>le?Q`WoNgWiuL$yG!0l~a<7~_C#<-g&SWBLj992ouj`_mJ>5@Lcfw;SWsXEb zSxOT4Qt@;thmn-5jnk$C@8z7`SMgp{0%ewp)(NdSy7vGp&J`)j&w813WJPi}pVo4vfad-En>T~)#rjzU5Bl4! zRx@F{)ub#pa$28;cojPkC}#W5CtudmswDg&K$K)`baiESb3uX<4Xqzu-u%+3eu!gT z039H>pz4r1%+kr-E(_$&h$*b_&7C)uH=5q(wGzSIkWhC$P4bQ?`(*aI*tOfEzl@Pl z&8$&{XaN*8;dLEoaR|S=+^Q}DP(w4kbeXg~rP_Ic|KPiKpKs*^GfGOXZ8XSddU0vo z`Vv`Er~mr(THj(Jk7&B*`fGi~z}W$ASyZCsDp0lrzT?=VzUEV-pTG>knbWH&0mef_!{W<)gH z4;)}Z4bj1Yx}kx8?n~C%mE0qOg2Qd8@of|Q&DD^vLJwBn+DaFyQi|dp3`s`D#s)%r zz|TMl);0GkR6dL>PD7D&kZz;h0vRx0@M$Hb13WyhN%BW6_5L7ei;3B5{nVJ7yC>~s z0^tkoXfzWArGYk|RP-r1U(LjX<*s=O{hqzw!MUo)Xw@wh^qx1jt{NO=Qr5?YDKncE zHw(r+%xdxNzo?HQD#=FH~)YG%U zexYY%Y_#IUMczKOg@=a+T(V4#)16{5gHzt?BG!UB<+ZWW0I__yTji$Spngk!QyUbr zv4=^XjOF2`Q0g``brFGsVMS=pD{~poj%y@u>m!%s=BWWEtT4&wSJ$|p!kcQFiUu2t zh^ekZQR^2=8yos?*#&VzN;}qKS=6|a(mya(m7Qvsz4AvAN}r5IpVEqoz}}WSR~&nK zi;H{YebDg%=Vgi<9mQx*4^-%Zjoae;dSC(i@?wQz^ja?rg(=H8c0_)Bpm_Q;0--Tc zBQT}b)|0)55Nv+6XwM~al|ev;@FnGTvE@T{$*+xde_>=$y?hy)K>j~)LS)>MerNmw z?p1r-uoG6}(X@1xfVCwJGrP3(@=6|gusW3+^z%I_;ysojonU{Gt&@3S{m-yiTz&mzu#FsNsdnX+dZn}T;>7}~*QY(r#7W9X1p)<- zBZGn;8qJ&+v?nOLv}opYC^tZ|$0p{m145?{rPrF%X>IcsU*laO0kVniaL?W-g7EFH{+ zpg^yseV)JEo!tsRlpeSgzH#r}`5xoO$Qsg-`<-rs9MqIBrIhph8w)=;`+zhi z?7iUu@MU1xUHy%WR{ADwjw3vB_dZ^IfB9OIFnP_$W8Rs$r^DG(?8+K-^OqX@QEZR@ zlKHqkWgncdtok8(>KadF-JSF^v}YFSZr8u!8FA1{7Ms{7C>RwSJlmqThtnkO!Qa0H zq|tW>$POL^7wmV|rzGH#RSgV&NrrX0~WsW~)3G*L}9*@OX1; z0F4o>y~pa0V`GE;r!6k;eu#^oE+GZ(;Z{hv$My}gMN8{Zgb?dljBY`(~J&O z10{zCnnoS{0{on^53lLDjP%8&7*}bcpZ^1KjRF+!DC5A~fRBbua+v8<{mQe?P-DJ- zPl-+BjM)yP7p$HZ1rU4~WWZj4I&xEMv!ix&mI(TWMMXYp?#H|tVafrNGa>VRq)*Y7 z-Hf{dRmuIZx|E4}oSoeqI4VkzK*zs1bN1{^%LhrQ-)oGF<6eFKOpT&de}DfOxe2m0 z3zQcnA4e!ED<8x+dHZhR5B;?61r117hlj6fY51eIEFcyMMvJ{}&!C<9tt&rKp0SbS zJaF*vV-RLzf3Dub;cItPJ`Np5Nb4W$+zH@KtSx6&g>Th_N^-N)Qmg}71h>AZ`Tow_ zpB;6jF7!=_9fUDwD^p2qUT(qAFyh6-uE}5Q;>=wyZY;`QW;(JX57J0Au1s$L#zy6u(^nl>!+ zq-$5=@0>L>H0VFj^)D@e2e%hs<-jlX!-+{x*2P_S;5{Zi+bX~Uj@XmB8M)V%nmO># z!W_*-LlcUsn22+2Oo-j_juu`1;i|g2@TMkvDn+R@(7w;TVdL3l<`mf%Sh;WF z3)`trbi>|VpKQ&&;`pnv|JL%#3iMr%sC0WU_XVfA?)1v=vE#@8NlB@v($xbpJ(~WK zL5csR0d@ukFOp+GI{U?_d#ReACcdgRzGJeu73}6v1pd$Z3eBrmuY97x0gJ*C0v{H7 zb!_`(6%|>Hf3G4rW@J2nYq)WLqwvsp$?h{Kiy_OWP0nf_ z@S%DUc9uX(^l%cykIcW+&3_zvo{g>%6F=mZe_u7rmuH8#UmU0DO>Qm+1?@3e^q3u_ zWPh@S*-rN=5D!Im_#y{Q0M%sW+{KLfJ(C}4ZtzpEc@M>oPFehw>UM1|K(o&JX5HG@ zF@j^!a1qtpJtyt9q5F!^*26T|KVjid8L~w*fKghuU3Z*2dm%ep(L{hHzPJdSe!_7N zKLdkXfc~CC5u+TAI-7dWCA?&yxc^>O4%#}%EF01y=|JcRD1}3J@UOKwK7o~u`Lep! zS^G2!Z*O_{&jeglsEJDzykc~XxaQLy2}*u}rOVB2aP{#B9FaZtlHjTkUlMp^zyHxW zNB{Fc5z^+q-pk)s_Y$lP+gbtAV&?w;4qxIuMh3p?s|G%}6%!Mk7MBT_L%%tY!|{n` z>FlDm_grcQi=55&7k3;)4%IFwj9_aYoRG;pvXbl@hP1ga$}5e&KAVKpYD6HezZ z5u>8|rB7*S+!7Gi{6xS?!jpIUDmXV}PK}NEswJan543$)1+T0m@i3;gq7Bh7XZ!e3Vkf0x*6>z+cuY6RBSJx)BQ?)JRF zN&s}w15>%y95-}`3cuk~+0jxB;yX>gMOjcGxv_7+u%xGlHD2Aj!5Qyh$Qy8Mw9W2* z?X}BQALJs&$Bc;K-DtiGmmwI|4l)hYnrJ$ilS|t+SL;T8? zP)%R8gwosfS${t-i7muqc+n~mX-nOlBs2PuNW24{9b0KpIW2xwcp%_?IsqxNL?7L4 zebXU|cz#Q?LhSQB=ja%S#TXMON zSo{C*2L9hobAyU@(5~;lpXR2i`NVU7AXGX~pw<6hrn&vw82u8~`wG+C$m{>xG&c}& z;6!DJqrYT#<(I24V{{+pn3m#H-)x7;pTP7DO{Vk&indXV~u?6>_l_O=g$_j+;SJ}>}DyZI`3wr zLi4gz=N$oKmA|pZE(z5oZ2ChkCWp`^G^N=K7LDu7yC@gaQ38sG3}o!9 ze!{19OH!V{{MroWhh2LV`8+r|**dDqU50C6yaodHbxvq{Oq+hFFD_WRjy()p?-B)xPsU3 zW1<@X8{YB>YOw~rMP$)6rq(-&ZS{Lu#zK{2-onZlvb@=0dD(MQ>7r&6#xqOa8&w4l zsWJ40?S#jT-06WADV?0Zhgk``ihy7tb$Ep*sS{mS*-NtP$6;Km&g-|E%vrTEFf{xo zQ(jcG#lX|ldu?}zH!%l3?1=rDKvb$&|OCq1uc^&F-OKPP*n)f9;XoeYG7%COZ!Ib zt~Q@M3EbWe=ppUxMF4D-8JjdTc=I4Q_}l+E)(v0fM!p*jVUGqUf5dUcY3kB*p_epc zt!dVqUc%@WeV^9v_WYrX)-H%QPo6!i%$>>y!ePWXV8st93K-+J(CL*tEFb6i? z3s0saK5KdVF_<1*5<0o$EcWp56i?`yo0;u9EY6Au7j48kE}IWWp8>*6P1(=bFW5n^ zhimN5eGhUl41)huaCz_bZ7BM7@>AWXn~+4{AZ2302*KrHL+{}*X;xwy4Tf=@1zgjc z0Xd(J7cos|v&7)%&!1?sqh)rS1sYuraVvx2S{=-E+s>68w>q_c?>{r$jIyMc;2^i? zNhQaxxhUR!=GLwMn&}2+FR`KtGwt%w3u<?aXUKe1!#T-s zqqQU|ezR8U`vEzDNJ;xIvy<{CPd){fdpq%)wV#p-5#ra<$A#CDK)ClzRRyxLriEb&*)|4Ospzj0C38-UO^087n7J@6A$kZB)h)6knR)J zm7dK$b%J_I)cD^hJHJfjTOI>)-pgx4l9Rj$9!U&7>(PqhmU7qu!p6S+`?qevN(I`X zaH)(Jz?!+0G3(w1LNs(3-ATA>%_j1n!!!Ga53-AhmoG_Hxftk(Bxm?-!(D(9Fnf{X zlKi^p-LVckfJ}JLUP~q1W4FMZ7fHY>l#9mq{U6>zDr;;jCLGrJz=13#N-OAsKrqv@SU@|b>wi5$=Fy7(q z!XL+SLkO&J9hU);g%0ZM&IhKkXZa5v%xTo;Qt`cq(m_n;*yDuRWu?_xX&*i%4Prs3;tb#-+m>S1=j&s>Pz>%Qr=F7%?Nd&QMV5(++A zeJZwvEjZP&Q%uZ~bOD^!z(q_9#af>T_LPhfu;!x)G*uCU5+E4lAdgCZRa*JWtCuVEx*-}S?On`qBFV7u=gmD7dE%kdDVhd_0^yJCovH)LMe6ufNUaUSH#qNx>i+zf_Vil&@Jj-aLo}O*?@MI;B@H$=8783;@Zw+(mwtV`XJ!*lS(%(mE$_ux<$s4o=ZNKAlm|b5wY1 zc<$H{63Bg6+XT=#uIJC6!=xPNE~jHO^E6O{-b;Atg=WNELWz0y z>;qGNnM3%^LA4(f&!JG-nK{*q=WVq3QWnQ&s#)LZFZm`U$@yqewoqI%O>TFwcbv-; zY#g&~^4!C<6wX4%vj-62N&6ju;`G#TMjGTpDcVU}P_smDH9sRq%~X_KdZJw&kZCYg zjB!+@xST*gf|wzOQyMgqbLY=%&HgdZTihTTjL#%&L6M3?dlV8O;8NQr#DEi?o}W{; zt|u7YM9Ai~(HKY1!ZOqy1z#$RM1&0XZO z#?#qZVHQEJ)GBj#IAld!>R&naJtUI12nTcSGBB8+@d~k(hyk4|-96blPOfX!*WJuw zDe)c8G3k(SxeY&X2(Tx*0+ugzftQt;xqXxd+CTDT3X1X0EJBG>3qvE?t+*o5U}AJI zL`bJ#Jk57s94c6yEW?YNq4#C*+3-taYZXfSFW;WP4gnMVVg7$dx4M4SP9l&Px=_pG z&Jo@97gl+X&6A&jf&J&}zNJ=BzTaPp3#b}2s(jK4Jm&AX@Xe65wXIgU^DGdG=2zhy z!M}DS6r-Jq$;rbAVYgsjARw|1dnVs(A*ZD6{n?%-^*QceCgS8N^<#frdX0n2;M!)uC%<#s}rt z76N0dIDW(MVY7t3euJr5Ap9ujE8jPr+_4gaVElNhVfx_c$v1cfvf#tjA zu}+M}YyF}_3@;85%&-2wa{zI1$)KIBj7S91g-8EHpTVFTKyEOlVCDlC2E_0se>j!U zY}<}OUr(RkhfS~XJwN1hyO}v_# z8t1rtb(w->^%hT`7yyBOyOfTmh6d)bRY4bHhg=HLYyH?fD%T>o!6vmamH1Na*>^+) z3;qhTtuCmqE)}AGF_?JF6m$tyViN+O?-7sU(rlGrd#2}h%*Cnb5G*_Z&nmp*b|BbI z)}ALarN4X7`s#A8>qfI^An}t4`1Cc&-g7t4oj)gF=1w?i>%86mL1Uk=_1+_2iHP~9 z$qIs!(r)J-|FU1zusHz0Mg}q>hJ1deExlD}RrfL<2k~}S+zoM0XiVS*w z^dX^~?rV8reF)#={d<$GYwPF=V050(R-}p_g3e$_rUJ&cS>nkOeI{Z99QnXEwGb=7 zisYee(5|7rt>{m)Il7fG*t`O}YoqkMDnCXf;PznVCoNF;CNPq;e3q5~Z4uvph^}z8 zdo#_swa+$YxT9$5nO4ge&1$o=D$fkUBu{J`x7lkUap(D=n(K1@jqfKUWrvx5iB<0n zY=89q0h{rm%E;p;0mjkKrox}LJfVo>?413!IQ6YdCAajv$YS2q9PRu~pP-2OxD5YylALWDxP4sUQ36(qEZC&&eEWEKY)| z?jNtLjBnqFsmmCo&3NcS5o8cy5inVm#5#A8z32|8e)#bA4W2s(i0RWop`m5#i`^k; zy5y~HY@c5BxRGbZ0Qpdg?Z6?ATqn)|y1L@~=LO4n={D&l+uMW2_6lF$t^B>btmL-P ztN79Y=401St7|N+d+~jJsFUm5lTB5?!k6{*!tdYb zrB`N%s(vBgBq-0o(4Zy$n=J4RpM>^}ASh88Aomw~vC8#4>DznCLFU0}xBTq2uT?2l z>uw(F^XzMds@(p?^|@RD`*uVFo4PR&C6B7Hf|?U8N-?h*=PjOqv_+&@e$Pr%EIXaR za-d-e$!VPh5hfKw5^z>?Dpv$wb{OBo!hb`eDKI=Ji0|#0Cw|j5R9p7(^1>hIENG;> zQ?Bst{9VYsi>4L_uw#}7G4Q%=13hcI9_HH+kK-(_LIPC`xtS<=+%Ft#$8V)-G0XsjT)@YNhz*SNZvzSYtsUjS5W4 zMB9Vgdu=BG0c2YMmRjl$Va9{QxRer1@V0%MLLcX~z-ChS(qGcvWbtUh7hIz=)OUTU zJrvyL78WI~<^{#T5vMv-J|()gO6sRoOpir!x=Mp?y;Vm;%<6k_sb_1C5L;-pxU&FO zvt*Ms-TwV=ri{*z(`sm>+7GMAY}6f2!3bKHe3BP=7{-IY`tpSfk$yJ%)wUSl^v}~M z!_I|?XnAX^xVZy|4(yxpla}{n*v7z;M)CFYtL*GOIfg2WdkT_Z@FK^x9#V_;2|1*; z_opJ9PE4~7Qv43ze)RUhn1c3cQj+D5ni^$j7!59caKz~5`^w@cpS-`WP`?rMCaAjl zLYLDR|I%2`Mp&Kv=GJFyquH~;$K3pQb(2{!{**#)bIl%&NsrBYtF|)_M}0{N^J6Uo z1DE76?2vb6K-Jq1=%@6|%+pNDFk5U}V7yxx|BX6*9XeOS?c*E%^q+tFe>{#yT3wcY zr_rYID~Ee^nr)-Wl7}3YG4LR&>o4h)m6Y~NNBE#pa46J*IZ{$F@`lI$Sw&Gh-kjhi!O&X%(CV;=S0Q&Y=B5*}UI0@1 zNbZxTU0jwfb4lT;QK@0LM;J3FywfFr?_AYCv1p}!q)Hfsp?b;R9(qXe^RsB9wxC z4n16Id+v(oc%z!A&ldG9uFgPl(TAyVs^|zqW_1rUXsEco{_2O@cIKky)@!EE!=zD` zmt&8cX)_lpZZMzN+=ltyv|LgTZ4K>Lb$W^w1E(tw#{o{pfT^Q?&3DTJLOB|+Z{M`a z*;yJmypQJunU+>*iu07*=Nmb@3@e5xy~mZ+{d%S<`qn&Po7giqTtjr-wC==U@2A!V z{tD^ku6%`X+}2{ImRyYly~u_J62phW*R65s=I5O6@7pIS$wnu;v~Syv8>w~&Ut+X8 z@I}>e;;eBV3r9$vi=I$f^#>pOQdCN}4j{sj!yx;1}b1e1{oeeThY{ zd3#1o@grll2~JK(kZf{dTo$_*Q|y|LBD3NV6JzmS8Q@OA5HP;4UBhdEb5tpC`(S_o z=4Vi0Xu2#_G9R|coRbI0Dtfp!w_~U+FNk9Xzuul*YUJitgw9a&^R0Gs?YO$YWPQTFfyk)aD5_!5KEzY^+?jU{bL zBL49+PtKvlQmzQglL4rl-^QT69Ydbqou8@6GAH(jy!G1LJ#sSJ4@KPJqvgi8qDaWj z-Zs{fq>A^QMNs$Q_~+whD||x}i_7)Deo^P^(wS#F*7qs0o_!+Z;X>=RRPRf#L>A|{ zAi1%e?A-_1l%oB45%`dhQ?~XMZ|6SMlA{0UZE;U^TY=$r)Mw#j3mG3jj?EQ#()CsH zGWo{FE0sgH~Hf_pbLGKkwF7JBgpAxPtl71d?R%$#tD9Z8zNBhO=yTI-K?IXMRp5>zMhseJayX;eBKdP)x2|Dl!>)1< z9_!zg5%l^Kxvn3IZx<=EUi^+Yi#U;Rpz;%sCAsx;afjm=Qpv5ywd@XJrsT}>aNI!T zizv7ub|!cjVEa*$ZJ)RovN$2_MBw2npB#JisLfgZQgUlZ$G2~%iu7gk1-PFa;E|T* zfX`CAqoX6$s#^-eb@DANg4%uvs#)8$v=W-Gq$MWqLM#APJGOtd6eoam`L%D}1V8tL zlTXWgPfo0jxy{=UkKS+^ps|q{Un<;OrCRgyPWcj$$Tj6A?6oFAoN%lC2r z8mh{YhLY1*2$ifl+ki|k{Lnrr{HxIxcq+w(s%Q~Y+6(%JK$X?UTZ44B80UDPVBy+s zX{XfQ zylj!u5nx5(Gb~Eiivkq{*_StStvF?L1-Y^}bulUTUEP}j&-Pqv`7mbI(%Yi9|1^Cp zo_l&pew7+~Hx%_hI7LkAJ4a^RQZYV&@SS91?)$6O(Z?}Q?h8fiY}YDXU@ zE8OJ#k~^aU(r_Q0_X^|2aN#Bu5QX&H$Y=w}@h+D(#QS645dLca6}2R{<0y1*pYp*g zc*}EMBl}O{yHmqwucMHR^0L8z0X+lTJq#jdRt;w{sCj-8Q-SH;Uwk3O6!?e;s$jzd zT0C#L<)#a|M18jIHtohCwws=dI8;B@VFoiDG42s?``=+~zSY$a6_U-I*TD*~HcfX4ewbY>djDHAIM0JHl5_Hd+`)cO>pkpi1kv)C|9 z9Ymv7Z{IRS>ALO21a6*SV04eo^q&_5n)Q}j<-v89t5eDp<6_bta@_b9+Z0ZrR9SFq zSvkBCq@+~SI$b8Fy`y^{zc<~JHXguXQ@OTG%!$RQv8gxj-cg``M(nO~vO<4$mD23r z|LqYDyN1?E1?R8h6_NZmXGrc-@kvQE&OMHY5wbN8>%iM@v{oVLbq{Rn6Bb?BJ2%%0 zHv{!HD+C!>IYP*{)4lHf!GOXPF--Z)#hvMs*j?(!9MN|N(UT2sbA4fU_v5XAV#1w@ z$*$4|&Yr$vup2T?7XDj4unKYB-*>yBR)%sfwu}UHS#S&YO1RM1$;|D=9V1$O%X7nh zecW<>eUg&M7i^?-E?=YAx^2hS-nnJl^l840wc$;FzTlwHkg^mj-ls7?hUbb$?T82k zxX zmzP=5!2VrZ@m$>9w`*%_U%q*Ldw6R|(2t;A(Zi%YyM=7Bb}P^m!}NOA7sRv2!lwHC zS@p_Uk7>S&k0*y|x=3$xL1OaT9PNRG1B;yo0a%Uy#6%UEKr~L|bb)hom>PZIxXw2w z+tgG~wNG&6FB^xbUvr?g#?&?I!ey3^l}B9us)hqU&JOxH>@cN3kkYqkh3a4*CUv1y z9&ssU=!G@$XQi7(fl0_pL9CUKiTkPz~btEz^x&yMJ1Xaz=Z>BUWA+SR>zi-D@l=g+ZNis?_m-pD$ zk@8(IBqP;n-7P&c{q&Oq=!@a}Hu(OdFMII)ixcKUWwP2rSU}Y;q**b)uYx#N3IrQot9Z86SnawDLcN7 zPub_Z;VZEa8|X0qqM)XxHa!FC8{K>#{H43je*Fw^mV%EDR$G6Ed2%sLt4_$s$Vjg! zO9)#Me-c>3<0q^Q47S310tEc$+s?l>(zBdzFHXwR@7sq16}F&g5qB|A(H@MCeK-ylM|1^6RM7!7o&%^=IhFjxUL+NN^HEtz{ZBrvO=1yhf$hP zL~U$({<^u)aq~Uk9Cv&gO9Xvvg@&WP<5$Okt#5;mID!yZMev1?0s_i6jcu0E&`Q z_ZAYQTpvDt0*xOL1#Qca8T=)%8Z`X+g)bTY`?t|Uju)NQ3UiqYP8Mw`43O^MNH^nw zxl(*&eRO1`2$dDct4~<|x%P6Ho#3HX~y5l?cSMN zvoo%)j<`o=K|Al$XIH*%#=Bc&uv2l^xf_8$v+xGRt^z>6Xzjmri}d7B08kFV6{T~KM1RZ{<5iIGnY^j9^QzaB3gbtvw$ zDK@b`{RHiE){2RRSMlAn>P!rb96NEz3iQQSx8O0qe#f`#f*g5Aheb+fEY8*)A{>ev zQtO{pMY+Q1bi?V~e7Pun$)0?d7RM1fOs+tWnVnc$xFvl~?!Akvb#1fc{EBOsXxVLL zLp`sH3w+EMOneX>9r-pgr$9#5({#W7{jy&8rm>p#3e18$2o5fTNp~Q$Y57G`OzPmb zZZ5vyT@h#Hn{vvHEJI@9#Usw2kkp zol1z0v;RTX({Z{)_OE_@Ip`F_^lQ>{kra7TZ)Vmabu8JHEL%$NEZ1GY-!ZWZJnFF^ zUBT}se*S-0L#UZt5P_7tML7`#MQVN%_nZrdyefqe{lc z6t@Hp|7sM$_-fCeV+a3p!S{4*2#okCSJxv(S}NMwQYa4ItF?j1vcxK@bZq`*%h&Vg zf-!DZu2&SBw3ry}ojaqaOp74u=(CgL3Er)wzUL3}79}OoeQ|Zbc4Hsiv14nU4k8KB zTbIdHuaD70RZ%3VP?4D~YzGlxd+v+Bs`T6Z0^RR>Lw&KdE_dchQ@gDH;hp#lhmFLK zAB*EQ7X^_S7N5MX7YbQLAWwxPa@U7YTfF&}$_!zZ}Bbm>FPRhM(rH? z-_?Y78O+Mc6_>Tt$q2XTUt`c zT7reQEVX|(w%q-~!dr=g%#jne^>ex1Ru};XW*3uM6r-)J?H)ElNDd@!Z6rm47@gj*FiK5RIm_J@jyF{IqH{QpVwu9_&Ry~{$qGt9~i z_CwX>+}x`=$-~HLA`9mPkcBf61<=U#%S_Ddp_Bk^vDW473EvpeM+1L87nc4MxB2Tt z{{jsMp!R&5j}`jp_N+#KN(@F9urxIV>d|j}Nj%!V>w?`&B4^g4BRjYD4qm-~2X zMeWco=(fA81TP#a)wo#5(KMub{q`<5t34Gs6g!f0`eN=(K^)=HTA=3MpD9Jj?y zZPblYI~*G$L}KW%E(OLvk?DNr-dySd z%b^&RSbZt~+*MCNK=eG}QB ze}8CgK<`pN*03TVL=b7^=?YHfFHY)`bD)I838~Yn{KkX4yx3`1&*EY15~ycjXp0}} zr?>1z57x&_yHy6?x9oK8oeJM1M^%(AfVUz%hZSF734<5`aeY`*GvQbba{}VynQ(8p zdjuR~cr2d=U=CFb;v*7&a}IoR0p5|dIeeM=*NU-I?L}h(Rl@i}?Ck6q@~(m`4rt<} z3Nb`KFgc-SGa%@_E=|iNu>?%gVKNhde#^WZUwAnm?gB zj#=o`xlU8WAd?XJ^}bXtR&V#k$9 z&7kL*L0Kvs*oUd<6En}h0P{xl-BDg;U}N^roy_L6AFfqE5e?{{%s=XIXP@j9L} z>d^5iZ>PeY3_g@2GrL$FD>`v9oJD^S&9!l5o8hL7a z!s`Vaau^!-?5&L!R$bCe=pvnMm4%=GIw3lS?E?h({({N{J{-t0^Q#2`6B1I35GXlS zH~Sb59W$8dSRoXKUYqNz@MzNE4T0ur1k9ibaRaV%3~D8*-X8{w+wa5F@~*z$v%l4b zA1#=n2>a$o+htT#(Fk+~Z$gfYi6OE5(swSe#p*?Xo|fq)C0v24s~R#e*}rb0t*CnY zEEXaysJX5a7Y9p=Y5qRtyE>NWgPQI%bG9aRPJW(vad*G#4!q6Fu|jGLP6_#}E;`E(nz#K~JpiMLYv#!?5j zBzT|M^nU7^W|-21fqQJ0nj}0|dhGDfOYa1mZOzw*SW3xYfJ$2UI5nf{wJ*ZG!j#S3V8E4Gn<>Z2^5W5$}Mh4M|Qb(fS3!11C&P2y0N9>WbKTkDd&xEt}r1wrF$S%Bt~aCl2yh2_U@Hd+k}U ziRnS$lLiwwu5-^XkBn)o(__>OUl7YP@!Q;3ebiw$Gbi7YQJpDB%OW(+?xLWu??Q9u zRY+`f4qg-wSbm;%p)>DZ{p|GWR5dN8w#~J%Eog*m}X zFwLkx9cL^@?~Pe#UcD-n*XaVjkQI(%w0{Psk!F|IiJp#^R|m;4YT@aqhu|VK?9=y; z_xEoBgSa%@knjkC4dK9*txjbdrmx*|BaJMK;t>O*#YT0$#BCQyhk>D4fI>vr0xB8; zR?})Ddq?Zh7SSs z+!SQdF)=e3R&1anl;Mjb)^$YI7NOEiL21#SvZDOot-UXuY01FZP--j(CeSqOj%kc_ zD5zmT@bl7W;QCfT=U`O1DB}ZdM1@_D40!tVZOl`du6Er!k<+la`zNZO;IbRQJ2n%=tG!FqoF(nmo?1otDF5u0?lOAIx{xWaBbKR1reiq;O%VN|)ru?4#{ zgIg#60gpT?k4V9pYu8{W(Y|UfhAj%ld{M+?>X`xIzGdjm*tM z`-*L3q?LFe2+b~dO79Wrbj7J2o>&5X%pT9K3rHIodsdxExzCIiNY1Jm>I|*(yQ|cK~N~UK4>k+ zeNgF8N>^W0=inIe{gtMd**K!hh(MLSXuisPG-M|uLK%e95kgz3$cns%wPOVz^o~9U zD{}4o_hg&zImSERYG7ifK&hdqy!_}A$@Y2uhrvQ+J}+5w-`Q8gE7#V{YEP z**)sNi-7t^WS28yih$U=xw$>cdA6I^g;Q`3aaxg+lXq!8rZoJ1k2LC&zZZ7bZfeTV zkqVwZr3cp?28hGNg!{**)GbU(6&Ei*w&`J38nQgDPHm=7TVr0yQrhRY<#UI9|8GrS zvjb4vLTZAZ&Rz$#*&m;RcShGwz55Md*kyd$8nJ+LL3wtzWz@XGNN=Dd4@=ZU3I|TAOq4A8N%|a57`8K3H!{_Pa0hsqOu@cO_Zxd(eur#QpnUPx%O^ zFb33WbermCwWW6M55nfxG#YG}#b5OBQ*Gr<7G)gzuU=)w_)JEomr0K=e-jm!zH4-A zo@YE0pLcvaX>9zWv^2e1?X&J10tlVzp>7;w>@#2S)X>_#ZN)P^2OUb35{u`~^`M_P zYwp0~orlM$J?X&%BqquGWqV#ZRDNC>={S6l`Y?fDgb=0a0vMQ`JC!Lgkl($_d-R^_ zeqI*>Gr?rZfO2x0A1R%xw*LuUv5V@WY9xDzx?M*N8!l1zHf2BQMRI>-Pq1oPxsV#~tpqLCei(NTwyvyX@L= zlsBRDPHpJ>==WAIpm)AB%} zf|@pG_JTaEPurO0#lTNIw+P1z`Pk{MA}C#9x6P?`(&W6VjZdNGJJS4x)p%8C+iBl6 z_tGbfEoFOP6d@kzXP1;N{Qh-LP5Le3>;E3EAmklMj0}v5h@ip2-+bEX&tz$T^zFyu zYk$tvYF#a5%!2IDrt5c|ZqJ(c>@+dC6j|R>=0fQ*+8~*!1V-P8s#JKY?E@~{(=(!t z>8`Tl2(gS`9b3Kj{@c5#8^6`Ahg%djRe8J4Y|sj4;ADiRo%$f1+zyC7uVK1@X+j_) zW93}nX{+@=^rYj555KPRem?A00i8-Osmg?nBDmn)MTUW;Q@1r|PU{*W;VB%i7t%rq zjj%&4l9BI7g6=UbA!JR=&f#i9@yLh}x&9;SNPd9j^)ATQfLJgpqucgKf%Ma-yUJ!i z9T;+}kWrOjESn(8} zk%iFG(H}qJwe0N=r}Yv<{mh>|g*5gd0xDQ>V|R#gOO|m04%2sZQ~;&Eno^rv5Gih; zW}3s#OcTp1OR+`bC=%H$Bi_EH;}nv5t%-%4Nsp5A9Jz{FYbq~;Z)Hvy$y~ZM^w$16H{Nj_dKR#A%nO$n6=Ub`KAZ4 zA1~}r?IHzdPzCtS`!t-JJpR+8QnYzPNFz@uL8AL8ho;Q*_`ub0Bp_d;Ko|;EHh^`p zA3j*qu?nA`Ic983Y`Y^PHf&TPlE2!e&+a{&>M0G>FCAXOf zG$P+|M%+lH-bB0V2_vImMTeU3daKuB8f*14jKcFv8wF;yVI+Jdc;4j(V>6zjMm>;jZ%dG3Z|0_Mr|HhV%n z*tGX%#Uu%Zu)!E9fOruSF+jE0m?_BX%C1Ev#OVMS+}YDMz%b9G?DB@>aAO>Y@KzuiFl@O8O7c6K`l2O^G!QLF}-70v*xB5w+2()veTHeQDNBE~K* zR;Iz8wDc#pP?U}W?8(gszEw_#^)Ay0t<*0j-oGU`PQDYT4J1NEBBq(Bpq$!R=hg6V zl*#@D1~%E!KW<^cgRHJm{t2sn`PeO!T--aFm|XrU16CgC#22_^LVboktx79^LsZZO z`;B$Ov-A!Vs{1!~VE?^pHJ&kQb29B?P{U;s)O+;-*?`3{3j)%w;NoOfXdykM*uvf# zeuRw*GcE1z-%9(CJ47`xaSwJX%8eaQbE09M`&1dQ`&QB^U&SjMo?Vd8cX#tjo^_!o zHt=tc#mC)zAOyw`H(btR@j716JF3XE*lmL2!viH+JcD)^NF(ANK#8@NQ1gSZz;)9- zT*|0SrT_G7FuPd9DBz@q=@bF1p@<}=a_IC6txBz&o-B1pynjCgqI))h_wvqT(yHro z&v8=0xupK1k`(a_TQjrjqfZFFg4hy=F0>4>Nq= zmPf5LA%>k&^Gtu1!?(A3jREzPAsC2~>#bx&BqV6xy$h{% z?fd#QT4~USf1JtniQzo~zfTj_X(}htK~Qfsen8}loj44A!1Z5^EY=q_x`IZTHG@jeGy!xorAGHfAle6+!f2Ck?!LFW_f-vh7ilEOpd7<3V zW@bc}j#Wd5F2z1ZFrkTfKtL(QG%{rtPqk!;(DB~6cLSuGm#UMpK)e<2-G3CBjwDM4 zqyD=yzZcse#23-y<90Y66AN@`f_jL!0eW8LxKm{8W1GK+puVLqVmcu5G>D(ATPF}C z3KvsE+xMTX>rZ2PC-oriOp%sQ721o9p~$!fOO~l8R2ahn(OFq{jq{;J z32A9=;9$&-f`U7huT$-i)H3+`O?G~wpt+8D$EfRyZW9}~obGn#n@z^K@PJ&ZyL zJPtL>G)Pi?9Xrtm7=dacjNr-Gm)>pqzR-WZHrmU@sB0Xmbo9dy87NE_R9I}03`tQ? zyJ33%rI%4rV{>+*7-HSy@7&=APhsX%Lyu2K@Wt1<*=w@0<95zXgs=@XjnOHyUa-}2 z_gvbJHM@{LDp8y1Uc7wKs{L(ZQO_^v^_;IEhY|s?WV_xOKn_i0ItGUZzls6AMd8`+ zO}kBul7}3QKwhN)fz#Pu>+?$2z~|*H|1gVvCrCL-e{A*C$y?V3s~4?0Kg9T^Zf+MB z*PK*+GwygSF(oBz$%$H|!s9Q!_I%X!0yGI_w~S9Cj-o8t-iU0Q$ZMy~qBmxM2ur0`;jnoK&Z~+8$#4Z1%i| zDW*|w$kY4GiJYAbzxpZS>2kTCQrapW7gjo83Q3@O4Dk}}nIrN1H4KL#nxe`}^mjQ) z8mmWY5wiuJm+5I2%oLPPI$bK4&P5=&Lw*Q;&({a0sw2OAd8S#N{?+t_LaB({ZVr5? zQt-b02++A*FUJ>m_kiubgGR3rEgD}{We)#?Fj^6Zrv%=gHR1%8Cg?fntzYzca`_-3 zFo+a`NeR(}p$z-0ESwWuh~HudQ>9hDfzxq?;Tlw$hALMVbGrH?lLVuB9JTcHF4vOl zTQ-K0LA68J4H0X0asPC>tBRmYIiBZNpS#Zr{|d}*^DkvpC1g8TSrMG}>1L+TKmU9u z`@HAdxc!6?iV}1a@w5eF6o1$~C#o*Zg6hG^*^GeM&V1Zrz?r%%1FsfV{sI^U9*PRI zI(3I)NwBra`t^h-f1pr>Yk`oTCwOl+AZ$&tHY;udYS%eol&D`ZL+8%UUCaukPai(i zec66yK_&Ru@#77IX5Mqk0@($bFJ4HxWG#g47)_S4=(8=8y^Y>6*-L*O3;A!_*w_&0 zp24k05VT{s{A`z?;Vi>@GfB{{6U`IgXrkU9R081*2@3MxTHFJw?d-c`kp7o9`(q>h34qXMvl5O0%XRrRLQ}P_gcSi3= zd2;MWL01dT={NQq25h*F=43UhCXB?zX=eqaCxz~p3 zw6Jf}0#wvKeE51A>58M%o8gA#`U;`(s%uYoG7t;u8OuT2J-PMce8KdoY2iukSAN>Z zVPRDja-O~hr2WVfW(S4|7vo9s_-}`jOs@05zNg@G)(=2@_L!q>Iztb zImntE?t4}H*V?8?^Tcy;jk*M=t3Dhh@u--^06Eyw17Y-Q>rjWoZc4R-5x>9Zl;3b1_D;)!K~dk+CX$zN!`>;v!H|u#5T=PUPXn% z{OpIzjIjR}@NFR8f6+vH5W zz8iD#S0q{@CVJjGt1LGc)rzWsP=Srn0egk`w%alIPWje*6H&3_{gb(*-&cVC#S89< z=y%e0b)5d8?$ypFU^w z##Pn0G0QTMWr#m~v7m*K;UV@z0#9nSdwoo4ASk!M^oz~amI;cuknU$X$eHbQ_^i*Eb1!-8+2mW{vO7oq23pGL5|6UrHJaklbp8I6R zP7g*Y&g^KFp*iygbH)%de_J(CT_Yl~3RxI?x{JkFL5U<=*6`ZAiPge*$r8B*XU(%E zK8iKVm!;s;L+?sK`O0p(5HQ;V5-d3pL}5YM}s9FaVK2)8p# z7dU8C=TN3)7Z;NwHnaZP`^uj-7j4Iwmh=#%LHQ)toPI=8=j4?VQ&fxqNb2n5=4FZ) zO4dDmcsFr3Ct-qRzYY#Xhm9hij2|-*P|cn`6~UPq`myg|3biv^=YHCM{x`QWtclbJ z>b!eQp?}JLpTfRZlh@}Nl)rpg5&oiv#2!emF-B^`aRNhx%CDDJ*Jd~Lwvq+898un; z@*u;L>ximR;QCzT$wkJ0Yw$|i)VPixJ^HJZnt(2_rM!RN9IBI2(I@St(vlCM*XmW4 z!nU6w4Y@wm7Ru(IEZW8oP~J~(hu&lCVIaFhSAHbfPz6-k`K5gVzn*>8<<#Y6@&Rex zebvLOvj4kA;Kkwdg^0BxC znylf^=>FyL?MHn5pzx*yh?Z#pju(&y1X`LkA{K8M6@E=oC;hLGB){%5I9Y3+v@OpB zbfVHyDdcS+c?duD7HD(Sd>8Bg)JFmdYBwnCSsA%rUgSN?oo87~u9C{l$RqulIIVn$ zWkLzhU%~GU`Yk(#klNCO&EV|Dr%KqSfB}3<`LpsVyx^voy!;mQq}AUNtblZwjm)bY@W%a$i_ah+|kcmihu#1?q^u`OlSC4`U z4m|Ox7DDKMuR&JJN5THF8&z!+*D+<@k1zVuy+w_h!oj$v*{X((TpT{v+!_dVQ z2bdlJ40M?by&2Xb5N6;+f*fWL0-;@`$ypUB39#Rihmci6B5r7J@AL{5SYXOW@5yb& ztf1{}A$USP5pzjNiE@f%Q0&~?kSrr>DdDDs&Z*uZV+%A;fBr;k zGIE5bx3K%HAbRlT!#feg(+5EX9Du$?rYECwe*PS?Q_gxd>~;YH6l5sy%3oF}K%)z= zsfpqi06%wS{rR9NKoW2WQY3y2JYTG0R{orC+*4%Ta02nbh+r5xMpPRDytEgU{wxOWasjmh0vDRCc$X*#jSsyY zcBva9RW+~)Ykc#hgQR>smG^qLqhPAUx2*tK`KP7#r=E9oWCE#x`ln*MnUEpDwGBk& zS?~nrRW#rTuCfX`b#t%gT7~ zq({}CpCn{`6aq~~3Q5dN=#)m#68XS$kXf)U!X!aUNPk_FeM(+nu>R*CM3RpO^$0}F z&Am|9IR!ED84GVNOaE#b!^kZVl5Q1R+gQhBSqZ!U4Aho{IfncB*4mW>ORI%n=hg!v zx6b(h!T2l?`4&#Wy8lO4Po) zr#hI65{-noGeSip!2)F)Wg4oy=bN7lsd?pr;37LabY(NGvFYj6Uz8Kwzh8!?6bZqt znah+58YDw3>9@pr<8~-0Qs3u*M}k*Mm#`1uCiCTD;i4kPZmf3y?hbMB8D>~^G;QA| z-nzvnc_Q`Bolh>KOk44|95nhRaJYymk)6GIBmCac6I|3nrL^{AQy!iW9^>#{pAp($ z^73Uyp5zGu1zB0qv-;b`#MHcOJ@CW<;U*~-LMD06+j}}>J7g1Q*5gincPkfJ{hqG= zYw#VIb`T$Zq_qzqS-{U?-hoUqvHe`~4!}Q&sh;zUG4ZI|*w_6%8?nH4c8=Ow_3V$H zpIi47yz*vjeqravk~xuMvGK!f=>h==xea|eB3(~NMzF+FeoDT7D= z0n0IIL|}s2tFSHZ?(5Mo48h3y33(`AzplEDz!T=NUQtKl-WvQXFK@^d<&gy7INdJa z0_&+WdPRI|4b_Fg=0Fw+pP^8`HH>x9wJ42RTDJ*A(Iu@? zv!1Rlb!8pSBq13#jsAv<9rqQdJzwVCmD%vJKJ(P9s|W!IFon%k7Qh)kbnW+s3WB&r ztDiUbZ&1>nuxMQzV$7{wVK_V{+l<4YUpZuJ^BrB&W1Gtwet#xx?VPSmxCZDNQRJUc zLHJ$%RWZ6IQUO%73W`dL2d=swJLb~sLx&D&y1EY09?b24 zY4cIWGm3ln8eCd+lYC>34`3tzw<*C2$^DN)xnN0|MmFKaix)4fEz#reT`ap6L1>k3 zi6g^mw2{a1Rq@C}9!`%)N@uA+TF*Xr)*8>Te)KNc6vhYua7#-|7_;yyZgR#j(pc)y zWsS>^c=#`0zOeP|_lazDeDmVUDMZykp&M4aHhFESGSvZK7G{-^q~jV0ch&dt;X}CV zaR+7Hd_8`@T5ucl2UHBQ(R5J%yrnj1!D$uW51-`lGLK~ltU%GR4DpxYPL$c<$%GhI z>(ZWSC^9l$*3>k~;ZjV0c-}%-60vPy>tT|-bIQ41^u~qhk(V{;cRv+ zhtN4&yDL{3Q}#b6)NOi)H9X%`9s$homR$^HpG4@SOGsU`j0GNorBHcXXnb#ss2UWU z-BrDEvT(k3_P6g6$^?NKM3=jc4YleE>s=R?Vq7^1XPt%L%q47Xv>4xNmzIx1_fSQy zKRzzEPf3aQY~J}|4g~#88O4B)9Y(g;$XO#`>tYRU6uPLCf2lng|fN|Mja-8+t#7LRr;AGIkb)McUN=n@p9)SQ;T#@?7&L zGCYgL_7=7bm1N{F2nVdue_rmr^^cg?=78COkE+t%@)3O?=GL#|N0CuZu;fVdt7?){Vf7C>+Um0X*g3A7@; zoKzbKB!;)qkEPRd&s#>IR?I3{4{h!Ec)73a8ev>-h?^P@QT2V+;|OK3mai{AFpJZt z6*xHJdGa`P>G4zmZ2k;>=*bhkU%g}6CY{q34NdM@9WfyYAojG-R!~MLULjeV?%^Mp%y)0 zx9=p6+G=dFN}6Baj>fcj!oB$Q8MPTYk<85a(dHQc;ll@YOG>24FAl7d0n5XHqylx$ zACKSK1M#XaUc4YuO|Z#Re^FU2hYCt~V@QjTn?$mHx53i}iCD-z`Mr8>Zk#M*V>Zoq z^3iOl4~uR=?QfOyZmqgm?k63J&swoCw##Mb<-O8K^Wwn#chJL-<+^?XLph)HGfA6< z2^T_V*jL03ofyWOyK0{Mo*1Tat9BkKC=&ova<=?B*TQ6PLfZNq z4{>N9{67dvfG%h&f>a}bu+L4j5jh?yoyDD``FeI=;_(enw$MT&)|&*unAq64B$oA_ zu9^Ux;=E7>33i+=&V&)@#~zb{u+REuE<+bd_)8b^?rDydPxO_$7C9_BAwW=gZ_Sb% z!YZ!yD|U&fstOR~6asVMzY2#IN7D2~kv_BXxjQrOveJh87uB%?ZF!MO+Qh@Fo{#|7 z%D7P`8p|3r$LJ0cE(G)vQk=U(d2BK!8mZd-V`~5hVe~aAx&(Gr;qu4(H*_LrAzF*u zTpp}Noi+LFkB`u7EloEh6vL4Nlm~U<#TVcGqc+fjl*YI3`62`l`~eveZ@ICev@~$F z)cWU(U2ydg@xGw_6TtK!P~vEsY`%U>)YDTtZ2Bwa!NE6Hxbq|SCjr!95FkM9tS+(5 zX=_+1c)Dx+X~|HYcNFh!=*3=jprt5cB5+NDrk#N`Xvjqckwuqa_Ij1x5IKVuzq$ZVugdD0q|7^Be_zL4f-ldtVAV*UayOvKo*!MsDrbZyx$2 z3%Pla&H+CsBx;kBf?lNj@7XzdXs0}KGbxoivMpQ(ZAST}$)n?~+Dq$lBcr3&dPa2X zFQK+(6q@kHig|u$RzrFx-b|JbGH9_C`RfZk3>?F4j_np!%jdHo~@kVN%WKkA1* zEB7>&8f_YvW&_tJZflH&D&DP#S5W5paPG8KtQ{-hUr^=DX)Rs*-yc>5<>bS~v_+R4 z?reQ6Ey65@4s@5LX&BPCi|yY}4Ow=}jvT1+b#=pG|3SFf#KhL=RewThfO0yZxA+zz znI(u}Nk?lL{@^W6I#j^#hMCCd49_9Q=g(cwCcsZSewx02BQuS}Y{YqO^=j!Oe1uaq zi|RNj5hC09yLz|8p{x-gir}w5d%kl1&DGL@kVTCpHY6Op#@!R|96|;@U^ITJKf>Gf zvQFFN$R2bQZmz8kUYnvE)HlAwh4hjLbnJOPI1>bQ8o-uVQeoxGrg63`?c5F`fcS0i z)-pr@h_NCk#k#Mo*b5Q2*6JOmTPR$<>O68gAf4p^QDw1fmlNRyfNo)YCIxXecsgCC z%ssUS^LwfrFVrUp1@K~C;O}%kSf3~cCh>7o6#xW;@W2kXlJqcq;JCG!6gT6JSmDT} zB`*L6){6!G-PI0CKR!N)(HHjSZHTz^@Jr1;#ZoIchq9m2r)*iN!j9VbJ|L^hzU1t) zZ||*;$v4^**NBG1FW#YrOau@2y?HQPgv^{d9twqm8lNjJWuHe$&TlX@8uj)>*OBH& zW?Ntg+sG}n;H$D4KvWCPyqu1T#0&$KX24j>yLWmfKZ>mCAbyapU6X6?>=>;^TNYy0 zH~w_h5?-9unPh!7aCu=bB*NMY9~o;XpuZ5BuyN0s-`^MyPt0ktNJ^pJTRnF;VKD_B-00}ntU`WlkREP4PCpUYVz0nQMBD~$Olb@YZ~DQ5 zYkaocUeTn$wBI2OtJAk#Ni$I87wfaZ({*C-Gg)C5BOs1Dopmk)pzDeAM$az*Ek(VK z>ze(BF>e{t%16 z4Q5JMIo#Aa2&?+mnVA`Atof!tCGU)0Y8BHs=;$bEl5ZOK`V-DJofkWoGre<<+zUk zIO{GlG(f@Z0vF9ZU*f^SHZhgmMV8i=n}fu9kl$yJBr(jIur7hnky8RHDXjlfM?G%a zX)I${fD$ojJcvNSZx;8*wZ5>CSJW(LQd|>OjA=p z0+Lvo&R7Og!knv~R*RtGS9S^`BlqNq{w@;-Xl09K z3HKTfI%JVB2rlRZ5hXtGwi8*Ui+!$2+YEPd^9u_{5;27i4*Xx*+GK1${jxez4W6Bd z@_gG{&geS!K@TSuDlKT(8bQ@w*xXpZxh_XU5tVg!C;et*R?|{z4i~xq0E4!@j@!}K zk0N>a(4_%Qf+%KeTE+mHH&AlieEWAf@gH@9wf9%1U3?Z09rpFhiB0TOnv;a1CoK_yH8 zJ=fdfqv-Yk?OyGFNgT2W{dOp{*$4Z+=R_hOG0x~yZ*7z z1yPMCpT#PP!7$t~OXs{_-y^efuj(2rMsuuSJUQi1odDSbBu!)*z@g?ofo7 z5fTr=ZV76Tja|0PCgPJR7aV`U1@+E5N)m{>LQ_oahi_Wu8fnLg4`JR1f>ADB z3cqZn@k$OV(Yp$Kae9yqiK?q_+#U!+*aHNl+%f;U-px@#N)}|LL$sQ?9Rw|t+d))3 zZ{6D9<3|>&w*~pP#Iu@e$cnW$PLC1D%>Su$b**x5wHhUf>|2d11tpf7d0E43RoJ1sXGk+L=x>Ko4C0+?v0d8|5z# zt+Mt(q;XsR?2~&)3_Z&p+*nBv1La}k_`G4PR97Nm|d1GEB+$Zyswg6<(4VmvuaA_v9D8InOgq%j;5{ zx-2~=cNPbISyP<4tY(?d<`3JJT=5PhxFUYLUHmzoM+(avWsw85f82+2M^f>i=?=Uw zA8qx&5ke~b2tJMAcPS0WArI+3n*l$E}(Ok6_k>t)}N05SW- zft9_rHuv-DJP|;oLJI^a9DG0X{N*(>h(;UXYfx!_poJS&TA|mB1D^%)<=*iBL`c!V zemdo_^Z|XmFh22NrVS@XRcgFS24*r$ zfLf-Wy*jyVL~hT^^PyU-T5LntDS*7Xz8MJUfH!xh6#mEhD3ZW`6q zj}#Xufjf`+EmsCY-7H=r6}0>si?2!4```VbeIOVQsrj)IoD*oF>c3cr`ZL zK$?YES`^>ZNY!;w9WhIJCX8QA4`6C$NP0&XC;m*C$WJi+nD z=BtR|g_A#eUVcRRmj@xoJa4E}nTr?1yqz5BN0^MQt@X}I?ihFbDqq7F(}=70%Y#S2 zLt^b2ld<&?LtMo4ZFCgk24N6Yci7|ms$?oDM|{~&uZ+F--Nhgi{_IAC?50nDL&BZi zp&J==`6eO1nYsqX4cVy9O5%dKW9pt>v-rCgR(*OMuO(3G9{(go3 zFM5#ppE>{he}DM@{a54T31>1g1hYWej0hD`*dwshrw=@So&)VU1k{O%Mp5zV>;A@^ z)Y$B&ef*(q0HYfA@v@GwyCN1Cm>)meth~?J4QU@x6?#hHBj^spdTX;uq?W;&( zf2O|^Q1_7P=K9!XV{TzBOWfo7*EhBpdYj>ph-ncf^uW!Q&Ee(_OT^dNp;Y1ml=({^ zodao&IAsxc3-3P}z<->I8IW{$2`|(7O{4sy))by)?*CrnF4R*0RX(zZh@=QhDX#^9 ztTjeevi7M{t_frD0{f#`)z|2@Z~s@qJRbZlY#vq*j%ilvYpJBzX zXMe5}Yj|QMQq?sygr}-jr3vRX7*fJ9H(mZG`Y@~qoQOKk^2FR+Lk4=LMje%pesgJF;2L_mUsSUq2_kbB=4c4 zkdv1~!BU9)LdAcZP3oR8-8$KucREdeA@&$y$*L1C@P^U2H8Iou$8(?&XEpk)whjj$ zgIm*~w8F2^D4?Ew)hJ!ts6C+JmNc~U1Q9s!DfR7#53h^zunf)He)Gch)MA6&n^PB5 zD#OdyY`XLzpN&m)gG8brgvt=H_O%egxgTLehog^7e}JLimz7J4^V-DrWSPG^EtbQZ z3Kz+n-W+>ibdGq-$cZ#Md6EJ+Falk?9F`A|k>RLy`|(9YDY1U;`$9nmoXB0v^y2P^batxGpqDQr`dLJ%E$ebcsWd0rFYV=mJ}qHCmgS-AX^ zNHRrf$fgY1RPs!#`83vhTCPo&bPd@e@g2)IqND>iHe}&FN4yJd47YR;o(|lUNypXV zASC`9x9hgCmhN-lzm?ZtLWkQ0i77FFhxLo}!X%rq`yrGX62D$@GKfJoXM}5C3zpKF zo&{$@q<27)Ok{e)xXX;ZScnuorg}n=Sczn^v6X_#-==Fr^;2S^>Oy41okWMyn9z)D zBFCrsJD-cMpa<<~-EgpVI{uW1vl5S^0c22r1@>ET@&xX2wf7}C**>SfD7fJk%0C=e zU+09R9t1FR3yU&wGn#^T&vQ%{cE_#xZBN-X$cZQW}SGZ8rd!kwp(*yBUlO)QhObGG9 z!VYHVpm{e2xw9DsH7q^ndI$#|5z+nj-6xh$&k;41fAuO34}`?vpzLQse2ln82b zTYcxNQIAYm3$jwlqk%>pJVP84VjGN-RFx@jqeJ3Z_yM4S zSv0(I;GogLqeq$Wu+yXz6hEbh^Z~*gAbla?(P}^K(G=u2HzVPNASY3VZFeNAIaoF3 z*l_yyks@{WPSxQao2j1a2gR{wyI%0T(E5Dy`{o(a;F<)|C?hU0H^4g4HR=s`0>?a{ z#nNk}8xl~k5b2z@3yvS|9iVMdpOt**HDW+8ItbIo>HZZF!4hxb{`XC8%XmIowO20s z)AZ0ftHvwDNRyLwS-3JE?KibAOEJV#hYx2y`;06vArAv2USLsiv2J2xAtBHTlo%R6 z6jaw5gzNZ(1~RD)Pyk27lWJ0POPNMZ4F+le-dR0&|MPOKcADrL&6=~t9NV`yLes|p zC)9Ma_vMI;^KXF{B_)|P=|VXfBF{R3^MuQnm(4}Ij z%nLu1Z~#E4heQpvJ2+y-A*{ZBbb;s_Nk~cgrIokdZvN7XogfiDhq8{e3U5*YE=*8e z(*87DtD(6Rd{{Vg z{c+^N^+*l%B*$`xuhx6=;^cT|2Ot{0(ygLGMkZ9qgJM!F?ZiiS;*1$18S=mC?G!Go zzMMo<*&FP=GYydeL4YOvW+zQi@)>jUl14l?H!5VTK8zc^Jv3F>aS-8dh#8K`&eoJM zva}quDLa$a!Xu!ksHW!6=6fv#-X`#g#EA;5QMR+4roWC(ZJUyk5=y{1lR0gmI*~B1 zsejZ2&JW9GFndGfXzj*Qq~;j`Er4ln?mO264~{|HxeIK0XTD_uwp}R7qC%n&yk*n& zocww3?_5M13X<4ecMfp>_mO8^#YYas zyco6q=PzG<{B($8jyf_m9_8l?B2yHX>#mx^4s@=B|4MP}b8iwvFgy*^Q@eJt(^Lyd zCllsAQAjSEoyy=4f7;^yyRRI5vVsgA4Oop?Jy>U!*OB``HegyK&}!7WR3FMJ@}zfs zr$if;A{g$EpFY#YWWN#-;e&z98D528%1A>M|MTZ1|K>7((d8j0gBcfd0N0sEPkt<^ z*nX_y#hTk)wO!^yG}xTPSD7;7%%4eaWov($QCoNMXqq}RT;xRQMsDpI19BUn3Av!q zu0&PY^51q8ehZ2I;PhsOLI;uPL$7WE%9LQg!IF(c@E7PGzRqw|4+4j+anHWU;od_N z9q2jgvGSFlxx+JMm{V@TNd)Mz?0kW`{>dA$v6>zps3ssn2a!lfLk1C)I}`;!a|>|= zgY)vNQB4r{+3vB{WCGxAWUF|T@4puowscCx0*;gV*3(EA1sx8v!i8VmeM3?5<9XC4 zEsR*IwFry|h`!a}fIl92K2*iU14tB++r|OO0yqI$mB5nqMS7`Lt)p#Q!xCenL86G7*eT5@iMRqn@1p3y?$aA?HxYzME)10v=SOb z2%i=3Egv-{r#@xEEEbi(7{*w;G3i`vd)zAO3~V+xw!E>~sPZP{>ge$ZN7BRs9V^X+ z64MzBRzbbr)eki7`4BeuqRvhds&*pr5h4+lAyu4rv4R6)(@zj0><2KHst&0lCkD*b z?5>1^f1^tgUfLev$jMb}`9{7)ofca!+QgWYuOgUZpVL<=a38rH3&)yM+1(;XX zPr0D{M~-?j;keGjGrxTYMQXolh36ZHCmWcR&L<$Kegw^I1PgsLF;VCjiyRxiDg9_u zTGHrcxqAq1^y;3({PYX0l4m9FxIPp9-$guV`{gFJ003UXsc4Z!GE&McC}1JDPEZyo zM1bIFZphX3a?2azGg7)FltfUFA9`}Y?_vQp$yHoBk9JlpZqeh} z*+R`Z1qBn&f4_Me9aVH;qpuug1Y)QJjYeyy^@qEy7*BZT-(z#Um18rTfyAT zhnM~IR92mR3D!#yDMjjIACHY)d;lfmTO%=o9A9cm6wA)bIS4V}E@}aL+hn2#p|9^e zvP!nDe4waHJ0!sUM76)8C6HInW%yx8HLd>EVdcPBrD6slk-nxxf zoNZtxfJBBo>4wyQ0o*&?n9tQ2A>mfnh)70QY!Yrl#%Nm|%@qY0hA_hXQalb9wMpLD z9*5fraJaX3b=6<^^wZOyXaa!Mo2^kZ_zSW=v+bv%kb(gck)o&P9LB-=iQ_mo>U)(@ zn>Z{z8k0@cKEsamEQIs2y3f03RmTDSG@ZOz&HQ)X1ZEW_TK)_tT6Fq`OE%+h`0u}$ zmO2Up+otBUZ5IP)h2F|bmAX`pMu@IXpw%05Cw}QlKACbky9RqN7c4Ohe(#-LI zXiODkL6WN&tN??-hC&EgTQdnUiXG{f#h0|luMfg4HNV`4rqUqc&077?hSvkGgX`Td zuE-xWsVNxt}hx zmClGWR7$ae?>f~V%D-`Vx)^n+8AJJ)Xs{pfGV4Y4n+C)R$Z`NOFpy~PCP?BQNBlQP zM3armt4|0yO3+wi)zz*W(oV$O%_{7-#4i4Ei1qoG{D0nAK{1X_+Ig>U!;yx|?nL~e z*DuyUmCQn^|E}Z-tly*XzM@ACr0c(Unh8|`(jG&RJwc8^6nR&KF@`RoiBWgLzrL_z z0*+(Ae2*wB(pYnHUX9Ja;$sh9{asC)p#DdO41U;qG7qE(sea=Hdg)Ofjm;}*y(W;q z@L2lLN3x9lk4eZtKoe+k9MNp_5`ybx%8c}J#=w&;lBzAp8rgGql7+f>G<>6(FsU-WT-( z0vt|W2M`gRr)gxYX-v5ps9l=~1Np_rS+tu%YCfEL2||kFSCbSt$jIRrP!l?CV4wx| z8lohc2C{}41~>)`S-~?AwSy3d6zCUSmm`XEAgLl^L&aGNSfYASz>^uCwT613)Ca?! z0BMP@PcJbBKz~k&s5W%uHTbGJVI%PWdK!WO#ihGlr11V>>|-Ht-{9AS5<140mM&4} zy-1hN)jhXOICv)WP6eTj&~AwvpU;H3C$fP=ZR_cc-2l z4Q{pLeSQBx>7bpagD-!?rOSw&zEF3iDM6Ulhz#E%8t=j9{b~iyAV^;Md4M?_l5672 zO`WZMN{)(_d6%vcc7=mRs`gOeHF=7x!OQ`p+-z?bsS)va@$aWhg61DUW|p z?zhT}5uy8>qD_AK@9K;w>5d6Y!7cy$tbi~7e~9P*f5h{+d^^7k8yJC|ho>#OPyKw`zX6E5HvjSQ5n2z5B}a)xU*7crHAnRz&*{}BVYIZd0BhgwRt2+C!#{_gmBp zA&~z~@uy9G{grts%{Os!LkPxDO+;j*{I~zR+&U5B;I~tWY7k`i{Ynp~Vf*%R|_7An+hac_>N~nkbEpMGa0CZKti^5>e|CMMGkUWb@w{&YLb? z{lC>Ud@KcGqcm2G|FHH!yMU;&zXvjU9JUell_jX+#;SteVE{b9B(dL;m5dE|ST>cj zz;wSC_Gu*k`Mx;#fBgz=Vh@8aG2JK9CUY6v`=*z%9%DR$i4eUf@RMkWS;IF>_=BRO z;m9V$-W2mHYuAf{t`yz8GFI#Q5jO25Xc>|`xD*5rV;@CUiB1Lc|30XLJRz>!M9$`m zm(+S3iT`N6#UK`oI7chd*5|m}@TU-%1S;x@ep#!{p|7HLjR0U(R8(MN{j^;1-y$z9 zJ|;N?c_Slt_w7qtaildcFtAnZ8{cNY0j;`KiD|kvbA&*DWeyY9xjQiV$Xz6%nS}VN zU`t8*o38u+WADA=sqX*yVI?H9A)8Pndv7WkWmT?1c0%^CH`>*@)_?^eq^~ZGy=X2iU^?JUZ1MJj{A|>ch85tQ#Z|u1b zmp@zvPSP>p>VVmwB=1t_mSl$*q_@EYE$H^&A3Ix|RP-c0*XG6h%YS^i9-%(`b_P3C zNI!z=;pDR)=`>BZgBT5A^Y+X!6>j}~azW)qDT4p%9ZB<>7(aPJlDNNSc~+7t^@)pM zWAQqp7TqjYkxhQD8Q!Y_fA6bc-oH;PZSfNcWV~jpEP$5)c*_|G63feTOrBpyJhV4k-z8R z^=_EJ!g=ti`S$6PXD;4^3}VId9vJ@5E;79TFr$)o$K`+f(`6nek8sdNvgvI>QO_(l zb>rEES`$HnS`VshUt5Q~^E!*d;|H^&X2N zI2@24!BGwm64r{_2MMGwfY;@V$y-mrFa+FzNH8FazQ1#L#D+Ef*WrzcT=EA!Ke=lk zoC7Eq+CK}CU0MtoLeIGX;Q&qpcp9L{1|_HJ14q!NAZO{&@~9bCFoWW07niTYvkXIE zBlqu1vt5cF4Ohao>>eTv0-uB6+qoJ772gB77jBaGmx-16Pk&0Qygnb5Ju+h50_uYj->+3W=v)+4X-AF$itB7w*=&c1ul zszB*YtbZ?w58fk$YXUb5fqC*O@}Eo?0HzOICaf;v^0?)WC{RoFuWP^>dEGlR?TjekvNk@phJSm%HD z!tAVdS=TSG34>*Cz#&zT+!Ev^9RN2X0kv%=Oh5r#C5aq8MgYJiK*jH%(SXkau2~@e zz#RxBCSHib!U z_;Q`HPDxB&ETdssV;Q*wT%dpun(pEkhunS6;ga!dy>0imhN+be&-X?q+rEjO|rnjUiow5#r)5GMK%dE+1%DDW_OL46TgA2I6hCLH6{(QjAqm0{6 z0-9G}c9>pDSRb@|-O;#7hJ4Tg>LXc*NFt!4c^Up)FpzFY_h>;l9RfEZ4+U>_bB2Djf znL_)dRiO^alW_lmnkO|{{Lg`4@PTOre}t=1U=pZYsXcbI+es2HYjp++$AvW$K~=a{ zo?adOq;Nfr8p?5upqFp^^z`|oC57sS)RkQ_a5%o`{_VLozYHDdImG-tdLunY>quG{ zMG_GGTLPEmEnvd>>|;kDZ-F%{rz{>|`xHm4Urj0~s=#QE_+SjT3zG+_r7t^UhVEN!%*UZTDQNxk!!@ZC0CEo&@cZs8KC`Ch zjS63VYHPbDqZ9JtMd?D5!dO}e3#8iyS!rjVVPuqfj1}PFnW-4T%SYxJX&4!?NJ&Xm zv8;&Y*L2kQE?}AD4cb7Ic2iT6rTD+6q6!8HCxfmV_7-(q>zmf&;o+GuKAN&&lJkoB z>QL2ytKMhk)Jix}Td`t4j(p>#C5Y@@Zlmwn?FSA$wBK91S1;6pR0hcr=4jXyij^I`W#c(mf6mP~JQ+ z4}nQD9;BEreU>+jM-}{6ePfN>=s(+iX1XG+uA&h93MkykraOTYijv1KD)@s|lGhTK zA}71CSY+V6B=*SMVjLw|9-H&&(`0YWAs=N(0MW*ba|2Br&I&7^8u{9+2^`)Gyyh~g zN!5UkbR8z2V7AGrpipD8yM!&#*4J;%xEYP`bVN0t)m?C?-sMD_Q+$6F35?zppLHvU z=DrH~djy^G`%H00PNZrA`cB^kbE?yx-pge%2UGOyp=!9lAh_a*F@CQkBRf0UZFlf= z*`No;`!ZPJPYHjvv-{+C;CTPDSIpH5h*`11em{QIzh-aCs$|KrLJ!xW?<>s@cQQ5iua$?*AQ zd=ufpI^GX7SB9-R_^BOq_0ClODkSl4ghu+t*yNQllwdW~Yxt3I-R~x z!F&Sv{ZP_v?lIMNrK?DJZO-28lo0*t-1Bl}&b4Amo;6bL=mY^6Dcg>G3Alav8`#v8 zvrsuyoMuR`771rJfT z))pN1ac351OaLDjf|Y(U_BBfU2~-4I#364Q{as}&<76tMAlB`Rm+e3o*J!D;1-3L{_Zdo((t!f*Yok>4&2Bo5UaGzjdi zYP0Y&?A4PAfJI}+WQ#Uf`|xXKM?crNm(NECO(dE35aSBSc4cyRzBP5%2Eq4!SMy8DiHxWD&l8={s9%+xK4~ zi@K94iiKru%@KK+x}_#80c<{wjNyxsVU)2Q@q^tZ6e+JC@Cu3rwUj(10VV*uBVKJHMFeF$UfmlV7qumSn z)o9`#(cL*icOf?e77|L9N%tDOoQ&J@qu*`xgDyQUAq7jsy@M&Qdj3iT6Ze=FZV*&S z$5}n_OYnbiC#fHrndw&Ba<|6uE$#IKHwKbpzF2RW!4eg5YtyaX{*}LqsaSrWF-fPe zH%7I)<=_Ve1UDp#`HXF(n!z9#8ov~D-mS~$-cR-k9qos>uZ@QZC`YJW0f!~z6n~r5 zlC5xfQ+axN+^u45*9Y8{>pSr{+RCFwM(7XLhE{wMTXphgdpdVu^xO}TUv(z0a8)Z7 zXQJ!n#I~1)v>-ekKuho|2vlM5JqJ9VkxO*sckdf@Kz7GBG&9!QRz#jZ1>9K~O7z)6 z{w-is-7#lS5R%~xbVUdlY3}&WE+G-Ox#@yP6^}M&yfq(GXJiB>Brw2y9;prL7G6qC zp}E3YJF^wGNMV|!(KW_?U#>jCcOQ4mBA^W%m0@-7)9YN$u{hQsKal3(UuP=p_O}BoB9tp-6#mS9Sx%Aj z21nV>n^-0h&d90O*eidVIH@`~#gW)k{Mu)SBA5QV^7#JFR|@>BSTvm6b3!*Tg}k7B zhXNThu?C?rYQW9`xRE!*5lZby+{bn0Ln|G!rDa`W@hs#_k+<) zeibwOmQVsi;$wvdB}ZRHLi5g;=;-A3DIT3yZ{MogFP}tg(qsAb8@o7EJjYwOxCI0( zA+es<7)@T%qzGnRP?0#mr%R|A7q+=6I{whiV{R(W+g8D$jShH_Qcja66?W&etZc1E zxAh&&HfQe`Vs1lu$j!rJ+4}o4>}6+QHbz2x@v!sZmd|R3%v(__c4XR$mhtmptJ!Jj z+{LgSeFG!S@A#wjIN3^TnqC868nS5t@JK&FKQ{*sUG79@pnZRK-~$j+>26ly&Lo$- zrh0fB2!w=$VSVi`t@KxVioo>u0tZJjxN*U4f*kk8hx0mMBCMGj{2b=%YddGP)~5$b z;Ji9_?%bhs!QiK7CobQI{$%8lBs>_f)i@4atk4e!un_weGA18Dcxy+Aadi?FpozxCJrZ2sZTD6G_d46F|U! zE({W5Noo{oQ;&S86!0T>=2gFYy+2pKO=zJ?OW*eO_2t#RkA~~Hsw#P|Cwt>dqz_#Q zwjG#ap6(NzAgaf#PpNh8FtqdW$!_mVjHQWZb3cR7`de@~LY)GB@Q{g^CSvbBuxZ6> zAeaijp>TIk+4%ZuIDamS;SkMyMpl*+*q5A{ogEcPJ(QJkU%tq}5q)s*rE;S~8k!j6 z6;~`vWC(=a-*qW{i`2T{j=MHd;dkTmxpCNN<1fG3^cR|w!TAM6@R!G3nFuWbY-FE- z_>Mrm=c1HU(B|fOaBSA8-D-F6OAaEVUEP_`DlluJT&~`alwGY206OW*hA{pq*x_d1 zp084~=J&Qr)a<&V?XVr|=4wLZ9No?tZ-C=5-Ime-+Zvh+;8Eq}E{Rxn)E4qW!evWG zhy2{28!dT|ZjC9Smi*Q~y{~dR$UD$>N4uknz@dGWoZPOpTpk8Bn)~PdRgT=968z42 zU|vld*X)ApFrC6^s@lIzN>mtfKBVsSsf4b1mEl>bR>9h5Vqg@x7;uBREWl$4!r++LH*0lN!(BE)EoZ+L7gLi55def6 zA!}`>H`cT#y!cK4Y@GGbw)7KZ-W?2uRy`KO1{LT9@76+(xy-m-&|@IX{r>DH9DHIg zwfyI`L2mMlLcS(H*GR=3SSb??aYnF05o-M_h2_ck8{{0a290Ow%U`F&{$45#BszW! z3P-TS_wf2Xlj5Dy63r=O*rA zb|O-tE~RS!486Nz$1!~qrDREcnBAOnipIyi{`t2$sT<>3v~%!$zc7ZINrr_>QrG*;nbs_T)X}F zgCwve3IdXb{O($$h*kGDZAw9(-$pw4j+RMu%@xPV2B05#R+FS*3&a z%F_|GqL`+iXgj#UcH6S$l`NErgM!A`7y)(nSuRRVd-v`Mbm$P>QLn9~fT{AYSt)KB z)2Z3h$d8PqmrKA^<2cWpIn!6^Nwz!D=&F&g50`RO`X!i&K)#q#%NH~7(?NmJ6}Y#8 zTK5q+o2w@a2kmam2t$u}g(tTU&VmXKF(HKUem~zY98T~k5%!RzMW#aMLf9-8dz)6k zXbA<&^nj9Kuf(=Cb$EVYO+ zHIB5y9!xy-7q*!uzaHCAeYo%GvSp1tP8fuj&1Er`jlXD>g0>M3vWLTiWl(~ndR|I^ z?^R>I-ytLNh4TyZW#ivl_0TTd9L|vA)*c&s|5+OKFr7oB^r_^YX3AJJ`aam+_0iK` z>6z2DveNN=RY!d)$m1x8)^odTM)j8F!?q7yaw)8kK|&A`M(JESi$X+Ca=<+&b02nW zvLcKCa_062ZejOj1GmM#I^;(mZIFzZEhkBxW94oX)d0+@AM(Tf_cr8^=O?qgLojDG zrl$xp_EOw%%~^7y-BFU2N1Gq+P;-}-n;iMTs<;L%4POF1)2M{;G2r!SYkl)oarakVMC zyamGzu?EI&kk!s44}HgNZNqkmDD8({FS9Aa@l$ck`}Y{cR`&_;VyibZ1%w^zPr#{H zhc1Kk_qr#)a#A)8c-w!2#6RddM5_>tIhVmK}Ki@1CC4hk6)}l^16(FRo z)EsWJV4lK#$C)+LCJBuR0#g}yHqiJEq(b7j-?oCn%35D&!&Ma^>tdHIAn*YqdS5^z zAD#TBVYVy1rIisGqXnCS&gT0-$?$k5?jT4D+H4?{CD+$q2L#Z{$}Y3~=?fG(N~NF*M{}1W+DAeFUSW@7}}Ca-L?&MUbCz>sG{_yG|e}9Qm5pyD$dn z8tm?m_#@@Z6M*vOo)zXd}Nhep`*# z@`Y`9E7k}dh(?PeeT#OsdRlRDOnA6D=+Y#8b_5$#A_Tp+c?qBZQ*l-!11oeksi~jZ zlWrqi#@;dz@J$xzBX8`Hm`%T4gsSs_lkKtX<61%TyFK0IS^A$pe9BE zC|oa=&+qB(b{m)pi)2DjobBv|Q>>g?%m}0jMSJPUYqYbq^ha5*TST&>V zN9x+|h(`z5%AcE;Mm+Q8a~@^H28j>Xq0t!>4R`P1ONgIpw=DQ6(|*b4&Mu z^_6V!itb!lu1=hujx|bku(PTwA2bH(al zPp4+Gur-z%m@?fqZ*=|vW4aJ?J*YOec7oFF&Muw|4hv5R(0&9DDRwO=2<$@hn_HtN`5b)0VC5Z$`FI5W~SZ4ZP|BIUG-fS)|TIL z+)28nt&N?Zzq)rO7bZZt`Avf4P&>h;Eo&8DTwG0}_vdD%4Kt;Xgu~ax{MMM04S|CvV=mH6GwTT<6+r)RTQE)}q*2|{K|gYBZ!-E&~B z-pY-usrvkKe;H4LprsPS9vtvr$>46R;$J{Xs%mMiY&dqpn^(afre-sJ+yq0;jiS^; zDrhw(ds6o<676!)vb#zq2Z(2W`S&<&4tl6d7{Qh5mjH(pc+BXPvCC#!gTJmr%?2gF znh>7+wEo#~O1BD>mZs*z`Qn^KY)vZMQPX*>M-t$ILYT{H*bX|CrtdU7nhYrqvEmM& zeZyXSKLvTfTGqF|ul@LjUfcD38~n7%c{+v2_s%`TcBHTjVb%LJ;h37AyzMsfj#UMV z{$iU5EHn9h=khU0h$sg;$YWKVfaz0qlDZ#VBql0pzk-A0ImS&hdbm8>C5D@OtM}eOeUOjeW|hSsY*fixCUB~q z7kCIoTR13vi)q)PrA|66H5uNVmX`JuZuL{^O+tWifY4vkkP>2QHNmf`q;kc#in450 zLtFbS;1&)5e&By*PbsER!(2+p@-(zn$B?>=^=OtKX{scu*Tldye%&a}&dO>GJ8}qK zi2MAJpS)sNk9z%`w(jX{?ciy=~z_N|z+MXwiJMBwh^^x?zv zz`zsy#$|;nFC$jNxhMcDf$Pt!GQwOBhRrM@B`&`$B$I{Als8`H`zOEQiN$#wuu>gF z8y}6}w>;X$P{7G>q91K0^RDKR34 z#kmEyzJJFH%L)fk`2An`9Bdlm+MHVQQ_{d&@tN*Pg$%#9-Q9PqR~rP@qNx0zJNO*h zz@fIiQ2E~zq>p_9_#W~Bz`)w?nmqhYjVn|IrV|yiyFaxbSz0>xo5u;k*x0C7f;@n6 z*qIQEwwoDKx$g+^#!kquz1}KNWWI5pl7{Bat5mAmC=M09qG?xfc`x0H)u)3YEyux! zywB#??WJ!XW}5SSg%A@DPio)&VFg9kWj;A&ee9g&g=2(0qFO9h0*1`aYlmR!kX=L5 z8W$BG<>@1ZZA9-IiT2F7yp|A;oa@QLc_#P%{*?A5ZEVxqF&G80@i5*&T=k`y8y7f+vSg!(9?@IT$*d|ZjD{-^*(Cr#cvt+gKH&xyz(Fe z-W_(Yh8bP#{KG#LC3(6jspKuSR^9#ISlGUNE4~6*R=^c%a_&^1LbpRQv2NKzDVTNY z#wux>on>YQD7E&2Pgj-Gl#}0HQ7ROfF-tON5y-`Ia(2%7@aa)&EZ@1wnVz3NzbPE; zYKhwps=|>E9k>)U@-PE?h;_@D!T3T3;_KLi?|om)yo-BQaN2kD`qPb+5G9zv6K_aR z(1eBz#s=VM4I`8_kE$_WA#%W^fyUNg)SeL@haoXmnZ;md9j?- zClRA$sM0~A=(8=7y6jv57F%F(u483yKmGV7DFJ2Awd)U$hjMK`k^+>*_a!itwdEc9 z#Sughj2dAbt<{GKxcwrH7dAgdM%&hewu2SYSFkozB%S9gR$cJw;J^{6YCx7lnjgPI z>_}JNkE?);htUd*dyni=g3c41#Y_{3#l*yZ{tmKK7SbRI_LQ9kwV^ETU@g*T1Ai#9 z(3?Mz8E5o9=SwL5pbp2)+s3-N9rkC|F+#3Zi~JJZ@El;R0Ol%20N>Q0+KDLET*;zC z&;w2a@QQ|cZ!+*j;3SXep|M|wAij7}t26t!m6;)TiM2YJ6-1q%S^O#H*}-ubl0ru| ziFlHzz00cjYfa^0NamiEW=DbFg zZ`;DdS+M$Bv)0JwMQ?AbO5VN+LTm&Mcc?nlOvT;j07qz;VTGHkFCM*&{DOg9y8nVSAJGYCGiLB}R$pVn4}``{ywV*0r-@<-lItrx2vCXdi~~& zV>flor%&pB8@a}-#tM~TP$q@G$0${nZEFxQ{VBOHk985@w94>X7#b%+F5gK4pp$#7 zv`Zl8A~4V|!;lg1^2Xf!>Qhk+Cy^JymV~+1*{gU}FS)rf-7e4=auM^*l&Lvb4CwrzaCwfL!RaA~VA7Kd1|< z5eBiJtE#Kv&KKuD4^rs<5HC&`GeGMOh47wea^9cQE0BJ>jE)vX43GkVb?i{-$BS5O zma7Bb%Erh);B(Y45dcMHi-niM>E+6Eaf(nHjiHhX?;{-6{5;R(C=O*P*45#80m@cc zWG@HYO9F_j7WVA^#-jb{_pe@O2&IIPBAuL<=-QAq3T{B>$=TCq#?Jynz$dbopNGe6 z%S_9Jj#$Uw%WLQ}pxr3+!zh2}`5yW*)D*lv!kJ~xTb878!3X`xbAsZa8ntu^1_6DQ zvZqe1uX$B*F~@DA-8vfdAlvX8*SCo^4^6M-%sAue*)B8Qw+fM=XStHx68bmd?Xb{V zjLMrfCcSuZD#df#b70_s5P39=ZvbKH== za38m^$LJs|E)WTj-VMD!KZ_Nc??zHYjXmlgqk2L~HXv=Qe39Eu*ybGf)e3 z|DNWco~=cnvwT5Lgb_rQF@wnqbn9VF6ZMiK{M4A5ix++zTUuJ3XYiQNcx?MDm~-~z zb-|OdL|LT#T$0JJUZ1!birSqja1kms9CZsxl^NN-Q*X=ojBWPh)ypRshl4Cr0``bM zYdyoY5=eWWt7f>WJyJbpHG6fA`~2kt9Y@E&dDP+!ZPnb0nz zsCEwuP)gryEPZuom)lU1LXo_=vlvA@qBm#am)C*6{g|r0ZV%N?ane1=e@6G!R~5F$ z^d5%9lmQznR02_CF?QG!e2UZ_!WvF$IYs4UNwCo^lsq)~N4Ecp!Y@sx?1CWth>S-X@wc~#~``b*VX9<9&=)s^Cji&mu=q? zhcgrM#9``pU!@SQt(}tYphZ*IE#1~9ODw77u>tsg)jjDDO-@)SY_fK#U{ScaC3tcY zwR4HK61We?pAtYD_BP9NjhIPdnDvo%sPA2>50X?qJ}CkI73vPp$@;(7oaongT#xj7 zMq7=_2@v4>xvS92%V<4-F&qiq9PImtWxPT0ywn=X_gEX_*hM)_xsNx3Unrc+)VOBE zN4aJY^)$3y#X_dV3DCTFEIxw0oJ@6~Cd`&YkKS zUQW-+c>_hSwe=ZCM0uG5uv$u$_!rd6}o|?DJ!OZs4&yqsO4{m$!7f zvxLnGR)8toYA&MsL>PUo21d&|$(GWglY1Hjg~?2^a=~D_m(wu#~vI*?)!@9(rdg zy(f{E9eULDA#A%hy18IY($wH%v}2BDlEoAMW$}L9-*vx;SduJue6cLWH&M71_GW}# zpidga3r?XoZ@pk$HauT8OGhl3OnK(aI27~c<;oBA2i80WOARoq{tnI}Oj4%=Do^Cs z5Vi9~aY>U+(Km$=Zkf-N7cH$?!@@skEwEQCRcZRAbJgvBsY(brb1GHDn|l3~S~7tU z#fXlp6SkdVTuCb(&M%K)-RgUfn9p|vPz^Fre!Y7)*n7qNxk0fG^;44-wqP21VoqAL zb~64I5!*{b)-(4XJ-?Ohm3mE2#6Y_H(Zhst+vnsZ=4R?CJkNZ;q8dt3D-?{67gScp zG|_fF!^=%`C~DelE4>@RiMUA8FQ{@3;mu_x76v=V5lGBavG5-`?_Eo01kK&oSS z%xoig|8g^R>0?65)AUATGIdQgI3fxLLe7@q)sW}p`JYaZ@P{brMF(^^-TO_tf-N$~ z{y2Sde618J8cbCfJFQ%gsE|g0iD00)UO+*E{HdVP$2UDBzbAZ!f?uRhl8YVtl~3~S zaopkA?>uAEj3$#Of7wXt*fLr#jF!=wZk_${L-K^cfM?#)>Vn+1%TAL2>LJ|VrK&jw z1ukw%k97N@SVWSqyX|;~gj#$#Ek*TB5~Jkj%a-2sssr5NpDyOve`6OO4DaAh4Th1niO9)gM9!+>jST&%7Q;o(2}-3du_J5)3bUI zHo8IONXR}TP5tC$A_kLcUqdwA_Dpw+z4G>7nL?CvewCc8?4w^31!lC0aUYYeUm(cO z4{Ee1J**NmllCCKt)v%G#M@WG%JP81p_=B%PcrCwfw#cHmi%Mu;7f7WAJ(H-FG-R7 zy`Y=RXbMMb*>;)~3s!1TCDW3vr?j*OPCtox)XbVab|oO~3pW#|?V#)+pHv5W3?(F6 zdFo*$s-K-gr;K23i09vucjmtNrGpLAK7UlHes_go1;$lso{`wptX|GPKtsR6!bdJY zCMI~{>Ef`=AYYX8)xSkRlFZiDD$c*QLciX&-kmR4`s($dG5Kd*=D*BP1t&lK z=ufqI@+o|^tb>)zg0;nFj7Oyl)AC6>H4Z`;0L^WSj%_X>rjgzbKwjyiOzY)tel87k+}j!k9;>e({5y z8S2^RgI89Xl=4llf?=6>V?=U+-F|#;ya;2KX7KI?aks}~9H!HcRAI*r@5N!DZp*k_ z#d<5^soj>ssH!Gu$b*m*q4yA^#l9<5Zrm~6oNRa$_WJpw*<*=r~;2I0Y0*)-8OhJvZHAUWX%Q+NZKH}Ur6=T-@l(s)7I zGHSa)m;1ZK6J%p*sT5Q)vrAR(TW`i9iyTK%BmJ3A)omxQCRU_<*lAlB2n=|op??mQ z)KY!;98;&`pz zPlBk8h<^o9WsiaVUjPF^pk2i)__M^*(?j-X#?6XhxOo`+FTIl}CRO2kp}T@Go2{$4LJp{gp_SfRASKhC2}qLO|# zZBZ0_S`ELVp+U&B?sUw?E@-|Z<&3XvFN@YAbix(Yo20nO$2Mdk7t?Oz2Tn7R-3%Q= zeX3+1)aMdOcUW-SX3xnXBjMm~`B-tL0Xe+Lzd*%s`CR>y8dpLQZgrE#rLU{ZO`Yb-kP5v>Y@j}=*DEUmg9&o ze)MB*-rZU@+PvQ3ClR&%zs~bXlT*o_tKK=>i(}&^9oD0A_`G1uNNzGETxtvOO&#m< z(``4&yWKduzNgaUl=6{BgN!Vf8pz$T6_kFCK)8=JhLm)HmGG~iu$s6u5Zi|C0v3yw7PF2f> z!c34bno8vzeg%C9BnK)3tFkrD=)CJvoO6>XM)3}BYdxB;(w+*2uUSBj9XZ;@iAsn$ zF7TDSdi{FPtm^hAHh!TeReCiRt=r2Dhnj~{{V#sR_&NU@0Kh8dX0Ffqu&3V6u?U9z!JsEDLI%Lpcf zb!fW*U-^OepAMb20T@P7d_R2rxV-4@bL&#mEfCSb=sp>NF(GX4+9SzOtGC7bSWhrr zL%uzD$eK-u(?bsUdMCs+1J68rXuB<51PC-a$0^HpiudbNNxZrkC2AQYxw#4TM+9rp z^@{o#?oSBZ&*Hc&W1UM3bmd5NU z31E^UyYmBsh^#CX_UEFHQde?s9w4S62>xG*ihb+b>#B`J%G zOj+RdbyEM$Mm#7FqV7c&dgD?=_!&W_W?4UCuCEWr;wdE4j7ptl>#WP#?o1L zPl{N!UAQC_Z^KV_@a^_l8^!x@s~%aaa2C+|OjR&)Yf}bY%*KF^c9LKd|$~ zwpSHfHTmdH;hg63r>9Y>uT#VeOxdiWwpv?BetTc0lQt9NpOHYS4aLVWF!NbI%gRN! zc!7`4t~8M_imVcic7ucW3TKd7k1@TbXvP6?Y#G2;j2E#Kw6|Sxbxh*tI?EYOazRWiDAC@7Doyn( zzV~-!2e2{#kGA*dbVVYM2moBd`L7x?@ktT)od{q9BoW@c@Ba2i7=FLT$asO#c>hX~ zMf*RsY+bp|oZ&}DCv|i%f%U6xR|vRmM}hljIC zmU*&81}Czz#0M9$YNVgdYt*xyF!G`hidcXrr+9G;xN>?gpFKa{}hb z=sS{c&mKru&uHss7{h(nOtexqV>}9fz<~4RzvSW+v5gFKm@#z9;g4&{uASq~;YQHw zw={aoqjie1`v+tY)b$1i*VpSV7&(x`QO5hgg>5K!Z-{6J-IKdJ#FfbO_F#_v#@@6!W)1z6j1a5P1xikPPs7Qv{IH#|@etf?D7o)dS-fVBE2>dy(s4 zfjQ<@w(|>rI35_!W(z{T?+9xVy@a*0vWH7<%tZ=>nA5M-&LgLM6zY0ssG^e6!KNi% z&H9!wkQ69_t=11V1e_LixtEsgV0{DUwrndF=h#wD1ooSLcyigDRgTK-0fvWW4|f{y zM_Q5FRM{{D0xBP{3D|jfay!}X8T!u)-bTXFMa?0O)qEcfo&y(HJvB*30qO-g<%5cj=c` zsNZa~U-dd~FiXzi6Y-gB(ts+1@_7<%M`X<^)CFnbn=accBU&)l7pOXL%0H%}WPS;e zCjd&ofm1dm{VWOW9w)(p2adIwWucLngDhI2>3(9I(SuzVI!9m~6N* zC?n(Id4clcCTCYyS9o9;u+x=982&G!mto+ep^p~yUrsVAS-_DX+^5o&BY)InGcbjW zBhk{yn!p*jW$4T7?l*eaht>ehJW(^&sfcXNuByVNi z20BzwfB28R>>YWR8E01Pjk5>#-Ol)oLI~EkvhSgq8V}^Kv&qRp03`#60y1DEYQqR+ z5Q0y)JNUc=xyRN*J;5mO>X6?*aA!wEb4e-&l%DVoekzq~rD`+-5dv^}CRsXp3kbUP zeW2y_A~bZ40sV8Z`__fgl~a-|-Up-z^_joImK498KeE`nO-6RAygDy8mxJ?n*c=L! zv30Og z{p2E#Ojts~10=n&`skOx)Li5@v!+}9G~B9^uJee~of3#pV3atU*HysI^3sP1qz4uJwuG~ z(Yge`Ug;}GvkeHPJ%isUVnxJOK+To}t9}FL_Irj^w;&BMvUlO7XeBN-PUoN1S_Rfg zu)$=bJ0;|~eleC;PaR0hN=hMmFlqqZ+1e^57Xm-0Cs_BJZ(^ZtdDNaDefKVV5Tj*h zJ=)I5W9<@1qSPVrw`PBc6_jDWQxTPwtM`#Z`68R4AuG)lBjDWM|OgQ<9?= z43&aY_XLbAXF#ehcjhccj%(0f;@2-R7*V`_L$vTaKN|vbp=P?LV4kos| znpHa?H`ESm=pVyKw@0{t9C+Ap*r+Fnh7MJ@yK5-kJz&b3mXi|?>>Y}M)l?I~8;~2p5*b&# znW8vgioxpG8GQ0XS&``A|C7 z;FHyJ`GoZRg0HOPwH}ESSgA8ps&-M=YTwd)_VmE^!S<+IA=9kj&*F1LOm{Q^>ohp0 z{F12!%%RzYGtIm+7|ZRjF>iDLhNFa={w$}u$z(Ii-33$u{71g>3Q$`@_y#y2sJXgQ zd>yj5dNTOB%VyDh1^5}D(dSb;n?6L90r>{E7h(&^ialAHbFPfRrlT&B1z&);NmcR{ zq*tGRiHVi6Pu%j2(z{Fiu`KfqKoQ4Y0W;O6Gm+u&_zc=)b(-)9mfNhfA~4!mfgF*`ck&&kVMKJDZZ%m^*ZJ0Llo=1{(U z`!=d*WmosMYw(Pn@>?f551b3-Fa{legEEiOjS)o&dS!$ooII#mrk`95(&?X!tl={!u|+nO)R$t5pg-BCaxW4YK40*(!b5lAei4@`9Jq_|E5K<-|ZF;j^k&gOjc&n=t3vVrXrno{;Z*I(|lxC3)G+sqdTPLhtnUxKv)x(KMbJO?yDL6c`Y3?QJ) z|5>1ac|r+Cwk2RZO8ByPPNK6s1qCmXV#1qc$1V>;}|n zk$OxR{0b#N@yqAQ3wL+-NVrb55ug^2fWigDB;FYoOoEDU0G?)gj(M6>fAG6Ek>6%c zU0W9w`5u7OfD8K(;Phq#wE3GzA4afmvSgU zbiC3JsoD*IhE#l1#ci6qTFVXj?L7()4&ag{4$Lk5mY0LbxiG7Ka^OYO8n5KMnRDL+ zsKF$V4b(r`QvZ{}nQ2i6V9Y00u>8WpuBH9GzwBPo^r0ljYs{V?o@Tv1AGo@2x4#dV zdf<*i`FFgKG4Gx^4!^bpfRF}Sr734sZ6#>4*X6?p{>zcn1B~MPb^wPZ6KNDd`CQaM zc&#zi66W>z7Lb91^_TAASCSB&Bo~hwrw3fV7D{uOB@qU(9{X1Jl{Md7S|mw}$cOC3#N( z_f+xe8;}tKWKWRJH^9C_z?KK*hsUiqHNaf(@YevyeDFLqK$OjSn;>**5D+ck6s3-? zfNJ4x3=kvuE3KYo=q53ck-=>nK(#T*oq*a;;4|#d;&M@HN|sfq?-4j`?S5 z)`G(FNq{H>Aw0QlataO_sN2fQ%6X@))C=4FX)ryCfSVO`b&OUFh|h0w5Ii@;4|80l zh>n#vHXc|E1c5%t1|8m-4q6@Z5k@Tf{diS{j8Ujcg94f8M|n1SKt`SX}N$rh(*Mx-dR~&<6@xZKnCgWa zRy(<*bY6XQ*g0Yp`b1P)!k8U#wz{0L=qOOTlT>(q&hFg<&@x}X?8hWVw*fqdyCa%& zT?&F_YZ4IZXlf#A`9Z*Y4^(S|Bbf>iy&-C2b2>kd&9Ct|TT&tL?I7TXgPPqZG-7GJ zaV!kNjLC5;#6X?n*Y}&Nc%57vc@T7r=DkNEq-0wG>Irms=m5=)ptecow=q86=mN)) zO*#T@GuDew(|-YMsOxbRFQ_s90>qI3a943EnHD6cUdQYFu6H6Auw6ik6u-G2N2Gso zv<{3r0?dUtbOB|^paL7Dp!q^cw1sBGkwt+UbGSUB2vW$KhDJvAt7u}aKh?SK3AnD| zoz9-(0*#37N;4$@y7#06@dR*^p`GC~KDQ_!Lsr~!BV^qO7hTQ5(>+b280H82H}LHJ zK;j!@&k?x$83%SSi>jQQfJYw+5XJyIi3B_)_!j_$DC?riEhxb71xFoaPB1HQKae2zT6dpd%@1uA5jyX(6Xa%8~%3$lj(rB#Q2`uJNhOJ1-) z$WbgTKpnRUkR^FlGxyD;sELx#+ApahrE?akoEvD;2d&n=&diQ)dv5WgY<3)L})Ymn}j3I{>5wd3l`fM}mkJa!lUyyB|?F4($jsGd0b6BL3Y!2nV}X zJ5wrr5n#)I{A^6whE*RfD=8xU&GHK9J*oHmdSHi82Qv;tyt`%Az_l64l{fkoOaUZd zev1QP_G5Csn1rYo21*2Axzf_p2ZG|?q-2HS-RQWqPLPrSR1wcTPPYD*_%y{wXTCXT zKArmm@T)2I6ty5G1E5$yAc6}HJ?DA>mh5bEeuti&9iiwActHfTJOir7Rls4-9}=hg$KK~sH5$WEWEVTo8YKo3+XKtryWe_qcieIuEI~{AgfWqsBFi_wO z_TEmQPuM}ADDv9L3Okg9_30o!U|dQSDc^Vx3`&BS*JGIT7O2I3L_h%e@UpUV?2o^x z0`_52ZeuJrHv>fB@6^%5d&?Cw2z3!F8mOqn#wGJPVSq$iklPwuY7z)8P7p?dC-rMbE^gTo($Gi}1Pk;{ zmZ?^*(y=>7pLL{*X6L#i7pQ&~b6lkqS+*~;n6L&(l&-lZI1LBbpO?l@n-KbJ#lD$` zydbW3Sm}pDsV|-Yjt(9mv8g{>zsH4>8>`N3V%vK;*9&stkt;bXvD0yBaX=JF=cR;~ zsAjb_f(Zv8RgA}fCjLnlW*pv`yB&#)j5IkfHv$F$WET;+O;DC~l>goz2%T~`rDVXUlh zwjHqL0wbMd1#{WE!ej@O9t^;bRBv`WTp3+Gz;6zxhX=z9X0DfSEYytczg(uY>xGx~u0$@snX+7J2 z@XOWdJv|9}E`bV*Kd(*7wcInQx9!wWy=gkNGDlY(;Uv}++=?71xy^TN_K4r+t~=Rq zGl#jny}hr?ZPez>Pzgl-z_F1E7s0ouR^}-PP=bC}S32wqz_m~>^CSZlleU#v0RSrx zvghCpm&3{WYRe2jtgg2)nh%H=V}`fe{~mmM{`3J3rJ>7n$GF7Ao=ESrPvEo#@T$x& z9fKl$Vs@Qh-7{JU%oI4o18yumeIoD&IL#L6K>hjo5bkX#V6g)5z%>niQPyxE#S#Z9 z*6p_@InAHW+JW>d;8e^l{SlRwB?)r3WSkkRM_<|EL&2R2Xnd~!YftQ@SN}XnDFQePQT)hM=CyiaqUV|f;Ja%>26)c| zE;oDCR!*;Jp5}|O5`c^707dzWOHgf9Q7QlrZjKqPRJMeHyq?T2$xBN#lHhG&=W>$) zkY`<-E-ZOUFGvR9U*L2Q=cyiGsU$-{C}B2DQ23($;-Y@t%0}T?>!nR@X(?VW1_0xT zw^HAnnT!|BgHlbQp6Ge(3_M6zz&MFQHB&{H`1Dy+z{4z>-)@rra(z5Z1H9S4;E8b2M zexK424oN)bj;HvRI4PUJ+gsy@R9tptw%Z@!bL94IcMd8gc=)U@1c^L$i)8Um4YLSO z5y*9cpdFwGL19U8C}620?2Ht5Yg_|lNKIl?)FfSi4$|ZIL}+NZ(eJN4aw!7$TIPL* zH|ZG|QtYn*)CAz*5y>pT;xrk}k;|>7=D41}`N_43>Yyv!U}h3%iqNzIqvNc-r3i>b zfQOuFe+^RoK&T#Z8uRe}RlqA^ws?XXnAh()>U<_9EswsjT+thFsQ7bjJlh9BBYL%0 zAohI|z~sbSSJ4n7SaJ`X!Wv(l?Gq9c>Vg1oj@8btOsm}X9sn;+qK)tXrqG3g=OqH` zf@C^f5#NR|B0W!jr5AQNUQgutHw(yp)IEDJ97^{Ic?dce5@oR->8d=BNN_1t9$Hbqr}CUz*3 zz^M%-x_ItG;koo_0VXbRxmYaGfjXm5d-GVWjG~@MDt--d-kybv1tlH+eS#B|IbXsb zgsUh7##%LRBoAl2L?DggP6s{r2GLh2NWfQ zgM46Kcb=B3zW&02(cmchsW^Ii1q-rUF_|wkAadqmJgdrX_~#@}?B*LIQ0myu%h#X# zq8F6{^`{mpzmAXR2~nNyGr$5Dod=LMfFo$>a{ku(`-DtzB;!Y)MlBu?Is)1!@h|`w zV#qlaA_i$2&xI2hNRR~t1Smn20rpTXNg%q+%?}Y>cSB=C+rNO~*3E5% z!;$~$sXl3^na3vHdPj8B;AuWm6G~o5OwzDN?TUS!w7fiJCoMe}78VK@Hz00npBKNL znNnO8T(d%^z^VPq;5r`;Z#c&wLksF6z^UYb?}(>r4-GlAJrUV5ZPPkC3!-6HghsKw zySuYFDde-JcF=3dj99Z*3dEpZ9m2!5JY>D?35GsYhosxlaVuS1*teNe9Fa1fussAK7zDA}SFF)LIuQ3`tsK|aSOLCaPiaMVaq>3Nk2_2Mx^M~ox*iB5N!<>lZa zq0)NLa1vlLE1>!^i1H!d;*#!4<=(r*o>){p=rCg}LpyFW3T}yC&G<_thgzidxSzs4*^BxrJTE@u!7A}Tg~(VX7R4C2 zByI<0t{y0(qp!3n&pf+KS_Q}<_-Xj`y!2W#N9C`9Cl99~;A%AP_hpKO7r89_omfy~ z8XXJr*gLA(`#C{Sycv^%#hnWc+C^7XWFevap9j7I0qHhzE!=&wj$qs>pd2I5#&_c} z`u)~jTwH@PmeOt8D7_@O&SNU}T5b5ccxNrXqey#pJskd*>7M)MzfcHsKH`KZfk%?`mM*8@% z6s~P*{y8aWX;sKDa3Hsr3`gqGB9vx4VE>)k4*?X8try0QRN4t!BB$S>1*&n9qpS#)Dm~;R zb#ZJzAdm30qQb^6wG3;g_$Gbz9w!0u0ycE80u0qiQC%DQmI#+BWgKCnHJUtUj&NG8 zncqng_x+-X$Tuzw;J;3{r?{|e>rz3D1BWU0DO-rv5h$H3XGu{&%gOIW#{c4dA z+K$v1u{59C^+M;-R&m*sW+o7~0PBWZbDy+|vA~e|{qBHYK0WocVrtF_KXq zaedEoF2`wMmXnG5=VR{T0~9>nA())Jy zA4SlWG5xb`Rd)+MWbExpAl$#8FPBp6s5*USC4k`m@PiGvJ#XQ zY=D+C(wog{T{M?73}N6%R$4}$j-iu#rVk&Vj1bZ1ldh>z$)@jSObw>jf`-qyFY zk76Zyi8D$zdO@+#LBl1fPr)a+PaHdJW&F1`*o7LGUkj&OmJ3zOa1=m8^+fOJSNlDajP~%L-mviq6Rd-n62H#qWh!HS! z_+KuL>Fk?rlQ_8UiTIyY1AP-v9039}j+utLQf+kYm%-ho_K8uHHo8fCRaSVV?X$+O z4bKvz)s^fQu|G6HXMuPre)I?;Fo*UVy;4YdnSyCPZRtsat{#mWNcmKv6G@IB1yw*U z*dY)_y2Kn8=x#_8B8GMK!HBORE?q6Q=Sx%E=}MOuPwq3f(QxqqzAG<>u;(JZ(OvjB zq6kZxaR(wS5eVDUUJ{7E$NBH3`1oIlSDsk@k5q-B+W)_T1v3}3gt2U3AH;w!$lNk% zT2H{_b_+fAqD~cO`YZo;<9o23bHS^=eCo{#YX>4yc!?-mw+vTeoIyFu0NtEu7@9od zaU_>TfQ|33dm}DV-@CZ4O)RxxmE?(sj^k3OP?Xo7lea?%53q8<

    Vb@u_a`nLqcO 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%0A8TC%<<-Uy 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⪙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%0A++uIKqaa<8m77g@uJq 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
    P^0%BG_eqg&A;W27_ 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 x>lB3<)?y*Pgbm4T8{|oWQ} literal 0 HcmV?d00001 diff --git a/docs/en/integration/deploy_integration/images/task_func.png b/docs/en/integration/deploy_integration/images/task_func.png new file mode 100644 index 0000000000000000000000000000000000000000..46b20ea9591b5c4dac47f9e68d3e0fc601d2322c GIT binary patch literal 196506 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

    j-6h_uU5R55DuiW3vNT*h>{b)?{ahHdpel` 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%0A5BJJ%9(X|! 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 Date: Thu, 12 Oct 2023 13:53:19 +0800 Subject: [PATCH 52/63] docs: update-quickstart-folder (#3535) --- docs/en/quickstart/beginner_must_read.md | 144 +++++++++++++++ .../en/quickstart/{cli_tutorial.md => cli.md} | 0 docs/en/quickstart/function_boundary.md | 165 ++++++++++++++++++ docs/en/quickstart/index.rst | 5 +- docs/zh/quickstart/beginner_must_read.md | 2 +- docs/zh/quickstart/function_boundary.md | 4 +- 6 files changed, 316 insertions(+), 4 deletions(-) create mode 100644 docs/en/quickstart/beginner_must_read.md rename docs/en/quickstart/{cli_tutorial.md => cli.md} (100%) create mode 100644 docs/en/quickstart/function_boundary.md diff --git a/docs/en/quickstart/beginner_must_read.md b/docs/en/quickstart/beginner_must_read.md new file mode 100644 index 00000000000..759d423b32d --- /dev/null +++ b/docs/en/quickstart/beginner_must_read.md @@ -0,0 +1,144 @@ +# Essential Reading for Getting Started + +As OpenMLDB is a distributed system with various modes and extensive client functionality, users may encounter numerous questions and operational challenges, especially when using it for the first time. This article aims to guide beginners on diagnosing and debugging issues and providing effective information when seeking technical assistance. + +## Create OpenMLDB and Connection + +To begin, we recommend that users who are not well-versed in distributed multi-process management use Docker to set up OpenMLDB. This approach offers convenience and expedites the initial learning process. Once you have become acquainted with the various components of OpenMLDB, you can explore distributed deployment options. + +You can create an OpenMLDB instance using Docker by following the instructions in the [Quickstart guide](./openmldb_quickstart.md). Please note that the guide presents two versions: standalone and cluster. Ensure clarity regarding the version you intend to create and avoid mixed usage. + +A successful startup is indicated by your ability to connect to the OpenMLDB server using the CLI (Command Line Interface). In both standalone and cluster setups, you can use `/work/openmldb/bin/openmldb` to connect to OpenMLDB and execute `show components;` to check the running status of OpenMLDB server components. + +If you encounter difficulties connecting via the CLI, first verify whether the processes are running as expected. You can confirm the presence of nameserver and tablet server processes using `ps f | grep bin/openmldb`. In the case of the cluster setup, ensure that the ZooKeeper service is running by using `ps f | grep zoo.cfg`, and confirm the existence of the taskmanager process with `ps f | grep TaskManagerServer`. + +If all service processes are running correctly, but the CLI still cannot connect to the server, double-check the parameters for CLI operation. If issues persist, don't hesitate to contact us and provide the error information from the CLI. + +```{seealso} +If further configuration and server logs from OpenMLDB are required, you can use diagnostic tools to obtain them, as detailed in the [section below](#provide-configuration-and-logs-for-technical-support). +``` + +## Source Data + +### LOAD DATA + +When importing data from a file into OpenMLDB, the typical command used is `LOAD DATA`. For detailed information, please refer to [LOAD DATA INFILE](../openmldb_sql/dml/LOAD_DATA_STATEMENT.md). The data sources and formats that can be employed with `LOAD DATA` are contingent on several factors, including the OpenMLDB version (standalone or cluster), execution mode, and import mode (i.e., the `LOAD DATA` configuration item, `load_mode`). Specifically: + +In cluster version, the default `load_mode` is "cluster", and it can be set to "local". In standalone version, the default `load_mode` is "local," and **"cluster " is not supported**. Consequently, we discuss these scenarios in three distinct contexts: + +| LOAD DATA Type | Support execution mode (import to destination) | Support asynchronous/ synchronous | Support data sources | Support data formats | +| :-------------------------------- | :--------------------------------------------- | :--------------------------------- | :----------------------------------------------------------- | :--------------------------------------------- | +| Cluster version load_mode=cluster | Online, offline | Asynchronous, synchronous | File (with conditional restrictions, please refer to specific documentation) /hdfs/ hive | CSV/ parquet (HIVE source unrestricted format) | +| Cluster version load_mode=local | Online | Synchronous | Client local file | csv | +| Standalone version(only local) | Online | Synchronous | Client local file | csv | + +When the source data for `LOAD DATA` is in CSV format, it's essential to pay special attention to the timestamp column's format. Timestamps can be in "int64" format (referred to as int64 type) or "yyyy-MM-dd'T'HH:mm:ss[.SSS][XXX]" format (referred to as date type). + +| LOAD DATA Type | Support int64 type | Support date type | +| :-------------------------------- | :------------ | :----------- | +| Cluster version load_mode=cluster | **``✓``** | **``✓``** | +| Cluster version load_mode=local | **``✓``** | **``X``** | +| Standalone version(only local) | **``✓``** | **``X``** | + +```{hint} +The CSV file format can be inconvenient in certain situations, and we recommend considering the use of the Parquet format. This approach requires the OpenMLDB cluster version to be in use and necessitates the taskmanager component to be up and running. +``` + +## SQL Restriction + +OpenMLDB does not offer full compatibility with standard SQL, which means that certain SQL queries may not yield the expected results. If you encounter a situation where the SQL execution does not align with your expectations, it's advisable to initially verify whether the SQL adheres to the [Functional Boundary](./function_boundary.md) guidelines. + +## SQL Execution + +All commands within OpenMLDB are SQL-based. If you experience SQL execution failures or other issues (where it's unclear whether the command was executed successfully), consider the following checks: + +1. **SQL Accuracy**: Examine whether there are errors in the SQL syntax. Syntax errors can lead to unsuccessful SQL execution. You can refer to the [SQL Reference](../../openmldb_sql/) to correct any errors. +2. **Execution Status**: Determine if the command has progressed to the execution phase or if it failed to execute. This distinction is crucial for troubleshooting. + +For instance, if you encounter a syntax error prompt, it indicates a problem with the SQL writing, and you should consult the [SQL Reference](../../openmldb_sql/) for guidance on correcting it. + +``` +127.0.0.1:7527/db> create table t1(c1 int; +Error: Syntax error: Expected ")" or "," but got ";" [at 1:23] +create table t1(c1 int; + ^ +``` + +If the command has entered the execution phase but fails or experiences interaction issues, you should clarify the following details: + +- **OpenMLDB Version**: Is OpenMLDB being used in standalone mode or clustered mode? +- **Execution Mode**: What is the execution mode? You can use the `show variable` command in the CLI to retrieve this information. Note that the execution mode of the standalone version is not meaningful. + +Special attention to usage logic is required when working with the cluster version of OpenMLDB. + +### Cluster Version SQL Execution + +#### Offline + +For cluster offline commands, when operating in the default asynchronous mode, sending the command will yield a job ID as a return value. You can utilize `show job ` to inquire about the execution status of the job. + +If the offline job is an asynchronous SELECT query (without saving results), the results will not be displayed on the client (synchronous SELECT queries do display results). Instead, you can retrieve the results through `show joblog `, which comprises two sections: `stdout` and `stderr`. `stdout` contains the query results, while `stderr` contains the job's runtime log. If you discover that the job has failed or its status doesn't align with your expectations, it's essential to carefully review the job run log. + +```{note} +The location of these logs is determined by the `job.log.path` configuration in taskmanager.properties. If you have altered this configuration, you will need to search in the specified destination for the logs. By default, the stdout log can be found at `/work/openmldb/taskmanager/bin/logs/job_x.log`, while the job execution log is situated at `/work/openmldb/taskmanager/bin/logs/job_x_error.log` (note the "error" suffix). + +If the task manager operates in YARN mode rather than local mode, the information in job_x_error.log may be more limited, and detailed error information about the job might not be available. In such instances, you will need to employ the YARN app ID recorded in job_x_error.log to access the actual error details within the YARN system. +``` + +#### Online + +In the online mode of the cluster version, we typically recommend using the `DEPLOY` command to create a deployment, and access to APIServer through HTTP for real-time feature computations. Performing a SELECT query directly online, with CLI or other clients is referred to as "online preview." It's essential to be aware that online preview comes with several limitations, which are outlined in detail in the [Functional Boundary - Cluster Version Online Preview Mode](../function_boundary.md#cluster-version-online-preview-mode) document. Please avoid executing unsupported SQL queries in this context. + +### Provide Replication Scripts + +If you find yourself unable to resolve an issue through self-diagnosis, please provide us with a replication script. A comprehensive replication script should include the following components: + +``` +create database db; +use db; +-- create your table +create table xx (); + +-- offline or online +set @@execute_mode=''; + +-- load data or online insert +-- load data infile '' into table xx; +-- insert into xx values (),(); + +-- query / deploy ... + +``` + +If your question necessitates data for replication, please include the data. For offline data that doesn't support inserting offline, kindly provide a CSV or Parquet data file. If it pertains to online data, you can either provide a data file or directly insert it within the script. + +These data scripts should be capable of executing SQL commands in bulk by using redirection symbols. + +``` +/work/openmldb/bin/openmldb --host 127.0.0.1 --port 6527 < reproduce.sql +/work/openmldb/bin/openmldb --zk_cluster=127.0.0.1:2181 --zk_root_path=/openmldb --role=sql_client < reproduce.sql +``` + +Ensure that the replication script is functional locally to reproduce the issue, and then document the issue or forward it to us for further assistance. + +```{caution} +Please be aware that offline jobs default to asynchronous mode. If you intend to import and query offline, remember to set it to synchronous mode. For additional information, consult [Offline Command Configuration Details](../openmldb_sql/ddl/SET_STATEMENT.md#offline-command-configuration-details). Without this adjustment, querying before the import is completed will not yield meaningful results. +``` + +## Provide Configuration and Logs for Technical Support + +If your SQL execution issue cannot be replicated through replication scripts or if it's not related to SQL execution but rather a cluster management problem, we kindly request that you provide us with configuration details and logs from both the client and server for further investigation. + +Whether you are using Docker or a local cluster setup (where all processes are on the same server), you can swiftly gather configuration, log files, and other information using diagnostic tools. + +You can initiate the OpenMLDB server using either the `init.sh`/`start-all.sh` command for clustered versions or the `init.sh standalone`/`start-standalone.sh` command for standalone versions. After starting the server, you can employ the following commands, which correspond to clustered and standalone versions, respectively. + +``` +openmldb_tool --env=onebox --dist_conf=cluster_dist.yml +openmldb_tool --env=onebox --dist_conf=standalone_dist.yml +``` +`cluster_dist.yml` and `stadnalone_dist.yml` can be found in the `/work/` directory within the Docker container. Alternatively, you can copy the yml file from the [GitHub directory](https://github.com/4paradigm/OpenMLDB/tree/main/demo) for your use. + +If you are working with a distributed cluster, it's essential to have SSH password-free configuration in place for smooth usage of the diagnostic tools. Please refer to the [Diagnostic Tool documentation](../maintain/diagnose.md) for guidance on setting this up. + +If your environment doesn't allow for SSH password-free configuration, please manually collect the configuration details and logs as needed. diff --git a/docs/en/quickstart/cli_tutorial.md b/docs/en/quickstart/cli.md similarity index 100% rename from docs/en/quickstart/cli_tutorial.md rename to docs/en/quickstart/cli.md diff --git a/docs/en/quickstart/function_boundary.md b/docs/en/quickstart/function_boundary.md new file mode 100644 index 00000000000..9c2c0b7ae14 --- /dev/null +++ b/docs/en/quickstart/function_boundary.md @@ -0,0 +1,165 @@ +# Functional Boundary + +This article will introduce the functional boundary of OpenMLDB SQL. + +```{note} +If you have any questions about SQL statements, please refer to OpenMLDB SQL or directly use the search function to search. +``` + +## System Configuration - TaskManager + +You can configure the TaskManager to define various settings, including the offline storage address (`offline.data.prefix`) and the Spark mode required for offline job computation (`spark.master`), among others. + +- `offline.data.prefix`: This can be configured as either a file path or an HDFS path. It is recommended to use an HDFS path for production environments, while a local file path can be configured for testing environments (specifically for onebox, such as running within a Docker container). Note that using a file path as offline storage will not support distributed deployment with multiple Task Managers (data won't be transferred between Task Managers). If you plan to deploy Task Managers on multiple hosts, please use storage media like HDFS that can be accessed simultaneously by multiple hosts. If you intend to test the collaboration of multiple Task Managers, you can deploy multiple Task Managers on a single host and use a file path as offline storage. +- `spark.master=local[*]`: The default Spark configuration is in `local[*]` mode, which automatically binds CPU cores. If offline tasks are found to be slow, it is recommended to use the Yarn mode. After changing the configuration, you need to restart the Task Manager for the changes to take effect. For more configurations, please refer to [master-urls](https://spark.apache.org/docs/3.1.2/submitting-applications.html#master-urls). + +### spark.default.conf + +More optional configurations can be written in the `spark.default.conf` parameter in the format of `k1=v1;k2=v2`. For example: + +```Plain +spark.default.conf=spark.port.maxRetries=32;foo=bar +``` + +`spark.port.maxRetries`: The default is set to 16, and you can refer to [Spark Configuration](https://spark.apache.org/docs/3.1.2/configuration.html). Each offline job is associated with a Spark UI, corresponding to a port. Each port starts from the default initial port and increments by one for retry attempts. If the number of concurrently running jobs exceeds `spark.port.maxRetries`, the number of retries will also exceed `spark.port.maxRetries`, causing job startup failures. If you need to support a larger job concurrency, configure a higher value for `spark.port.maxRetries` and restart the Task Manager to apply the changes. + +## DDL Boundary - DEPLOY Statement + +You can deploy an online SQL solution using the `DEPLOY ` command. This operation automatically parses the SQL statement and helps create indexes (you can view index details using `DESC `). For more information, please refer to the [DEPLOY STATEMENT](../openmldb_sql/deployment_manage/DEPLOY_STATEMENT.md) documentation. + +The success of the deployment operation is dependent on the presence of online data in the table. + +### Long Window SQL + +Long Window SQL: This refers to the `DEPLOY` statement with the `OPTIONS(long_windows=...)` configuration item. For syntax details, please refer to [Long Window](../openmldb_sql/deployment_manage/DEPLOY_STATEMENT.md#long-window-optimazation). Deployment conditions for long-window SQL are relatively strict, and it's essential to ensure that the tables used in the SQL statements do not contain online data. Otherwise, even if deploying SQL that matches the previous one, the operation will still fail. + +### Normal SQL + +- If the relevant index already exists before deployment, the `DEPLOY` operation will not create the index. The `DEPLOY` operation will succeed regardless of whether there is online data in the table. +- If a new index needs to be created during deployment, and there is already online data in the table, the `DEPLOY` operation will fail. + +There are two solutions: + +1. Strictly perform `DEPLOY` before importing online data, and avoid executing `DEPLOY` after online data is present in the table. +2. The `CREATE INDEX` statement can automatically import existing online data (data from existing indexes) when creating a new index. If it is necessary to execute `DEPLOY` when the table already has online data, you can manually execute a `CREATE INDEX` to create the required index (the new index will already have data), and then execute `DEPLOY` (in this case, `DEPLOY` will not create a new index, and the manually created indexes will be used directly for computation). + +```{note} +How can you determine which indexes to create? + +Currently, only the Java SDK supports this feature, and all the required indexes can be obtained through `SqlClusterExecutor.genDDL`. However, you will need to manually convert them into `CREATE INDE`X statements as `genDD`L provides table creation statements. In the future, it will support ** directly obtaining index creation statements** or **automatically importing data into a new index** with `DEPLOY`. +``` + +## DML Boundary + +### Offline Information + +There are two types of paths in the offline information of a table: `offline_path` and `symbolic_paths`. `offline_path` is the actual storage path for offline data, while `symbolic_paths` are soft link paths for offline data. Both paths can be modified using the `LOAD DATA` command, and `symbolic_paths` can also be modified using the `ALTER` statement. + +The key difference between `offline_path` and `symbolic_paths` is that `offline_path` is the path owned by the OpenMLDB cluster. If a hard copy is implemented, data will be written to this path. On the other hand, `symbolic_paths` are paths outside the OpenMLDB cluster, and soft copies will add a path to this information. When querying offline, data from both paths will be loaded. Both paths use the same format and read options and do not support paths with different configurations. + +Therefore, if `offline_path` already exists offline, the `LOAD DATA` command can only modify `symbolic_paths`. If `symbolic_paths` already exist offline, the `LOAD DATA` command can be used to modify both `offline_path` and `symbolic_paths`. + +The `errorifexists` option will raise an error if there is offline information in the table. It will raise errors if performing hard copy when there's soft links, or performing soft copy when a hard copy exisits. + +### LOAD DATA + +Regardless of whether data is imported online or offline using the `LOAD DATA` command, it is considered an offline job. The format rules for source data are the same for both offline and online scenarios. + +It is recommended to use HDFS files as source data. This approach allows for successful import whether TaskManager is in local mode, Yarn mode, or running on another host. However, if the source data is a local file, the ability to import it smoothly depends on the mode of TaskManager and the host where it is running: + +- In local mode, TaskManager can successfully import source data only if the source data is placed on the same host as the TaskManager process. +- When TaskManager is in Yarn mode (both client and cluster), a file path cannot be used as the source data address because it is not known on which host the container is running. + +### DELETE + +In tables with multiple indexes in the online storage, a `DELETE` operation may not delete corresponding data in all indexes. Consequently, there may be situations where data has been deleted, but the deleted data can still be found. + +For example: + +```SQL +create database db; +use db; +create table t1(c1 int, c2 int,index(key=c1),index(key=c2)); +desc t1; +set @@execute_mode='online'; +insert into t1 values (1,1),(2,2); +delete from t1 where c2=2; +select * from t1; +select * from t1 where c2=2; +``` + +The results are as follows: + +```Plain + --- ------- ------ ------ --------- + Field Type Null Default + --- ------- ------ ------ --------- + 1 c1 Int YES + 2 c2 Int YES + --- ------- ------ ------ --------- + --- -------------------- ------ ---- ------ --------------- + name keys ts ttl ttl_type + --- -------------------- ------ ---- ------ --------------- + 1 INDEX_0_1668504212 c1 - 0min kAbsoluteTime + 2 INDEX_1_1668504212 c2 - 0min kAbsoluteTime + --- -------------------- ------ ---- ------ --------------- + -------------- + storage_mode + -------------- + Memory + -------------- + ---- ---- + c1 c2 + ---- ---- + 1 1 + 2 2 + ---- ---- + +2 rows in set + ---- ---- + c1 c2 + ---- ---- + +0 rows in set +``` + +Explanation: + +Table `t1` has multiple indexes (which may be automatically created during `DEPLOY`). If you run `delete from t1 where c2=2`, it only deletes data in the second index, while the data in the first index remains unaffected. Therefore, if you subsequently run `select * from t1` and it uses the first index, there are two pieces of data that haven't been deleted. `select * from t1 where c2=2` uses the second index, and the result is empty, with data being successfully deleted. + +## DQL Boundary + +The supported query modes (i.e. `SELECT` statements) vary depending on the execution mode: + +| Execution Mode | Query Statement | +| -------------- | ------------------------------------------------------------ | +| Offline Mode | Batch query | +| Online Mode | Batch query (also known as online preview mode, only supports partial SQL) and request query (also known as online request mode) | + +### Online Preview Mode + +In OpenMLDB CLI, executing SQL in online mode puts it in online preview mode. Please note that online preview mode has limited support; you can refer to the [SELECT STATEMENT](../openmldb_sql/dql/SELECT_STATEMENT) documentation for more details. + +Online preview mode is primarily for previewing query results. If you need to run complex SQL queries, it's recommended to use offline mode. To query complete online data, consider using a data export tool such as the `SELECT INTO` command. Keep in mind that if the online table contains a large volume of data, it might trigger data truncation, and executing `SELECT * FROM table` could result in some data not being returned. + +Online data is usually distributed across multiple locations, and when you run `SELECT * FROM table`, it retrieves results from various Tablet Servers without performing global sorting. As a result, the order of data will be different with each execution of `SELECT * FROM table`. + +### Offline Mode and Online Request Mode + +In the [full process](./concepts/modes.md) of feature engineering development and deployment, offline mode and online request mode play prominent roles: + +- Offline Mode Batch Query: Used for offline feature generation. +- Query in Online Request Mode: Employed for real-time feature computation. + +While these two modes share the same SQL statements and produce consistent computation results, due to the use of two different execution engines (offline and online), not all SQL statements that work offline can be deployed online. SQL that can be executed in online request mode is a subset of offline executable SQL. Therefore, it's essential to test whether SQL can be deployed using `DEPLOY` after completing offline SQL development. + +## Offline Command Synchronization Mode + +All offline commands can be executed in synchronous mode using `set @@sync_job=true;`. In this mode, the command will only return after completion, whereas in asynchronous mode, job info is immediately returned, requires usage of `SHOW JOB ` to check the execution status of the job. In synchronous mode, the return values differ depending on the command. + +- DML commands like `LOAD DATA` and DQL commands like `SELECT INTO` return the ResultSet of Job Info. These results are identical to those in asynchronous mode, with the only difference being the return time. +- Normal `SELECT` queries in DQL return Job Info in asynchronous mode and query results in synchronous mode. However, support for this feature is currently incomplete, as explained in [Offline Sync Mode-select](../openmldb_sql/dql/SELECT_STATEMENT.md#offline-sync-mode-select). The results are in CSV format, but data integrity is not guaranteed, so it's not recommended to use as accurate query results. + - In the CLI interactive mode, the results are printed directly. + - In the SDK, ResultSet is returned, the query result as a string. Consequently, it's not recommended to use synchronous mode queries in the SDK and process their results. + +Synchronous mode comes with timeout considerations, which are detailed in [Configuration](../openmldb_sql/ddl/SET_STATEMENT.md#offline-command-configuaration-details). diff --git a/docs/en/quickstart/index.rst b/docs/en/quickstart/index.rst index aefceb8f206..aac5878eede 100644 --- a/docs/en/quickstart/index.rst +++ b/docs/en/quickstart/index.rst @@ -7,5 +7,8 @@ Quickstart openmldb_quickstart concepts/index - cli_tutorial + cli sdk/index + beginner_must_read + function_boundary + diff --git a/docs/zh/quickstart/beginner_must_read.md b/docs/zh/quickstart/beginner_must_read.md index 37f1b57ae8e..f5ba729613f 100644 --- a/docs/zh/quickstart/beginner_must_read.md +++ b/docs/zh/quickstart/beginner_must_read.md @@ -90,7 +90,7 @@ create table t1(c1 int; ``` create database db; use db; --- create youer table +-- create your table create table xx (); -- offline or online diff --git a/docs/zh/quickstart/function_boundary.md b/docs/zh/quickstart/function_boundary.md index 801d5d1b111..5d656f8eb75 100644 --- a/docs/zh/quickstart/function_boundary.md +++ b/docs/zh/quickstart/function_boundary.md @@ -147,7 +147,7 @@ OpenMLDB CLI 中在线模式下执行 SQL,均为在线预览模式。在线预 ### 离线模式与在线请求模式 -在[特征工程开发上线全流程](../tutorial/concepts/modes.md#11-特征工程开发上线全流程)中,主要使用离线模式和在线请求模式。 +在[特征工程开发上线全流程](./concepts/modes.md)中,主要使用离线模式和在线请求模式。 - 离线模式的批查询:离线特征生成 - 在线请求模式的请求查询:实时特征计算 @@ -164,4 +164,4 @@ OpenMLDB CLI 中在线模式下执行 SQL,均为在线预览模式。在线预 - CLI是交互模式,所以将结果直接打印。 - SDK中,返回的是一行一列的ResultSet,将整个查询结果作为一个字符串返回。所以,不建议SDK使用同步模式查询,并处理其结果。 -同步模式涉及超时问题,详情见[调整配置](../../openmldb_sql/ddl/SET_STATEMENT.md#离线命令配置详情)。 +同步模式涉及超时问题,详情见[调整配置](../openmldb_sql/ddl/SET_STATEMENT.md#离线命令配置详情)。 From 7aed9c724b2e41f186f5fc7811b11f2950e35b3e Mon Sep 17 00:00:00 2001 From: TanZiYen <104113819+TanZiYen@users.noreply.github.com> Date: Thu, 12 Oct 2023 14:18:19 +0800 Subject: [PATCH 53/63] docs: change for index file of integration folder (#3485) --- docs/en/integration/deploy_integration/index.rst | 14 ++++++++++++++ docs/en/integration/develop/index.rst | 8 ++++++++ docs/en/integration/index.rst | 13 +++++++++++++ docs/en/integration/online_datasources/index.rst | 13 +++++++++++++ 4 files changed, 48 insertions(+) create mode 100644 docs/en/integration/deploy_integration/index.rst create mode 100644 docs/en/integration/develop/index.rst create mode 100644 docs/en/integration/index.rst create mode 100644 docs/en/integration/online_datasources/index.rst diff --git a/docs/en/integration/deploy_integration/index.rst b/docs/en/integration/deploy_integration/index.rst new file mode 100644 index 00000000000..15bff333619 --- /dev/null +++ b/docs/en/integration/deploy_integration/index.rst @@ -0,0 +1,14 @@ +============================= +dispatch +============================= + +.. toctree:: + :maxdepth: 1 + + airflow_provider_demo + dolphinscheduler_task_demo + OpenMLDB_Byzer_taxi + + + + diff --git a/docs/en/integration/develop/index.rst b/docs/en/integration/develop/index.rst new file mode 100644 index 00000000000..13e8fad3619 --- /dev/null +++ b/docs/en/integration/develop/index.rst @@ -0,0 +1,8 @@ +============================= +Develop +============================= + +.. toctree:: + :maxdepth: 1 + + jupyter_notebook \ No newline at end of file diff --git a/docs/en/integration/index.rst b/docs/en/integration/index.rst new file mode 100644 index 00000000000..023bd3c9ab9 --- /dev/null +++ b/docs/en/integration/index.rst @@ -0,0 +1,13 @@ +============================= +Upstream and downstream ecology +============================= + +.. toctree:: + :maxdepth: 1 + + online_datasources/index + offline_data_sources/index + deploy_integration/index + develop/index + + diff --git a/docs/en/integration/online_datasources/index.rst b/docs/en/integration/online_datasources/index.rst new file mode 100644 index 00000000000..7b2232ef05b --- /dev/null +++ b/docs/en/integration/online_datasources/index.rst @@ -0,0 +1,13 @@ +============================= +online data source +============================= + +.. toctree:: + :maxdepth: 1 + + kafka_connector_demo + pulsar_connector_demo + rocketmq_connector + + + From 50a11edcce81cd92fd3b34af84e7bb3a13179e3f Mon Sep 17 00:00:00 2001 From: HuangWei Date: Thu, 12 Oct 2023 16:42:31 +0800 Subject: [PATCH 54/63] docs: add case when desc (#3523) --- .../functions_and_operators/operators.md | 27 ++++++++++++------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/docs/zh/openmldb_sql/functions_and_operators/operators.md b/docs/zh/openmldb_sql/functions_and_operators/operators.md index e5a7ca86afe..31d79184bd8 100644 --- a/docs/zh/openmldb_sql/functions_and_operators/operators.md +++ b/docs/zh/openmldb_sql/functions_and_operators/operators.md @@ -1,4 +1,4 @@ -# 运算符 +# 表达式和运算符 ## 运算符优先级 @@ -19,9 +19,7 @@ %left UNARY_PRECEDENCE // For all unary operators, +, -, ~ ``` -## 各类运算 - -### 1. 比较运算 +## 比较运算 | 操作符名 | 功能描述 | | :-------------- | :--------------------- | @@ -37,7 +35,7 @@ | `ILIKE` | 模糊匹配, 大小写不敏感 | | `RLIKE` | 正则表达式匹配 | -### 2. 逻辑运算 +## 逻辑运算 | 操作符名 | 功能描述 | | :---------- | :------- | @@ -46,7 +44,7 @@ | `XOR` | 逻辑与或 | | `NOT`, `!` | 逻辑非, unary operator | -### 3. 算术运算 +## 算术运算 | 操作符名 | 功能描述 | | :--------- | :------------------------------------------------------- | @@ -59,7 +57,7 @@ | `+` | Unary plus | | `-` | Unary minus, 只支持数值型操作数-number | -### 4. 位运算 +## 位运算 | 操作符名 | Description | | :------- | :---------- | @@ -68,7 +66,7 @@ | `^` | Bitwise XOR | | `~` | Bitwise NOT, unary operator | -### 5. 类型运算和函数 +## 类型运算和函数 | 操作符名 | Description | | :------------- | :--------------------------------------------------------- | @@ -97,7 +95,7 @@ SELECT INT(1.2); X:表示从原类型转换为目标类型的转换是不支持的 -| src\|dist | bool | smallint | int | float | int64 | double | timestamp | date | string | +| src\|dst | bool | smallint | int | float | int64 | double | timestamp | date | string | | :------------ | :----- | :------- | :----- | :----- | :----- | :----- | :-------- | :----- | :----- | | **bool** | Safe | Safe | Safe | Safe | Safe | Safe | UnSafe | X | Safe | | **smallint** | UnSafe | Safe | Safe | Safe | Safe | Safe | UnSafe | X | Safe | @@ -114,3 +112,14 @@ X:表示从原类型转换为目标类型的转换是不支持的 | 操作符名 | 功能描述 | | :------- | :------------------------ | | `=` | 赋值 (可用于 SET 语句中 ) | + +## 条件表达式 + +### CASE 表达式 + ```sql + SELECT case 'bb' when 'aa' then 'apple' else 'nothing' end; -- SIMPLE CASE WHEN + SELECT case + when 'bb'='aa' then 'apple' + when 'bb'='bb' then 'banana' + else 'nothing' end; -- SEARCHED CASE WHEN + ``` From db52ce909d07ffa0a07bbee97cd07fffe7e1c009 Mon Sep 17 00:00:00 2001 From: TanZiYen <104113819+TanZiYen@users.noreply.github.com> Date: Fri, 13 Oct 2023 17:46:23 +0800 Subject: [PATCH 55/63] docs: change_for_status_and_index_of_maintain_folder (#3478) * Change for status and index of maintain folder * Update status.md * Update status.md --------- Co-authored-by: Siqi Wang --- docs/en/maintain/images/showtablestatus.png | Bin 0 -> 145996 bytes docs/en/maintain/index.rst | 2 ++ docs/en/maintain/status.md | 33 ++++++++++++++++++++ 3 files changed, 35 insertions(+) create mode 100644 docs/en/maintain/images/showtablestatus.png create mode 100644 docs/en/maintain/status.md diff --git a/docs/en/maintain/images/showtablestatus.png b/docs/en/maintain/images/showtablestatus.png new file mode 100644 index 0000000000000000000000000000000000000000..a5295207a77cbda8469eb2239973c98e1dbb9877 GIT binary patch literal 145996 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 literal 0 HcmV?d00001 diff --git a/docs/en/maintain/index.rst b/docs/en/maintain/index.rst index 4fa52ab9eef..e8c3d324877 100644 --- a/docs/en/maintain/index.rst +++ b/docs/en/maintain/index.rst @@ -10,6 +10,8 @@ Maintenance backup monitoring cli + status + multi_cluster faq scale diagnose diff --git a/docs/en/maintain/status.md b/docs/en/maintain/status.md new file mode 100644 index 00000000000..9fde5d2d96f --- /dev/null +++ b/docs/en/maintain/status.md @@ -0,0 +1,33 @@ +# Maintenance Command Status + +After executing maintenance operations, you can monitor the outcomes of these operations, along with the current health status of the database, through a series of commonly used command combinations. By sequentially running the `showopstatus` and `showtablestatus` commands, you can ultimately verify the successful completion of the relevant maintenance tasks. + +## Step 1: View Command Status with `showopstatus` + +The `showopstatus` command serves the purpose of observing the execution status of specific maintenance commands. The current NS client supports this command to track the status of maintenance commands like `addreplica`, `delreplica`, `migrate`, `offlineendpoint`, `recoverendpoint`, `changeleader`, and `recovertable`. For a comprehensive understanding of these supported maintenance commands, refer to the [Operations CLI](./cli.md) document. + +You can monitor the status of relevant commands by utilizing the `showopstatus` command of the [OpenMLDB Operations Tool](./openmldb_ops.md) `openmldb_ops.py`, as shown in the example below: + +```bash +python tools/openmldb_ops.py --openmldb_bin_path=./bin/openmldb --zk_cluster=127.0.0.1:2181 --zk_root_path=/openmldb --cmd=showopstatus +``` + +Due to the asynchronous nature of maintenance command execution, you might need to run the `showopstatus` command multiple times to confirm the final state. The state transitions for related commands can occur in three stages: + +1. kInited => kDoing => kDone: Indicates successful command execution. +2. kInited => kDoing => kFailed: Denotes a failed command execution. +3. kInited => kCancelled: This state may arise after manually executing the `cancelop` command. + +Once the command running status changes to `kDone`, it signifies the successful execution of relevant commands. You can then proceed to the subsequent steps and use the `showtablestatus` command to inspect the status of tables. + +## Step 2: View Table Status with `showtablestatus` + +After successfully executing the relevant maintenance commands, it's crucial to perform an additional verification to identify any anomalies in the table status. This verification can be conducted using the `showtablestatus` command within the [OpenMLDB Operations and Maintenance Tool](./openmldb_ops.md). For instance: + +```bash +python tools/openmldb_ops.py --openmldb_bin_path=./bin/openmldb --zk_cluster=127.0.0.1:2181 --zk_root_path=/openmldb --cmd=showtablestatus +``` + +Upon execution, this command will generate a series of table-related information. Of particular importance is the column labeled `Partition_unalive`. If this column's value is 0, it signifies that the table is in a normal state. Conversely, any non-zero value indicates the presence of an anomaly, as illustrated in the following example: + +![image-20230113144942187](images/showtablestatus.png) From e9f01ddce7e3f88a8c8c712cc8edc299f7ad6b8a Mon Sep 17 00:00:00 2001 From: TanZiYen <104113819+TanZiYen@users.noreply.github.com> Date: Mon, 16 Oct 2023 16:39:07 +0800 Subject: [PATCH 56/63] docs: change_for_jupyter_notebook_of_integration_develop_folder (#3472) * Change for jupyter_notebook of integration develop folder * Update jupyter_notebook.md * Update jupyter_notebook.md --------- Co-authored-by: Siqi Wang --- docs/en/integration/develop/images/muti.png | Bin 0 -> 174040 bytes docs/en/integration/develop/images/single.png | Bin 0 -> 67164 bytes .../develop/images/support_function.png | Bin 0 -> 113659 bytes .../integration/develop/jupyter_notebook.md | 64 ++++++++++++++++++ 4 files changed, 64 insertions(+) create mode 100644 docs/en/integration/develop/images/muti.png create mode 100644 docs/en/integration/develop/images/single.png create mode 100644 docs/en/integration/develop/images/support_function.png create mode 100644 docs/en/integration/develop/jupyter_notebook.md diff --git a/docs/en/integration/develop/images/muti.png b/docs/en/integration/develop/images/muti.png new file mode 100644 index 0000000000000000000000000000000000000000..a07578729d140a93bc018995a086cbbed9130e63 GIT binary patch literal 174040 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

    0#vl&!0b0uHw_v_G3uHA&7~o^d`W& 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 wYu=9t$* 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

    ?@SteSw9}o+aES6eKwJ*eoGD(I*huvPcSLT< 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 m_El3w$X|a~}{3uP+L;nZn{oir` literal 0 HcmV?d00001 diff --git a/docs/en/integration/develop/jupyter_notebook.md b/docs/en/integration/develop/jupyter_notebook.md new file mode 100644 index 00000000000..b15488958a8 --- /dev/null +++ b/docs/en/integration/develop/jupyter_notebook.md @@ -0,0 +1,64 @@ +# Jupyter Notebook + +Jupyter Notebook offers various functionalities, such as data computation, code development, document editing, code execution, and result display, through a browser-based web page. It is currently one of the most popular and user-friendly development environments. This article introduces the seamless integration of OpenMLDB and Notebook, harnessing the functional features of OpenMLDB and the convenience of Notebook to create a fast and user-friendly machine-learning development environment. + +## Integration of the Magic Function + +The SQL magic function is an extension of Notebook that allows users to execute SQL statements directly in a Notebook cell without writing complex Python code. It also supports customized output. OpenMLDB provides a standard SQL magic function that allows users to write and run OpenMLDB-supported SQL statements directly in the Notebook. These statements are submitted to OpenMLDB for execution, and the results are previewed and displayed in the Notebook. + +### Register OpenMLDB SQL Magic Function + +To support OpenMLDB magic function in Notebook, register as follows: + + ```Python + import openmldb + db = openmldb.dbapi.connect(database='demo_db',zk='0.0.0.0:2181',zkPath='/openmldb') + openmldb.sql_magic.register(db) + ``` + +### Execute a Single SQL Statement + +Developers can use the prompt `%` to execute a single-line SQL statement, as shown in the following figure. + +![img](images/single.png) + +### Execute multiple SQL statement + +Developers can also use the prompt `%%` to write multi-line SQL statements, as shown in the following figure. + +![img](images/muti.png) + +Please note that currently, executing multiple SQL statements simultaneously within a Notebook cell is not supported. Each SQL statement needs to be executed separately in different cells. + +### Magic Function + +The SQL magic function provided by OpenMLDB can execute all supported SQL syntax, including the unique offline mode of OpenMLDB, which allows for asynchronously submitting complex big data SQL statements to the offline execution engine, as shown in the following figure. + +![img](images/support_function.png) + +For more detailed instructions on using the OpenMLDB magic function, please refer to [The Use of Notebook Magic Function](https://openmldb.ai/docs/en/main/quickstart/sdk/python_sdk.html#notebook-magic-function). + +## Integration of OpenMLDB Python SDK with Notebook + +Notebook supports the Python runtime kernel, enabling the import and usage of various Python libraries through import statements. OpenMLDB provides a fully functional Python SDK that can be called within Notebook. OpenMLDB Python SDK not only offers a DBAPI based on the Python PEP249 standard but also supports the mainstream SQLAlchemy interface, which enables connecting to existing OpenMLDB clusters with just one line of code. + +### The Use of OpenMLDB DBAPI + +Using the DBAPI interface is straightforward. You only need to specify the ZooKeeper address and node path for connection. Upon successful connection, corresponding log information will be displayed. You can call the DBAPI interface of the OpenMLDB Python SDK within Notebook for development, as detailed in [The Use of OpenMLDB DBAPI](https://openmldb.ai/docs/en/main/quickstart/sdk/python_sdk.html#openmldb-dbapi). + +```Python +import openmldb.dbapi +db = openmldb.dbapi.connect('demo_db','0.0.0.0:2181','/openmldb') +``` + +### Using OpenMLDB SQLAlchemy + +Using SQLAlchemy is also simple. You can establish the connection by specifying the URI of OpenMLDB through the SQLAlchemy library. Alternatively, you can connect to a standalone OpenMLDB database using IP and port as parameters, as shown below. + +```Python +import sqlalchemy as db +engine = db.create_engine('openmldb://demo_db?zk=127.0.0.1:2181&zkPath=/openmldb') +connection = engine.connect() +``` + +After a successful connection, development can be carried out through the SQLAlchemy interface of the OpenMLDB Python SDK, as detailed in [Using OpenMLDB SQLAlchemy](https://openmldb.ai/docs/en/main/quickstart/sdk/python_sdk.html#openmldb-sqlalchemy). From 8c8d07049cc96c8c658cf391254b91cca8cd9fdf Mon Sep 17 00:00:00 2001 From: TanZiYen <104113819+TanZiYen@users.noreply.github.com> Date: Mon, 16 Oct 2023 16:41:22 +0800 Subject: [PATCH 57/63] docs: update_openmldb_quickstart_of_quickstart_folder (#3486) * docs-update-openmldb_quickstart-of-quickstart-folder * Update openmldb_quickstart.md * Docs: Update-modes_flow-image * Update openmldb_quickstart.md * Update openmldb_quickstart.md * Delete unused file --------- Co-authored-by: Siqi Wang --- .../quickstart/concepts/images/modes-flow.png | Bin 0 -> 333650 bytes docs/en/quickstart/images/cli_cluster.png | Bin 0 -> 358166 bytes docs/en/quickstart/images/state_finished.png | Bin 0 -> 170727 bytes docs/en/quickstart/openmldb_quickstart.md | 79 ++++++++---------- 4 files changed, 37 insertions(+), 42 deletions(-) create mode 100644 docs/en/quickstart/concepts/images/modes-flow.png create mode 100644 docs/en/quickstart/images/cli_cluster.png create mode 100644 docs/en/quickstart/images/state_finished.png diff --git a/docs/en/quickstart/concepts/images/modes-flow.png b/docs/en/quickstart/concepts/images/modes-flow.png new file mode 100644 index 0000000000000000000000000000000000000000..c4856e6a5f95c9e988d528fb09ecea7d581cbfaf GIT binary patch literal 333650 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 N@ksFDnuF)l{tqT=w+R3M literal 0 HcmV?d00001 diff --git a/docs/en/quickstart/images/cli_cluster.png b/docs/en/quickstart/images/cli_cluster.png new file mode 100644 index 0000000000000000000000000000000000000000..8a90127e3b266a89f4010093e7c8701a75b5ff7e GIT binary patch literal 358166 zcmbrl1yodTyElvqilT%8(o&*QN_U4ygVNndcY{cGNT;-PcS=eO&Cm@qbPb)~J^ypw zZ+-9ip7S2x^|*$`IBSNzXW!Spu3z1OveIHt9uYi3K|y&U{!v&S1qFj01qJmT`hD=q zhZC+T6qLt$CPG58;zB~NWNj=BP0S2XP(B96#63_}=)wI4kNx~q_0?T@LCcJfPhL^^ z;0o~Pzj{aZkTUw^v+qH~8lSCp$b$?-ltk3+=)M)A;WPy6I?@qhyrbD#e#iQG-Urq0 zIA%|(xel?wWHgX$%V@htjB@nZErFJF{J~Ft)i*K$j}1a4B#a_S?xEhGl-{Ro<|kX} z=B(XXYM|8s}-Csn}v(un zoJBF}t0UT(cldmKiN+zzjye`?6AglZ;m8hxR z`y|~DY!3Wl#gej@mJbr>qD1|^ZtLMs9R{ElH5c-Kz-CZ<7rU=prLOWuI%s6|!3@>n z>80h}I!($oN~}qow_Tl&JIJ5=`kLx?PYaLen>SAKsOT7fcQC7=<Z7`L*te zK9h)Ch662cJ~q8{s5`f);>)zGgJ*;NYvExNXWn>TFI&-czM(~z8hZ7-!uvQ~Pf)*@ zl0$uW--~D!1;=Xi(|ysblhcn&EI7}-`N#sl=~}59dzPR-7dY=0$b)U(FTZpRz7SjW z>W;wRXa3`V+Iu56@_S0YmEujL8&d*u`{$MCNi`3STs?pO2@Z=bt(?BY4&wy|EAN}1tQlX8U;cMs_Lm)&n|3)2am zMB5HXd@wPTdBei|UQMut&v__pHR=3pnqxfSbL~XEW4-6liI?DTrQq@X73o;1I0>5$ zUs_Bzi~wcsJngghvHlJI4^TE8gJ2(yqo!_)`QzcdeX#Z>qBlERTU=KvxYs1#?m?}R z3V8d77}~q6M~P3ir@2T8vbOP^h#o_O82dP(#kx7FF|q58d=rHc)eL4G!; zCoVXsWMpv72O(M zS$94n$QMjY=Xu#`_x`nEXq}E(NN}glhBE8xZbLl-Wi-XmaUH4swC3-5rS#jx(jEBh z7>?ei?2lx+(9WK_u{NV>h3|E29VlKs@*v&o;=@@-wf#=>JFS%>gklvt=jo@1FMM`< zynTLHvDss5J_{6nJsi1?;iyOOA&%X<)~7bI)_+pcjGCA7B;q5bLKMy0i05L8(k!O34NU;3ml)slJKBm6O{gtXJ%q7aj|3Y3&tXs5O#;7n%*r-@Kdq6XViSyKc ze)zEoJu^L>cTN^v_Rde1QHx>Ckqx^CF6cZdnjf`3m87TrT*z!0H(1Aj&dauX&HMz2gl)CgV2%@zAR6nmhNvl??+w@{A|?@ z{7vs&abM<%xF4q9lAmBZnxEPenU}o!+K<)l$B}9G8=2`P>)XA=2_*`360H+m6XnCN z!nY?RByh#geJR6K%*d--sH-da;B|PypnBJAcjJH?UxzNbQGoHEWiopqdMsx>#xcqc zVSRbS^6u5d9#@4}lUkG1jYp&RM;i=l6N0CdEAkAJ`lS=?pRf=Ry#M^Z(rmpsy(J+N zMh|-kW9qL-)Ox>HR$3WQ8KF{G!LBoG&{O%itiMX#+!b^Lw=0M}6HC=A(<@pbrmj~g9etkrHMd|u*r?hz6(I_j zp#K$I6FiySyOu$-xw-PrdTxE1H{^Z4WxSp8aVen%Gh3Kiq*~F;^-}-Rhjs}+w#S~l zZ5F~80WT<|!_QWCcT)G7A=f7#+aoi@VnfUb&TIUG{aZ78SMsUnnA&W^j56UE3`z`< z6R%5><{$5Un8#>RYvj8)yZGb2>Bi~4I3obp&ZGmF$a-N4<|P$UeR>Bksp4#`M6}B_w}5 zMRMq~(%-YT4dceL$BOYMdFR zq#rwWcd*(EN4EJ79-pol^aAx(x)np+lixGy7`AN)i!Nt%Xatu05d0BKH7D)xN%3_} z)_S&n)>u}f_!9|@gnR-v-PuBG_-OwKrAg>U)=~7)&IZp)dv^)`LgZ44T%xu6_Z1(L zX9LpAN%4FKe<+*8uf#8z9wy`^G%)h*ubhovrP69b{R%DaD3vPiOTHvmr`vvx7Ni)I zs{g>Sq~9a)S$xI2Tm>T9Vr)n8+Nk3wygIqMB-@46&b6ICTYWN+1qFWkKeX(WEYUD= zHEK2KRMt8T*gKk-yqq%`6{c)861s(A{^!r0hx&@{uUvm05kF5k^pKO4`!YO}_g3Ll zhAIAaii1vl-%;mi+HqZ6pzQ@1DPp>^)QFtVL(-scu;t}3lgui@b?vLBQ}^F$H1#I5#gfBwaI z;kZG8R#8GF%*wls%@ja5-x2| z?RJ5#-{me@9xm>V-n9bb@z6;RN0oNqbaEst*vx^=eNzleo|({g>{sb4f+C|lO! zft16FS2(&!t0lkIx$Djb78L2J%}9%;aofII9t#{z9_=1LP33Td-fpMcZreVAyAMog z?KfNPEA44vRi9UmYaKOBI`!Z-ur~H(ejG8OTT-PZ)9ir|?RqjU zwFJqnIkap#D_XCapRwJ;-i@9cZ&9-zywW`08)(^+JO33ni`C${B(Ur~gN25b#v{*t zF;uqr;o7?MSphYR3{NV_ZpeD$QTG`iXKGLCu|v#_$v)fOS z;zsjuN|gMTtm6IeW9WwKX?6af*{F zSEh!-EHj+UGVY5$c%RSb*G6KX75!Ph@j$v($C*9G7>Sct$&BZnK$?L0UnH9OfOVJ| zD2p3PNuj(0&(Tp(?+~D%foFHX5APkq|M$7*oi`}={(1c_3W~o83hIBpM;iP_{)+%V z$j|)8@4G+m{;zjnu>ZXGzn-HakAXr+Xg3IcJ+%6$VvB-;ONIQoBQ8(5kAlLFA}%cO z+3C)9%7fZ*;dy_tj_<#;875_)7rxruBoESn2APWSDVC9)6gMbt2Q_7ws>tGY`ZYP0 z>D{5h{k*L~Tae+=m}whN7-btJE?qaVz&+SxqOkBbA^8fHwh{87Me6*=vpYE0|IWj! z{+aQYQ=&Kb$Y0(4w;uZs?mSE$e=f|A`tLjl{Jxv>)%enWTgMCF9j}0zQC)h*19b>MB*UUO!v)Bnf%1p)$FNGjyb#01nCscgpJ&^ z=BYIeeB6Jzm|oa-FL)?@65IafqUe(iaTdW9i*lwdQs-x62%A%HVpYGst(eesO$dBO zoj5APH+Qr--bdtdwggLYod0m|{$uok>E_E_)9M)uE=0#OnfQB{?625U8}DSU^$}`G zJmIrizC<7gmmZ z$}ycNHcWMfeE^?saK61hg%Dac>X5i@e?S~hXt6z<&;LSG(eYG*nL}xJ5xj><%j4XD zgCVb^%5-9&uy#2pN3F)fFpL9My3&Ifu<*e6xZuPkHr zz!Oc2O=eX6F^SE%Tz9G_`YFP>b;0%Uucb^(#t<(NN2Oc+$uUc+ioR*66%t0wHOY_Ne;z9T3eq<^F3bW~NsP-a@aAsLSMhW{{IXXH@s*G}H;Y~n+SJ(lr1cjS^V0>j z_fc{r^Qo4#Gn4k3MULL3L$-Qy=>P$2oy9f&l-k{t-gQ^Rc4>4lu9#&SQ_&ZbV->q% z)}}APw4@1YXxW8t#Jn?8Gf4MI&8)1`%q^I{-gqvESgE<*AP1i^1ihihyZH0b@vv>%eKy`cJDx6&S} zl8e8pu2T5G3pS})J?Egt^*WS@V6Y7!#>g%@s=(+GMCKbl&8lOhYFK=Q&ZPvGCX`s_749 zn>Q}9EUAL3R`Zd+<~v{g0kLSy$97%fpH04YI$GB&O`^D63U#_$B#qJ~#TxCOI z;?O<+W_hc&&g$*Ew9tLmk8VI{Uw|brKVa zXV;Y}WSlmeg4C&ln|+CA)V|pn;<_ODRoO;)c@cYXAO<8r$KV-Vi3*K9ffYPM}f zicqJjFOHS;kQzD1#wmwdJUuJ==o^$VTGQMP19g7)F5KRn28ok6uSOcq)>=yyx;CHA zyPnK?WAPf(w_MqH|EdM;2NZc~*@64@@dT{I^V)Q(wrp|$?@np#^OS& zKH^|Rf+3+Di?5Ng+G8b@tv{Ua#`t93)v{tGj4KhG&S)bj0+S5dtz77aFH~zvFuLxg z{GKpgq6}((L2-S40=IMdlEO}!=S>}CZmXolVr$Fine=;bx`kED!He^CLr3aP<|6#4 z1i{~6YoT9AdqdF!_>UisU7kj4#>u|@n=vq!#9xs0c)RwhJX=C3)O~9963Ugmvf#l4 z8y{?bKhb!UkAFRXnT-gouIMIlRTdcjqNHIk@7ZFF!0#=}j$?=pZVUW|UP*gbS=}(b z7xZ6Che@3Wh*{ggsk;|x&@h^V$uhm9fFyT5a5D#;-(2qP%iX$@gxk-dvPbLEX72EOWE`nk{BFuRo6 zskIaaZrNOAXHES`ky(?5CsHNil$-$L7{9!m7{@0058x@~Pa9E>zlkF#`qPCMg#Glsg=D8r5Hxazyv;BK0CJDqcsYODG>-uoygDLL8Z z+y3s1WlLx(J+TZZyBv)M2Swx4wK%QV0fy$YWyMK95qVVCjvr8iw+mN@K}+PIP4Yij z=~fGllEWe17y!NZ{+4aa)nV}Opgh}{T6Rw`Vf&eqEavJH&~TMo-7YqAVdF|#EZ}k_ zG^kJeSy|ij-%v|k%(Em=abdH=`q1xw#y(nB z(lJZbxEP~pC)1r0`z@&9$&`^y46WeE?KQ(S`|@VOADZKm&-ejO88_?C&@n-)qL`sJ zE)c2B@>uPbYDiCO?Fclx-imIKh@iolX?#QgUWVWP3leluxDl576}HiAIxW}%^4k) zbpxrkn_?Vrix!5C%yggJlyJWodIM0WD_dag*GYv8$;hE=jA?WyqLq7%Xr)w-UyGyCkdr$`SmTAK=v~V1BL@t+u z8R#F9J$%+?Ff4+{((aE{6RQAj6D{vnD+tR(r3wIEH2qX@>bg)BBVyaDgKk32*286I zu)J)|rL%*UPZg4b^Lv4r+Q48*tIoNH}={(bnS%s9@XV<-^ znkn62xmjT&{Xt{@ zK&jvN2`4?^BLd2-0N^^lYy^MQ)w}&Q+EvT#C7DbDs)U&R)VT~=iBy`$QLFpB{DDPgn3l*zQ9TlO|kL3Gn|;oVwSx@hC6`KVcyxuI~!_4 zFLfS}X6G~JuL0^Dz}Q&c{4A!>vfE8a4?EBMVrkmK&2L?za;CN z0t0_N9!9*)O6Zf5EH<(GdYfF18QJFS6n-2DVwPTs zs|j#_`(oTHra`6->pTS`E#;*-`e4T&g4MgYmDe}e5ZuPn)PEsrQgrcEYu#Qp8fvv3Xa1* z!O7Di>3lMwtQRW3Kdz_-Q56Zhd@9hR&@XM=)Gf>c$piG@4}D>JYH7Lq(XbdHF5~-R zt2FJ%?zA43noIA3B-#~~b#59;^?B?07zTYpM$hz+S-jF!NYfR-m5ijGS7l9X``OPu zafqDXWw@=uBB_(DzNct0krw-nHeNaAC&npd+WU5^1Rv0HJ9hEb`0}zDO0nsC?2PDERw@-!sI*LvvH4enEysGc=rsz#D#;d8QA&*5*Bah&0O#P zvTbz%k=ng8{wx1ZSsxkYxAgGoN)tW0BFmMRT)e2o_*%Y;P2>GCYQD`>Fv%{$Ku+4 z58yEX(vT0`L<&#iH9>{n4<7x8qm1T;&v^pKm`E8HgW>h|8E%QS@~nvZ?yO!okD>2x zbpBF(;wl${HUq#(-*au3degyI!uc%F`jw;G@|n8*Nj+)>j0K1}1k(Cs{@_NUkPB6# z>y_PKSM!|*t;&3+Rn>mXcR!?SIG}qa7u^!)RX2xb`R(PxZFQWc(-InuW)K3NmPtBd zRQ4W2ZSxS|_x&c+Jv~SgXJ+_8&xMBL+;~vY75bb}!L@DGyq4$naZxQk#v!dk>v#LH z;Z!#^oi?8+zK#?0?o#!e9_qBS`E6c7riHbQ8v0a%_wx|eKen>j@2$txG$ z7)m8{xL2hiAzUjVO!C3LI7Ac#{-$z@VrA$M^2bCNvdMbMeCt2GY59aVvI2klxXRc0 zKBspZsfKP)tx>FA@tn@;u}&LP(8PBfS}R)H8aL%aXimTEV$Hh7X*j6Z&e>0>U<{Nr zog`|UtxQ->vnN%w!0bHtIdX{y1>q{`ONSu{kMr_f|9-97LER-1Ys8%6f;rw!>6?BI z)KE2xI)fjVLi2~&31T){`F^b`h{FX_MTE^n8=;Zf&H=5AcyPR@p*Wwuht!pG>TUTd zL&<_FFMRc!hf<~~7axATAa^*gbO;S>`4zI~A-G+5Ql3#K;6xQ9| zToxJR?anobd9QJb>f85F#l7_D8X&vk4`{qTnIZH^UsF#!BvaS48x|>gg(+Y^=0a~m zhC5@`{saPWlu;4QrHP-_@A9|H9EDWp%ZAHq6-|wgMT{lqOTQD@tJG=BMGS@la4;?E zYwQA=FH1pTdC-($l6j%k>_cvx0hoP&ULIk`2XsqNBNJnCe*2Qd&i7ohZguK#hOuHL zuy~G!f^>zibTym-<|@0ZKmCj$C)h?Ar`v^tTn_b2IVyy6^LfGSv7PzM4ex-i&P6w= zX9y&S;d%SLHET?rTL20zL=0kZov>e9woqZ>`e>KMiV%onMiSkWvlnhCl^do}@m`U0 zi@Xon#|*AFp=+QZ5_{vL?FwKCl-vYbxpJjvFwdcdBGJ?j^dkrEH4mTmcRaZ02=rvr zlvElu95vi!AcEjJM1I$cVx1pyks)kB_#fNgQp<{=TU#mD!INkAPzerh7AK?<4<6s9 zBMOJCoV%P`+*D302DGrJQXHmD9G32DVd{8k5GVLMj%Q+%xmUYA)RD(grW)K*l3HFoxGA(M`A74Vw9@o zxQ~W}qzb=Z9FT7dEzagCP}88FcG{oywfPQq(pQbivt9t2GbhuvOYz7J42L52Cp3N5 z^gh9Z!+`=g`FiPiapP@9>dox_wbn=tA`ZvK5QnXSy<*=^N3c48JWk!Fr(B?H`4XHL z^T9KMVsT0ZOk)h?dQGd}Xhkm!HR;8Q?u)g*a^eEh*1Q>qWDh_s!{v@ZIm@~*jq6Z+ zrY>!jN;%?d6fa&~7j{jDDFcV!A}oh$!Yx!+m=y$>M8w8FB<_ zurdv>XU?6rN7Lub9<)Qro1&*a?Sz)ih3=W$^M$%jpQZ5aHUz+N8d9PsxG#x0lI!}8!A4KX^yN-^^FLm-s5u4xf?&8@ACQO@$S zy47D{O(Wb8S1iJ@^tUNIDc~Z|!45qLWJYhREWQZ+t(2=Zq zR%$r7;XY%o(c;t*%WLkQC#H|rznk+9&w7T6V+r%E0B7Nk_N+~Nv!roxQz@b1vHwWMjMQAyWrOP0W}A1X zGjOFWdP^-n^a-}r)8HoMw)SyVs~DDsef~FrREMB!hmY2W+k;wgJobQ;TUQ$X)x2aM)GHK-tzvTnJLQ&~s@M#^SGe*G9M z;nKOtQ@CKnjAUw)p|xEA_Jc|$L%-)bJ1N1ypi|~N=@oZ*njv?4S`yp2?c0IN0dScb ziaa4$?aiK$^YDwjack&#a1z6L@cP%t31-B{xbTc!F(7-nYlSr1C7^r=hTLe@B_v*z6`OuEjq7UZs+DjEK!Z(O0HK1@z?xfPOUwP*%|V;>)X}Z+GtXvn{y&c5}LJf?aSM(yHu?WCvRx1HeBK-2tHwV;W>{r>!^|xvzr-eYN%03X>Qv)Xs4~)Xme% zxxfAias>1xwy2(kC$(fHFd)5@sy~{#1R}HH%nULeYCkYcrl-=}HB+<6zw*%g_VWd= z*SzmxY$-P6>XYY-xq@DB?QyuD#bM~Vh zdxa3{?41X{3|d6c<6@X2I5TmH!8v{$B1}C4~-%R7(DaWHJ~3pFpjKw zq0u0}f)jVD#MHlP;Xzc`G*%WoottYRqcw~_Jg*^F%p#S~;4XCaRvSkYdy=#}cvap` zcl*QxNc%Vn$#sdbCDH;=f~}A&^!n;-Ld(+}csteP8IQv#vfO|P6km0Jy241tJG-%V zP>3S7Bb1m6Dw)Xq_RCFF?Y?|WTr7H1DeKG#>Ok=m`tm>I)f#8GpU>S6ehPR$_2pb^ zeSJWSmor>E%nMf3$@FbpCkl{sEo5=auwB;E{Rs;Bkcu%3sK2cKL^c6cy(G_dD2I?L z4%$G#$J>jE76ReaahqMH9i;bbj^`#nRX32rl>|(dS>l}eL)}y~B4Bz}udUD;iBf3y zw`G^9EB7HKf@|X^~gMXQQ*- zqM-x^0OnKAD851biKIvp9?g*=teAJ%nzBVNO7Y!)oqEG=+;Oygi0=Z#s6(hLw2{LU zLA#$0>8MLEOzcUeKYj77)(Ud1Zd=FaV}5MXsYQG>P|5yzWRH}7b z8|7`x0EKLu;AU~d?pNO!F(PZ97t%_Pl!~^}*MvR>$c7D`lK>sb5E|fXZX|%UMD=~- z6aAAo1!;~>)!8{fOzB#TlRLaVS^{DpsQLA51-SjwL62}&v`ym67fT;w@STzGN^Ono zOA;#aT$5#VPBY%^2Lri>2&|qTt4KrWO?D4;+nHV%M^{(b^w)xf%rMU0t{<<>sQ5yt zGsVKI=Sx=`prp9d*H&&esdflXYCn;Xz-$A!qKd%_Ti~nAR38CpuWmG(?)o@Ng1#*H znK~hjMsoRRy3hk}eu7hg8>h^VNRPj%nPkWGKcXodT4ijL=~7%6&tE8i^X(*Z4XdEW zc@|&*b%IOmJ>i)wPolU1q|M`dsgA&7={OJNiaX);t1_gn&4mbeZ#A7v_dP1YVMPvp zYw;=D&_qpLKPJtOYR!$usUOr}F-n?m0|!n4(=zFz#l_c=BVc=-vtT4uQg>zvq+jfu ze@LkZNktgKW3urw2F4)6d!8kwi&T%4IUK@#MRHSJ@|Q3k0GYakXRg{@&9SbhdU~zT zmT$Qk=Wtd|xal(D-S|BZQ+v--#FqkhNFTt`c9)Vr8VYxqWLlo2XyMHbj95K9RzMaR z_fvY)Cb)PXmfFr`P+IKC-$2J8YHUjjt52M^gCkuzP)FbFC!VP|@)gT>5mTmo(l?z~ zD_t{?Nf}-`W}_stdK2+g-|VGehGur?1+0tE(u}T+47c%ew}A<+1GqX9h@=!Huv=$T zG5c%VBeMtxuVzD@k#$j(0}VeBSQ;_(DZn!KRsMiUM@p_hy9mM|35x4Z$Ky&#K!9s= z15aX=PIu>u0*-*J{2nkFo3{%V)IJytm^fMF+5#O{H7(Z=q$_oQE4IU*-MqLC_AXFT zLp8M2{<~#d9PsH|)jiJFc863go1BCAs|R68Y8drw^G=y`6TtVdxPGPDDNmN6vY=WL z{K_X6W9E-qFOtK~(8{-1N!Sc>BSPNRDF61Qz`U6M{eaYr70`eg!wnf{VyLT1>3gSr zZSSG@8!wh|&~WEefkP#;@#0vZB z!g(+LJbwB9=kw~iBelXT$L-0oXv>cE+FF@diYPIZ0jHH&&)Z9_8o~w<_3(&E$I6HB zms}O~m8Tz;4Q9D-uMj)R&cNbPA!8EcznIjzImDZhmgYVX_I}CP!(!t?c8o`7G<~?1 zcN*bFUSEIMOCxoAb8Vhx)Yv_80*0l<mk~T-4CXHT``RM)U z%q#>mlXc4jdX5pyk_sGRuG8(Ani3VgP?qg8-)xPpO3&*pt@{T|81q-noA83zHN>b? zMTxUB!Kd@<&xuB55t4|*t7MJGA|Vp=^wlw&SbSBfo=7rO&D6Wmz^v|et8!v`p8O-a zgt^6ktbSetaIog(!(T;z(?S`&l)VuCKbl#Ru21hXS;f47e8T9roF3%DWu#<6wrQ63?qVyXIdh@Lcp*oFB_wp@etG?yl+3krEEpf>@pvJ|>i*N^`Q57b3 z)ydL>#r(wq>>~3f+Se33$ zBB4S9_8p!0y+12RQ5Vk*>73tWkKC;G{P+hD`i_HOL-UKkhTtEsrQWGLa)3{=`g`=f z+zBtEzeR7(gc~KAymsW6zzv=l(Ba`Ikgz4SwMM$dhKsFUiqQjYf!C)C3mWC8EzhLc z)0fFpMWssuF8bt~pEibYrJw5yJa!LvwTPqF*iK73LZ-c>0p{n5J%ZyYhC(DFg!-C5-&-Df7fC;Y(XK0dU@Hgth_Hxm(FHmpqGmMl?P1uEZ(9wdm)NJrfCEjakj;t9cbtU zB=O+b!n$%#mv(m>_DK5CV$qdwL1akH5gB`^67Iu*!FGWl%R5?UYvesH-LW4aJz6Vj zZe(&ekM2=A&*gTuFR6n6LD%O+NEce0JeQ!n&!%!`Hsp4{9m`tNHbrPPWSsqC7T~d1 zPI{luN2G@n_4cMS^Vr%(Wt1UjaULI&vYT{q67jCYi>Xd$vKW;v-Lfy0)h_}x@o#b3 zka0n1ug+(bmv~{e5VEGM##|0k`DW$=+%7$coj^1(*Ati6RcUkMi*p!IDkEh~vs( zzhV)CL+AGLTzXzK!6VDfs8PN`sRx|&DXKj1j_i~Zb-Jy&K?RCAr7Jg+ze()9O5p$M zWecx^F$>j9{^{oyF>`Ed4^N6@$i>^DqAsBc6>i0=+&8i2z%hP)mzdM;y|T!_GKg={ z+k!!5^pL#eKcXv;kZ-$K2QOm%gJIqWlBVv8$Fio&pJY(Wao&7dOg3s9h}Ap)8wDBL z3nuo{eww!BB(aY#CEyJVDIygj zg3xbWTcQOxYhwSiUL^c86205SNW;U`9vHG|7WTho`n^Z)Lo|H~Klisk?LMdFQ5 z@4uP59%4kE4&$a=_P=*JVDIf+IIdjiJDdFerSdUY;hGSa&u{-n74rY|hMywX z5v_G1pEDnk{e_10-(Nyd_NW7NnIvBx{m)6n|HJh&#zCdDvcMVtCHObVyZ`n*{C%%p zdD-^;5{nT;-gK&{UJg6c$Qam9k!TTLV_sAcem1Q5LmnKjSj0^CmA5J420I)xz`e|J%n;ayGN|5;N!c|BqobjIkjxm-IMwsU83FFE*M1MB7?{;{Mdj7e-=>l7B-;*{z!FXA5kM?X0LFWF%jcNup(DX| zUtDra7Yv5RQh{F}%#@Y@@Hrzh;fwZch*1)&dH=hDLfy-uM#O-rVZ6?L1W;*aS}ylm z7LX3qi_v#&LaOx6Gxo-sqlZT;X$G+>{pEf1rnkt99T&-P+`Q`^4`Le#{kPY9o>Y6s z`TlFMY6DPrWY}0q%iW;66(|tJW;6XIYE(a$qe_M`Ek^^M2X>2Ob9+TNf+ThR zdQ#aY2)pI24?3@dD8Lku-L|i9KoH(Gm&QV<{YwzLglmFj^LaneskUnmF1AXNk}T?b zX6+|5V1N%4MN1AHSDViyqT#8(|1t>z7Q;wxGavv?Xv-a9bPcQVX zNJQS4B`!SZ>r^aoi|z9+M$WST|AU_gna#%H-sc}}aI{2*z&c@jKqq2GkH@wVCf04X z9pssKlrdNJgmtoYSJQbd#_&%B*{+=lVWOsSt$f=ZF0wV-o>yU$Ks|!|BW~3G6Kmgf z8J_v4Xkje@M`t)!AmdwwJ4@euri%a>Bx+`^z@>5+073Rxm3_Ii>}en+YH|H-q+UZRAm1*D}Mh^ve3P5R>LURbD(+ z=46aC16M#tv&w(D0NpeR49hvuCJ->1>cph|Ib+jD2LXJPO)6@3^kw*dJNI}U2ojGc zTeUsheQ)hL$a5MG_7l|i9Lv%N#3W#V7}e>09d5lzfY$Z#uqBh(hl#8^a2(`*rcHtk z7K)r%B~~qqYsKn25A};$xL54gYt_cCs}=2V&PnaZz+j~{E9`88`rVU#Ed)uw>D6x$QJ23oQw^ofFyQS|lt%q3 z0P2zMO?Phk4as1(rcuQJg=vZmL@k(V9(|C*#FJrA;gGfk>vgt5u-ohGBE-LFr^t@_ z2K6c00LYe=~hj{2n~hEJ~LHEzl+4%ro;EQSovhI1oZK^d<|belPOF><^@$On_hL6_n^)uh^J2QeJMt!cqvYX7 zvH9oDn-Oj!Y`cvJX;T5bzP5m>Qtqi5OD*r0ra)u+04`s39Y}-<9>hF}i(Ru&?o{8(fkx2j%s%Z0zRd-CWUo~Bli3^LA@zdupx zy}H{Ath~`}q(L)M6#mAJTZqVQyR2K)nh96RS?=5T6-bQsnSn^!{eyXs8!`iIlZ~%V z^0IlT6g_op0%-Dj2vIj=V=h{pmgzPq<)|7Qa@grrH6iTIm5hQH+q*U5fm5&!? zCmqoQS!xWL>Rg6nVBc*6Tlaq1Z_v>U!nhAVA^_>YQ>_H2Ha1TfMQ)Hc&2dLwD>0uT`4< zuzY|}N5~}mfC!$fkBv^`(?%7^&a{fCaBX}q?hL!b@OT263wBz83+OAA=<)&1tqubP zc)sMTtebMfw-${9oV&QzEs(@L+1rqW*$JH7QsY&S)Mf)VU$Am1GMQWC>wxDeckN}J zSchrbCTdUXL=jFQ7FKE7Dhe^;IUFb2EFn?OC=O;JK`MO5#GQ~&!z;~WitrT^F z@R0Yg#GT-e6eGrjo|aU?2YD~eY(W?^=Xl=iYR!b9-??=4YD_^U0jXaHrIhKr1J`0& zL`_8Cr={*NOApzu{avGGw2L~rff&%?hqfdl>29=Dsb8_!hvOp2%Gu*F@kr}DrukgW3Uyl^le?l5jqh;akQ0R3in$3;W4;p^^03>Tq@czx#9sB8_3zl_`{^1 zoiTx&vtRmUzx$Jqs9ATucoLh7Oh`w?Gn3UVj;5c7+$e7`e)HAAeYBW=EWT5zepqR$ z_U#)`vp`Ct+if|JIx!%L#vncR_}jQN5iSpM+XxnopLBGnFF_#(vT8Cht$iGq4e=Oo zpWp|VNd}{J(uR5XDnBeESr&&M9(F{rEE^KVX7>dqB`I7!HlGMH9bwHQiMqN?7{nXb zTxUX$K<<=ikGrE>`lUc7MsJ6B>xO;z8PMbP^FCc4lC_?|zc>kEZ{t#&A%kSuK3k>j zg6M^EN~69uWLjV=T+K>j8XxWwHnnF+ol&y2UxIn{R6h{kR$NCtQXYP;xCtUDLCjm4 z`nmwYqjD$ps8n?~IO&>OxSRGi@qd%zZ`@Fs!>d++`{W@ z_+{V>))PEDJosVs9w^dkTr!*`Xq#^i9szx&r1NMdF$tKziXWMQq>TWgI-PAH7Yw4Ek-qvTOHyMS84nGT^jea{#Z0fn8oSXj)*0 zN8y{~15Qr*IPT8vQm1UK77yrC7V_1~;-`aP`@snP$5+i@!|3p=`{|rPCZ>7Xs`te= z`;pqMC^S~{nuIUu!l;R{IM=S!;z}0pNz7+;msSu?~E-{(Gm=OEX~UR(>>rK zjb}EAG65=E{-Sdm9f-#%{tDlJxwQ%c#3nzOg=Qpve7s@*w!n9LE@Ns=y0i(lsOp^g z*ipsVH->fjzi{ZX(md1I8FwspQ}#J+`Mz|SF7pBRpF;6yteS-flg%d8OzL7V>2?GN zm+8-Q`&upaVO$NfzEs-CL|^0(n{mLz7z?TE|4{=BcP6`aS;x1h4j?nic7%38BxpJ< zXeXy5PdwnsS23~XIoEy})nZj>I&g^Je(_$3&x8&X18>G zka4%xe8dIcWFjEjSpn!TG*z0r*zi(PveFMPU!{5IM(lP#!Lxr$y}@X{i|B#aLZ%i| znD%+NE9Hu4vILihUx-Ap*^N`L?*BvDTR=s*xBb70umzD+K)OX_=x(GD0Wo0c?rw$@ zq$CxDksJvz=x&g1MnaH~l$P$H&hPel-gDlwp1t4mp7mdAujO8|3})v3-S>5UukZJB zDR#gw5pfdRf=DGgMB6~w!mds~CQ6kXT_do7kCp_3U*9k0jlc>kKlpS46Lt4Ci0u${ z7G{iVPlBy4rgXiOH$b?{54=bx@^?d<)$ z*IB+%#(Lz{nUe&JH@QuVC!dD4ZZ=-RP8AN0$~QiYo=f6K!2*8zp;6Knct!)CjgYBAmezp7M|M`r5I|}psFBSkKF$5{H0nf$-lrYgN{PZ{zgbokSK#44- zRzn2hy&OO0bkP}-1-RI6f^IFT-TEs2=559@tEaGlzP^XeSL$-uiV~_t$gn>(EGEUYV4Dg3s{O}ZBNo)R?|@9v zmR~j?wgj%8s*}ZxucmhbO|EC;83e{YC7CQ=zz=o|EHvQS09w$u>05r%I_v0A0)pih zr79Rxj$w^<$@_Vb{7SPdu}#A4Eucvq3o1rS-I#CUZ`Z%J6B_P}kL&P%Sw1B`3Ezw?qu6P=DOw?%i4psLnq{ z1k&d}mnutAz4-gV_ySfZKNX=B%17u)pF z)Pr#r03w1VDAFrOu}gk?@AV5KW2Ee)%3N)@HmL9wM98&7q8w=UWJ8dCW}UHW4mCx( z2QMrh%EKelO2{d#REQrG+_%=IDl|RD(=U#20WRMze|Wb37om zrs7J}ozf?#ECb~dh@f8~boDE0!!tD5`A1|uQ6i*QnPuYx&lc#_^3R#xW^)nOGgjKk zBXjAF%~@q#U>1zqa`oZ&$fG{Po%h;92l<8yy|0D^!kC7u;$d{P#kgXK2XS3MX$vSO z$FniQqg7_j8H?89XOALNh?4d*l|8It2qVTK*j3GrR*t+UMb1HEeto(1$3r$v}|{r5Ta-%3d|5l zosm*6KH~2;?^Qv}fJhq$l@)3`A_G!;S-;TXYcbQS z4+Ph^`@Plf%61rNlULeoRRz40Scs?B7?L_z(2uu3cr`?D?!hkcW&5t#(AyYrPOu?r z=EA6dH}s;%AkpYJN(|EqbcC#rFJtXAp(k+A?kMVIi$ur?kS-E z=-YI%N*gSt_i1)fquEFB=j}U6K#KTVtO3jqG9uRitjEI#cA9}KTF*`&i8pSCUTK<1 zx^dP?c*MR&w5+$jECLeRt#2&T7EWSjN{LDMmn{j2&pA2%ysAN7#w%ziv`A~*5=n<9`EWwx+ z&kLewATnQyyhXZ;U{eh2Tq6sn4WT>Rj=L95f9!qrH46|2dOs|mwK>SOwzn@1JkR#y za&#!W^}c`>C{{uY8$C{LHfF8!dy3Q!<_Z>YEoJ7)N7PkYPOSvO0>GGtBKuY0Fga={ z`#uWSqb-8uaJaPbawQn@bjZCgHS}?LgDuh)y_fcY~b%vP7nmNV1 z0{SUZ{T^)(PV)_N3<(vp_eY{OodgdXvt~-A>TepqO%F0k(gjMs`SM<{3PBmVY>{XYqoNQTl_geS0^w(nY1twp%yg@{=C;4!C-G80Lbk39E^nP~XfMr|y?- z(Sty_Y1&{MJ)oRS$1i9Cq2iw_NvGUy{N(DcGatFK7YwY=L&KQkrsxsm41p2L$(;+; zW5qubW~MMlTSgdLva#J9C2#$Si=({OOj*6c6WQ^Ud%bZJ*Cv|)5eqtK`?ek4+Vlhp&`ZiyXnC1^jTO+$ljN)8g z+y&0nT*G_bame3iCN3kQ0gshtU&3Q4o-gYE*AXq7?Zd|kN%dQz!Z(VjBNk%#)o!|h zSWed_mB8rRJtu;HZ)5zr6dJNcEN*N=%279P;xRi=<1o5&bKRp}zkNscT6l$k*FeU- zJ)*0G-1#O7sC#VHcyq6CIB3Y#fVO(dCwg)okG}h>G58*N5G+{3aGzQ6>%=N3B`tcr zf!^A2Bj&zkeh=Ny$Tc(Eq;r3rj&8pdAbXpUCm&n zs$a2uNU#Mf95cV<3iQ0z366vIta~Bx@RV4w-3|F{#H6Z#231YiNp3i;(5{@$m6p*l`uYWLQnoJiSs*G{g^*UKZre?~8tM4t(GiLxf z-=0L!L@>!E>Ef&R3XPlL%wUmoq57Gf%@GOL-nbhE3v2e1hiXV&M4vP4)2@N_<=#S9 zDY~A1M`SO+XzCHoKJ(^WpYOe=X=a*eFvPIk+SqrZF{8F<x=Rv(izQu_w#%Ox?WUhv-7`_kX=M1K1&z2~q4QO;aSX+!u5<-n3 zSLyi}=Mvl^@%itmn368MpCq|r+PxT#FaArkM#W;$i6-QJrPZ-kI)Opj(9v=8m~-l; zMk6Cq5tVmDoM#2*EziP7jjTn6ex1BKWkO@CPxT=Jyz0Q0JuV2VKQPGH*O#z4MeU(H zQmyd)^1KT=DfKHQ z9}bUv`k2cu1|HkC2~Ky5XMEV$x}67|Df$0jXNn;a#|m4r-n)elZ`6bMmSVb-U_YK@ z@%xZ=IYaD|EK;`p{nq214=U)Rt;4dA1*KNN}bCol?a z)taz-ckB=m=EW=-or6LrdAV|ZU+H6!V)e&!ZmW=GTNKcdHmn>$vvpjmqnv;y<~pz) zv%pVvCh2VwF;rx*oPRp~@;VWh&Z_)&cib*K-=coPdm*OaUAKaEp3m3&!rZ9hj>r!W z+p^1_cd&&##-$_vBpQWs(aPv@O9DwwKdxkjd-diZh@f4NC}U{N@Sah+oEK)wDtgQq z-#)>m#ouiuO3{+wH#_^LgKSU=nj1k=Jr2dMd>&iV4F#_$dIvOj3{oVXPijo~$e0Hu z>dNz#m%#=qB&BaUB;e5dpB`4s%jY+ngx<{iC{kF5e$9N(wM9-kwi*=mZHq(Mgz02c z)dEvBOcPO29JL+}NeE&sk=|FA!_i}d$0Q$^SY*EX02M->d(y1QFOj^XdOZY>fFku3 z6-u7Cmx-W*4_m=^v&QNR4Kx}qaTuX^7vh?^F-)lM9a!=g`KNcM^Hcg1_}SH;J}CL} zElPKI(ImAcVCTRjSo}M1mZ*+R+k-2?Sgc!eto*J#rW}p zh!U9_#?`dEMDCcOHSKj?I9lsJTANeBWu$z`5sx=-0;@ZXRTzQY^#-oOhacrS(ND0S zdP2{sH0fB;L1wIHM^md&-gr;fF>WU?Q);os$CiyeUnvOT0-bL9?g3=Ffthnz64j^- z@YWB#n8_zL{_jj^^MST}!=42RIj7=J$ap~Uw5+(ZhG~eou>2hTA=Lx?Wh2>s(L`sJ ztxgi<%Mdro{0F054aW(0mH~*0dpm0Q+*RpeYkJ8N3bb#_G^bbR4Rm{5`Az&R6(_bC z+@=z$lQVfbu@7A+cG8}*6BLG@8D`v_&N>%B`jHzrw*P~N!UoD%n(>0G8(s*wL8`y4 zG8dRSY$;lB^2beH;hJ1W4R`E_Y8bVJx2vT2Hd_&093zFmMRcmuS^#2)O91i|p*Z~O zjtN09mA>YpUA#=>%lwl^Bo<|d!GaI-kLJ}ubmi{G7t!0%NRP9f78lSll#RLyLy7rv z_fZPD9+I99h8toz(dxz#)hFQdDx3E_;tM%W`<8%i+vTHLw^0c$wf+8*V+`0W95}x- zn|9uC1BQr##Bm_ok4N&R%@Vj=^C0%n>E!QOi%Kmij3WCimkWQe&fjh-ARWIK+@@m{ zo7>6T;?j`{f;{iLgWrlY4Z7JN#G*bQYb-?CqTnnYLwC4KtC1$FDW^KjwdDuj^0c{A z&!OJv)oZr$M-f8A!5Z&)ns5AEm4j0+4{wS5a0yhp_fFvs9aXFVi7jN6wW>xFfN2eAzI2c8I0obN)w1&i#D7tVcUvaN|~pp3LZ!e6(3 zIW_`$5&3oRfFxi}Zo5m)aBk-){TAOLsIvPU2R|)(2c~F7BlYXGoIU-6D>24V7a@Xy z`A;woU5ktxNxoQI>dzMEw~OXb`@8$n6i!JSfTCnZ@tXMVYQVxoq24xW%q0Nml%a18 z?rjF38~VI(!%0Mnid$|Rt`oqvT?hyH3FnZ$gzTjkL$PCA(t{2kT!hz9X1i#k!M(2P z@}Ud}n3JzfuFUvFgQ|6?Lz^@3z#8@azN&ux!9AnV3ku}cH#hi5jMx(Xps3u+@oHzf zhrVf0xgN_#=Pfo_kFj;lK&}lR`{bvk#qr!83|E#fR$&IGCwNf(7BC@EwY^vh5eb)> z8?P*oUz}Y0Ao^3;`7bd7?1t#k2eQr7Goj8g`rX{+A(hF?w9D5HUB^%2c6U-4qds{% znm(Wllt7hyTL-OVzu;w*E61NR1BVL%-={Ts58Ylg?efS>;K%qX|H_dI!#ITb=nUoj zghvH>(E$Ks9RiCyb%r{O3P6NlrgwO$?p|O&D@q|zd*^S=8o<6GqlIOGGjLYjf`1FU z+m8zajQYya^e&9jk9L_N=%-8Gbu8DFj4TCuA}L_?S2{(Tv!Vo4eYkE;zIKjK%hE!f ziHsi^m6B;TTA$g(mM!tCrS*>!G4WtWMyw*(sXR7sW#UHu38qno0ZmeA#O`MtNuB9j z8buw%K=L<~VlhKh{{ji?69T-2IEs3nXk960^?WxbN7 zag6pIz_0{B)~NMFWO)oL1zqpY8tuAgsfYc6m_gZbZLyn^x2vVyefPeoUqwPqc&J%t#Bw>ay; z@YM8&gxS#?bM_bAq8{I3FF7+r}Gf$>zU ztx&KK<{N}lf+p%56D{*z)1nb|R@J-$g=ZIn%hKhS?d_)c@Ij|`Y4zf$Ao*vrovyDZz*UtPaCuAFo9#7|DkEDRNrR!K5%}cf$XrS7t#b|2d17cB7bjcWB?qxugOS}kLjK%%`H^Y zqtInRwxIMP;{6MH3t|VRcxnl6=LF{n!t_0yD8^M14Qmm@ciYJJMRubkcfTgall|7J za(|daqOayE5=rk){d0#8**Fiwge?gZA$`i%U%O(srHHZain8f?kUvT|J6VqUa$Q=z zN+Uo&W2)x4mQMSoDd)HsB!4lm%F>7 z-6k6nZH)I6rs{u~MkNM+R3Yk3*QB5tIG{F^0jN4gDu;O&5^znsj#*OLmB608odoWy zjVzw|1v?5CeBrZl{XcB7OZNo-OR5VOsDab(yasz!rhLtWS5`~^*$P#B12Gr;BQsH7 z99sI)ukHm7$ABN!F_7!ZS`5fBppyH^RSnDsdZwHR%WiHnHtw@zGd9u76ly!UQ==zT zi34?w4}SgzO2_cj>HScmhJl7SF&*HZvrp&Yas+?yuaZmP&nduhpzzW>Kw8v$+8`%8 zV(J#p>g-gG^t>9#C!qw}w9#iT%nDHbwW7@VKWY{oCW~eVU0r+L2s`I4wyp^djYZXv z65#3=Veq?bHyPkfTOi*O6W+c*C7qxTbqsrZ%}#{C<=Aw-hJSPhQWu>dvcqu5L6>;| zJ}23iG& zl)lWg_LXvzS^D#bD9q_IpYQgl{+gySA392mct%tZ?VvDq~_4B=Ys zj9M&?%d$hv_SPau$0Uc0T?-YZfCRiLAZ~{>v4qP+Yy2<*bDSk;RvKRwCb|VvS2|T0 zy}2|uq(33pG70E5=q#740ul9Sz3QrIAC67e-!)llj6%CzD%h#ij@6lxQLMHzq0v^G zdns@-FbY0BTlp;&+zr@Zy*JY8Jl2K!J=C|c$UCjvqnuSF4AL21w0seip4E0mOtQ%y zfzE1LSKH#zY&*~V(sYAwDutnU=5OyO>`n?s=qE&unc>{F`+LJ5HBj;GYx%N|tvI^^ z9~iJpZf*?NHlhrzMS5CjuFkDMwv8|X>Lm(p7ilM7l}K;U3|ArH*pJ`J5l;iuC^fB7 ztv3j-a42r{Q~}QrT(!dm9|1QH+k>b@_bopu(iPA5d)I(j!H-70XQhn##TT*4p+e~3 zH$a+mN%?Jz*QBg;Lb-mfaDDLDpCq!(C*$L#!h(j|@5YoDf_CI8(Ukmm)rzMAigV6^ zo=yeRa)M|#^~O=uyr%LA$0Rf4nt?!KyGyAo5<%^Ct6oSn-Mz_S{&UZDFy*hQXzT%8 zl%*-Vg^t|&R@tWe2ev5lJ{=8*`t_L$Ry87dE(L}MN;d+K4hneVv!LIVI=?Miln?y^UBO%_=oMacs$D@v zl4QR&PgjYLi6{+#}QHCK>YqDh$jx4xg>rcRtA>+JJD*}?h&wVmuSWDk9E7hucf;c2%Cq_A<0mi0%D8=qve|oM}|lOyBI0+ zsdI3y>}ha&;;WhOcC_o9vfk9{cCHubU+>xZg(DtpH^GljGE^0qq+`mfLEDLJ-Ve{;@fYQ;IWfw8qLigXvntK9&U(#;c2-icpdfNP0#z$?EV7~K# zgR4P-T&E3e$p~c^kGOH2fa{iu|b| zZdhh~ttrCXk5|S%Cos+ZJV(0VExogdZYR_0rJorj2%Zgj95&rYBJaQRA|8c zzyya}WeQ0cY5Kfq*8=?AiGr#U-~vt(h}_mI!Z2?kCLt*_lm;edUf9Cl3;@)6B!BeI zs9s{D5ivkS+Mo#$Jp}$miGKb+YED$|$3RC&V%_stI|MZ)hrt&upAXnie&;xF%O`sH z*@WzhV&h`h2%84oUWWayU1ywRn)LXve{%mex=-igY{qD0<|C_q!Qpt9V=T& z=+b4BTK+DsS8y1Aw-d39V>9&W*T***Zdr(aSNf>1@BftUmJxyCh!BAU$6LNPZY82E z{^BgBL^euLf=2q7VSeu?q!Wg^*MIuDmhJ|*_uN)3e}*M-+llQOQDedXS{&YqFfDF- zR<7r_(_XNsX*jP)JAWr%n{i~m(@b7VrEuZ|_Qintutk@IyO2a&t5sAT;w0LgGm5yc zLSCgMXap86JmvhdVx;$hL($TrxU(IshAj^Z|GK=JMRDJCi?|0q%P8No@(}tDpi3TRPQBu?3LHiJ)fcxmAICcYje_)p&V+mjd?9^a| zeK6wpM8kBS@}PPCZEmIq$Go15H^ie6T948B=Go6j)UF?>l!T5bXAfJ9OTHXf~ziH{(?fw3K zNq@=>jmeqiC~V-(jPB>(8vp@&+Z3z9Zy0=l8$xfm#kHIA9L;nmu%$vUz+)rxW}`Q& zHSWWX!<7`$jrj|zV5nCgjZ^L?*)B}==z zy);Z+$p#oR>6xF}?_U@e`YRND2ldIWxSRmGLWHZd!Mxik1My(OJ!RM})+qP&={tJE z)4#X2F<{MM>vrNCNFy$RFI9Lzb(MzOmzYaDym$>n^Q%xM^7q>D@j|q46NvX2V6$%1 z5f9VwL0knGgUzX*NDV#M9tSzrC-D)b&Mv5cUyC}^6byf?uVk%( zL)XI(Cdo~$$h8>)zA|AT@_IpoDIQgxJNz_^%oK4c72~m$`Vlww)V{HXY-#9-_`&dz zGbovB$)eqg*ysnqoJnHT=QCT!Ak>cN_GQz8yx#hu+Zs=H%+oL^PQq$~Gp)1C_~Gjt zTFfo2Q|D-D%{R(zbt>Y9d)=1Hf8D%ee5~r~yxpliMqiEO88u0cb9oU;-$pHrX4?Gb zta9?jJ;SA&!nZo~_Wmg_u@v5>F~LPKXe>nBTCay?arA^bXKq2H{n004=g~gKC3vwX zv`6>W^7R`qCtg?6lf%7;i zjiz(pDbg?2*E5XZ&ut+i*_*jI6y5`O%kiDyi#Ao04wfo!Mr~6=<0a~S9P<5Q@6b?A z+@N{=X-uz}VR&pEVymj~HyF$>OS|{(;V%d}>;fWnvDPuk_;6*laLK2aK2##k#^Wsa z55jZ4Lcn+s)Pb+=LY8=M^#g;3iDh8VH(L&w>Mi~Og>ol-DNGRqB`=sayyuW?wMKuQ zhl3Jz3jznrXO`o9LSfM;ouPX@P7Q0^8qk#A8D&y(J@)j6&(YfwlN5&nL%d()krdf400%Qxj4P;RDQOUM5#A(oBDnhrz9QzY8IP6x09DH{c zphl(EPeN{|lOM~i8OoClAdbJ>-k>89K;H+^aFkL0kMoBM#)1fg9NyNzKryp$|w&KUFAlmLC46J=J=VbTv z*7wi+q@))^=}jfUQh){0_I`b$ZZ>X7l4R57zw9bB!$sa8v;2ID(|Y64t~+n*$R`TV zMM~@=Lz!SMjJ9SET0uyC%a?s4e4B-b`=Hpgh5YI`xGi~|E;9uH2l?+itx=Dc*EET6 zPl)-&AC&dtpU9fOX=^{}tyRDcPb`G8H(Um_KcuB&ff;ui3JqzwZ=kGZUJ;Iak;jf} zxFQfh>l@|r8~1xS!`y`1e;@B>X{842<1Sy2XV~1 zZxSe_=&T3vLU6MQ9C*t=KND3tAJ+1& z!QJ~LZWmci+F9ML>U&Wf(k~Jg!ReUR8fY!jvWu|7?!YyG-{Avv_#_P3!WD~GH-3N& z(a#!M-oh#Jnc=dvX~KYU!-SqHxu^S zD2U4&_Tj$^FUHN*Pu?9d8igWmz*g_!XUEI+hTNlfo|2qGa~WFVaqf2V-l&=zzuL`Z zQsA7Ciw1xF%S%u|HU%@#gGQj!MEqqbld+UHf%Ssom5Mm+gt% z4(IKm1@2t#XZg3C1PjNqp~tRnG&l5&E!1z=^|(d8y1keMEZ`kddL`9eHPCtMJda%_ zx^Rvz^V^y0VaIRr=2CafTxD%X9{O4Pc7x;e#Sb>y{7DYDK{w{<$agTYm|0;`^8J86 zpoJgj0{=QpmTT^O<$jrSJ!DVM8)(w!i^v4y9;KGO29oXkXuV+U!$r6L4bjfTd%gZJ z^wvu*NocRc-^x_Eq<}}T`Rr-bQ#3_sXPhW*{&6!A3H9iG;+gW>V?(`TPu4!`ixz)T zm1L^b*cgJQqg==prTrW$zm9G8#^TJYE5O zD6X!{U)}}2s!;!`YX{^s%1nE!1)xkE6NQ1hrd~l~q@Woaphg-?qsv@k@*$fsM zE^^e;D0EWr1(2tQ3+#N)yO0Ihp-$wVyLN8j{8`AT(S$SXt?`??ZYGV<&A@WePUTnA zE;(4AtHw>mg?8bbgle@t&OO?Ur@B>+4xL^B*)na~oZ9Q21_iDx?bN!B&;#_7yHt7l z2ZFJ*Q(sn3l`*@2bua8&-^Eas4Y$(+y6PDm=u+0*)H%1zY;<~1|KYFtlh!<=#C`UB z_RQ+aCqCf9dc^wTxm^COG6NvB@iiEyQ$-zK087RpwCF+WEW~0RLcKwBx921$XO!Mw zqT5MkhgYxDIKMa#5o?ivEkIkG+F8FnGF|(Y`Le<_gS8I)X6oe+h)J%AMrwK;3~OwB7%P`|;mym{dVu-JkO9=rNniBfw>2 zJ&g@M95K7|as*Pg=HFC{pd^L&Ah%-qiZ!X2JQebjNuB(<#c$Gry$9ChROJVM5%_m+ zv(3f;HpKAX+1&UqcEtrYs}_mEELv6O{2Z-!bid~B&logn1D^}2h=xVx|6o|2V*Nk=OaJsG{l8y-3rH0DvYE91{7?PUkMQ4L5AwMM&=1ps@Av=dIraAk z;JtM{^PCuKkxYc zG~qS{0b#%ObON<4H=~KK_0#e7vFsBZqbxFD;kFm2zz=jLKKKje!PHUp3 zM$K~_GNyy~xc9IkUoibozwn?KzQGD<`cnk?p@PrmXKjTJ82j`=lsBo1W3xSU;Ki^6 z*m<@D>;>B9&dRn96Xm=R8cWCaGg!e?RBsA#h=D0>OFnEz4K1v1u1H$H&5okp0CP$Q z&=pbc0Swh2l!pKF1mBr`zt$6SCOaPpwIB595Gr{^-Ik6mvu&$$PR@o=7v>X{4tA&C zpfyhvU%);Q{?-`~(6yI3-yi;BvpC1d&ZRS1;bfRFzuM6RRw$+dnko9C0JLIS^n*xX z{FH;l!FPb@He&(NFTjefnbC8x^?WmsrKHZgW&~8fAC<|^Q4YXJFVvr?rQ5x>4iQ)% zr3&?*fuM$kM0HRX&?@Y-s@o;2{qH+T0BnZGzw$IM16%=gBv~_CQLsz>DVP;VsniWT zAtJc)aOKJyFR;5Oe~nu50QmB>4%p~wJ=QG=CHEhTPlHu!-ShefYt)6hZ8iXg$dPY+ zS3S-8paWVGmHQ~!6riL7_5v@D0>8)a_%uPNlcoMtwi~AqIRx^X1kDI6x?MXBb6Ze$ zZ5q|JW8Jarl(Vsd96ss%P>{yePyR9=04e^kFTgO!Q8Y$@!a!hfDdJPLzY8N8ni!26 zza+ScSqDFzkW>9m7y`^{s8@bM8rE;%bX=6^g`l{7fN8dPE+wfLPrRtjT13_7Ykplqgs0w8=ziI$ohOE)m4 z%5k@k((N2lZ2D4#(e4`)g6L0zg)_KVDjkU@ndv)feh^ulw+w7eEUC!D4Bzp(Ks)uj z%sBdO?W7(8N4{|&+bf(ZMed(l%=?Tph%@z8r}fJdbfE29Ta0y_*xJ3kye*LY&#N2m ze9eX3q{_QRb~P1bv8K0w0w%}IE4PIDCBYeo{7}?P?-<-=%#ZyaPsFyKJVX4fACO>^ zX+3{5!zzE6W>)uQNKVQa2X~R&?(&0XWam5ZX@{os!$Z*e7YRbThzPQ%arGpOgYCD= z!c!@gFnWihaVvT-I-^%@OBb|D@=rQK82JzH5iF-(N(`9|t^;kjoP4SS`0c>lSZwCE ze3)t~deX73Unb?oE%HLBGARVvo#L^Y8rF7J)w;uedtC|Ebgcs2WUyv*QVnZ<$&g5F zg+Z0)R#n#e)hN|-%mF{N?#aB_5X!o#{tkN48W#87e9#KaMsg_m%p=BuF8gv&S9TDO zEzS7VAa>@4oZ6Y^d5hW|~Lb7~H(aa5zGaV8aIK9Xthz;a; zoFo$qR!TzT0r|TwVAlvGZijuCG$==z&%f^~j2w|1^a;~9xtKi8nGWW?>Mk_K6f)zK z9UylMWM8e7_H?OptLdb{ukoO8?)FviczBHcx4-NvfD3vGM0f36 z+yN^fDdKu2W!Bx%Y6fg*E$nFgHFEDMdP>F&8OJ51Isj4anF98Mr34KrUN~9k2>#AB z{1306>O*(+v{cU&-((?5mH{0s>n-f-`ZJ&B$Afg@% zRO3EU(?Jaw{DC*V?k0OJ9o$?_7&5Sq-d_gYvQ;tZE5Z;NQP*F}byvbIe6{@!D1Xg@ zl?l48fWN`WaLcPRmbM?@n=Ki((t(2@7hu<|aNy)G;AuRs5kvX-hH3P~EFq?K%8r zA6(!24r5P7ZO3^0zJ$K=(a?v-M8;QpX_`>Itkdv40XP-T@r=e1Kota#hR!?6*C_Oz zgD63)69GzoH_i#LTg0R@?2~dbo5$wVcq6&p$N4c%di;I3gw-7KBKCXLCq$Byh>GyW z0U#b1Q&X_$LtMtazGQxaUq&QL5f2_n^Qf2FbSp9N$_WuB%>H%5NdHF;_OyNOyhi>n zImI9bBKGt%xm=KY2tIgtA^+oTDL4*Y*@pP?Uq4<9S#m!z8hf%8_5mq`);Y`OryOI< zF0nBOtfsINB7YM6(R4A7Qt}Dbn0m}LpFhxzj{Eiix&c^8rEXt4QqVrX^4Ld=B%Rs$ z)ErQA)*M`O)wfquJoVZ6oGKpTXR)!dJs4S}7FB;1sFl^4yLvB}pPF2My*rUHDlmSF zQApHv?N$@xwm8WFI`?RHOTK-%Sc+YC*OK|pI^HbSAPT{Xxx;}5YvP`qq~=M9K0?_V z?)}_nr?_Prz-`2P&-VWRRGO#|N3e*8!bP+9p zJPa3i@PdB5eX7oWc`uIaM%NQ~55&uXm4q{6D?pbh+iGqDCMj-#m9flp{1(#?a21$b z99A`bP?IVNglZAi0r*!l!&O?)jwMBtM@UsE&G<#O8wxYOu36!S^Fhm@2m)*soYf^ItCt=yPj9Sb&F&XV&R(;kBtO~ZFMBE|ms0NgP_oY7| z>a7JuNtwa3d2hsm>JCWg8x3YAaZ#kp|H??biE zq5o9Nl1}5?RQd1}gzRXkgWD=8fwTORY^#OtTbzAqD!-Nbk9D}PQ1Ub8P-SeX>T;)< z>G)L2&X<7Lv4VkkSl>L0w{LdyD?dZ-ALvS8H69?t8`ZPD_LPh6xt}y<&ODGcA#AB0 zSb~$N=bL2X?nTxwf-SQ=kj^zdXzPc=`2Ikf_*+Ue)3-L_Ps`O$G(JJw}f0# zeuGq#wZ`?iREbS$-Imqo|8%GP_w92@8asggc@8DN<+InR27zF!XRTx`brm3oWuARe zh$2st55E>t%TOb)EK`1?%-?=I#BA%_Wg6de#t9FiYVy-BMobCSx&RfFHL%)as@k69sMKK_Hj zB$oHtUVB1gD^n+^0Sz8F7AA#)Yb!4ums~P*3U<;Onfbc3L^(8(4!R3^L=YQx^^*ds z$6z}=w>{XDs`&|_Ict~CYc_?_`B$uVUH!O89~Qd$)js=UD(#vD*QlNoX^byC;V?t`zM%g>L)iD9WEQ_#(9J|9_3f@L>S zaob40YYZYp9@M8w3qe4Sl_^l?&p=hvUs=Hq7aqJ%I?yg}g zS*XQ%dP5v_`Whur<#>X6@tMxtr=CCKyWi1b0~xr$jthIL%31N&gPc$I9leWcipCD= zFC=V3cSzOupxF)VF)Q!%O(17!tY<;{y!iH42|=@u)jJso`B7%s-v{{#Rk}?sf*oBj z(Q0hUd0GWG@qIUW4`>$g*lGM}M|ztDrmTdDpndhaSz#e*&%Vrc4;B-bzJ|(339y%? zMDw&#WA}mo3y-(cp)%d{)P!xNg2_8JD4mw}tFLqER+woPY9S~F-&4n)zcTwAZn>;} zKy?vSs?97vArM^BWHy*eWTbL{|CJ_?((Kor z46pvP${;ql@UG2)2ai;Kadzvt@{0;>?Hs`Q@X}~@KL7Ud8_uRguW%Z*)0?BTJ1v0X z4nX{1nXY0yYuQrwbqaOE*|^Fe!w+$?FFIo?IfHR*-MRLD0VcycmxYe*yuLBulS79! z%Aj6atLfo7-j9CB^5e3YwC^l@dw)T~A~Ten?@vn#`e+BE#UD1Y21GYfaqr=K*OJ$b z9|OY>{#kQrm5_YXNeqN4cai?#;@w!cL;`}^jV7c~rb#i* z`3a#Jix2ao_%#js1{Mpq1NKzP!II4*I>f91(q=e!9N`I?&ys4sWzt!$RxC>%(4TpJ4R>if$H@CSBdqPhNx68StD zm(>!iTCp|qsd%2NX|>C3EWrRFsZv|5cpHyTVjWpHCi z>HRl46bu}zetM3sLd_@5(~ZctTL4Y!md>$Rg6X>>LyZLY=<3J_e7_gCia%wQSAbgq zLUgD-IcipbwU)dJjbVdY*6)eX28Co%w_JcveR?8ri;v(c<@zS^*8GHeJXRsl5e`G% zf`;#7i#R6WSJ#`cMmpB-82UE8h-(PEa~#PMU;#m9hZsjU((+qAN`BX+8gYZBx8HaF zhJS{XS|Dj=w7qqL(@h?Wdb4ZSQ$y!Fgd=vLt*DZlr(jKSh31k$pvpnTTefvUH6NXW zCl&RjlqMBJpKnKu#QA?3lolGD0@VsFzQ1)|*ZJWl*h>BSgT z&v^@fg7SnM5IIr-k>jc?wK>Nd9{(h*K+FoO`eHJVNpQLw-;p^XP zjPaOS;45kKozq5nlJ}VpZN2a*M;jX^NG%H%<~UgWD(brEJgUj7)Au{}lmuO!0h`l# zvXb~-vJ(0D<-3`Z%n*dRb88jcDFAF$@4K67|0L+M1j~lfyiuV|<5Sj0BV^HUX4#GB zXS9hxi|qbVTj(i&V~`+qFG#djx1}DwbYb=Pe6?!-n1bEakimxBzyC>Kif*&bK`&%OKdC+};K@z>SmchtS-AZ8AsPyqm!R`&DurnuybaYjXASJMMtXYK;x{ zmVYbGf(yz{Q%X3XK_mPMOE&OgwR7DdqL-2|n2mQLUshm1%aVVtAY+UDVx<|{em~#D zcpQq|zP1lB9A4t*JJPr%6^2A1;lx!;&NIZ5|$w@6L4pqM9 zc$a=zZC->m9sk^?Aqh4`Mcz&C4@aK_W_T?p!B1Y@8>T2*{iAxuk9IaJwfj`1$duV z=`VdBXu(g z1#{pfBZ*dYe$JVkuF(R4#NC4j<%19n*D~fXVl$JFn2}v(tyk7Lx9H_{pSNn9_I)kE z?9|jy(`=_n)%&wGjG%0__R{YZMXi+L8^J;xNfJ95Rvg$7 zI07&IRjHOUwqIvJ`q88`Ls=H{IJ^uL4LZT@@R0qN(5jZktk@qikE1ZESw-C;anl4G z+C%%esS4boA86gP1LU({y}hRrg?(jHw?f`sACiiH2qF;m(8g}h->!QVfM-W_5iA>6 z4IJz*2Mo~)Z_!nkn(y-u*xvIHnOwB0u)^mKLC)D!J+(oT?2TqeD;nr=n zW{G41moz>SLB->J<{{O+bSiD~HG`$=_O1kxMRfZF6pxHD20xzV5?xK2p*tr3a+ul} z((n2cNN*0#jYYvt_$u`Z1Nbccx<-E9`*E z^Xpr~zo5hX*`RFYrOdP(Api^4jbjbn!ODf>giW;z-cuNPN%No|>!$oPPcTzbr~=$r zUI|Fi{}*d-9Ts)Fukk7(f^;k0pbQ;DH&Oy33PU%DbSMqdAl)4zrP2-3jg+*6bV*2e zo#*F@z1BW^y?bBRIe)%huQ1L$zj?lK-=AAOl|(ycgR7eXwvh_P-tpRSmwn@}`uB-& z`lF9=d9zDu0*X4{JH(#OqXqkNj!Z-&T>6GZKB2?-4Rex~@}Xqq!Wcv0uRxz#< z;n-m~0Hnit&LM$Rr~{aCc&Nh0|(Gg)H-I$zQzN8Vn!_ z!nQ+R<31NiBsvWyd{MB6(nVM8j>G*CS*P2FYw7j@Y|d6?k9Z|q+L*WK`;$^BN~doU z?8;yxQ$o7u^LsQ0C&$Ci`+>i|XHX~3?5{fpFg{v8-}Oye1dC*K3en2uC;2TlA_t%d?Es{|G=FzIk`Vo|s}V zVxiJPgBv5k_$jC*`GQXMNz7A5Ph86ApalZ$bxpJDV6{Zv_2jjCN1k0&zsVNdNQ5)I zp@X)jJCZF^19R1nQs(b+AsGl;^yoyFv-cUgu(AbD@ zrdomqd}=a&Rzh!ByjO$K?Q){%YPwGu_s%`GRRlPx&t1A#yA`|0skj+r^jE{<088_P$A5dCZEdUN9t1=P&DT@^oStNBndM(M~^X zV1Qx&kO+tRGZlhGV#XhRbN=#6eEL%5;y7GenvNQj*=%%ZZ{)piNil99bL#%!sFKU_ zxz%2}0%HYp874U#Hc?pFL12dpuBUCleA^&Flam7{kr6_Js6(Q}H#-Zl@@slAn+5x{}h=8l;mij8n74N5Uu zzI~|C2u$?Cu|+&UVm_A|UBp#6oe$38Mn+dKKo336IT-BdG}HDbKo=)!Wz6OzS}1w@ z4T>ac_L8q;&fYKgKB}KY%C-lMGkOHC? zvr1?2jzW-6q*C)RJ-jPtjWqo3>x~da6&(6fZgPTsmh7Q|ZTbh*7kzdoL1g4xjSf2_ z>lSb_7K5IWvE|Ofz zx%-`T`1|V-9h8!Ur00lcDoZ6E)JZf+WGl@$$?Z_HI#dXG+(s&iOraNQ*O4_vCH<_u zx9w$3X8t?rM3Ta6b|*R|*w?fD!9aZ=r$1VXD=3fU1*=Aya2-iaq`r&O;G|BXmR#=l zYBA?8{?>(dKNsD(l#9JzucOy3IOo4O{VcQ1;1-6b7VFwC1$_m>T5Vbd!3YJ~VkhY; z<<3H&z?c+06lgW2vvVlk7qQ?Efn_M)0LnOA2$ZPOrH&7)YQ-;2hcfVFNE)OPdBzcK z*2r$)YkljKDuo|fmabW^(@@t*KEMa&>;2amBIop&=^7i#yhIL2sN-5~bbL+&=m{ki zpKEhk=zE>SRJQS-FG6PV7has5YaB({EirYE%5Gj-pjWzQ5E0M0WJAB++LKhOgcW8L zG&dayr4N8cPUBoZdM*?XBeBDYmry2GJZH^y}u>ng0FhFhy6@? z%0$5mdM^G+DsP6JT|e6nBD>j@obDkk*bH}y!K;#>8{?9i%w({YJl+LjDh=>0VSK&C z<-9xp14h@AhYbNaO8vu}gIDzYC^xCO>|#$qV!<~xfiv-RX>-_kRxuQss9+ks{vMRJ z_L7~QB+Nw4R1w2!L&`WYCGP0riZIUr4Sd=`^TUVN8r??t_d|lA&1)rIHxdIf$-X3M zTbWAiJ4!l@0&WAI828R#>~vN+VODFG1euFe5sx}BhYZ#6Yvbe@_o5;*oaC2njB(s_ zQPjkSu^Q$OMPqEk!epl%+P?En?hb+EDFvl=&O<15v@m@!;=~?<-;aixqR!3_blSA6 zauEUq%ryq)L0S~s(u?aUn_bb5O=NF=bV6H`Pd=Qnow-=g(I-{m5Vm>^V89?iEkV^fP-%0dHW; zNTSH!opCO$MU~`zk!X(@#YDw<4oP|uYtM79>^3-jZZ=iS9^6I=@R>E0-}OuR0U?6d zCTCbwbJ7*cjTc#~6)HdW$Q2Vo5&7D=ZoA;oNfQ(7$>FiVJHL!YQl&v3-BXI|Yedp1VW{b}@NQ(nb%aDY~TD|F|9p71&; zYodB1GsuRJxVYda4F_QyvIijM`EJAH8th00c{$03sj+%8wgwQPwd{|;-S!G@_r5f? zq&*~+*e@reS_?2D!F=yra@J(x^nY9j{^Jk5*^xivrItSEibz6Of<++nR_S*GT!TvV zMI_Na!{8uFdVVF+g$IHO5U@jv=ya>C_$E4cEnq>!ss;?N69JM|@PT3nS33zRkF4&> z7g?$n$tib+pfWkP783e#w7w90XN{%E|c9E9HCqd+$3 z*%C1!@S$EdCE}y1?pfidlN5;nyPYlHfyu+7ZKaCIWkPn&% zL)bDYr*oX~PX2T9+k%}=K|ba|3yll1_*o2`4y#Yy3Xj=}kMZKer%i{dLDg8YYiAA9 zEklhE<3lTd(Jxr-4)g0mI|^hU+z0Szuc)0@35Z+mpHG1f7T4T=sDC7qTC%`IZ-T{n zC*%*I;}nViHu<2Ti0f%PzH)^JGLqxLYQL3NYw`{79pK14#ad=NoW(Lf#)zl$ZFQ&SGus`@u%0nL!XwQ#iK!sPu49tlQ1Mc6`Bdq~BV-aobk_ac z>oUG=m1woxGIlY-g^Sb{?52vIF4?oNfByUP#Pr#tjKjRu5A9RtA}MB%k@ly6yy=08 zo231n$JTkthhny>Kmw{DDu0nC?^4$_#NrB4`vi#M@ zHG<~vJ@X7|GbHJ9GWygn{5FBI?=0#eZfllQEQ2sw+ZZF6aZKhIBV6hC3ZKUB118G7 z?6``vr=(u02iRyBJ~Ye31^p<};jMnpMuCpzf&>Fw`0#t?M4hPQ+m2Rf5p4s;r@c>+ z*I+fq+tt#ye{G-><+c%hDj7hlMdKth&SUZ^PgxV%YzaF7x=YSWaYxCW;3K(97)Tc$ zXAY4Rr%8wqG2#MdCTX|u2|}8Ql4xl&g>&em7cKM~l9-u!!!y<8R+3H;|ok*uBtUrVrOe=IAiwVIxOh06Z;>rz@6R+sZ zjKRsSP==WnH4Q@*n4#w*VA|B*5qFiXkaWD=WCY9TB^hozOGczr+wjE_&k8Yt4Sl0DQy4!uYUO|q8hUThwzPsU=P+<-Q0`#6uL`vstMxySr0deC# z9C8vh&y%#%lWi}9Jw`kH&Hl)!7WmWV2vMh}1YYZ}Cupi_9O6o&!-$IXf0E;2`rU`U z!6M=AZd`Rkwntw8i)@!MJra-YRrr>O{7#+c!PCJoc!V3O6b;T}66Tg#ifvWfM~j*f z`#IZQE{n}i8tG`xzmG+=*!~vWWAk)Bd>%{TaPhvKZNhalBU@*g2QA&&Gh1Xvz<$nQ zrxE|Ah6}6oHUarpNE8k7tTdu2kQ*Szvpsc+(Z`JtzW32|H8o2ap*tdTw^dg9zj|=a z2H;}F=x?lcm}$%TmJFCEmefo9$$moo~nl5-c zDbL*VP)6QnQ2(~CBWPN&9VTv?w6)JVyfb7`ROHb1=e5|snWINrJO4m&rYp@SC#6(k zcU5zuv5D;wR%z9U-wjXnyZXJB`{+cvADym2Q_o{_u-fDNI8bvbbC3By%B3Jxp^q3{ zOA;kpAU36SNl9l!C_wJUu}7(+2I1VN(%Sdsq^kvXmKUdij!cVqL>|Mm6rc<5X5in~ z-T%g;a5m#SJae(krFf~>&u09ghI0Mqeo4vLj}>S}>>&+1KVJos>7!Q@A}g_XDO$^H z@#rNq3DI*^uC-6Tz%gsO)uuWE&(7UDqYFf!#1jc_63s%txsE2}i*qms)vGdH&vGws$e1JB@R<4O|Y*GeTPoy}4ln z?C{}*!>Ft-!i;_Y6)#T*h2eqL#v5(2;p3))Ca*G%cz(jBD0a~`INd%DK#~!UN4Phs zw(ZZ?mD)w)nDD}k>bs! zb&jP)h^)6AG8sL4F^G{Qu<#&FYY1cyrl&QA%(N5pq5$s7&%kpkvh6^S{+tmfb7skA zX>o8$gt`u!Lg+`h+)`&4SEOtdq*!#^9A{zu)jF5+>E7h}LNv)w3{~DzL|Q!RW3@i2 z2SG0sB1&}%R=)zVBCSpmEf8?%mZ=;iS2jq|mq7dibxWjf1uEpj-#@3HbjcAzqpGp| z9CxrWQKV~0P&5q^1Yg(cJd$@^-W5=tM>nPK9+k3 zoFryi1WFP4aihTJeXdvdje@v%y{+P$i*-P={)_sRN<7U}9-FDlIHlSvwy#^9f;U}d z>?t42iGj<*;ngt-7wcH(2@ITG{nH9%-kC5;QIWAZfWxtfVRO~qd!+Y3=^#a`@-5NP z6>)&%)HN}Y%yT$~V~`NwBt+%Cf5EJ$ExLcE6elFDGb9(OiOXvoa=IZtXB%ClJsCUr ziyI-!ptqL5`~4tAO*O9G7N9MGB|6W;^cQwzr^J%_Uk;MQsAA_s2~}Xl)L@Kh^D@L| z-00UFkndU(zjy9@-3c6b%W)0%^_=K)s8Ry_jZ<*pIa}U46gTd@Yf-T%k5F%WCfdx_ zyNG{!$#AFV_7@e@D|)5ZNrOVuJjj~oHDpE))pIL$z|7ACTAGkr0!1VyCRR(xN3tQ7 zG4?pr_eJ+))k9e8@)ww15Y0%rA58+7oyEs@&xriLMh3SPT;ZWu2nE5ie%u@MvU3)J zoQ6UlgdzS)@bm6r5o(PzyUvFwy$T@Su&h$3-Sn2mq45W6kX$Yo8=>hYuZ3-RMY?De z$=-o>9~khp`FmBiv$k4lTk6tq@b#P*{P#)aY=F?C(xc!q%dyQa*B1rzK7A!Pyq?q* zW#*rQju+#58uD!3Zv7Y%pf6>Ld!a`OX4!A;9z?^&OQM4g?@d*NQd;xv*MU`p7Nb-8 z3h6-D*Vz+D+&V_-59@99 zM()ju-NxOJUlMq&@}?@Si4jMZejM=?lQXTkBn4u1(K1&#V-wcr1}7@6>X)lgy3ZTv z%yS!*0P7EBK`L4ZPMzH^lX?T-9Q*Amo3cC#3F9$qO|EfliZ=%^&0PJ)b1u~^3h&LA zUdB8Y7X>+B&yGg2S7!_|{TPc9m|?K<+@ zSw#*u8!F1_=Dg(AqU0|0h@PHdrr4-n^O;b|XS<7Bb*~BXJf>V6AoC8J54s@#4UIeH zJZG5WlavL1(Os}~+F%0vs?xHJvUN?Ta|R=8*qh70wUI^zX8kdLF`sF^3>0H(afy4W zR*Ee2zBj@vvOVisTed`kd3^Wu12$d;Gc;zE5+IV;0pP%WW=L%php*(aq}3SPlYioa zGv6&A9v(>GFsdT6lD+}i@=Jv7FTj{0UxtfbJ1Z2K?h!=4j2#uoaH+HiKk0Z(G;Xi) z^s_&z5{M9H!pEXd>mbtV!yx7D6R}UtLo|Jo5iUuheM&_kuho{!UiYkBJwv=A#+m9V z3Jaq|KpQ7F=T!$WeeFJ>eR;(Y@MUFfQM`${tK9vA^T`%y{#G164z~&`T+BfFL5_G4 zlpzh#V#8fvt|Se8X?ON%>}$ml)wn0W;dJG5Y}~|sU`bFmL25vZGVDN7e0m9# zF0=o#%50QoF0J0q6975}!7Mf1Dc zRU$vh#4KeJo~Z+EO$WUqAN;rL+{Y0cfN#?-U-11KgF|?`Vua@C0rGS`vOP{VCF$NbsyBp>|dnwGCHtO_Sy^zZ5> zshG5ts82LD*a{nu$G-Z=OdZmtf;A8Q-j2hY!tap3A&T)yX2!-N08o<1o-vM5>;o=4 z#O>QgtZ!li!B;alniM8aNdt?opQb2LT!YiDF=bYG6k>D5p{omII{~UJ#seGUxTFfk zF&|Uemm1d^IhGbL$Z=ps$!itcF+1`~WwHm1d00`!SzU1zD+<0ksz;lG5q%$TOj<&B z5V8Z8dtrq18s|}iPOo2x*ve31Qi{x6i?poX;!g^9YIOY$Co>N~%NvG-o5v}MJ3ByE z*U)04Y_LF*qNQ8IJ-&xs?*X-e-+yc%4nF?HMxEV@<~yta@jL1SKI?3hm{3M4YA>Np zJD$Kj3JsdK2V@ zpz8EtT@EkO`AQO$75NzkqWX3(fItlB>!9}10xgIO|D@|^8w8q$>@gPn_5e{dZb+$?=N|<4Wa?AW zf%Xgv@W%7d4kOfPkd9TQhR^@{VQ(QB*s^7U%!-jrn^$fL2~*C`v@J51Ym<#TARP=` z%c?T2P)6x~=G;sCjN{-jy`ZL0{>a}yI}~MYfzbRjS9gw{ev9?WI4AV2GApRK;t_dp6itSNOf?~7efsSZ;&2t%`& z)4!`ROP*Ky+e0SUcE+muCyXPZv1^gT1DhXsL$-WiM#5(}dzdoLha5+K6u^R5SVv-lXUFA#}gI4qeW-!KF&m!l^^;_d&IV5bpp8FkYpDZT1(o zb>+z0-{s0&s8tP>{(v@k4@+G9!&q=0w9;8XH&vsn`hGgEFdE}TwyWIbh3=WBxZVf@ zTkSHQg+HY|fmKLpup;^B2(AnnZi-apH4dRO!0mE@>^8Qh#(5ufjJZ!mqhCBEULc`& za>9-QzIy5HmQQ^+DJfs{270wn%f+9o4SiK3J!UlHRM9~ie!f1)ZN9uxt$UP^<1wz; zWc*2Vwd#_?$tV<(Tx=uS%|%1qBH5E9L3|!h#eGsPR?tm+xU0C1h&WD~sEN)F0)$vZ zqj7neWgq4NzT=m;>psqizNs4@lY$P1c+cgFw8txu)+q{euJ6G3faYKV3Rhip;)cem zO8XDqu8^((8wLE228k!g26t6UQEtDLe)Ck_sxgHgNU6a-0`<$y>lJkLkJf)BOMfp$ zhV%h>t>)I)tazy5LUM;zkTUQsSn{KamZbrk3PTU>&Wc#rRwvjTM`>2P8p`B`72N$v zonlP*$N9Eey?!|3-uR--6kP)w2(V1)YLiZY4YtcuHkXX2XHohY6=T_h=BRu-@Ic$` zwKp7CO<1QQcsuqnLhK`_eth2r`^>Mp-%doF@e(P|N{iYFktQL`$^|2?D6m_rb=9o)X3SuQs-e2{O?0v8qLLz68z69daO z?Qj65q_<1G);Wzbp47=NY3p*VG7w7smQMwjosIYdMp*>Wt48l$Q_)D^1#k!b^O;4x zbC!UK2dt9iE}fX})T9ofu$0@80A?^9&G*2U+Nm5Z69i<(zqnf}?{}V8D8_K>AXEDp zjXKS`VmI}r0UD_=iXHRQ!G$?EnE`+210N0|jmZaPfODE~LFoFf3>nSRd>Z|iA6op6 zN!gaTj#W^TGWBWsYY89npfA(d9TZTp%C*VOm>2h1Y5SD>nc>dSTYd{*bD?xf<1!0R z;`>v4PKw8^gL~;!bc6MP~1;?K_z#<$XPChhjqW*(~*Iw z=AjmqYlzsap(V!1o3Zl@#Pi(Z6?$c%J-;zU%Xd-}CCw}ndPst>kQEk4rk{on#4Y$w zw39$$w5CUz^p20p)S*ue(T4=tm^zaKjFgc414w^$E$6vT3}$% zej@jg{j2630`a{e-}VKtrf9tAd!%fswv1d2>4mJf%wkMbqlIjH1g`0mc1(NnlrQ>* zDSeJIvfW$Q&kO$ICe1``r{4WK+if|Gj&5lo~nBZ12) z?EV&s3CfqcQQGW_{EmK@oMf5#WLZunMJ0$R|8YF%bJuGusNcG-av(27E&eTLRB(qe zktLSsrL97|S85vxd&pwJGRX-G1FkuVWxeZtV!+oBDlK|{?TeA@CqS?!F<< zc+M~7vHi_y-B(MIUDO{+cl$hs`!y8B^%z?KW&_2WyERLQbSC*TT?;*em-n;ET57j9 zR<53HJ6k-B1$`(74*$@Br77xkB+gU8&o`ys`au5zhhXFMa%&npI5Q7FmR=5$p$lWP zx@d%8#Sg93cAxiVIeC0r4S4jNEsE8cnfLp5w(lPh$|m*s9oo|@X2}!Q%`Ey0O@KYUQeYc(y94gzd|IeFB1N8a`6Z+$Yu>mLOgT+=(JZ!xA%AHIZ# zJtWT9d6#d`@TOSi0%ouMN#+dMEmRsxOE#4^*X1p!a49U69dM)yE?vx+2HT+ffAR#I2u}yMPXJ9ih=~_TLYKxVq89Sj(0D0&Gn6uaEp!n`gFz6q0 z54lBj3@{pIyfe0vfU@D^33i#LK)(BSq|;r?mgR?gnAlvSM}I26157-_=)F#h{RLpj z8x_JGkcHGzzi3D~wQV;1TgNBni3&z-SjW?eF~+AlIq`YkbUl>=v@x04p8r@T{=>%c zU+=>|&4};CGBt36Uy*UB~L}J1B{O>y((pWtI#OOoMZG$A?#Znn9gq4ta>n z_c}>ae6a+&Ex|l07f97sf${r=j{_^J1s$-2*!R%)16iga3$GW5UpD;~`)EAt6jW}p z-1m?2P`L$=<3Tw)^3}=40m#OuOV1#qGql*!0QvBW7suO*V5QdYb$vj|W%+}h9uBXY z8o~H4H*oXi`(_X`VFt}Zz&w!L%|`{OMHTau(lC}+fz7?__p+(&gv;{f)Om&WUzP}v z;htLYI@W)Bsvs4l3|p$gZ6QDYM_{@FwwT; zwlfrOh)D{{hE}m#5YKis#ok=pa~iec_1L#V5CRUZDq$tfNSMuFK0@3F%=6YPYjEdJ zSpWWlc*N&{@97UH;PGNQ+$J3o5vr|FA#dKJ#&S-Oc^~?cMv= zlQViB`n>OJgsK1k_IW>tzG+7z&2RnhPr$#uU;p_n%_js8N)cfhN%7y#kl1VFc!VKZ zIZ#&QV&&Z?@P?tnD!xmYf?+04(@ld?NZEY8!5{w{6N&fS)o-UCLwO+3v>k%=d@X)$zw^fqCt<8;14e)xIAe{M`wHNJCY!T-ftkRa;dMTe z@pQV%%8F7Gpp^Onm_+6Pq_0diL8?r5pmBXU@~ur0Si!3-=IR+bM#8+nHySVK$_cg zZEV7(@hmrq*DCo8*k^j3ZSO<@@5e-eZHSxg#fw?zA`k<;`tA-^i4n@xGPErJ_$2Ig zWLoa1NRNB;+q4`Cl3TIgZ`14m%{*cZUbg`}j)fb_LSVPjPX>%`Z|g+?45g!XV+^ic z88lLn0KB0sU}Z715eCLCz0g`{1_TeU-dyk8e5PMI@{E!u;^yz% zCi8D=D94yROk*`g@U-G`}&ldz=we1kak~L@lgEeIesBu zf7So~UE1s(0(Qq|U^2`lxK-k-VOiQCfiCKHYgO*-_ubM`w7_Ysu$b%1?NA*y zANw5lP{h6FCmogb@1sm#tTJRSJLWnHs@`Y z@%*)~B~Q+AQm&5Kr$7`cdPly^f^w)$*BUzk!{>g`I%N6N}L}6G3hR$^&+S9zN?{J1No3p z$=GDI6(|6Gy&pti34)G#8^s66Ho8+?H$J^p%}n`G?{e@h;*hWtU3U6$2W@24_5Sw> z^EEbue7dv=(79HU(%pce>rczNU%7;GU`+S+*ad6|-+I!q9js#mwoGyuD^NIcleEQ! zY3sLjcGB=x@dj`*_DibfH)Wfcf4;8p;2le5W}UMP4?VZO1Z7ko$f!1k-ydK*S3G)d zXQiO}{Qq*{MpNO)+5U#Gd3*xEoS@L|`yE3C-$EM)Nln{M+5s4}lEH8_`Q~y>bfp5} z7&wDrz66kMoKSlO9)4vEHEHT2iOXh*N6Q52e?S0nf1g! zia0EJjQsy*V+0iVuk--u8c8?ePhd5WNcRCBp2tE6W�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

    VXM9Wy18+TTF}(xdOfcDwCyjWKO$=v)sN;gqUAs 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

    DjZU2c8yG`qDYq*n{BCsdunX4&RGq`V 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 PzpOo$ zIeoULZr5=V=B*AiXQXie!7<2LnebEXxVSCw$xcGqPZC_5ZGiy(oHjvbf8 z0ohdARFik?QAAVhLJxiQg*xf3Db3bE$|sJw0(tW!f1Q}Y*t_XoZ>_~<@AaFQ^NAx4 zL{pUO#B_GmlM7Q~i+w8>YOGeC{ymAuT%nCCe{8*XWi(@f zCG^0`c5e#l_F}#IpN}ZcM_@A(dd5VPB4?b z%&H!PIiyFE)R$yrhinuZ;uCo~;Yq8>bttGA!5cQ3F!kt37~+D-PwM469!(4Z)J~U8 z$+bX+YoPsaFEcF2@uyvlPb3-!r_$)2d9W;ssUMsDl}rpV6b_+qtXe=yy%{9cn~&hJ zAF1Q!M!t^m`bGDOg4WA<%aEeMGi#h!o+`7?w|Q|GUkJO^={(0y zXHL=P>yfyw{Tbj$e+H1ECvMT_-LdO2HfSW-K`ojf(%c(?o>z8NNk!1!nS1UIM?)pt zxh5>rC709$0>?Ts1W8Tjrx#U_lZ7a<7x<_{_E%fh_ILT@Pl^BuxP4@!ru%%VJBzh6 zKO&=TZ`LhQ?d>zCq5i4j$$*@exrF6P{#x-V5$@HV-8O%cJe*Ga?!Ickd( z0HH5_8bsZLJ2U{;PtHwpK%YD3LZ;dGEsqPV zpyw+H4kWOUUch~qP}*#r8qc@)?dw0Q%s!hQzcGJIbn8HPwq43SBP9jSe&Vn#R@MsO zy@_J%Hm8xJPn`_qf5NFJbF#l9y2DaG69&o&KCjohFWp<1W$3v1geF}-I@@Qn4`fw+ zn_7J=)z476S;So+(Y?Q(3CA^Kwx)`=64@vt+O5q!-=ARD48NJEc<$914CHfNbK;3i zR(tAB>t~Obet2~EZVg!lK`VLMp`M9RHpdsyBrq}54s|c0Rl`pahc_9g(gW5zu`cAe zg$c^(Co_CzlA!Y(^bOUBHYvld5_DPUkGSFYm}AVTXKvT8W+$;c*{^TIU6W6JAn~SF zjg6!-TxV=e2le9NiOf8}@j8x3VfNv`W8u-iz77i0 zijq%BvooZBGC8zQ3PKpLY^@$4ydg0<4f}fS7b@e+^I#2a$Ch9Ju-2!x12X_tVQp=pjMX&F=clpL zFs^WgI&sJMS$w=R5x+9^GvbF@9+;hOy*}j|O^q)DujqSSr2l7At(FE&2)uON*Y2rK zn|)7JOma`@x9^p31V(Ae(fd~hEZBSW`?yWdf~?%iANP}IZYNd2W}Ay7pV_;WBAczc zEJ~|Y{E?et5LIh(=nkmdDR=7WZz-y8h#QEWs_IPG0@VXMO9_+k7Jk@)4EJaKuPj31 zRMWO`%frw=`Id}(H5Vt;yR1fKewp2_C9@P0Og)ZRzkX>eX84bbQ}>$9JQ7cR^|~i7 zX;=2&BR!amTRlV%OxYrMH#l|CzR%ZVZELp@A-B-(bA|B%jy6yqwfYB&T`p|iiFINe ziy7*Hd}>FjM7zD?H>=OYDb-t=zFPD`Y&PK!itWf}c7fn&BnfZ9vY;HG_AWa{YfzkF z2@1^dMxXdnO+o{m9^=eq%6lyICqkO$9^f5tqd#sw`GNyd3Y~SNRC?myB;3D0;f_;8 zav(10a1PovCEbVV3%;Fi&&%e_RdAG&uK%O0zwID_Ob0jzX?~6qhst}`4B|J^LvQDH zQeXYVE;~9m-ly)+svSYw))SvekHkAP^2=c3Tl6EGxN;>H4vz-55DN|Vq4j~P==HOb zj+LB)#b}NRjxSwtn=N-RPH0nj6!3c|W`OPfBtJrrzwP?S(XC-0RJkK#tOk#PVw-`U zni?ONw-;z4Y);G}+Vtcfu%k3RO3f{mL>HR7WEc{z{ZC`@FYGN*=H$sSvq8Jq&1?o$AG}QEZjl32msI}91nV|=%(K-g<`2Wbp@hO%z4x@tzw@FsMkFvm zN&#t|7@AmQIT4k~E^^lXvvbzci(2P)jw_oLFs3tnHgaDTro)H<#1CvqMDlxpuJ>CK zoAAe&?at&PDQWM%-V4oE%ANs8A^Tu?(YWPLc798DdW*F6*)p?h6)cScG?h@Ise z*lbo_Z__bTkW8&%@MizdmABv}m1*f|oT6l_0Cb%^JTc6mn;hj=+il1gz%fT%I z^$?zea|ZtLGiuSwSMDZjQ>hgf12ANTv$HX}9r=2-%}pH`C7>Sb2~4R0V1a-(QWtr9 z7UBhH1CupKSpq8W03no6@aduUPPTx0EC-Rd+dSPY^ej)c?n^NfD|;$m@16HwpK?2E zT~3xYYap&UZPyVf^f56pWw#Fvf+WvW2%6MMtKwTN+2hxdR*Q8%2+T@*M_6*cqi$HS zDv(DLY8SY|$sf3G2_}?#&ZEeX*n$Ew6rmV`(W&1Kc4jvL0sIBDnR3YK^Xge7Q1&e(4FtD>p~qGs?b}XceoA5Q+FS5c+Yb_XDoxrypR+1 zLJJdZlyWtZ$1N{7$6xY#o>JG})xV~GB$Wu-DsvV3WBj&asAntj;VVl1J`{%HU#<(1 z>9zR31iXp$MNH0 zyt6H-j~ITgB}oftaOG(DBhefkUWl#0-IoS&vUpOp$&|JF?bh7T>qNCsjw>m6?UK(m zAg^issEANgHtuR?X|mS+cMYpmrQFWCu=jP9&yo-F5ULX^xAaw zxi7fLg7rDlTFrI2F)s3`f8mCXyurJqU$j=>%Cm!if|Fx%LGIcIC zrh{47@@rPHl_aUR^qZz0<{^URQ(ziyefj=085C(;_~~5aiOg)+$>x7rn|~D_Ppgx? zqafw8zL{sj^qqroo1NMNt@sy>1CMmm8u=3r zycBE*%N7laH+kWR*xp34EXEKa*D3oo6$^M#4@Ay9pL0~r(=J$#i`nk)OYxWi@oP8y zBZFANfq+ZRhV$!Gv6uu8K*Oql1(*;(^pStr>fXw^d5J+skLI?iUEoqComZeApuf^3 zXgaXtI4qx+UuQLoKHc?97QNGS}^kCyn`*q&`&B$y@B{ z7*Dzgx5Ia`*odDKxMT|u0&8^$c2 z@_`yFjYK3Im(sXWjX@R;Y~)|IvAzkca0-CegN|OvMQ2=5iJHb!Zny1?z`L?pAau%C zR3l6httFvAArSi{-_(zCU`nBJ)(v_p2PN2WVOK(aZc%_lT{Ogm(q%v?{QF~MR(%+WjCg$JHu@pJHs<&pFNK1Cf6(mGHM=|2#;T388Ftqax*C1?knh=k%bR) zI!pQEb{Bm&);X7=X>^v(prW-&=pup>4zIlJ#J9(6t^92C9!u8ja~wuCcrz13WuWWw z0E!gjS`NICwp0E!RnTYs;p4-{S;Ihfo31Ys4d4;~V(n8PZ~ltqTd}z@OuYgv2cF0g zshKVHZ?)!sZcv4duV49YaD5Qcgx0@LySv`^s$eqIy!Ptyk0>)}byV=slYvfKsyUxc z$Nu+eyXKt@-u!I3 zHOGJ1!(JqSw_uAur@5{xH}Fh~mrvDs(vf`_c(5bw8Y@oR@|7v^GQVqQ4(+9FVjsl)j=mkU7PT(`6qfudD=l2f|8Yz1l;}zNkLtezVDB`>I zvv$yn^htqGSOw@`%~9md6U)ZH9a}2^UTJjzv(-znVMFIIa23tRXw*J zR0P7Dvj`M`Hrs0NDadHrszUAC7rlL4@iaQ^k<*qTvM*Hl4nsG4l{ll6Z?c_C({<3`b=Osz&aqEi*TruE>%qg&1jQYw-()V}Tg7w?tpV@U?d+I*#dH;F6cO`pR8n4Z$-LnO`YC4fwt20SlG!7JQfFvH zU-`%7^(Iz9+8{C;ZPftBvn)Ufg-D#!gP>vwc`@{HvrC(S@p~{S`_RUd%A%WlzyY9q!HEeyr>| z>k=M8-YRW(t4W$P$=nSg)i*BukB%N)LJ}WMbAZWB!p4TT#&bBU=i_HR1?xW1S z1yN?rmvz_y^=5uppO>ucz*(4Ii+qeA(yi*g{JAs{?Pku*B#y=?2SdCOWPhBrW-yBg z45l}+_qO1&9w)TOBrE$T{t+SPGQkAX2@P^v-fla+SV<4 zLAmH+;o$Jqsnq;beP@bMg9kFG*kt}MBa)yNh_ko@B@e~G=76EsM00j#X3kgt=`F2H zDmqkoS21&>*%G)-efB&yyiLC1F|I)wCgJ6QtT@TpSmc;hG7{Z-Edi8n?PV?1)C_Wm z7MpmmO_uRz|BRBGqdjl&eZbr}F>#}F`c#F{fH!+8q2go?K3}y(w7s&}Clp+3R{PGj zyl2s~oGv%xY3WIW2|~gsgbf~wBv^0dGqV@iIJCVfz?mf;eB2oRy4<^Z^QD{ka&lA; zmuUwZFv+clLJ&C9+Z32hfQ1m|K5W`d?$-1@u^1b7|5I9mOnVHHZ>r*?q|6=%GywPL zhOQr=7`M*4Z0)M~yvz@yNVB1YeWAQlrcU5%_dH|6%hqmU^(u&v;8F?+q24Q;q7uF} zU9XIO9b5@8gE5sDmiZWH890{=w-_92)9>ufQ~cZPWn^WeBIsC}$g~-0oZQ@^_fn2>PDQzOXh-M@`I^^<)<3paXB<)z04-1dwg^zd$IJDS#X27nLv7zI+Q zslL3v2DvpHBW7cakW;;Yc{(CB%KC0FW+>r<(_gh{cbwm!&7c$d{or|Qw&4=0h>xx? zV4e`VJsxO39pa3rVeegj?f#%fd@YYZ1KWV7>fxgkJUdoKIc&VCn}~f&7A0h~LTWZL ze%sO=o~bwz&9>1>mPPy$XL%JVGBy7o^l*QR)L~(T2ph_Nx~Rx@)%Blno%dcO@8oKB z0hf25Xi5eJBs)Aqmsq@dXvg#94_(^>zRd;j@y*(7K$>;&k9{80w$(AF$Gm(HarIYgWv&NZ-g4rEg}K{h-Am23*;o0FJ5B(!#a4uDxd!eJX=gWt?(}ddqu($% z^T>Pn*(z%u;_)8D-2J7tAkqC))`d^6YYex0co?=~uJHK~-(i~r7UM*7+R>qhG68yx zD_>le;{HJbJkB|9?aT-MrS|Lz_CB#{S3LsD`dw%8MnAAlrY2 zLsGdSMNA*X!jDmU+o$B5?p3xT$z%y*eyX|F&hzBil9ZY`$H1Q>jK#d@(_LP!!US*d z!6CKLO`)(F^w1AiujS+-_8s+T{zUMfbX1&5sRbS(hORR;9W)E$urE@v(u_jL5^lP! z`cH(ni@i5ya9frh#G{r<<>0~Ijvv{inCY`idac{{O_#;1E2E@gzCeQjz`My-b#dWH zm&jY5bHd{gURQSa?l(9%ix_)Z%?HKB%Q`;aTQ=^L>RD_|8hLDiEhElt-PC4(P_;Nw zKtfiWSxv|{379}5#9(wD_b8YUojgss-rHmI^X9SNlirYWA;j6aVc*$=;0I6Fu+doE zv+k|R7l75=XVg?Jn~C)PdjJClXST`QpcO9gPJ&gpU5aWnBcO#9vabzUDhw~IL@mzD zSaQJg*Ewdy>Gx7`KdIN&PYMCEYxz{qztiD1%zb(#@~{GOx)Hekqi8#a*g^-{rh~jA z!YQYC5q_c6e>R~X&`BW!0&qXdqIhVwLw&pb3K9s5v`=K3=cVBuGt+G_yU3KT-2T|uVRpXhOOOX8y z3IndJjWguHp*Tu4wv35~-8_h(roi|}+ZmU2IrAW#IX!LAfJY~*<6;|lniAE_-^e_W zA0>T$3hXTeOJo1!67&W-I8!d@B1>iA$S*>Ulh9x5Vcl&X_a|icYiM6vU7kSWpEtkL zt;o$oRs4$qJ*504YDB?+w<1zHG!t88M|XByL757wo~5#4NHywY8ES~l$z_kmWp-TN za~2>m7ZfJNveFYG*xgLi7FoOfo!bM@yeSR-SKtlhD0eXfa+HBZDpCDLAyH*!^-Uu8 zer9<8yA}Rh?q)c_(iW=ap;`LDMYn?yjTx6Nkd0I|woY2ODmVLQ$}L2wyLyhF`pezZ z4%+qs+ld^n^{gKW&0rN>pE@JCM!ba%(-2I(B#3>XcgdFS(d-{*J#v4ic89qe=5*L7a!c@?LNxjUsR+0rli z20+k6QI6O7h=;bogdjuuse>23q`Ks9hO+hju<~xcVID(=u~*YwM+z3lwo4@r61!?m z3T|(GFYTj#^dsjz?DzIh^rewIVI#<7dPv%^j6H&)pdopmoQ7$!CssB6QY#)XK(W-~ zB#GARYI#vUXS>0u6a4E~is{dDhti&3XU1g{w_7V;XDf3GIBsFI-Og0-D^4TY5Rhx! zr*$DDr+sP+GXV2BJ#`9rs4@vE(&b~Gj^;PJF)i(ae-ZHOmadYc+S(lZ5$^E1I}7y{eB{p&{qA7hjS{Aa>%7t+MxU^V zN%(zx%0K0x+^L)hR3>z}Vbu7ra%_ZHPfub@5fN^^yHo>Z_jj(#bE!f28;27olSmTQznSB0skp4^#YVBD zot8-M5dA`h)9f&~gZRF=&up9jldX_?0M?|hx3rH)jeNVv(Ie`RMx#};(1U_2__O7x z8H<$Lx}*Kh?P)X=P9L^YvE6jk)(G<{z&q9~e%fs1VQQtMliBEUS2Ub|r=h!^AJBpf zuN;mch>Zw=Bm0Bqb6uC?^~Bfj5&x@TpdK~-jN9em6ghqKGyO+@Xnz4`#ty+FkNNY0 zfC7#H8-YkGg9y~rU~U4i6df^S}#?UHTHA3*`+ZjGbl=7+6>?#FeAyK8^!=px7& z)DwDA>exr3meh0hgL^awx*r#(*SJ4ZT)Ida8PQ| zBOzyvg>wuC-}~tmdY`b!toB&t$TV?%X_|$ zeG6DiIr$yCc-(acyp(-;On8ltA}}xWwn}f9s^6N=KXb*6^y6+slf@RFiBCpBh|vMh z!LHzT+*1`tc~hn_mu~&dS8GiVU`%Dimq?~fR93}%vZ1bn4+WDFg*9n75OL8vlAqax zy=*baNlpScfkgTpPGf-Jzo|T%Zr;0YhbTsrF$!czNQ0cTb$WjN(E2n~asR5k_3Z26 zatjeI@OtgO>GP2&UG@JOS2yZB;OKL9*rjuF#j*!7cEE`g_( zC8=lRVb`plqERcKpWslb9MA|i-3?q9B#>DXY_gFbQV|Sj+>b#nTwG`xUCf}kdJBc{ zc$)T+Xskdg=_i-Ull8`^s;mfd-Cgep=Bs=dwHI>r6x-+w9ez!hr?*q&>78k-N~O(2 z<#I6{=|`lN-#-Jz01uW^GS@pykR5@A<$H{KX=_|1wK}$p0_4&#K=+A!C=6yHc-ljt zpxb`hN9r)jx0~kp>v{_8fMhRnyc}rjq#7tFDA#4Hx6DNK_P5tVgl%Pco?-vxO~|LC zPY&d97Z4IC+()2=k4Y%Jx;0BiRvApmt$9lyWf*yOrhXF$pK3G{A@KMT{H4lfxJbPe%jtSd{tCvza7w z4twhobt9b;ZEBrD^^OB&8?O&`Z$Xa(Nvl_)q3o^A-a+L=vtjk7M-1l~`-S`Q}>(VyaQYW!-&ucsuu0B6(xm zfAMrFg5JS;1MiGMqO&!6Dswr{i5X*0TnDB4rNct_aD#3jm+Txh&b}=O1dKp5Ijt_X z5FO|obTRyj)>_?l1!OK=;pqcgv1?$t>Gc_3oUE#YF{?*#;O9}Alx9w->Nw?H$le6^ z)omWP)&)N1_Tkyfsul{C9>nP>Aje7RZNJNWU|MPBJq?oCi=Awogkva(vGMQ(Be_FP zLj#dcy)~@I{X@Zd*DLCkCRP3Yc=tBjWqu~T=C`r2vAm#*LY?aD=15fs!oL3{sRP{I zzF{&wEtMg!90yvamJ{4*(Q(LK$ieHED|@ibT@9&G(6eVx7c$}^s+qmGM>ikaE@La! z7Tum`ZGEm=+`XSRlca;wi76zhyJ*C99lxCtJB)r>HlkDZ%N~g$(kh4=1*C5r2|j9h zSO~#?peX|H*sds{+p`u@KOBro)HStZ-d?1c3BH0$Q0OGPSnE0gS@;p(E?ls?EDar) zgiJ}k9W;=asHpX|;2*G*M;d5rd@D~%BJE$sU5-fh8fpHB0$D&gC=g^_YCbp=sxWzL zF0SW({O-6rTx03?r7SlzuaLU>aZ9Q&wu28$SnxD z!#w1#$J$(Y33Ui!$|h69E*I7e3JDvR=ULI9?ARvK4^#9y;jw>&V!a7=G5DEGFr?U= z*@9n&-*RQ$Ur#>vBO+tADz$c{t7()!v(5}dG;_tHX!rSh8P@W)3y5ZmH6WYHm9P0c z0Xf~;kpQX^*? zFQT`fbMWMwx+98(fxNp|PqwQn>1Ww4{rnCQnI*gw!8eY_*G&YQ0-h4W{rqIB+fH*s zp9#g&a19y(pE!^{z%DJYVKs0B$xY4UIp&3KzfgEzJr``*l=GIco`$n<-c4dLt%s96YPr$y z1$``&BpoIJF+lzIU-)TjZMo?6@DB-Qy0cYY=3JonD7Ign6f}-!jgO5L6>WzHzCpD8 z$0UoZ77%$kQ<0-K530Kh*t%#`1C>t4IGka8Z)e+^4s=;-gVX+Y#E2{Zyx zc{vP9G+J15>=F5Un6EaX-`^-%c6Znt+a&}vC&<_Uay`@LOFy{gh~aB%lCv3{=}@uOv$?x>Y=uh4=* z@8E8u!AuFin`2rm-^|TtYeRjkZpIQ#_Ys?TTSKUZKzZR=;b`XTZlZhx;wROc46?q5 zeJG2uq2T}FfK~uFe3$2*plw@+xP9fEHGH!#lT2dp_z@VfShbm5+g<{8Gc%@y@Sk~{Aot{Djxf31aAX$iQZ*RVcD*1u;#gA%R%jecWyXYe)T3tJpC=D> z&AN=XcPZ@s>!W*m?T%i?>FlPMkLoH^pagv*3n6k3U9f&75NVBM>fX3+LQbSOT!=q< zR@7K<77Cr|o}(@0wFGV4>Sr>_QjkNDIF~FQ-Ctp&5Hw1U&n>UspP_%3Z8h{u5+FO>{jngmM(kB zcsZMJE#~XP241P#obaj5V)D0b;^>omtKb!o0FCt-XilDUM8?u36ZI1IQPWellNX8` z+A#Akmb=ZY%x$RNsH>;vDCzeW4Y>nuI26<>KC{{;Qz9Gq?=0DH^K;ZZ)J#}Q zi42L$_{W&n6Z&e^$O(7UdW$rZu82$w7qB7?Sk)FwI7>;_*%2jIQN{9GnJRrHr%z_b zg3m5L4_!QY!DlZ-ML*|8y~KDfiY}7k_p7iFV_vxG&dsIlVE;(lmHD8&`3TDROLH%M z+>>7T6(%M%CS5LK>8$SQF7+J3Lr=+*^)Ywke7VVN^upD2G}&y01eeG?+1Nfte@CVn&ONIzK&~gKv*~_5`p=#ROf-_3)Lptt?pHXEV_kAMc!gGjSsXDO3!Hn zNb3NwP=UCE_l*`lEUv&OyP6yAfE{Y^l-TyE#V32kT|@338Mk89D|@!+Xo)9sVxGk z(9A_cw57JT%wFuZ47MqkbyHa7Qf4sLc#eC}l8^ZRPR}UH%2_PW!%?S(SoU=W!)a&K zm#s`N3m?HW^Ci>#>iW;1_o^#c!xD z+f&Ge4qIF{-&hkW|NHKqFj26S4Z!L9u+;;flk`k=hP!2RdE?CwJ*WOWkoyrp8+s}y z`peY5J7E?6+0C^uOIyjRPAxD3ZCh6Uecf&w|E0_E^rE7By8{0`a|+uJp-c0_qSl8P zl}Rw&8cUYBNVSMpfhlWR7CoWD9CY%1Jg)#*HY7$ZD@Ehu)b|`28{1OVpaYGHe2Nso zQzh*8Y)jVfnLPNmySVqFIwa1GHfbi|a>4L#-Dn?ze|VLN5!b|?pA4%jOj`}^*J|2K zL8k2HY$BKbm?ey<@C`F4kS7&$VXe^eDrT}(Z=+$8R5swOKACgv=*0ikBoD~s%ai-d z^%>KbfglD_hy!TxYGY}K{m}Co&U6_}C)&?m^vNlqXj9Sdiede@lecBm z@XQlIn+RvEZ2QbMDTpp!LcwCUi_^t)HzTHxhG_Fc>n`AU!X!~I|DUc7>K9mf6Hz2h zt7rCRxqGo$$UpB}(Ls|>-q`lhZ)br^VBqdTrpZOEVu;IO{j?;6qA|bw<#kpYFaPs8 z?}ytzP$<;z2h-8s9tKF-s(dMD_ZQ&t;Wx!aF6K6(T+sV)QezEa5oM~8!Bf7Zf_AAf z^gMpHMk&(leecmbh6~%#y1cv(WYiN-XfXu<v z8zLy!S}TBSWVJ5&@0wbBj`JXnR}CI+FH4gDeIS1LS+mWnsqM1N4kjgZt9;F}w)_-k zaG(88ew)=5q0x^)^o5-xC$iFNQ}%xgIRf&&ijYWSau6UwSkK76;|#yMOYjR|i{BQY!CR-YIyF%{ zdNsVk8J>)$Gm?~79FzYGEc?S2G#UP4-ILZDZuNM*yZSMJWa&)aV3)?G_kR?c)9E0<^#`uu|R?4HmE-9Z>rq@iY&8-skKiqy^W@XvQ^LLC#qRF^|9C+hOzs?3H){r;DR!W?E;XV@^*yqz)<{Eqk zuezo4RIAImh*ay6l-r=WXm#cTKco4-XNuK670SE^IG2LB5z3w3O((;%KT0((dS3M- zcUNuHITWVBriUUv+zw;6&*@FsO{NUJa#80?Q5({D-?eaTaxm4+z5PzsU+?`_W`6T~ zWzd|=uI2QfhnZYc(gutvCJwy6go)u+zp?5v+7lwu-wt+|5uL^3!7!~`3-r732Iu7f zsgN1vDpfTW10{Bq=^Crd=_mbbl(P9oRoOWfwI(IiDrmWv@A;)b{P2hK?!zxrh3eZp z^?Tp~AI$(xAJz-n$ z_I|Qi#)Ka#R_&a=r^ViQRwuT_3VsN#h3NfjQf@g~$=?_|`h*#F!u)KD_?6w^1w`!( zk(770Uldw=_@oOargjb6lI*9vMZoSBPQu{tk$f5z-HBlfHD(73W|=eIcW|PQsE{^@ ze5=yKv4V6%s&s9;WA1RQ+78<)Xor1dX8#TMTd zulRKemsPFhOtHOQgRNd;Hn;WsxI~6PXGDervCjuj>V67<##_@Kl*wGKVu6@NO#8R6 zi^qY>j=#?=5UqbE5nIXEP0cG)Lo@LO$!t@wB1PK%DYmL0KWD`Q>Lu!R&&&OH#_Egc+X*Ty9*#p`38Baz#=|mp;sZuqerk9XwJjaboMd( zAqi=7-+|R?cPxc}d4usduKwbG%sVeukFgW@2q92kTw3S_`uhRBoW7@Bzou&2eFIM| zO>R?UD(;DmVww337D!2rBAPvf%hklCe2L!*kQ82s$SccNW2m7sf(Z?q~*iCr)8=FvrShY0Kw6ew3$|O@4OwhtTq_aHAwduLceH6QBWQW zdd|(ltvL`dd`~1^qj$JkE>%lviA9I61eD=(<14P3S}!ExU@_r$UP$+uG~szrBOM~O zHn!G4LSL!EHYwf)thV_(@JoR;dxdC|L=KwVxKQhH*!yYE+>|c>{JU{)4521xiGHE5 zYHJtuK^JYZl8Z~qr`Vl}l#=jduIl~jDb_iheaK*QN7sQP=U&Wykl8ZUE78kIm`PnQiVp<%4nv+ZZ%2@ z1Dzhv{_e-eNr%*OJ8As-J(E;jy34C2Cq-zW_dMTx<~*JIDsJ3q>wLS4>XKRp%Eccc|XM_KinIg_Fvk-Rzh zzH!l+&*9R!aRn~8l!&tNEMOPZg&J@t>VW_$-gW+lhk2-fQaPfwoxFUxbd1$o#dRVw zkTCLZDws1ez(W%aX%RYR3!ZxuB7Vmx)hsxnVyaM>-G3Ng3tz^J zi%&FbUN#H@+Mw@gQ1`(Ml&sfB-G5SiO9ytym}9ua$yVF}T&HotXaQe-dH?dvbQT>- z^=zLMx~t2OcH1^pFeeCR97{d$T}3&QS#;RGQAmxqG!_7rZ*?O#ZM1!&9OHQPf;NFM1g`%xU1}k4@T@3#{ApZ!>eV2!_cv|)Gk)d z4~5{*9iA_bx;yg1%vUe8qJLsOT26G;KD!>p*f?P5;$Aj|iDteO@fNlxaHgXQ?U1Fh zx$~OFw_c~0`St%>Q#iV_cUELMwb92qAVoGgQJ!@$VX4dQ*J!`WfNyiRcN!4|E;6!l%ZK8ojWRk05XG(cHat4%W67vdm{MQ?HP0oM)Ot)fn} zDx4D(Zg%l zt7iI2FBDiX-Qq5;e}{(3GxG`1w`N=ueg1oHPHRpyLGcS{oLYAM$!=872ed%;aA7Aj zzO-wq)s&z%G*7JGau$IH_4OEv$FeN0Wtdwic3Qn*C+9eQS+^5qYWK?JxK~PbPeziB zRzbyN1>2E)f24G(g5|F0$m{T9*=J^4^!=L4R>)9c*0?#GzChG0?C~LNb#4#1Ia5e6 z1Z|Nmw7M$^lJNU|Gc5%F)MM%s3I@8{=Po|wlr zZB*#Vi`@Hqy)&G#A8W<{bVg1SSYyf8klBR4T#opao_2e{oqCNh`nf7{@5*HC`a*!&^@_4(NV zZRFlB#`1Aw7N2n7`$+P53a3TPDDUQLc>B|%o}u8w#Z=bcND2VI^l*y@zmLU(Eb0w{ zaXs#9p8XN)7M2T-{aVNO_77{3yZG%4`!6@{c2ezTV-o6&E_1Mdzw`1D(JsVv`29^EQQJwNeFSnpH8Ay8ZX@q2pfsme^kWEJX>4nJQFV7{EM5ybF zq{jlqOdt}I0@xq$bJ;ZB639<~pySiPFzM<{t5ix7@Wm0th3)164LZ)ZZfoyA-!pJ{ zuyOkz3YMEP7>&YX_gTUtyVBgkK{P9Qx9RRuRrbEseHm=T6~uEzxFK#s!NA1o0Cy?n zK88M=p=lzmsx{1pI2-;loFnCV$aoUzfA9`2Z1Df^F37ySMY5Jdv5qS{U1@|{PMB1; zi&?rRwTUFLWRRqRn&wV}y@9WV#$;YX|Jl+PYJ-+uDzmF)e7H4FW|bYbF2}H~vKo`( z_SfDn6IsLjO`fMmj81Ecbl;1QtJg9zQMTouf>W~+_^}vdzWvt==rf6C>dp0|wQ<9g z#O4&xF(=x}8+D2F@2uoKPQKcVl&Lj+H;TAyNRGLz-k}f$M`nmQ&wkp4jbm0cuop$1 z#Y1P|S&^nVX>2ySS*w4wo+t=SlKG5enX#p{i8x4I=4y2=5ub?mlhw{e@1=EvL#Tc- z{~5n?Vm6)Dw4MFg2e@2O8JkhQBQHo0wIc<*XYQe!Hn#p@y^a(A{@5}x9baGZ9_vQB zPjOsAGWEmuUwfz)oDD1Fg4?sAQCMTur)u`ChBiz!{_mo+g^B8*dMVCV>X8>_Yjy*L zbxTrr5xh#7yt_5;$Bhb;=_hyM%83j#98&uKe0a7|FWb9cL0cozpxCb8{&@FfVPjIn zWM1+pSy$&$Tvhd5X6*B#)_CyhgG{>MDQBHA=o3){q-q=^s_YbCejT~nPxtQ z&3~KbBXNEj+mGfC$JRfgIy0}0sP#5O&wcT2bWYbve!iJ(!`|%0B?|lfdFGQY&$6>h zQkq%yV`VoER&LpxS_;Ee=w;b+9=Rg}$H5Q2N?>FuQ+Mt|046~#GE4LF%UXu#(Zoh? z7?PW`3Xr;%M9)}h>1-ZU<1U$ee+rTRY6D?^{2>yN|F;Kd+{@dkZcmi$}Wo7Bx?pC`}4oXrRU zmgzUSTJI|)7omC`*#X}^MKk1+ec~9x?KxZIpS1bCo8X<{>VeG7@6YPk&D725lit!? zTvRdDW=DFj`J!_g_5B`4OxaBcO-8`{|Iuj(+{SK7XZ2Pca`V>?2f=Xr;x?2(ftR}0 zbLk8Bgx&b=`+!SU;Z<7&}TJYdk>n+=X z{U}I*+r6pKLJh%N##@S7-3n|=OqbYOlnQ_UuFsSZSS4+9wl&DV6(#1EZLJhXu-!ZF zdRWzu1~75o%L)!cyzR<|*F5_%TdI|nnaORVt)x<*fc>l21ev@2Ljl^$S_|3)QeDav zuv$OMT{uUpjQkhz&Oe?k>mZ>S3C_%HQvEzLV8k*}o1Os8t@b~OX8x= z0W#hp`~kBV0`vN-U~$e(*=;9cY^>bX)0ch|CO?((bjPLDQfIxJdDfz-ktGhxwaJhO$1 zEzana(qDsM`d2Zb&OhK3zNYNc8|7KXr$FQa*uVNGX;6L#G2$(zK_d2TMu~LyX=7~C z`HHY1Mb!WJwnZwKj1|~(+(wl6#R!!&@9vOx<;ADJ2KVI94@afZ{MJkKs&)h=&!qsNObXHUAI=Uu4P)qiG=|g*=xFPpVNtt~rYj+k;Z1j|u?=0#$q@Gnr}6tZ)&}#l0NTrD8G6g_W7LB;D|q+R_DlHK zZoqPPFOiyannCH$xS<~hb+U(E7u8WGx&5U2+qQ+N2VU&ep1dmkdZWNdbp5%c{i{rJ z+6}i_)_^P@mkxkC9<--t$1jzG9;sqfK2!WxQei*-yBKFH{U4?;`^f{M0qRx^C06^) zQ{N`wh-Fg*oLXW2=?g1^M@tRj`AXuMdS-Bh(9(uQb-hY#GH&M^*R#=1t#dsGdl;Hf z>KA%l+KIOCDkQDcN0)A*4#eeq?eJXLH;$U%VzWOI*Y+pDi)*JkF5#aB;L90bV=4UO zZpz0>;B_bN{#L9a4kc>T3+ZX<8|(KpEnadGDK>XAMhqkD%_P_J?WAhdvigC;u|P@Pgyk;MVX~b!noX?P?rX zm`N;+Lw#IvT=9nZc`L*yK@uwe{>WSQa&@wTgIjqePQVBN3h^Wq5^WD8D;0q*?bZ#kv@h1AxR;@JSC$^Mw@si%cv`L^y{lJi((gT<+!JU0re z*uI|(vsb9EYugpR#b!6)C z$Y?y$N{qQ0S2N!8xz~cYCfh%XTU1Ad<;V@$=gu-O{!yVdi1khmiE+qFgHJzj9M2+a zBD1$i6AQKHYo9R^?f5QO1Ud|~s>e93-!&W+@sPQu^x+B#W6zk9QZC5!k{MfV#@~F% zq)yjsf8Wg6^5WZ#sF?{~h%2KLA*oWZiOqC;!@a~*hPU)VT>=;pn~wGR@QZMjOI(;C z&wE0qEq+p$wLLMHA@+hI4HY3$<>)Uw{X1-j=6E!8_y)Y_*LH4MF)`5)%M8bG62N^A zilzCW)pDOp0ypWyxmL-!Ysu6%47*LJE4fb(>2%D2;h<^fOGC{U3kvk1^Km92A+hbY z$jb;1AdfdMSW=FwJ6I;W4&cSNmNwu}_TEW03$iIn`W}2gZtig%;|& z+oby0V!bPnnoJs8zZN(zg`ZwFskdK|(Yo_h|5KzaD}+E#)N_$rX_H!iDC$4IIb|b{BaJy>D!oP)vQt1=EIzVI`-|FF{ruTX zV-QEZI55RBKxC8YZaSJqGAVycWneFLJ3xz{w?*Q9Po(c|$PlZdz=tG(1xKf1(9pRL zN()!SpSrxaTkE11s5OOy?=z$~61k7HpP+ zs+DNq?LjpgtzIyeW$;IFVx~a=Y}N>JZdp={VrA*_CdQMR8+*@;?X| zv4{&^cF^$b2Lu{1{mhAo;M+<1Q>ZvXTrjmI@C%sKmh>2Z)k%D4#0CE1FmcqnO0So@ zyht(xZ$&mU$36o$+x-W@_b8&x|NSw~RRsyXLZSaXS=os~!0~wgVCb{1Cn&~y1y3?* zxsJ)usgiD31((=1-taGbG~}K7dS0C5;O<9T;2v}qAr2Pu!lrB7B2OUFnlr!hx+bF} zEGJe@saqFZGb)oRcU$Kqh6(pG(Cy~r8}v#RkD5f>gxqT=ybmelGjBq6fB@NKFG*yn zdTn*(MR4v+8$r@#gCa&Q4mDH;#@t|F2W!!4vKE1W5DnEvK=#4od_^qI!3K0ghgB1k zkpJ>_*QRei4bfkvmRTLaI_7RTYuH<_ zsMe2y5gdbziwl2f=_yK#aCk*fw9y%Lb{g?+K`;8qt71!xn~3@SjF>RyIU5h1|6K1! zZ?S!v7j}-dQTb5Ptza`fHTHLwtKm_eQ6Qn1W4H;i)4defj3IzVckXFTI+h*iRi$N? ze$__xH=}}ETM3hdl29?g(RiiO`0;OnR)D=df%ORHrOEMYGW4jr+st3eV4(7PWYm)TcnB({EO_na1~ul`;*ONX1KG zej?QMQehN67&XQO6wkBEwE-^&j3^ThBj zZ$}2wqTbSsHg7h_EmVeDTYB-I0Uo2I0UIH-7g@Mq2BrXef%X_yX+jKrjCcF5exNzN zMlQy_?x!ZBV~`nU^|=|Y>;;~xuxZsU50 zk$LG>|7ExNbw9;oP9Gq~SNZ4X#dr_{&(fr~H0jnK*`*ltvn`u}gE#h<^>9-Ge42^J z{hyB6Plks5s6@KsbqoRZFHfbLHu@I76-g=?MYNMQdC621EHC=)qCgDTX<0KUH3ZEr zY6KZI^jy^TDBTl0yxmu*G+|CK^gXPF{Rh2ckVcehu&S9aA^5Wq<`(kw|xGJl^qkp5qt}Q(09ga2L z?mXkL%Y2l3f%vZq&i7v+ud>>xqk$6q==|>8#pvF+KHEmCHfh8`IgzJfkWFfro-IY; zsfm@5T%lH?ZvN<3zy-eI1e{LG{q=Ee0~0VQXX--3a*MWQd(bsCJQ~pU5}f@SgEHp5 zXv~*$dt~eniFOWHqX$H>#i6={^w7e?1kaR1C<)ziLtP13s5}&Oa#{WfnL!sQ$bAb= zl9h?Lml!1CAhsN`Q2)l~+ouLkmj={6QBu~*xH3I7=pkaGR!&odcm(rhr_>8{D)L;l^e^7>^YO&Jb7)1cG?hk}7{i=UiX^lLB= zn3;f2(2YscPqV;hd%;+l@TGuR;gs9t?l}FaV3TumVroJ6aCjrFtFDq%dFz8x=HXgb zQqL&vZvpFLT%@x9U2u)UX^CCEvR4o+H1^Jj7# z(7*hTWr$lx{MVcgQ|9Z%1&){I=XvaYnJbQ(^Z0mir>lDhPxG{EdM?j;a{?un+ODOtK=`oUz5NecXr;rv&Hn=a#>!IsC6{=yo~db=<_ z&_2uqI^U-3`5Gz)0bRV-m5n$KfV#1KbKaJdQL%-5b;?Z13caH&tTMV%aWX0~>2C`i ztTAILOldE_lmEYHBMb$6`cZze;+8?Rg+1OHMj@G}D~Xk%^WA7@YM-eA)sZYlHD1AF z_#M}LMXfT_Y5)ke+T-OS)MnbF6Te+e$(I$`uYM=kQ&ug(tabj|wv|5GlyzIcIK8gd z@)dm+tJI(3VlNiE!J#50%SmN)vXbtPtqNf6E2RXEbupo1k^T*DAHWUsj+Va>7uzUQ{uj^&UA-f z@s>bHyXbSl_>1(FO>R`w*uIz!w?}U60@k!x_Q#J?1kiG~ma^wxh&SH-h#mA8Ad0$@ z)mF;=D?>%dLGd5BnNo3(-<*>-K$c zkKJ^l5c-qmIfz2$<3+(`@|f4h9?VXq&+%Pd&*=tauw1u}Gaq4@FJKWDf;Sao1m8~Z zDAKb2DUezQx08u4WaOd16JOi_2@2`(42M1v>P&_K&92kRNsu(|eyFFkQ=*ca=f&3W z10MHfs1WetnaeH}cq_X0VN>Wa=ke0uiA%awbWNm#K%-gLceP-#C=~@rqlb(6s!5@3 z;0~ERKm~8nQes|qCm&CvuCHaFVlo+jC-owX^4cKC#IXIei)^V9vbOU<_K^nnKFdH8 z_6Gb$CZeaA;lG-%b`#9-tJ8Za4mm{@J6{Kq-d{Tj-_1v_!^AxvrE3~&8ffhVkweLQ zrG=1^OqcE3i!vsBKhABB8>D|DCM@&1TU^B%;~vAuKV%y5)kU1QKJcCG((AP?NgWEF zaKC8PS6<22bnngn6-q!KME+W{P@?7f&`gN5ukRRq?3pDGzR01(-o!V02LGL|)poah zynna)E4?zA;di%dq@wQc?eZb8Mi0$__6HK3!fO(8g2^Nl& z^OSx-yP7B}NXvGE!1X{=gLbnr{pqDD3jAuve4Ezkp0mNuhsqTyDzg4ZZO1tBJ{t~o zZnZ@(x($_`m`ie-kYPd{kA7LdsoYF9Qke#gXr@1m;_jz$086J<-18N9k2upo8v>UR z4Xql-Lc2MjO;QCF9nS*WMi`oGx4-sW+Ys<0KfJqvjBe%G`4x4Qw6vq7=P1#Q_O#9j zKZRexKRGlc5#@G%JRal&ogtxr@J7YKcps0dI8eZ2A-%f3KWFsk)wZOH*7nzT+ruPW zNYT%KkGsyR_j8$EI}nq;BUCe@Sf1be80x=O^UJcrcG{ogXl_b&Cp1T<%L6_GP1sua zU44^Q&G#)vWwh&a94Y6Ij+GQn%WAYMV&nX}rqS{axPgtrtLg+zE3wfdlwn~1y2f;t zYuOZ1+ACBeO(@o^V{eR#dnHw;^tF{S?yH0vFAwC!_Hr&tKY3}DSYHu)|AzfnRxRsj zOq-q+_EMY55bC(*crH3m^u>Wi;bB`rzoeH<@Y&L zWns!!-ZNtedrgb2`I0tAKcVP~#)7c-cI zuBhJ|LUuZ8U4Oi&K=WB0n~DXDKjzR| zD3o>uIWE|v0+_tl7mWHb>*Wh<+)48u+&!0~MKY8GOWKrrFSk%(E_BqqDgNnV-d8o5 zfxYrfG@_z6$TG4YRHEQFcwh?fyD;-TS+EGs=Vc-#?xZG)Ye);aEm75`fAd#H;q<8t zrDsn22@WBs#!Q4ct3pW8w43Adubnps8Rm;Ok(`2?Up99YeQs4A?%JO`-DfqIIwP2w zTu*m@vsu(oG{l}{MEu7p{`;cwi+RlV`&8cHJHhUSeL1OAkpeKmn2%{&0+U{8D7xHq zl@vuu=R*)fq6Oy+UOkk&sh>ff%qL0#2vZHG;L;vWWSvqu-?dy0a``c=MV)2(+-`5a zu;sX2R&gZb&5lO|;FcHi5a$NV%~6~P5y_c}JGOD)VbritJD%-K;XT4jKTY6pnO^TN zoL?wfv~8Eb$^>hhFnE5vxY4h)3oVxDOcbM1pC%c2S08O6yLc`0&m~o1NA2s72v|bw zsK;L}4M~7DU@+}^SDAU1j66N=*GYf?D}0=DIqEWr zBBfEQ$$5WH&;T#juBUK2eJ)@#^wVfu!_rGc=!V8ri>XEcQRAn9!`Iv9oBL@61<5H{ zS7(yj@3AadwW-xhKNidAsrt7RJ8I=FI9b-a`5WN?6aepUrwQ3BrTwZ)>d+aDFPM7% zFon(h(4^|*yHm;Z68333L)_kl`^>^|R|{{3GgQr7!Mv}2Iz5KX;3(i?YzqFkWP%RV z5>0cg8qkxSNZB-1f4rc8&U8id9ItQWF*Ee7&kb#{FCAVWj=c}MX({NwMpp-n8xFfqZXGpMU-Ez3 zw3f&92zwg;l%fYE-;w5}y^Nr&e}|qyDXl|y4Z!Z%bZGLyB@&7n&qep67*rTd3;yG8 z{50Fp_HOhAz5I12>q#X*@U5YU!QGwFcIrASn8?MZhQM{F5?zFCY?%l>gxSY zB}DM}J(Hsca`*tJ$xvK5GaE@si|3d-1d z$qs_NDT|?nj}N@bQ8l-o z_A?tfhSq&ni>Uz{N(`~lbg&GH`%GMTEu!Z@+yC=VYLL%jqnpf6&p_up!#~jtz*&++ zYaN4JWmRzNI4@LH>a@bs0n_vxnqr>6Cn{I&%#Q&d&F=?7-}4#UIR&#(KX~D*v!t^C(3X#-ml?JA8UqFxz0oLiPJ2o$UccJ&qprX7L&Njgh1+DDr`iF1|MPH5SJHc{8eY3TpT~d`(;#%S|A6A%s9E>N}p0!t9@Mqae{A8Jr(+ zS;n?@VCyT(!seU(n_Uqyo?Wtch(@wYQ2nrp;R~GsS{P<0jz>q=v-;E>CI|GRWg!QA z+;xFa5q)Z+sC|D7Nob^vYxISaj4LbA#WTNTHpo2y*P>D9P?l@5i6X;nfTQaf-e?qA zhUrg=hH^os{l14Lz4u3Bz8TKnyf0zBW&O!l{vNn1K|xI}^2i688_n({0yw%2me^34hrw z&7~DQ^boMziTCnyzAtG8Wd>^m*Xic>`M-he;S!XGrQKaZ5iG)rPw;j754yLG7nke* z$*ui|`HHvRu9ccSl%h7QPg6hb#xrlLG4K?bWhH`m z5STF`q>GEw%AbR7oK-)m%pMk)TAfbz8fN`}Or2#|RnhkK6$C`2L{S<9X{5VB3F&T- z=Fn1yI)Kt5-QC^YUD9yq?(RC|fp_D*?{okA!{!Sgcs6VAxz?Ox{KipTa}Vh3!5{Sf zxP8rNQiRfC`j2m-N=?%0OSK?>o6z4qN37qxk*}t2YbF!;ZE|vkT$T4Zico(AXGP7j z)f5{yq+GC$jcAlpB#*>VU?#R%CI813G4KhZC~R+aV0FBJ)t{^Wd5+8C__=UVlfkDK;O@!2fqwo}8ncBR z$fS0fC3h_U2dYgFt+yaSX3!_r*M^P?-Gli2+7`>Xt}-btDNMLE%YJ$<4m zZ;{C8H56YQu!yjW3QDCHe@@}Ty-nDGz2;l|i(84H`z9YyMSQnC2acIMNkJatGMtVw zS-fKtL_ZUeH#oHXL@Pvh%7Xbkpdh>NTdTZGhAgS5t}93NJkQZjwj&>QnSS>ZZp^54kA?b;YWe{lTJjMUc61>7C>u!!lYhjJNETGQpc)V>uoe(xH*-7 zAJ!HX8R>P|_NQ#WvrrrmXN`o?9psT@4b_|eMAZ70DMxo3pylEA6Z$Mnlti^gBN~?bD1WyR(b-byj?(P_cEw> zr*$UT8%gxSkWlL9TV8QFXWj9UJiYOe)Wq!7t!tTOH;qP@t`PK6&eDOSz2KS^$KSjw z9?xLZDV$mJ+&%03$1ZZDbKI_*1k2Zcx>pU;zV-`vdZhE7C*CAGoUJ!9T(E9H23gzp zzhV|(g{iw6i3+-VUlNFW-h&)MY*}@6?>-Z6zFU#qZxktMw;*h`j`b#+Y`!QHQ&5(K z`*?lz9`*o1K1olj;MP|??Q^NDcIdX(ce@G1!I-FPzEW8xhRK+xF5=xyl`&Rrn~ zR=7#LH35br7EzQ(wWat3=LzgKIm90KWW}159WB!uXlKIMV>;~dd&9rZ%okluB!*lm8{Zli@sglzI zGcm_K+~lU>^zT4BUd;%2dR~vrtzmUPElYjzl#5Bk&zcG%aOvZ7@y>HVR(AJ}I)Uf4KvIfXbT+T9SxQ&(nE2qac>{=C14LIcYWO#ql z>T;T$xC(lFPFdn``W#}>ulG8$?i>02DVJ&--TJq0K zu!5lQ@KmwHgZ_040RS3s0+u}=wmmUkodq#K7a96H$@O$$ zves!k2lWWxk!V+k*0x2ry0C+!+<};jtUx5}XM(^>%L0f??>b@EI8?H)wU%?&rS*h) zhT6t=R&y3}!fVc*cJ$MG2Rn=8$YCCGyvTLp5fSdP32Zc3MES>t`3r6{cU$kTr}Gx1UUV6v1o(mu9P11sqyk8KwWi#xu5c7o&Q=IpbS1b3({zI zfv7Lu1kIeD80_^gW~ty`ZfUzrsjoNy;xIoXhCgofFfV6QaikEdmA{yCJHY%3D@E95 zx=Iw24t)>`QT8fMjo;oP{77-6)0r^vot* ztl8!5JN)`-YV;3G$~T1eY`B<8dKz~gwQQs}7%mZgELNc_nySUznNmYC`}JV?|ErN;f~}gG5rgFqA@=;|EKvNjEN*z7=eexLz}>xPVAXT zzWO7ftMz`)0Ed_g?brPVvJSdE`NSz>)!m;|M`KA}j#z zG@`{d$WI&d?%nqa_Xk1$icZ!#lIB+#MLtkm3Tt?T*~uYODgOMH$y=HH{uA8*(-%Lh zHH+8mWp);Y))O*U+NJLfrBtlHyIo~O|B1#7S}P*1nEm~#-req~Il*edYAOqkIUT^? zJNIGv&w%`66rJG@YVO82dg>oc*ZCVq`7U<66Pj%krYy35!>knykaU;!#t2OqbY=}S zg1ad%sbN6wYDxmzJyY{>ROsF!RZ?Ljrbl59MnYPTw36A^bhtt;yQO)2^=+2E5A3$X z`A5k|CrPH~hrj#GDKlsBuYum5UfhaU1!wV03!&m0plVjnek7t{yBDPpjUz`IDVQvN zj)0?x(<)(U?{@|D$UOCv93H64Ea@XQ)@yJp^cJQ=)%2mKi@x6(-LJR^}NDk7>_&;2UQgX)3o zFaK*?bRi={l)mFtgAvtpi2;#|k6&M{dJ#ig#6yYGLu^s_N*v}!bH{^I>eQ))dBRm? za8D%Nm()VUb9- z0U>}HH%HztsyTMOD#!Ah2K(t` zjjj;?;z_M$golMD^(;74oE3JO(sqa3xL-Da(2F-1&q~!qP$usKex5bo?1{4Lfe)9G z6#YN%_ST9O5765%`|~G%j*!iVJrY15a1pwmth&OPsgS5RmmGpUP8(Bqa$6tEKes%T zCU8757c-QoGSB1H%vpgB@~1=?*s2<6n_E~L!D`MA_1s(6d7?2)v(3I;ukF04A4HLYr~g2YjcAfNTLoSs@xYbE_{ zr^4AI<=>q-Q<|#LnNF$8C+qJm`tXKW)DD|Hu z)VDO5djn_VwJdkf6@<;_8n*4a#b`p<%9(Ste{O#-$t$WK8H6^o9*tJzl8hD9Z0fhG zP47vw>1OtEatCbB;^hbMkR*GDH#%Jp~d=gBkilTZf|H z`xWzn?aBn^N}r4VL{2*8cq=pRu2KrQbk=L$zqY?fuk{o$O*s)x=DI1KGYv(Yq+@Mc;!Y?IA)xQU?;X1oSbzWc z>9Aa?`I((}h`_eqnEU-kdHZ)aZ%O&A$WAl`bP#+PgVH0sqX8gHA}IWcS_%1e6d?Wb zqlA6{;0mgd76afVUK=b%L%d6;A*6@E$?IWuF1mO)yoYjR#tLi=piIBE{au*9h`@vHMcFs>k~_;8kR7(ymO7WxwL%F<;prsCRE@gXZx_LYwS&Pmu- z7J(!ja9Y&Up<}_~M8m>O|DI`C*MCYvLi-^-v*8^GXONj%b~|pq1v)P!jmIs#UBNz# z5to+bkG+Bq%vmHL5Y)$km$X$Pa6QGojm%b($~e%7=EhrDydt4u`=;*UcHf(Vm$EF|8oS-t^jO(;pe=#&*rcW zchPN}MX3?*!!QDV?4_E%>@(>2o{sE@O!HD$f>&nS_oCePIjRw(Cob{m(TP;@cnCG4 zgwozqk+;edog;EYXp(M{hI@!37|ZYjR{bA8sXys~oC!R#LAk|aHK4*Od)$J-IA(Gz z-6~Yv#)HA%e4~$}Hf8I`Xm+HN<7vf5l_LV_p=t0_La1OciC{~iUPe+q!ko;UyH>~k zY#`+5dC)g5&t%`{RX9$7o%6A%a`@E<2`Ja54UnTg1_W54&k;xnQdJ4|*v(PG@q;d$ zDe=fZB!0jt?;X6e@IoyWNtHC^2o~o(A^mBXWK~(16c1|j^qWHTt zf@5F(W%pIV2}iNy$v?#L+~TZT0-1_AX$8%^_I@t5O2v7eBPK*#`cZ!9#8`iQjoj4! z+Sp^8wEaj_uu!V>J7E5Ti1PZS2)F%VGGr>U^5Wu`i>m>QZ72>Nm&1RWm!egn&QL6~ zHwOK%*3S96bZ2s3b*IG5dSdM>nnMSmsc$jFRBVSvH4RafcP9CR;T1Q3%3A*)>L~i1b-Lvy>LKBMg5%0Kz!!Z6m zmn$!DYW0I-L##DpYu;|lgW48auR(8ty*c;Ex`hV935kVq)zT56XN?=RvvfgzQe78DPXMW zotY676TQX2g^KP&a4|F6+0b{rR(+cs~TQJswM2D!k@XU|qRX+lU!|G{dmgGYy z*(F&`9HB^m@EHa1Fvu4R;3)1twL68hD;IgTn=1JEzbibmCVLTHBLE=RytygZab&S% z!#1%jH_#&pS|#{J#Yo&w+J90Yr>I5YlqA`|9&@^!RP*PO5PXxFMPfi;u!56%{e`QL zh~=4l4wiGBe^D2vN!Z2)CN!kN>xA6$r3v(0em)O8P|q@(nIVS>7|DDkT9zy|((%s&{h>0Nnr=SEa0(U9sB-Q3FE* zv;MKBwwZw*qdnriE)38dh(9x>OJzk=M#*gv$B1-ztJ(` zT>+R&OlG3gxRbVvQzu*jB>WV-S_^s(V_(x5y|i=ncvgLILzs&$U%%RGbCC{&t4f#y52zoYv2l~?MAtR~uV{xnVw=1Wc73G86Mt>MJ% z+-#w2eH6kA1btL22Etgr-0@FaELF-`oIMveTgVGE zHGdiDHKyToA#yn*yrc9rm}GxILrIm#>&JAn+hSlHxv3tUj%XBvY)n zjHEquLEq!oKIYq{M?Y%b&aqn9->We{Ex!xy9g;-~+e_;87$L7MES~fGdV>9+)lf0{ z3*&=Zo@~+u?xp2Hsh_mng3#yj?;epZ$81fydXkG`84zB5af-R0at8K}yUa&FIX|Z^ zIGrH@6IheK(h?RDDk!iGz(D*`uf~1x%}X4ix1w<(=Av_~&tGuq5$$e+0U6gn1M=6f zvoWD-9~li1ZNuR%-Z#fs2UzzK5{Pm*iUU6A&ja-azc~g@M#BK-zN=*^2tiThp zxvNF+@=9;`6z{B|LtF7s8=%p?x8k?{{AR%CM9rl@)4uPFBhX(0D6P4;akzp5V0A>Z9?5a zHuB`$6d~5Y5?0^fmx(E3X9tP#B-@^#lx6Bl@cN*xy5W&E4|lwp!#?Z?#JNT7Af<r6E5I{NowWe|Ap_=HH8{+56K`E39&2Ktz(7zq{_vN@G9<8!)_$b8_`^+q{N2T&>Imvhw# zHyhFn>TGI&IAdys-!BjFXK6c|GVX@wU+w0**t7k3?3**ac<}?5vGP!|U`%ta!oBmX zB~`l3u*azlEp~7k9=cFGteEI}xswkJD#ghC53Kaxix|vq!Ezc)$H#+Gpk$F4<30*^ zh4tw)KGGjh(?ypeD-6J+ZbeI- zVy%E}GjY6I>s*0_@~}f3io~_1-eG@Fn4h%w*(PN$QH@@h3CWaADHE)Y&Fw>qlNQCN z)cgtEX9XIKMhHJ!oEKReE_#Bh%!b_Gu@Wc`MO)I#glwo(w=c}@#L9$RG8kz_(bep1 zWG-cN_L5{0`=;mEJ0yS#<;{>8PH$pBuolBmhw9DqMR-T3OB7es z#~O9FEP2r1Ve_@^n%5uS3nM7u$Cqvk&r`X?Ba*DTX!I6Dof{{3PfjG?LzA#tve0~q zb}6OLL>d%)WIj*}sFJ#)#x~{5Q#_0t`w$&T3f_1**n93?tPBs#4n(+ziQ_T~HMzxw z#rcTelAe<;jYU!}aHC2;gx~jKo-zme5r04a-4wxbFB&Ccu7r#A=4h#Ow`RrTIq1{p zng@gl!QGFgQ92MVFP-PqZW116!p>fFR!DBpHiYL?tNCjE$RJm}CLw(FvW|Ca3xRLX zens(;M0TwnemX?qq@N;fj&q40<=`?*Wl0}?*||^hNWI_!bl$;mi(m(hN}(4bmjKLH za|WqHQdykmxb(#Y0h*#&DArvR&TiU0baxg{_?CvM)J>={3;x|754Hn3-*G{nW&hcY z(t6#^*ny37kko5fwV!UgI~7=-4%k{|K{r#JvbRkl4QaNCOp_rwYv)8rm!8 zhZ15xcUCpCp`K$N$w<7j-*ECO2FT{)seZ}w>Rgvkqhk7xr>1WocbWb%4a7&O&W2m6 zk1PMRw3BfLlil~VmZ6~@7nw{bZQ6bBTd%)DE(`UF>zxJeb>>=ZEh48jnIO$2Ns~68 z>s37)SFNj!wUkjK^k5K6YrD;i9`xNpz!OhFIlTLgF_1mJ)tuPjTj`in6E<0qdkGui}<(-sBfi(rc zZ%-LpOABq9L~>g%rJL7_O)Wx~L?^wJ-Rfbwsg*W9=B6xt)}+~8Ls^f-S4(-zkDKom zTZ%mR?=)sepk#L>4AEg`temqLkWE3sLapOQNfO9%?qvwhh;l12oIbcK+2@#NIQWw# zl%#(E8k<(>osOyO39SlvEJCA7m0i^=>PI)i8dFu^_)nc=@6C+_B4tImSbR)EnjL z#DRVKu`MZ^=I`R@fL(8$XsacpIvBlx;#~Y<=s;6(ct*ltz1`6McuKGIF*(H<_9MUk zZ4WxZONI}(FioymdcLw(lVX?s7t_1Fty)yl9znP4m>6%M!qB-}s|joL{-GrlYTmt! z@ty{Q8X2{vb51~hMpB(qCvCn{6<)fy5ZB1$1Y@>Q<)pV=iHogK4JIy26@b9D*((4t zmoolDm`u-BNjdhf#l093a@y)Ihm?j6!d7#6#}B6#0D~;KU}H9?g`uu6hQ(YS^Rf?p z68yLmthy*|WK!bn1)!(oLOoWhY+ zA5U+mNFox0dR;|uC8`d3Gq?Dx1Z#Q*htZmk-)8^1u>)A8Y%tR-V2T`jEh~jm8+xF zYqD-hP8WxarSlzE6ys2>ejGPBG;2OS=#hywB>Yz_|L>|dsg0LSP)bxpUhw{_@3Qx@ z!_P{>f_#MIU6WTo>(m^@xQ*aUSHVMk4nK_ziv;OCoW+tQJ!uSeM#!uN=09$>RMMV_ z5<82Hjtk0fSLu|xdB!yJ{qXl;Fdk%nK~KCsD>O$^+j8e~H#?O^LRCyJa>_a@$9u>I z0fbTL6mGZN8+%snQlNQaP)I=9;;-B7!BrD&ow3LBgRvfN*iU8@NwD6AU!q#!aS-sL z&cwI#mRtq@TYHTdHNju*M50-4gf!1z1rx-Y37U0K2NUwi`xeBXw-9Hcl1&fLs`9dX zj`8V0;FArTAzBl2l`7uI=(wioCS-rCE!St^k1aO4JpcjY8bbCN-iICL^+Pv^S9i2KU0HW2(uXz1dCWE92j0Yyf-Kp^ zyOl*(vpvK)`;+g%ggpHn0z!y)808Kdk$hUNHVx;K(BOh6sVkvpzkQ{mv7DUS3iz9S zCvihzzS5dwNxA7@(>-cRm#U(&ROzxtrz}bSZu*NL4IC|YD_}J;+37-af*h=Ov5N|G zP`5rhf~*nRIyU=9a-3Vtl{T)5QXEdhvlk~i$Jbst&w%U)Uycv$!_%P>{;Lnr%jXNh zxeP=(`#%%wmsVXddskPp!!a^>>mU&Ixa{$@6TatHXbY{0*+XM)>K?~0DjH?x>k+Oy z`E*LHw{IP0s8O~+bp%%=<-0DY6(X&4)c&+ z1tsXzO2=+52^jd6u5j4hH+1*LJ-u>9;`3*LZfeBk2nYi5?w^h5uUDK=tTECbNn(|H z|8V(n(P3K#uo?TNXjBM|r^Mx;@6WTFcR3Tew_)#1o0idjx_c=0`@oUU)NZrfyc*kf z*wGA;WvU;EwdZPl{}JWifVG@RxHa)&^%TVb-kgx;_Q(WUNx@kUKpysMORyM^a$HT4`N4!twJO7%Csmp z3dqT+V^^7dzT_@vG23?_Rn;^{G}`@Z_c!MJ#Dd_v9InTdD_?EyQU$jV=IT%kJg_?T zzB@aKGK(pSO;_U!W0^mH>QxaiCyWpY-N%C{y`x zq6tZ^pb|>lQCPtpov$xf zC->n?@P@NH*ERNHIl7LMdu~4BFefcfJ)0C+t+}Q~H}L+Cbh+*2)uVCOg{+?%5Mu3A z3`Qa~O{+e1HQ*O9!N<1@zL8VY)0Mf4hvT;w`~}2EpNYigbifb$i`UKmKU3NGh)54d zh7>^Oi|?X@6YlLQ9qsU#PKL!BaT zQh47sa-R&I(tFL+JnfGDbLnzB7h>2<;r+E=>Q#WrSPD{BpuEPs{WYXTXc5%?_h{`wL7@UM6Q$DQCAs`}D`Jv5Tm0 zME$``42u&^@z-tG^6}NPm{Z+J$Gh5@jXqZye%I-A_DA#&Yj#C_`AG)?EdfrLs|ndm z*AEm>M;v@|ZbCG^Vpg9-+{?=@_C70GA}8zXKq~a!SZ=2s1O|z^B=fL-B~T6LFl|Y+ z8=%^#L(h5ZW|%G6OXS|PWOon3hnmX0J5WO#{ymLQ%VRi3ocIPy<%4H&~$zZ!W7tQ5lySs}nFi+BZ5wOQtW*ng$PQg)&u8aHS)?)l*Z!|2i1qI{@igrST zZc$%gaDq8Oy#4^N3evKmxp2updF{AgV|{ zU(nF+tz7AH;u%!5a=TGO=%6BAO96yoOx_MxhQ!d*dS#~F)4PCx=~qh};}9!2zXT)h z5Bm_C6|Ofa>Aq<1{k{S+s}gi8eBXMU^Z$Gj|J_TU#WNx4D#3J9XlC5FBRpL$4=2?O z#ex+}mx>#XfATtxh=R?Adfw5R9%ymR=YI?Pvh6;dF;!hWfv9vm&Ki_5nIh^}D7XCJ z{*1b_rYySX_G(sCoaDzJ7(R1Hi&gGkf8j$;tKKOJ|LH~MoVw2Y;U#0Y*;9Zrg-At` zHMy_0bGFYo7Nu1>eH*;*uuxt)?Xiv_pE~v2{4b}~t;b&z1Z*t3@PQZk&RIv1kFY~5 zv-@vxNsPwd+OvKYmVIs6bQ#Z;{5PkEAsfn1bA9o*{Efzjg4Qq?;^B6il<(>~MqeGv zsqQH&IGkj*w7`O59@6=%FsWekPet@EWEgQ4q*uCP|2wP9urX%Zvj3^?Ym6U9Ic004 zSJ?%@L8>XWOW@|51xP$@aYAlLzhBw+U}iqd}6PKF-11fPBz)qn>aco zpanHnF68?LZRa;vC;+cf;h+c)!U~Z$+mx38TPL|HK@&*DeEq0;VI^rLN+Rn9mVE;3%oGPB&Uzsko#lHaO3rYN- z=;rq3&lO`g)pDHJ$gF;>L)C0_%`RzvV1eI6EboP&!w+<^ll03VPy@hi2l~eS3MF2@nqZPjXxC^y- zC~_likvUIEF7}H777?qmOtA)o=2=usv3g!lPm$OmJ7AQ9eXDz~B)o-P@oB!Dt))Lc zV{X7Ifg7A)-C*(ibL;9G9p+7_ZSz4xkmF`>>~23z?EV>zlPA2pVf{g?+V0S9!1HYJ z-||1ze1h_}Bdq3o+W zP3aGE*!T*ZaIhqazR)`O`CS3cV1W`4Y4~;dTS=YCD%~URtC?8Onr* zQLI|z-q|SZdp1m?RSxzsP!*Tv6BBjCP%8CX;%M3KuD`nP$OtqF!;sj5SvGC8#z)ij z$-X#Lar#6J5(NbxxomL2cOwMN`p&k0Ni9$+{em5ZjU@J)FZN8jn(U5ielM`}z)|&_ zpE70{|L>mXwBQ-k_4;I$XE!U-VMgB{se+&aGFJuqN$8M~Os6%>yL#BQI`EapGC=&V z)6wF>At~Yb)w8RT(100i;l}LbDV(h;E!@J1THwHaPY0>_Z`A^RIFX#RG~e*nl1rN; zs?7MYbLGnv#Ip9Q*wHEzuw^~UqaRfJFPgGF?&h*N-ikhvhMv%XQd0~QO`qhIk3QIn(mVtL@w7T5#Ej|gd~DMOP01Dwq+G<ps86!9?o&OZA>F{gTLnAxWa?=`&)f z#%Ck(Z5ZCeg~!T5t5!&>`8dpdupd-dZNAQ8MPDK{Ga~fN-uPkCy&d1Qk^@6ofqUVGDIkzc5QuvaYJ7dV$KJRCSGgbOATrxt+L3 z)w$hwiEp%-isG~VwF>9}R|2pq(Ja$NRljq$)%UKdm$~R>F?0}_F|ANRD=_HqIZo;x zn0oArw3<#>V;RyDkP02Hle0=LjP4_oO#L%(qbpz$H;__%JGp`J{53u_UmddI9jHzi^&@te3jx zcfA-LO&9g%*6J(Otfut|B?z4#$47+)BtMs}$OU_>5TDbs`OepIeucBgUh$< z7LMalG_D3cY*y;EV%c_XVc$*fz;`|p5>8UATGO9(D!#}KCgtO?5>ID_3KsPerxxUg z3KWUU$fTa; zH>f|sODQUn`p_>%T_Hf*WiGGwho`Qy$CEQ1n0vXbwq$32<#&S}@L-VD9R5!RtA;6^ zAwKCy=vNr^Ef|tPK4Qvt@%n-9n?pgQ?nJSg|+}p zdZuJBOx$5uOE9hZ>O(Pt;(@fEXwpohXN(nuj{EYMRic!4%_!NazwvVQ%JUmwwM0y) z0)*V4oqXe<7zEn4s>2Y?B~}ShV8TNkw*g zWN)Uew^uA^C8XX1D!~N&!sPyc6b);-#eR!?SQj~QxYn0NeZ{^IH%Fwu8CO`qI$kn6 zGF)~B`C>Kcw3&zxbeA356#bKEuCL{tw)|b{e^zw?x*xxkNq;1%a2>k3+WdlfJ)(N zkF5<;>kZLqnXA@k3Kye8qG*f(my0=v{LTD-IXnRY&e36%gF3$!K3SKqf6CNr_0WOV zA1y65zu`jNJ$1f+ie-KPkVty~WO4U5X2oJQWHDK4TKvmFK-yu>Jna=9E5L9J{@i#_ zVavhW7F=tE;_<(d^idbjxzH(re_`@-A6}s`8S}83v7x=(A**@Dk~I(aifx3=4ohTRuOy?h27f{*Ya$D_*GGXov+LGpPrDEv{P~ndBYa} zcjTFT@9EZ0TD}_(LgXLFbDV}CP7R;q?ZO}2504$MF6JHY$T>j|6}c1f`|`~5`4!!I zoBU*n0JHIW)-Dsj0OGKdg>$h4hJ1;<64Z>OB+SzOY_n?Z_Mc%FaHdp2ctJwJ`GT}- z`Czv45&%k(TM`KBcf5e?DWyWMPuIP9scPbd+D7QU#s!qw)@LOBp~nq+3hZckgdgt% zX&xv^7`@n zW899cS8dFlPh}|n`T-3UzU9$ef$=Yes;2HZO*!;E- zk;y8N>xu&ImeC=*b5jBuWU|r`lgNHX(shJaFs%=TD1EgPrfOj!CzT38RAWdomyNg6 zg}Y%)mHo_Qvpk_jC*lGUHG-$)l{%3_Cr0`G05Sb4zO@rMjL>+xnLTeAi`^vg(7RKy z0q;~~_JkoKO5;qB%$x&mf1dDPgN)BrM&eb@yXY!|p*KTG=W#JbuBzFzUwCn+$YaB6 zkg>KmnWi?%=}j&967rZkuN0)x*~9ZV)c%M{?&L6o30VygwGGruYhE4SfC2cEf0aUP zIVj?^PDyUiO}lbSX54R$hwf1EuTIOTkzq^lk59uxoZB3PACreOe3OFfpFEbA$IR4t zy~W?dy|$CwW5j((I&-@fiobFl&r&(gI=wo2W6OhhuqE6XJ%PGlPqd}_#6w_H{hGz3d)uZ6tE z#%9Ep{(bkt#dU4rvh8@0p@7$!EAIj_mr57rlsLs}ySgkb+fitn(=!W7V=^x$# z1{3===1(`0_|In#)n_aW!gEyS7GsXW{XV^(HbjByXI-{+N;Jy(TtW8yT6kes%md<{~wg;e`@Ir57%@| zpVc^0e%Y7aFXfPF)qWF99ZSj_wZ)s9qiVW<6&y*Sygy|>3w^yWNPo+$M&1?SOY12m z_4ead3`(OIKgPYrobS(Aj>@6X@kwIu#xz*+h}Y!XDh&fEd*}c1U7VXx>hwR()@^px zVxpfiUK2#^<$DqBqx_PFf(Q@I`{L7;qZ5F~X!tqmJL+*x1k2MrBEWepjS+oaVYcdBbTN(z=Gz^e+ja9CrO@FfJ-N?u zC9r<6cVtGza>HBpTcrl@>dTR>$Om2o;3%P7-S`mMNj=agLcJsJPh@hp zu}0-MR^2)sb@!Pau3UVmouOFL*)t*=^cQMapv>I-bzB*wAgUFl$%DL;w%WTVY6H(& zvWMO|c08vtzaTc8Oxo9HX45FuKTR4+=H)@nYEumO}3~jlp`5=~8f}QXCrUR2k4h2TPHOq`b zaz82S~x_Oq&OEe=>O8$-*i{~XgBquB5e+y1vyZs)FS5cZ^#~<0PycJSH>~Q(0 z1MxIyv0v*B=Ie{IvFoO~R9F%ns8zihBL#GHBPDlmSSvV9Url`=Zr0;?7(yV@BO%@hlUnhM@& z5IN20Cq^&UJOfG43Ed_nBsgq@i|GJQ zro_(NbppkNJAI9@U%z|-OVkeB!VsY&L@Rq%zqiAj?w;Z_o>1ULz$7Bn+{Ok|oZ0>1 zJasxtGO|PV-?`zxH^vuBtxCu1G)NtHb@__*Z~!YzU5ieBM?rx?@1>&*OKJFe6?Ki<!5rpW6T!Cic+UG-bs z(&%DqE;N2iB)!4CHaG?}kL)gwW|^>KzNyVUORfW9R{AUA?UjTLO;6KTi;^jn{~;zj*Ahnd`Is zJno9`q`KW6O3d9aZPjHp8GN}_*S^sC!xOaWu_%K-llzk(OA_G5>FxUq-lr1-r1{VG zD@Xi%M>@yW0gN&tbtEN6#J1f;CfAbWGJjTw;VvIi)3I)j#PkLon)i2j9*)W^R2%g{ zk7_OVL%O*0KdXI9cjv7y8pcSTfr;Mo!i0ycQ^pf z*=kBQXiE#~OG6wAt5a*QPh*CtvBub@r)sFT2#@}GPzeQwJw zF_J1})qyj~s2l!P{>M${!?qlZrW;Gn>%r{v0SxVd1VSjU!s_lzA8#4qSDufx82lIV z&6^B1s9OY_)~}%l5`IsKJpl9z2*TqztQv2iTuOEjNnj)TY(hP!)i9(Fn_qrV1+qXM z39GN8X zp@gy(-_rmN@3$UOQc~)KwkPc<{tR1sfM*+tCB@xs366)n+mv;_wfPQP0n>(eDd(9L zw3OPjCdK`%k4a1baJT8V6C64MfN7qcH17+5-ooA_CzHg*%%^p|s$q2J{PHH$eS>@s zNQJBr1~;$s>D*fX;thrEA+G^oGhUms@S zj$~W&pFouY5Tfepk9fCBa|>^v!$k2;j%Ljz^_MI<;HzlO#d_uB*yBG}B%{Ka7)pjU{%6t1`?X{srJax7k=Ol$46`q>-avhg=!bmt z?1}@ShVgC5R2aIV6gQ;U56EF&>-w*=60I~3N)v`=oK@Mt$w0JFj^6!^=fz1<EG)ucEb1}eqeZh~eO6XlU|qq8<#YKWRwRX(@*$meW zd)Cm${AM%;{rix0T&95x$>8^95EB=>VS)K!(^uv!kDE0S!G#RBqqC_KW9xDXWaHz5 ziQUZXO+(b{yjqpEzc#Hls{JS|=3hj%I9t^LigJQq_W)NryL0}hH%3&S6V)79>mxwI zBd@}t+?X!WDFlt@Ac3T7^i2y*B(k5NSc$&fMor6iMgmu|G2Be+_Xah&@LbQ^T%i=~ zSR6@WKfO~q^*Tv9|3-V(R&iQPRQQwaI|FWIv#fs5A@@)oC>i>csKr$K^&Z$&UCh+Fc4#nFy40v-HMTn?9;HG z7Npo=kA&Ypb`*`t2^GDdmoNnY%uiiE0+|ZmQx4a>ppE{sEnrgZ=5ixBqB?rvjZSm1 z;N|rZf^3j_9}pn0GgUGLl&-X+@FD0;%EW-uHn*4llAwtHc-SW*)Gz;6FI~yA5tzQO z=2ueMEm@Qjgh>H z2vFD&Yw6ebizRWf=GfPiy{3RP<1Tb#r}Tedxt1IU&$+^+C%xjzKRKEE8MAw8dDZQs z$RzWq{PqYs>!2)elOiH7c}qmzb&P%zt5W?_p*TUU;(A-A#4BgsWqONjoKHExylt1p z99Zh`tx63o^!Juf&p{#quMFwp5sN^7Q^qbBZlwHdpcI>(0T0XD zHzs^41D4~I5!xl8#n(k~&ipxw#bbSy`C4rrD*fA)M;y^|>t~VQZ1Ud~$U)Ktp+iFE z3okJ8-cQZ6+t3J;)3Yuz(5id85<`j zCuz!_=J_jAoJT4}%C-QaozqeP$rGlNiXlGcFPjH-8!$4UMavCnD13dvV>yjC%MQ%1 zJB3^TzaJ$?6?%R8^eC#@v7xw|wPqHd0r442{AMdBjcS(ux17#j0_sI7y0F_)88!l= zEM$D&ji3q(T0$BU4}9#0+NF+AezBMzKq*#2v?QiypB_+WgX1b~)3noB6HlsX;INkY zgG+1)R%#pM-nZ$4L4_HLLnBHfvp$qc4nsZ`Ar)B$l8nwCmu0csVJ83D@VMjsix@$Fn|K2xPRgdIkm|JZu#GHX8qTSlCg=Veowt;POzT z{@n6pD?B5@%-R~qGo*jTy$Ye$S&WT%x;H%2m#2V?Luxg!~X?*HmOd1{wM15{^&Ral4e|UP)|5=4+=gf-&fYm zR-jCLqT&#eq8@k9oDW5Hm@Jx8Yj;1}zp%E_o%Lz$6|Pjm|do5FK=E@a6tTZGp8+dcYR5Y zt7aV^X1nz6|Y1tLB*Xh?EaLYMM zG-B~Ze-ShGwf`=(s{HbuPTB0Q#&}j%?!=nW^`u;LKD?hwu`1x?!XaPXmyD(DO1P58 ztkc`>`nbEf=$^G>w}~OA5N=Y`!%a$zn$*F8!>tSU>}9HxU8k)<|9?MBP`4eIA;Tb3 zXS$aGu<~XA{eJo=f#Y>YPFwnf-IW{lJ3iiv9p;~6aV1CKy-tm1V8qWhNL#=))&){C8!gqbetz?4^s99`<*fhfnlOt z!g}s-VZGahhr6`%X~PKN7(3h%?7Al+e;^}3r__zp?NK*|!C=~_V3$6Nz8Iz@(pM3y z7(CIhWuBQWZi{1VY$~3|bOVRT^GKmlY}$6n(KG>7)-%^%jT$lD0^&5GWIfz=yKkYb z)qypyN(E}dh0ey6#N5;Ok?hJMQMl@Nx=a|KTxC$6cpN^)e*CG7z+R}mL)-)0?Y@-3 zf-q>G-*KCe+HD=|ys1c-A0`3Qg}Gm2jGje2Tt;XQL&(PCb3!~S(gQarGg7sA_JO{; z(w?AuKh`6SLa_(?R%8q`fjeY^3x8=+Aoz#Pj~Z4KA#kq#GU%lkv~0RBmbs(06@>~6 z>9PNV)&s$Gu+g16pr-~YZpv8S5K5jP_?<&zQfDW(s1U1~c&cMjxDnqL7s zmHF!wcoL^jqN0N9jpH{i{zUSHyZItogmhhgpNeepa3DZ>GGZnVM(XQoF6uaMH%_Zj zw89j>k$~QuP><*n{Q16i;{ok1`T6hc_#|yAPqi?jN|$%0m~@yw7pvr1@=D5tc`Q;~ovf0?GyJ(=Fi=mCvzqEg7DRgPI(N z3`#vy1qC~w{e&eygs_Elj7*dq{MGDMf`B(b^|XkxVb$?Ocse;%y;l7EEh8)Zqwbmq zTuV*4hj1J|lBXre4l!Xb*KR;hWq)sOyJLIT;{1?#PU)7OQ~}MxVGd0R=&dzX^WDIv z>(l@>Knco;Rq+oZ*l&yX)1aw*SUXiC+l(d$Jc&c)WEI$bL|Eo;&c>&NPM|;L4IfJ1 zL)%7EvQp}^TBAY7-&6R(>!>w@m_U2q9?~&E$ay@54Hiz1>CkDQY0z%q^B@X?cFG!A zLII$A>7VY{^NrynZ$<3+DWJ-HdIYFZy-uJ>rgbrMPC*X!3Z4fH$sIl){SrjqQXTKs zu7qQK6Ic>s5y_zKx(9lmI6Bcs!o*$N-Q8(cB5x(QC>60rn;IZ{7m?~LPrN&QO@&s% zxP5tlmL&|Q3F$&_&auT&?Zn1MJTD{ZDEUE~W%Rn1HKFeouQt-Chhj|{ELtIx;3xvz zEYxpr#7R3z8OS<$@Yg(2t`fH*vsaItTW++$>>KYZUI_uj=*z4hdmUFU^q$`1{7!zA z{G)f^CdJ8^RCbrvL3YohP+Qp9Kg18-cBIQ^0<(_0;!zAs3l~k4nNU-c0!QK`yqa#g zRDD_UIk*U{2McX+7(l50W$P^=7@f|{!FH%b8)k+gLHYL*&^dJ2VyYUPGd$fZt(Jcc zGu)kWpW4V)qP8rvrM_-qOk1rHDYy_x8rTG~Rf9-27Y$H*E)y68k6-Pd`>|0PnkzgiSZ<-LKdr;`@$ zNVT)KJ(h;kG9PX`D+JjbyTJKFDePJ=aLA!`p#L}AR0v$`@P`kp+fu{+9 zwQR_xEegGoalz=7whdKr$I9yW>(d+|%Kwgu*24eKfssAPRBnD|?}nw3oKYNbbg*(RU<84`jb1sI(L30*j0(Tn50uKr$HWAPwxqVxdBi$@ zPW6#RKV-@_3zVy@RT!;FTT(6*+j!ou_?X({Ce-obEO5|v{=2}D_!aZ!a9FEUmNiO| zEV%d-yDush74qlcisDPN|C%J{%Sh@sn3;dCD@lqUjC|?HTd;JsUrlcwQcZ{v za)xG>A;3GvOjg*C#4cCs^a797yi@!$~Qnsc9LkiMYK%A zhHu@Cq?q9d(N z_Ls%ot^bZ}+%hLcr15`vX=!t~G>m5HcI=>*glVKmmf>6EqHX_ixr@HhBguEMX#?i& zYrq9NncC7DW5Bw=o5$;BvDVADdn{UmfSpD8y?_$pXU0L)o-UEfT>_naKX?4GLfrD~CE5w{Ab533{O^M`>nbx=SC*aTcp*qsF8T;lwibD}TtYHCbQTMyt8 zl=#o-Q0T}E<_A9$tfHY^0wwh$!Wp9Ccu!2;(px>tyYj*Hps|dWBa6H?+%duYK#cTv zJ~(u5hPIowl{4tM5CCx$a{;%C4T&+(rAm>}@I;C~VpOSMGCiRuLm)Xx3On&0Mt?Fa z{9G&sts_+U9}Fs!62tCqRGPQ+a}h%A63g-SJ_!smj}Z2DSriOv4_lhA9w75YBf_KQ zBS2RZ80QQX%Z+y1GO!6PskD4blBYyy^*jK!(J|<(`ib000LrsRDln{3zbo}yQ9ZZg z+Y|iz$^%@_L%&~MNMvMsYbhoC@eb$D~8$=_IpMu?fSEZw2?B2<->nsQIdGds!TwW-l2R zk6sD?KHIXlAD}dQU=IYrc~i;$dRl45PWg!>H2k{f&oRzmu2<7N?WNR4~7#&Z^x@ zra5}K3jwKAjXpEdUl=%VQqGN;XeC;n>%(ZzXmzcM%_#pZ`~E90kH*t9mvLG^o|Q{j z$Nh%%-9GH`DRzm{ll@Nl>23D+uev%#?$j;Xa;&gC`(`DgicFiqa-zn(zukeqaI^uZ z+L?SBOaQ2r&jh;MGzvi=3m2a3AYZH{uBHpu#!VANe z9z;6vCM6orwKVaQ{di0G18&#|41ugD1_IEbpX}v6V4?0A@C#45@1NpE%2K+x!fCsq>V3xOQnqXxnMN)B4iIz2qha`NCvx5Gqa0?LHVE&El*T)aoCE(AiJ`CCuwC{k z*r|Cks`c1TfE0WW5D^D`IvG+~IP_?dwtQKFas#ty z{0`M$3cHrdu)F$C=R7~$=;Wt1S&c5Y#~&P=<{}fN$`fZdAvE&>I8sET&pvn8O@P;x zJYzDWZD%Vgkc^v+8VNuzUX;9YF;2)@loDDNr@syl4=?=~kjbL%*iq4GKmrRQIq^8y zq)A(yI(fd-D~bY1zXTc{$O)miq28P9uICybJFK7Wrue+$93>#pvK5(nK`}4na^*rD zM7o$%jbR<9n%*2?3zDnINiNl@qsHyHLvkLY9Nn)b*&YKTDR#PP`=?Mc&x3QSC#}!j zaS2@1&DM+(&#`8ygvO2do?|*fq^>4PJc)>v%1T4$zIk5t>jt6+g^`5mvedf)b?fAi zyf)Y;i?GZv6@Tp8XP7f9{~Hk5rLOei@(?z7)7Z3g5^nXK>BN4dd8F^v`)9|s+?MY1 z@@BaCoLU4G*;2`0`^di(E$k^r&)ldWOAQRB|oM^{Ye_xM-}oF z8i$6MEA2AtymbHzzO!E|>Q#HJR)6)&;%s+2r>d-8hq6wVsN#1iwp*EEd0EsY%gc6y zDg3nXeC@42tl*9OF49%x{bHLK_!oD>rnlaG0^{aXX?hV1> z339c3OQUuJRJUzSFLb-S;a>A&Zt2bO-ig+T+&rdy)`GK}TdO?hTwyf@Y-^4s6S zr+1?ic{-%B3%_@1irNHN@ttkYkga1#|F%zXwzPcnW+t+tzU-)yw!p(sA~c@0w9+x* z!GLC~V7+16XrN04>1ysgShL$kEHbUu>9$`Cs(DznLR%9VW4v?uE(NWXQXJ1Dg+AmS zV63%-ve=4xyB8=-lw5q z6D;WH;??KwPi{2IG+NUzYA!0h@Z`$xF8VGvC^Hs2mvhLGTNK!BI4|*hQ(nnj9fgez zxmxr7^yy-^>>nz|mgO6pcSaL7EqCG%H3mfd;eetQ-8WV+Nks5LurRrpeqgF}#h4In z(SQMJR&Wl0y@MbYQ>=)Y%e({8Kd>zSN||2r`y|rb6NJvgVk&Ci49c3xzo7Ky&zgZE zek3<6R3>)JP0>?^(^C$edvA=a%;Aq-eKE9fb!u>H+4?xPM{fmta?hE`DnA9(`+z7y z<>Xno3>gU#80WiWVjw+*SZdBz-*?3NBNFQJe?*yer$wi89d$-8&bB_&TT?z-=0^lJ zQMVjcCXayMUo=qZTu&Y;COSjiS@-F1cC(f9^2Y|!T&+Wh*)@MUZ$C?vD{c9$(%~gZ7`=(nFbBQwN%!{6{FQ;fe?}^(SYN)(V z@lsTsxZV)W4oWI-ny4|01llnQMW8|Er@02B-&3LkLBCL5oxL>*U4Nl=sQDqB^H1!aRco00hjSHIZ3!FD)tp==!1A#z{ZwQ&A4;{A-vn}Y2&eTk$!VeE((E_PP3DqD#k7apjRrxsTnvUm_&x=bbPw{aFxe8u z)|Vdc1syEknGgur2G9ej7#4Xg;#j9>=V=I#&^}&V2AN`9=GiQ4)<{$uHXC$(7xn;+ z01Q^iD^t2bk1xMNAnoFAc)cRQz>R-^7V&QeY&q87?#I)7^rLMSc?tW4?%P<=E73sP zYJqQ;Hh>|f^bQ2Rp-oG>8Zc>>q%^6k*kflP7sr^1y8{}#CuZi)s~5X>dVQ`Y)O0B^ zJ+pS~R?v(P+JGuQLl`W6!yFXPIExpb{Wp=*E}2Ixr{=U)^W1DZrv^5Su6Qqu$?*YYv*Ke-cWzmCwRs5SH(nmgqiMKUk(?VAebusu{5baRK<32K-X8b3$nn_p9e50tj@$h~GdxYWi6p4Fpe zg(`(S)0Urnr#*!?@0@$T2yooV4r-tJE59j7VM-$33E1+Wh0DAWaNzn$8}*~GvVC%6 zk|bHA`6rKxyi&6UP`jmY0E52ET>#uPoU$ttdpWdFiCo^P{%cpTbDZi(UA(Bt@Z^^M zZlw=FgYZ7SVZMc2m`5j4tqkwADs9+t)+>^}0&}+^s|9tf0`q1Wwb>&zZHMlz zubMh}LzY2x)nx_eVkneoiVfT)#Al<2&6#Iq+(Jboey?+XAq>`{aA*D`)2rkUTxq=9 z<->~{_(jj8Ys`b9#1i!8Yb~zOkvCNmG-6!iT@X!Pkm*1T zlUk6A#yw#ms8-+L>DD-tQZ}06^QTrrApxuUC{!U*V9G^;qgGwFI)nWzH1E=fEjceT zK`olh5E2yRJbpoQaKw$Bu)5y5ej^^g&PDud3HjoS+F9IZE{Ti7cLsq{^prSea?xzo z-9&nU?S;y3(btS=_NF=<*VgwZ=UlG&wf(n_9@$!dUnB2DpMTNJf{M~rcn5ZIvuORY zxq6(2*%CWmCoZTgt_k03BM&It7oswZ(yD4iw$f2mqXtbs3^_Lw5Z?hK(H6`GTM7Up zE|O1L9`{LtMfLI|?SX<=rtEJ}D)Ptql}GR&76pgljoqvOAX;jhxQ(T`yVZ_9C`#gN z)6N~}Nw%~TUCcXQ3AAb>A_}HZI;E+!>PWwzW{j?%nXOYThVy64HtFUGsd?K&fkNVT z_|{{E<;#WptF#B+@s6AMi{?_$Gf+Y)%`S$}rN}hrdPXSQv(Ws*?&V;f#wBchpO>K( zxel*y)Cs#e=@?TT71cXGn2lx$*Pjez^$X6yymPq!#~ksvF-%}YFHAgh5UjFcY^L9( za_zqW-+0#)wo>x}djEa!{w)z&*l?sRF$YetI)5&(nvh}7= z7C;k<3A-j(h7FwmvAPZ9lP5;Ev6)W~2X35B=BBnEle$bX;~D|EM1%#?MrfneDW6nO zUA2n#7}NScppEiVB>rSBK>Yn>p#NjiECi-~kp)vf4F(jtDaMVqVo+goL-5LF$XpexbiE@_i_VUFoDE1#Jj1g6>rYmY;Y;letHvp z2)gwB%FPtf^mU<5;uW)IbqvYq7Lhxs$9C56LrS#n`w98ds1hZZvr?a@vln{RsKv$|rF4+wG< zWtfEL(U@df{Z3G!oyod7NmD6P-$Jyt&Rg&5;!9Q)Y-%t>@l^3LRK7TS`ws6UGhh{~SJJ z-5}Ef=yF+vfJVb%0ciU} zCmYYWi5Y2rfIZbcykqjxcz#gg|DbxUMrib%I?L?{6&U3rxkaGbc7cHw0j9iP4)|5p?15qly)KW z?%vn}=RAepi6YB^`UXvtcR#e^Fi>o@g_8&YdWcvZm$eA2qSl9N?YsELy_8frb%wSV zG-0V@*)mv5^uIj;2i=q76plCUe?+&Dy?~r6OjhXb6abVyL~gSf(^JD#<#INrsgm%;wzF&H-(_LU>#QdAgPK!Qq)Gtx3u08_33>>Yn?n zr)E42_2tJuV`C#@m;)^`3U|3CN^NO1^OENKo)dK<3YJJ7&w)qw@*~kF#^yuZ+*OWu z6<>9~{x%mQrBW%a)7KIs2Tw}LB(gVjv(Nk~P^|V$tgFF^?ITy|jcv~_AJS6UgO{tu ze=uD_1SB<^=I_ItGK(8GMDOmmsdr4g?utE+rl_~KXV|BrL(~2Sj=GT}OD%Q3hgZYi zWqU`x^4=7y#~+(|9)t4M2YWr4{AGDQBD7Ticj32DL7Gz8tg*2Xd58VmZ{+Q&hpUV% z%B8oeF1n>R3LWQ@Z8sDdlcKm-zwp-2+i!JpgUXp4`hESMndGf3_+lT>kUnxFNObNn z?(PDyoNmUMl5S?-SrryWCe5gn*XtYlK6j|iK2B`>PA=V776b%dm;5Bn4!wVw_lWE# zcSMq`Qz<-!hcobJzRx#*?~RDl%K|c^d5h6Uf>$_Z?Lo+zr(%m7dAOvlkV9_=f4D>< zD*?Qjx&iQj1#$aDi3csxeqmJjYZ&fp8klSAK*+#7vq|N$=;OnH09IDVc`f+tcZaYX znF4@=Sy>AJqvCdiJjF$QD3;Veu;v%UxUgQgE1zK3oTE?OIgi0Mw)`z8c)R-zxD<_O zD7Qk+s9v|?g3t!igoG}A5^`|(wQE8B@`E%Nmb?759?{GpJ{Fvft>xa!#E0*cE6T})>WdhsN{mHd&l!&9s zOucWp_e&#@@*Kbpqh7tQ;{Ca9fSxh};@2JkFa`U{Xy+7g&T``|v&g4YOmli0z5+$xVC;>nb^=4vEa8qEAgnd{r75=DplJ(+|xfmfV1Gz&NOqW&%JFg%(i6w?K8L_ z5uJL=_Rh+;HC)qQ%COlNB0O!3(65Su))gJ5Sy|=+l=1 zV_lqR)oW@^Y>3U}L z+uSu63!WrQ607BMj}cdYkgKj?4^WPVD3g7LDcbt6=ItNyH#ln4BWAgsBZu^#X9(++ z`iU4s;Kn`YuQRQtN7W7ec(3~fSB+pPOEjAkPS;!^A}J%tmg0~$ zNchD_W%tXhaA{B%&Y=LY-G}6XUo-vwFl{c~c&$4UZO1kA`je>SvsG^H@#2Z?f#Mqc z+4L{X8H#0Nw<4uHZ#20kMsg?3sj+uJUrkbushTcM9p3AYBkRvx;%ypF^FJh$byR=i z&xDe0TDK=n7!*b1w~=eeJ=(&1(bW z9z@m`juXp@Bmom)GT+u36zF0S3T09PL*Az%e(%_p6eIwH>2uA6Xk#Q~Ki<`sOAf^( zM$X5*j6sdz6ueyEGKcExwRl}BFj2cvzupka$`|9l@8>vCpFHdPd0Ybt`GDE9pL^fS za?j~feQgu%oHMHE`hd^y8arSaZ5&_3>;R#F6ZS% zgRCLmF@%j51_ ze{D{PMP=H*$-C{(`J;KtnX`@c8AvT5T5VLsbMy?8Kf-!reXi{e<<{HH%Zw-7F<}l4 zS|;6l+@I$B1UX-180)cd_}D`Stf@gQ+CQ1*%c>c-#8**DGPY>DXno zm?9`Lf~e+=Zehy|vEHK^k6ABt-5++5_}*V|Ou1B!ZB44mSPSQy+o#m}yE1aeu2ioH z-!%v2p{jzIt#>Csj~lyVXdUd*SYAlC`@OHZpWOdBA6G}a%${7uTkZMLwM3+UiF!cp zm&rW-*2OoEfl(8+OPeKhd%l7{9-&{!+;ioI@5%UV{R43k(uV&cmEo$xfiV4K2l2g= z^9~cR9P2AuRzl>@t^QtFb1Jraxvp(*5~5^J!)^WaSxd?AwfTk^`1%#zWb%h%_q=;j zSJ(#+LvX!v`i3}dDDW%>W$P53AKS<7!HAy1Ei?JPvk(`&9hPcTR1L6?{5gY9-7o6RP~>`DM(^4scM)KU@Soj4SmU=L*!wcI`^! zmF0&kCQmSi(iI9yVHdi{`vri~FJR_uXMdxj!zb?zUz<7;Y5Q(aXfANhff@N)WI5q$&Z2V z8?rd3CK0_ruO!_b)^Y%=HEj>4AInAuoZ%>xR>neZl1K>lVFN#8$)fdRi0EsIX`?xz=+ z46NCgyb;ai?dY?q&xyJgfwq-Af0|8WC*=ax6g{PX)i*i3is9mZ!S{)a2tVyuOf-St zYn?l7M05T)j!hAbXhdLi_=y`kbC!z+n$LJX!Cq^BT5~Y&LFGg6hTl0Dv~jiSn)9Pv zhIhDs2PjucHCEhO!`}mOZSI(MpUR!yXwlAV#nPziy*XLeHo zeFUJToU|9)GepCLeK&8s&bJSI76?@9Bi82!>y@nIWif^U|8^3Rm!ayl zXi;2xt=_f~FQm>Zwu__O#!{C8(OGvohpuCd#LD%_B#HN4Y~5jl3Vgw-tgMmttGS9Ck?Z5k0OKuZ&JKVh1UW zl9@i_>P84SPPa*JrH=&VnPj&}JfGGSa6eW3t^D)=v@{n(S86e5VIizYoc#Akv>JMp z=>iUo(3jbY!JS7oU8xnXIA~hsxR!PCo7izA!yXDg+Ed$L8%(0vk6XyUS||6wsbUNc z2h^VX)Lh9YUc4Qt-0`&ZA3G>TskW?wiG%fTQJ1|3jHR^^jQ(Ap8B+;~rSY=UTtNWi z08jA#wH%D1UBo`6A|wj!!XuJu%Hfm9w1Y9*H{4Lv>F-qTJ&p{2Xuop^EqbGhCSm$b z^!1X(lq=|ga-BwTaElVJ(6%IMSp2G63!wa=j6a}KKSS1Hjz zS*t_Jo((|uys-&%%ZsGrRq2$~KHTg~skJ9T|H6j1{?zYBqq_(;Jlj^)bwLO4+jk>X zy{j~jK=ykFIXFUCbP8_6$0d1(I2oV!I~4y^EB@-fkpG(zJco*TDx48Ciyj#y!xqgu zpHy;4g7^IAOYf$MfQMJiV?Ub~-haJ?+b_pcA|sBi5DD<~1+6RYxw}hNbM~SSCm-B# z2l7o1zN;LEtTue_Qx_!vUc_fQuFrR1MLm^1sc+tUQCV(UI$da)JA+qDuvdkS#t@;T z%7T!pE*u(=uwp41kHILJ{LOPKL(aUz`YN@JU9tlg7$xAqTxxNsoO2~`sET|9p5$?k ziD8>Rvv!d)Y93qA5Oa6IUXZ3AX--Ab$I1ij*d(|J~7sDw>*&z96(t z*Q8VQTInEUGhZ)E{orS>7q9SqGG^tdBiF&MO4PoJ^7g-oBNDl3O{%sLy zNBd^TmnX7Fe?MHlp-TmCMdP+&uq$qKMsy13qu#z?XO8Q|A~4?XJ^{OAk${LXkgCH? znB7Ki{HSkfD#b9}+i4y=jq3@xbgW^r#3=ICoqKD6$_%M55Bc@wZnM&E7biPJmgYOM zQhMl(glDrz+zFRHS(54mYBe<@Nz!R~9Cc{}Hn)47^IGZ5dqFypdP@zC_HbO5B;@%a z=eY726@V$;2nCHg`OWV&&1?O>tA&#BE4>-mnv$Q*IkP@kvcu(PX)(ZU^g@T>49w1+ z6>nlC&YpTsc61LdExCN1Wx;Nd%bAFR?3|z7JHP(_S=odKaatNZ4>>}2Ode?`INJxX zxhD4IAm`;voijw+PK29*?Dx58yh}YCq%IuaUB`b)#f!2I8rLcg7!F z2#%JnCb$VfM$^x&@t-Qg+oMcgBYR^xrdHN<`B&fHzGrf|W$5wO#SdvJY4yb5qHZTK zs_{9*=2J>$qZJebMn+W@Z`%UDsAEPBuD5?!>%G{>mb_zaI+k^Hi5)OeFTe87eY<~G` zLbr0|3K;!jZ%l$AL!~v-Z!K$J*mVWpKxdm{xMr^1+}HisUgOIuC(45wb@8A*Xof-; zojB-7lL_ID5sFz(PVRW0nD$p;MW+N7;2Ae1p-8D1pgEfQ8o@*o*5QkW3c9Y?^HQ+- zT4$G7Tz`f?=XNn@&TOqbJPG5|d+>X2*6u^b?a~O~-w46CzAS@lSssauXU>05Z?-M2 zbjU)^tuTi%7RAt?8djegYaS=tH1j4L?7GfU9R{JNUqlFdx!kU_`%v-hvlY$awo>Ws zQwP5}ZSB?B4dK~KbDuKAu}%JP;pooa+R@HpwQoIkFU*J2lD#7AQ`tlhmD5V;7}D5| zoUsfVPTbxvp)%v{Lw!hi;q2^L>wNQWHdXgX+9ih7tzBkguH*2MuKIb2W(lqh36O_{ zO!HA?q|TA5w?6<-`LjFdHiD{~ZVE))@UH&9xk>6Y0K z-a;`#esciTVaUh9Bh34!vq#o5Iuy3yhAaqjfK^b z-!wH{)CBjxaRhGX`WzL^&E~L6wHq5x=BKo>^~^*)t7HzAt*(DOBM;>l<6Ih&bo+5p zwtE^jCSLZO2{!FE6kbq~)pvt`kXMyRM7%7}k-3X4`5;WMDI6zt5?^t_v1!pRf-exw z*S2_-z{ITWSYtc45Vx&V z$wP1|*8l)lH|OJ1L_O<^Pj+0K7CUVl6U_JpJy%uAp71p(6J}*)=w8lr{CWE73~;)E zGmJ7Vt^49&I8|m7+iUf=*U}S{%?cQ@eDXJ8FKyPig=OGzc#NS>f1;y9UVTS2>bOyj z4eBt91Cb)SJ5H3kUgA#byG==;;ZQOlHoE}@UdFn#wt*YwwdhLTHXCKlTBG&9TFBdWb^Zjiw%hBguFVsE{^8{AwS6SFfg?Zi;(ejztHW~XcwV(| z(nV8r2>YUzmtgr$|GT(@P}E@(ETV);q2%^@3Tg4shczO8|Vtsnn@ zB&w^O8s|(3o{jFleR6hMA-7dBb*q3o(4b`Xqd=vPxZ|?BHonRZ*KEi9H*xqDY8m4PdPUzW=%D`NQ+l=F=$F67Nmtg6aL z+XYzAT6;3Ade&8#lID-$>ZU5vhU`FNcqTBRHw8Z|C2<&gVb3_QpBh1>egR)>j~R`i9S=#M&BUp#^Xsh19lY)unMUI}jeRP2X`qt+sKO&u%896muCc3BdPl z-z$FK0!RmInFJ2ngd88u+lFNrhXA(qfqwQ_d(cO<-8q4^ElO%P=l+R~V-9&F?#K_J z(_D?W6gesOQZy=1V9o_W%zRkW)m?v)+%fZN?GGJO7N~J{bSf3O;dt_BUJVIhe=Ue@ zvoYO}>_}Rx0d+ac#a&4IwLd_41_>{^%7pT6kF8pBNd6x5ZgA!(JO>1chkoA0%8PgdZ!olH@_v5b z-ki<7$(j3IuzyvsUYt;@m`xuBwKT%%#UcIC>D+330!to#_{OYaWO3x$*rP-6>qoZJ z(B2b9pH-RJ6_WV2_OQMQdm4n8Z$)n-!4{nWc$V>`-<3QW3!+=jqT$BsekXv*AhnZr8xfpP)hyg|ucCUY1Gp3*j1J`=pW2%-<!9-`Yqzw859|sQqrhRT z76EbFH!Zh;>uibn;RDasjz7gpS+XvC+cK+M)l`p*P|TLOlFIywxY=6mkQ>`#Y!3|94*ki(iAq4&>IRs~TWe7H9<>Ls-oyr_F z+Bk0=9P@0An@vwqFP=hm){Jm-cyxEsO$e#Cts?(%avm%4! zk{f1z3biaK@|7J7vF+iiP+Rluok3mCTx$Ie!+`x_cs&HDf9Q~wrXK^^m*dJHpTev8$#~0@ZO;l^T z$J5;ijI_)bF-nmNY4J!^0r2YEk(1W7Grf*gclu$fRWDUdB9YgRVbg0J#&J=2vNf7Iu#+` zUb#9DOlXP&KB*6Z8v*gN8WqM{kKm-Qnwn>TrA14U%Jo}9*y?kmMKl~W%y&b|k8J2o z912MhFOh}iy8(w`0g~`+_402azzm4*k$Z4Tf68=PCdk{sd{*{CvwL5-Qc^tYMacOS zapTl=?TR=JIKGZx-}=vFc9_}L5ev|tB@VrjyU=Lvi7**h*l_tXIy{QybND|?p|Qos z)Fh~~%3><;BKL8YZ*FEb0uWFruag{G2>)FLfT{V<7wTGKdnzahxJ~;*JrlAu>ICfb zCWK*ns;XoXfnA{dw%QDOBgLvyCojjE@LNMpW~zK5RHd-8;5pp-t?e)`7H=}%Rwvau zvrA4HM}_hJ!N&xGG=ja4LvM4mK9F2)+oS)a#JH>~K)zV=s+D{0m&m&s|2l$_SabPF z`@AwZHX!lofrf@ke_XK?8@N>Lo2K#j>|fx!;B3r04w;NgFX7LAQ^cPzvK(ZA3uVEX zZ~94^c3+(4+dnU_V{wVq#fEgZcNTwJnkh**7@g!CW=JH~-P6P@{ceY!gshkvR zT}fWCy(llHAD`Vtx++g{-3;fVxO4vmTBQyPTaziMRg_i-cGv{+sRKOQhT3$Uqm`S^aG zjNS^=Docfz>NmCz#5Oq9Y6x(IzRjN#xdM?NxHfhleM#SdhHnG~Fx}$>yFWnw)5!jN zc&C?M)G9aYen=~LlALI3_;#uCiq;RNB;tBzwwt+hCt<$2ScY&e5px{qR646(8JY-M zxcUTL+)EpK8q};re=Vb|WRz}CS=3iel2x70MX9nTMY{HuSam{_yGwQZw*hD5$Q6GU zAE!!e)jMO~Rkxh{SRC%HNSb|GPgY#K?x(Jnr3d8!{wP^wi-RwMU#>d~M zW%D~$!F1kBlQS2v{Hc0tf?!FJjm^(~+jTgXP*uv@W!^6K)2hPDaq=#y!nXd(OsQIr ziA|-D;*!k@bBifXDvs|)L3Pd7jpt%aA*fLn`t>F;5n7N$ZvI=!$1+y?bwpM9Gn^}! zQ`%p?U1>h2g!bF+nK3Sxh!(3b|x^Ok*>!XRj#Ll zS?Q%JC3mfZ@%Ksu-kAJhs7|s1@K9?%j=O$OU%9in0R8swyzV_%wdVq>5fcY>P5M-~F^I}K2UGlVO6$IW_T-*`kKq%wiarbdK zRiiJkznOovJ4g4a^vQIAM6EOwksjcJ?KT81iMvEnw7+$Io$`Zf_8T)Gu8h}X7XZ7f zBCAf7umj1!U%)s60LwqUjhj&(ijjfWsGdJmI8%GWs7~&?O*HFevuH5s6nyb$Unqhz zoI~*JL&#nQ&%0k)jMQm0g~Hz0*~N+!Zf-1+az9sjB_x^|5YesKvx*GZSKV~ikp{lKa_8^`Ws1^slj zo@@zcD~ErY`OOB|g5+V@j1{{iQqD}0EEHS0=k-@h>$=QTe~Y5TVBC*qHw`6WNkpr0 z`FUS~D0#3mcVBDJoo>F)j7B7eKJfKokuE17jI56SFRH#hp6UMmzk`b06$weGgi5Fs z$zf4QPN|$?jyc;9HkxfEDyMWOryN!eQO;q*u*jJRIc=Epc^kuqnf>1DbKjrO_xE`C ztH-0M*RI#=x}MMLc|ETTnYbxMnzO2nca@9!BCVbDPB;ZuQ&i)l?#+=VW@86YM=>%& z*MbjNkc)a?zc-znXRUWv# z@7dbDp4A!}vPc3jl|rs}#sP!jFhDF)Bl;*$s6J`ukj#MB>L^xCqJYpQgDx zK$PN+%;n`QHPL}}&ISB4muoYtaT$Zhm&MIydR>*Z!>{Q1#N`cFg#nhWTFL6he5^vs2JZfA?~5_~1#R9cnC6&WDyEHpr5m@{KoC~0hu8I_3)*#6IQR0rZ~^xae0vVq_Jpsr zCnzq`myAUfJq$&lM3Ew^9>wZSUD+-htb%Ig9TN3uqL*dugd(L)-L4$Gb%s=KEb2q- z#b%`SX>Er)lUQ2oZx}3PbZ>&vjZy`hCLdZI&t4%>*T>0G2}&OPosW7H%llxIS~$v= zwEkqkD+Q+@w<2AJ8BA?3D1B>4YwUxqr=?qPR{SDMOD9eSAmk6Z4hP|sEnkmxkcTsQ zD2s0|OH=~txn7bDd8?n%lUQ2@1`N!6xC?VxDqUg^KV7+Vh1_d@tg%YB=1Y3+S>-uCg40v>~ zTwf!~`v)fT&F8^0R}wuCOC|dFU*U?sv4GJL6;`Ei5*_7qOV>JwcQBs0eb`8A6PTv{ zWui_gW2DhSa@YWevjC|E)_|8t0BSH~;#KvmM)rp>A3kDKMp8_Oo8d1zZj?0MaG4_& zSq-J1x)V5nk!|AXm!JN6LSn~eeb6#(QK=xr&SXQQD^w%v?B>YFzN zwiIByDgHw#|Rmg&at`{9)|n$oaEw)0C@jreCk8bhD#l{bP@o72V2u zapjIvad%(o^Tt`Af};Rdh4=9z6zD)Y{e8|_!LH^2w`@<~oV~MZHvsqK!SEc~(*bK9aC_wDC}h=TQxA&}ZQQ?1IHSxuS;R}(FY0&E{q6yTLG zB;Q9t$Z%A`$C`7ILwC}EjTaf`A~VkQpZ_5c$n$mfSecEmru3ck)3RaZ7Dc8VvauF+ zaP>HBge#v*xN1u#A|-rg3N`dCUdLfe-m;qGJWnQJx{8VHBa{L3CYkX9gDgUFn&og9 z_LdxFO_|e^pk#Za1muTv*R`p27-|5a_`O^kRkcg4!cJS~B^ej7O6brg_65H{4(LsO zMk1&OA99pasA*toio0&qksm}0g#~~67K0t^XS~CwqC3sxs_lnWMjl6D8(nS6qac3c z5v#%!H^Qa|t2vZVPEg|L!|@KA_1M-48Ax!~Ez+V=+F7KZ&9{(t>`x0z_ahanW{@08 z(Wr`jz$dKlW`<;r!GQp}&pUB?vj|hu#Od-U!mxWw$$8lmfP7?UEv36D+JNcoa@yME zMzF1L(@Eg&i|*Qg=bd7`KgpRpISB1SQvF%|$++ri1yUwJE5rKo>;ki+#45fVHZnU_ z`bldOm{lH4-};>cJI#_!@$O0ASZtd% z`diwSO8X(qg5DB?6i;O>Fe7$!6cgaPOBTtAoC5Y{5!xNC!G==?ayUM$RwYvpGON3l zQf8k@?7Y5ORG^T$p=O3n#!;Ty6bCHBXNdYl%LUy4lnN&`LClqBTG_M7gp5Ku4dWUx z$*Pws?XO1U8SG~}>XnibEp&qJo|JRVyW%c;5^d2E)q4hQ=!UI>Td*S=<+nB&OA6N7 zH$jtRx^Uc86M74FC8~?~b;OgVUQVJ0`{Fl5NDRt+K{`#H&uFQ|f4qHFtd0}C>qIkT~|0rQweQT;JqfRG)603xH03d7;c!LXt1BcnpqvxsXbe58}} z85a-)YgR{Vt&tXi5j}EXv54VJQ@hK_Esv{!@pLw_=c%k^l_I#RjEDq2`_E5@LC-22 ztaj!X$2C3LNB`&CI>&N7Zg@7h$%JO8V0*Q)FXz#38dwz8=&eWIKfhx?%t zs}do78pRB9iiwRQ2=fy&431A!fAqNwx|}|TDR7-b62yMI+5Rwp*rZ3(>~Vj)h^Z6S z{V2}B3Nl2;K7KOv_}$BW;7BsDM>y_!YnOqR0_L{MVic5Ai0IQK>Z;&x+XVE#i+FV< ztt14~x*D#DajUml@2rw?Dpnb^PP@Ung0Tvi*F1v!mck;hwSgx?^|XC=Qbmt7u{PT} zV%gsD+V2{pL~#)8&}SbpTF5llvXBxcWHP7xvf!SttIYH2x?>DsqQ{)5Zi%DQv#T!( zuzy_(^2`IT|5#0DRx=nlLJe|S-rhrfsrZ&U;8#U5hCYe2v(I%tQZ^od3-s0Q zxO#7XuCrpl<|MT9~A1tUQQEOWi2QGki9k7&)D zE`rlTdsDe1TXw$pz_rS7Yi`2X2DX7v@h-q{A7>7ds}6WQvfUG-z)McExlx}Nzx+u6 zk^bSbw}1PpXLfF0P1%x5Ge|l6H6@nG(?PZTDyGcNh^NZVD5C}ON*+<7-b5AqG8g=y zMWQ#!cIgU*o>=RIUzjfHhmFh=DK~&tBLw#o25FrzCMLV3_9g86u%H+c>m%%u^{Z!p z3&$ykR~K4!Id^>MVhz-N^yHKZ*3xN2Dys zDAX^GR5nsrrcZh@-srad;kJrn*SW6QhxM8{TW)D$;11U-U&mp05rr#lv6!ABZkhjj zO?Upgz%#3e<+>xNVm9D*LXi;LD;MUv-v~?e%+oq|)Db}sXUz!%;V{lDuseHB6R@S( zviPJ`C3`kGH52743((foA=qf}tm2&;fNzDI)DL64rgv_jyC3yS!DLiS4Dkkvohh-1 zc#QC}3Pu2Ty&S-;eU!Ji4$qUn81ageBM<{mLAsaKiSUQDlPG0k#6mUqt3?_H1&(U5 zmU?lh{x=;{qR@&0;PbdOTr4n2{O4O#@pdI%ar5s~F}D93Y7V#efQBF&r+oByYXcgs zDMe^d4I8M}%E{ZCmcWC(lgl2Hlfy}27&VMngq_dj#*|t5hYRzG+_q%xiPL-E?Kik`@2KM= zvt#12U*wHiDyL+WhS03A1q)9*7I6S-Sv?`la`&>1l^gOW$n@SGh*fqy?K| z=A6#VjK7i^m2>$l^@ZF5`A-r3TY;Nmfp5Uc*>$K*;kW3;v)-8P0;}`+RJTuFoQn$s z+3h>sJ6o|JEQqs?qcGjJzp^(H$q=&UI*HN@VrjG3_MBe{#0#kIdhGC%^>7%w^ea-= zMq{NksznZRo;0XruMK!Lm{9e{C|#>(ZD+HY-@|KqM$SIvmxJajVl<3T_!XLYV15qH z7P3m|wfw&=y{`n@UH1b?d(S$_MkjL`+`1zhA+2-;Vww+X0M-lk8B(m8?}zld*kHrk zWgRW=b(3y3Lomas4m3sTpUJX6foXx}ZaSxmcDhH+R zkN_ad#{bGJdB)qoE=a4 zmi3J6%6@bBlQ{gnRKR}usEonoJ0rg1{Pq=6GLGRr9j`+4uJ7IxsgYy%8b2&5t_qw5 zmp#x@{pDKDilhmy7x*Ca08e2PHX{3Hib_7*vHpwCNsR&4`ABpVO=b^cTklCWFvZ!- zG^cQ_d0B<}T)vxZ;AH!=6ZM}EF8llLKR8gY?!;|RSsgCIk%Ji+um7oW|Y>#W&*=Pj4V0LFEzl>5G1kccQ(;XU!srd`$|23 z!8DW&e75do@h>+2J8+{oR|^Xjxa^_yQUoL!XX&N24U$aa1`9H^r5wBj$R)Q1-HOf~lZ@-p)f?s2>-Nawcah|~s0VKZo)UZ0``@U|BtfZgeV|Hnoh`QHLRMGl zKqGG80&l@!?#AqRgqZAB%}{1*oba@eYr}8(a?7umoV~b^)1$ON;}dJ1v*B-pUCL)L z32EIZ4vvhK;!qDzSRgix)g6l-L@`-pqa_81yez|V@IFdVD#1A9)Go}h$?r(XJ&v1M z5-OGO`-p+4XCWENFJ_aEcQiyy#)odk>uha&r$&EFyW)qKSZtVo2Lz~VJJ#E>V#=I^ zqy;_1Ph=MnMJ|%E6iOyg6A6|=?R|%B5B-51`@UQ8j_4a7NeF^T9C)agH2vu7!!L&( z?9+KQA90=fh1cl8tBM{Q@Cdiq_a1z_$D&p`7*=zt_K?R906u~!_5O}_s~-kzJ#PHi zC@|D$^oxcAU~^LS%8p$I?{B_t+z5=Mc+7uoP|Pj0oAQbH@?Y3+OgVH$?ApAvSm^P- zmanr07%`Iu+GV+KvZ{v_(ERzAiz%#d&tCBJy=s!QPj z^xLWeafG3jp*htV>%pw~af?`sm;9|*I{gMxTB_KiqUqI+eLU|98SjySj00I`=!$xh z;IQdL)ni?os<95N_c#b3h0s3jqUl2bMj+m4NmMF+hdTNKSX?I?cU>_#w^0+?U#XN* zVN*LMCB%A@onB#GQmwGSZiqNU--os+P~yJQ;9&;Ii-R4J!FmB4VNb%vQ;#uY1OsBH zowj}c8-SCMZrc5}?=msj*qC%a(rNNL4f$yKQvcr4k=UoeVKg>{OPKQFCDd;hW9L7o zFwCb5tp@5<$QK>A0W%^S;RHy&^is+pa{jyz*zDNsV{h^_Duf|k#pT}iQEVXP=B!0~ z4l$lQUbQnK`tRv>AbBJF`}8x0kJLMZ^W*Qr^YWkqu3-y#g@6Yjg~_on+VYSWf$3V+ zRzP}06s(3t*73k0i`st$&z!M@;=SQ%*nv(}2%Jbur+j*)g&kok9)YkZsCF#7v2a+$ z-ggBXG%o@v-6sk)6!9LlNG;D*(Da{R?U|H?5z@nIe67hS)X)?Dld_JmmD9V5}eEc?IWg=oTBU=cM##n0-9IVy7=0UPh3nN?O9gx?=lg$jR%MP;=Mcsu@E5Srp zLu_hNe(EsA$NWT8x!FHNQDjC^^X**M6qpWt4?GkuPb_7%R_SfzU{G5Z#w9=5^MuMQ z%SQ&jAP>%@_>9K3T`HLY#+&t7EHY>G=hDmfWQTvi>oMKS2Zy@S9{ozd>I#)tq^X%1 zlIm;Ugfw=%p~$b{L`(|JMjmI(F2u5gff&+-jMHTxEA*d45q_n9#mYVxk}$4DkK~+~ zg0&%UP${(sKwsI-60x4D_;CqRVBwQYEL*qmx8k>rcaYv}8-V)p9uhGgR#f*2wt>yi z4QxfQjr^jfJE8~h!-eiqRh~feU>|@?upy1fl>BV8S9?O5TFJEtT7#|a$peJe3?P!f z=>nUcQp$fO_;iaXkuoTL6it$hQkgO4WbqTZt@NwVD1ZHb9wnd@lvF-gRvPn%?kt_^{^K zYsN=)(6&-sJHaAxro0*@~mqlhKqpnA>RU4vCFh5;yV{Z;yd9eQfR5H zA0R5XylB&(kiH%paU<^lE~q5@xFIp4F-n||MHg1~v-j)&27vR}MmGiMKvf$Q66*y3 z9bhU25+x%_CABbH_aIAnx~ow$Qv|1ie+#vYb2FC(bAjatW%4?Jh!IaXCHN}eP81gQ zOvYC!GLU~aYXjkr`CnKxHW@ZayM~Qteh`$?|H<2y+35)1%+QW;bt4yA1*ZvqbI#rj z6Beowj!G!s7v&l@(4J7@p0XMKQ*YJK!kegAwvhoL@q@7tbcsAx0yk6%W=0~))ko)k zF@C9osx&tWV?A*dbrQsB#ixDP`D9VAlx^pB?7VdfPR1~?tg$_zOxI9uC*SIK!iG&n zmO_9X!hRnCxME&)Sw4dRimm0_6D8{iEpcDP&VTz!ih+{|Bc2!%4x;!Z8S6XIRlhH8 z%^=E(F()MTz%3^`DUF-&{{(%bM$_FVp(-}|`eU`z2{xUnU4gs#h-c<*@%2Ydg#Mr@ zs9PHG5C2JO+|W>YdZ$jmZ(p-nQos0ng>jbkoT04eN*k+GEhiD+Ug71wbhjut)ZM5< zgxG|%2HhG_p+;GhVYfS`f!or`2Ew(K-Ux{UXmMkb1tc$A6p~o`YnmvG1rCK4sp^R% zPL9E*B;G({D+VS>5=W{L_%_&pyG>z*70GEcoWw1)q7r?yR(yH^2B^p4BKV3~)YjL{ z#wZm`SWR(}w|(*V`K7qE)XI-w#Ow%BSP2=A{UKBMTg4^>pjI1Yg_LLFUK{U-jr{%L zvTozT`G5FFU9%2n-rxBSat<~XzK)`07{4Yl@G^BX)alp)zh(7Y%$BZMDtd}}O~H!E zYWFGr_ntUSx*iiT{r$dpC@;(VTUffI+DPyjbg)iMI$Qzkn2m0|YLGITwo!eJS|icqd>qM>bv{-n3ci%Qa-_?(4taY-&)12 z9f39>6y^cmQuWhOtVn;G4xh@oddg87?_Ndtygbf}7W4!5ecfk(8c32R-8vj>q)w;m zZoGX@_k4z!KYir2KEU%m0v*2P{slOU4k#kb47*-#V>U$;@0DdB2m~6 z8~BRmFC60u$X{RdyN`E$OEzAMD*w0N0Hl%G+aIYRuxfzjh5}pmI73`3icy2iBz<0! zONp~{X`(%Rsjh)rqQ`m?)@C;W3v2}8$l4UStY#`|G1)D>a$#pUw_7S$Q>O@Sci=OH z4bW`fTpv(8fHwOn#F8;U*As&8j)DG`TzRdZUv}+QeH4LDd@aLCj@UG?@n4(h)PxtFkGmX&+WzU^G2IRP{cTu|`p6d~unJJ?6`1)Raj=^|v!)xm z2Hw8MSn(~{66HJ*y}p?u&qbx&pCr>cP2GZc=)AY+q`i?vNgHvBuMU1KU z(F*t{Rz?K&a%%Eyf7c!RUm}Vha>IrCMvS+DS6*Hp*rdkc0gQed(xX_rDMOl83T%W; zL*P(6cxH?8%!~YWgl2IB605fKg_5p{youj|vC1GsVd4xLDACp?BuK_^JVf2SNF8hF zU2bgODv9y8uDEluu}{!t!YWn#AHV^G&g$_!H}}AlGx#2JR$|d*+&u#nFzu=3pH7n< z&@zN;GR{0J-Ba83(N4ePnl`3P=xut?Ld7 zph6J{OI5jd|20bHB*M4WHG>V$B7#eb`}WAfbvDHcB{A0E2heQ^&Nh~D}6bvp+= zv~R&JFFBs7Tu&+S(Qhw0mj{7UV7(*IAu5t|0INU3ilD6MYt?A3*~NO`Mwnx;@PZ;@!o}fl1y))Sw-P5z zcWma-M-bC{9nrD+B^IuFxBeSfi2_@K#kxTifO3Ml3UFN{G8XtIu-x^QK3p7CbGP%@+|~i>;2f7eq$D}N@!*}(j+Vhwe{aAWA<8O=+E6@tUE`AH=-)YyD&MX&I+gyS;$#!mq`h6cW=#?7^GbXJAr-786d`>G6ALxE5%a8>3< z;y@3nHI|I#H6B<}Ws_FeteVlHoXN1rzoqvvaiyo>oZrgPGx?qtKegG)Jfj_*H@sZ$ zZ%;HTvnPbxEz26CL`=6@g~}bwY$_aDvnCfpnEN2Dp~ggAY_&Uyai3H!q2v#soN4&{ zK$QK|J0Xq;eDf)7u~Twtp)9J;x3l}@KiB-+jBr@28P9X9o}9PL<85xwJv<8tHO>G< zuUJew<4xa7#Y<-&Yf>vFiQK{LKVp#)plRTZr`00xqw9qOeb=jVx zc$firEkhK7r(kXHZRUmF?rwA(Ru$ai?}ppM94(i(9s0>SP&0>rgF1Mm+wi1fz$gB| z=Y)Vqzg3jW(U(IFg?!sAlyOl`Fqk$?5%&uMY zZgMbxspsnG++jJ;(dLcC$ZoA`TVm?}J$&7g!e8sZ;WEAhpgy_m9QzNoUs~tpSw#AX zrY1wq6f8h+ubV2gmuthWTl9%vxsFH=Uh%pAKUgsoDFaNPcdCcDSD^+mwlx(;Mmar$N*bPe#8j>U5K zyi$NAa^9l9n-A;$GCqYWLX6}gS5wGKc{Rx#U`M8hX;8foQGyoK%mAh&3P%8+F{;gg z&=wiscdUiws3WsTy$Zy_mmDmhu)%3AT6aBi+4*9M{5nEUzgE^N@)x;IXZd2wAS923SLsxV)8Th4^n> zUkL?AqFmZ-Me|SE{A$iwn-?_+kfW}r4Oxxfm_)&hs&nohB>;rvzlF(aQo!ec{D6v7 zuteP@ZMnL9MEwkR!6UK}--OH(L-*Vs2%Vv7lPgbLB=&DPT1uc$c?0BQsX33c2Jg<;A5l zaX(^{#TJxlvI~G$qz$=n6uUMdYbR@x>RZo8@ry;+ik>Abm=}4P0*kLn;ebqs(cz|5 zika_B*SOAI$aa$j^v8k_L+y6`eS4)gzAFZ3O}v(VI(YmCOi63|wZo96FhaDlMR%+8 ziN0VKfrg|llIS)biYILtrT##tVvT-|lfITE7x<}~??5_5;AU|rNPd<8d^Z8CV9>U# zd*Ww9+lix^+gu8k-LYkK>iKqj4xsOtCba{9REvLx*mO)mQ**h4X%v49%N8#EmpnZ! zLJ7nz2AO4b>Q@~nxE@5Yi2-NP|E2{1X@Zq|K^QTXfs~l+Cvp2r-NRHQcb^%8jO>Ac zUC=!Uiu@q!0%Mw=co57ImX{mq@^-RJ)&Dq0k?Pggr`Y)E&=%QhKhmyP}}0U=DlYgGBJ&J^q_+Gt3GnH60fQZ?)--`7D&r z;u9)gZL*L%-c8RTWF^YJn!P;>qM?>k-lp$Bp#CX^`3u;u+gMx;M0EM+#TP8I;UIWLuk|u@T`IPrrXet5v|F}d8c~ZDbA6|GaY@26p!tLE2W#Ay)x{OYy z)zkw0IfenB#bQkqn2{Ra3WCR^n&!*cJJC$*0}y@&3SwX({FE+XRu zjjmS=Pb&^g-o9t*he5=htuE!BFqrI##PbRClT@VErhgoN^}j20osbM{jLz&@Yi&m5 zwoW!nBD`6@UTOW7>t0lCu8w_R)d-!w(Z_Mc#tvZCEmJ}N7Y^Jo6WkVD(>rHqWt409 z-QBe`xw8Y}0&Rzcc8yl{IFoMvt;+MchjMY9%_I|QCWMRT=DR$hd+cXguoSwoj%(MA zTxt5@5{!lv<+5^ns)J-l?oSm`K7R$bNPr>bC*eTPQ85Xq`Ow4bO_w$`53+N}qNj0v zE1ADAV`*A-SMxV^rsP0F;VnHW0TFw(%OZG}dc@gX>;SC_xsaVM&UTk1Xip%jIbC6T z9uP_-E-)M$H*mG<1<)>@z0_){yk5TIN*P->69Fi5w@z@em(5eZVPJ113DDjSub&&2 zv84iLD^d)965o_I=I!>0feJmTfR7tK?dZ@z#%5+}i<`@B)b`i`(A?J2pzimfjl`NI z7t77$OaBGOK-7P-uL{{5YTlWD?U>$|n#!vL^zly~UaO&!#^h@-7cWq;!MR^>Naf!P`j;IoEWsz8>3S zbxzk94;Ek2k3m%w#`J~Dv7J8*H`6L2=qFbN*xgX-Ue)^KpCKXeZgJYA>#duQ^gmhm zBvdE2pB)`Nq&>HBl2?I-V&ZW*8q?=Bja@ZJSld@dVq=}=k49|bmrxyXn~@>EF3a4* zKKXDjk-OP&YiPtCD8E@^j1qpv<|$3qSFfBPnzIwziZ{PwtG=CR#9aPv&Auc`KnbBAs* zH1wZc-V#FZMXWXLk7s$r`|i=)`4N9NGwrH*nRSqDnU;N;Hfyx1#lpO8*XDQ-z5iOIHFi~h?QgOQ-9!W7CO}S# zWt@XLo4oeE2GTuF`OCkV@4EEp?da_pXAEmN<)nil99r-g4OBP7U5;|kWRoN(cQGe0 zK`ncOoy#kWxjQz?x!D&S@q!)%R8DYJu2$tpl*4G{8rSd{KSlx)%bBya&uwUWYNDnn zZsAW|DUYf3EqKg4ymZE4nxH&dqwj`IDR#i^Mp?~6&37R^M181Py2N2a|Nrv2X8DF| z+L?3>JAFMwl14$bddlFY98;1rv=yvFR+}?*%m0B~_MTvI;m4?`>S(m-O)^`e?}6(WTfG4OMDf`nhPRp8tyY>WwM8px5kkTbB)o zzZ(tfYfdGrO^hnDhfN7DHY2ySRvLux#j(G-E`TlTZ_7K9xB79N)-tHJ>WW$}wBlZ1 zOTPA1(sQ`p+aN%C$y}`tw9WU1yW-anu<0gs*#`6erYD!l_q6yR*?-FOuVi85e7rur z`Jmg=C2dK8gR{8PQo|3z3N6f)0gW)Bw#}FxX`{Z+DgrWntK#R6r?G4GAMzw zc68P@*=)P@!rF#lQVdOU!b>N-bO@3OH7A&pd0N7zt)FWp>|RbmuPJdWd1hGO@X#5p z&4xowSAQL;Oq8>%s2ASi;aRBwzQxhWdQ+RB(%G8pw~);e@Fmhu8#NzS^_n~|umfvb z^2bkv@3qQ$PZ)|$s*z`oN^up%wO`XNak$!5v|AVrJC>6=O^pr;4T4opGm~D_}74ln6O zyT(WF-q!g`$57{HXbl&Rf-63C-wyhrg%_N;9VA#PfUeW>haUYbU)LfH5I2(j95$I# zaJ0dyWZv6JUZPQMB}!_zRR4S2r7=`U`l0z#-jPxzT^#?(bakC{?%Jgq$4wxam++ew zY-DMzeL7P9z(g&Zzi-1d0_QgHHsnrFYnKP|=`r*v>hX~}Wt_nQ&}Zwkoe-Vv>=mxP z4KdGd{cCMo%JR@U(C78A8Xh-KNQ#ZhZKj z+(HFCN)7H{s(H%jX@}lX(edYLVe*|zr9vQ~1Aa#>uNS2`kb`GycRQ%gw;`9*ErBl- zAY7<6tSA<+`)*Qe_w%$3V;Nhhx4Kdc;85#RB^h2}pe48&!lqCxcvG!}MY#Sp!XPVqPtv#e2WK zzA)oVKm@2JCUKF9_4%zVJ_X? z2g~RW3(~(Sd-yn8oSk}TM@Y7{h+DxT%U7HwSCjrU;`0EP#&07F7Py5?_v~^?ZljtF zviXS7t9Cmcvj`jdp-zn*pw<`+-`zWk>gh3ErKjDBR`{Wx zP!*;3d}qzl({52!XSP>!!m9dPIKAQSA{VexxJX1a ztJkfwqb-N&$= zGoxzbSJ25rA4U`pSok*EFRM?>`dNUU;d2{eNS7re#F1SSV} z01*$Z50Zfj4gYb(r$_M+wy%r2D`NI|VTJAbS4#?EZ(vQU#r7u}!l6Fe=iWrVZLv?{ z0d;GEDWTu1T?b*Iu=R|qLM?JNo1f28{|_NR?Ix@4z+eRQNJbZ8XARM;70|?g4(UC4 zYKzwNYJ?QgwF+z+t@X663p}Zm?8RQ$H=waqv13uYGW?>dXq=r#*Mk zWp(RIjZg$S4G9YMX{HqByDR0gVScmfx;Xq#DP>RW3&A|6nMl~6=m*-_%8fy9` zQJFktJXOW@AHI_xOUdW*z3T&qkx?(W@{OnLa#}45%cXj2{yMrN6ox1Q_z>on*djMG zTT*TUpj)mCF|ue!0n#V=tAWtOxaKhMR#HgShN{d7%AG?(7gB>0s&1h+C_(n4aJp>Z zE(Gl0<=|UY09mFQ2=6#otYm8B>I=n7+_u}O7i9>~y~fBs1*U84=hOpR4g^c1`6`UJ zuLDK%Q;Fu5KzkZ7p*BntCrKr$K%GiA3*l zR+s_3HKB9#xpG{ki`L8$&8|$T*E{aa(3$FkMCVrm3}C^dS65Jz^3&w^xYd0wcf^bz zz1Jj249vCfjRafLqASVa+JSqFX>r9Pw0>IHvh^nT!OnAzzxl89D|bIaLQUznhz0JQ z$s=pL9(>l>$YU1!3A%v?@NYq3<(mb!tQM}UV~T|&>#jShe#CFe&ENkjnFVmT`#YgVBQJ&GQBI#qm|kH9*Yi<-)b?pEl_0Yn&Y|9F96^!i6YF3s`cj(IomsVM)E(}!LoU>iy&SA>rU@obMy3vU>|E&73hCtr_Q_IgAM z1vIF-X509z&WryvLmO>fAN@*)4cA-qP-tEUBm&0Ncqp9eb8HNoUmihIzCiWUy0*6$ zG*FneR%6`HW&cK-kM>x;TJd(qY%Pe1HfThpI#40-kJ$7Y#@HhFBn>YTj5z&h^QUSA$X>{7YTy<4w~J4M>C~IAby4w&g864dEaj#63^xft%^0}* z#hOr-)u+4Xrt=+Ec0G?4POylI%xp^OOSe!VH6`7N~&P({Mx0Lge_xWZ=O?BTZS<3(h`m5Fpoe;0C}Pnh*WObK-5q-HR0@DGsA z#Kz*N4~VvBxMWdP2E#7YCmb!5N7BL_9%|5Q&-r!I-t`2Fq{HWu)fria*OPs9WF)tb ziTLjXchWF-lMRj!zgA+&?hZ=8hUr%3WSJ2g(^9@`AwFVS_wWo()Z2U9tl!7kXgrJ- z(&shROfQ}wc+npm=WAx}8J5%9&grs6HeDP?NUo-^ZG~bricCWgOJPp;pTUCx^)$g= z|99^f4#1y$UZG$$b5ydbdJmte<^Qt!W2=z%pQ>KPL@$Eb5<2>_bnJW=up;q}l)P!MDemGiN|=|(m3kIbgn*s zN8Ir(q=4@|Ujk&-XiUO;z}sfiW_UtG6P?PWdWYzk=AAVD&dW1HPkj5~Uc7JL0&G*B z!kwkVHOD}{Y;JNVe|&1Zr%Nec!*%cZ?7#3yF+QM0d|>nTL`sNU^{V2`US~CJ)DZ*z z>0s=)eL6-Yc>7+LgkPOBwdpkVw2xEGA;HbgH{zsvF3s$H0{UtOVobuWW?rJu0(SI` zS>swX*)8d%n>kmQC8DaAqtsjx=|5vvtTmoUF-yN>9hzKSc(@%1dMSQwL1sx~_AdJ2 zS_WNX0Ut{;Gqs(1vFHx!@{2<>n7rQ1wfuv>{v@SY&{kUM7mw&JD~G@RP7Dk+dAqK9 zJzU)HeCIU(t!TLuFCwkC20IjfVdXKcAGgS2=m}_5Y;CO?V(sIf-u49f?4gtOYI%$K z&-_%1mSI>0{=b8(;ZX_=3{h zjGm=0fBDKxKwLw zTwiP0PS?}^(kkC!#3jeNvwP*4Ly3AejbxyIvlQ%w4-8(kMW`PgJfuC@m(Z{s_;Q`A z>c{!N)03a)V-1x*covc@Vx&DBXYViUXB?tG6L(6%<9P48jUwZ^K%lQfCG3_DF}|@P z?%!EX&bSh1{sV<+%ivR9m5F`Vf9=bq6tQb5)_`sYEN)lC zuZ5hhPJ|~xkImL5;UD)^6=WaL+8C?NyX7iOm{#zar`hTAqP*H-nM=4fyV2nPF;&2o z;Bht)lfL@-adc98<2?}-F~pU^t8Y#}{e02xTgw8VrC*$UBl7I_FQ~U?;k|#Fl%1ki zpLOW`-spC3v;H9%s((DODc((}Z&J`vzBIA?l?5 z;Lgb|{t11V*al-e?kc7^A)chjl=Rpz6dTvgH=D z=O(DspddufJ5h^fsAY!TmW#%) zb=7o-8=?AJJh5S2JgxOd5BQZwk}4?^<3s(cooaGc@V?_`^ReaX+H1k554BFUeolcB z<7d6e6gyi!t4t~qUrf+P-|LSiFTyKg#+js|Z} z=qGs{B0IsZd}(Ce*E5)DuuS|4vHX>XHme`DuMc5XJa#Z>%SFaNr zwTew6P*XQw0Zf->`cebP<82rHN@#DE!VKNw58+V88q!GR=pl4qrSV$ zpU6k#tK=i`WyW5gc+IG2)-2=>wU1!XqK;+)vE=O;RphnGf?>Yfjjw)jT$B*j2(HQm znA|d5XF9aQ{YuJff@Copr$KQhTY^bf;tB_m+?t>(2;G}9`g_EEifCuQ<;?<}L1%oA z-FVOeu9i-Vf4^Jk7M;pV0yGyzOn8yEEugqxa%obYuKkk`|Gh1rFEf(-bS6!Fjb^zC ziosQH)BxIzMdw(q>II)%^7#)<0nl&GmRz|2OJ%jmP8sS|eZe_fObLwn-D35h3@vu( zq`;QlW}79Ilt6Myt<^hr?cmgzhEnsll%IUjMNi`M))dB_MRs4BKphK^X3pwoM*6CS z{%D9NY%{%GU0f|?CD-7<@F5I<<2X%5Nh~Dx>F-P;mr7M!VsKsV;Ewp@q}c=YWqY5M z_BT4dl7P8$y+j5$01HV14F4(={}MiMs4qv8;-M)y;hQZF_Aq}sm1DruW7JhzhL9x3 zi+%n>98|J^g``L#nmvFncLOvM6s~jy0rd4oyHKsJ?el_B87{_Hl_o zDF71+AvNp$?4d!wCixh9r^0CU|6ajEF(0{m3Xp;M%GDs?U$*|TXE5ntr#ddJlG4og zoC)|ljXF5_*qR+;b~s5+_?+eMZ+IVdWUj7;RFy`hynF z?Q?mpY3tOlr|Xi-3_wMkD-@&#fCZ))PeAaGa&tWn;s z#76;RPE+H@jD#ttnJ_ytXd%IIvRQrqkE!#Hr}7W~e?vH_kje^)5M__Dj!{-d5-K|( z+3OthD3t69*~d;LE8DTN_slvRdvkIe4$knqZ=c`y`}qEuKYBRFeZSw=^}1fK=cNV9 zPm)69Ms)4ZZ8nqfyZ%badFR@?TK&TUaS9Bd{B#dFhsB+YlXNE@9okY7+vUG;g<0+w zr|}~XTKFqW0Qr*4@;>g?qFKLT&chnhCDm;^16))%N?!-nUFv|7v3dQ}N^*tI*NvUL z({-msw?`fNeb)RQ_+k+E-`KuyZx{!BjjOjD zD%%H4nGEk@N=MQ>>iNdguU)S`&u_)#-LNcGv^FKk=syE|cxe0je3cy+ULm4r`kdnu zS4)(_#pfyp#yg8HH@qKFs`bp96o9R1^X>9^P(fF{UYjp=VU;T_7L(SB31JzYktn$8 zEgc{FZH~~{`}VN@LUD(s8q5l~ZPZ+tb~;`rRyHmyj*jtHba zz=A8VOeW<8=6^g0yXTteR4xQ@nB+}Ot^k~)S916P)v0M<4z>@JAystY5KNjiB`!}s zH(jca0v>b1v$N)B!;5)KS)~%?^XB5Huc3wqV*JL&!%X@|%KYXYq{~&spwR6X<-nx# zlrjy+2#|8^uFtKxTTvi=3=nE2E8T#e-vTr2%uWF9Rnyb!8m6s(4)*~72LI8aYDHim z@M;bAYg8s50Q)k1i6Bwq5|s3>o}>2a#@riY)C*Me)xv>42q26E0f59IJ(tPJi6r<$IE7XX4NH`{nI_68^( z&5TCXJ82Fj`-*iD-!6INLfe7?k^bdnZNBCKYZ>gY)J`AqVzX5`j!|7soffG?wyk$Y}W7<3?@(xnxczikHeTA>Y36mPjKv}LZ6Z>+X^9&=y?YIqjg!I zta=`Q8u(y^|d@3Mv4O_C2_gq_*_@RoGaE>1Gd zP9>*{_B{UwyZF}IP45{Wy4!sJx7lM|hQNNmnbJBSC%$&(0LaD_gfenxNP4IbR1jwT z2w|6sd`I&qxkIlH@zZ>iYe9v2A8 zkaDW%4ySu;Z2bbk>H8D_gPJbXeq&`P+mMsw3m6Jd&`tHgU~yjACse){J79?XC-P?U zPJ^oLZM%P|@Q%U*(YyqBGKPilqrZ_emmq8z#oSUUb%wF|yvjJ} ze%tycQ)iEAz3V*qUY4rQ$P5eeir&%tTcQAaB*qz5no!HVxF53`n z!5cW@UzD?FHYeQe5#n*kL$qxAw4toyU6FKpz)*oa9xCovB;w_1;6u8!{n`k+VOSS> zQZN+?`gMt@SZN>i482cSB>VCZvUK%ODDZoWijSI)UH9mg4KP2AXwG^fhOt8@Q=Kltc?Ism6FW{ITncEySW zdm9zSWrF6z@#NnL{|>F&&$dI94l7MmHha#sJXF*y>QoF_=Ia)a>k@17>~h@9*V9SX zPiJiG;$nKU_(xz>4Hv?-;KJlX@49!P*+tHZ1T3Nhjg^anGEcj5N&A-=pl!u!L_P)v zq=~-s7^K1CV^OC#ANIg$>UqlpHm_(NXswV}3!TM5^Nb7%Xul_>Z;@!p?SyUf1IB0F z`LVjPKZ+co{~FKwnRN>LtyAxP^%dS(cA4+hB}JyJmV{QR;^=pTmXcmFSK}f$Y&Vmh z&687Tm)XDkev@^E8)dk)Zt0~-O3Y8Za;uNe-X~$*dW;bxxOaj$Ie?{%{J8WKJG~PP z-^8Sq#s4uaqW{k_bBp4$|aR@Wl z-t3r-?5h~>ZDo)XLp`bX_=C-C4jUFc=XS8FN|qb0Qy-w^Z*lmKb7J9SJ)F= zr$oHp+M+rxg}uwq@~fR~bJQ{JEow2imVLM*(r@k~od;rQ1tT!7>+jA*_G%60#Aob- zMu_PZMZ#LMRqPpI_s9q3 z`IyA)8^42)t`;0lmGmk@0JO}1^5gOeiu#RVSB(FcyhfB!>r zoaJ_02e?en{eaUGv#?HrF*8A(>pmAhWi$C@2*yKI(O$`St1 zLu&&73+rY(dj}I!UPy}OWd<($p3kQ`KJ)R}Kvd=HhQXdTV2r{3bij_(;k$tRW2Uj{ z@LJJol?lm=EXAirLH=S&Zoq%xpQ{Uu1TXxM2|#h&ekq;=i<5miH7U%s-Z-AJv1fHs zRkyaOOxhixkEWWUy{_plzP{fBKB!a+SK7*~2+HKiJ%w*&(lBr|C|ha${6tr;Rav;J zA`^a!Ux>9JRdT>3UXuIb>GRxIGeiBh(H?)eV6?L8bJ89viv6S|W@UpO(rTx};1Fn9c-)YW))dm>WnO)@;fXD+4J2^u-#;KkQbyNKuYeyA{>1nFDm86f zI>6W~8v|-lGgqgneNe@mo(BE(&g$bcx0=LJBtG=_B(hLU_@@dQS=3OI~7_7u$Rz0_A`1za`T``Yd$I#BMqX{8xpghr_9 zb~xnkUlbw$PApSUYZ-%6iL%athHkx>-`cJez>}UCs>fJmVuKY30W%B=TexIsr$?8c zVM8a4kH)RS|678U{!b#iPlk5(=6AMp_dX^$guJEV*=&hSv2Wrlr43Q@dy3D)PISl5 z+mSf7+RQCOpQFVhe30!2H+^sXf{Dj-YQCZ|bq%xrc*=f7);Rtyxfh=+Zvt^N zY_Q9Z$-Uc5sO18{_7ClEMdIs!-K$VZejc&V2>ka@|*EZ zjNeRn9DhT`hu16w3!?s^7O4Ztia0<1vhwmIkIk)bN*{GP9B-i(IHQ3b^YyuvnCQ_q zBU-Sv!%`Swm}Tk8H5JQd$3x9d7okDOmjY77<*8rJOP?Q4p7%JjT1&sbt9v8hB+Yb5 zZgb#|OpIT=W?ELG=w%VUq8n<`aeJl0 zKX0+baC`IKwP4Yi=yT0^Eu(Mzu}a>XxigQ<)IX zxHQk=p9KtUN@SntRb6W}ecO0b@J!_QStNtCYnMU|(TKxDi3EIBX|?q2ooAU)y`9dDt`SQ>|zb>zfo- zpWmgQyZn`jErnNkJazhfOOxDh!D-@-xfu@&vxslAI~P>hf>g#~s^F2}uh9@3D7F%M z;Hp~HSMRTa(_vePRw=|c2?ID_y8dgpzM4v}20Vm+na_{mXGFy4-Y1Ls8)iEx(D~2u z)X-Hr`t94U?RvB>k=EODiaSxz)(@R{R45!t==%kbQ1q>SVODcD^Chh9ydn|tf>O*^ zpL}cotvK^vSTofkE+ar8)6MQ}yHv2&Q|jq69vo@oX$7aB0~nR4N36U?7F9nbM!119 zv$Y|{=_h2vF)X0h!Mv1C1|n@%0Q*)5^m~2JZE_`UAuGCOY!b|lWK`IP;zggx4b_#7 zJ2ATKRk(zCqE3j9TE3n_ft`6Ozr~L$)?FnTmua!lyj-H2Z z9o(UwC^SP$aASfAqIyi1zEc9>jz!*w@}RHMW2qSNxdrq#gMT-`vtkP9!*>vTM_{eL zKhck}%R2m0Cv zd+VmIwnBZ8ONJy3*`(o!Q(QZ-a#aa2KKu1mG4+1-eJyADjk4q#roR#q?QvnZATn$7 z34Vvci_CuWpR1VJg>N(cqZm+&Qa*&gR8@a&oL8ee=C^>&5m;vbG1dOt%%-?>I&)13SNOrIBPzcaW1Ab7tJOS55ubrI@4)G ztkpW!-=KE>T$1}00652-z?nN#Z#w7PRCeB$&JB5l-E_CiR31NOJoqk}XBIY%Aa0?| z?3zjwcO)>fN--%hRFo4-m{}CcX$9Ot7^$^eEeXckz?3W% zC=PEaG%PU7RR89J#-^jEoM#q4gF)Mqlo?c&Vvv6H^kn$3<*w%@B#eGI%X!NQ!BvrJ zS2^z5Ni;-0Q4A7K$36Ku^5L84->`K>8w77dgsBIVVy+dR-2Enb0{@>R-bdf}b^Ho5 z3mcsEmln;fv}Ctp{wSq=@$cts0VYuDt4}^p*FRj41z(6`&ij6B_SyCx4@Kq72z|G1 z&BXaNn>Igk+jsi-K>e6O%oy(gJH6ZjHe}t6?dR>AxuUE&MVByTk1f>avKItX6HI_yja#Z3`k9#ZrRF%lGg+SjL#f{6v_b9 zKG?r1(5u0bX`#*={&sySmZ$7<>&aU#l}vi_C86CEPz>Bp>i7TVLUxupx4mY-=uhK& z=;TJvM4zhXLJXK2A_(UgUOy517wA(vb@gUixdeS0drJMka5@(pF&~3b+kkvTER#6v zDM)A_V--JC0Q)|zpmN4HKIP?~TQ=f3raBwUtsgYjy>sdxO&F_xGf&mI+G_fcRrfx! z0B!XD5C20QO~Z^MyQQA&!9vF6rD}jl7drxZ%D4PoWK~@Q zyxK_{42!uO-wO+fj5T;V>jQbc$$0L(ZVk4Ky(pQN)v z6Rbt%-HRF|_p8^M#NT<$s$c?#(GnQI=;+{f`THaed-Y8J8vw}_3!y4;46I_F6i`WPfK0<1%d8~yHx zfu0jM2tWZlMF}Vik?#4`E-rhmYEPTxx;OMll+L}CfXx}|y-~v}o%S39>48E;7Qb~LH&$lAl z258DkY|EO&557XQCvDfKXmKMA9)>@T1L#_Y2}-qVDe_8X+0trN`yQnJHeA@S%oEWMG@=zxeoPhX-Q4C&AO$PAN;YnpoN%YDK8sfZ3$&LK=UsDy5({|o8e1m}q; zpzWUC2WDk3Kwqn;+XUO9lY}%-EoZmO?I_KBmfQsWb(Y%WX>bk3Af}fmB1Sv)N0Ej# z0C3#z+Xpfq4$j{<{6i{5*ZiQr+$RD?=7pICJf`#7a$~MNd67` zL2s~ft=0d50;N3Nxu{E3CB;JYbb=sq}>&`z6a|L5ARJp2}o#9 z8!MV5J<4D$1!tiFDDNNx-y!~jp)?`#;@L{-`89oqpcGIL32^Wv0CG8CayG+y=?z`R z>6Vx10qhgu$Gzw6Y>gaDK06tfVecL^^;ECm{#38w=PL{5m%O?djt@7Ua}IU4oSXZt z7n3pmbr}iSDR>#f@H~n4PF|y8L)?ZwgW|NW#q%bMs087Tv-vX~ zmlRMv^#`pY6(E$}tm-&ftB$TSqi?I6EDLHq{{DDpcaXRoIdkFKPnU)I z@#2Zc=eD{^Tr9ghKO&oykGdLgy3l5prxO_0`$a+f7O8WS3%W6Wt ztlm`PXwpg_KWIO|%mCgsPdOe40UhEqq{;84j2fMzs#zogwH_C=mv+PcOTHUD``QJ1+Dg5=8g8FIl;|nPJfBZgGW6V!>a*iz*gd`5#B&D)%yB1Z9{jqK z*?xCX$FS>R$DVpQX{DfQE5TR=u59sHpUUL1YcnUnj>_-^=048KFUBbb>{1-Z zyL5;0W9PQmZ{j9V3H<S(YYF9*ds1?W%{ zPA9Pap%db|_eySz&}2Ji z)c@G&)?Kg!IeG(I`O8#BobCxd`R-2^X|y~GMTsc;m1Jh7I{ymVC)F1;{-tmQ7A?4N zQ57F`_~@p%2TV^f!Qz>pD#FmwPg{YnnP2wY zIKv~W5G z(^V$)-Cts3s(S8s3Ekwb$-k(70j|sC@f{})EkPiRbyg_Jb%_(VNa|vC15w0!T?EOl zAWayotaD=N_!7x&Bp$aUle_H|vs+5~C4wIo|7RomS669L#~RtYC;1h4tWUn$2lX6X z)kF~O%GR~kCir@+k6@w*W*E!6%Q0$&fV-*^q^AJv=Sm0A%7cX-n5|USDF!Z_)Ei(0 zJZ$BbJk9=j$L*IeVp0jLMp4DoN8GGuhyQjT$hVb<$pzMd4p+(>V+9~Qw;AxA-hcM2 z*=nqBzn3tgYRv=?r{XnMF9+v3eMvO2h$QU zvu^`z(GCo8f`)Gv4A!Z<-pi^?XCOss-IKaaztKG&VOnPno@X2C8DafllE~cZ&l{y; za}T4p^x{xD>?D$AQCGcDh}v&Dqh5>#GBnO$mUs$G*{qLTVVCTxJT27#Nn>h=0M|_f zxPC@r)ha9{UoZL1*CUvB({~hni<;yrAAMDB-VLhx`u6V)yRIi){U6>+YDP$Zf%GF} zLN?OR!(S(i+9~cPIW>^?9l$5ac#poge#+b4 zZoR+pUJWI1(vfwzD`3)L=tkWL$W|B5kqPTli1!;qm-6UEGXLWO{M4I*fr)mPk>0a| zbA3G~#^%~6@T*=-2abK~r@!O6Mt=^2<>D6LQ1Ibuar^q)33Bo6kC2ORZwBp^i0i)Z z(asKg{^Lomv|@Zx^@Ji4>D!c1oF={voWlEdgmq?5S1|@-Xkx|sbwz;F z^9De`wfvme0DNrLP_3q4p0=P#xrPjAdOIl^azpWtN5Ul=7=u+1%#&YOi*aU_%l8}d zUsC4DJ_$4S$T}>ZwNHrjF@5_7UT4{F;`6n`uN32j3$pQi4*_;k-eZ-g{}hAHsJo-&iM8z`qd9qBgi*!OEu0Y`Z2A~dnHP|;#k?Qe-M(=eUF@-HJ*U0_vVt~MW9d;4#s{}X3{Lo%?IA}>bAW=zHg2&r8> zxn-XgKmL4lkCFQIz=4L`#y@}}v~E)R`pPq}pO=vZL!%uXvoSH&)D=Kwb@13Y(%4Id ztv)(qK)F~U(s0WbqqxA@zglWAD)LY)U6Xacw})V8hXpp%Jn#cb>nV7s{tP(q+tZaG zpR76q^c`of^AQ(08z4@tCBN*(Xtw%14|a)~DZ}K06v1V#nOjQ%sN`qwE__cLL#TGC z8SUg0eW~yCSEumCx9ITDk*`Pva1sb1o6*IY_;etv3KU&MXP;!UeZnys zeEwvSXx=TKF5GF@LzpHewp$fwFk;2b|iNTIqN4 zgv8Z%+zNnah5M{4UgGVO9*ZHq0m46BiBB+d;>FwSUDuAY)vWOTd;vm0m8Vv-+DjFQ_AQEDhm{HM{ZA|iT4aqA^B>RIK2Bjy*(9P>-JSYh9$%%cfi+WBQW zS4Z}Cgu!_C;(}6)f|JSNoWNMw=^voEB8GoFOnU19FR40GUPFFqp=w%zEatKlEdR-P z$Z?M%Rp=H40|ZliefAc<0l7z+F6u# zQXL*B^o!E7NkD-1^{8WwK#o}4ioF^!S1W$<-lzukJ9UY@n4{KLNYE5t$L(nJb2q_u zH(OwtZY?o#32=NL2c+9v~Y8JTvHC6VZ_lQ#2$jw3@WpV z*<6R;N{?Ik6-)Fi-H>KIS&2{}N9zC>sFx;>0e@W)DsxtVrgSa_Fh(MbWy ze84cmHA-$P+CCuC$$!6%D6gKIc3F-m``FUOu|agV#~g1~8q6dgbH=h?|6Rkv)t=$P zv?%*PQ+0x7io;0aU(+9q{nmcM+wSS|oe$Q~?Wtz4moaH#J+%%yIv7W7bq!AUv&)Mt zi`PgQE&y!hReWc#?5vAF{s=C<&_7ANpc0g}MAnvl?QQ%?v!%) zZj^!XiQqyZ>$t#Qjs>|M@^`p73z?P7v9}^;{lW+iWfo=8UXq(VFEl`!@%ICl)=->! z;J;6bgs7BAM*A4l7$PH*$zJF+%4DK2ig5^*M*trDau*>`?<;o#DO@!7O^To^_xsnF z8V*|(<11o0Rsg*gUIxYtw!rZnypp^>1+T1l10sH=D~GP7XJDhsTn?CaX@{7iA!i^$ z0i>4uN2Sv%vIR0PGs7WR)aLJQ!NAzI6R4jpUz?wFm^sF8B@MoP)CV6^?^j{o1}O)X zs`LtFdXaYRyoWM?vOKNebGl*Ie6?Ce+R||j+?1EVop`0G&b5|q!EXVz7@Z;p%^SX- zW|V)_bdk^%bl?jo#jW5b6e@3XZL&s)&Ya|B8Vn+?IW`=J@XKeW6VmUw(Gy6s`+z8l z@B`!FfTxRn&)w?!0=VK~60DZY`4XRdT9yRe>)^pYGZ|~9Uh3GP;n~52Fj@=ZCd43s zyxDbsz7JXD19kD6nmW4lv6-13O9b5K#?W*msqNqrN)uX%`%q1=FQJCRj3oJX!6j~? zR=+yWJfaig+%FFGf?EJ(5{iB2i!r?F562N!5c~LOTnOmQ07R}=WY_p9BDj1^@l%nq z)u|j1@|Mvzc#qF@*p<(9%&ZE7fwe0R3p_?b* zU}qhZ7agx#2@xB*Ntw^}_@1p+<;{?8>l2|J#QE3n4pHrBoMdFH#@ZA2Q)a#w)n!lf z)Z;H72hup}C^7VdA%=V^lBF8M=7DVU2zUsS<-M8k5M-1@M+A5eo4Eq*4{%!7rJqgb zSXsnq@~4deOp$hc*m08REC-dngzXI%NTE)jOp?VoA9e$a0bQ$12g74S!jjY-MIL8~ zZ8RP&yzn-?oZfp>9!%fiz~BTy@5cLZ#rUv+KmZ_cM9#xn?Z*n!5Bp%5m4> zpWs_>ZxI*W05EXM(Ji2GnIL|8Y{Zeg^n2vDT#GNrwfFiN~7uTNLnbbc|{7 z*^sMC)J(L^khWgl72!9@s~gvKQraVj!Y(s&JmQeaAB7zC>dJ7Rl$Tr~U!FuC~@R!`obNTE;a` zL>v(Gh?s>&b>PvHUBIfFZOcf_6F;9;Z;v_nnl#ee>xjt(J{TbP`Qu~D&zcUyrF9?l z3tFhW2zbp@wfAT*aO-32{#jzQfFGb!O!EgZX(yv~Xtk0C3g~~5l{0j`KjP)~9`4co zs;2-3%)jq{g!F`zz-xxnd{f>(J_0O!vw|{(A6=iK_sBe(x+wzIXL_2_Bx|CBb^@7I z^eGM=)$n-!!sZ*Pd~xyoA=O4@DJwrJ<}rXa6% zR7788!p{DuA2iNr_I3{R!6lUEeFnGyf`=Nas!!v6ThEpM2v$mVw$&MTMz z#QQ`jc&}%=Kh;-xxWV&^;mJc}n-Va%Ka8U0lcWfEMIULjtK0GIJ<;#6^NjO1h__2q zP4fyqC0lAszi}!#^VhB|gVMwpMB?m}k&SK86kFt}6sYyew9=nK)b^y@8`~!v7h3Od zq?-=k4u8S1x$$6n(^h7~?z_eO<%CtMZ`QA`mES8-9oIY9R(vh9ngNV7lBg`N)!iL+ zoNG@FK$I-VdkaPMCjTAOI{-1ae+|Dm@%%9TW0{IUzDt)PD+0_^g~R$q)LExG(l<~{ zrYO9H*hMQ3Qo7&%2S{UQp1i;)d-)1T^xYta-QV=GAUEY zr-GOK*8{o?kVBlr@%9QZ7UN>k$3=cYSsz0H5x(^^mRl6OT){Y>e}w@%9G1aUQNIBa zt7cZty;~L#%^i|qCQM9<+tXA(af_86_IZeTLD?}#IS-LoIBbJNs70dX}>2;@Eti0*(*OXoX^a6P<>1)?_-3W64YG?|xC@!{Trv`RFAmHx`IZ^l&0c zkGM9`vvuJT(A!(;Q{PfZB7J(kL$OCxL5unDSK6OXnO(0g|8o5H?4{Lhr%Y$Yw*+VR zx6`K_=a7gDxc!AcZ+s#k+kk37h(Byo)5gt4-#7wN7v@;wWXVs2b>tVjICv9ufwNQQ zXmdGgq1*68!0avX~IM%QNjsnyN|p8kO8i_vXveEV6XZ z$Y1VJ{gI~XO&gIZ@+`AQxLn#@4*mvK4y_{xBCMUoH$>}kbZI?+C)=@0>ys4Mb#njbtrWtlBfp9M!>dZK+2CiVcE*~RaAhbQRqtiiX zs#Jvz!?KKq4s=-_5VqXY>sJrR|H(ZIjRYw9v6Fj{3SA>Dd{1F70`dg%Xsh-oeJU&% zA$a*xVh@6dMfS4txcm+ZqsR4!yYC5u zr`=mIV=;Kj`OBA?UuZi?g2{Q4#iC>=&B7?gZ>6VoP|JssZevlq#A7a9y&Mm(Z{doET^q=g z%P}kbW&M1L?DS4YX>^#o^Vj0&L2v?H-y9I5u)k`Oa6fR`anbW7S-NVJTZ^U_`yr{X zlsMRfeO*zUoj|mg8~!J}cPZ!e6}xiw zSq)i`U@HFx%Vt`t;}TVXtHJ!0{z~n;@(FpQXQL zg*;9anpT^TpKUe67+sT)2E5r`VEc|Mup!NU);wLtx2M%WHpI%$Z7Z#XM+>c8#xH4m zT>GVpU!;YHjLU$g`{(FY=WQm_oN-@Z#DDoZE+ICd5McFq(gT;~n#_Q9^BCeQDrJ9% zPkwp`aBj;&=caws!_JmVkI(gkVt4&UqX{s-LI%zQoPR{6$Wzf04C)JENVm#yD$vct z^KHR!T||j#mCU>5^MPx)zU!ZlI+LIo2-cBrNoS}t^)UAWKpvT8+m=>|;vTc>Gl zW3=DCs976sTl-2eSq>UUY%!au$%{Y45P@F*S@U1D4tzJ*|QIXjUQZMQcz15l$@hK<5R=tJ&-K7<+29MH|T) z92Oq6UB<6voFL>RN+8K;stKiy^U}tMZ>8A_?d0!V@x^LT(Ic7UD8g`yw*L=(7hYR? zIZ-{D=ouhDw^;vj`tNSs^T~LUz%e5gfrkf}FSc#hzuBzf~Ne1X0pzC&$ zarX0IDlKO%W7d9L$);fa5I+>%Q5Rnq63yq18l!g0rCqSP`dc~_Vb_*E?g#L~Civ%O zKpIexPxEuKhZ_TRyrRg^%EhqY(czHt`On4Kc)!-(@RzWDp-wtCZqipyH(r6>S3ZZ2 z#ucNPa;D=;Ah4=jt^*yB*h^(oi>Fs(tYJgEPD8IBR^j>U=5O7}?=EmXHfG==ORLi+ zhFERDOa@ZCxrN(w!1dRJs&i|4vSyH(EmA_&lG(@XyOX)CiIG?5x$~PL3+-;qIh+6F ze7@FNey!DcZY6T4o!+HDury9A6bqG1&iHg7m^U>4(_fQa&S)a#$|upK1fDC}o(Et? z9RLfe?`1P<%N~FX2ipgwh7v){Ame{E ztifU1dOIm?aLcjhTC@Y+&JX#4@>;R&qF6RS!C6(~k+_&UK3e>WCL3i>(9HMaoz^ti zTtwgr=*7VgU7lvK)aE6=ap~XXg`mTGJ|&dL(d{m4>-9fm&1H9Fk+KT5Pd6^4L|sS| zkSHH+>hv>CV0gjQx51J;mkLjp9PFg~`V>+YwWSkd_ z^{;p^H;j?KmX}XEq%+B0cAl(Bxnanzay*LZ91toi1{#nBu^n}{_h)qIaXfb`PDjJ^X;_>|C=RRhF8Du*oqvF)ozpnt%99UY@-&CRMzC^~{;0ffqLQu= zj6BuHW@#>URZ6UEQCXG;Hfz=9i1}tD;D?zT{%fng)coRU)AIcFN`}vW;O4)$eSdT; zea~)YF4j7ho|0?{72H2m_#oAc(QlRKk~ZE>dRM(-uW(WMjWoQ+V*hu--WFRr0GHo$ zLd;+5VG@?Hmfsy@4s2dMzy_1S^`C)@NxOQ8YQ7%Hjx6R@dgI9Dcbvc}*Xgkd;rCH$ z)fq~?SGwVYm&5WR+^yMfP6-|sSu#!6# zuNI{ZWnOu8hyAv3(&>%LBzMDKTsXv&=-tiAG~f5()fm*JFQe18eJMVUM!#7{sD}GQ zWK86+vfEC_d!V4y-~t0DN^lRGCfJ*<;HJ}QB`v4>u9v#%znz_ZL-?8sX}TIj!w%BW7vgLoB(Ep z3~#5Dae?m119CnMuI}Ob6Y6D~BVqe+}5p}tD3Y)5I3+z!z7u4N64J;vX7z2~WI}RyD+Q3DsO;Y1I?xhPz~X8>{87Q@@Oqa~ohT%iv!7#@vQI zPYg>hj=gr+e5Xa>xGE1m7vmBq04N z2)xMy&=lYzhhsiiq>gh81MXz|)4OqFz_K@}U5w+^*29(+K+RM1_&i`KuimC$H!%5{AKP#J=@F)9RYnVS?B^>C}ZU0!KzEpM9 zm}|6!X*)n(>XhU)DQW*=$mAc-MD%0yMRhlEo2eIBQ{gQkF`-u)Wk_XwWZj~-d?2ZRJTp_pcTeWHX zuA-Rvb_W&U`j&A2JRo@ycQs9O%FPBBfwAp+RzlI>P;JtFW8yWj-UDy_Yw?&1oL6i$ zD|kiS{RIX@&1DXB6APS-$RL11G4r+;U1sgXKik`K!4BZmi9-r&9=ZX}qOhM>1sJ5^ zT^|ik0ZiElqqM>JgCE$c+=HU)gQcxV@7%(huPf7#5>1K#ou44Fnlg?o_B6H;Vk6=KF_d7^qBX%7aBl?lUZToU# zG1AC45$>@y>aakAepesbPg+T@6)!=Tyr16%$+L$32Th!n_^P}R z8ibg2zn5$jP*y?#PJJr&wbEj?J)LUv#h{Bpm!k5)R=rN2nu}?X=X$v`tv;Y$a(M6Ea1w7o$lb+safjgop-kU zTq6h2USLZIdgkNJ3v(YZXahDehzXnj$LU=OtI!;w?QCYhY(>8kXq&@izt~J6iJ{)l zE$q1DPs4$xh4*GD^Gg}$rRV5@?1Nq>w5A_kYD8zqmHJW>OQpuRkFMzgyfDM!NGb-otUaBw3R_H9#88hobO#Y9I`M_}tt4K2w( z7%)f?y_zamJ1VAbd4mz5896N@$Yk0gnCXTK$?w%3y-p9M{#Z2|7 zk9sMdW~sg(vsR{B_4dCA@5~~Lk5{u}wweAq>a-n$T$%u*s)=DV@#_rWgmnOu+v)a& z=4d}`-4r+xh1G4C73D>787=Qx9O{G?0_|N_p8HpfUHu<$^^zZ`t{9sn@~%}b!}CnP zrHNLEDT(|PkhUf9;ZOs@_koQ~M}#(Bv4-1c7wfMMH{8Oc9g(j*%;eONdrDUNVQ}Ys z@etn0#m~eKv^WCHb4tFR_$^Pn!n+BjpKg;@ zo}@j>gtdq*&%v=W&O^REdl3bzqoGgfMYFl$_rv@G-1{LSTfSa;MkpFzDk4={36Hk( z6TL9@s{#A?G=&xI1F0s&JX7Voz}@ZsyX&Y$ng#(cE-3eQHJ=5#U6^2fJ#op=Yt2*Y zGh}C2*B=}|5u;%+wgUj)I<~-2^{7gd{_1g7+r98$k|$J=-$^5>B(e9r6APS>_UTfW z&(gda3kAqSRJY%qQ@-Bj>d{}|ZhIkk1{`N8UW%@z-#G-$Se(FS4uUnQMQlyC7P7Im zNrSl^K?>>RBrY=4+rv`!QYA~QNfS349ZVP46n=r8m#s9cza7Dt)WYM_0^QNB+FmQ+ zk9vxJ+?IYv@|9jyvq!H)CEA;S)-R#icTaZ!iuBl0_56r=|7P`|o2LEYvK%tOs(_wK zgHj6lrvTe!WMijx2x8|_a=tM8qWl0H3kTq0(>GT-t^>P)0v=YyBdC6pcHJ(ZX%H=u z={>7ai`f^s9#C3iR#)y@^?r>WA7uIR|JXXqsHpxo+EWrrii&_RAP6c*cMTyRQeuJ9 z4k<0oFoT4kv;xwhprCYvbayul-8l?73QeYF-C1Zk1C9F%1&XAF1wC;J!&!H z1Hz7HDGy0xiG$q%K}i*~@j2fxPg!dDR6FSJmNPLiuX-~Ba!jgxr0k`fqs3~}eVPp_lF`a|xkAZ04KkaT-%hn^U7r*MH*%+M!pqV{=JIn!WII%?_b-0Vyn72-c`-4VQVoI`-Mv68Jg;pfC_b0;#5d zXa>vfMYapx01-e_;HdkR>*umu46~n`^h0a^>wmFEs_ijy9zY#NgMiu*y2?-9T!&^F z?P|zpX{(WMJQ3ag(5QA4x|(2?*Ox6P(1G%>=US2&I=f`}gLHX0KK0%Bf`k0&5Pzv= zj3p{dX^X_YIB`1X*ibmH&1mC3=EEl`wPev&_>IYY>Z3(V+-+Rn%Z*CWA4qz>^7MjR zUy93O%b2c1E%FKzu^hNBb?%R~kyj()OZIh*Lb5U1^q8(iB<1&x1=O=MMJ_4ZiE{Bi7`c&ZVCp9lV5(e&yyNc1TZ)j{&*5dLtxa)9 zf>bu`nJ0Im1z|T@Uxe!3PSn0*Ku#vN{e}V#Fp^zsTb)iBi2a%Pg!p>>L7ERmgl69PV~Go z>*Y&6$2xX4S-DON^EZsE>F?Nll!EFL(;Dj|w(WB(_8V?Pu|Ufe4A>)-LoIMhr8XJnp_e+Ui%5cxXME zMVf5LWdS#C(J<24Q*BeJ>~v5JYR_3H9Dv3S%#C^KKrXEkuHD`~=$SBFqx66v%i;aH z)555P<0mbA-)Qid@cop^ALH7H>lcE!rm#H%r!UNIt^%_30>5hqZuJXVa1$LjD^>3& z?T+8%_8Qi!Qw9pu*}MnY2Zk-aANpo1rk)L~k31-Y`%+Ly%}X7qLB*C1@*Se9qMeSB zkj{Uz3J1Ab0u?JBff}EGR5Q5d*mQc%ywS0>&A?|*A%?s;hS58ArCz|BsP@?Bj_A*D z%kQ%xv-E@KkWHDXQl>SY*AdAB-c4W87Qi^`Iseke=~Y)^W}PS9!}p#{xSL1F z5mGk96;dA`lzv*Y)ah1^HudB8YYL-6C@Jn=E(X{xakrNWOzG+lh81gPdO!MLCjKH= z0!Gb_w9dKl39PDhuSB&(c$g5MR`Lq_BiEtJ7M3>2)jiD5^^-YP6qD%FX#V@0t=;8! z>GU`@f0lHGYwda?jw=yMFT6A4uE#v7Xw85b+Hndc91ZRL$)|*BN+Hrc_ zqLm^*{j<#gp|7|Py|(VUx?yjvrDum6VKEC{Bdy<(^jPg0d(rW#?D(e<6678w?YF7< zdXh(HH3#$6?w=t(_@gDldG`e#{u<{qx1-Q>M;^OQ5{24nJo)A!^RF)VAh=t`r&=tJ zJ}NlB;6?4xq)mPq=sm`Gh6shS&?Eg3p6V42zW%SaQF}06ni=+AJ$9dD+_yUEG1?wZ zO?d^|By-0wLh|OZ1?UbhwVAPAL4jZ7tWzO7I`{th&1hz9SOUrQ(5kpBI}I+k=;g}r ze;+BE;>7SrcccZ!e3+}`_OQrYV@IdjMY1HC$8vfpXK8;hMoJugJBTz?{ugY6+_Jw) zUt4Tv{4Nux#hXF@Y(`%Tn^O-UQS#_)F<(eX!eNxzrwu#tZT!7LHoNGKh>*yQ9IgR@ z-NIFeMuu1c+%i*l2?uA{bqJF-zDzlyHAy8bFt&ry;uKJmJ6MnCmBs9W2rxNcC1Ta4 zVK)sXI*WE|O7(J=Nv-FwmN!jIYWP6x?oicy{ysv~kDR+%siLvG^Tpzqvuf>i3TsXh zKLbCPY#z;mg6)X-zf3P9jU%=3oHDUAUor#h0)LCxkX#L6_VZd7aUOcz&ZNE94gXFf zcW3KMJ_Y6A=JejstwdA?hGSY;khwGQ!%lf_UjLVmA9loJdX);1PY?+cvwvc%8IrYF zJPu?3HOLYg&%}H2?Tirq$sb|f&~7B#ak+64|1TJUtoS|iGoX#?9Qvg(+VS*7(NeRrBYPFK1B9X)laM zk~>q~K04q*l~3)%Cbod>houwaFJ|8wW>Ue*A!C4?RV8LGpFb_-n0dfKu;1!bOFHKE z9Tqa~2iUXa#6|sY4$pLMFhe%Tw&itQ34cLtLmwNr{UrBe-EJ?5da(2t2r8-SO?@?1 z&_&~_0lXaP>s}$$?8xni&J1*kLJxe;dZ(F*GeZBwd%(!lRyL^X5U|izfv}-<@kHM2 zu%C9?df3mZGC!}mlTVat4m0D+*4FRwd#br}8`uvsgAs0U2^!CKC9jK^)D1=_wM6Aq zMJ#UePvUdPvZvh>e&_>oves*(q|%M6fv3aijj^#Q&M{nLI%=`m=g$_Hk0a~iqxo~c za5!6_5@pUo-=_9A0lCeZHU18D{Q_qCvVw)(CK|N;H+0??)ij!O>30$F7$VQdEr`59 zO{*Nr^{!+9hB{s2@ieL|OZjDM>mx6E@#V(ymhlrHWNY@)oWKH1_@|Q{W2mf4-%kgS z&E9Z|WVL9u3nNy~uVx&&Yvkzg^G}iaUBAUW@3A;E=zJq^xPuxm+~c+DutYv6ZWLKx z0O6^r0;YEi@fp6xGc|&C=TnGB=w7-{ojJ<9d(Vt+^DvaX+5?ebIIIkz>B7!?1JqaK zo+9%40duz7)(_=pTPJybXD9|`LFk{G+ncoOI$s@Zc_x1iw6(ECP9S@mlDE%RHi(xO z7Z!s{YkbTD9Y?#LCMywZlg!D5<1tQL1!5uHqkO%QzS}0cmh zVBC-LX$n+=MI`!$^9U^P^Op+U!Kjqs{f|Xwp@D&6DAzDfvFolWio7=0bC%+6P{vJXv_Mue*PyF@bho3=wx}1SSU%BddE}{39 zOaxk4YJ|Sh^1IE>iT`llANswdQ!Gcd4s>Td0{KZWC0)9?)+6iMdT}gH1jD!}iGYlU zA>L~q#XGbQ2Ok|q2(2<_YZ9I8NYOG)<9YTsY{w{{ z;Bw`eIT80V&dE;G7h-3>JE+%^mHtF*10V8ENs;krN}E~uP+^10g6rsZ6Bf_|O&`7! zFUY4ZB6FG@SHBY&JeX{TRioK&5{`55<@NbwG znrJZeWS%@MH_ilYAKKlW?vxgW#75z&B#w|3je~UNIT)gk?RQxk>ceJvK zq&%R#GXe65y>Mjnb*%KJt~YtAUAoz>k(9R?Md&BXsTV|c!t34Fmuc9`>^#UPPm2HL zE+PyNW)y>;6eJuh4EE~VMklHltn``u+<2Nk{lEj#mP*Y??T;3T+&ckwqsgS7M1+R~ z1hZ69okS;$q)m*XJ2Y#{5FuG*C<cWmpSq4=yhB4j0qQeRpH;XI)Nj*Q0v>=RMEC1e-2Z+kQde$!i>-f48q z?y+M}nTQA|DXTIalT;2Ewy2_eNIx#c(DKlcXwmkKmb|*W9S>ueb4=98MA;*E^S&}` zymqEvRfaVu?nV{(L@I2$IV*ewDB-!-mTxphEe6LfoyFs{et&j|cW)c3OHEiUN>%lnF)1W|z8-2gP zH*`9}n%j}Pz5G#xFEoljz#-%=4+?k-Ja06EE~_Kye|8CtY#Pz$204_S$}qJ2v*2}3Pp}wD06NjssNO25nD`Hq1w~}7-u8G4{wNp9eg~RQr4hfBJs&F0- zMhMR+W9!{ec4Ux$X|UT4#O(=aW0S$9I5))>5hc zk@Xkp6CTj1eOrJ}+->~#@CCjr3)@EOCg+!ez`HcxO)C8vXEOE|$F*1v*2Q{0MY}x#I#oBvn;eX+KPfevFii`&{3h zBk##Z>GXXERGS*kKCOp^Oa4Za18JbSqq$p-ZZ$Grm_0)UQ+nTu>ojCsQM7B*uVme@|N~-Bd2`o z=jCyCUT?<&`yL_7@}D6K#%4I;)rq!<-LYV&>{<#r3Qr1YyG;9Qu!gpq`h3Jtz7^>)< zcK~AN(`J&}2_{Qb_HTZ4ImgHC1-s0?WKt?@tZ#yhlB&AgW)}ULzbr;g?o+@u)5L&o?4MXhZ!OrV?`+XM3HUKI1!UfV)Ocw$<81O$U4cbmPs}Zn$B?`D85q8 zsZC4<2>}OcuVbXm$I)RmIkx}1*oR@~FJJb%C6Drm$sL%7j;s~ykJtx6k2`~E>(a+H ze$E2pYTdAGvrvV)x3ih!ju_;AI3Rnd5Eo0AsknRiMJO8i0Yn!mZA29I5}v)U0_MoE ze@2}!{KCkCL7hIb$Tv*O2X{-{9BzvFo|fjh=GJz~PJP|y*l8ohCzu2c7I5p0stwb* zh)G=J4wnVXo$E9fhDoqPP3vMNUz=8qd6pzhPt3kHE8eudJ(a(I4EV+@89Jwq&M(t_ zuV`hp&O8~ceLcg%B zxUK!O+UD?6(YrfADee$w96RonSnyA*yUyxToURbbVT4qRg!?2vpiOs<`THZ-C%$lg zODR)Wg+F@$*2~+dbA02+{U0|7s~@#{bN&$-oN%qJlFdWmR`Q`_Du;W{ za^2Ap#U8=J%4sXadHZXBIXce4;J~Bp?Mu&bA|OOXRGk2(vfR|JbC7Ge>C|LD$mJcH z95(SJf!^Iw8;$xVqisYXP2{$2!o^CVoKtL#v6zi+i4thB2v`Jp*9~D^XO#7-_j_XaPE^mEo4A40Q;s@++*tH zS0~Iqz!v?IUnj`WpZc^;n^{-?RpLl-EnUsKMKfMv93CePqHp1l_Qor!7|&7uK_6YW z(B+ptUW+C<%%~T*>ds&(PMFj|%)R4-7qc(x0JMWo5V%Q-SVdl(&N@VRG;{n*!EuEc zcn1L|*vi9uR{ZDYA_)(utggy=FpcI(*;*T9`fAQzeFjn2hwF=K)&FJh)neMzvEi_1 z<2~OGi5w4#zv!hZKa{+T4lta*s=G$y!Sj4b@Cm+w^@^X{R4hFTI!8#erYM&EhGL@P z&?!RxjlqK0&KSw5QyYJy6g`o^%6h_WW(Iulv$rYjKFAjs^~@(I8SeaebU_m}g0u>V zAn!wt0{&9-@ai!^1P1EH2cPIV`a;j0>ivPCY(Mq-45$|QG{cVc2KZ1}EP-3klFM)_#cSh_Vb)I0IjEAe%da_l>uLey9!qa=)hZWqMMvRV zh2;O(KGfx!)mz4ZmJ*1USM%C2?gdU8_Zsle9!|}k zmqqkdjHM177oMi3&mDO33J9qDaByhwWkE+XEON&8%WYf!p;GlkcYZFKN5EfmMKhY; zTNK$~$guatalu@HulnNZ1~ts#R`up1$q`AeTT;p-Om<&kEJK(5jr$n2>#~_!zT*Zg%jyoC zi!UPrgnK2@^X1C!icmRPra=IKhhf1PU_vZ61XD3^*^8G>kp3CK$>7lE+3P%c6L6le zh8HQ-pNCq?q_g@;n^H-N&$AX%MZM5!6~pHOv_X7j*`_(1W1W zGw?!%bj@9mHVirW08HC!#txCO8>e_a@!$XI?g!t^BIbAcqJGd(+Opwf@cqkhq$P8s zxYATa{}urM{(v5fzmaRqmyFYN{x$tJ2Rf(~yq7a8{751!GQG3AHOPx|xvBBiHO|m- z6t-2|zG~;6jj?>|4PY5Kr&A(JVNIKPp0B&MW1nN9Tq%%L$m7 zBmR#f*8#A(yZ;bl=WB>|9tLCH-OJ^^lZuji^+f+7CmAC#%_%5S^vt=B^#s{V4(=*! zD>-h&S4m@&ig&gI8!z`-h96bNv!mo|fGS~!$kV9^m)xJ67DsRbGQSuc>B$psqzSX< ztb04&gpSd9>6qv$v*(qiujChZCO^m%>UcP5{rk)!!f4O;sjA21&XWbWKQk(=j)wj& z`PuRp>Zs!~(2?vsvE&=fH}A-NTsgUMrXv1Nr>>m%BOD1oUVy0*5Jd>uuHPFLm;6Y( zdxhUafgz)dc#Ss9dbc-*L;6}=#RqVY-#Pa9RuogUaHFSrU)6{6s*=w>cv`+!N#aYH z?({OMJ?^bVM9X7mSolk^C^DpfH`No*-_ftynEEaG>)IZ||9csR9M2+uG-yXy;F z*E+K*U_{ROQfNQ$y#r8TwHW+*|7T%VvB~3~LN1}*roEqa@LW{dDGYL|hfZSgTKj=0 zjlVfucDTZ75ig}Q_3&CJ<%B4m`+~{Mk_k!Ro=XyJ)%Jty{|rfew45tbdOeADqSIlu zV(L72Zua5(Mtz7Qkx91v1Q50S-ipYZXdXmXT*G}*uIIAF?AqQt{;qF7u_(d3@RUx3 zULeucY}47P^j@j(fF>ndoLi~67O93zS5-jb#LAwLcXM)l%na&TAxr&vA~7*w*|VQ|PE(fmj|M$R7tSug2KWiw<-it5Q#quJYtgN3iJlr8vxL}6~N zxMvu!PKs40!e#ux9_y$?Cq~Ow=u$7`Z+gFqB()atn49UPwjM5CbVmZt=knM$b-FyW zG1B^*_s&ojLg9NW;_=lF|8K*vPi&{L-CF{qs4sa_s&;6)eZxXr@Jv!Va^3lT@o&Sw z=wE1%w)uRhv*NqpoipP@fH(x6h?Bqz=cqPh7)(>x0S>O?f>WY_o>?Oh{UXr`mcAQbT^`>b`Hs;NKz$?0X-(->g25RB+X2gyv zytv^}%ekHrmfL%*nF?6AKeFFh{C=_)%X2}WiCs(qh=#4AP7C4+yZ8H9r~OY2+FU}< z!!QwYH5G?<$~#soz8H5ej$e`Im$ievb0-y&~9Mi7MKqW?hh=8NgY4j#r8XnU(x?6N6?j=^grrw8Fv1I|1 zjIgjtDWHIT=QivMT!hK^2>rH@%%fzxIf_>DW|Z{xZ7|Zpf#!WW=oLjv2W{6J&me@d zzU*+Z>rNHi51#0seMJ_2n0B5kC!|odZLK-uL;@QJ?%>mkOttk5u7`_~i&)Lu=&>t} z`M{q8Y6L3AS*hVs%x0`Di!tCc{d@pUlpgml8UCWn>sKXTPDw!K3y#f`3HRj`<9aQY}RI;9-3XjYnWO)RgW8eL+=`1A&OSry*dZeFsYi^LB3(r&IGPoB|iv&uzQ& z!2QstL=xdXxSfYLN4d`We_FQ<0nDUzSt_McR_I7b8bqEVhSt;s}e`ucYE>9v0&GGtG-GL zlSW}ck;z*<;D9R>{^>8Y!QJC@?RW^eBtKd5O-_g`<`Jo2I+|%>ej#d!fiINIkx4`+ppeLm9gp%t)t(0@p?=Y zsKdp?0CgMCkxFmW1Eh=OllxXalDDmHha6I?TM08oKWdbYzK`Ak3&p#9QflngCN^zbOYP_++1N}d&$PrdVwfBvr5T>h#~ zK?>=47TM8#)A0GLWEf%2?g4we7E1L3yC}tC9E#LgU^yMO3S2M9T#R!xJc+@YgsL7y zYoh}asAC}q_g4<4hcd5oskpD(IthQPSe9F6_bUD>u`KnQet{+m5(lSw07nT#FqRKZ zc{)(GXYa$6tG2n9OYt$O9b@>EKPOK+e}7nfXn2qzN)??o9)*merUn8ikYdwRK>o=Q zSkBT|C9XSMC-TYR6&iO`01~tQKR2sY#6m`t*82$0ZrCJvnT{G6Y71D9*!gS{jrG35 zY}xyEq(vpLYy@z(+GcSJoiEC{F1Vao8PpfsEdzu|?hF>sc2hk`zJ2} ze0{*VD`vK*`RG3u(#ETs7edIiXhkh+i5JKMsa-nT!J&BfU#a_rm73hChD2HQ)00SS zVbd&mh3u+oEPO1WC8dB~&VU}B;cwXY*#{*AOMTaCbVJzs^V#?p=%Va6Hyw-RRmChgylWBm=4-!MC0SQ(M0`+f?2k54x`(XGiI&^ep!O2&dgg};NhokFq zA6OQB#~15pTCoGPZuob#07E19Y7lZS1AE*7kIWyUXZhW=nve8Mf zta;za)1ynvF`G1TGRRW?XNc5ch>onebj`G#g3r!I)sW6DCdCX zgY;Ea{1w@&FoT7_S=v!6R(Fa-qdE5l*3+S#$kk?P(|@X4p5cHb@LS-T6AucdO6PbN z*W$@x-WW3{al+%UuhoiA)xP?`y;G(px+;STnVAy5+F2WO4o(MfWf(W1!p1Woal=Y_ zF;uvB{(p0oULKtI^XlT+sqFR)a0MpXP?udVEWFjQR z5^Y(A4Rsq^f?DiPiF##s1?l*>KyydU!~$ifVxD@WC&mRieUo=OkE|ZndENT~x^n7$ z&)k|aVm5St11QV8OlmE52pDoRC3m_tRQ~M( zkY^791E2H!Ddy9E>ldwI@b?Ki#s*ny7>*EvJF^yq^4-|@b1_hO4XCOkmZo&O4d1`4 z!2)vb zGFTDX{uWzpb_(XOrGO?AVGE)5$lh9El&tgW|GN>W0<R=#&W*!0_DM1_@kX%<3#?mv}{lsTjF%P3=%z=ctM>JkMSn zPe*?aJxJDM`f1rxt+svH$%AX|!!Z{FecW7e%jILcB6Py~ycWh4_p2t=!Lkr|r^3OZ zQ&Pklp1#ayfj-qLAK=VQ-O{%mkOM%<<(V?e56-qU!r1%?q8pxtaS)Ta244OPz*1{3 zIClijOp=kfb;OlFEHQT<-%SHbT|I^@_)nF)B<>jr9Z;b9cAo+ZawG0Aj}@`Qmn38d zStX?no;P^nhlgc!}E|~7?DJhzkbOlP6#VL>>k5u{XOQ7gROvT$s5yKQ0GBAlrbZ+6wGj;dKctE4UF7>h%z3bZ63Kfq$Y=YhG*DF#drA-Ga+h zIRa~!1#dQ>ry!-zQl;EVvkh|%m+mr5uua7RUokLDt5-KzxV{Y}Dq^1|zY_xmz$Jh))Zt9d zuCg+`Fd8)3Yy+Wv>(boT>6S&7gU@Dbf8OtP7%ZcP!I@J4S1ETnxJv>Bx^@>B$i)CR z@p?pSO7{5&F4A=#`tIzegD=T-=U_JoX|tdt+EN)Oa2xSI$n)&6DFch;AUbJ%`^sg> zaLbT$7d-gwzsp;MuU4(X*Wp%_6J}zwHJ~^X5gThV7VA$fm8I?7CGH@1K+(2>hJr zI8CwezJSH+C7+36^07CoY_3_lKoQWM$;PA=R4ZZjzO&ewwa?g%)R7ibO?v>lyMvm6 zl@+`T9GQS@-w;0efXuf7@8VhMn%6e;Oxmj_-bgZ-WI`@wplJM1x$F$UJ?uGZf}9rs z?ey{cM2A|>%QB(7ChB5OQ|XDE*Y6J(XrWewm$I}dPPR6Ez)+|A)o&k%MTh>ggWw}@ zxnX$llVN-1M<<|mX6D%Fd^=FWW)-qTu=So%qF``C)R`!~byv-qCbk%@*4l&8_O z?4C@|1}gVU>51eA`?I7l(MrubjaFz>vx8V&=9z&pr%~~*MbM>l86a8;Bx#MFX&i!d z$_q0nk%Q~^TET|O+yxL`T?!sv+dxsO?oRK%8H#B_9Ih_`vcXHrcq$trVs_&ciYx7kwV;#FjT zoTwf2;WcHxHh&9A_nA@K6aN0o`X__5nsZ)5aR+&C_!P*MN%Pwm zvfDfXwoZ8t+c38}(s2A!m}xbXU!#qO>OtKPx|Q_5v)Y1VI&%Au;*f4oH=z|Tj%x50 z?xkr(&sOMPN444J-96W-U0)Rb2 zEH<)bxO>Wc>RLtf2CnS?8GC1`;f`wUj*&#bZdR@!hxuQDGn-#4>7B>Dh+5wvH}B24 zM2XyBSM^*g~acp5kX5`gYDr(2E?s0iBs@f2u36ZFtoaaJI!&O zFxA0WFIoiz^xvA$eq^nqy~x7eEH6wKZl~HepzrmdIfkW#^`WjBt9I4Q#=uv$eiN_A zA#Vz|N9{W+pMvz%OO362YNCFDOKqddM`04H+xB|$RP+I?6@wNzDRfsV&6Ic%0@JYov(7V<9d zhc{{6P_*uHoYY=B$7znC^pmZUA+58}BOo{8-P3y!6HAjq-C2|wDC?MGjx#; z^SNf=j-Wb9buF`}XJ&k7)TsPL+$lJQHw4lx*N<^EgG_Lzrqr$bq+S%lPKsMkBeGv9 zWG1`4WiZ)b2rmf9@I|aI19+tZr7tgble`;U4B`qQ;*BQUreUU*gNpct@10dY7RFre zv|8>)$OSYcvBn&WBm@cG(LGv3ifA+aOg*hgkE}kJ8UFzh7gYC(he_qgaCi-&E%Gp6 zvjiM~U@|9nszY&)7>}mKV5E%pIhQOleWnjAm5;!cMGdfWu*n4WSAVNJMB5WVOLPri zHIS-1+eKTqX1u1zut7h)pJ(tXEZ~yg&rDRCTbga=vT4muJ`pIdx(DJdIC7F+cVI`ns9RdfK7M8yH)=>|<{#yk^?y9=sh< z<6`wvachWKI_Ji9?xT~hr10)F!AyvF`qX&Gz6Z-T#%D6M`6i}I_$E|fTCxiWz*S3y zzpsY;=(ES)PHmIme6*`U;cyVaI_{c^4ae;!Vf*QCwbYGkk9SXmK-gDIzE@~uI>1y| zlV|nY(1mTRXUXH8J#3N364ZK{b(trA)lc6zze70&Yw#=Vd8b*3Be%wz1Nx9`94`Q43xIz`O^yFX01I)2ZZ5?hK*g9(*F?CrT zM6ZMB$Ql3~zAddvD`Eh6!MFYGw?YqJ0K@Utz_O>n)dB`j{2qhhwLqmeRUSOb_lwML zL}TOdQ5zVoI@8NKRPoPcFKB^VI^4OaRIV<75znWAf&!_|r?(O(Z>Fd2EHZoutIa-U zODT*k6Gen>`w)M&j=k?pszAXO0o3twYLTZp?bN3C6DK9P_QNq_q(n7>GXWa_2y>^( z0p2Q{G7vE)xfH2139?vr0uh*}vji{?wN#Pz;)9bl=h%{ab>rp^H$MAMxZLi5;P4%? zhrpaW4?wK(Z#r3s@5E7r^bc2gff3J#6QoN03zj5QgtL_;f_q_!=c7G`4zu12C4Jj^ zx0pGt=WtG^KPsL}iAfDHwU8)FMWu)q4fR_DRl@CElj2U;2Z8Dv|87UFZ!R2u{wHAV zn(#Rr#*7as_3;4P#@Awx;%2!ai?Or})jq&}Eig=v8(3_;iC0>PvP3?c6aSIv{DYnw zNFcZTI^7KTimtbUt;sYp+OfhksBY`t42FJ7*-16E2CP5MI_($8j<>#C6ZeGRwJV~r zVaN|{;k)}tfJJTznX%c+)DvGiTaxwAX5^iueRA@6 zoS^!wLLD2j*};icd^^EfK@@eZi?b1NG?6(C`rA)jhZ1L8Mjm?DLDmH^3t;jCk=xO) z*~iT1MnH-EW`w~}d5^mymP_}kRMA~M{I`@0U&+(%whDM}jO==>R$t>%I1(_|n>@B_f%$?U3#H;Wtneg+^mJ6y4!uO2kRI0BtVBD`rzkUe?wm4fD ziIq6m>v(o#zxvyBGTh6BPtBjst#o}J?M%?%!-5ez6O4;Osf_y#h`lHA>Gs!{&FYbl2D2Jgu#H z5Cn4fA(8(wsG*tcl}4lZ+zLYu-{ecZEDOEM>(vco9Rf`!a_NWrsTbbp$3Q**z2?Um zXhJ>3072>h+`GZ{uN+*~BADMKcQK>pNDXO8shspn<0%k8+;jm=_cb)Ixpjhr+0!7_ zJ7?p@FM2N^VWJcq%F5c8rA-2F`%~Kuup2O}NTTL4|Ba2wbNO`yfSF5x_S<+<*rVqm zH})TxZeI(W`R?#pAf0gvB;d*Ex7kjCc`pbt=?gaFZ@LRn!8&?_PqFxL_v&;S%DDM- z`kx$G`=(|5<%Rh}4Pg`V;Qtk#(qX(_35J2l;pb`Gf51_+W?lKf=ex=7?7hhxwI{-> z%p~3$qnLGRX9cWKE9{)#-TLd*34!kylDSK%5 z^hLD0y?p)L*nleJ>|#QihCn*x?^$XMygY-Jh=i8B=JUY}FZR!kvmD6P*Sva>eOLX0 zuLggr!?FioQF#2A_R33IS~`*kgVKXU>>0-?wl(hP3;fDXG3D4ar`ZcP@zn{9nPV+k zXNwtY%U+H5(pdZ5uH%w_gpa)GKf0Whz3GOp{Rn!_99*?W9vv%V>{~BSYDYPnd~5W8 z>KyP|7dtJs-qRGll|)yxsCq3ImqP3-&qhm5d8a>!SVSkk7PsMYrTdkxVN7eTILc88 z7j{c}(F%6}2Byk7J0tVgu&-dG@)8TRd!0Nlq;e<<&gGf9=eafFBJl)`0{jHyg5jj& zHuk*|kVw)+`>26b$z%D_0!8i{K%6zxUs}-b6ia;!615Sh+T~{FF)AKBu zHiiHp!CI5}BFD*bn$_lk)fXlIH8!IG?#OVf7DH!%YFt8yv{`QTrOky5s_j%*l29e{p{#%SNkGC z##dlW=-w3W$Q05^gI*xCmno#r_4L5#uP>^e2VI?`IHA|}*n3nk`YY@K0O>364DiAL z=hCDWkiAoKq|DOt>t~DgHrY`u3=;S zD3rE2{b#jhFpnn%yUngP@DRD~_#+PnRHF!DVGD5*AGR3%3^%A7Z37C(L+PFiH#E~k zsc}8JL*HT+|H++n1!S-e3U+GCotRy}E&PUq#qDV^JmCwEpH;v-P(r<*WTXxw$Hae3 zC+syr0&|oi%wpTrkTx`#^c1Z6f)lId8T`+|(hDqvfA89|$zww!<2e@eIOW;ue-ohE z$)b#FKLPnq90Ony)Un9t=C3{+1PUU|G<-D&gPb`lT5qxBnzlFOi9d3<{P2|^*ES3g zM-jH2y}CWPs)qr3(w1JS&78-~PD)Vv+g!XAg1$lG&6?*D3lU#VS=>=y*F09@EuSY; zZI|f>M^F(j9fP6E3zEn;?Go!5QG*Ml>s!FC@_}}Q%-)lSFg*J(cNl2&wQdt~B^2+Z z$4%TO%*Uwe?R7tXn6F(;_V*)q_THLDEq$lI7qa0BL&gW+*=Ry@!EptnlG}=>xjk}{ zj!gu&mi4xNhP8#a@Rf}JL2De$e<2l5z~=Ks-V)i({dQ{>a?O$VS&`Z&4oT~vj~fK0 zX_{QLx3k*oq;dBU>b3^3CMMVw++DGM1p}NL&cK)aQelcwwT&O*C6agAgzY89d+ z)yywDghi7*xHhpG4fP1`9E~t^5ThVdPSIeoMX2)AUhxqC^jZ5tnBH*kk5PFtM zaa+lXmt7iH=Q~3tWFIU&c}jRy1_+s9ozvWAi!~h6FpA?8YEXv7(|nOqgf*Cvrm=_b zeuusDyc^ldqU62h2FMTCl3GYfy1?*b_{Qz3Z$5w`g450#Kog~fKMQrCy_M>@60fGu zdK!V)f_1QZVJkq;Y(~`(0pyI)GRc&|$ip;&@&tAt zNP4hJxD>6ipM{3_D4|y%=r&WTP1*$~u24UK5t3si5mHV2k zE8X!?0d&`|eq-P|Y7R2C@@Kupc>fxg%5Exm)Ge+Q^7?sxW~%Wt`EhzJ33j`1lj|*X z-qFlaY8$ei^)%NAwz4c?4G%JtzKQXyEfFoNnQP8`Myng+o-c?-*#tCst2c*0Fn$8q zHfobTbbZK2?d<3M;YUZlWUS-D^JE!cVFllP_XvI3A%|gJy~l5>1XPk?4Pj{C76}n6 z$-r-7UplIH@`D(L4R4vf)YfH4gZ2@x4^R)X7Ff0XzBwsW3&+o^YSYeYnXoB^M1JHx zBh75=j~zfeJ$`uB3K-)OH-vWN&(w#}z!#Y3S1U)d-K9!CIVK}wf3uxtU($Eh_PTOf zx6HQ_&i4IrUAg@VPfKF)ws2wH9&M+lFlE`5s^eK7pM-K&KyxR%Xhz~0VBQ;1km4lk z{6;GpmizIgb^;7gV^%i0r{08^q%^%HwIR-F5j?&CaQ?}o z)Z-Nr?}xKW_?no=obPB+a8haQrL&mU1orIbP()H=3SZ0D>S_B@@AY|IaVl*6m=;sW zbTTS)P0D$OaP4av3zHtt57s4Sl9F5X(xj8~k}g84=1upk6`ziY?zTpS> zA-NCF?`Gb7PC877(m5b_3Q3rUw#9LLRDBS@&3tFB4H)9KET!TVAO`|8FyA<@Pida> zji0RL0*gq_=V>-shOb0ZKeI~HQs92dmUAovXf&lH;h-{~Zz(!;SUh&yg4Xy2I&DmR7G z`rkqVW?y~qly32%0un3!jRL+`fCIaq;4KviB#yG4+6VvLhjdTFimqsNy^ywnlPT;2 zNfTwYS|Y`bD3cP`cmj5B6^`hNI1)kMZ;>o)<6taP@~y-891guMLKf6Lkb{4@hlmzy z`^B-P_z~H!Kz5H%rmlf2(e~Ryx`IMlkJ;6KYN_6s-WPS4HjKk1L_Cru4bxM&+aKVV z%u$}U3`7)aQ{7NRknC#=6WH(ieF5Z_dwwQg)%b`!wJ7>~O~cDA8#@$9ykUieR9@j< zRIv|DIhWPW^|s~@2(;MJH0J55Uwbw=YX*FVyq%*@WEUY^{Q}`a6G(qFj^RPa)o+mD zRk7U*SKhMo8dU@61C7J0(ka914OVF^rjm99Q6Dyy|w<~A(^gG|4Knzz_V{?x_?V`w<;cWk)NR7mdmH{ zAG~(>ZoZ^#2R;0b^xZT7rCprt(%cRGL4|Yb(`^xr*$&SPE-Nq+Qqo~@VYkt0NPp~N zpuo4NQCg%>gas-Xa(F23wA)S9e#YK^V~Kj#-$F98gbupTJka2sP$--^UHBlSQd-eu zLGg2Y5|e@qhK{C0DAlA%*6+niclR$wIETEs5}OUnb@Felo~wrYc1Rl;8V^O9KzE7> zwFr;f^n?SH?>#%Xt`C}rDR^4iH@+py-1y2Z#!zrhklH~{^jwUdqkUEF&JtC@`p1(i zeijRsDb&+B;%ot{Pa&h6bHtg{MMsMG`_lI>0yt9ppVp>bONch!S`HFS!!Mt3YK}g7 z;vaSOjk$MbLP>5{iAA?o&;L5wvr!mdB&lfn z+26}g$G{I6L_yD`5htM3lST8(fyCcbMqrrM@2LK*KWUc;3FSA*6$)RGD1-3Nu;Z)5 zR-~hzO+Os{kX>5ZZVBniDa7I=+vGm+^^Od8+I|%`#i?Bbe!NH|lx;NPQb-t9;6`i2 ziM&y)4`U0d7}v=wA*5oi`Wj!~ks{$TD?u+pvhbeVQ>X*BpyjRMa%I_&O)j)ik)hg; zZw(gof4F+@sHWDSjh7N2NH5Yt2t`2!1SC>SfB*t=q$w5zX(AG&_YMI3-~H~of2qr*OZR^Fyfe?t^Bctott5np1lyPL?nKjiT&= zytayYX3)h4`-3#B7s3Soq?PcIJ^c(s$(|+_VRvFid{^{CW*a^kmQ)auqV(IrRLOri za`C1rLp1J@`JGnllfFO4_x{bP!^7zh16)cOms?*SEg5;hE54V^TF;J~7b?7O=R~>o z{*H_EQ~EC9ZEp}hs*u_K#go0y0^YmXDj?9jNlkb00V1?AEk;UHUjK--_<=gn>a)2X zj7!zgn7r33D=LPGLGyxPDe4}TuPsjk&QzxmEISuOQO4NsH73Gk<>b_G>xgmMlb^yE zlMK@@0{8QE*pA7$e%M)X+Rv%Z@hrs9>ynQ-5m0YV4kvK0T)LD(vQ{i>1?Pwt6zX)Q zt%m{=y5N;_U}hK~uAVrmoc@uZA=n3iereRmi8aF5WpK~y9tDl_llLEb=$NO7x_+kx z{U}qddXO@FrZ(`|Fmr7;r(rN!lV;$MQ7ag~PgU0LB$dJP6Q9wW>zL+9{h>z)hSHBF znV6v#jTW(zuSu&A(3N_<>P4r2y00FPG2n-goRoufOqTyfk&Izr=hF z*|Sp7=n?geO(Qr6S+&5t<$w16x(HClgcD5YL$cb#mz};d>7IT*yamk$(K!hm)?W2^ zY}5O}!B_VqFfUusba8fydk?vC5+AzI}S6BPo8S5vZc&5M&gFqS`(Gc!({ z=O55-Vw7km^F@y-n4*1m>N&KOaYoy_=JO(kx5owtu$h!uc0f?IbWHI5R=6`=h+Q@C zeO<=G)G@vJcsim>jxJbo63q_t>Q!(IkJMov?0&wrTGjzed5r&~noWay*sUoVG7}}4 zQy|T0CTJ2?x-oK};Me><xv9Qbxq1hE@t;)8UaV%zR<(MGbDE@owmFPBp4E1*aek z?GooozyzDhAgB4Yg#+sH>bG&Ud<|p7NT^98GY@ztigbA1q+-7>Wy82hVZTJ={eYb& zWKDrjcxjkw4;Z=cekp+u_df+Ud+A06!?Ait(C`nhOFS9fU))u~xUKA1+sjb1tTPT2 zR4{fSTlGq2|IhKH=}!0dcJMY=nK;dphl!Xpwpy^q;A4kEGjh-t$0!Z zR1NltOWC{ve||ZWPW6CA0~`}D!?;FdZ7ARgnh4mkn023E7{7Nm{jca_-CsF=qY``L zUIRHPr(-6)i~$q*;XE|2PYHVX@h{t;+}R3+S^L=O^iIO8hp#!&GZzr+Ly!Cv>f*o5X6dv1 zjT0k&rEK-2oRXO7dCm~7gOra)uWL8ahNcrc-OB{^-b&m7GJQVf4dKuIZdkIghOscU z{JeIBEnF@|5kyMAdHKsZ=67(~R`hL#JWcot)bHRq)X^rr^QOP-g?fUUq!0NSbPFt$ zeqG#oY9e4g-62gaFU`5_hexIVG9AFHBux7o!2S2<%}k0bt|wSR zsuw58{M2xOvLRzaa}}->Otx7H02J!1XE7qfmeG zoq4R3QHcaH!W{dhAj$`f%`Cf#qR;riz7R5Fb3()UNrIqP z2)(dzki|y=17Cg>+Xi-8c>~CH>-U)A6BN$sy-3I<_Uqc=M}Ni=cFp$14EG}-PUm_% zZaoy`Aa(+te>RtR^{Yxb;y-$b{9&ee1cz9uhlJOvjOO5uz&T$wa#RF(s_(U$ zGdaOiIq1ShS;rQQRc*&;!sUfdQr@7{ch8+BDl34ee{!N4iTxc=?SoSG%{u{`AGWw2 zb5!v1ku7$$b~*j_`}IRnJ{bIi`{DO57(e5`X!=ZJ^wt!MclDn5R~iqx&>mOOi26%S z|K)LX+6;i9k%>-@hxOfSn~^iYSH%PozRuiMRi=W5w|vDUIA{93^TNMK0^1H5f+Ugk zcGfIW-{hwsy(oy|*yKv;iVx-CU-CjKbzrMf4gn?C0D*fwE`Km@rZA*OzHUZ~7#gfI zaVnV%oG-2EIW7noC}O!b#h+rb)rMsW@?Dl@lMFDJQEAVKdy9>s(Gb|WSwF7bex1iw zqT_j2@{pu(aI=PeETp63rUcY4r$|iW5(ERaPgIgckX~cGvXCr%sa1=#V;^fhdt^Ts zA;rq{DqD)oPh!(3TyjenoPpb1^Y>sJ$3Y9!CsBttiMwdx_5cx;7b4E;lN(hMZt^$p z?j+r$cmkX<51GzsF!PH`}Xc`m4NF0#Bc zeRqH4{4(3IQg_<2BPHb`8UO)rQ8jm;u;SlZ)&dmr9@iJiM=QJHV;k@G@ z(x+=qsMVT=cU&LqiuT2}BNfmAiput(cb<=0lAD6Td8D>Mn>^R@III*pG}C2mVmL+2!P2DH{8Jp7 zwf>;V^jU3$mglGdvHMAAHL~*g>$*{kUZV|xiB0+q{~vA#QOaZ<#85l5(~bRS zTryI%FMOEit=EXesDXUeht9a@5j7XEn9P#x4e5U$7Erg(a{&phke<5gpqnHJ{65`# zNts6+|6|OfKgm_{UajL$3pN2doRLayvq~|}^G@Y6_WdAXFhAiY8Da1Qt}vb5btYOR zF4^+i>YatoYj_eMb8X;jjUO7Uf+e&A={El~IX$lfOe=N)gn|9dS<{n2Mm>p_$cyZf z`^3W9^eK;Xf;?9wU(V?{+WL18{ClW3^ql>Nvx64Zf86 zM$C66Z80L5ocep#IgpS|#x_4=+?+~A7X0i_@){c+)*3tt**EJG4{T_wY>sx16m! z<(^PFP^c{;3UW-0!w>0~d420mC*^D3%C#Eq-q}s6rLo=QSqu9&D~)8?Sz2zo`l?Oz zLh*)PUbiqGdQ~*PqMgZHf|yQ6KS$NP8QVGP6>|u%Cay=-V%Y}@Eq+n_W4$ppuZBtf z)sERxS`Suhe?U0-y%?0}J)c4#bUxWa2eyr@hHV1^h?2XrPqzrdSt?&XW=iQTYV1!% z#x*{i!cCrN!3hxTCm{=4rd)Vqgth0a@+`dFI)mw%9Co5itWsY@vvU7i1{>1ucLbuV z3D~Mrgnz{}6D?IBCujFk%0@QA@by0lH7qZ=;7AI&)lrdQlZ(;HbM;h{3Hq4268VKF z#I_E!hVh11NC-QoNE*Mib;T^cfKJEho#<4c>QT0x(xETiMssQrIkS7k%@v1CVQ=44 zt&uZzL+sS+0(e4YYS=ur%J%%YXvwn%Bk3cR(piNzs{K$gjud841Gw!qUW`{b6s2EG(&F-j(mO5v04WMFY3o4MbJ1;q;XQ?3AyWAGXE7B;V~v zvcq?~5Xk;0MHGb%bdPc5{CCYp7S!=<=~E*-iYM!9M@WDScNHzD)z2to{xG42xsg7R zZ`ofjk9~xX`06>65^GiI4#3Je=Lr5d*7o>jX7R}FeMp6`bazh&wnBy@P765Tg1v#c=v_Sf>! z|0JB3(a0`;%t@B2jL^c)2GD)yPf_zyC+wTrS>P_JObFjFVaD_2ZPHGANFi-{!>w0i zH-Mo+{UvIrxeFSGJfe|%?({jUp5Tz%D=M6szT zX(?N*Wz!(vgU4T9)#30^WE$3`klZm+!Lwc2^JUwWAIQK={W+IV&g2I$+g1h-Ze=@z zVx8c6o)}g*?dpS5n|`d?)LVF$TxeWM?T%5c@C*+DXGlkkvR?J*u&RI7M5>=jvhGDO zc*oEGPyU!6@$4UlQ5AtZinauKE*tJvBM&|jkeWTZ#%SmZvM_k4G9(aw*_N|*CHtt6 zx;bTxYPa()lMz*y>_xtwniExP2?I>=)quEo?D*oFQ2^FpeE8Ew71}C5JyY{fL{8BK17J~mFSKGU0L<) z2OmDpSQ!23&xg$)!nWu+_ddpj`)?+wE|(S#&7~LCaaBKSlq{{OXQm!#DbQ;0XXo|& zq^h5@0Akr_^6Z|lE$&&i9r%HYt!A7K4@o|>!Vo2gC--fyeI!TZtK|dg2TQ_SloDP3 z*CeyKPMt>m@ds0LnX|tZPJHoCr*M3KV@+LNk>q9F8R$a7zeWwVh$aj@v_>62^2j`i zj51atR4ql}?(UdUu^G2v9ng0>=i4+JW-|+2ofH)RO97@L+Ml-+r&(pp7xkWB`g=9) z$#~b8ST6k)C7owDOIPD8A?Z$8nA8okWHpZ~r&3U*t&g&$(f=rJJTYPc_oVCC352F7EumRk-6&*GQB>Vl0dmUvU{x+J5}1VWhKCWX z9OI3);xkuiH!uO5E9bG=EURF4rQs^k!QjCp++1*i#flm{GD{Voe z-02^mvv1BYHBlrDyE$8Z{L0)a=Uao5c6lkY{D+`Hc8#$ceddf1Sgah=wvN$58#|?V z2Jo-A1bBdhp)Q^OR>ZBq2><+Qhomm%#Im5udphH&`qBA2loaFd`rjf40eSwv=aQkF z*m#c~PFP#9Z2Dn8hqM*P(C}&U(b-5M4P??)Bt~XJ%c*U)C3QJTR+&bL{X?KPoGE#K z)#gd{x5rssEOn&UWYO!l(@AlR5rMy_zkxEKtp?yYh~{NP_Mo{v_w|QV<@VaWByThF z!iOgyI_fI%7Z{Pl*E=uRjU~T>^Ir%5;Rzr-X7fYaJxYz-&$o6Z>!!RjuT;exb#v`| zu|guQ_&4j)u+r@g7V4F#bTQT2deI6k3I9k^OS!$MCE71Zho9I?P(mDhBfxy$%<|*> zv4_siq63(qoS$S2A-md+jzv!Oo=7uu-9}Z6GL>8Wf=`yt8$Wx|0sD8~Rx?8XytgMm zOzW(F@AEA*y(lNvcSh93@Ln9hh~uN*{7s|h2{GmpN2tlX%T*?ATHGuG2jbvKSr@tz zS8_=8RKZ4?dwNZuHF_!&6MD?IuP%=_ChAXgN`&q|A@=I!=~JoRCrZqS2aC3YV8nXS zLeN~@^$Xo`XR|zMVP<67fS@;lQW7Jio^s_n;hOa;T5E1%e=Sz5oYIco`&e-8@L9bX zZP4hP4fx{mJ$hxuuiTtTN9z}=c7y<*&dceVGql0Rr(T^e4TIG^Qd~D z%d`)BV+5smV0Jk`1nRq7Pi~p(H?zcZM|;4Vss>e`A+3ke(dCthG#?Q*tz5nta7jEKN{+`EsZg zC9wkS9WZ0DJ&wLgzn|0sf~cwnW(TlJ3Es{j8be#MuS6*I)_TkiN!q0{w^_{m_{!}u z(|*Vh@CeDmM#dWEm#w)!f|cY=xBI^enpC}*_>~SXsC^(7ib57Khs-@UJu$JefyM-# zs1D`eq=*KjtPcbdbG+D@3b$C507h!PyQX=ZYGCb~xA%AJ+0l<6SD?fo>HHRvI=vba zmz=XZ|G4T+vuH|l0b{>SB?o&KQ-BSj*Ct(Gup@^Gnp{#*7{6LcO@;2V@j083i8cwt ztm$O0G)M{U^`l`#n;c(?cNmP$S1BOxYWS1SO8u<%=&IXa6SUU1UI5GQ@0D05!d7YLV=*B3(Aq-`eT= zU=gRDuT*aTEoO2&B*6Pj&p%Ca7Yy8%nh~o9Y;s$Z0p|5~a#lSq0ZlI@jhD~ZAseMA zgN(m{k(i}ao)0Rvw7g+5Mjr#>b#y@M&igZUt%mRjWcI%~oeJ8R3sl9dN>t|L6gQzK zJIn8=?tG+Dbcfq-w105M@coVq55@5dAR?$uQAbK&EoNn0z%2qPcL@S|e=R9{m#>|R z99dgQeiGv1eav_HK(9D=>Q7I$R6NzXTz0wRrL(I|w}d-c@4H$Y|GX zi0u)u?zkB~p-KOmZFLIpuj_k=QvO_1GIG@xO(dVCRfmhyw2S0l3KgJ#&c1`P$k{OG z%#oPqI#*bR3d~{$^RjC=Yfa_&E46>rgz3=@E?BOI07*D%25)mkl$SKUY;V5 zqfM#GL5%0jH5K0TW9b9_I2Bb|GT;Nbaez%G$4enG4!v(L*G@&?Rp~CMgUdlnX4{ zG(8r}30}YcCC6_fKtHcvmkoPNjM}0<(7Is!TWXSCa71h?W{WkR_b}%N@07IT?aS0Y zyCkYkyDsK{=k< zf$Ywgm@bZ(UHNIFg|fGy69mfa_Yl0=mp!I64qX=B@J7vFvLelLd;@0nk8QC!Rl&56 zFGOe}bdfeqg)#PMP$L-#UwL(rnD$<3%4>hPN8r*76b` zAioniVq8qWpAFf~-I55R_3PG*lsTZyFu7phqVi2PZmX%UJ@LG)&2X;*}X`E_*J$Jn@bt`2X{2Ie0T zm1AFl@y%iJKMk4rWd`kk?lvhv#JIS)f+ zMOYHG4hd+2pr7TXVSs0}%uLF^(t9l~bEFAF&Gxf^j zOpC43t1El9+ZXK~VpqVQ0=PLmL@|FpxU~-?`f<7zG7R_&hAVinU~A?eLV0OC{r+iGC428{NL8zNTG1^Z?m>Xv<)zVR`d1n z-(N{fNmzOh>g;_P2)WFR=c+9XFtSpm-RxByFhEKP+Zh{OLIP_5dDyb95F|~ER%XvJ zwCAUC)i*;)Dj^$yDeHz*kXT}eQ3=hKQMM{|Z08Nl;`yew=6NRU()dyTTZ4j3&o?rq zpUGBsPX0Hu=J*@EzVhrQRb4rFJj?9*a80Md&Hzf)Y{D6K+3;)i!+XP%3Vejhdo35` zcBIPUY$}J3whTm)+vQM8&w&n*Sr8bv_Z`gGgR!h!HGeYIziEu(c>#nYvzJ7h%WlRG zh$wO#P3kucI_!`!SlcG{)ftw@X!%MSM(ZC8r4!(QFjfwE)kJ*;+#PK1K$QKf7rVcUQ48gFuwaW+Wgn>p!~jywP?TDhk+(Dv@x)7TQW}nk zw%$g$rzg|vD9+Ng$5CFv#sy*M`t~uJuteGOiQmU)PZPDtk=gD10bib$zZp$lBtRo8 zEeW~LhIKPg0km5Q8aRhJW!R6o6JD6{R+9{>WWPuy?&iUQevZKBSte!HrMciX&U>?; zglnfc=yP{1>gmRw^KFg}<-xZoXU2LuEt|IY8HzKHxLm3i^hslya)d^{FV=}$h9aI* zjFnPGFDuYA`^{>8JpLVQ5=SKX9gh5PIB&ERBVu1-Vhe%_b9qS57UGEi`z_%v+YmEu zj~awD>k30-eY5?H!lywzMi;oqdK$&PurQ?KO4@g&ZVd?2PnC9Exm`gU`5m@!Lbd$Z zRgyNbF#{#i3890mf3(-TgvdGtFzq|FOjOZ7f@I40Ncd{UrVkn?z9_T^9U);!u8hrS zSY<6E$KZ%g9o^^rGs_0*s2@S>`;ib9j5*mhJhqT^X+w8=D(ixoOvRzi`N)Ojz<03z-+}b5p4L{^B4+rW4zJ3p zZM~34F5GKSQh(NQQKM$5Gi*KDQiQ|X=7M5Q>Q(zrUZ=Df!0u>2@!j0~qxi;nxfjirD)j z0a|ks;g+GtEy)W>&jk#ohx?71;R8&GwG4=F4_L-8THw?$*j*asAu;4884?LodLDoI zTXPEXhKcm)mIUVq<&3T1dD|#tSd9q<*_GCE(PbuDcf6A)AQehTo>{Qm#AMwYl)T`& zRVM#WrXTC{c0F?~FatT&kJy~^{<^0dgh(2X$wR$1@+Ts~1>;D=Wrs}y*2&w;+d4Gek}#fksR!^#&k16{-XgVOE} zv7s6mhoNhqft^-)1Jb2E{Z`-n0i@;g+(z9E9wB&pP3TC~Ltv4{4)8 zixtTr&6BF(>xRMkS(W!y@;rIT5{QY{c#qWANr!DFN#kj8G!by#wC<=$gB_K|mkgn- ze_}s)iozW()`L@(DfkE>FRvv}8_vMF7Epkind3k^f*8 zG%g>qdz{GQd5SzwVK|d)i+r)IZN~JRZ{d0GiPn#AA#LOkVkL2Y21W^# zE%u$v+SI1{L`|2_t;zygn+iWsznO39ex}Iyo9jd)ftM^B@yaDvjc@LuT!}ph0t{Yi zbWyIxBZ03$`XoH@e_#A0x>aGv?_q1F4{WC<(TBQ+9qyZA9j?Sxc#^M70mfU>RwTKhO@_EAX5x$9%7Q%(yzb%EcgF2voGG5#~z{hb+>%#6* z%BNsfO9I*x!%%g+#`tIc$co117G~sdK`NimNDfD|4(?^t;pId=pH4-f0HL_`nfqeUnft&9Wge8xUny4f9Lh9< zfrrrwZBQtf48^~-a(ezx01Z-MtjEZNG}bh})i*1i(`d3H8R!|*Al;wFqr9{7;3$d9 z`irUe-s(@azY$`0YUqcQr0j9()llh~*TANEv;~OvS2-33Iv7>AJm^p0e8e$Yc_W+E{7rgM zVY@p@YqEAE%iu2s;q9nY#i6)ir~)VEn5Gyu3JZt*fiAoVR`Uo-6-3IGRlbhG;K$T zxB$l5`N@<(7-)f_TbcwlUiFa^IN;)-aa>VO$Rj85FpTvQQu^41H*=ldCMan(c}O)P z-2w!@bmjOhbNc9S?4Q+`v)-U4tNsE1pA+FyNi@6S^Aa?$E*fAL%)EbxsH)uN2QT{BA zyb+64?x6!z2vhTdl2@0%bmjWr47^0Yy4yB&xen&0(qnRtlpSqHa(&b5~!d)*1Eo;G?aCKl@T+Vp!{C4S#xpc{wfJ4~1UI|x^a z>ohe#cGS>#FK?vpg%|aw#~a3L5-#c{7FI`f$`pW@Zy4b_TI=mykIJQ8gwcgJ$5ZbE z_&M#(BwtjhJeZYA-@p@&0UI?D;KBNNANeokA@$&i;ix?2kxyqu-87s$mWtCzn;o#+ zb$xX})kxWM%kwMk8*O>Uhmbd46<9V=>pqfXZR*n4n9*BjpG&g-t}l3e4|y$vhm(JA zrfz0o!VT08o)mmM7*kD?MDXuUz1YRZw)Rkr!jT5%@Qku$_Mgd#jdA z=ol*v&iQsTdCkUHnAi;hw<2`+Fn(LtNh1#kMWmAg!ZsCVZRyG zOXVVIPux3uP=Z%Gl=$8QWZcejiHSAfyMP|ipBvGBy(VE; zs01DSN&@CY{s6aUs_cm66Fof$l5N2FwX1N4YKIxYe7BM_(Pg->2PVP=5HiB}rUx?- z3)zd$l?otq8cxsMLx_w7<+xG0lKg(gsUP&s_DtH(X`EWFNM0~}){(Mc=u2~|>h_r9 zx^%SPcxukfU{KEGjevvZu$OOV=qT-08`bxPLW(c8x_x0|1o}k4_v@7RyQ|r)ylW`* z;~o_QH9%c*xKlg4XJ-~xkZsLZGO!6MeMhBSB z3ylb$H{r!gYY5=GFMnMuDC#?@N)C}>Q+X>oXQ>xOES;}=b~G#xs5|*AhMnPj|DfIc zOMaUi=`XZx0D}1_6^-#mQ{cYYjaor4tI`Pj%mJ?QK}X(pn`q+b=3>1>Vh1ex+rt(| zvG>Xniw&bzlXUvPv*#AAado_6tdq9Y=4gl~6KXZY-;rOJQYNC0@p;g|lM0?g) zm21t)hdPpN4D=PK;NXx4zv2gY5d>Bv_DYZ6)AFi!^e$&d1<1Y`CRftr-sfC(ER%=Z z96R#T122z}o}V?FBTx2LX1?JyB$tplstu>am@gcGVtWOIDAwKALaR5&435)8EqLe- zEh&HK!xCFc!*pv6CvkZY3EIy|<{gWr0g3QVg&~mz%Mi7to9_K|94#Ke(7nf;haFk( zs{r~rAzLN??xf1pTgnUA&lDxbsp#h#2C}@#EkG*$pRVu3lXz2ba^CAla3VyYo`}3m z75k-`%4uoU9&Fe{%}P{1B-&;RMseo|DCKdN7{y(0{B}*Ti8iDr)g(_F529PSq*WMl z6%$PCBtNdoLK>yZi~Vl)0Jt$f0b`>B)s!_1SaY@*JjD3wfh5E_ug?Uc3Az0%^oqbC zfHKNr)W~|XsR1x*_i%2(63j~i``3@S1rqZnOb52F^|}etey&ei*w}wxep<7x>ONc` zvQd;>Qc_8IQ{mw01-Ny_094=hV`@EzTvO3C$vNs9Znkno+0p&%8Hw(h45y4}P4gz_ z{cUWadPQu|7$tR}Hclp@IY5l;@KA<#JvEc|Bb(v4MmWi-$Hyn!TdR;7NMuXPEucbc zZC-#E@h89r%4m`I&sTS*9bg~z_=858o#+y}89r|m4IsYTo=c`xSD89o)*F(Rn z3@6dc(crBAUfJ`-|H?6 zE=E--;gl!R@sBjf;T%PK=sZ6IpqfRgZMbk6!@4E8UypO7&=@lE z>P{)U*60xyPW+{CNUIR9D`W3r{IU_~&e=Nxg3s!H&}Hm@s0lZgG|qK!5&9Bj6ehc+ z@?ZZJ_!7b+Cfd7&CjUJSl^?X6P{;={q{k_S&cF+-0X(k`#eIPN(t)ZqTmcVqs7zsU z76AHSda6D{xw50|+s{`&qiig-H{O2;4-Jow0+vV6{m$$bR|Y(OX=5)v+65iPsE)0q z-vl{a3Vb|(T8}iM>cqqISUYYH=ZM;rgyi?-8y-xa9#7(rSTvz7xA1b|&1cx=Xnk_R z>-k8Yk&wJb#c2ZiIs0NJ?Uy>r!lb@PQ_CFTgPdP+p??5>UrlKE_zgr!_+N8P4HHTc zj?>E4%kSHRo4J&WN&lU3;I)g2!QMX-U~wAHaC2s3y*#)5lKNZq!RV0K8j>MC>o?)*1xV?J_5S+_P{2R&%c5H{?deB4j`FotO0KMaw-5%ZB5y+x*xz< zg4W(M2&ssV(LJT8R4+Dcgj7I*HK0nN6n_LD=dJ-br^hl=PfMu3jE0*yO!5eN$zG0ydsi7rxbB#3S1?NN;+jLZ@c_O9_k z=OlBX`vGEe^vq;&4l&GdW)%ga)OF z#!BSh|MwxdU_36(TFlqnzQ+u_2H?F#jQX13^EB>E#%7O}AJUhfzi3pB*nVsof5NX^ zZuMn)&yPNm3UmPSv zX2l4^>L9oU%1oNxbAvonQ5 zzR0ckt#iyZrTv}A>}myE6(Pq~IbE%zq)79+VCQ#!qg^=x^MZANwQ==eA@?*{{$QhV z_3^7e(QkEJC)08X@!H5$Tw23VAea#13qF4;u9)oE`dGavJ0W~18`iV+3nJiE$%n0G zI4K^v1^Lm9S}{1d2HD*HP*XfkQ~dfD0}wEm_f!rlmJ3K%6VX83q zc1*5a%5l2Di649->b}q^n)E`ivR@Jg+GcG*JD~6q;e>ZglcMtxESD>(x<}VKFt+AT zdf4%oY33IYe8LGCjCLJ3B~qu-xO=N>Cdv)v-Yoz@ud5N8$4<)KLf)(W9rxOsisT?s ziFMk3iK>Sj%d`nSy~Q~|gusV-0iygHKfVILpZ5F+Y8eLXH%oH?XdPWiSDwF!B4j1v z2133|uPR_vcG%~vZEMIl4Ncd0J&&ohzR~KAWXhjuPXxiO5Au@T7j?2z*U+HWcQZ3H z(*^i@*W*&8mKLTDa1BPb?-(4bJ!ln!_uU~^pWZPBHf>Bt@vl-*4gk3-?Bh~mF$>T7p0Kf-H!3C*uhNDB z0`8Qtmj;D4>Q+|7s=HLy2Z2b&mIV(m<(1gw2Cp>fh!NnhbhtL`j?Uc=0PRnIUiG|2 zzy3Nd+LWE_b7bG>uBA0g-BJM|TA6vLaEG}L#%;!a;{cb#W? zKie&dV8lprHx1tZ*66FKGo?`_qlu{hkx}BW9}@jJL;0=D5j`_$dyDB1=nzDUjQGCC zZIp)$9DynCG`=-ztkJu$>K#b48O=;h-2tl|ce-|TUdRD8A!YKxctExGsDnXqxxkb}LCCyfuD(>vbiZZs zzmz*Yjl<_5REisw9{4-o8gwGxVMuKxg`@hPMHhrGw16; zqqW=prXD+Y-FTTyL;!_xv;rW8|M>aucn`Y6YH3a@_?x!b(A{gqXjl%AxZlhO1EY;q zrm^_E3U`crx5P6@)7j6fURGp_?MI@1l-U@+s0HR@B&{#8-EEVX(`bnyP|rHTchdyZ zl*$oY(q9_-F6$`LQ0eE~`3T>kdA07#oC05`5G7DsuhF@#6c<2}Ggv8kYC6*XUX9>3 z;V{!!ou|>v$z<$)@F(pTb+s^%+~w12|0>zL5qh7O8}C)kVp9M9or{Ee-u^xmm_*G$ zfk{WqLU^CMh1vCr&x$nfM@?T;cV`kXe_O-m3bzR-d}U#Dme5VS!J|uJA#pOI>lHPN zE%Vv(EpI_uAF-_;pn&|h%$yuz`>(hF#QnGMs*nN@NEv%fXYF2i=mRXjC|MKc~G_Y2~M&5FQCkSU_}? z&>)IxBG=1UxS8~3tG`Uor5GE*r(*W=VCv2HTT(V2?#uc$+Lh;P(jP>*_9a)PSa0bd zF_SaWwo~Cs3eTiw?)==&VcDQRjr+q)+C3&`9`+!NH_cD(@uk|i>2=rvU4`h&`~6F9 zuC>$A-hG|FCiavNdt-JkKP>$WkCP62>J1k@lZ40b^R;POyxC*j1~x^h?mFyL&@DY$ zOxW_h7-r2^`;K8+O*f=`=HXdRInS@aBR1oq3Ur#YA1^<}r`#9PX550;KaCY6Zey8{ z-#c&T`=<#mo|OUKka}suh)ui_b`mdz7eg{o9toh^9|4i@kZ_KfCj#ga0r-h?^17`5 ziJd5R{~&4!2btV&-#JDC=%FQ$LSHjx-lq4XXP*nUJ?r*9Pz7ItZpRaHszh0$z|aTe z$*1qjZ~M<^T}YCjqPj;j+{S6cfD4CIn5>>L??w$)!wO62AwX^l`Z9``smeUnpNQ2V}Oxj01WA$0G!DQ>h#~aula)c2Y}B%1>XII zDO_iGzJk7jmYkWvuAdx~7I@1>C3;dHv)IfF)KXt*1 z@|2Z*K<>-j3toN}{G1Q41I1`lymmn(WQKt}4@^gBlCC#9g0NB>z&N(smi8B(Q=oQ# zF(U@uCyZMJGs{4v3xKgQ1(~u|Q-5RV&{229mN@$mD-fqDUBlh7l(MBQ$H47wpy3?` z0K&rLl4j|vpIL*p+foX52>Zpfdp00zC&=`Tfqs*q1#y0wsc`ak#_iM3GMq{B&M07| zM&E6rG3MhwdXwPzg#UsqtFLCz4W}P;rNTm}cbhMu*|fgM*Jd^)-ED4yt1VdOkL!Ys zVQdpzE9ol2AcpF)@VgPBsg@$ca6KF7;Gz-kuBW))&V?YtR{ zZOyzLIJ=_hB#=4>xdz~swYe`*cfTwtv#g=N!TiQO9KAp`NxCYaA@m(;C19-A{DPhC zAM6k38OlhB`b&6&hjt+~3jxH56RIm93BNGrGar?~q%{!1!16-=_3S#3+PP4X&o3~v znyQ(aT$mK~gXy?W(2cf2sVV(NV!Q(zi|Ed2#pz+iIebSpLl|gg?&U)1t-{-f;6|>; zA5G4{x_Hdn#~&o9(FM08LlH`U9D%gqCS^QeA$tuNp!`JTPcNxkEQ(n>|AR5Yujn8G zqBxw{MXf*=z;{&oMKXXrFRN{lfR}OdBCL(R!b3tN=|s<+Be|0&BEjjym-a?rgh~n* zGSpn!%M3FMD79KT$~UMSj%Mt(mSW!NC>{5&U|5P`=6Qk^Y7E$o#7yo~?G*1^q;f$> zkBf(G$uY2rASXGi%c+edn9@p>KCbWC(dtKIQet%TI>0ckJl^{ycifKiqwLy~OQaxi z*X01W`vCCa?r{pOInmDXXwzTFe+^oGI(8u2M2?jTZsq`VYMb9tBpMP22}CG}C-E8@ z09xa~ub;r5dfNOq`roGTzn}Xa3l3;;0)@BgSbcBRCORn$fV|(FRizma-1sB3I&*bH zW?rI9r!>hVE9e*3Cmu#y#f+uYv!p3B<;s2SS!n(hp*5MTT+i-{`YY_3*ZlIDW}zE5 zc0|YL!4E+l^*pd?PPBx~JYd_m23|I>6(P-~@nBy6MlvHpDp$-vx6}R4%ljnA1ex&& zhzYq{Eta1q0mkvrtvY~Y`T6$YT*7`HSKlr*muClX?AKFPxvtwK`yz$5$$Cx?&=Oo4 zzOaK%z1xtsEZ(oi*Otx&0r->@8VYbDn{OGb*KVIshb!RPOkS3K4ue`9I21&^dM#mBpaY4lGo7rhGq!$UP#u@h}))+9`62>CgUhelpuK~X;LQ=+UOZbyP5yn zNHt^@GI?W3J7$Y-^2gR-nl1tpMotL?O}!u0`XYoe6~p&eT`@T@Ss3%Dy_35Ov|OSk z_xl}WA6mY>2MFkqRu3urUk5}71?GOP@fr5?gz5ekbvpNMHnG3GuN6}ml{%`zR_$OnBfP^U95nuZ z87Q6|y6BXY`s3CZx{CU37?eT^wI_d;4&}qYhSLh&<^2^#!^G~8q($%rU zPZs&3UOFnK-g9%*${a<952ZbLUPA(!HM|4wIB$%;Y25&dhfxq?U0#Td>6au7W89e( zj?n`s+g(x4MK)GXIHs3LJ%W@P>|PY-jF z*YX|bWzyOOMh2B#0-NRhD(UB)ox9sOwA?`7+Y)R~+JBRU#gg5TO>RP`!e5g1HUKW= z^HqGTu^D{qK^ADt)_oz}ZxNM;45jSPvESgoA^+Adsyu4wIm9+KdV&F)BYcqb40;4; z7UIV_^0r!^lP}4gh2iHs$Za^JU7Kmr$sbqWajV^T_jkei7P^xC$=J0ga%O`qUJgiR z)Udk$Cj3BErokojzo$G25FdN=(0C@#IlYd;nq2^c@#R(d|jpGl?2hp`D;t%qVYrY!A$$uZTlXsU*q%xRb+JK zDS&;x;R>!8MZswu87xsj@E279Jsr_1p zS3HwJmWsk)v{H7Gw-g6CMx}CCVTTw#MTb1z8NQf1KA5D7^lL5@i_$#;2*a<8Sf|p% z7P+Z)a?vmY5v|uFBGlGIh_`(pv_>}1+bm)0&XuB z=eWbP?C`@rzsA2HNHL0Jux#~=~q!tLT`%pxMBNH zEW0wCTS-%#b1fFk`Ga$#;*FH>3BGsw+AI?_8#K*D6WjRM%t`R9M~u?y4SHI*x1Cg^ z8h$nn`49OmWhr~7C_vAOW}}vePkACPvhU0|?gRkT_fSdb|Ki;73N*9`hycdF02wRw zPnuui>iOWa6^~;Ow);3pzzc-E-b5u&2nPQ0Lk`O0Z#2@t<`2#3l!CqX>&?u-;;+42 zC%|OE(&uURC)lHg*Hn>$%9R*dL@X3>qfTi?eheh~zD)kjk7c3M z|NEg9aD;%91^j%6M~2t-UvV2mN6*|_rCkUVm%t-p$9@K52Um5;F`o`bE!?t~8R;Ic za*kD-RTRFRRwaEOS50D+6KY6gd(bN-d@|TQFW{B*QV-Jv)UuK|U-*#fKRM)~HxjHJ z**qNP->D4yHNbZCSUKu$c@X@|AV%+52tb;W3c}Y9-d+d**2zn*xi80KdxaGTvdP5I z&2RitI5<3picps_e6%h}J)dJK8jDj_zDXZ6rZDtFh)vrtps@HEcKjpkr<2vTP#qws zr$Dt>7sCKD-B814^kLZE$J`nKNiGSEG_FTC!93cy^rhFS3n`7Q0LJ~~8%lv;(vy#i z?SSWMns$l61YaRW=%wTa{bp`W!86C0MB(!`fpsVU%zCsGh5%m;ell>S+_5aQ`(nwt z3EGyz!6cia94WS_XGi}vd$IpZz<`o>9`=Sq}~E{oX6+}*%;bXI^koe zJxr=Lo0t1<9|}+b#MA9!{#ONH*#du~-grg3m)gCB5sknA*e=?pHEbQMXQLjpw|Xx1 z6zCniz2&y-%qkbpP-n z7y2?CfZYDUHLGJl9b6yOjfn>I-CyhgR5x`}x}v_?L6MHqI!P_N&c*7fe7M&22DBIo zBxO$2%4C@9a9^q-%{nxIJFt}_smHYRAIqZbeuP&{u4x?ryjaGv5*(=5PW3 z{tPT)xuc5}v!+&4A;9%&jHgoe0iE%ejO@6$cg0zJ!Uom91ZV)%s`VnwC3?z&+xV{U zSh-9pqvKZJyonmnIo0L}J`x}$1Np8bGm&;u&4rb2kD4yHPzol;f!4>1Op-5QqdxoD zudgG%BlEBSuL4PA|9|@?{{<9Uri6A2=PDiU`6OZ0D~>7>Xgz`cnMpv*NSXRe-NZOV zldB%`|FHFyQBihnxO7N|fHYD95-Q!HARwWLAdQF&C_^)Jhk&$*(v5^jcc;Kh_t4!j zbPRCz_|-XUo&PQ!pS9!OSJn!Nh;W+jttHx|P~p@}Fqn$mJc2-3A1W2lEmlYLDE|EO zLCr_js97y+7A@geU!!%+8QID70l)>y=i!P>$IL!T=bm$}>)DwVWcD`2EjZo#2a9bK zhV^BZnCOy%nHQemb;ps;b*#d}=qlSf5>d(OvBt$>^lSu|})E0*HLMpvD3zvQzigP{|G)_!S>6J5pBx#X1vf6rr zywk4%JA*;d%<MYQpaqEc3cP-kgmp{soeq~JV$|AHg`?_+w_q~!W% zW`s(sKGmk!hifn~ISut^-+e24}oPOfN4W1XL1fT?8>dr*o+vbrF{k`MSD&lhus<`=CC$t0b-W2Li#CM#1s7JUq%+w z6SVX94w@56+2 z{e4ilD#RwR9;E-6r{~oW2UdFec6M4y8I7HT%z1l)LRw8Inl$El7=wB18sS)PoKU;! z^#;))P-O&hjCzgW6#w|N-*C1^R_t(ze_aM=zBIU)SOa4-2zK}@fIuA{|NfXJQF0iW zdW$ddVlQ#2HpniO1&YkZNF)zr6Carc@xCo(!~Z>Wx(2alkO^z1lY4YlhkEF45G?KL5^HC5~05o1m3wc&XZTPmA-G{zBTcHGu8&y0i<{afY0q_R{(aTEOZjX<0Ld6wLB< zyY=sG!!e!rFR~sw21r7a1ez5wUA+3m){kd~h|>ZkPSYc+UptE}@LW8@Y#iq;e)ngZ zY3^tq-rmfCx5a3#&|vlPqbhCAxi*9GJ@Byyk~SEtSQ~Hx+mZ!AXaUo%X0f@ zKUFx`Ssuf=LiO4gUaJ>dcS7x2U-97q%fLu}PwLWUAFwiD*eU&Ycx#a-Olg_4N$ozeojfSWyRFsClL#os=X_{M`R!cPA5&Wf%di zVztICh?=TI#d2GYgb0Tg=GAtI3{5kjro!S{*}B+BmZQcWu;!f98{i3~{uyqgU0xC@ zGtiY#ohhZEL&yAw$ra~%mN5EA&F-C&+tPA=kE+QkDeONpz<-vGIFP zG*ol%pA8uRmd#xISpHWaJu!q2}B{R^;7X5 z>NGK0!(1*?C5D$$=p||Ny0e;w|7@Q&^aLPEph z2miE|_57Hp+4GnO0-a&cH84ymL-h=(vik1g74i)-=BZ^Q-(gV%kIOAh{rDaCiv9qQ`P#y?9?G!iFB+KYPRUJ~bGaZ-q#4DypjVEi7_T zcN0q#*3%(jKn|tffs5eVSsu9KKuU=7{NwWfLrYeBE)p1T)NnrO6hVDf-4TfMrqhG1Fm&R-a) z^_g>FnA;ite;;O1rl0_`#I!AFYX0^H_R-Q(*%{C9BlV@MahSJn3ocb)1#{mM z>e5~{MS`vxL*`;`&FP#2y7WI_M}Gh@d&OXD!G-5Nj#eT(%f$d*^gzKAIyas-|17E( zpU!=+CKqKEs01pLTeX4ZNSQfxKx7>!!cshcOf;TWaH*ecTlt&6!KTz;v|)t(GQ1hR z%14uA^(OZ@;;Ua1`mM|<4)VuSx8gKB?_JCTVaQ0e@@lJt_K>=9u6=i}(!Q^G<~Jqo z#Fo}NQE}=2%o6U2njHs2FUmX+W?l@DpvWTo z`}Yfl)LW1Kt&U?`0Hq+u8$+mGjOKpHqkmHD?pde9HTLcsQ}u%_UEbJUedCaNm4qV8 zl}$g>3G8i?q@gS5C$9(2|J4$R-_&jwm9a*M-e+L&1~B<)`8IjL`f^_!`X&gXZ>0{` z_69+zQ>ahL&U=bV%ROx&@e)8dw0|a(vgH45%mR(zSbyL>zFWPX!n(;tk)4SwBy5-Q zJ%fv0QQT_cBH!_KzC_S74E*KIoM-I1#-u-N@B>8fzpMK12b9C3(y4-56|nuIS8U>I z=ykDbITVGh!#iHO@Ppq?Wm0I6x)5Bo&4F0>l9pPe_JcWtd$vyx!Sne2(fWW!LSp$# zzt+Gs7I1l)uUIF+rNZkV{P*G!?L_``&WCjm9}vnrbtq4VMNjw8GQ(Zpt>g$Bo!Veo5E)XB&o9LSK1gSL<8n9k^MdVOYGf45q7Ly{vxHOcP~<1 zgGcloiZHK#{rbRM!mb7*R9XlSVesM0Rx7}+cB}Tu95ZWYKSC#)JS8ec1Kq_+>!b^?iNGfot- zNkJ8lD%)|9Tz3;Vwt5RQ2}nS(4ToFCNy!~V z;{9t~UxxfwIU?qaQjWAdfCQ^kAxbqs8|V)NBxo7m;ZUnZJi{C#9|7f)aq5(voTW@M_Phi$U)j z=OooHz+@#{qu^1`ag@tgI5;|W72)m5>?L@}Uw0JN8~+9~EI&15{*$qPYve zV0;J?X}$uF366j5rvmf_r=+1A6vPgsB7dI)SY{)n<@`+kF9-Ye?kI6%@G>?Yv=Uf8 zidkMBy5l-MJ@>6V*Llv>f=Dml%S%{Tz zP`8gW%8sC=Ar&Wl#eCq>B{64;5&{gbF)~nVNIYwZK~u0R+N%t91lA{s!^b~)BZai3 zK^P~_d~{=WFMm_L(qJR_^|_Dk(M}=X%=et-*d@Vx=AsDTO~={%4No(#dw71oSeC(m zphG3LJJ_Kc?VQx7O~2W{#@!hUDH~~A!i8-Z-Q5jxu>+;097f%HT{1Uz!w{vx2-2HO zrfbL+Z1Q>S!;}*obszEl`lDgddvaG-;9~!(=zWAXi=sO;JeDXn1Gtv7YH*Ag+a(0y zFt}wdx{Sp=N$mG2mXFU?ZJK;OcrVa&B1WnusXrFw$E4t zScR$i3?RSV#tMdC9zt%42UR^~M+7@Lw)q5Q%#rzlj$^nrFLP~kiw;t*@CmIo`?+m; zQcj~Y{Xx%!35g8Smb`i}g?U5Poq^>FPF9i5XiZoNb2rD!EaWu+ox_uUFq3BvSd zZYmU)!BNxRIBs*@a+{$zL{_QG;rK>L`Y_t@lcb-suRw3cGX$#7MAgr^vn#3d5LHUa zxl`K8Px4i4A~(r#SlJo8{Dac2%`vVyg#tM5u2F%OsAHJ4B}p9Qt9O}DmSb)_9gO%^+j;+?WDGUc6}0i<0~j`^hdrZL z2-G-{We36YKqWI~_p1&;XKgP0@wvnEpiA;?hi$6YGHB)l_eC6N;j{tU1{y8bHIM`r zh*BMyh^*(^Z^aNbv;+mP-q`*ube{oPKDuOY(=~Xt+Arw8N`1=%kpaCmF=@#*gc22T z_5AzO1aenlw7Ym+mh+pDSgj8usg{tS2_||EvhMf|RXJbqVo-es((RCZ=jhH-OqwC$ zl=H?3{rK6Zl!fl9mQKp8x}Fbdhc?Xw@Q=8M@80jBZc7{jNLj}09iUC2!F%`b%gq;S zZ2%?V!P#cm9qc~GT*Skm2-xr`!TrcxSQYgf9QYCpCtr*su2+b=Zy5P$Y3em+)8{lT zT-LjjrxQPwD#Hs~Ff5dmlnw{vE0LxH5~i366kp56jo8^_-IHreKo(E#$RlSUeniO< zBW1>@tw%MhE3h~2$hq(Ph#X^oje}!c-sy>&mn;FxnV-puA`lsIYLJ;MOx^~=J74>D zHXE4MzztasQLKxI2#!2AHky9sQSOBL2)oFmfhueQs!4#D(p@d!%6y-p>;l(A3=XLj z%}GdahK*rx51K&#`=b;W?knG(roU%&Gi?C85K;#obpJcEFC6}TX2<(5=)IL0T1pE@ zn_R~A6^GH=+px*{Vtv#nqT%H4mgSZq zF#3eB3H1ed6?ZXL!_ydA#9KdZ{Q(l0dDev}0Xe5+Kg~M?-s>rDmSG~ zJ;rt+Lczd{;m#2eGGP97Kc8ok{eAiLB-lzL?lga8OjBWwDFil&NI9}+z8?{RI&OvV zoNzEJv``xW6H^wnAN}0}QO*C}15)CQEL>r)^c2W+o8uPPaLw*gU9o*8DV&aCPQI#q z+upbsM7IMlXixDsac+U93@1;+QhXB>Zc#c zE=wWX^@>*{z@n5D7@>&D%3D8#`0nGu-&TP!1&E47JJ=h~IQhjIz6^wDtrENmXmq-_ zq%}v4e`$oneai+hu47FF!<%j}DTCF2XHQ)Q6JM1u%}=xoG!|6(q%kTVe_%IxynI{? z^{*Wo9)1T-yJAByapuiQf)FodOpZ+a~$kmZ49d>G?eQf9|c^q%0Rnmp^jpRI@ zVCo`#Xx}#i-P2L>&+Z#6?fJ1=UyUb6sc&4B1&uVvg4;%ZOS@^t2lnn_i#4?~ZFKC1 zrU@r7Rx4!eu_O5W)AUB&cKeL0n8chMmSqsHa2hH3?Y7uG7=_(Ehb$^2^P9A~&0V5P zZ$FEdxmnUE?N!C6$(kBD7T~j0pLn;ExIMgH-uK#0Z>+)!G-A~kb_;{P9U8stvVm6J zXRH6t+4cBvL+;av60tY_Typ`SrUi>uji5;MG0v^z9l%{w>|&XRrz6KAej;6$G`z+*e(he$U8WDs zNt_39sm48#FX#xPo}Mf*qZlYJ;Xp0MmE9D$vSm5HVXa_AYI)q{>A6~Juhu?~pOaxG z!(h-Nv5aNep%>&W#L7x#*#bcueo6v>5h>RV8>7#Oq5|C8H=Q+nEc{1F!IK}6k4PUq z*n{BK%!Z=9w9sL=)oB}pxw5<_b*G9^(`hk`c|XIMP8pM!pGJQs&f}iWUbV8fILkIK z)b0LPglkUwQkJlqVZ+>Ph5h+&jzWv>q`ffksw>0)fh(ER0@?uA7 zKxOE<7_Qf=YUszIt}-j_KJOD9p`84MNpGEn+@{ksTG6)%I7P5GUR+VTw%1iuJuRRk zhgNQD&31E!+H|M9DU^rWR1J1-v@*V1T~=!w&MZh>zhGe)yR|D^tI#{u_MZKQfAeBb z`?g!S;nmssGqlz>BwlHRp?UqUJsn?aAVwHC;=Do=Jz|Uvl5@V|F!RmT)&Tx;H;28( zU9X-@qxAX3?;tEGL~s7|ZJ6{^*x0wKMsaj!5 zB%GBwXoJOj<=UUdGd8_V;_~`bhj~en@^b}CRDbm)Y?3nmRt)jg;?BNvciuv>W#}s< zR_88_IV%^ue2?S!cs*DyB6YmrPPyrS?)Glnt?&OC$$c*zQOA2c55&7>o3eo5p}2BX zgin|gC8VHFfj7D2T;y18DxVK+D|CFyr7OiuYv}!Yqt^HuTG&MW$OjMbj!|EJOO=CP z;HGfiJczr>>-+GCzJhu*vYyx^G7Zzcl`!B^?%F8X&)HMwvVW~VIT1@1QaJ8c`9orF zGfXLxG1=t~UV)7YVx?LA&+Plu>DA-opt)8wuYEvBqhJe^g~|$*5IGV@#)EPBLh#cV zZUg-e&`fT6nQx&|xME0S4&m>@c-^odCT~6RD!glS-$>Tbt*wq)2DV6oS-lc6lmU&O zlk0stI(s76BdJF(aXqtft*%YqcReAgs~24@a=}qa zWphIlM0lbo4dukCpUY{&Hm*i*D9c%o`)aQYk^0&mq`YF*AN2B%p+*uqZng3eE=?wJQ z0!yL6g!!sls;tXkr+vfS+R!($CR;DR-q|yf{k+cfCi_!IQRvum)XerT|HytrvSw5WtZPGokAC2uv;;rOcKrLD*_18aFH&Cqs*L(p$X#j;xM?wDgf^1vb#lWyHQ`@Qt2`lC2WY9~XYex6*ZxX35t z4R6ned9`wc2pulY zWUIG1)wM_QODZ)jBsI=q;x@<_2}!<7{hk`3I&Fu1exu--oVMPk5w-k;JOlAr{zY0z zjsbr2HL+aoH`@wB3MIZ62~yee-Ik`fcT3zQjWoyxVJreconI$jzv|ZL*e!tG#NH-p zw5#6a+X*}h`aLG8bCl?na~sOYj(l*jU#is05%c0G4i;-{+g_I?e=n>|yk;c~p26yI)m43V5S zOwrNkl(FL(Yg0X&H@wN)b3EFAW-1|>P@X>yY*e5fyV>H{q2Jb z6Vm@_kfn)%2KkXL_O5U9p{${~FS)50#-s5CQX~S^p*GzJawy>-+1P!nTy}O2WG)RG z{zR4ej%09}%?>&f5#ng3>cpodSF_7T3#@{+?{H6$ho!bXEoPg0yGn<0AI2`0<6-Ul z@JOFCA~;(}g7*1EJlxO8iZL&t?2<8_e526}%|?D+K6J;-0x}?IDW=%-Stmi3>eb&z z|Cf7D+%`6ddi03{A_J0zK7MNeIJUap2`c(}2i1B~|Mrc?;6qr7n6VoH=k1ceOKdQv ztL{Yy@!XTtbp|auKHR;PyKUc>fO|7j>Yy2Ow&)grDI?-9ER-YCz%|9i5J$0}p|tvs zma~|BEqc=l-vh6xi9pFO!`-`RO>y3HtNgg9kE>Um)Q=w&sxL&-IlE0fiB!G#CeifB zL!qXr0(+RVNwVT18o{2ZT-;T7=kjMH5X@ItSB$-p@&F<3Tyw{`?bwvmO%q>%e1hf+ z%`o8H9O9x$f8lPPg%Jy4f8T@NzgG2If$~d|QhXzDQl8v1lp}99Jr8V-sA3%9{o@{u zvyb-5AVtN^nNBHw#20<9fSdHVGp{kCB^oA5bTT$8%vip2tIdtg^JY?17LR0`U%SO! zNbTL>Uhih}Ys;1_tXX4icUtey{sjY8qF516L+xbLwK6(J{7XYeFmok!xai0i-o2`f z$?JT5y?vUPg3N}*Y>SHCE}xIcu}6v!==M;PN7rys%*a4~z#)|lgxzo%Rv?Z7Fc zt}N8JH-DZ7=OOZTqYstIj=mU5-6E(&#=9(WTsUA7b(46-=H;YWa3}AH7F24-_O$F8 zBRTRrGwuBok8at61Pg{-9;|13dqZc~uQ~8TlWZ~<`0wISnrrm(Jo8MRcCE=4ovHeA zL$1c?8kb|`b^;a6OKUm0W1|KC7r=zVPX}=iJGul+aq@>Uq))VAr)B4n*T{>bT_8ZI z0!SF&#V*kki~e$ytyoHF?wv~ZT?x1_DNT=wROr6SJaM9SR!u!n&cpfYcHRMb{6{xp zI^lm9-8NVxmcKk1=GXz?s*Go3z^eN{%gP^H8LPaG+9Cd70ZujEjmnK`%^6j-C!1ATOoxdRBghP^C?R`a~s{DtuRObdW|Q64BHzr`oeU zv^>zaTaEIxcE#Ml%+PK^-(Lcy1=Nu~!_OZ4BXl2C@@PMB@#l&6KCd-1Z0Jrgg^c;@ z+`IgwhUeO}HRx_S*HKZ)^QHRwq~R<6u)|WIkeS*xIui#1oVMXL@_^CnN9_t8cyMi! z1)+UcG1Sv2^5(19$CWw)n z-*h&Fymp^i6)yOC&w~XdqLm8O8fMDR$BMWj?!eJIwTs2Qx_b7wc=}hh6 zG-$j|5P!45C5|ffirijf@*i$F1zBmH2IX!8`kr}HsStminzbTmek*K)nGNyn4s=V( zGdH-JEHpEDR;`5Ya9B+vqgTRl{h4R>%1K5qZSq8te471QyHCbZL(8TmH}!18(A46i z*Mr424wuMH*PTxfbU&I6=_Q6z29DirC|sSuE zG85rQ?FG99D4WL*x1@FrX+NhuP(FX*f_&xN?@xX*tJWPBaM)FS7Vx$5sJdTVDpDFv zjdn9jPA^SQd~fimvodOH6BmknyMM7X{nUMyn+Q6&sOy>Iwz2wpZ_9D7+BZ@i)G^o3 zxg;?#_W#OvY+kmy)>vJ<@J|sR&R`TJ$#-f|gGuA(+B`D24cxK+a3KFSb zWOscjfyK7KVaVKa-<92_hiReP1~~`~v8gde6jPnQq|NH9TVg+dv%QXnc6+S+RPHsO zI?h{0JO$cA)pJZ@(KK+op(~jlc>$>vI}Gb`@L9E_S{%zhh-X?gC-};_*OG2(uMnlnLqN5n7Wxg z+N-5-UI(^no3ae#J0rs6;ic{`PF->qRa*P(PeNoL39XH}yyu2`=q@kL5XFLKRrkWz z%llgyTJ{on2*(Z1ge|%lL{V2LW6^?FD4xdlrewVq;sDQ_-~{hL>)`7fp;@b$!Dn0M z!f0%ZTwot3abIiCk-p&&L%`MCA9sgr4Oz$Jhz3Q_*5&6~m5;^ekC(zgKMl)_V$yap zH^wWH&Y^35MtoxaSY3FVYYI2sBps5W#Z8ER%t8p-^E==2_MMy5>1+_??4#=ein?Q^ z`?Eh^y}`bD^#idZoacWSai_&8&ds3V=pa)I~|Yr>+C zX~D5(Jn5_W8Fa0&SHUUysjo?r8}jDZ=paYp8$r1}*$J3|kWKGE!pRrp^AMXsXY-Bw!y5G?EgTZ(8?0CuG5(~NmMlz^ZhRYCgedt3v&NFWmp^{a$Y35m$Z-l2?(c9 zeYD^A@R|!xCq|V@WoAsUh{td}TnL#7ShcA^uSPR42q~bRi4Ag~tWayBEogpfm`*D2>iHgeAL zDI9iDANFK4B4HO@sXa=k$h9AJ_>)Ka&IcLaz6lXQdMtf7;-Y&Bnn-Qhb(W}#0gC#O z^kAXWlaZHx%Qi z)jgex@L1&=<#PCDGw0sNuI7)q`4Tx+$Y<+!X*VEpoWGz;hZbX^BvM$7aU!>i%wCb{ z{R-8tFL`dCwQXX9Ts}4D!&d8U`qfmw96@npMPF}bTkV7RQlKJp45?k9cuUfoe7Sjk z)=u4nE8V7)`;V)heGrSShWW@Yak7EN;GS8e<9%Pw9|4VfO;-6Q6to4L1g~ec?nR4v zxzlWLMi7hO?U23D5vp9|GRTp3E@KTW21^YH+A@S(7I2YAZUHA_F;Nnsk~r zdwF7<9qpm5A-7N-2-SMPF{gvQH&z}{KE;km*vD>+!DZ4tI^GZ1&s~~-ldE4TcrH75 zUFj;dc`Pp1t>dcAyKY4h|172L7sWtQM8u_Eg=wg|25j!%R3vJD1yhNd16Ylaw{v|T z@8w?O6%-{3AF&D8`r9kUjK|WPlL)m+z4*ss2c;&JFMgHxHKJqfyK8_zdn`~MqOv5f zF1+kpOo(ULUbNeK2>P9Zz!gzTVhw;#&MxvkN8`K=&;i38Mx)dW)|6@RNZe0f{@hr^h_)Z}@NNqh?JA$70rIrPC$xvfU9{N*P54U%Rl1q{EXnM9?*47xc ze@HaGY`nPO+o3H|K!ngZ-8}%tBns0Lg*vsHi;md3B!V;P-DJvefJKt{85QtC*!*E3u=)?`yB+SzR^Yef_~%i(eLK9@i3WW{XlN zk$!4~#GE=URo1cTkqPP?@e$dmpnDrS&23IN)r#QI3t)1%PeN13B&h}6hq+Snedbjs zHkbGTHn@3QXf=X$ToUc0hK^TOuxjv&Vk6%HODbo{<2kIouAwXC>uFuFUr;HXMt$p} z^OJrN98&D{R+pbbe?qN%n8qZTJ#xR$9&DS3 zNC>{h-W)Z){n6?eoQWf5XF#UOtEx#9OB2`Q=ermNoy0gF-;@!Tv^fY?l@`2xvP2bEqmIP=2dXa9HXL>WK59B7cyx?``8eAA2G<)3Od^&5NV&~}-y zu@4y*-SorSoxi*;tM_v6D=Cdv(iy@Hy6F?;EUg;iHr1d^K{R z20xG-x-av;htuvb6|Vw=7~>9iNno73L#Q zv7aP}IA4y;HCvmaY&!yYpUUYu8KsOVDcqsy6aaiXAi!i7zt?jb*kYQrbT^_F%4A5d zvxuY~qlG)pobyU|{~lgrXnumU^_H5@l9hqd?6_uJXc%Rg_I1-vMpx-WYuFL^BH41) zK#rDO@aQwOcO>X)u9Yzs3>8MkQg4uxqIzD_ zUn9E`vidw1nBt<~FoPx7}mTr8um#gdcnX_RL82HCECdm1@g!SJ9{v#Ludcl7e5{FDfd7r?Y>vC6Ua_09}o3W^-AGIP>$velv<0#g|9K3Rg8z)N>zu zMt5;tyPd_q(Ec28fb6h8Dd*z3WyMuy-oo`Rbb-Dwk@-@l4qa%beFu= zD&s#vRf8+;R}|5$G3b@SGO||9iTN8mhCg5ptJvuf`I(4Di|%bQ`{YUsxp;EDtEVsF z(d3_zS?%p4vZj2llX*5WrcAobQr*p&>w)HMQw!4{LM4j8?X>UV=ZgGVC$tUb{ zt><(K`Igz2aw&D!x%=2Q@3oQYc5glgZfV4&YKy-%i*Xk*{<-`~+~i0}A#cek2A12H zsNo^8q6FKgP^wL1iK~m}aTIx-v&5r-6IUM%&jPYlm;p7lrIyaq%B-H zkwCC@82-9aCXG}U)IQP^daAp657w#$LSz1~-Ta3-`h<|;g8&V_;^F*EU4#!7v*v9x zu5=WZEO{duGQH0WI33`ClKTkUIcgY?(j?56DV z_x#3*L_i&23$p;GhXtNcP1`lVNYuSts>s9nh$w9Cm{;e$~3uwQ%Zr4`te`k^zYfyC}uU@huQ2m7p@EPHQI`_$o9|bnq@{M5&YlkI4`c zJg4q$6mPuL{x{{bK64i{tKb~n!-BQ7V#4TSZjyay!^tn3T~PTR-Uv##*+j6;2P+?v zaw=YYrE-VjwqvFV#8o0wCosOb7}McLxIlWw{`hSo74hw@NTQXZ!AJSvtmL>H@+!8 z)+ZtvLZqA)A|Qyr{+Ta(!IGI-y{mVE-Sj@d9412sON!iR#4+|Bw>DpXfPE@ekMvH@ z73;^fGjCGH(7rT2}Q?`3ypIbo{LXloE z{PPYs+2c$GjiEQ#U*2-=4`dmpo-ZVqB=+xF4A<}XnoXqtrbiKwZ>e!h; z$@P_bH#1i7k%|`1EqO)^31mlj);q8L)jTFFUv@-#_GOB_{yO-B+XN{^6T#0I7r2-X zX4m|qz57$lA|L9#ds547B(nf>qL7Wh3hXbvdZyV2@(%7ZyxhlcSq`Os({i*O=@Ao7Hq1T~ zgCj;i&n~sfsYHHBH``=4x+kwN-*DUH$uI^uzN-E7^(T~cK|+pZZq4*_W}jM*JB;DE zQ~${N)xtJ&WPhlY7tiOj`@No-n2wf_hY6Ggi@HVH!U zyilPpSxz)~h1({)k^4dWVf)!{Cch+OZQ!8|wLYWy?Bf93niYo5Q|$Oz=<^&H2f+O} z3l!H_S76sCmB+xQtwkg@()@=T*6}n4M3)IAPz4YopNnGz)6vxq{%VSWeXeEgpC7TU z3_v=otXT`xkDTGBBkB^o_|HOM<}atEAME!OjHjfBbEfqTN&E@)twzUMnM@yh$ZLyL zHksb>!sYM)oL*A+QA1X1P#57lEToFh!vv7P;##2r#N5iFBnwZx1`{}T0;!EA%Z7Z) zpW(K83X3q8D`h5_-H?EYhWDLc?wO%FQui&FT5;TwA1)%b`x}#tM9_I&aX`J*pUhC#}m@WWz#X{nm zLKYRvlrm|SEAB6aV|}d3-!R%QzFpX!sRI%THXGdp=R$BL1kby960s&fl>lcXo6&(` z)c)V@;hU-NY2p=?%#3&|=$~Pi4W&Nb-|3e)>jB{Rk^1_o6_**(pTE?Y|CvNCMMOf` zHl(;=iIFtpE{`?94nmY> zZJDrz+^TUz#wcS32UV0wXWy(RTb7uprD$s9YQ0j#duzkKms`Z68?2qWYdA+rMOQdB zlKghF6ql;6qUnW4e^Ihj2Q)=`iZM}WwnwdRe9KX~%r?aE0_os9R>*aVdv+Ab#XlK? zgH?|gs61_X;3A&fU%4cpuQxz;?4(RrW3XYLu;{B%`s$_u;UwNWV`unK-#z{73yTJl zota-0x6jsT7eksD`|NYaW zsVdJVx4b(^LGWo1Fgira)CWem<>JbDaC7oc=Zi9k-@|${Q*9VXf%bq?SXq;!7(ewB z*@gioZ@Z-%q8;aK!>P7I!g*_k@=Ru`O#>ltv_)VLkU+K3@{}HaIz5VrQxASayp7ZYc`lp!qvy6A1 zwI3UnobKLevma~wNfLx4;!PDKT=v5_qX0nQDtA7=5afQ#<^NxudNGvM_+ejq;AkTV zwyz)>)8MWDXvZXhf1i{Y<)qmmb9qkc!-TIgZtQ){wjfunU;yE|{|RWr_%T|j=Ytzr z0$PE%t|?~G#R1C~a&rIEdH`O4A7B-o4pnIyMFdNMeCe3&wMVc1+`IK82ldPGz51j6 ztl6*w8dFZY``@3E<{eK8w!to&K$zeus$?@+(l_m{zwHkqAw2$c%-z+ycmI%iLl13B zd#~iK`*)91rnf_UzMa9VA(7=Q&rczGZ6v~^%>;*ssMPV(S=fBL_aSm9(o4`1$pkz^ zA>If0;kE(0Xnhvtx#$?4vMGaKKTu$r`;%UV@)Q+=M;b4JR?SVAE*dBCuBe^KTOT9l z#i}iA+9{Nw8bq}|$|~^SqSxpFg3>EJ_~#a790=kT6+v~XW9Ss;2__hgR=YW}@0czD za+>PbK=Ix9t_iuyzY9PUmBr`Fs942aFa=tc;nLpVU8dUrEw?h-Kpq~X1#^qjiG(pq zxSnEE#su$ox$n*i-wMXN9WFCjPYcXH;FV`Rk6aUp6_$3i=f=GGxjt$t9IZKy3c(p( z!#r%SIsw092IVi{K$k=mHV@>0&?xZk(RvVl6aNJhhjn7dMk@@6gY9_yf2MJ$5_Jo*;cEg4rlF6+zm zznz7lX}!B}WX+!t{mIwGfo)sXcK&bcuDIgr3Ny>zKcVig3S^{)9tDq0y~r(@`5aX| zB9TkXQ>|Zt`J4E8tz^IBOl|JVQy1h|D5E#|edm(0K_Wl%{y!QPtMSQmMWdoRlV}T) zzM_J^wv!g#4dL6nZzz6OZREy#)a^f=ZXT{#;ro-@sj|6Errx$_R`O%0p~NnB-3R!Q zv;Pso4cry~bURPizdg0pPn24AO*EW;Z{6`>djZSmQt{(Ye!W_fxsl(w%Lm7KSrB*D z;}b%+l%!N5^|1K2+3$8j3Mh2E}OADl9(P2^6$5=>+0o` z205u5>M2A8ga?JWdtt;-QRqMx$c9o1)$W z7TLp#=FgDz@yRm%tQM-5QCbOj#Zrblx#$j7x}9%TsVF7!&!d{^!E4=c~y>oTGhS0 zE9@;j-`M_5&{g-hg*lF%x(Mw~UVV}d-V}k6@`FhEq2BS{qW+`lXP(EU%E28+zOOjc zCFTiM+o{JFq-iDhPY5d;B1-MD$8bpr=t6~mmwZpLzIk|c=6}$*B6xqsChf+}Mfi1= z|MCUtLV8y$p=AyOde)jp+39N~w=L)YN;1J6>rV+fF?E$jUi$G+ZHV!G&nzfWSZLRa z!aA^=?OH#cL_9TN<5e0@9Q^ZC7Fhh;a1*NNt^13@e0Qv9+ipA=h5T5clysv_GB z3tG6F&WfQ_{#&e(p6ETw062|`m3TkN>~H%9W@$Ew=f6N{Z4(dy^q7{BgG4QY80R2& zkhgAXKjxr5&JGjr2)!CJc8i>iculzQz(UgNs-E2yd-J1}+_N0%X+gR!S(>JNuQn zoQJRR(mgsZ(NlUg$DU=B^8)VS%Wx9t@ow-#u$N$`SWoh$d@{58lAo|J<$|Jw2M7T< zOTt5&A2eClbrp6BDnSs?sI5`D0 zaZs_H4Da+Nj*2 z1y++6RI{xMrT5OBW=`q9Na!M}bbJ~sr4Ox`rc$q(m%9CKc)rqbdepU7xxKDYvAw2N zDWgtj5wGUe>UDVB;M`51F?sTY1Ma}Mzhv4LgX0k#_eP&s;`i+NA%L4mQ=i&mR-IPRweS?$O+qf8#BF*hn@3DR+y~T&}FYu=dl;Rwi z&Yte?{7w<=FZwRlQj{doeY4|H?{<20n!CBYlb<&Lw@Gzx^+If5I*%O_lG4xjX)2&K6S6(xmSfnvIp>g(_iF0_%$h@(t7a z!_fVAw|==AQh}*>e})?QsRxZXJ%7`Ea{O6P6(soQsGc*0IjIPG^uWzT7~3&UY6LG| zs^1`CWz+;VC$9jD|+3UU2k zzq)ZNh)()r%cWX+q;)^?3Qs2GxYb#-5Y|D5M^58I1}0C88oiT+TA;^|8!X1(C&C*? z;iNh4@DQ)uT(sp2s%}%5k$zGxgHhural6tag@Z_>_`GDFicYY^*TRLs5F8JorWcnD z4`($LxGjE998fj7&}&)+I-gCaESYF2Bu{8LXWB7wmO;Q@fQ#3zjoHAghW=E&gz<*9^un-T3yn`@OlJNeQ znTl_LV|C2 zp8jk1j`%S^q}SmQhDR;NM=w_wvHxvQSIOl_g4zWr!`GysS#|fBrOVs)fvNQUQ(V1% z2!(?|!|#s z3|OAdvg@O81>s`ij0&z%IuVw^B)}G+6}k^fSu_yG*cI8bo3b*3_N8cD)n<;Al5~%& z3k>SBK9Nzy${gMyqJj9`9;7TUR!zPOqYTQNK;>+=`qdLYH{O+zRWP!aHr3bDbDLQW zp6|sl?NCi|)s1TEW%{$LOb0K{gx)tnQKm-nJSNLnNQ);}C)8EI^TRo&)Dz6P=Rp8j zJz_|oLdm>867Y<-5x>fU=GPg<%hF&&9~&0g6uZ8uT(5)goW7;l9ng?^cMCOsKQfDn zaa?4dEHoiB3GAhX1-cetTRo>M8LrM<$6TjfU5u5vditMUI%HN*Z~rO-$}t!@1l{4* zDQ^-I*!f`@y5Fyo?BvENzn*|2>}0q{y(rMSOT2HbqI?9ei-r1{#jQ9N*8Cr)-aDG@ zKm7aeLR+h)rFMN3)mlZZ5UZuCXiHHeR;?1Fh>@VGD2kSvL26g+l^|k|DiV9eUO~i) z9pg^F=l8qs?>YX>ImkKh*ZaC&&*$SoZ8E)d0JutZFipGA<9!Yt0gk)&kS#X+nEU6# z3~?V_39+QOeW_I$Mdh=ym=JSWOS%E(JQuL0kb&-7Y2;M0 z_~mFt|FLuQT|(F#Rx&m_ZZ~-F+J)%7XzeYtt$Ik5{*<9#@AhO)Wt&s#!m8+&9?T=V zlTcsc=2+FSY%fiC+x^8JKJc=P&Wvq5TH-5;&i)0eojbsia5-1K@-|%4B%SiK2`)p% zG(F$*9+^8;_V3K;uvn&nm;+`o2m3psCg^;9(BSDBuVtO|lx`+gINst6G`B+jJzIOb z(QCuvHsOs(0*~nomYX@meSE=bx5C?C8ylGYoj9HWceSPimlg0x`P)MGmdz%R0wa~y zyjq?-T9vFxNrj^c;u0aU!iBAaI%XGY;%AQD{<@o-?^5XWU1d!_>fDtqugH;e$eO9> zU1bQFNZU7(6T^;TbJ%8zS2s7f3u50*=}gsZHnetasya{mwe}wk3-pN~erua<67pT)`^ z)S4`5CRsnetbmc8FvajD7LWC<@74Dc)tkwd+I|mi7hFComY0~2j|&`oR_L2b0$KOz zNyt`&OwSyNpFn>(7|`7Q`r)H~2IShpCdfP@2-f}M%>?kHA|c3|m44%eY1;W6 zg6H_)%I;=GFL@9I0r?;O7;HZR!JpeXOL!|53qF)N@@ZNA_Z660--N7|{Cy-4 z)*#OK>Wb%8l^Z)hwm@rvdNP2+vlJoxUd=uOK-ZZ?lR^cZ8KTT{z{b5xclV<<;4w zx}RQe@~O|fm9MlaP8F|0Y-ejnF8BmhZ*>{_6f;io==JLM# zNqj6WPZRnZTrj2R^htMpy(}h-)+@OfGiIf8olzE5Xhr@z`6_1i4!=)7TjB#*RB#|G28U&AIRf3CclE=Z{6+Nt@|iuDM@?)tJR;>Nzd+>(!Uk##>1#~r-J z&3ye3p5#{8`@l=VvU_BGZh!n;5yl&BJdQ{CCGg1jvvLuwH6ea){f_<{5(5eQkryeB}y;Y|;*#+Kdo^&tO5cmzhmUf2KlYQ{A{C_1u zZrp*XO7g2+Z}GnJ`l@W%!WWAeam~AenPH!~Su`{6ec~7W#9F0&v(97vQ46rjoBGMk zF{;(&e^BL@+Ncgd@u_nRHCrXbfA^D@QwX`Y>zS&y$rZArGV$=}M5I~bY)fAWRye7Q z4Vap=KJC&El;UBHkz{GUHzV%!$yCkJ@}p1;NLZKSnyjA|1rE_@I{*FYusI$XrS!Xa zUPQ%3{-;HJ`bsn7<)OTb>x-7#<>Su+6I6UleU{jPJ3~Cn-Its$Ia%BO46eGkDfK~^ zVVCL(OA9|&;K>RV-hH-(^}e(i?(T9=m6$;pr*eUFydp0JZg(y}*ifN6ylt^%T4miR z`URvmS7xAawYHHvC$rwU^79HmaQabwp_@%Sjo)a*g=$zZyNl}GQtHybj5-G04Ci$0 zp1P-2)@b;2V7Ldue_^-7-#VaOs7j)m;-FXV$9PqY2tNH#$y3pc+;5%&fjif!u0Uuk zhLnz8x8I!b)(5HQg>`3xa0q989Gu{BySOs5%ieF&=S7BN5Mt}ol!7x- z7ktMa$G%27OU&IzaTt*53Rp=9vi*cW#xi|^xY@wiyAhdzHTqs7;|Hk;s~@9`eh-=D z8{pT{ZM6zq(p?iF38cAetm_R_08yu~UWI~AqG|r%z+0EH)up&8*;PNZ_uqR^tLFdU z<}>ebKORCx`A_pb$P#H+9-J~YX49DIE36rSWgs@IHXM8TiLkJ|HTv z+28>Uw7z=yY{~Bso^)g>tX~GV*R6(5u;Hb#WeV8y{k)qaMov`@1hFbJWke7H*zbyv zp2|Z)4z04f*8WC=m{VO$3vA9)rMu5$!EsYE0Ts%tFw^-H(Q?0^eedY&ix4IgHs`w+ z$-NRvZp<$b8z(DO$V)!E(3GL0*tYFmo8pq&zD)m}3h6W_8hzDO(hn;UIDub(z=|O>Mb$zQRmVfg?mhItk zuw-Ro`E;#g$>|hvXHQxf&j?c4*skE3>31S4pX>a6>P!^%Rd%h&^S`pet4{;`-=rAH z!@HSuwEWjKTj;0zPo8{tw$j1nRz=6_d)J9LuRoJ~CC25`5taUFL)Ah%4-8;d^WXy%G1O#SVD`-*DjGP~ zwo~Cc_Y1ge@7d=0nS|58LRg2AmPfq!43VmkhHtC{QX9Y<0m9&1iB!qSgy?Ua&m0+y z-r#>8-rydzzc_CMeahADmMeT-k#ynC;@qxyaEZ+6wV>0z(SyY#rRAeH3RJg{iuM`h z()N8cw z=5R^C@=droAzl4c5?@h0e&*8|LRu_`juY8`fZ<2ONYqlGJHnSvJTA9m2v^ZlXO#VDPzBh-u}4P&DX<0LGI8EEtpsHc|Y9j#`+ZZza{f zZWhk~wg*?ic5^$)71|N%>y_|7L_wGgh>U+mr?K4KRkWo>p?4BQR+K*l* z#3QPl*>tP9*z~GZUO+pr{)c*iQ%kvVecqtH4X3v*p2Vn;V54f!)V~J9LM5CsdBlE* z1Xaz|q5Pw`H5EPKQi;q(lIm%Ygg7IB*WrCU#;GRSynZaqNB+E9(3u?Q4T>Mv5c6DL zQ!pSM=&B{5=*?d52-P92=Vw&8P5YFl zpN`q?qn!*$6Ex)Y-$#R_Pa?PpA6_)BVXtWlvR%2bZcP|myxB?$j-kfgL?vSVq)Ae- zM5KZ=xPkfoxMX=7=lW_p&pttW0~nB(FW0$|4!{ZdjS;#@`>5K%^|;!2k}h|8ZUsU3NW1m93 z+ta`{K8+S|v8I;7ot>*6QV-gh9{=VNuR2~(T^}*~Ej(7ujO*{nns-GnooIRnN~*=D zl#>2bmhneXc0qI93B?507!`mT>p^lT^i0(9HWN=QD)YwL)Vr41&@LVt>Al?JE(EN2X=4o3?bSLq5(%h+-7_zLqe}XfW#mzMp{paW~Ibflj zZoNokVLh(Ba{YkpF!mG&v{R`k>QRV`BC>@gD>FK_dw|s)rIF~e#Jqmlz%l=QH1+uB zI+(G}p9+Fo$e+EUboWF+7V1SHdqokD>A~0u)F!4CyIbdRx}G9C+E`r?6rK)j(Ceh+ zn`7jk{S%-5e}m|oYl#`%XFLzn~x9m_`M?xNNl9N3DaySdD|AL@zU6(^NQmXobypQ-y zy?Gn(k|@F&CQG=pGA4fbglGJsaGl3r&h1>9?OWMg2QNzsftj}=4eEi7>NNk|T%a9z zn6^g9ygu1v^dE3%Yy+sHS2wueiL1bopXJZ`S*Hurc-0L^=Qi;3H4DOG4D@m@PE8UM0kkT|re`EE$qh@9b`%`PC+{^t@)7eQHJI@1`w@fZI(C<5ST^fc z*JxPYS=}(YyYl@mZ6@tzjrPY-Kx6Qg`xhFoDwcJJ2JdiPE$aSux0>zv%smMq5wNNQ zp$5o==5`~5Gm>MrM-JM`S0>-|9zcfQ80QK!4zsP8(Xh1GJiC1PZ04L2q-~HPUxP{o z)O$ZsQ2#?k)u9Q~J842c-FT=T-~3kAi4Tp>Eg?JMtMohY&OtoDlT9;$I!x5rLk%bn5$5 znc$dUl6jo$y9)_iXIBRPV}R6qHd3fijq!Uhdt18vJCi}@r>kNgh;3hurj3otwFx^3}!jy(@lOgv9)pyXVsZ;>FbRl%3@vNn8E09~O_1RptF4M{s#Qp$FNX zjs*z8rXzB%A-IH=&m{UgcWax);3@7F0KmK0I}|3QFWWdIMf1o`$a(|afA}R4I5Iq|g8GS9=$S#jvNPn} z*^}D+Z~4-wW;*S5O*{ni2zk%}pWx+kvjP;U{2g(2L>iTbvzkqijzXhcK+y2$JGB{L zNtkkMH=O|X^C~?7KeQi;tg5w%Djr;}t~M!T=B*(juI(%FwTW(_;__d41d5wf{9*H{ zGR+NE%Nt+jk}t->twEaHxq4Fs-y?BEl`*@_;eXFTZ%)#$)dew&PP<6|cEmFlR=(PZ zw;k^U1jGX-imEe{s}=~p6B1S4{-(v&}q639nxn_3urWxGE62tHmyj4l5@$*fY;l~L-9f-dWA?|{~pNF4Vo zau!aO&HmSefB4H<A2Cgn@CFi4SoREB#~H0GX$hlA2WTS zj(?sCPl}{ro=I4iqOo5VR60Hxj{jA7;Fn2R;KX<(@M1Sgv8QubCAn)~17^U zO=|GKSBR0?juwO<5xv*^z$*t#SBC5m*+$X0^i$?nkBFB~a4@zxSnli1jJFTBRG(p$ zOGW!--ac9yh3S0>h%7B>Mlgz`XK^L^eJPERbUks1#QfFzVuPJdxI((4TH{X$(cc{8 zzH?@{_8h3Z?}x|_@%}{7>ciRxuN~7etm@Ip9lT!sU?#_?r?OpE?0LS!D6P`sx-7UTX!`NAGHuA@!h%sQlTEj21!Ah7848Y&&y^^LN` zpOBm1%i8?;3h{(ibcEGx-C!kI&W>M?@QI@}SqkOhm;yYUn0+&i=`c&ns4JvS? z9p4MC_1SY6s^lggxt->T)#kc8>zTpk^Rtyq#|M+dlwSRC{6px^%*DoPr;oxuiH#(% zn~t4t@8i)iYTy_C_s0%xAI5#I#js?u5lHRNy^~m3ytLN+4{ER`Y@I;`EP1)9I7Kiy ze%4YdU-;vnk#kPPZ04L`Qy)LH(7k&;G)hh0C||?W8t`&b`H2~8kI(v$4xXLVIP|xc z3%S?CRO>H}kr2|gS>t`>HHnf_tUmxU$h0957 zXSf+JQzOc{e$89r0L+114ci3`kVsM*!k?Nxa%LXpb@&pzoZ2Olx5&!M5YHv#?V9kS zUHIvXd*5#f7+98OcA2d}5NSwLhkk&hkj2LL^qHpzqbrsD%g;j?Z7&T4myNf2N}7` zzm}Bm41Bh8o|5-Q_{(A|K=FlKZ}Ja7(DwR0mGvM|zx*NXdf-JqQu^8%-o(wXSlwn*+(-Pz+CypK zD=?1Dqo9seSP!86+VoNNsKA91)E<}CM2CP}+3~)(v)6Kytx)S#H!Xi|Kr}B{YyI#! z6gN$o0PIh6B7@q6WSv))pw|7OW-sO*%Z-f{Bg>{BQ{#X+FPR}88nlARm1Ld>>%P!xIflqBe65IEC=8t51h5zbA(r{IuX(I7V=!uKy4F|f7 z@f6FGCjstDwN-D6!LsG;t6Rs0`|^l%xlWKfu-wi4Gr?!w zU%=&sXFq4X-t8}AqyfybYn@VzLT*L$fxlFCr3(+Xl#!E@^i2$f%!W+$PNVUz`^}jB z3e5NiqLjq*yx9xQ*V_1|0$r5yN477G`0nGC`Ct4?Y$_1 z@e-u15dgNqFrw=#H0~5$gfZW$$j%zd^hTvIN^13S$s8aOs z5Y|VK6GU+3DsTy>AHk~99}>sDk;vpooQIimKbAHY-M`@KSf;o9bE9$4Kuhf+}+S37VW<;)e-E#6XPyYQ^J{hyiDbBa%PcF%|qD+bx%q7g2@t+ZE4eWgcy+3vdVf zTEyex`O5~Z_Cl@o8Ue>pG!EKJNVh|5LTXockDS3EGvV!X!c$pEh@+Dv1t^cFL4NJ)&NnWk)wjDy6wvb1t#FkpDq~dXNaH^4l=zRyy8iEt{(rElEmrN(Ka&AP z2>&$^^z0}4fqwE;KxxhVb?ROI;5s#8sWZ{pcX#4~lkE?>o%o67!2bx>WUkM{U&0%z zMz^!wuM31&wk-Y`e3^DQ>^5Dk{mlBB!A=7UX`9MI9hRm3QupoysnT`U#v3f2oEP#h znNx#B|AS+v7N)1~Nm|_e0>bFbVwCQhFN1>q{)lGzSNw?km?D*6fBX;oRC2wEPu(?F z!$fBO=EoUNobo=EW_o`qMDpJpVPNr-H#2&g3sI0n1#Dy2Ks3on+R&af*#NCWFA8IWG;9D-o`QMg(?lD0+_TK#i?h4j12SuZq zH=AMF_kCU*J?R!!^zwZ19~XQwrk10^9l?-L)vDm9x^O346LJ4Bnum4jx5!aWq{U?P z#`H>MTgPim^lrqQY;-HBuiOl;x8V@AuZN!;i$EV|zZS`^K zwq!*eBYReL>Mi+V`d)n^TYf)4Q>aFdIgbI6?ywatBDYR@ANHQ_g7*l1kp>JI%yEKL z`!pHrC0n6hq}W$}pD;>w3E*-ylcB!lP8GRDN5m%RA-PDve}nu; z$%K=_l;IC`Oag3R>QzzYIFFcYF}PgJn;1t93;niQ$%b#$r!LQx6`FLjJKgL5p!uTN&U@!GUJ<+*LvZx5^|>xKE!! zqBt@|kDydKC=HmXXBelkEPvFhWHkI{q&Isol|3BuhvQYs#0re z?VIZekDX}h@I7_fl`&11SYQ_~dkdnaSDHNZs|2eVqnW?dDW%)veFHYU39X+b9D_Z% zLDfuUE0RuUHl@ZcJWf?}XT6vTzaJ!(4^?2xjjD3J^L;EiMAPjZptLdbu`f3m#7uzJ zCLYQX!^XKKVvo*xy%MNZ8LbAwpJ*3c$DRwAgQGb?od z)c8w>;HimRpyD(6^ZE$@T|q?Sk<685_wvfPS}O+DQWu_;DKzBVVOOmtIV%){=3Z@4Ah;*68r(c)f*r# zn{bqftDFc3G}$>?zl6|HZgdRBWheS_0@+=k+_fy%LpVrx+PKcVDl#eT99{O@E>XP2 z3S%>R*PHXeO5Dp|2zCZCeqeUfzfaX6bCM99@kGisVR?Kwf#J!XJ$Ub_*^m|T&#Lpo zm@U7ls}GGe6-li{y{$fnX$_SPA);m>xby%cqRinWuC?NOQ~s=xYGzhRm*3dB=YjRe zA{p=X86N+?sk^Kwc`*qNZg3)?~5B*IUfsNjEL?!Bk1^>>ou`K$4a+1=^ zlm_ZkYi6I*e$(2JJ!F5IAi=pAoxzmPb_?b@v9enxV+a9daUk9^_bgHsQ6ut>_zTXT zS$;)R6D#@wS9+-MbX%6gGu-(}EV#U#O&gwK z+e8iLp96RlV!v%9HJ;cFNNTpSi$U6BX=Sv2{xfW7-=+MsB*e(if4Migu(Cyk$S^{h zF4Emo6PALwFNL@|4x$}Wt@zTtanp6DpN`Lks zD{#v&Wsl~>Bk7nb?zLD53|I-}-Dp#x220j^-2y$c63~a^Kh#oKg~JpNGY;xInG_bk z-D6NWnDiuBZ2BAj=5xsKvmYn|uKmi6yTJ$gX2c{jd@vIfL@isLzMj0geREf!END5! z;dR4REoDV2>Xk$_6Z^9CWWT~L-`EHvtXY+AS6P#>}y!mT_o5e%Q2pnZbwPX+zVw zL4FtV59B!DmeDO$#X-&N`LImu!)1%}^_nHNWyjmg?6@nCO` zJ$S?M<(b8$!EjQt=!lHtm9fW~*>@K=)!iTgSy-;ea%XWuHq$P~asrebTCo|X%@_8^ zZObLs7QkI)LOzf2J;HCGeh!j#m?=&`ZD}{x6V3A%IRf5`nA zNj#daj&K=g^rdQD|;%c7>#@_}d-O;pl7UB4@ZM06uq z$9x$EkDip5qQ7AWh)h?zJONY!q}ESMijWR?PveuSyU%w;E)_qH0OSBlFhD z^)VYoW4uK0yV7x(4nFYkS>b{9dRmMT^nEi79^5zB%hs!l98ESJOLVN20Fztpk8zhu z$D$G2QS4^UnT{ZH;bg<757DWRt-d}GC#kxtT z;6_)%G@(ns7{Stn&d-8mQvXv=aHf8kfFWaDr{1XdJ!2`OnY7TRMwAB_3>v*I+d&6L z%ZtHfnJYO!bPiQW6Et+uB&)dI73K9~y@A8P#b~^dzO)q$RYZRsADfs+oRB6HCX^)T ztCkD&p{`}$!7DIcl+}0T)jbFPPk|C?%9=3{Pz;i>s5SLD802$!hl}J_njNo%G0HFQ zB92Y#ohMrhkUR*ZWZ<}}Z({~j*#*x7CgfSmR^&bg-;oSLH24g7&bok`3b!7lns+9ndPDk#P z&1sLh2qN&Fh+2y6v~d+J0!|q`y#-_Hg(dWEog7_JEWYgicgFkxN;q^N_4@%WU7gE~ z54nGpWIFobl3kczz~2qBi5m3R3_$xl-&zgHv)jKrn0 zGdl>Vwuec7{Xn@6Z;$^tPNEXtz^_Qn4YhpU{(_5^6Er6ZxlrJ!z`8)3@2r7u7`OlrKd0Lad zEiruF=Z=hR(ah|RcVG=g4_B&9Au_^C!i1d3u-gH=7M2@ZaiV7Jgg$DbLg}~)bWxdm z)AGaliH)@KqID9tC9PqpGf|el?dtvwDKNE&tNk(fhJ8=+qbVR2{ZDr#)^WAu{m}kx z7-g6LYVS>{|E@f_4@Z+pWNiOzQLw3QZn0P^(Pjctn;mZmC~2)6 zj-VPI2jqX0zbVcVQ+2cKKldm1BbsiLMyUc3as+LabHru2mPcBl;-7tB#Bg5!UsQ2` zt8!;}sc2t@*4@a&Luyrj{|Wnc*M)&M$K1hs!12#zREFE`RlJ9W21i|rLF0yZDKR4C zWGhFshCa>bb6aW9DKWn3yW~e1ciYyBUf+VQtX+=+ob_MP@>PPYkL4^esr<8F-}|Fk z6WzoKo>$%<69~1qsj70x>hJeKhA(_|RkD5T?)ji^mX3KvnNHMGUB^2lnze7YJT^cy zfn(R|s@Axz)|4=VohtCgKsHUZ5m2kY()y$S^Ql+97mBw&DSjNMVpm@WZGDf0-Anj> zB2|)erq0a8Hp?#KseEZ-NsBMQ>o*;@NaH97IzAW9m+?2DZ0qB~jKV}l)!03COndYP zLB3;qUtzwSP|K>7%yi+StcD7X(A_^=OS$z$x~h$9_w(U;KAMi<~ zh|7adISghq3J(q+@|n-no#hJG|LD(PWOqicxi6vjMs=2%%F;UoHL1=jSc#IU6SR!Q z#pW=Uwbiu?T!=Bp7N?tc^df&2+bn2VO|?n5`im4drO} z)xgRHO!ptxr%mrF{5KQBZ6VZfVf?p^jt)HU#zllmQ8RC;1U~lc$OkU#pL6@~Z9Hb+ z9YSeV6<=H3tLar`QrjE?jiW)V{ixAETUROfjoYjEp76r^*Lf@pS|KI3NS~FNs-m@+ zQ2AyAeCftXe(w&}#iYQdl&AJP#s?Qbx(x6cefrSaA#TwZ6~t8W$yT##>WmX*!B&YvRm`N7)2t=_MB2LR8_$q#>BH#brH z?ed*yGak2iA-yt1W;|Eq8M2n_ohY8b%!@r?lKZsenQZ ze^0mYB?OgOXH!@(gdq{w@ig?2Zmt>a5>cyjYcRfqA^3kUNp=XSnz4S)#8S*rcUb zCvmPWLVFiw)&r;Z{5oS-9lhm9PO_HQx1yK1&)(tm*4x^e=3$;R}@fJ{ouls=?iIXjUPu}kB=4t-v&ucWBiqk4w&XjQfv>q zF9Wu3k>AcO)HcY5p-RzL@r3;CcIDWGCzCC4uWzfAyD+-)zE!7DYG+#}(o^>5rpNuN zO!%DN*Ehm$mvC^j^3eG#R8k;wKNYVlMQ7mpx{rT31}Q{e>gyqvZ79lpg0MOt*3Xm& zUKY|l+D zZ*8@=k|+lY`vuwZlNqg&D*ET6xyG}ff*+l9tX%AyZFZIWlCh?e&W-g@o}wBe@7-neTV}wPPk|t_+Z&)pz@QmUw@~ zdjvJ7l@8~e;iLh5O+;;8Xq9AG$kjZqQrwTy^IKSVJjdBsnhn40hgeQZY-csE zY(Zwx3H@eG!k zud+&3U&Riy*uPRxjjBWKa{MkCs#ogqzsNKM-JWUK)0LA9%*tm!U#uWXnu{zLFi!>x z6ZC)9-B?JPIpQsfy|io4Iasl6>S~d9^0DrRtq@tfysC2e;MQrIdjM<%e<9K zbt(zIy2jTp>JvJ6{ZffRL}|jY>W@vMiVKaYbr-L|Oq0O|sTo|8W%VsPg1g*WE&!uG z`>fw)!^RE90OzFbLcTD!JYq<{G>|>=0OecCj?%Wf72mJfP1Eh3H6F{E_D>KQ!`$lY zo#_MWkmuL}xx5=&JA1KeI>R`Hc(~`y?mBSOTl~A_RD5&MRn#GQ{1jSw=>MqB@{XHX zs=Ow~z~gW67ybLp>Z=cc+TRfyN?$hIadhn>k@Ar;PrhTBpIZ{9{QA6Kb+}$vd3w5L z9vD><<@-Z(ZbWy^vh$Kf)I8{>{`WAGv{=_g6d+~;_AsWs@((Vo<)Qg^WM6d)+qTcl z;47yUlcCf3ipX|rL7Jyy^QSNU+iPFGz5ZQA7dzlW`Oo~7Whh^|PGl8ZXTD?|Y77J4 zkf*;M)UMo~Srd88qOx*yQYNx9YLP*O^9;H+MfIuD7XSPyxSxRg5BEUhRmR4SxMtxk z$JwtFyL@L`;ZqMTOUFn+u#p_wW{n`&`J zt=bQz&vN7s^nQ4H+(1uzr`tPFE#cRd9eViR*YSU6UOgy6 za|%D=dY@l$_3>|og<+|%-+~X`)_TNPk_`iL%qwi{sz$bKANbMtE8YGrr1YRZ_4}m# zG3V4|)@$>{Z}4S{nD;c(wUFE5JS*)lM{H8KM4h=jem@<{nb*WNS=b=oG}?8rKe<+cytRB` z?wHJ>wR&%Nk!dN(2vm$j=VQ@W`|5Cy%kj!~ zMb-)rz7{?e+{t1jchNSg;tC6}{GCCYneeJT|k$iFht8#qvMJ>1F@1 zI3>Q74D#+kAyL>}+U>gU{Pos9{5UvU;m6_IVHp?egT4Jv7g>r*-t>2R2}9SC?{E*b zc3eXAf7j#!t;=3_L}scnj6Q$~U+&q#C=WT#U)aQLD-;ge+!eKMV}2Fk z+q-L_IR$NbwukjeZ1LIpX}~BiQof-+Z2cp%gSd40@}0s_bFJ1(OW!rZw`Th_uzyOv zQxrPftz$+;R&nLI%44Dol2I<=Eqv+jP^=%6cWveSHVT?^Nm-(vcp!7VTgpy zNZQA3M-CDCbQsZfy9HZcaz$KTYhYt&fk|lh6ZF>TjYpg#oz{A5On|A-Yq+R)8 ztVwQ?WS|}at&(5Kw|`S*$V;5urI@j5^{I?s!JFLUQKh8GNH^CYZYUF9b58`v3m%n- zd?xNT6cQL?h60A6nKs&A{njH7c~|XH)Mu^3JbJ?IL=Od@e>BPsW80fauFE>Uk&~Ti z-XS`emMbnRp&{tNvnoG+MfroS;qC3WR(7wsq$`98 z6dTAZ`dos>@-{02d=_HT_2+_5>B?Ug)SYy1m2&Kht+{!M&X_A$rJ7d1JXHiI7DULW zI9xO>SxEoQpZdJ|^@oRfB8`z6mE8rt1HBsYb$03&a@pcV!_4^l5f`X-$IiWmuap6` z0?+p~%PhZL01FZ{wr$zRV-$)T9v+I}9XbZhRZb(`2^q|%?#fd>rVLWB4h zN;kSo@6QMD-||}rO8o7!Fv>h_W6v+8JZ+#yZ7AMSWXt++doGHSg?4#5P9LNmxCh{0 zEd8RtVp0>4#V|D&bmM9Ll%IIULG4ekdry568UZSbZ2tw-340k%JO$F9X8{^E+&>sh z*D+4F`^}a9>Yg{PobnczQ*QlFTswF&^0013x0W53{Y=YGeE#7|jK0>C#QejPwO@yk zRbH@Kk)1J%!1Th5sGSvu<`Dt!BNtVL!+rH+r@E{MSeI%Dis>;&Y#z~eM792X6R7B zY~gV$a-LTGfsVM_3CqwQR|nCY{~p@9{_(7Rcw1y<05KWo^1-7>zP6GMmT8$@&e^ZRy_}^CQ=YH7SAXRNjjlL&2i(~-htf

    x+2M0@RWc-?g;~ip}oTS0YD% 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

    ci?58LQUpi+I@Y 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 literal 0 HcmV?d00001 diff --git a/docs/en/quickstart/images/state_finished.png b/docs/en/quickstart/images/state_finished.png new file mode 100644 index 0000000000000000000000000000000000000000..75a6d8cc0935fbd9b16d9c61e07073482cc82ff5 GIT binary patch literal 170727 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

  • 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 z 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≫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 literal 0 HcmV?d00001 diff --git a/docs/en/quickstart/openmldb_quickstart.md b/docs/en/quickstart/openmldb_quickstart.md index 57c3c0d2e75..a670b5c8945 100644 --- a/docs/en/quickstart/openmldb_quickstart.md +++ b/docs/en/quickstart/openmldb_quickstart.md @@ -1,20 +1,19 @@ # OpenMLDB Quickstart -## Basic concepts +## Basic Concepts The main use case of OpenMLDB is as a real-time feature platform for machine learning. The basic usage process is shown in the following diagram: +![modes-flow](concepts/images/modes-flow.png) -![modes-flow](https://openmldb.ai/docs/zh/main/_images/modes-flow.png) +As shown, OpenMLDB covers the feature computing process in machine learning, from offline development to real-time serving online, providing a complete process. Please refer to the documentation for [The Usage Process and Execution Mode](./concepts/modes.html) in detail. This article will demonstrate a quickstart step by step, showing the process for basic usage. -As can be seen, OpenMLDB covers the feature computing process of machine learning, from offline development to real-time request service online, providing a complete process. Please refer to the documentation for [the usage process and execution mode](https://openmldb.ai/docs/zh/main/quickstart/concepts/modes.html) in detail. This article will demonstrate a quick start and understanding of OpenMLDB step by step, following the basic usage process. +## Preparation -## The preparation - -This article is developed and deployed based on OpenMLDB CLI, and it is necessary to download the sample data and start OpenMLDB CLI first. It is recommended to use Docker image for a quick experience (Note: due to some known issues of Docker on macOS, the sample program in this article may encounter problems in completing the operation smoothly on macOS. It is recommended to run it on **Linux or Windows**). +This sample program is developed and deployed based on OpenMLDB CLI, so you need to download the sample data and start OpenMLDB CLI first. It is recommended to use Docker image for a quick experience (Note: due to some known issues of Docker on macOS, the sample program in this article may encounter problems on macOS. It is recommended to run it on **Linux or Windows**). - Docker Version: >= 18.03 -### Pulls the image +### Pull the Image Execute the following command in the command line to pull the OpenMLDB image and start the Docker container: @@ -23,18 +22,10 @@ docker run -it 4pdosc/openmldb:0.8.3 bash ``` ``` {note} -After successfully starting the container, all subsequent commands in this tutorial are executed inside the container by default. If you need to access the OpenMLDB server inside the container from outside the container, please refer to the [CLI/SDK-container onebox documentation](https://openmldb.ai/docs/zh/main/reference/ip_tips.html#id3). -``` - -### Download sample data - -Execute the following command inside the container to download the sample data used in the subsequent process (**this step can be skipped for versions 0.7.0 and later**, as the data is already stored in the image): - -```bash -curl https://openmldb.ai/demo/data.parquet --output /work/taxi-trip/data/data.parquet +After successfully starting the container, all subsequent commands in this tutorial are executed inside the container by default. If you need to access the OpenMLDB server inside the container from outside the container, please refer to the [CLI/SDK-container onebox documentation](../reference/ip_tips.md#clisdk-containeronebox). ``` -### Start the server and client +### Start the Server and Client Start the OpenMLDB server: @@ -48,19 +39,19 @@ Start the OpenMLDB CLI client: /work/openmldb/bin/openmldb --zk_cluster=127.0.0.1:2181 --zk_root_path=/openmldb --role=sql_client ``` -After successfully starting OpenMLDB CLI, it will be displayed as shown in the following figure: +Successful started OpenMLDB CLI will look as shown in the following figure: -![image](https://openmldb.ai/docs/zh/main/_images/cli_cluster.png) +![image](./images/cli_cluster.png) -## Use process +## OpenMLDB Process -Referring to the core concepts, the process of using OpenMLDB generally includes six steps: creating databases and tables, importing offline data, offline feature computing, deploying SQL solutions, importing online data, and online real-time feature computing. +Referring to the core concepts, the process of using OpenMLDB generally includes six steps: create database and table, import offline data, compute offline feature, deploy SQL plan, import online data, and online real-time feature compute. ```{note} Unless otherwise specified, the commands demonstrated below are executed by default in OpenMLDB CLI. ``` -### Step 1: Create database and table +### Step 1: Create Database and Table Create `demo_db` and table `demo_table1`: @@ -71,7 +62,7 @@ USE demo_db; CREATE TABLE demo_table1(c1 string, c2 int, c3 bigint, c4 float, c5 double, c6 timestamp, c7 date); ``` -### Step 2: Importing offline data +### Step 2: Import Offline Data Switch to the offline execution mode, and import the sample data as offline data for offline feature calculation. @@ -90,17 +81,21 @@ Note that the `LOAD DATA` command is an asynchronous command by default. You can - To show the task logs: SHOW JOBLOG job_id -Here, we use `SHOW JOBS` to check the task status. Please wait for the task to be successfully completed (the `state` is changed to `FINISHED`), and then proceed to the next step. +Here, we use `SHOW JOBS` to check the task status. Please wait for the task to be successfully completed ( `state` changes to `FINISHED`), and then proceed to the next step. + +![image-20220111141358808](./images/state_finished.png) + +After the task is completed, if you wish to preview the data, you can execute the `SELECT * FROM demo_table1` statement in synchronous mode by setting `SET @@sync_job=true`. However, this approach has certain limitations, which are detailed in the [Offline Command Synchronous Mode](./function_boundary.md#offline-command-synchronous-mode) section. -![image-20220111141358808](https://openmldb.ai/docs/zh/main/_images/state_finished.png) +In the default asynchronous mode, executing `SELECT * FROM demo_table1` will initiate an asynchronous task, and the results will be stored in the log files of the Spark job, making them less convenient to access. If TaskManager is in local mode, you can use `SHOW JOBLOG ` to view the query print results in the stdout section. -After the task is completed, if you want to preview the data, you can use the `SELECT * FROM demo_table1` statement. It is recommended to first set the offline command to synchronous mode (`SET @@sync_job=true`); otherwise, the command will submit an asynchronous task, and the result will be saved in the log file of the Spark task, which is less convenient to view. +The most reliable way to access the data is to use the `SELECT INTO` command to export the data to a specified directory or directly examine the storage location after importing it. ```{note} -OpenMLDB also supports importing offline data through linked soft copies, without the need for hard data copying. Please refer to the parameter `deep_copy` in the [LOAD DATA INFILE documentation](https://openmldb.ai/docs/zh/main/openmldb_sql/dml/LOAD_DATA_STATEMENT.html) for more information. +OpenMLDB also supports importing offline data through linked soft copies, without the need for hard data copying. Please refer to the parameter `deep_copy` in the [LOAD DATA INFILE Documentation](../openmldb_sql/dml/LOAD_DATA_STATEMENT.md) for more information. ``` -### Step 3: Offline feature computing +### Step 3: Compute Offline Feature Assuming that we have determined the SQL script (`SELECT` statement) to be used for feature computation, we can use the following command for offline feature computation: @@ -120,7 +115,7 @@ Note: - The `SELECT` statement is used to perform SQL-based feature extraction and store the generated features in the directory specified by the `OUTFILE` parameter as `feature_data`, which can be used for subsequent machine learning model training. -### Step 4: Deploying SQL solutions +### Step 4: Deploy SQL plan Switch to online preview mode, and deploy the explored SQL plan to online. The SQL plan is named `demo_data_service`, and the online SQL used for feature extraction needs to be consistent with the corresponding offline feature calculation SQL. @@ -131,11 +126,11 @@ USE demo_db; DEPLOY demo_data_service SELECT c1, c2, sum(c3) OVER w1 AS w1_c3_sum FROM demo_table1 WINDOW w1 AS (PARTITION BY demo_table1.c1 ORDER BY demo_table1.c6 ROWS BETWEEN 2 PRECEDING AND CURRENT ROW); ``` -After the deployment, you can use the command `SHOW DEPLOYMENTS` to view the deployed SQL solutions. +After the deployment, you can use the command `SHOW DEPLOYMENTS` to view the deployed SQL. -### Step 5: Importing online data +### Step 5: Import Online Data -Import the downloaded sample data as online data for online feature computation in online preview mode. +Import the downloaded sample data as online data for online feature computation in online mode. ```sql -- OpenMLDB CLI @@ -161,9 +156,9 @@ Note that currently, it is required to successfully deploy the SQL plan before i The tutorial skips the step of real-time data access after importing data. In practical scenarios, as time progresses, the latest real-time data needs to be updated in the online database. This can be achieved through the OpenMLDB SDK or online data source connectors such as Kafka, Pulsar, etc. ``` -### Step 6: Online real-time feature computing +### Step 6: Online Real-Time Feature Computing -The development and deployment work based on OpenMLDB CLI is completed. Next, you can make real-time feature calculation requests in real-time request mode. First, exit OpenMLDB CLI and return to the command line of the operating system. +The development and deployment work is completed. Next, you can make real-time feature calculation requests in real-time request mode. First, exit OpenMLDB CLI and return to the command line of the operating system. ```sql -- OpenMLDB CLI @@ -176,10 +171,10 @@ According to the default deployment configuration, the http port for APIServer i http://127.0.0.1:9080/dbs/demo_db/deployments/demo_data_service \___________/ \____/ \_____________/ | | | - APIServer地址 Database名字 Deployment名字 + APIServerAddress Database Name Deployment Name ``` -Real-time requests accept input data in JSON format. Here are two examples: putting a row of data in the `input` field of the request. +Real-time requests accept input data in JSON format. Here are two examples: putting data in the `input` field of the request. **Example 1:** @@ -187,7 +182,7 @@ Real-time requests accept input data in JSON format. Here are two examples: putt curl http://127.0.0.1:9080/dbs/demo_db/deployments/demo_data_service -X POST -d'{"input": [["aaa", 11, 22, 1.2, 1.3, 1635247427000, "2021-05-20"]]}' ``` -Query the expected return result (the calculated features are stored in the `data` field): +Expected query result (the calculated features are stored in the `data` field): ```json {"code":0,"msg":"ok","data":{"data":[["aaa",11,22]]}} @@ -205,7 +200,7 @@ Expected query result: {"code":0,"msg":"ok","data":{"data":[["aaa",11,66]]}} ``` -### Description of real-time feature computing results +### Explanation of Real-Time Feature Computing Results The SQL execution for online real-time requests is different from batch processing mode. The request mode only performs SQL calculations on the data of the request row. In the previous example, it is the input of the POST request that serves as the request row. The specific process is as follows: Assuming that this row of data exists in the table `demo_table1`, and the following feature calculation SQL is executed on it: @@ -213,7 +208,7 @@ The SQL execution for online real-time requests is different from batch processi SELECT c1, c2, sum(c3) OVER w1 AS w1_c3_sum FROM demo_table1 WINDOW w1 AS (PARTITION BY demo_table1.c1 ORDER BY demo_table1.c6 ROWS BETWEEN 2 PRECEDING AND CURRENT ROW); ``` -**The calculation logic for Example 1 is as follows:** +**The Calculation Logic for Example 1 is as Follows:** 1. Filter rows in column c1 with the value "aaa" based on the `PARTITION BY` partition of the request row and window, and sort them in ascending order by column c6. Therefore, in theory, the intermediate data table sorted by partition should be as follows. The request row is the first row after sorting. @@ -227,7 +222,7 @@ aaa 12 22 2.200000 12.300000 1636097890000 1970-01-01 ----- ---- ---- ---------- ----------- --------------- ------------ ``` -2. The window range is `2 PRECEDING AND CURRENT ROW`, so in the above table, the actual window is extracted, and the request row is the smallest row with no preceding two rows, but the window includes the current row, so the window only contains the request row. +2. The window range is `2 PRECEDING AND CURRENT ROW`. In the above table, when the actual window is extracted, the request row is the smallest row with no preceding 2 rows. Therefore the window only contains the request row. 3. For window aggregation, the sum of column c3 for the data within the window (only one row) is calculated, resulting in 22. Therefore, the output result is: ```sql @@ -238,7 +233,7 @@ aaa 11 22 ----- ---- ----------- ``` -**The calculation logic for Example 2 is as follows:** +**The Calculation Logic for Example 2 is as Follows:** 1. According to the partition of the request line and window by `PARTITION BY`, select the rows where column c1 is "aaa" and sort them in ascending order by column c6. Therefore, theoretically, the intermediate data table after partition and sorting should be as shown in the table below. The request row is the last row after sorting. @@ -252,7 +247,7 @@ aaa 11 22 1.2 1.3 1637000000000 2021-11-16 ----- ---- ---- ---------- ----------- --------------- ------------ ``` -2. The window range is `2 PRECEDING AND CURRENT ROW`, so the actual window is extracted from the above table, and the two preceding rows of the request row exist, and the current row is also included. Therefore, there are three rows of data in the window. +2. The window range is `2 PRECEDING AND CURRENT ROW`. When the actual window is extracted from the above table, the two preceding 2 rows of the request row exist, together with the current row. Therefore, there are three rows of data in the window. 3. For window aggregation, the sum of column c3 for the data within the window (three rows) is calculated, resulting in 22 + 22 + 22 = 66. Therefore, the output result is: ```sql From ff58ddcdacf638c8ba472e152acd4bfceddd9333 Mon Sep 17 00:00:00 2001 From: aceforeverd Date: Mon, 16 Oct 2023 19:44:24 +0800 Subject: [PATCH 58/63] build(brpc): upgrade to apache/brpc 1.6.0 (#3415) * build(brpc): upgrade to apache/brpc 1.6.0 * build: apply brpc patch from 4pd --- third-party/cmake/FetchBrpc.cmake | 15 ++- third-party/patches/brpc-1.6.0-2235.patch | 91 ++++++++++++++++ third-party/patches/brpc-1.6.0.patch | 120 ++++++++++++++++++++++ 3 files changed, 222 insertions(+), 4 deletions(-) create mode 100644 third-party/patches/brpc-1.6.0-2235.patch create mode 100644 third-party/patches/brpc-1.6.0.patch diff --git a/third-party/cmake/FetchBrpc.cmake b/third-party/cmake/FetchBrpc.cmake index f4318ac21b3..af7e3b910d1 100644 --- a/third-party/cmake/FetchBrpc.cmake +++ b/third-party/cmake/FetchBrpc.cmake @@ -12,16 +12,23 @@ # See the License for the specific language governing permissions and # limitations under the License. -set(BRPC_URL https://github.com/4paradigm/incubator-brpc/archive/a85d1bde8df3a3e2e59a64ea5a3ee3122f9c6daa.zip) -message(STATUS "build brpc from ${BRPC_URL}") +set(BRPC_URL https://github.com/apache/brpc) +set(BRPC_TAG d2b73ec955dd04b06ab55065d9f3b4de1e608bbd) +message(STATUS "build brpc from ${BRPC_URL}@${BRPC_TAG}") + +find_package(Git REQUIRED) ExternalProject_Add( brpc - URL ${BRPC_URL} - URL_HASH SHA256=ea86d39313bed981357d2669daf1a858fcf1ec363465eda2eec60a8504a2c38e + GIT_REPOSITORY ${BRPC_URL} + GIT_TAG ${BRPC_TAG} PREFIX ${DEPS_BUILD_DIR} DOWNLOAD_DIR ${DEPS_DOWNLOAD_DIR}/brpc INSTALL_DIR ${DEPS_INSTALL_DIR} + PATCH_COMMAND ${GIT_EXECUTABLE} checkout . + COMMAND ${GIT_EXECUTABLE} clean -dfx . + COMMAND ${GIT_EXECUTABLE} apply ${PROJECT_SOURCE_DIR}/patches/brpc-1.6.0.patch + COMMAND ${GIT_EXECUTABLE} apply ${PROJECT_SOURCE_DIR}/patches/brpc-1.6.0-2235.patch DEPENDS gflags glog protobuf snappy leveldb gperf openssl CONFIGURE_COMMAND ${CMAKE_COMMAND} -H -B . -DWITH_GLOG=ON -DCMAKE_PREFIX_PATH=${DEPS_INSTALL_DIR} -DCMAKE_INSTALL_PREFIX=${DEPS_INSTALL_DIR} ${CMAKE_OPTS} BUILD_COMMAND ${CMAKE_COMMAND} --build . --target brpc-static -- ${MAKEOPTS} diff --git a/third-party/patches/brpc-1.6.0-2235.patch b/third-party/patches/brpc-1.6.0-2235.patch new file mode 100644 index 00000000000..450b4c6f427 --- /dev/null +++ b/third-party/patches/brpc-1.6.0-2235.patch @@ -0,0 +1,91 @@ +diff --git a/src/brpc/builtin/prometheus_metrics_service.cpp b/src/brpc/builtin/prometheus_metrics_service.cpp +index 7bf8bbf359..88f675bb81 100644 +--- a/src/brpc/builtin/prometheus_metrics_service.cpp ++++ b/src/brpc/builtin/prometheus_metrics_service.cpp +@@ -82,6 +82,12 @@ class PrometheusMetricsDumper : public bvar::Dumper { + std::map _m; + }; + ++butil::StringPiece GetMetricsName(const std::string& name) { ++ auto pos = name.find_first_of('{'); ++ int size = (pos == std::string::npos) ? name.size() : pos; ++ return butil::StringPiece(name.data(), size); ++} ++ + bool PrometheusMetricsDumper::dump(const std::string& name, + const butil::StringPiece& desc) { + if (!desc.empty() && desc[0] == '"') { +@@ -93,8 +99,11 @@ bool PrometheusMetricsDumper::dump(const std::string& name, + // Leave it to DumpLatencyRecorderSuffix to output Summary. + return true; + } +- *_os << "# HELP " << name << '\n' +- << "# TYPE " << name << " gauge" << '\n' ++ ++ auto metrics_name = GetMetricsName(name); ++ ++ *_os << "# HELP " << metrics_name << '\n' ++ << "# TYPE " << metrics_name << " gauge" << '\n' + << name << " " << desc << '\n'; + return true; + } +diff --git a/src/brpc/builtin/prometheus_metrics_service.h b/src/brpc/builtin/prometheus_metrics_service.h +index c844e1e7a0..541b395c82 100644 +--- a/src/brpc/builtin/prometheus_metrics_service.h ++++ b/src/brpc/builtin/prometheus_metrics_service.h +@@ -31,6 +31,7 @@ class PrometheusMetricsService : public brpc_metrics { + ::google::protobuf::Closure* done) override; + }; + ++butil::StringPiece GetMetricsName(const std::string& name); + int DumpPrometheusMetricsToIOBuf(butil::IOBuf* output); + + } // namepace brpc +diff --git a/test/brpc_prometheus_metrics_service_unittest.cpp b/test/brpc_prometheus_metrics_service_unittest.cpp +new file mode 100644 +index 0000000000..b5b0bc10d5 +--- /dev/null ++++ b/test/brpc_prometheus_metrics_service_unittest.cpp +@@ -0,0 +1,42 @@ ++// Licensed to the Apache Software Foundation (ASF) under one ++// or more contributor license agreements. See the NOTICE file ++// distributed with this work for additional information ++// regarding copyright ownership. The ASF licenses this file ++// to you under the Apache License, Version 2.0 (the ++// "License"); you may not use this file except in compliance ++// with the License. You may obtain a copy of the License at ++// ++// http://www.apache.org/licenses/LICENSE-2.0 ++// ++// Unless required by applicable law or agreed to in writing, ++// software distributed under the License is distributed on an ++// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY ++// KIND, either express or implied. See the License for the ++// specific language governing permissions and limitations ++// under the License. ++ ++// Date: 2023/05/06 15:10:00 ++ ++#include ++ ++#include "butil/strings/string_piece.h" ++#include "butil/iobuf.h" ++#include "brpc/builtin/prometheus_metrics_service.h" ++ ++namespace { ++ ++class PrometheusMetricsDumperTest : public testing::Test { ++protected: ++ void SetUp() {} ++ void TearDown() {} ++}; ++ ++TEST_F(PrometheusMetricsDumperTest, GetMetricsName) { ++ EXPECT_EQ("", brpc::GetMetricsName("")); ++ ++ EXPECT_EQ("commit_count", brpc::GetMetricsName("commit_count")); ++ ++ EXPECT_EQ("commit_count", brpc::GetMetricsName("commit_count{region=\"1000\"}")); ++} ++ ++} diff --git a/third-party/patches/brpc-1.6.0.patch b/third-party/patches/brpc-1.6.0.patch new file mode 100644 index 00000000000..00dda45df47 --- /dev/null +++ b/third-party/patches/brpc-1.6.0.patch @@ -0,0 +1,120 @@ +diff --git a/src/brpc/builtin/status_service.cpp b/src/brpc/builtin/status_service.cpp +index a6f5a4dae8..67880ec1a3 100644 +--- a/src/brpc/builtin/status_service.cpp ++++ b/src/brpc/builtin/status_service.cpp +@@ -37,6 +37,8 @@ extern MethodStatus* g_client_msg_status; + extern MethodStatus* g_server_msg_status; + } + ++DECLARE_bool(enable_vars_service); ++ + // Defined in vars_service.cpp + void PutVarsHeading(std::ostream& os, bool expand_all); + +@@ -47,7 +49,7 @@ void StatusService::default_method(::google::protobuf::RpcController* cntl_base, + ClosureGuard done_guard(done); + Controller *cntl = static_cast(cntl_base); + const Server* server = cntl->server(); +- const bool use_html = UseHTML(cntl->http_request()); ++ const bool use_html = FLAGS_enable_vars_service ? UseHTML(cntl->http_request()) : false; + + // NOTE: the plain output also fits format of public/configure so that user + // can load values more easily. +diff --git a/src/brpc/server.cpp b/src/brpc/server.cpp +index ce5a0dd2a3..4e1fbbe424 100644 +--- a/src/brpc/server.cpp ++++ b/src/brpc/server.cpp +@@ -109,6 +109,13 @@ butil::static_atomic g_running_server_count = BUTIL_STATIC_ATOMIC_INIT(0); + // Following services may have security issues and are disabled by default. + DEFINE_bool(enable_dir_service, false, "Enable /dir"); + DEFINE_bool(enable_threads_service, false, "Enable /threads"); ++DEFINE_bool(enable_status_service, false, "Enable /status"); ++DEFINE_bool(enable_vars_service, false, "Enable /vars"); ++DEFINE_bool(enable_connections_service, false, "Enable /connections"); ++DEFINE_bool(enable_flags_service, false, "Enable /flags"); ++DEFINE_bool(enable_rpcz_service, false, "Enable /rpcz"); ++DEFINE_bool(enable_hotspots_service, false, "Enable /hotspots/cpu /hotspots/heap /hotspots/growth /hotspots/contention"); ++DEFINE_bool(enable_index_service, false, "Enable /index?as_more"); + + DECLARE_int32(usercode_backup_threads); + DECLARE_bool(usercode_in_pthread); +@@ -465,31 +472,31 @@ Server::~Server() { + + int Server::AddBuiltinServices() { + // Firstly add services shown in tabs. +- if (AddBuiltinService(new (std::nothrow) StatusService)) { ++ if (FLAGS_enable_status_service && AddBuiltinService(new (std::nothrow) StatusService)) { + LOG(ERROR) << "Fail to add StatusService"; + return -1; + } +- if (AddBuiltinService(new (std::nothrow) VarsService)) { ++ if (FLAGS_enable_vars_service && AddBuiltinService(new (std::nothrow) VarsService)) { + LOG(ERROR) << "Fail to add VarsService"; + return -1; + } +- if (AddBuiltinService(new (std::nothrow) ConnectionsService)) { ++ if (FLAGS_enable_connections_service && AddBuiltinService(new (std::nothrow) ConnectionsService)) { + LOG(ERROR) << "Fail to add ConnectionsService"; + return -1; + } +- if (AddBuiltinService(new (std::nothrow) FlagsService)) { ++ if (FLAGS_enable_flags_service && AddBuiltinService(new (std::nothrow) FlagsService)) { + LOG(ERROR) << "Fail to add FlagsService"; + return -1; + } +- if (AddBuiltinService(new (std::nothrow) RpczService)) { ++ if (FLAGS_enable_rpcz_service && AddBuiltinService(new (std::nothrow) RpczService)) { + LOG(ERROR) << "Fail to add RpczService"; + return -1; + } +- if (AddBuiltinService(new (std::nothrow) HotspotsService)) { ++ if (FLAGS_enable_hotspots_service && AddBuiltinService(new (std::nothrow) HotspotsService)) { + LOG(ERROR) << "Fail to add HotspotsService"; + return -1; + } +- if (AddBuiltinService(new (std::nothrow) IndexService)) { ++ if (FLAGS_enable_index_service && AddBuiltinService(new (std::nothrow) IndexService)) { + LOG(ERROR) << "Fail to add IndexService"; + return -1; + } +diff --git a/src/butil/errno.cpp b/src/butil/errno.cpp +index 9b964e114f..2e1c4d379d 100644 +--- a/src/butil/errno.cpp ++++ b/src/butil/errno.cpp +@@ -55,14 +55,19 @@ int DescribeCustomizedErrno( + #if defined(OS_MACOSX) + const int rc = strerror_r(error_code, tls_error_buf, ERROR_BUFSIZE); + if (rc != EINVAL) +-#else +- desc = strerror_r(error_code, tls_error_buf, ERROR_BUFSIZE); +- if (desc && strncmp(desc, "Unknown error", 13) != 0) +-#endif + { + fprintf(stderr, "WARNING: Fail to define %s(%d) which is already defined as `%s'", + error_name, error_code, desc); + } ++#else ++ desc = strerror_r(error_code, tls_error_buf, ERROR_BUFSIZE); ++ if (desc != tls_error_buf) ++ { ++ fprintf(stderr, ++ "%d is defined as `%s', probably is the system errno.\n", ++ error_code, desc); ++ } ++#endif + } + errno_desc[error_code - ERRNO_BEGIN] = description; + return 0; // must +diff --git a/src/bvar/default_variables.cpp b/src/bvar/default_variables.cpp +index be02c50a9a..f71f9dd7cf 100644 +--- a/src/bvar/default_variables.cpp ++++ b/src/bvar/default_variables.cpp +@@ -111,7 +111,7 @@ static bool read_proc_status(ProcStat &stat) { + } + const std::string& result = oss.str(); + if (sscanf(result.c_str(), "%d %d %d %d" +- "%d %u %ld %ld", ++ "%d %x %ld %ld", + &stat.pid, &stat.ppid, &stat.pgrp, &stat.session, + &stat.tpgid, &stat.flags, &stat.priority, &stat.nice) != 8) { + PLOG(WARNING) << "Fail to sscanf"; From 024a0d0f5cf4763f5a6a32c8bcffe231547a6f58 Mon Sep 17 00:00:00 2001 From: aceforeverd Date: Mon, 16 Oct 2023 20:29:28 +0800 Subject: [PATCH 59/63] feat(sql): lazy last join (#3533) * feat(online): support lazy last join For SQL `A last join (B last join C)`, it'll optimize and run `B last join C` lazily. Both batch mode and request(batch-request) mode are considered. * fix: tests for `A join (B join C)` and also: - rm redundant code path in group_and_sort_optimized.cc * fix: comment * fix: lazy last join correct expr resolving in group_and_sort_optimized * fix(online): last join (where) with cluster optimized * fix(optimizer): skip cluster optimize for specific condition Skip for the case `JOIN(TABLE, ...)`. Cluster optimize only happen with `JOIN(ROW, TABLE)`. * fix(optimizer): distinct column name from different table Do not optimize target table if resolved column is from different table, even column name does be the same. * test: more cases for lazy last join * chore: rm mut_keys * fix(optimizer): KeysOptimizedImpl --- .gitignore | 7 + cases/query/last_join_query.yaml | 810 +++++++++++++++++- cases/query/last_join_where.yaml | 15 +- cases/query/last_join_window_query.yaml | 10 +- .../toydb/src/testing/toydb_engine_test.cc | 7 + hybridse/include/node/node_manager.h | 2 +- hybridse/include/node/sql_node.h | 20 +- .../include/passes/expression/expr_pass.h | 12 +- hybridse/include/vm/engine.h | 3 - hybridse/include/vm/physical_op.h | 28 +- hybridse/include/vm/schemas_context.h | 7 +- hybridse/src/node/node_manager.cc | 2 +- .../src/passes/physical/cluster_optimized.cc | 15 +- .../physical/group_and_sort_optimized.cc | 276 +++--- .../physical/group_and_sort_optimized.h | 29 +- hybridse/src/vm/engine.cc | 21 +- hybridse/src/vm/generator.cc | 4 + hybridse/src/vm/generator.h | 3 + hybridse/src/vm/internal/node_helper.cc | 42 + hybridse/src/vm/internal/node_helper.h | 5 + hybridse/src/vm/runner.cc | 93 +- hybridse/src/vm/runner.h | 22 +- hybridse/src/vm/schemas_context.cc | 24 +- hybridse/src/vm/transform.cc | 141 +-- hybridse/src/vm/transform.h | 13 +- 25 files changed, 1272 insertions(+), 339 deletions(-) create mode 100644 hybridse/src/vm/internal/node_helper.cc diff --git a/.gitignore b/.gitignore index e7e91890044..14fb8ee1485 100644 --- a/.gitignore +++ b/.gitignore @@ -108,3 +108,10 @@ allure-results/ /python/openmldb_autofe/*.egg-info/ # go sdk !go.mod + +# tag files +**/tags +**/GPATH +**/GRTAGS +**/GTAGS +**/cscope.out diff --git a/cases/query/last_join_query.yaml b/cases/query/last_join_query.yaml index e37d87a4044..2715bcf7341 100644 --- a/cases/query/last_join_query.yaml +++ b/cases/query/last_join_query.yaml @@ -12,10 +12,27 @@ # See the License for the specific language governing permissions and # limitations under the License. + #################################################################################################### + # LAST JOINs + # support modes: + # - online request (right source optimized) + # - batch request (right source optimized) + # - online preview (standalone) + # - offline mode + # unsupport: + # - online preview (in cluster) + # - online request (right source not optimized) + # - batch request (right source not optimized) + # + # Right source is optimized case: + # 1. Right source is ANYOP(T2): T2 optimized with a concret index + # 2. Right source is ANYOP(JOIN(T2, T3)): both T2 and T3 optimized with concret indexs + #################################################################################################### + cases: - id: 0 desc: LAST JOIN 右表未命中索引 - mode: rtidb-unsupport + mode: request-unsupport sql: | SELECT t1.col1 as id, t1.col0 as t1_col0, t1.col1 + t2.col1 + 1 as test_col1, t1.col2 as t1_col2, str1 FROM t1 last join t2 order by t2.col5 on t1.col1=t2.col1 and t1.col5 = t2.col5; @@ -178,6 +195,19 @@ cases: Z, 3, 3 U, 4, 4 V, 5, 5 + cluster_request_plan: | + SIMPLE_PROJECT(sources=(t1.col1 -> id, t1.col0, t2.c0, t3.column0)) + REQUEST_JOIN(type=kJoinTypeConcat) + REQUEST_JOIN(type=kJoinTypeConcat) + DATA_PROVIDER(request=t1) + REQUEST_JOIN(OUTPUT_RIGHT_ONLY, type=LastJoin, right_sort=(ASC), condition=, left_keys=(), right_keys=(), index_keys=(#5)) + SIMPLE_PROJECT(sources=(#5 -> t1.col1)) + DATA_PROVIDER(request=t1) + DATA_PROVIDER(type=Partition, table=t2, index=index1) + REQUEST_JOIN(OUTPUT_RIGHT_ONLY, type=LastJoin, right_sort=(ASC), condition=, left_keys=(), right_keys=(), index_keys=(#5)) + SIMPLE_PROJECT(sources=(#5 -> t1.col1)) + DATA_PROVIDER(request=t1) + DATA_PROVIDER(type=Partition, table=t3, index=index1) expect: schema: id:int32, col0:string, c0:string, column0:string order: id @@ -335,8 +365,6 @@ cases: 5, 2, 1590115423900 - id: 10 - desc: 右表没有匹配[FEX-903] - mode: offline-unsupport inputs: - name: t1 columns: ["c1 string","c2 int","c3 bigint","c4 timestamp"] @@ -361,3 +389,779 @@ cases: - ["aa",2,13,1590738989000] - ["bb",21,131,1590738990000] - ["cc",41,null,null] + + #################################################################################################### + # LAZY LAST JOINs + #################################################################################################### + - id: 11 + # t1------>(t2------->t3) + # │ └-(t3.c1)-┘ + # └-(t2.c1)-┘ + # Easiest path, t1 finally joins t2's column + inputs: + - name: t1 + columns: ["c1 string","c2 int","c4 timestamp"] + indexs: ["index1:c1:c4"] + rows: + - ["aa",2,1590738989000] + - ["bb",3,1590738990000] + - ["cc",4,1590738991000] + - name: t2 + columns: ["c1 string","c4 timestamp"] + indexs: ["index1:c1:c4"] + rows: + - ["aa",1590738989000] + - ["bb",1590738990000] + - ["dd",1590738991000] + - name: t3 + columns: ["c1 string","c2 int","c3 bigint","c4 timestamp"] + indexs: ["index1:c1:c4"] + rows: + - ["aa",2,13,1590738989000] + - ["cc",21,131,1590738990000] + - ["dd",41,151,1590738991000] + sql: | + select t1.c1, t1.c2, tx.c1 as c1l, c1r, c2r + from t1 last join ( + select t2.*, t3.c1 as c1r, t3.c2 as c2r + from t2 last join t3 on t2.c1 = t3.c1 + ) tx + on t1.c1 = tx.c1 + batch_plan: | + SIMPLE_PROJECT(sources=(t1.c1, t1.c2, tx.c1 -> c1l, c1r, c2r)) + JOIN(type=LastJoin, condition=, left_keys=(), right_keys=(), index_keys=(t1.c1)) + DATA_PROVIDER(table=t1) + RENAME(name=tx) + SIMPLE_PROJECT(sources=(t2.c1, t2.c4, t3.c1 -> c1r, t3.c2 -> c2r)) + JOIN(type=LastJoin, condition=, left_keys=(), right_keys=(), index_keys=(t2.c1)) + DATA_PROVIDER(type=Partition, table=t2, index=index1) + DATA_PROVIDER(type=Partition, table=t3, index=index1) + request_plan: | + SIMPLE_PROJECT(sources=(t1.c1, t1.c2, tx.c1 -> c1l, c1r, c2r)) + REQUEST_JOIN(type=LastJoin, condition=, left_keys=(), right_keys=(), index_keys=(t1.c1)) + DATA_PROVIDER(request=t1) + RENAME(name=tx) + SIMPLE_PROJECT(sources=(t2.c1, t2.c4, t3.c1 -> c1r, t3.c2 -> c2r)) + REQUEST_JOIN(type=LastJoin, condition=, left_keys=(), right_keys=(), index_keys=(t2.c1)) + DATA_PROVIDER(type=Partition, table=t2, index=index1) + DATA_PROVIDER(type=Partition, table=t3, index=index1) + cluster_request_plan: | + SIMPLE_PROJECT(sources=(t1.c1, t1.c2, tx.c1 -> c1l, c1r, c2r)) + REQUEST_JOIN(type=kJoinTypeConcat) + DATA_PROVIDER(request=t1) + REQUEST_JOIN(OUTPUT_RIGHT_ONLY, type=LastJoin, condition=, left_keys=(), right_keys=(), index_keys=(#4)) + SIMPLE_PROJECT(sources=(#4 -> t1.c1)) + DATA_PROVIDER(request=t1) + RENAME(name=tx) + SIMPLE_PROJECT(sources=(t2.c1, t2.c4, t3.c1 -> c1r, t3.c2 -> c2r)) + REQUEST_JOIN(type=LastJoin, condition=, left_keys=(), right_keys=(), index_keys=(t2.c1)) + DATA_PROVIDER(type=Partition, table=t2, index=index1) + DATA_PROVIDER(type=Partition, table=t3, index=index1) + expect: + order: c1 + columns: + - c1 string + - c2 int + - c1l string + - c1r string + - c2r int + data: | + aa, 2, aa, aa, 2 + bb, 3, bb, NULL, NULL + cc, 4, NULL, NULL, NULL + - id: 12 + # t1------>(t2------->t3) + # │ └-(t3.c1)-┘ + # └--(t2.c1)----------┘ + desc: unsupport join on t3 in request and batch(clusterd) + mode: request-unsupport + inputs: + - name: t1 + columns: ["c1 string","c2 int","c4 timestamp"] + indexs: ["index1:c1:c4"] + rows: + - ["aa",2,1590738989000] + - ["bb",3,1590738990000] + - ["cc",4,1590738991000] + - name: t2 + columns: ["c1 string","c4 timestamp"] + indexs: ["index1:c1:c4"] + rows: + - ["aa",1590738989000] + - ["bb",1590738990000] + - ["dd",1590738991000] + - name: t3 + columns: ["c1x string","c2 int","c3 bigint","c4 timestamp"] + indexs: ["index1:c1x:c4"] + rows: + - ["aa",2,13,1590738989000] + - ["cc",21,131,1590738990000] + - ["dd",41,151,1590738991000] + sql: | + select t1.c1, t1.c2, tx.c1 as c1l, c1r, c2r + from t1 last join ( + select t2.*, t3.c1x as c1r, t3.c2 as c2r + from t2 last join t3 on t2.c1 = t3.c1x + ) tx + on t1.c1 = tx.c1r + batch_plan: | + SIMPLE_PROJECT(sources=(t1.c1, t1.c2, tx.c1 -> c1l, c1r, c2r)) + JOIN(type=LastJoin, condition=, left_keys=(t1.c1), right_keys=(tx.c1r), index_keys=) + DATA_PROVIDER(table=t1) + RENAME(name=tx) + SIMPLE_PROJECT(sources=(t2.c1, t2.c4, t3.c1x -> c1r, t3.c2 -> c2r)) + JOIN(type=LastJoin, condition=, left_keys=(), right_keys=(), index_keys=(t2.c1)) + DATA_PROVIDER(table=t2) + DATA_PROVIDER(type=Partition, table=t3, index=index1) + expect: + order: c1 + columns: + - c1 string + - c2 int + - c1l string + - c1r string + - c2r int + data: | + aa, 2, aa, aa, 2 + bb, 3, NULL, NULL, NULL + cc, 4, NULL, NULL, NULL + - id: 13 + # t1------>(t2------->t3) + # │ └-(t3.c1)-┘ + # └-(t2.c1)-----------┘ + desc: t2 un-optimized, t2 & t3 has the same schema + # the case checks if optimizer can distinct same column name from two tables + mode: request-unsupport + inputs: + - name: t1 + columns: ["c1 string","c2 int","c4 timestamp"] + indexs: ["index1:c1:c4"] + rows: + - ["aa",2,1590738989000] + - ["bb",3,1590738990000] + - ["cc",4,1590738991000] + - name: t2 + columns: ["c1 string","c4 timestamp"] + indexs: ["index1:c1:c4"] + rows: + - ["aa",1590738989000] + - ["bb",1590738990000] + - ["dd",1590738991000] + - name: t3 + columns: ["c1 string","c2 int","c3 bigint","c4 timestamp"] + indexs: ["index1:c1:c4"] + rows: + - ["aa",2,13,1590738989000] + - ["cc",21,131,1590738990000] + - ["dd",41,151,1590738991000] + sql: | + select t1.c1, t1.c2, tx.c1 as c1l, c1r, c2r + from t1 last join ( + select t2.*, t3.c1 as c1r, t3.c2 as c2r + from t2 last join t3 on t2.c1 = t3.c1 + ) tx + on t1.c1 = tx.c1r + batch_plan: | + SIMPLE_PROJECT(sources=(t1.c1, t1.c2, tx.c1 -> c1l, c1r, c2r)) + JOIN(type=LastJoin, condition=, left_keys=(t1.c1), right_keys=(tx.c1r), index_keys=) + DATA_PROVIDER(table=t1) + RENAME(name=tx) + SIMPLE_PROJECT(sources=(t2.c1, t2.c4, t3.c1 -> c1r, t3.c2 -> c2r)) + JOIN(type=LastJoin, condition=, left_keys=(), right_keys=(), index_keys=(t2.c1)) + DATA_PROVIDER(table=t2) + DATA_PROVIDER(type=Partition, table=t3, index=index1) + expect: + order: c1 + columns: + - c1 string + - c2 int + - c1l string + - c1r string + - c2r int + data: | + aa, 2, aa, aa, 2 + bb, 3, NULL, NULL, NULL + cc, 4, NULL, NULL, NULL + - id: 14 + # t1------>(t2------->t3) + # │ └-(t3.c1)-┘ + # └-(t2.c1)-┘ + desc: t2 un-optimized due to no equal expr + mode: request-unsupport + inputs: + - name: t1 + columns: ["c1 string","c2 int","c4 timestamp"] + indexs: ["index1:c1:c4"] + rows: + - ["aa",2,1590738989000] + - ["bb",3,1590738990000] + - ["cc",4,1590738991000] + - name: t2 + columns: ["c1 string","c4 timestamp"] + indexs: ["index1:c1:c4"] + rows: + - ["aa",1590738989000] + - ["bb",1590738990000] + - ["dd",1590738991000] + - name: t3 + columns: ["c1 string","c2 int","c3 bigint","c4 timestamp"] + indexs: ["index1:c1:c4"] + rows: + - ["aa",2,13,1590738989000] + - ["cc",21,131,1590738990000] + - ["dd",41,151,1590738991000] + sql: | + select t1.c1, t1.c2, tx.c1 as c1l, c1r, c2r + from t1 last join ( + select t2.*, t3.c1 as c1r, t3.c2 as c2r + from t2 last join t3 on t2.c1 = t3.c1 + ) tx + on t1.c1 != tx.c1 + batch_plan: | + SIMPLE_PROJECT(sources=(t1.c1, t1.c2, tx.c1 -> c1l, c1r, c2r)) + JOIN(type=LastJoin, condition=t1.c1 != tx.c1, left_keys=, right_keys=, index_keys=) + DATA_PROVIDER(table=t1) + RENAME(name=tx) + SIMPLE_PROJECT(sources=(t2.c1, t2.c4, t3.c1 -> c1r, t3.c2 -> c2r)) + JOIN(type=LastJoin, condition=, left_keys=(), right_keys=(), index_keys=(t2.c1)) + DATA_PROVIDER(table=t2) + DATA_PROVIDER(type=Partition, table=t3, index=index1) + expect: + order: c1 + columns: + - c1 string + - c2 int + - c1l string + - c1r string + - c2r int + data: | + aa, 2, dd, dd, 41 + bb, 3, dd, dd, 41 + cc, 4, dd, dd, 41 + - id: 15 + # t1------>(t2------->t3) + # │ └-(t3.c1)-┘ + # └-(t2.c1)-┘ + inputs: + - name: t1 + columns: ["c1 string","c2 int","c4 timestamp"] + indexs: ["index1:c1:c4"] + rows: + - ["aa",2,1590738989000] + - ["bb",3,1590738990000] + - ["cc",4,1590738991000] + - name: t2 + columns: ["c1 string","c4 timestamp"] + indexs: ["index1:c1:c4"] + rows: + - ["aa",1590738989000] + - ["bb",1590738990000] + - ["dd",1590738991000] + - name: t3 + columns: ["c1 string","c2 int","c3 bigint","c4 timestamp"] + indexs: ["index1:c1:c4"] + rows: + - ["aa",2,13,1590738989000] + - ["cc",21,131,1590738990000] + - ["dd",41,151,1590738991000] + sql: | + select t1.c1, t1.c2, tx.c1 as c1l, c1r, c2r + from t1 last join ( + select t2.*, t3.c1 as c1r, t3.c2 as c2r + from t2 last join t3 on t2.c1 = t3.c1 + ) tx + order by tx.c4 + on t1.c1 = tx.c1 + batch_plan: | + SIMPLE_PROJECT(sources=(t1.c1, t1.c2, tx.c1 -> c1l, c1r, c2r)) + JOIN(type=LastJoin, right_sort=(ASC), condition=, left_keys=(), right_keys=(), index_keys=(t1.c1)) + DATA_PROVIDER(table=t1) + RENAME(name=tx) + SIMPLE_PROJECT(sources=(t2.c1, t2.c4, t3.c1 -> c1r, t3.c2 -> c2r)) + JOIN(type=LastJoin, condition=, left_keys=(), right_keys=(), index_keys=(t2.c1)) + DATA_PROVIDER(type=Partition, table=t2, index=index1) + DATA_PROVIDER(type=Partition, table=t3, index=index1) + request_plan: | + SIMPLE_PROJECT(sources=(t1.c1, t1.c2, tx.c1 -> c1l, c1r, c2r)) + REQUEST_JOIN(type=LastJoin, right_sort=(ASC), condition=, left_keys=(), right_keys=(), index_keys=(t1.c1)) + DATA_PROVIDER(request=t1) + RENAME(name=tx) + SIMPLE_PROJECT(sources=(t2.c1, t2.c4, t3.c1 -> c1r, t3.c2 -> c2r)) + REQUEST_JOIN(type=LastJoin, condition=, left_keys=(), right_keys=(), index_keys=(t2.c1)) + DATA_PROVIDER(type=Partition, table=t2, index=index1) + DATA_PROVIDER(type=Partition, table=t3, index=index1) + cluster_request_plan: | + SIMPLE_PROJECT(sources=(t1.c1, t1.c2, tx.c1 -> c1l, c1r, c2r)) + REQUEST_JOIN(type=kJoinTypeConcat) + DATA_PROVIDER(request=t1) + REQUEST_JOIN(OUTPUT_RIGHT_ONLY, type=LastJoin, right_sort=(ASC), condition=, left_keys=(), right_keys=(), index_keys=(#4)) + SIMPLE_PROJECT(sources=(#4 -> t1.c1)) + DATA_PROVIDER(request=t1) + RENAME(name=tx) + SIMPLE_PROJECT(sources=(t2.c1, t2.c4, t3.c1 -> c1r, t3.c2 -> c2r)) + REQUEST_JOIN(type=LastJoin, condition=, left_keys=(), right_keys=(), index_keys=(t2.c1)) + DATA_PROVIDER(type=Partition, table=t2, index=index1) + DATA_PROVIDER(type=Partition, table=t3, index=index1) + expect: + order: c1 + columns: + - c1 string + - c2 int + - c1l string + - c1r string + - c2r int + data: | + aa, 2, aa, aa, 2 + bb, 3, bb, NULL, NULL + cc, 4, NULL, NULL, NULL + - id: 16 + # t1------>(t2------->t3) + # │ └-(t3.c1)-┘ + # └-(t2.c1)-┘ + inputs: + - name: t1 + columns: ["c1 string","c2 int","c4 timestamp"] + indexs: ["index1:c1:c4"] + rows: + - ["aa",2,1590738989000] + - ["bb",3,1590738990000] + - ["cc",4,1590738991000] + - name: t2 + columns: ["c1 string","c4 timestamp"] + indexs: ["index1:c1:c4"] + rows: + - ["aa",1590738989000] + - ["bb",1590738990000] + - ["dd",1590738991000] + - name: t3 + columns: ["c1 string","c2 int","c3 bigint","c4 timestamp"] + indexs: ["index1:c1:c4"] + rows: + - ["aa",2,13,1590738989000] + - ["cc",21,131,1590738990000] + - ["dd",41,151,1590738991000] + sql: | + select t1.c1, t1.c2, tx.c1 as c1l, c1r, c2r + from t1 last join ( + select t2.c1, t2.c4 as c4l, t3.c1 as c1r, t3.c2 as c2r + from t2 last join t3 on t2.c1 = t3.c1 + ) tx + order by tx.c4l + on t1.c1 = tx.c1 + batch_plan: | + SIMPLE_PROJECT(sources=(t1.c1, t1.c2, tx.c1 -> c1l, c1r, c2r)) + JOIN(type=LastJoin, right_sort=(ASC), condition=, left_keys=(), right_keys=(), index_keys=(t1.c1)) + DATA_PROVIDER(table=t1) + RENAME(name=tx) + SIMPLE_PROJECT(sources=(t2.c1, t2.c4 -> c4l, t3.c1 -> c1r, t3.c2 -> c2r)) + JOIN(type=LastJoin, condition=, left_keys=(), right_keys=(), index_keys=(t2.c1)) + DATA_PROVIDER(type=Partition, table=t2, index=index1) + DATA_PROVIDER(type=Partition, table=t3, index=index1) + request_plan: | + SIMPLE_PROJECT(sources=(t1.c1, t1.c2, tx.c1 -> c1l, c1r, c2r)) + REQUEST_JOIN(type=LastJoin, right_sort=(ASC), condition=, left_keys=(), right_keys=(), index_keys=(t1.c1)) + DATA_PROVIDER(request=t1) + RENAME(name=tx) + SIMPLE_PROJECT(sources=(t2.c1, t2.c4 -> c4l, t3.c1 -> c1r, t3.c2 -> c2r)) + REQUEST_JOIN(type=LastJoin, condition=, left_keys=(), right_keys=(), index_keys=(t2.c1)) + DATA_PROVIDER(type=Partition, table=t2, index=index1) + DATA_PROVIDER(type=Partition, table=t3, index=index1) + cluster_request_plan: | + SIMPLE_PROJECT(sources=(t1.c1, t1.c2, tx.c1 -> c1l, c1r, c2r)) + REQUEST_JOIN(type=kJoinTypeConcat) + DATA_PROVIDER(request=t1) + REQUEST_JOIN(OUTPUT_RIGHT_ONLY, type=LastJoin, right_sort=(ASC), condition=, left_keys=(), right_keys=(), index_keys=(#4)) + SIMPLE_PROJECT(sources=(#4 -> t1.c1)) + DATA_PROVIDER(request=t1) + RENAME(name=tx) + SIMPLE_PROJECT(sources=(t2.c1, t2.c4 -> c4l, t3.c1 -> c1r, t3.c2 -> c2r)) + REQUEST_JOIN(type=LastJoin, condition=, left_keys=(), right_keys=(), index_keys=(t2.c1)) + DATA_PROVIDER(type=Partition, table=t2, index=index1) + DATA_PROVIDER(type=Partition, table=t3, index=index1) + expect: + order: c1 + columns: + - c1 string + - c2 int + - c1l string + - c1r string + - c2r int + data: | + aa, 2, aa, aa, 2 + bb, 3, bb, NULL, NULL + cc, 4, NULL, NULL, NULL + - id: 17 + # t1------>(t2------->(t3-------t4) + # │ │ └-(t4.c1)-┘ + # │ └-(t3.c1)--┘ + # └-(t2.c1)-┘ + desc: multiple lazy last join + inputs: + - name: t1 + columns: ["c1 string","c2 int","c4 timestamp"] + indexs: ["index1:c1:c4"] + rows: + - ["aa",2,1590738989000] + - ["bb",3,1590738990000] + - ["cc",4,1590738991000] + - name: t2 + columns: ["c1 string","c4 timestamp"] + indexs: ["index1:c1:c4"] + rows: + - ["aa",1590738989000] + - ["bb",1590738990000] + - ["dd",1590738991000] + - name: t3 + columns: ["c1 string","c2 int","c3 bigint","c4 timestamp"] + indexs: ["index1:c1:c4"] + rows: + - ["aa",2,13,1590738989000] + - ["cc",21,131,1590738990000] + - ["dd",41,151,1590738991000] + - name: t4 + columns: ["c1 string","c2 int","c3 bigint","c4 timestamp"] + indexs: ["index1:c1:c4"] + rows: + - ["aa",2,13,1590738989000] + - ["bb",21,131,1590738990000] + - ["dd",41,151,1590738991000] + sql: | + select t1.c1, t1.c2, tx.c1 as c1l, c1r, c2r, c1rr + from t1 last join ( + select t2.c1, t2.c4 as c4l, t3.c1 as c1r, t3.c2 as c2r, t3.c1rr + from t2 last join ( + select t3.*, t4.c1 as c1rr + from t3 last join t4 + on t3.c1 = t4.c1 + ) t3 + on t2.c1 = t3.c1 + ) tx + order by tx.c4l + on t1.c1 = tx.c1 + batch_plan: | + SIMPLE_PROJECT(sources=(t1.c1, t1.c2, tx.c1 -> c1l, c1r, c2r, c1rr)) + JOIN(type=LastJoin, right_sort=(ASC), condition=, left_keys=(), right_keys=(), index_keys=(t1.c1)) + DATA_PROVIDER(table=t1) + RENAME(name=tx) + SIMPLE_PROJECT(sources=(t2.c1, t2.c4 -> c4l, t3.c1 -> c1r, t3.c2 -> c2r, t3.c1rr)) + JOIN(type=LastJoin, condition=, left_keys=(), right_keys=(), index_keys=(t2.c1)) + DATA_PROVIDER(type=Partition, table=t2, index=index1) + RENAME(name=t3) + SIMPLE_PROJECT(sources=(t3.c1, t3.c2, t3.c3, t3.c4, t4.c1 -> c1rr)) + JOIN(type=LastJoin, condition=, left_keys=(), right_keys=(), index_keys=(t3.c1)) + DATA_PROVIDER(type=Partition, table=t3, index=index1) + DATA_PROVIDER(type=Partition, table=t4, index=index1) + request_plan: | + SIMPLE_PROJECT(sources=(t1.c1, t1.c2, tx.c1 -> c1l, c1r, c2r, c1rr)) + REQUEST_JOIN(type=LastJoin, right_sort=(ASC), condition=, left_keys=(), right_keys=(), index_keys=(t1.c1)) + DATA_PROVIDER(request=t1) + RENAME(name=tx) + SIMPLE_PROJECT(sources=(t2.c1, t2.c4 -> c4l, t3.c1 -> c1r, t3.c2 -> c2r, t3.c1rr)) + REQUEST_JOIN(type=LastJoin, condition=, left_keys=(), right_keys=(), index_keys=(t2.c1)) + DATA_PROVIDER(type=Partition, table=t2, index=index1) + RENAME(name=t3) + SIMPLE_PROJECT(sources=(t3.c1, t3.c2, t3.c3, t3.c4, t4.c1 -> c1rr)) + REQUEST_JOIN(type=LastJoin, condition=, left_keys=(), right_keys=(), index_keys=(t3.c1)) + DATA_PROVIDER(type=Partition, table=t3, index=index1) + DATA_PROVIDER(type=Partition, table=t4, index=index1) + cluster_request_plan: | + SIMPLE_PROJECT(sources=(t1.c1, t1.c2, tx.c1 -> c1l, c1r, c2r, c1rr)) + REQUEST_JOIN(type=kJoinTypeConcat) + DATA_PROVIDER(request=t1) + REQUEST_JOIN(OUTPUT_RIGHT_ONLY, type=LastJoin, right_sort=(ASC), condition=, left_keys=(), right_keys=(), index_keys=(#4)) + SIMPLE_PROJECT(sources=(#4 -> t1.c1)) + DATA_PROVIDER(request=t1) + RENAME(name=tx) + SIMPLE_PROJECT(sources=(t2.c1, t2.c4 -> c4l, t3.c1 -> c1r, t3.c2 -> c2r, t3.c1rr)) + REQUEST_JOIN(type=LastJoin, condition=, left_keys=(), right_keys=(), index_keys=(t2.c1)) + DATA_PROVIDER(type=Partition, table=t2, index=index1) + RENAME(name=t3) + SIMPLE_PROJECT(sources=(t3.c1, t3.c2, t3.c3, t3.c4, t4.c1 -> c1rr)) + REQUEST_JOIN(type=LastJoin, condition=, left_keys=(), right_keys=(), index_keys=(t3.c1)) + DATA_PROVIDER(type=Partition, table=t3, index=index1) + DATA_PROVIDER(type=Partition, table=t4, index=index1) + expect: + order: c1 + columns: + - c1 string + - c2 int + - c1l string + - c1r string + - c2r int + - c1rr string + data: | + aa, 2, aa, aa, 2, aa + bb, 3, bb, NULL, NULL, NULL + cc, 4, NULL, NULL, NULL, NULL + - id: 18 + # t1------>(t2------->(t3-------t4) + # │ │ └-(t4.c1)-┘ + # │ └-(t3.c1)--┘ + # └-(t2.c1)-┘ + mode: request-unsupport + inputs: + - name: t1 + columns: ["c1 string","c2 int","c4 timestamp"] + indexs: ["index1:c1:c4"] + rows: + - ["aa",2,1590738989000] + - ["bb",3,1590738990000] + - ["cc",4,1590738991000] + - name: t2 + columns: ["c1 string","c4 timestamp"] + indexs: ["index1:c1:c4"] + rows: + - ["aa",1590738989000] + - ["bb",1590738990000] + - ["dd",1590738991000] + - name: t3 + columns: ["c1 string","c2 int","c3 bigint","c4 timestamp"] + indexs: ["index1:c1:c4"] + rows: + - ["aa",2,13,1590738989000] + - ["bb",21,131,1590738990000] + - ["dd",41,151,1590738991000] + - name: t4 + columns: ["c1 string","c2 int","c3 bigint","c4 timestamp"] + indexs: ["index1:c1:c4"] + rows: + - ["aa",2,13,1590738989000] + - ["cc",21,131,1590738990000] + - ["dd",41,151,1590738991000] + sql: | + select t1.c1, t1.c2, tx.c1 as c1l, c1r, c2r, c1rr + from t1 last join ( + select t2.c1, t2.c4 as c4l, t3.c1 as c1r, t3.c2 as c2r, t3.c1rr + from t2 last join ( + select t3.*, t4.c1 as c1rr + from t3 last join t4 + on t3.c1 = t4.c1 + ) t3 + on t2.c1 = t3.c1rr + ) tx + order by tx.c4l + on t1.c1 = tx.c1 + batch_plan: | + SIMPLE_PROJECT(sources=(t1.c1, t1.c2, tx.c1 -> c1l, c1r, c2r, c1rr)) + JOIN(type=LastJoin, right_sort=(ASC), condition=, left_keys=(), right_keys=(), index_keys=(t1.c1)) + DATA_PROVIDER(table=t1) + RENAME(name=tx) + SIMPLE_PROJECT(sources=(t2.c1, t2.c4 -> c4l, t3.c1 -> c1r, t3.c2 -> c2r, t3.c1rr)) + JOIN(type=LastJoin, condition=, left_keys=(t2.c1), right_keys=(t3.c1rr), index_keys=) + DATA_PROVIDER(type=Partition, table=t2, index=index1) + RENAME(name=t3) + SIMPLE_PROJECT(sources=(t3.c1, t3.c2, t3.c3, t3.c4, t4.c1 -> c1rr)) + JOIN(type=LastJoin, condition=, left_keys=(), right_keys=(), index_keys=(t3.c1)) + DATA_PROVIDER(table=t3) + DATA_PROVIDER(type=Partition, table=t4, index=index1) + expect: + order: c1 + columns: + - c1 string + - c2 int + - c1l string + - c1r string + - c2r int + - c1rr string + data: | + aa, 2, aa, aa, 2, aa + bb, 3, bb, NULL, NULL, NULL + cc, 4, NULL, NULL, NULL, NULL + - id: 19 + # t1------>(t2------->(t3-------t4) + # │ └-(t3.c1)--┘ │ + # │ └--(t4.c1)------┘ + # └-(t2.c1)-┘ + desc: nested last join + inputs: + - name: t1 + columns: ["c1 string","c2 int","c4 timestamp"] + indexs: ["index1:c1:c4"] + rows: + - ["aa",2,1590738989000] + - ["bb",3,1590738990000] + - ["cc",4,1590738991000] + - name: t2 + columns: ["c1 string","c4 timestamp"] + indexs: ["index1:c1:c4"] + rows: + - ["aa",1590738989000] + - ["bb",1590738990000] + - ["dd",1590738991000] + - name: t3 + columns: ["c1 string","c2 int","c3 bigint","c4 timestamp"] + indexs: ["index1:c1:c4"] + rows: + - ["aa",2,13,1590738989000] + - ["cc",21,131,1590738990000] + - ["dd",41,151,1590738991000] + - name: t4 + columns: ["c1 string","c2 int","c3 bigint","c4 timestamp"] + indexs: ["index1:c1:c4"] + rows: + - ["aa",2,13,1590738989000] + - ["bb",21,131,1590738990000] + - ["dd",41,151,1590738991000] + sql: | + select t1.c1, t1.c2, tx.c1 as c1l, c1r, c2r, c1rr + from t1 last join ( + select t2.c1, t2.c4 as c4l, t3.c1 as c1r, t3.c2 as c2r, t4.c1 as c1rr + from t2 last join t3 + on t2.c1 = t3.c1 + last join t4 + on t2.c1 = t4.c1 + ) tx + on t1.c1 = tx.c1 + batch_plan: | + SIMPLE_PROJECT(sources=(t1.c1, t1.c2, tx.c1 -> c1l, c1r, c2r, c1rr)) + JOIN(type=LastJoin, condition=, left_keys=(), right_keys=(), index_keys=(t1.c1)) + DATA_PROVIDER(table=t1) + RENAME(name=tx) + SIMPLE_PROJECT(sources=(t2.c1, t2.c4 -> c4l, t3.c1 -> c1r, t3.c2 -> c2r, t4.c1 -> c1rr)) + JOIN(type=LastJoin, condition=, left_keys=(), right_keys=(), index_keys=(t2.c1)) + JOIN(type=LastJoin, condition=, left_keys=(), right_keys=(), index_keys=(t2.c1)) + DATA_PROVIDER(type=Partition, table=t2, index=index1) + DATA_PROVIDER(type=Partition, table=t3, index=index1) + DATA_PROVIDER(type=Partition, table=t4, index=index1) + request_plan: | + SIMPLE_PROJECT(sources=(t1.c1, t1.c2, tx.c1 -> c1l, c1r, c2r, c1rr)) + REQUEST_JOIN(type=LastJoin, condition=, left_keys=(), right_keys=(), index_keys=(t1.c1)) + DATA_PROVIDER(request=t1) + RENAME(name=tx) + SIMPLE_PROJECT(sources=(t2.c1, t2.c4 -> c4l, t3.c1 -> c1r, t3.c2 -> c2r, t4.c1 -> c1rr)) + REQUEST_JOIN(type=LastJoin, condition=, left_keys=(), right_keys=(), index_keys=(t2.c1)) + REQUEST_JOIN(type=LastJoin, condition=, left_keys=(), right_keys=(), index_keys=(t2.c1)) + DATA_PROVIDER(type=Partition, table=t2, index=index1) + DATA_PROVIDER(type=Partition, table=t3, index=index1) + DATA_PROVIDER(type=Partition, table=t4, index=index1) + cluster_request_plan: | + SIMPLE_PROJECT(sources=(t1.c1, t1.c2, tx.c1 -> c1l, c1r, c2r, c1rr)) + REQUEST_JOIN(type=kJoinTypeConcat) + DATA_PROVIDER(request=t1) + REQUEST_JOIN(OUTPUT_RIGHT_ONLY, type=LastJoin, condition=, left_keys=(), right_keys=(), index_keys=(#4)) + SIMPLE_PROJECT(sources=(#4 -> t1.c1)) + DATA_PROVIDER(request=t1) + RENAME(name=tx) + SIMPLE_PROJECT(sources=(t2.c1, t2.c4 -> c4l, t3.c1 -> c1r, t3.c2 -> c2r, t4.c1 -> c1rr)) + REQUEST_JOIN(type=LastJoin, condition=, left_keys=(), right_keys=(), index_keys=(t2.c1)) + REQUEST_JOIN(type=LastJoin, condition=, left_keys=(), right_keys=(), index_keys=(t2.c1)) + DATA_PROVIDER(type=Partition, table=t2, index=index1) + DATA_PROVIDER(type=Partition, table=t3, index=index1) + DATA_PROVIDER(type=Partition, table=t4, index=index1) + expect: + order: c1 + columns: + - c1 string + - c2 int + - c1l string + - c1r string + - c2r int + - c1rr string + data: | + aa, 2, aa, aa, 2, aa + bb, 3, bb, NULL, NULL, bb + cc, 4, NULL, NULL, NULL, NULL + - id: 20 + # t1------>(t2------->(t3-------t4) + # │ └-(t3.c1)--┘ │ + # └-(t2.c1)----┘ │ + # └-------------------------┘ + desc: nested last join + inputs: + - name: t1 + columns: ["c1 string","c2 int","c4 timestamp"] + indexs: ["index1:c1:c4"] + rows: + - ["aa",2,1590738989000] + - ["bb",3,1590738990000] + - ["cc",4,1590738991000] + - name: t2 + columns: ["c1 string","c4 timestamp"] + indexs: ["index1:c1:c4"] + rows: + - ["aa",1590738989000] + - ["bb",1590738990000] + - ["dd",1590738991000] + - name: t3 + columns: ["c1 string","c2 int","c3 bigint","c4 timestamp"] + indexs: ["index1:c1:c4"] + rows: + - ["aa",2,13,1590738989000] + - ["cc",21,131,1590738990000] + - ["dd",41,151,1590738991000] + - name: t4 + columns: ["c1 string","c2 int","c3 bigint","c4 timestamp"] + indexs: ["index1:c1:c4"] + rows: + - ["aa",2,13,1590738989000] + - ["cc",21,131,1590738990000] + - ["dd",41,151,1590738991000] + sql: | + select t1.c1, t1.c2, tx.c1 as c1l, c1r, c2r, t4.c1 as c1rr + from t1 last join ( + select t2.c1, t2.c4 as c4l, t3.c1 as c1r, t3.c2 as c2r + from t2 last join t3 + on t2.c1 = t3.c1 + ) tx + on t1.c1 = tx.c1 + last join t4 + on tx.c1 = t4.c1 + batch_plan: | + SIMPLE_PROJECT(sources=(t1.c1, t1.c2, tx.c1 -> c1l, c1r, c2r, t4.c1 -> c1rr)) + JOIN(type=LastJoin, condition=, left_keys=(), right_keys=(), index_keys=(tx.c1)) + JOIN(type=LastJoin, condition=, left_keys=(), right_keys=(), index_keys=(t1.c1)) + DATA_PROVIDER(table=t1) + RENAME(name=tx) + SIMPLE_PROJECT(sources=(t2.c1, t2.c4 -> c4l, t3.c1 -> c1r, t3.c2 -> c2r)) + JOIN(type=LastJoin, condition=, left_keys=(), right_keys=(), index_keys=(t2.c1)) + DATA_PROVIDER(type=Partition, table=t2, index=index1) + DATA_PROVIDER(type=Partition, table=t3, index=index1) + DATA_PROVIDER(type=Partition, table=t4, index=index1) + request_plan: | + SIMPLE_PROJECT(sources=(t1.c1, t1.c2, tx.c1 -> c1l, c1r, c2r, t4.c1 -> c1rr)) + REQUEST_JOIN(type=LastJoin, condition=, left_keys=(), right_keys=(), index_keys=(tx.c1)) + REQUEST_JOIN(type=LastJoin, condition=, left_keys=(), right_keys=(), index_keys=(t1.c1)) + DATA_PROVIDER(request=t1) + RENAME(name=tx) + SIMPLE_PROJECT(sources=(t2.c1, t2.c4 -> c4l, t3.c1 -> c1r, t3.c2 -> c2r)) + REQUEST_JOIN(type=LastJoin, condition=, left_keys=(), right_keys=(), index_keys=(t2.c1)) + DATA_PROVIDER(type=Partition, table=t2, index=index1) + DATA_PROVIDER(type=Partition, table=t3, index=index1) + DATA_PROVIDER(type=Partition, table=t4, index=index1) + cluster_request_plan: | + SIMPLE_PROJECT(sources=(t1.c1, t1.c2, tx.c1 -> c1l, c1r, c2r, t4.c1 -> c1rr)) + REQUEST_JOIN(type=kJoinTypeConcat) + REQUEST_JOIN(type=kJoinTypeConcat) + DATA_PROVIDER(request=t1) + REQUEST_JOIN(OUTPUT_RIGHT_ONLY, type=LastJoin, condition=, left_keys=(), right_keys=(), index_keys=(#4)) + SIMPLE_PROJECT(sources=(#4 -> t1.c1)) + DATA_PROVIDER(request=t1) + RENAME(name=tx) + SIMPLE_PROJECT(sources=(t2.c1, t2.c4 -> c4l, t3.c1 -> c1r, t3.c2 -> c2r)) + REQUEST_JOIN(type=LastJoin, condition=, left_keys=(), right_keys=(), index_keys=(t2.c1)) + DATA_PROVIDER(type=Partition, table=t2, index=index1) + DATA_PROVIDER(type=Partition, table=t3, index=index1) + REQUEST_JOIN(OUTPUT_RIGHT_ONLY, type=LastJoin, condition=, left_keys=(), right_keys=(), index_keys=(#123)) + SIMPLE_PROJECT(sources=(#123 -> tx.c1)) + REQUEST_JOIN(OUTPUT_RIGHT_ONLY, type=LastJoin, condition=, left_keys=(), right_keys=(), index_keys=(#4)) + SIMPLE_PROJECT(sources=(#4 -> t1.c1)) + DATA_PROVIDER(request=t1) + RENAME(name=tx) + SIMPLE_PROJECT(sources=(t2.c1, t2.c4 -> c4l, t3.c1 -> c1r, t3.c2 -> c2r)) + REQUEST_JOIN(type=LastJoin, condition=, left_keys=(), right_keys=(), index_keys=(t2.c1)) + DATA_PROVIDER(type=Partition, table=t2, index=index1) + DATA_PROVIDER(type=Partition, table=t3, index=index1) + DATA_PROVIDER(type=Partition, table=t4, index=index1) + expect: + order: c1 + columns: + - c1 string + - c2 int + - c1l string + - c1r string + - c2r int + - c1rr string + data: | + aa, 2, aa, aa, 2, aa + bb, 3, bb, NULL, NULL, NULL + cc, 4, NULL, NULL, NULL, NULL diff --git a/cases/query/last_join_where.yaml b/cases/query/last_join_where.yaml index 6a341d001d8..110debcfcdf 100644 --- a/cases/query/last_join_where.yaml +++ b/cases/query/last_join_where.yaml @@ -8,7 +8,6 @@ cases: - id: 0 desc: LASTJOIN(FILTER) deployable: true - mode: batch-request-unsupport sql: | SELECT t1.c1, @@ -38,6 +37,16 @@ cases: RENAME(name=t2) FILTER_BY(condition=, left_keys=(), right_keys=(), index_keys=(aa)) DATA_PROVIDER(type=Partition, table=t2, index=index1) + cluster_request_plan: | + SIMPLE_PROJECT(sources=(t1.c1, t1.c2, t2.c1 -> c21)) + REQUEST_JOIN(type=kJoinTypeConcat) + DATA_PROVIDER(request=t1) + REQUEST_JOIN(OUTPUT_RIGHT_ONLY, type=LastJoin, condition=, left_keys=(#5), right_keys=(#9), index_keys=) + SIMPLE_PROJECT(sources=(#5 -> t1.c1)) + DATA_PROVIDER(request=t1) + RENAME(name=t2) + FILTER_BY(condition=, left_keys=(), right_keys=(), index_keys=(aa)) + DATA_PROVIDER(type=Partition, table=t2, index=index1) expect: columns: - c1 string @@ -51,7 +60,6 @@ cases: - id: 1 desc: LASTJOIN(SimpleOPS(FILTER)) - mode: batch-request-unsupport deployable: true sql: | SELECT @@ -140,7 +148,6 @@ cases: - id: 3 desc: LASTJOIN(FILTER) - mode: batch-request-unsupport deployable: true sql: | SELECT @@ -232,7 +239,6 @@ cases: LASTJOIN(SimpleOps(FILTER)), different index with join, fine to online if there is no order by of last join deployable: true - mode: batch-request-unsupport sql: | SELECT t1.c1, @@ -322,7 +328,6 @@ cases: - id: 7 desc: LASTJOIN(SimpleOps(FILTER)) hit same index with order by - mode: batch-request-unsupport deployable: true sql: | SELECT diff --git a/cases/query/last_join_window_query.yaml b/cases/query/last_join_window_query.yaml index 96467eaf787..a11fce4369f 100644 --- a/cases/query/last_join_window_query.yaml +++ b/cases/query/last_join_window_query.yaml @@ -321,11 +321,11 @@ cases: min(c3r) OVER w1 as sumb, from ( select - {0}.c3 as c3l, - {0}.id as idx, - {1}.c3 as c3r, - {0}.c1 as c1a, - {0}.c7 as c7a + t0.c3 as c3l, + t0.id as idx, + t1.c3 as c3r, + t0.c1 as c1a, + t0.c7 as c7a from t0 last join t1 on t0.c1=t1.c1 ) WINDOW w1 AS ( diff --git a/hybridse/examples/toydb/src/testing/toydb_engine_test.cc b/hybridse/examples/toydb/src/testing/toydb_engine_test.cc index a4cd2b095d8..02438aeebac 100644 --- a/hybridse/examples/toydb/src/testing/toydb_engine_test.cc +++ b/hybridse/examples/toydb/src/testing/toydb_engine_test.cc @@ -91,6 +91,13 @@ TEST_P(EngineTest, TestClusterBatchRequestEngine) { } } +// ====================================================== / +// BatchRequestEngineTest +// test batch request mode only, with yaml: +// - case/function/test_batch_request.yaml +// +// TODO(ace): merge to EngineTest above simply +// ====================================================== / TEST_P(BatchRequestEngineTest, TestBatchRequestEngine) { auto& sql_case = GetParam(); LOG(INFO) << "ID: " << sql_case.id() << ", DESC: " << sql_case.desc(); diff --git a/hybridse/include/node/node_manager.h b/hybridse/include/node/node_manager.h index 2dcd013e36f..ab87e588a53 100644 --- a/hybridse/include/node/node_manager.h +++ b/hybridse/include/node/node_manager.h @@ -151,7 +151,7 @@ class NodeManager { WindowDefNode *MergeWindow(const WindowDefNode *w1, const WindowDefNode *w2); OrderExpression* MakeOrderExpression(const ExprNode* expr, const bool is_asc); - OrderByNode *MakeOrderByNode(const ExprListNode *order_expressions); + OrderByNode *MakeOrderByNode(ExprListNode *order_expressions); FrameExtent *MakeFrameExtent(SqlNode *start, SqlNode *end); SqlNode *MakeFrameBound(BoundType bound_type); SqlNode *MakeFrameBound(BoundType bound_type, ExprNode *offset); diff --git a/hybridse/include/node/sql_node.h b/hybridse/include/node/sql_node.h index 6118c164193..bbdfc83313f 100644 --- a/hybridse/include/node/sql_node.h +++ b/hybridse/include/node/sql_node.h @@ -476,9 +476,10 @@ class ExprNode : public SqlNode { virtual bool IsListReturn(ExprAnalysisContext *ctx) const { return false; } /** - * Default expression node deep copy implementation + * Returns new ExprNode with all of fields copyed, excepting descendants ExprNodes. */ virtual ExprNode *ShadowCopy(NodeManager *) const = 0; + ExprNode *DeepCopy(NodeManager *) const override; // Get the compatible type that lhs and rhs can both casted into @@ -581,8 +582,13 @@ class FnNodeList : public FnNode { }; class OrderExpression : public ExprNode { public: + // expr maybe null OrderExpression(const ExprNode *expr, const bool is_asc) - : ExprNode(kExprOrderExpression), expr_(expr), is_asc_(is_asc) {} + : ExprNode(kExprOrderExpression), expr_(expr), is_asc_(is_asc) { + if (expr != nullptr) { + AddChild(const_cast(expr)); + } + } ~OrderExpression() {} void Print(std::ostream &output, const std::string &org_tab) const; const std::string GetExprString() const; @@ -597,8 +603,10 @@ class OrderExpression : public ExprNode { }; class OrderByNode : public ExprNode { public: - explicit OrderByNode(const ExprListNode *order_expressions) - : ExprNode(kExprOrder), order_expressions_(order_expressions) {} + explicit OrderByNode(ExprListNode *order_expressions) + : ExprNode(kExprOrder), order_expressions_(order_expressions) { + AddChild(order_expressions); + } ~OrderByNode() {} void Print(std::ostream &output, const std::string &org_tab) const; @@ -623,7 +631,7 @@ class OrderByNode : public ExprNode { return order_expression->expr(); } bool is_asc() const { return false; } - const ExprListNode *order_expressions_; + ExprListNode *order_expressions_; }; class TableRefNode : public SqlNode { public: @@ -1648,7 +1656,7 @@ class ColumnRefNode : public ExprNode { static ColumnRefNode *CastFrom(ExprNode *node); void Print(std::ostream &output, const std::string &org_tab) const; - const std::string GetExprString() const; + const std::string GetExprString() const override; const std::string GenerateExpressionName() const; virtual bool Equals(const ExprNode *node) const; ColumnRefNode *ShadowCopy(NodeManager *) const override; diff --git a/hybridse/include/passes/expression/expr_pass.h b/hybridse/include/passes/expression/expr_pass.h index 1c41307c28a..c88b7ee8585 100644 --- a/hybridse/include/passes/expression/expr_pass.h +++ b/hybridse/include/passes/expression/expr_pass.h @@ -65,15 +65,15 @@ class ExprReplacer { void AddReplacement(const node::ExprIdNode* arg, node::ExprNode* repl); void AddReplacement(const node::ExprNode* expr, node::ExprNode* repl); void AddReplacement(size_t column_id, node::ExprNode* repl); - void AddReplacement(const std::string& relation_name, - const std::string& column_name, node::ExprNode* repl); + void AddReplacement(const std::string& relation_name, const std::string& column_name, node::ExprNode* repl); - hybridse::base::Status Replace(node::ExprNode* root, - node::ExprNode** output) const; + // For the given `ExprNode` tree, do the in-place replacements specified by `AddReplacement` calls. + // Returns new ExprNode if `root` is ExprIdNode/ColumnIdNode/ColumnRefNode, `root` with its descendants replaced + // otherwise + hybridse::base::Status Replace(node::ExprNode* root, node::ExprNode** output) const; private: - hybridse::base::Status DoReplace(node::ExprNode* root, - std::unordered_set* visited, + hybridse::base::Status DoReplace(node::ExprNode* root, std::unordered_set* visited, node::ExprNode** output) const; std::unordered_map arg_id_map_; diff --git a/hybridse/include/vm/engine.h b/hybridse/include/vm/engine.h index 3cb7564be98..e552e5889c6 100644 --- a/hybridse/include/vm/engine.h +++ b/hybridse/include/vm/engine.h @@ -420,9 +420,6 @@ class Engine { EngineOptions GetEngineOptions(); private: - // Get all dependent (db, table) info from physical plan - Status GetDependentTables(const PhysicalOpNode*, std::set>*); - std::shared_ptr GetCacheLocked(const std::string& db, const std::string& sql, EngineMode engine_mode); diff --git a/hybridse/include/vm/physical_op.h b/hybridse/include/vm/physical_op.h index c884d0bb7e5..ee3634615c8 100644 --- a/hybridse/include/vm/physical_op.h +++ b/hybridse/include/vm/physical_op.h @@ -155,8 +155,10 @@ class Sort : public FnComponent { public: explicit Sort(const node::OrderByNode *orders) : orders_(orders) {} virtual ~Sort() {} + const node::OrderByNode *orders() const { return orders_; } void set_orders(const node::OrderByNode *orders) { orders_ = orders; } + const bool is_asc() const { const node::OrderExpression *first_order_expression = nullptr == orders_ ? nullptr : orders_->GetOrderExpression(0); @@ -172,18 +174,11 @@ class Sort : public FnComponent { return "sort = " + fn_info_.fn_name(); } - void ResolvedRelatedColumns( - std::vector *columns) const { + void ResolvedRelatedColumns(std::vector *columns) const { if (nullptr == orders_) { return; } - auto expr = orders_->GetOrderExpressionExpr(0); - if (nullptr != expr) { - node::ExprListNode exprs; - exprs.AddChild(const_cast(expr)); - node::ColumnOfExpression(orders_->order_expressions_, columns); - } - return; + node::ColumnOfExpression(orders_->order_expressions_, columns); } base::Status ReplaceExpr(const passes::ExprReplacer &replacer, @@ -286,8 +281,10 @@ class Key : public FnComponent { return oss.str(); } const bool ValidKey() const { return !node::ExprListNullOrEmpty(keys_); } + const node::ExprListNode *keys() const { return keys_; } void set_keys(const node::ExprListNode *keys) { keys_ = keys; } + const node::ExprListNode *PhysicalProjectNode() const { return keys_; } const std::string FnDetail() const { return "keys=" + fn_info_.fn_name(); } @@ -555,8 +552,7 @@ class PhysicalDataProviderNode : public PhysicalOpNode { class PhysicalTableProviderNode : public PhysicalDataProviderNode { public: - explicit PhysicalTableProviderNode( - const std::shared_ptr &table_handler) + explicit PhysicalTableProviderNode(const std::shared_ptr &table_handler) : PhysicalDataProviderNode(table_handler, kProviderTypeTable) {} base::Status WithNewChildren(node::NodeManager *nm, @@ -1287,7 +1283,7 @@ class PhysicalRequestJoinNode : public PhysicalBinaryNode { join_(join_type), joined_schemas_ctx_(this), output_right_only_(false) { - output_type_ = kSchemaTypeRow; + output_type_ = left->GetOutputType(); RegisterFunctionInfo(); } PhysicalRequestJoinNode(PhysicalOpNode *left, PhysicalOpNode *right, @@ -1298,7 +1294,7 @@ class PhysicalRequestJoinNode : public PhysicalBinaryNode { join_(join_type, orders, condition), joined_schemas_ctx_(this), output_right_only_(false) { - output_type_ = kSchemaTypeRow; + output_type_ = left->GetOutputType(); RegisterFunctionInfo(); } PhysicalRequestJoinNode(PhysicalOpNode *left, PhysicalOpNode *right, @@ -1307,7 +1303,7 @@ class PhysicalRequestJoinNode : public PhysicalBinaryNode { join_(join), joined_schemas_ctx_(this), output_right_only_(output_right_only) { - output_type_ = kSchemaTypeRow; + output_type_ = left->GetOutputType(); RegisterFunctionInfo(); } @@ -1321,7 +1317,7 @@ class PhysicalRequestJoinNode : public PhysicalBinaryNode { join_(join_type, condition, left_keys, right_keys), joined_schemas_ctx_(this), output_right_only_(false) { - output_type_ = kSchemaTypeRow; + output_type_ = left->GetOutputType(); RegisterFunctionInfo(); } PhysicalRequestJoinNode(PhysicalOpNode *left, PhysicalOpNode *right, @@ -1334,7 +1330,7 @@ class PhysicalRequestJoinNode : public PhysicalBinaryNode { join_(join_type, orders, condition, left_keys, right_keys), joined_schemas_ctx_(this), output_right_only_(false) { - output_type_ = kSchemaTypeRow; + output_type_ = left->GetOutputType(); RegisterFunctionInfo(); } diff --git a/hybridse/include/vm/schemas_context.h b/hybridse/include/vm/schemas_context.h index 1541c64201d..b2e68d9477a 100644 --- a/hybridse/include/vm/schemas_context.h +++ b/hybridse/include/vm/schemas_context.h @@ -72,7 +72,8 @@ class SchemaSource { // column identifier of each output column std::vector column_ids_; - // trace which child and which column id each column come from + // trace which child and which column id each column comes from, index is measured + // based on the physical node tree, starts from 0. // -1 means the column is created from current node std::vector source_child_idxs_; std::vector source_child_column_ids_; @@ -127,10 +128,6 @@ class SchemasContext { base::Status ResolveColumnRefIndex(const node::ColumnRefNode* column_ref, size_t* schema_idx, size_t* col_idx) const; - /** - * Resolve column id with given column expression [ColumnRefNode, ColumnId] - */ - base::Status ResolveColumnID(const node::ExprNode* column, size_t* column_id) const; /** * Given relation name and column name, return column unique id diff --git a/hybridse/src/node/node_manager.cc b/hybridse/src/node/node_manager.cc index 3c5b8ffd36a..8f6f80d7517 100644 --- a/hybridse/src/node/node_manager.cc +++ b/hybridse/src/node/node_manager.cc @@ -301,7 +301,7 @@ OrderExpression *NodeManager::MakeOrderExpression(const ExprNode *expr, const bo OrderExpression *node_ptr = new OrderExpression(expr, is_asc); return RegisterNode(node_ptr); } -OrderByNode *NodeManager::MakeOrderByNode(const ExprListNode *order_expressions) { +OrderByNode *NodeManager::MakeOrderByNode(ExprListNode *order_expressions) { OrderByNode *node_ptr = new OrderByNode(order_expressions); return RegisterNode(node_ptr); } diff --git a/hybridse/src/passes/physical/cluster_optimized.cc b/hybridse/src/passes/physical/cluster_optimized.cc index d17928742dc..ae53f588930 100644 --- a/hybridse/src/passes/physical/cluster_optimized.cc +++ b/hybridse/src/passes/physical/cluster_optimized.cc @@ -41,7 +41,7 @@ bool ClusterOptimized::SimplifyJoinLeftInput( for (auto column : columns) { oss << node::ExprString(column) << ","; } - LOG(INFO) << "join resolved related columns: \n" << oss.str(); + LOG(INFO) << "join resolved related columns: " << oss.str(); // find columns belong to left side std::vector left_indices; @@ -116,7 +116,7 @@ bool ClusterOptimized::SimplifyJoinLeftInput( << status; return false; } - DLOG(INFO) << "apply root node simplify!"; + DLOG(INFO) << "apply root node simplify: " << root_simplify_project_op->GetTreeString(); *output = root_simplify_project_op; return true; } @@ -134,10 +134,13 @@ bool ClusterOptimized::Transform(PhysicalOpNode* in, PhysicalOpNode** output) { case node::kJoinTypeLast: { auto left = join_op->producers()[0]; auto right = join_op->producers()[1]; - if (vm::PhysicalSchemaType::kSchemaTypeRow == - right->GetOutputType()) { - DLOG(INFO) - << "request join optimized skip: row and row join"; + if (vm::PhysicalSchemaType::kSchemaTypeRow != left->GetOutputType()) { + DLOG(INFO) << "request join optimized skip: left source is not a row"; + return false; + } + + if (vm::PhysicalSchemaType::kSchemaTypeRow == right->GetOutputType()) { + DLOG(INFO) << "request join optimized skip: row and row join"; return false; } auto simplify_left = left; diff --git a/hybridse/src/passes/physical/group_and_sort_optimized.cc b/hybridse/src/passes/physical/group_and_sort_optimized.cc index 287919d9406..ae333b6af47 100644 --- a/hybridse/src/passes/physical/group_and_sort_optimized.cc +++ b/hybridse/src/passes/physical/group_and_sort_optimized.cc @@ -15,15 +15,15 @@ */ #include "passes/physical/group_and_sort_optimized.h" -#include #include #include -#include #include #include #include #include +#include "absl/cleanup/cleanup.h" +#include "absl/status/status.h" #include "absl/strings/string_view.h" #include "vm/physical_op.h" @@ -47,9 +47,47 @@ using hybridse::vm::PhysicalSimpleProjectNode; using hybridse::vm::PhysicalWindowAggrerationNode; using hybridse::vm::ProjectType; -static bool ResolveColumnToSourceColumnName(const node::ColumnRefNode* col, - const SchemasContext* schemas_ctx, - std::string* source_name); +static bool ResolveColumnToSourceColumnName(const node::ColumnRefNode* col, const SchemasContext* schemas_ctx, + std::string* db, std::string* table, std::string* source_col); + +// ExprNode may be resolving under different SchemasContext later (say one of its descendants context), +// with column name etc it may not able to resvole since a column rename may happen in SimpleProject node. +// With the column id hint written to corresponding ColumnRefNode earlier, resolving issue can be mitigated. +absl::Status GroupAndSortOptimized::BuildExprCache(const node::ExprNode* node, const SchemasContext* sc) { + if (node == nullptr) { + return {}; + } + + switch (node->GetExprType()) { + case node::kExprColumnRef: { + auto ref = dynamic_cast(node); + + if (expr_cache_.find(ref) != expr_cache_.end()) { + break; + } + + std::string source_col; + std::string source_db; + std::string source_table; + if (!ResolveColumnToSourceColumnName(ref, sc, &source_db, &source_table, &source_col)) { + return absl::InternalError(absl::StrCat("unable to resolve ", ref->GetExprString())); + } + + expr_cache_.emplace(ref, SrcColInfo{source_col, source_table, source_db}); + break; + } + default: + break; + } + + for (uint32_t i = 0; i < node->GetChildNum(); ++i) { + auto s = BuildExprCache(node->GetChild(i), sc); + if (!s.ok()) { + return s; + } + } + return {}; +} bool GroupAndSortOptimized::Transform(PhysicalOpNode* in, PhysicalOpNode** output) { @@ -239,6 +277,48 @@ bool GroupAndSortOptimized::Transform(PhysicalOpNode* in, return false; } +bool GroupAndSortOptimized::KeysOptimized(const SchemasContext* root_schemas_ctx, + PhysicalOpNode* in, + Key* left_key, + Key* index_key, + Key* right_key, + Sort* sort, + PhysicalOpNode** new_in) { + if (nullptr == left_key || nullptr == index_key || !left_key->ValidKey()) { + return false; + } + + if (right_key != nullptr && !right_key->ValidKey()) { + return false; + } + + absl::Cleanup clean = [&]() { + expr_cache_.clear(); + }; + + auto s = BuildExprCache(left_key->keys(), root_schemas_ctx); + if (!s.ok()) { + return false; + } + s = BuildExprCache(index_key->keys(), root_schemas_ctx); + if (!s.ok()) { + return false; + } + if (right_key != nullptr) { + s = BuildExprCache(right_key->keys(), root_schemas_ctx); + if (!s.ok()) { + return false; + } + } + if (sort != nullptr) { + s = BuildExprCache(sort->orders(), root_schemas_ctx); + if (!s.ok()) { + return false; + } + } + return KeysOptimizedImpl(root_schemas_ctx, in, left_key, index_key, right_key, sort, new_in); +} + /** * optimize keys on condition. Remove keys from upper node if key match indexes * defined in table schema `left_key` & `index_key` is required, `right_key` is @@ -249,7 +329,7 @@ bool GroupAndSortOptimized::Transform(PhysicalOpNode* in, * otherwise: * - `left_key`, `index_key` corresponding to Key group & Key hash */ -bool GroupAndSortOptimized::KeysOptimized(const SchemasContext* root_schemas_ctx, +bool GroupAndSortOptimized::KeysOptimizedImpl(const SchemasContext* root_schemas_ctx, PhysicalOpNode* in, Key* left_key, Key* index_key, @@ -258,15 +338,6 @@ bool GroupAndSortOptimized::KeysOptimized(const SchemasContext* root_schemas_ctx PhysicalOpNode** new_in) { TransformCxtGuard guard(&ctx_, KeysInfo(in->GetOpType(), left_key, right_key, index_key, sort)); - if (nullptr == left_key || nullptr == index_key || !left_key->ValidKey()) { - return false; - } - - if (right_key != nullptr && !right_key->ValidKey()) { - return false; - } - - if (PhysicalOpType::kPhysicalOpDataProvider == in->GetOpType()) { auto scan_op = dynamic_cast(in); // Do not optimize with Request DataProvider (no index has been provided) @@ -386,12 +457,13 @@ bool GroupAndSortOptimized::KeysOptimized(const SchemasContext* root_schemas_ctx return true; } } else if (PhysicalOpType::kPhysicalOpSimpleProject == in->GetOpType()) { - auto simple_project = dynamic_cast(in); PhysicalOpNode* new_depend; - if (!KeysOptimized(root_schemas_ctx, simple_project->producers()[0], - left_key, index_key, right_key, sort, &new_depend)) { + if (!KeysOptimizedImpl(in->GetProducer(0)->schemas_ctx(), in->GetProducer(0), left_key, index_key, right_key, sort, + &new_depend)) { return false; } + + auto simple_project = dynamic_cast(in); PhysicalSimpleProjectNode* new_simple_op = nullptr; Status status = plan_ctx_->CreateOp(&new_simple_op, new_depend, simple_project->project()); @@ -403,7 +475,7 @@ bool GroupAndSortOptimized::KeysOptimized(const SchemasContext* root_schemas_ctx return true; } else if (PhysicalOpType::kPhysicalOpRename == in->GetOpType()) { PhysicalOpNode* new_depend; - if (!KeysOptimized(root_schemas_ctx, in->producers()[0], left_key, + if (!KeysOptimizedImpl(in->GetProducer(0)->schemas_ctx(), in->producers()[0], left_key, index_key, right_key, sort, &new_depend)) { return false; } @@ -421,7 +493,7 @@ bool GroupAndSortOptimized::KeysOptimized(const SchemasContext* root_schemas_ctx PhysicalFilterNode* filter_op = dynamic_cast(in); PhysicalOpNode* new_depend; - if (!KeysOptimized(root_schemas_ctx, in->producers()[0], left_key, index_key, right_key, sort, &new_depend)) { + if (!KeysOptimizedImpl(root_schemas_ctx, in->producers()[0], left_key, index_key, right_key, sort, &new_depend)) { return false; } PhysicalFilterNode* new_filter = nullptr; @@ -438,19 +510,39 @@ bool GroupAndSortOptimized::KeysOptimized(const SchemasContext* root_schemas_ctx // try optimze left source of request join with window definition // window partition by and order by columns must refer to the left most table only PhysicalOpNode* new_depend = nullptr; - if (!KeysOptimized(request_join->GetProducer(0)->schemas_ctx(), request_join->GetProducer(0), left_key, - index_key, right_key, sort, &new_depend)) { + auto* rebase_sc = in->GetProducer(0)->schemas_ctx(); + if (!KeysOptimizedImpl(rebase_sc, in->GetProducer(0), left_key, index_key, right_key, sort, + &new_depend)) { return false; } PhysicalRequestJoinNode* new_join = nullptr; auto s = plan_ctx_->CreateOp(&new_join, new_depend, request_join->GetProducer(1), - request_join->join(), - request_join->output_right_only()); + request_join->join(), request_join->output_right_only()); if (!s.isOK()) { LOG(WARNING) << "Fail to create new request join op: " << s; return false; } + *new_in = new_join; + return true; + } else if (PhysicalOpType::kPhysicalOpJoin == in->GetOpType()) { + auto* join = dynamic_cast(in); + // try optimze left source of request join with window definition + // window partition by and order by columns must refer to the left most table only + PhysicalOpNode* new_depend = nullptr; + auto* rebase_sc = in->GetProducer(0)->schemas_ctx(); + if (!KeysOptimizedImpl(rebase_sc, in->GetProducer(0), left_key, index_key, right_key, sort, + &new_depend)) { + return false; + } + PhysicalJoinNode* new_join = nullptr; + auto s = plan_ctx_->CreateOp(&new_join, new_depend, join->GetProducer(1), + join->join(), join->output_right_only()); + if (!s.isOK()) { + LOG(WARNING) << "Fail to create new join op: " << s; + return false; + } + *new_in = new_join; return true; } @@ -522,39 +614,6 @@ bool GroupAndSortOptimized::KeyAndOrderOptimized( sort, new_in); } -bool GroupAndSortOptimized::SortOptimized( - const SchemasContext* root_schemas_ctx, PhysicalOpNode* in, Sort* sort) { - if (nullptr == sort) { - return false; - } - if (PhysicalOpType::kPhysicalOpDataProvider == in->GetOpType()) { - auto scan_op = dynamic_cast(in); - if (DataProviderType::kProviderTypePartition != - scan_op->provider_type_) { - return false; - } - auto partition_provider = - dynamic_cast(scan_op); - const node::OrderByNode* new_orders = nullptr; - - auto& index_hint = partition_provider->table_handler_->GetIndex(); - std::string index_name = partition_provider->index_name_; - auto index_st = index_hint.at(index_name); - TransformOrderExpr(root_schemas_ctx, sort->orders(), - *(scan_op->table_handler_->GetSchema()), index_st, - &new_orders); - sort->set_orders(new_orders); - return true; - } else if (PhysicalOpType::kPhysicalOpSimpleProject == in->GetOpType()) { - auto simple_project = dynamic_cast(in); - return SortOptimized(root_schemas_ctx, simple_project->producers()[0], - sort); - } else if (PhysicalOpType::kPhysicalOpRename == in->GetOpType()) { - return SortOptimized(root_schemas_ctx, in->producers()[0], sort); - } - return false; -} - bool GroupAndSortOptimized::TransformKeysAndOrderExpr(const SchemasContext* root_schemas_ctx, const node::ExprListNode* groups, const node::OrderByNode* order, @@ -582,13 +641,18 @@ bool GroupAndSortOptimized::TransformKeysAndOrderExpr(const SchemasContext* root switch (group->expr_type_) { case node::kExprColumnRef: { auto column = dynamic_cast(group); - std::string source_column_name; - if (!ResolveColumnToSourceColumnName(column, root_schemas_ctx, &source_column_name)) { + auto op = expr_cache_.find(column); + if (op == expr_cache_.end()) { + return false; + } + + if (table_handler->GetName() != op->second.tb_name || + table_handler->GetDatabase() != op->second.db_name) { return false; } result_bitmap_mapping[columns.size()] = i; - columns.push_back(source_column_name); + columns.emplace_back(op->second.col_name); break; } default: { @@ -602,11 +666,17 @@ bool GroupAndSortOptimized::TransformKeysAndOrderExpr(const SchemasContext* root auto expr = order->GetOrderExpressionExpr(i); if (nullptr != expr && expr->GetExprType() == node::kExprColumnRef) { auto column = dynamic_cast(expr); - std::string source_column_name; - if (!ResolveColumnToSourceColumnName(column, root_schemas_ctx, &source_column_name)) { + auto op = expr_cache_.find(column); + if (op == expr_cache_.end()) { return false; } - order_columns.push_back(source_column_name); + + if (table_handler->GetName() != op->second.tb_name || + table_handler->GetDatabase() != op->second.db_name) { + return false; + } + + order_columns.emplace_back(op->second.col_name); } } } @@ -754,75 +824,21 @@ bool GroupAndSortOptimized::MatchBestIndex(const std::vector& colum return succ; } -bool GroupAndSortOptimized::TransformOrderExpr( - const SchemasContext* schemas_ctx, const node::OrderByNode* order, - const Schema& schema, const IndexSt& index_st, - const node::OrderByNode** output) { - *output = order; - if (nullptr == order || nullptr == output) { - DLOG(WARNING) - << "fail to optimize order expr : order expr or output is null"; - return false; - } - if (index_st.ts_pos == INVALID_POS) { - DLOG(WARNING) << "not set ts col"; - return false; - } - auto& ts_column = schema.Get(index_st.ts_pos); - *output = order; - int succ_match = -1; - for (size_t i = 0; i < order->order_expressions()->GetChildNum(); ++i) { - auto expr = order->GetOrderExpressionExpr(i); - if (nullptr != expr && expr->GetExprType() == node::kExprColumnRef) { - auto column = dynamic_cast(expr); - std::string source_column_name; - if (ResolveColumnToSourceColumnName(column, schemas_ctx, - &source_column_name)) { - if (ts_column.name() == source_column_name) { - succ_match = i; - break; - } - } - } - } - if (succ_match >= 0) { - node::ExprListNode* expr_list = node_manager_->MakeExprList(); - for (size_t i = 0; i < order->order_expressions()->GetChildNum(); ++i) { - if (static_cast(succ_match) != i) { - expr_list->AddChild(order->order_expressions()->GetChild(i)); - } - } - *output = dynamic_cast( - node_manager_->MakeOrderByNode(expr_list)); - return true; - } else { - return false; - } -} - /** * Resolve column reference to possible source table's column name */ -static bool ResolveColumnToSourceColumnName(const node::ColumnRefNode* col, - const SchemasContext* schemas_ctx, - std::string* source_name) { - // use detailed column resolve utility +static bool ResolveColumnToSourceColumnName(const node::ColumnRefNode* col, const SchemasContext* schemas_ctx, + std::string* src_db, std::string* src_table, std::string* src_col) { + auto db = col->GetDBName(); + auto rel = col->GetRelationName(); + auto col_name = col->GetColumnName(); size_t column_id; int path_idx; size_t child_column_id; size_t source_column_id; const PhysicalOpNode* source; - Status status = schemas_ctx->ResolveColumnID(col->GetDBName(), - col->GetRelationName(), col->GetColumnName(), &column_id, &path_idx, - &child_column_id, &source_column_id, &source); - - // try loose the relation - if (!status.isOK() && !col->GetRelationName().empty()) { - status = schemas_ctx->ResolveColumnID( - col->GetDBName(), col->GetRelationName(), col->GetColumnName(), - &column_id, &path_idx, &child_column_id, - &source_column_id, &source); - } + Status status = schemas_ctx->ResolveColumnID(db, rel, col_name, &column_id, &path_idx, &child_column_id, + &source_column_id, &source); if (!status.isOK()) { LOG(WARNING) << "Illegal index column: " << col->GetExprString(); @@ -834,13 +850,17 @@ static bool ResolveColumnToSourceColumnName(const node::ColumnRefNode* col, << col->GetExprString(); return false; } - status = source->schemas_ctx()->ResolveColumnNameByID(source_column_id, - source_name); + + std::string sc_db, sc_table, sc_column; + status = source->schemas_ctx()->ResolveDbTableColumnByID(source_column_id, &sc_db, &sc_table, &sc_column); if (!status.isOK()) { - LOG(WARNING) << "Illegal source column id #" << source_column_id - << " for index column " << col->GetExprString(); + LOG(WARNING) << "Illegal source column id #" << source_column_id << " for index column " + << col->GetExprString(); return false; } + *src_db = sc_db; + *src_table = sc_table; + *src_col = sc_column; return true; } diff --git a/hybridse/src/passes/physical/group_and_sort_optimized.h b/hybridse/src/passes/physical/group_and_sort_optimized.h index 612b9cdaa67..1d410f2b8e8 100644 --- a/hybridse/src/passes/physical/group_and_sort_optimized.h +++ b/hybridse/src/passes/physical/group_and_sort_optimized.h @@ -16,12 +16,13 @@ #ifndef HYBRIDSE_SRC_PASSES_PHYSICAL_GROUP_AND_SORT_OPTIMIZED_H_ #define HYBRIDSE_SRC_PASSES_PHYSICAL_GROUP_AND_SORT_OPTIMIZED_H_ +#include #include #include #include -#include -#include +#include #include +#include #include "passes/physical/transform_up_physical_pass.h" @@ -85,12 +86,22 @@ class GroupAndSortOptimized : public TransformUpPysicalPass { Container* c_; }; + // info to a physical table + struct SrcColInfo { + std::string col_name; + std::string tb_name; + std::string db_name; + }; + private: bool Transform(PhysicalOpNode* in, PhysicalOpNode** output); bool KeysOptimized(const SchemasContext* root_schemas_ctx, PhysicalOpNode* in, Key* left_key, Key* index_key, Key* right_key, Sort* sort, PhysicalOpNode** new_in); + bool KeysOptimizedImpl(const SchemasContext* root_schemas_ctx, PhysicalOpNode* in, Key* left_key, Key* index_key, + Key* right_key, Sort* sort, PhysicalOpNode** new_in); + bool FilterAndOrderOptimized(const SchemasContext* root_schemas_ctx, PhysicalOpNode* in, Filter* filter, Sort* sort, PhysicalOpNode** new_in); @@ -115,12 +126,7 @@ class GroupAndSortOptimized : public TransformUpPysicalPass { bool GroupOptimized(const SchemasContext* root_schemas_ctx, PhysicalOpNode* in, Key* group, PhysicalOpNode** new_in); - bool SortOptimized(const SchemasContext* root_schemas_ctx, - PhysicalOpNode* in, Sort* sort); - bool TransformOrderExpr(const SchemasContext* schemas_ctx, - const node::OrderByNode* order, - const Schema& schema, const IndexSt& index_st, - const node::OrderByNode** output); + bool TransformKeysAndOrderExpr(const SchemasContext* schemas_ctx, const node::ExprListNode* groups, const node::OrderByNode* order, @@ -134,8 +140,15 @@ class GroupAndSortOptimized : public TransformUpPysicalPass { std::string* index_name, IndexBitMap* best_bitmap); + absl::Status BuildExprCache(const node::ExprNode* node, const SchemasContext* sc); + private: std::list ctx_; + + // Map ExprNode to source column name + // A source column name is the column name in string that refers to a physical table, + // only one table got optimized each time + std::unordered_map expr_cache_; }; } // namespace passes } // namespace hybridse diff --git a/hybridse/src/vm/engine.cc b/hybridse/src/vm/engine.cc index 6fd0f14a904..4fdc368887e 100644 --- a/hybridse/src/vm/engine.cc +++ b/hybridse/src/vm/engine.cc @@ -94,27 +94,10 @@ bool Engine::GetDependentTables(const std::string& sql, const std::string& db, return false; } - status = GetDependentTables(physical_plan, db_tables); + status = internal::GetDependentTables(physical_plan, db_tables); return status.isOK(); } -Status Engine::GetDependentTables(const PhysicalOpNode* root, std::set>* db_tbs) { - using OUT = std::set>; - *db_tbs = internal::ReduceNode( - root, OUT{}, - [](OUT init, const PhysicalOpNode* node) { - if (node->GetOpType() == kPhysicalOpDataProvider) { - auto* data_op = dynamic_cast(node); - if (data_op != nullptr) { - init.emplace(data_op->GetDb(), data_op->GetName()); - } - } - return init; - }, - [](const PhysicalOpNode* node) { return node->GetDependents(); }); - return Status::OK(); -} - bool Engine::IsCompatibleCache(RunSession& session, // NOLINT std::shared_ptr info, base::Status& status) { // NOLINT @@ -271,7 +254,7 @@ bool Engine::Explain(const std::string& sql, const std::string& db, EngineMode e explain_output->request_db_name = ctx.request_db_name; explain_output->limit_cnt = ctx.limit_cnt; - auto s = GetDependentTables(ctx.physical_plan, &explain_output->dependent_tables); + auto s = internal::GetDependentTables(ctx.physical_plan, &explain_output->dependent_tables); if (!s.isOK()) { LOG(ERROR) << s; status->code = common::kPhysicalPlanError; diff --git a/hybridse/src/vm/generator.cc b/hybridse/src/vm/generator.cc index 2b437ca2602..28542a7befb 100644 --- a/hybridse/src/vm/generator.cc +++ b/hybridse/src/vm/generator.cc @@ -726,5 +726,9 @@ std::shared_ptr FilterGenerator::Filter(std::shared_ptr(table, limit.value()); } +bool FilterGenerator::ValidIndex() const { + return index_seek_gen_.Valid(); +} + } // namespace vm } // namespace hybridse diff --git a/hybridse/src/vm/generator.h b/hybridse/src/vm/generator.h index 51bc6b1e674..4dded0d6ebf 100644 --- a/hybridse/src/vm/generator.h +++ b/hybridse/src/vm/generator.h @@ -230,6 +230,9 @@ class FilterGenerator : public PredicateFun { const bool Valid() const { return index_seek_gen_.Valid() || condition_gen_.Valid(); } + // return if index seek exists + bool ValidIndex() const; + std::shared_ptr Filter(std::shared_ptr table, const Row& parameter, std::optional limit); diff --git a/hybridse/src/vm/internal/node_helper.cc b/hybridse/src/vm/internal/node_helper.cc new file mode 100644 index 00000000000..9d97c14374a --- /dev/null +++ b/hybridse/src/vm/internal/node_helper.cc @@ -0,0 +1,42 @@ +/** + * Copyright (c) 2023 OpenMLDB authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "vm/internal/node_helper.h" + +namespace hybridse { +namespace vm { +namespace internal { + +Status GetDependentTables(const PhysicalOpNode* root, std::set>* db_tbs) { + using OUT = std::set>; + *db_tbs = internal::ReduceNode( + root, OUT{}, + [](OUT init, const PhysicalOpNode* node) { + if (node->GetOpType() == kPhysicalOpDataProvider) { + auto* data_op = dynamic_cast(node); + if (data_op != nullptr) { + init.emplace(data_op->GetDb(), data_op->GetName()); + } + } + return init; + }, + [](const PhysicalOpNode* node) { return node->GetDependents(); }); + return Status::OK(); +} + +} // namespace internal +} // namespace vm +} // namespace hybridse diff --git a/hybridse/src/vm/internal/node_helper.h b/hybridse/src/vm/internal/node_helper.h index 89b47ea4e0f..7b9d5044748 100644 --- a/hybridse/src/vm/internal/node_helper.h +++ b/hybridse/src/vm/internal/node_helper.h @@ -18,6 +18,8 @@ #ifndef HYBRIDSE_SRC_VM_INTERNAL_NODE_HELPER_H_ #define HYBRIDSE_SRC_VM_INTERNAL_NODE_HELPER_H_ +#include +#include #include #include @@ -63,6 +65,9 @@ State ReduceNode(const PhysicalOpNode* root, State state, BinOp&& op, GetKids&& return state; } +// Get all dependent (db, table) info from physical plan +Status GetDependentTables(const PhysicalOpNode*, std::set>*); + } // namespace internal } // namespace vm } // namespace hybridse diff --git a/hybridse/src/vm/runner.cc b/hybridse/src/vm/runner.cc index 2072715c613..be954653b91 100644 --- a/hybridse/src/vm/runner.cc +++ b/hybridse/src/vm/runner.cc @@ -25,7 +25,6 @@ #include "absl/strings/str_cat.h" #include "absl/strings/substitute.h" #include "base/texttable.h" -#include "udf/udf.h" #include "vm/catalog_wrapper.h" #include "vm/core_api.h" #include "vm/internal/eval.h" @@ -139,10 +138,9 @@ ClusterTask RunnerBuilder::Build(PhysicalOpNode* node, Status& status) { } } case kPhysicalOpSimpleProject: { - auto cluster_task = // NOLINT - Build(node->producers().at(0), status); + auto cluster_task = Build(node->producers().at(0), status); if (!cluster_task.IsValid()) { - status.msg = "fail to build input runner"; + status.msg = "fail to build input runner for simple project:\n" + node->GetTreeString(); status.code = common::kExecutionPlanError; LOG(WARNING) << status; return fail; @@ -338,19 +336,17 @@ ClusterTask RunnerBuilder::Build(PhysicalOpNode* node, Status& status) { return BuildRequestAggUnionTask(node, status); } case kPhysicalOpRequestJoin: { - auto left_task = // NOLINT - Build(node->producers().at(0), status); + auto left_task = Build(node->GetProducer(0), status); if (!left_task.IsValid()) { - status.msg = "fail to build left input runner"; + status.msg = "fail to build left input runner for: " + node->GetProducer(0)->GetTreeString(); status.code = common::kExecutionPlanError; LOG(WARNING) << status; return fail; } auto left = left_task.GetRoot(); - auto right_task = // NOLINT - Build(node->producers().at(1), status); + auto right_task = Build(node->GetProducer(1), status); if (!right_task.IsValid()) { - status.msg = "fail to build right input runner"; + status.msg = "fail to build right input runner for: " + node->GetProducer(1)->GetTreeString(); status.code = common::kExecutionPlanError; LOG(WARNING) << status; return fail; @@ -364,19 +360,38 @@ ClusterTask RunnerBuilder::Build(PhysicalOpNode* node, Status& status) { left->output_schemas()->GetSchemaSourceSize(), right->output_schemas()->GetSchemaSourceSize(), op->output_right_only()); - if (support_cluster_optimized_ && IsPartitionProvider(node->GetProducer(0))) { - // Partion left join partition, route by index of the left source, and it should uncompleted - auto& route_info = left_task.GetRouteInfo(); - runner->AddProducer(left_task.GetRoot()); - runner->AddProducer(right_task.GetRoot()); - return UnCompletedClusterTask(runner, route_info.table_handler_, route_info.index_); - } else { - return RegisterTask( - node, BinaryInherit(left_task, right_task, runner, op->join().index_key(), kLeftBias)); + if (support_cluster_optimized_) { + if (IsPartitionProvider(node->GetProducer(0))) { + // Partion left join partition, route by index of the left source, and it should uncompleted + auto& route_info = left_task.GetRouteInfo(); + runner->AddProducer(left_task.GetRoot()); + runner->AddProducer(right_task.GetRoot()); + return RegisterTask( + node, UnCompletedClusterTask(runner, route_info.table_handler_, route_info.index_)); + } + + if (right_task.IsCompletedClusterTask() && right_task.GetRouteInfo().lazy_route_ && + !op->join_.index_key_.ValidKey()) { + auto& route_info = right_task.GetRouteInfo(); + runner->AddProducer(left_task.GetRoot()); + runner->AddProducer(right_task.GetRoot()); + return RegisterTask(node, ClusterTask(runner, {}, route_info)); + } } + + return RegisterTask( + node, BinaryInherit(left_task, right_task, runner, op->join().index_key(), kLeftBias)); } case node::kJoinTypeConcat: { ConcatRunner* runner = CreateRunner(id_++, node->schemas_ctx(), op->GetLimitCnt()); + if (support_cluster_optimized_) { + if (right_task.IsCompletedClusterTask() && right_task.GetRouteInfo().lazy_route_ && + !op->join_.index_key_.ValidKey()) { + runner->AddProducer(left_task.GetRoot()); + runner->AddProducer(right_task.GetRoot()); + return RegisterTask(node, ClusterTask(runner, {}, RouteInfo{})); + } + } return RegisterTask(node, BinaryInherit(left_task, right_task, runner, Key(), kNoBias)); } default: { @@ -464,9 +479,8 @@ ClusterTask RunnerBuilder::Build(PhysicalOpNode* node, Status& status) { return RegisterTask(node, UnaryInheritTask(cluster_task, runner)); } case kPhysicalOpFilter: { - auto cluster_task = // NOLINT - Build(node->producers().at(0), status); - if (!cluster_task.IsValid()) { + auto producer_task = Build(node->GetProducer(0), status); + if (!producer_task.IsValid()) { status.msg = "fail to build input runner"; status.code = common::kExecutionPlanError; LOG(WARNING) << status; @@ -475,7 +489,27 @@ ClusterTask RunnerBuilder::Build(PhysicalOpNode* node, Status& status) { auto op = dynamic_cast(node); FilterRunner* runner = CreateRunner(id_++, node->schemas_ctx(), op->GetLimitCnt(), op->filter_); - return RegisterTask(node, UnaryInheritTask(cluster_task, runner)); + // under cluster, filter task might be completed or uncompleted + // based on whether filter node has the index_key underlaying DataTask requires + ClusterTask out; + if (support_cluster_optimized_) { + auto& route_info_ref = producer_task.GetRouteInfo(); + if (runner->filter_gen_.ValidIndex()) { + // complete the route info + RouteInfo lazy_route_info(route_info_ref.index_, op->filter().index_key(), + std::make_shared(producer_task), + route_info_ref.table_handler_); + lazy_route_info.lazy_route_ = true; + runner->AddProducer(producer_task.GetRoot()); + out = ClusterTask(runner, {}, lazy_route_info); + } else { + runner->AddProducer(producer_task.GetRoot()); + out = UnCompletedClusterTask(runner, route_info_ref.table_handler_, route_info_ref.index_); + } + } else { + out = UnaryInheritTask(producer_task, runner); + } + return RegisterTask(node, out); } case kPhysicalOpLimit: { auto cluster_task = // NOLINT @@ -709,8 +743,7 @@ ClusterTask RunnerBuilder::BuildClusterTaskForBinaryRunner( new_right.IsCompletedClusterTask()) { // merge left and right task if tasks can be merged if (ClusterTask::TaskCanBeMerge(new_left, new_right)) { - ClusterTask task = - ClusterTask::TaskMerge(runner, new_left, new_right); + ClusterTask task = ClusterTask::TaskMerge(runner, new_left, new_right); runner->AddProducer(new_left.GetRoot()); runner->AddProducer(new_right.GetRoot()); return task; @@ -735,10 +768,12 @@ ClusterTask RunnerBuilder::BuildClusterTaskForBinaryRunner( } } } - if (new_left.IsUnCompletedClusterTask() || - new_right.IsUnCompletedClusterTask()) { - LOG(WARNING) << "Fail to build cluster task, can't handler " - "uncompleted cluster task"; + if (new_left.IsUnCompletedClusterTask()) { + LOG(WARNING) << "can't handler uncompleted cluster task from left:" << new_left; + return ClusterTask(); + } + if (new_right.IsUnCompletedClusterTask()) { + LOG(WARNING) << "can't handler uncompleted cluster task from right:" << new_right; return ClusterTask(); } diff --git a/hybridse/src/vm/runner.h b/hybridse/src/vm/runner.h index 25857dbdd0f..64e712bbde7 100644 --- a/hybridse/src/vm/runner.h +++ b/hybridse/src/vm/runner.h @@ -857,8 +857,7 @@ class ProxyRequestRunner : public Runner { const std::vector>& inputs) override; std::shared_ptr BatchRequestRun( RunnerContext& ctx) override; // NOLINT - virtual void PrintRunnerInfo(std::ostream& output, - const std::string& tab) const { + void PrintRunnerInfo(std::ostream& output, const std::string& tab) const override { output << tab << "[" << id_ << "]" << RunnerTypeName(type_) << "(TASK_ID=" << task_id_ << ")"; if (is_lazy_) { @@ -947,6 +946,9 @@ class RouteInfo { const std::string ToString() const { if (IsCompleted()) { std::ostringstream oss; + if (lazy_route_) { + oss << "[LAZY]"; + } oss << ", routing index = " << table_handler_->GetDatabase() << "." << table_handler_->GetName() << "." << index_ << ", " << index_key_.ToString(); @@ -960,6 +962,9 @@ class RouteInfo { Runner* index_key_input_runner_; std::shared_ptr input_; std::shared_ptr table_handler_; + + // if true: generate the complete ClusterTask only when requires + bool lazy_route_ = false; }; // task info of cluster job @@ -968,9 +973,12 @@ class RouteInfo { // request generator class ClusterTask { public: + // common tasks ClusterTask() : root_(nullptr), input_runners_(), route_info_() {} explicit ClusterTask(Runner* root) : root_(root), input_runners_(), route_info_() {} + + // cluster task with explicit routeinfo ClusterTask(Runner* root, const std::shared_ptr table_handler, std::string index) : root_(root), input_runners_(), route_info_(index, table_handler) {} @@ -978,6 +986,7 @@ class ClusterTask { const RouteInfo& route_info) : root_(root), input_runners_(input_runners), route_info_(route_info) {} ~ClusterTask() {} + void Print(std::ostream& output, const std::string& tab) const { output << route_info_.ToString() << "\n"; if (nullptr == root_) { @@ -988,6 +997,11 @@ class ClusterTask { } } + friend std::ostream& operator<<(std::ostream& os, const ClusterTask& output) { + output.Print(os, ""); + return os; + } + void ResetInputs(std::shared_ptr input) { for (auto input_runner : input_runners_) { input_runner->SetProducer(0, route_info_.input_->GetRoot()); @@ -1180,8 +1194,7 @@ class RunnerBuilder { Status& status) { // NOLINT id_ = 0; cluster_job_.Reset(); - auto task = // NOLINT whitespace/braces - Build(node, status); + auto task = Build(node, status); if (!status.isOK()) { return cluster_job_; } @@ -1215,6 +1228,7 @@ class RunnerBuilder { private: node::NodeManager* nm_; + // only set for request mode bool support_cluster_optimized_; int32_t id_; ClusterJob cluster_job_; diff --git a/hybridse/src/vm/schemas_context.cc b/hybridse/src/vm/schemas_context.cc index 214da20caa5..7d794bc8f92 100644 --- a/hybridse/src/vm/schemas_context.cc +++ b/hybridse/src/vm/schemas_context.cc @@ -382,40 +382,20 @@ Status DoSearchExprDependentColumns(const node::ExprNode* expr, std::vectorpush_back(expr); break; } - case node::kExprBetween: { - std::vector expr_list; - auto between_expr = dynamic_cast(expr); - CHECK_STATUS(DoSearchExprDependentColumns(between_expr->GetLow(), - columns)); - CHECK_STATUS(DoSearchExprDependentColumns(between_expr->GetHigh(), - columns)); - CHECK_STATUS(DoSearchExprDependentColumns(between_expr->GetLhs(), - columns)); - break; - } case node::kExprCall: { auto call_expr = dynamic_cast(expr); if (nullptr != call_expr->GetOver()) { auto orders = call_expr->GetOver()->GetOrders(); if (nullptr != orders) { - CHECK_STATUS( - DoSearchExprDependentColumns(orders, columns)); + CHECK_STATUS(DoSearchExprDependentColumns(orders, columns)); } auto partitions = call_expr->GetOver()->GetPartitions(); if (nullptr != partitions) { - CHECK_STATUS( - DoSearchExprDependentColumns(partitions, columns)); + CHECK_STATUS(DoSearchExprDependentColumns(partitions, columns)); } } break; } - case node::kExprOrderExpression: { - auto refx = dynamic_cast(expr); - CHECK_TRUE(refx != nullptr, common::kTypeError); - CHECK_STATUS(DoSearchExprDependentColumns(refx->expr(), columns)); - - break; - } default: break; } diff --git a/hybridse/src/vm/transform.cc b/hybridse/src/vm/transform.cc index 60549447e42..8020c99741f 100644 --- a/hybridse/src/vm/transform.cc +++ b/hybridse/src/vm/transform.cc @@ -21,6 +21,7 @@ #include #include "absl/cleanup/cleanup.h" +#include "base/fe_status.h" #include "codegen/context.h" #include "codegen/fn_ir_builder.h" #include "codegen/fn_let_ir_builder.h" @@ -38,6 +39,7 @@ #include "passes/physical/transform_up_physical_pass.h" #include "passes/physical/window_column_pruning.h" #include "plan/planner.h" +#include "proto/fe_common.pb.h" #include "vm/physical_op.h" #include "vm/schemas_context.h" #include "vm/internal/node_helper.h" @@ -822,6 +824,7 @@ Status RequestModeTransformer::OptimizeSimpleProjectAsWindowProducer(PhysicalSim return Status::OK(); } +// From window (t1 last join t2 [ last join t3]...) // Optimize // RequestJoin(Request(left_table), DataSource(right_table)) // -> @@ -905,7 +908,8 @@ Status FixupWindowOverSimpleNLastJoin(const node::WindowPlanNode* w_ptr, const S size_t id = 0; CHECK_STATUS(window_depend_sc->ResolveColumnID(ref->GetDBName(), ref->GetRelationName(), - ref->GetColumnName(), &id)); + ref->GetColumnName(), &id), + "fail to resolve ", ref->GetExprString()); std::string name; CHECK_STATUS( left_join_sc->ResolveColumnNameByID(id, &name), @@ -930,7 +934,8 @@ Status FixupWindowOverSimpleNLastJoin(const node::WindowPlanNode* w_ptr, const S size_t id = 0; CHECK_STATUS(window_depend_sc->ResolveColumnID(ref->GetDBName(), ref->GetRelationName(), - ref->GetColumnName(), &id)); + ref->GetColumnName(), &id), + "fail to resolve ", ref->GetExprString()); std::string name; CHECK_STATUS(left_join_sc->ResolveColumnNameByID(id, &name), "Fail to handle window: window order expression should belong to left table of join"); @@ -1603,28 +1608,12 @@ bool BatchModeTransformer::isSourceFromTableOrPartition(PhysicalOpNode* in) { kPhysicalOpFilter == in->GetOpType()) { return isSourceFromTableOrPartition(in->GetProducer(0)); } else if (kPhysicalOpDataProvider == in->GetOpType()) { - return kProviderTypePartition == - dynamic_cast(in) - ->provider_type_ || - kProviderTypeTable == - dynamic_cast(in)->provider_type_; - } - return false; -} -bool BatchModeTransformer::isSourceFromTable(PhysicalOpNode* in) { - if (nullptr == in) { - DLOG(WARNING) << "Invalid physical node: null"; - return false; - } - if (kPhysicalOpSimpleProject == in->GetOpType() || - kPhysicalOpRename == in->GetOpType()) { - return isSourceFromTableOrPartition(in->GetProducer(0)); - } else if (kPhysicalOpDataProvider == in->GetOpType()) { - return kProviderTypeTable == - dynamic_cast(in)->provider_type_; + return kProviderTypePartition == dynamic_cast(in)->provider_type_ || + kProviderTypeTable == dynamic_cast(in)->provider_type_; } return false; } + Status BatchModeTransformer::ValidateTableProvider(PhysicalOpNode* in) { CHECK_TRUE(nullptr != in, kPlanError, "Invalid physical node: null"); CHECK_TRUE(isSourceFromTableOrPartition(in), kPlanError, @@ -1654,11 +1643,9 @@ Status BatchModeTransformer::ValidatePartitionDataProvider(PhysicalOpNode* in) { CHECK_STATUS(ValidatePartitionDataProvider(in->GetProducer(0))); CHECK_STATUS(ValidatePartitionDataProvider(in->GetProducer(1))); } else { - CHECK_TRUE( - kPhysicalOpDataProvider == in->GetOpType() && - kProviderTypePartition == - dynamic_cast(in)->provider_type_, - kPlanError, "Isn't partition provider:", in->GetTreeString()); + CHECK_TRUE(kPhysicalOpDataProvider == in->GetOpType() && + kProviderTypePartition == dynamic_cast(in)->provider_type_, + kPlanError, "Isn't partition provider:", in->GetTreeString()); } return Status::OK(); } @@ -1795,10 +1782,9 @@ Status BatchModeTransformer::ValidatePlanSupported(const PhysicalOpNode* in) { // - ValidateNotAggregationOverTable Status RequestModeTransformer::ValidatePlan(PhysicalOpNode* node) { CHECK_STATUS(BatchModeTransformer::ValidatePlan(node)) - PhysicalOpNode* primary_source = nullptr; // OnlineServing restriction: Expect to infer one and only one request table from given SQL - CHECK_STATUS(ValidateRequestTable(node, &primary_source), "Fail to validate physical plan") + CHECK_STATUS(ValidateRequestTable(node), "Fail to validate physical plan") // For Online serving, SQL queries should be designed extremely performance-sensitive to satisfy the real-time // requirements. Thus, we need to Validate if the SQL has been optimized well enough @@ -2422,18 +2408,32 @@ Status RequestModeTransformer::TransformScanOp(const node::TablePlanNode* node, return BatchModeTransformer::TransformScanOp(node, output); } } -Status RequestModeTransformer::ValidateRequestTable( - PhysicalOpNode* in, PhysicalOpNode** request_table) { - CHECK_TRUE(in != NULL, kPlanError, "NULL Physical Node"); +Status RequestModeTransformer::ValidateRequestTable(PhysicalOpNode* in) { + auto req = ExtractRequestNode(in); + CHECK_TRUE(req.ok(), kPlanError, req.status()); + + std::set> db_tables; + CHECK_STATUS(internal::GetDependentTables(in, &db_tables)); + CHECK_TRUE(req.value() != nullptr || db_tables.empty(), kPlanError, "no request node found"); + + return Status::OK(); +} + +absl::StatusOr RequestModeTransformer::ExtractRequestNode(PhysicalOpNode* in) { + if (in == nullptr) { + return absl::InvalidArgumentError("null input node"); + } switch (in->GetOpType()) { case vm::kPhysicalOpDataProvider: { - CHECK_TRUE(kProviderTypeRequest == dynamic_cast(in)->provider_type_, kPlanError, - "Expect a request table but a ", - vm::DataProviderTypeName(dynamic_cast(in)->provider_type_), " ", - in->GetTreeString()) - *request_table = in; - return Status::OK(); + auto tp = dynamic_cast(in)->provider_type_; + if (tp == kProviderTypeRequest) { + return in; + } + + // else data provider is fine inside node tree, + // generally it is of type Partition, but can be Table as well e.g window (t1 instance_not_in_window) + return nullptr; } case vm::kPhysicalOpJoin: case vm::kPhysicalOpUnion: @@ -2441,45 +2441,46 @@ Status RequestModeTransformer::ValidateRequestTable( case vm::kPhysicalOpRequestUnion: case vm::kPhysicalOpRequestAggUnion: case vm::kPhysicalOpRequestJoin: { - vm::PhysicalOpNode* left_primary_source = nullptr; - CHECK_STATUS(ValidateRequestTable(in->GetProducer(0), &left_primary_source)) - CHECK_TRUE( - nullptr != left_primary_source, kPlanError, - "Fail to infer a request table") - // Case 1: - // binary_node - // + Left - PrimarySource - // + Right - TableProvider - if (isSourceFromTableOrPartition(in->GetProducer(1))) { - *request_table = left_primary_source; - return Status::OK(); + // Binary Node + // - left or right status not ok -> error + // - left and right both has non-null value + // - the two not equals -> error + // - otherwise -> left as request node + auto left = ExtractRequestNode(in->GetProducer(0)); + if (!left.ok()) { + return left; + } + auto right = ExtractRequestNode(in->GetProducer(1)); + if (!right.ok()) { + return right; } - vm::PhysicalOpNode* right_primary_source = nullptr; - CHECK_STATUS(ValidateRequestTable(in->GetProducer(1), &right_primary_source)) - CHECK_TRUE( - nullptr != right_primary_source, kPlanError, - "Fail to infer a request table") - // Case 2: - // binary_node - // + Left <-- SamePrimarySource1 - // + Right <-- SamePrimarySource1 - CHECK_TRUE(left_primary_source->Equals(right_primary_source), kPlanError, - "left path and right path has different request table") - *request_table = left_primary_source; - return Status::OK(); - } - case vm::kPhysicalOpConstProject: { - break; + if (left.value() != nullptr && right.value() != nullptr) { + if (!left.value()->Equals(right.value())) { + return absl::NotFoundError( + absl::StrCat("different request table from left and right path:\n", in->GetTreeString())); + } + } + + return left.value(); } default: { - CHECK_TRUE(in->GetProducerCnt() == 1, kPlanError, - "Non-support Op ", PhysicalOpTypeName(in->GetOpType())) - CHECK_STATUS(ValidateRequestTable(in->GetProducer(0), request_table)); - return Status::OK(); + break; } } - return Status::OK(); + + if (in->GetProducerCnt() == 0) { + // leaf node excepting DataProdiverNode + // consider ok as right source from one of the supported binary op + return nullptr; + } + + if (in->GetProducerCnt() > 1) { + return absl::UnimplementedError( + absl::StrCat("Non-support op with more than one producer:\n", in->GetTreeString())); + } + + return ExtractRequestNode(in->GetProducer(0)); } // transform a single `ProjectListNode` of `ProjectPlanNode` diff --git a/hybridse/src/vm/transform.h b/hybridse/src/vm/transform.h index 22a7b74c5b9..caaf63b655d 100644 --- a/hybridse/src/vm/transform.h +++ b/hybridse/src/vm/transform.h @@ -141,7 +141,6 @@ class BatchModeTransformer { Status GenRange(Range* sort, const SchemasContext* schemas_ctx); static bool isSourceFromTableOrPartition(PhysicalOpNode* in); - bool isSourceFromTable(PhysicalOpNode* in); std::string ExtractSchemaName(PhysicalOpNode* in); static Status ValidateIndexOptimization(PhysicalOpNode* physical_plan); @@ -319,7 +318,17 @@ class RequestModeTransformer : public BatchModeTransformer { absl::StatusOr ResolveCTERef(absl::string_view tb_name) override; - Status ValidateRequestTable(PhysicalOpNode* in, PhysicalOpNode** request_table); + // Valid the final optimized PhysicalOpNode is either: + // - has one and only one request table + // - do not has any physical table refered + Status ValidateRequestTable(PhysicalOpNode* in); + + // Extract request node of the node tree + // returns + // - Request node on success + // - NULL if tree do not has request table but sufficient as as input tree of the big one + // - Error status otherwise + static absl::StatusOr ExtractRequestNode(PhysicalOpNode* in); private: // Optimize simple project node which is the producer of window project From 88e9e79aa133860fe216c6b0f4eaa1570b35668a Mon Sep 17 00:00:00 2001 From: dl239 Date: Tue, 17 Oct 2023 10:32:24 +0800 Subject: [PATCH 60/63] feat: support show create table (#3500) --- cases/plan/cmd.yaml | 17 +++ .../sql/ddl/SHOW_CREATE_TABLE_STATEMENT.md | 28 ++++ docs/en/reference/sql/ddl/index.rst | 1 + .../ddl/SHOW_CREATE_TABLE_STATEMENT.md | 28 ++++ docs/zh/openmldb_sql/ddl/index.rst | 1 + hybridse/include/node/node_enum.h | 1 + hybridse/src/node/sql_node.cc | 1 + hybridse/src/planv2/ast_node_converter.cc | 1 + src/sdk/CMakeLists.txt | 3 + src/sdk/sdk_util.cc | 96 +++++++++++++ src/sdk/sdk_util.h | 35 +++++ src/sdk/sdk_util_test.cc | 127 ++++++++++++++++++ src/sdk/sql_cluster_router.cc | 39 ++++-- src/sdk/sql_cluster_test.cc | 18 +++ third-party/cmake/FetchZetasql.cmake | 8 +- 15 files changed, 392 insertions(+), 12 deletions(-) create mode 100644 docs/en/reference/sql/ddl/SHOW_CREATE_TABLE_STATEMENT.md create mode 100644 docs/zh/openmldb_sql/ddl/SHOW_CREATE_TABLE_STATEMENT.md create mode 100644 src/sdk/sdk_util.cc create mode 100644 src/sdk/sdk_util.h create mode 100644 src/sdk/sdk_util_test.cc diff --git a/cases/plan/cmd.yaml b/cases/plan/cmd.yaml index 3ca7d89ba6f..50b5fa94343 100644 --- a/cases/plan/cmd.yaml +++ b/cases/plan/cmd.yaml @@ -704,3 +704,20 @@ cases: +-actions: +-0: DropPathAction (12) +-1: AddPathAction (13) + + - id: show-create-table + desc: SHOW CREATE TABLE + sql: SHOW CREATE TABLE t1; + expect: + node_tree_str: | + +-node[CMD] + +-cmd_type: show create table + +-args: [t1] + - id: show-create-table-db + desc: SHOW CREATE TABLE + sql: SHOW CREATE TABLE db1.t1; + expect: + node_tree_str: | + +-node[CMD] + +-cmd_type: show create table + +-args: [db1, t1] diff --git a/docs/en/reference/sql/ddl/SHOW_CREATE_TABLE_STATEMENT.md b/docs/en/reference/sql/ddl/SHOW_CREATE_TABLE_STATEMENT.md new file mode 100644 index 00000000000..dd411410e65 --- /dev/null +++ b/docs/en/reference/sql/ddl/SHOW_CREATE_TABLE_STATEMENT.md @@ -0,0 +1,28 @@ +# SHOW CREATE TABLE + +`SHOW CREATE TABLE` shows the `CREATE TABLE` statement that creates the named table + +**Syntax** + +```sql +SHOW CREATE TABLE table_name; +``` + +**Example** + +```sql +show create table t1; + ------- --------------------------------------------------------------- + Table Create Table + ------- --------------------------------------------------------------- + t1 CREATE TABLE `t1` ( + `c1` varchar, + `c2` int, + `c3` bigInt, + `c4` timestamp, + INDEX (KEY=`c1`, TS=`c4`, TTL_TYPE=ABSOLUTE, TTL=0m) + ) OPTIONS (PARTITIONNUM=8, REPLICANUM=2, STORAGE_MODE='HDD'); + ------- --------------------------------------------------------------- + +1 rows in set +``` \ No newline at end of file diff --git a/docs/en/reference/sql/ddl/index.rst b/docs/en/reference/sql/ddl/index.rst index 09199ec27ba..dbc94cc1f3d 100644 --- a/docs/en/reference/sql/ddl/index.rst +++ b/docs/en/reference/sql/ddl/index.rst @@ -23,3 +23,4 @@ Data Definition Statement (DDL) CREATE_FUNCTION SHOW_FUNCTIONS DROP_FUNCTION + SHOW_CREATE_TABLE_STATEMENT diff --git a/docs/zh/openmldb_sql/ddl/SHOW_CREATE_TABLE_STATEMENT.md b/docs/zh/openmldb_sql/ddl/SHOW_CREATE_TABLE_STATEMENT.md new file mode 100644 index 00000000000..e697f687846 --- /dev/null +++ b/docs/zh/openmldb_sql/ddl/SHOW_CREATE_TABLE_STATEMENT.md @@ -0,0 +1,28 @@ +# SHOW CREATE TABLE + +`SHOW CREATE TABLE` 用来显示指定表的建表语句 + +**Syntax** + +```sql +SHOW CREATE TABLE table_name; +``` + +**Example** + +```sql +show create table t1; + ------- --------------------------------------------------------------- + Table Create Table + ------- --------------------------------------------------------------- + t1 CREATE TABLE `t1` ( + `c1` varchar, + `c2` int, + `c3` bigInt, + `c4` timestamp, + INDEX (KEY=`c1`, TS=`c4`, TTL_TYPE=ABSOLUTE, TTL=0m) + ) OPTIONS (PARTITIONNUM=8, REPLICANUM=2, STORAGE_MODE='HDD'); + ------- --------------------------------------------------------------- + +1 rows in set +``` \ No newline at end of file diff --git a/docs/zh/openmldb_sql/ddl/index.rst b/docs/zh/openmldb_sql/ddl/index.rst index 116b9ce29c3..efd36734261 100644 --- a/docs/zh/openmldb_sql/ddl/index.rst +++ b/docs/zh/openmldb_sql/ddl/index.rst @@ -23,3 +23,4 @@ CREATE_FUNCTION SHOW_FUNCTIONS DROP_FUNCTION + SHOW_CREATE_TABLE_STATEMENT diff --git a/hybridse/include/node/node_enum.h b/hybridse/include/node/node_enum.h index 4fc914799d0..16e18291478 100644 --- a/hybridse/include/node/node_enum.h +++ b/hybridse/include/node/node_enum.h @@ -283,6 +283,7 @@ enum CmdType { kCmdShowFunctions, kCmdDropFunction, kCmdShowJobLog, + kCmdShowCreateTable, kCmdFake, // not a real cmd, for testing purpose only kLastCmd = kCmdFake, }; diff --git a/hybridse/src/node/sql_node.cc b/hybridse/src/node/sql_node.cc index bc5aec55cf9..16b88cd51ba 100644 --- a/hybridse/src/node/sql_node.cc +++ b/hybridse/src/node/sql_node.cc @@ -58,6 +58,7 @@ static absl::flat_hash_map CreateCmdTypeNamesMap() { {CmdType::kCmdDropTable, "drop table"}, {CmdType::kCmdShowProcedures, "show procedures"}, {CmdType::kCmdShowCreateSp, "show create procedure"}, + {CmdType::kCmdShowCreateTable, "show create table"}, {CmdType::kCmdDropSp, "drop procedure"}, {CmdType::kCmdDropIndex, "drop index"}, {CmdType::kCmdExit, "exit"}, diff --git a/hybridse/src/planv2/ast_node_converter.cc b/hybridse/src/planv2/ast_node_converter.cc index 19bb0ccfc6c..c0c3864716b 100644 --- a/hybridse/src/planv2/ast_node_converter.cc +++ b/hybridse/src/planv2/ast_node_converter.cc @@ -2172,6 +2172,7 @@ static const absl::flat_hash_map showTargetMap {"SESSION VARIABLES", {node::CmdType::kCmdShowSessionVariables}}, {"GLOBAL VARIABLES", {node::CmdType::kCmdShowGlobalVariables}}, {"CREATE PROCEDURE", {node::CmdType::kCmdShowCreateSp, true}}, + {"CREATE TABLE", {node::CmdType::kCmdShowCreateTable, true}}, {"DEPLOYMENT", {node::CmdType::kCmdShowDeployment, true}}, {"JOB", {node::CmdType::kCmdShowJob, true}}, {"COMPONENTS", {node::CmdType::kCmdShowComponents}}, diff --git a/src/sdk/CMakeLists.txt b/src/sdk/CMakeLists.txt index cc959f6a23b..28c41e09c52 100644 --- a/src/sdk/CMakeLists.txt +++ b/src/sdk/CMakeLists.txt @@ -26,6 +26,9 @@ if(TESTING_ENABLE) add_executable(db_sdk_test db_sdk_test.cc) target_link_libraries(db_sdk_test ${SDK_TEST_DEPS} ${BIN_LIBS} ${THIRD_LIBS}) + add_executable(sdk_util_test sdk_util_test.cc) + target_link_libraries(sdk_util_test ${SDK_TEST_DEPS} ${BIN_LIBS} ${THIRD_LIBS}) + add_executable(result_set_sql_test result_set_sql_test.cc) target_link_libraries(result_set_sql_test ${SDK_TEST_DEPS} ${BIN_LIBS} ${THIRD_LIBS}) diff --git a/src/sdk/sdk_util.cc b/src/sdk/sdk_util.cc new file mode 100644 index 00000000000..f6027f7c08b --- /dev/null +++ b/src/sdk/sdk_util.cc @@ -0,0 +1,96 @@ +/* + * Copyright 2021 4Paradigm + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "sdk/sdk_util.h" + +#include +#include "codec/schema_codec.h" + +namespace openmldb { +namespace sdk { + +std::string SDKUtil::GenCreateTableSQL(const ::openmldb::nameserver::TableInfo& table_info) { + std::stringstream ss; + ss << "CREATE TABLE `" << table_info.name() << "` (\n"; + for (const auto& column : table_info.column_desc()) { + auto it = openmldb::codec::DATA_TYPE_STR_MAP.find(column.data_type()); + if (it != openmldb::codec::DATA_TYPE_STR_MAP.end()) { + ss << "`" << column.name() << "` " << it->second; + if (column.has_default_value()) { + ss << " DEFAULT '" << column.default_value() << "'"; + } + if (column.not_null()) { + ss << " NOT NULL"; + } + ss << ",\n"; + } + } + int index_cnt = 0; + for (const auto& index : table_info.column_key()) { + if (index.flag() != 0) { + continue; + } + if (index_cnt > 0) { + ss << ",\n"; + } + ss << "INDEX ("; + if (index.col_name_size() == 1) { + ss << "KEY=`" << index.col_name(0) << "`"; + } else { + ss << "KEY=("; + for (int idx = 0; idx < index.col_name_size(); idx++) { + if (idx > 0) { + ss << ","; + } + ss << "`" << index.col_name(idx) << "`"; + } + ss << ")"; + } + if (index.has_ts_name() && !index.ts_name().empty()) { + ss << ", TS=`" << index.ts_name() << "`"; + } + if (index.has_ttl()) { + ss << ", TTL_TYPE="; + if (index.ttl().ttl_type() == openmldb::type::TTLType::kAbsoluteTime) { + ss << "ABSOLUTE, TTL=" << index.ttl().abs_ttl() << "m"; + } else if (index.ttl().ttl_type() == openmldb::type::TTLType::kLatestTime) { + ss << "LATEST, TTL=" << index.ttl().lat_ttl(); + } else if (index.ttl().ttl_type() == openmldb::type::TTLType::kAbsAndLat) { + ss << "ABSANDLAT, TTL=(" << index.ttl().abs_ttl() << "m, " << index.ttl().lat_ttl() << ")"; + } else if (index.ttl().ttl_type() == openmldb::type::TTLType::kAbsOrLat) { + ss << "ABSORLAT, TTL=(" << index.ttl().abs_ttl() << "m, " << index.ttl().lat_ttl() << ")"; + } + } + index_cnt++; + ss << ")"; + } + ss << "\n) "; + ss << "OPTIONS ("; + ss << "PARTITIONNUM=" << table_info.partition_num(); + ss << ", REPLICANUM=" << table_info.replica_num(); + if (table_info.storage_mode() == openmldb::common::StorageMode::kSSD) { + ss << ", STORAGE_MODE='SSD'"; + } else if (table_info.storage_mode() == openmldb::common::StorageMode::kHDD) { + ss << ", STORAGE_MODE='HDD'"; + } else { + ss << ", STORAGE_MODE='Memory'"; + } + ss << ");"; + return ss.str(); +} + +} // namespace sdk +} // namespace openmldb diff --git a/src/sdk/sdk_util.h b/src/sdk/sdk_util.h new file mode 100644 index 00000000000..c3c6df9e407 --- /dev/null +++ b/src/sdk/sdk_util.h @@ -0,0 +1,35 @@ +/* + * Copyright 2021 4Paradigm + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SRC_SDK_SDK_UTIL_H_ +#define SRC_SDK_SDK_UTIL_H_ + +#include + +#include "proto/name_server.pb.h" + +namespace openmldb { +namespace sdk { + +class SDKUtil { + public: + static std::string GenCreateTableSQL(const ::openmldb::nameserver::TableInfo& table_info); +}; + +} // namespace sdk +} // namespace openmldb + +#endif // SRC_SDK_SDK_UTIL_H_ diff --git a/src/sdk/sdk_util_test.cc b/src/sdk/sdk_util_test.cc new file mode 100644 index 00000000000..c214f592cba --- /dev/null +++ b/src/sdk/sdk_util_test.cc @@ -0,0 +1,127 @@ +/* + * Copyright 2021 4Paradigm + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "absl/strings/str_split.h" +#include "absl/strings/numbers.h" +#include "gtest/gtest.h" +#include "codec/schema_codec.h" +#include "sdk/sdk_util.h" + +namespace openmldb { +namespace sdk { + +class SDKUtilTest : public ::testing::Test { + public: + SDKUtilTest() {} + ~SDKUtilTest() {} +}; + +void SetColumnDesc(const std::vector>& col_vec, + ::openmldb::nameserver::TableInfo* table_info) { + table_info->clear_column_desc(); + for (const auto& col : col_vec) { + auto col_desc = table_info->add_column_desc(); + col_desc->set_name(col[0]); + col_desc->set_data_type(codec::DATA_TYPE_MAP.find(col[1])->second); + if (col.size() > 2 && col[2] == "no") { + col_desc->set_not_null(true); + } + if (col.size() > 3 && !col[3].empty()) { + col_desc->set_default_value(col[3]); + } + } +} + +void SetIndex(const std::vector>& index_vec, + ::openmldb::nameserver::TableInfo* table_info) { + table_info->clear_column_key(); + for (const auto& index_val : index_vec) { + auto index = table_info->add_column_key(); + index->set_index_name(index_val[0]); + std::vector list = absl::StrSplit(index_val[1], ","); + for (const auto& name : list) { + index->add_col_name(name); + } + if (!index_val[2].empty()) { + index->set_ts_name(index_val[2]); + } + auto ttl = index->mutable_ttl(); + int abs_ttl; + int lat_ttl; + ASSERT_TRUE(absl::SimpleAtoi(index_val[4], &abs_ttl)); + ASSERT_TRUE(absl::SimpleAtoi(index_val[5], &lat_ttl)); + if (index_val[3] == "absolute") { + ttl->set_ttl_type(openmldb::type::TTLType::kAbsoluteTime); + ttl->set_abs_ttl(abs_ttl); + } else if (index_val[3] == "latest") { + ttl->set_ttl_type(openmldb::type::TTLType::kLatestTime); + ttl->set_lat_ttl(lat_ttl); + } else if (index_val[3] == "absorlat") { + ttl->set_ttl_type(openmldb::type::TTLType::kAbsAndLat); + ttl->set_abs_ttl(abs_ttl); + ttl->set_lat_ttl(lat_ttl); + } else if (index_val[3] == "absandlat") { + ttl->set_ttl_type(openmldb::type::TTLType::kAbsOrLat); + ttl->set_abs_ttl(abs_ttl); + ttl->set_lat_ttl(lat_ttl); + } + } +} + +TEST_F(SDKUtilTest, GenCreateTableSQL) { + ::openmldb::nameserver::TableInfo table_info; + std::vector> col = { {"col1", "string"}, {"col2", "int"}, {"col3", "bigint", "no"}}; + std::vector> index = { {"index1", "col1", "", "absolute", "100", "0"}}; + SetColumnDesc(col, &table_info); + SetIndex(index, &table_info); + table_info.set_replica_num(1); + table_info.set_partition_num(1); + table_info.set_name("t1"); + std::string exp_ddl = "CREATE TABLE `t1` (\n" + "`col1` string,\n" + "`col2` int,\n" + "`col3` bigInt NOT NULL,\n" + "INDEX (KEY=`col1`, TTL_TYPE=ABSOLUTE, TTL=100m)\n" + ") OPTIONS (PARTITIONNUM=1, REPLICANUM=1, STORAGE_MODE='Memory');"; + ASSERT_EQ(SDKUtil::GenCreateTableSQL(table_info), exp_ddl); + std::vector> col1 = { {"col1", "string", "no", "aa"}, + {"col2", "int"}, {"col3", "timestamp"}}; + std::vector> index1 = { {"index1", "col1", "", "absolute", "100", "0"}, + {"index2", "col1,col2", "col3", "absorlat", "100", "10"}}; + SetColumnDesc(col1, &table_info); + SetIndex(index1, &table_info); + table_info.set_replica_num(3); + table_info.set_partition_num(8); + table_info.set_storage_mode(openmldb::common::StorageMode::kHDD); + exp_ddl = "CREATE TABLE `t1` (\n" + "`col1` string DEFAULT 'aa' NOT NULL,\n" + "`col2` int,\n" + "`col3` timestamp,\n" + "INDEX (KEY=`col1`, TTL_TYPE=ABSOLUTE, TTL=100m),\n" + "INDEX (KEY=(`col1`,`col2`), TS=`col3`, TTL_TYPE=ABSANDLAT, TTL=(100m, 10))\n" + ") OPTIONS (PARTITIONNUM=8, REPLICANUM=3, STORAGE_MODE='HDD');"; + ASSERT_EQ(SDKUtil::GenCreateTableSQL(table_info), exp_ddl); +} + +} // namespace sdk +} // namespace openmldb + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/src/sdk/sql_cluster_router.cc b/src/sdk/sql_cluster_router.cc index eea62b508ff..90054f277aa 100644 --- a/src/sdk/sql_cluster_router.cc +++ b/src/sdk/sql_cluster_router.cc @@ -55,6 +55,7 @@ #include "sdk/job_table_helper.h" #include "sdk/node_adapter.h" #include "sdk/result_set_sql.h" +#include "sdk/sdk_util.h" #include "sdk/split.h" #include "udf/udf.h" #include "vm/catalog.h" @@ -1625,15 +1626,37 @@ std::shared_ptr SQLClusterRouter::HandleSQLCmd(const h return ResultSetSQL::MakeResultSet({"Tables"}, values, status); } + case hybridse::node::kCmdShowCreateTable: { + auto& args = cmd_node->GetArgs(); + std::string cur_db = db; + std::string table_name; + if (!ParseNamesFromArgs(db, args, &cur_db, &table_name).IsOK()) { + *status = {StatusCode::kCmdError, msg}; + return {}; + } + if (cur_db.empty()) { + *status = {::hybridse::common::StatusCode::kCmdError, "please enter database first"}; + return {}; + } + auto table = cluster_sdk_->GetTableInfo(cur_db, table_name); + if (table == nullptr) { + *status = {StatusCode::kCmdError, "table " + table_name + " does not exist"}; + return {}; + } + std::string sql = SDKUtil::GenCreateTableSQL(*table); + std::vector> values; + std::vector vec = {table_name, sql}; + values.push_back(std::move(vec)); + return ResultSetSQL::MakeResultSet({"Table", "Create Table"}, values, status); + } + case hybridse::node::kCmdDescTable: { std::string cur_db = db; std::string table_name; const auto& args = cmd_node->GetArgs(); - if (args.size() > 1) { - cur_db = args[0]; - table_name = args[1]; - } else { - table_name = args[0]; + if (!ParseNamesFromArgs(db, args, &cur_db, &table_name).IsOK()) { + *status = {StatusCode::kCmdError, msg}; + return {}; } if (cur_db.empty()) { *status = {::hybridse::common::StatusCode::kCmdError, "please enter database first"}; @@ -2990,17 +3013,17 @@ ::hybridse::sdk::Status SQLClusterRouter::SetVariable(hybridse::node::SetPlanNod ::hybridse::sdk::Status SQLClusterRouter::ParseNamesFromArgs(const std::string& db, const std::vector& args, std::string* db_name, - std::string* sp_name) { + std::string* name) { if (args.size() == 1) { // only sp name, no db_name if (db.empty()) { return {StatusCode::kCmdError, "Please enter database first"}; } *db_name = db; - *sp_name = args[0]; + *name = args[0]; } else if (args.size() == 2) { *db_name = args[0]; - *sp_name = args[1]; + *name = args[1]; } else { return {StatusCode::kCmdError, "Invalid args"}; } diff --git a/src/sdk/sql_cluster_test.cc b/src/sdk/sql_cluster_test.cc index 359ac431573..70b6f7a20f2 100644 --- a/src/sdk/sql_cluster_test.cc +++ b/src/sdk/sql_cluster_test.cc @@ -257,6 +257,24 @@ TEST_F(SQLClusterDDLTest, CreateTableWithDatabase) { ASSERT_TRUE(router->DropDB(db2, &status)); } +TEST_F(SQLClusterDDLTest, ShowCreateTable) { + ::hybridse::sdk::Status status; + ASSERT_TRUE(router->ExecuteDDL(db, "drop table if exists t1;", &status)); + std::string ddl = "CREATE TABLE `t1` (\n" + "`col1` varchar,\n" + "`col2` int,\n" + "`col3` bigInt NOT NULL,\n" + "INDEX (KEY=`col1`, TTL_TYPE=ABSOLUTE, TTL=100m)\n" + ") OPTIONS (PARTITIONNUM=1, REPLICANUM=1, STORAGE_MODE='Memory');"; + ASSERT_TRUE(router->ExecuteDDL(db, ddl, &status)) << "ddl: " << ddl; + ASSERT_TRUE(router->RefreshCatalog()); + auto rs = router->ExecuteSQL(db, "show create table t1;", &status); + ASSERT_TRUE(status.IsOK()) << status.msg; + ASSERT_TRUE(rs->Next()); + ASSERT_EQ(ddl, rs->GetStringUnsafe(1)); + ASSERT_TRUE(router->ExecuteDDL(db, "drop table t1;", &status)); +} + TEST_F(SQLClusterDDLTest, CreateTableWithDatabaseWrongDDL) { std::string name = "test" + GenRand(); ::hybridse::sdk::Status status; diff --git a/third-party/cmake/FetchZetasql.cmake b/third-party/cmake/FetchZetasql.cmake index 09d4f3d6761..b2b1d580593 100644 --- a/third-party/cmake/FetchZetasql.cmake +++ b/third-party/cmake/FetchZetasql.cmake @@ -13,10 +13,10 @@ # limitations under the License. set(ZETASQL_HOME https://github.com/4paradigm/zetasql) -set(ZETASQL_VERSION 0.3.0) -set(ZETASQL_HASH_DARWIN 1b7e9c68d7fee29abf734be57934440b6891d4e80e22d8a92832518914373bea) -set(ZETASQL_HASH_LINUX_UBUNTU 0efb4feb822440e91ccd8c04d3a102cac9730745550168266b3544224fc86a63) -set(ZETASQL_HASH_LINUX_CENTOS 098ecb71b8a3dd7d8c6887d3b2b9306f0a130434f135754fd9930ccb11d80fed) +set(ZETASQL_VERSION 0.3.1) +set(ZETASQL_HASH_DARWIN 48bfdfe5fa91d414b0bf8383f116bc2a1f558c12fa286e49ea5ceede366dfbcf) +set(ZETASQL_HASH_LINUX_UBUNTU 3847ed7a60aeda1192adf7d702076d2db2bd49258992e2af67515a57b8f6f6a6) +set(ZETASQL_HASH_LINUX_CENTOS e73e6259ab2df3ae7289a9ae78600b69a8fbb6e4890d07a1031ccb1e37fa4281) set(ZETASQL_TAG v${ZETASQL_VERSION}) function(init_zetasql_urls) From 9077b86de86c8ffb4da874c1b781be3f26275cb0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 17 Oct 2023 15:40:07 +0800 Subject: [PATCH 61/63] build(deps-dev): bump urllib3 from 1.26.12 to 1.26.17 in /docs (#3538) --- docs/poetry.lock | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/poetry.lock b/docs/poetry.lock index b2e0c52c7c0..fa522bb44da 100644 --- a/docs/poetry.lock +++ b/docs/poetry.lock @@ -670,17 +670,17 @@ test = ["coverage", "pytest", "pytest-cov"] [[package]] name = "urllib3" -version = "1.26.12" +version = "1.26.17" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, <4" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" files = [ - {file = "urllib3-1.26.12-py2.py3-none-any.whl", hash = "sha256:b930dd878d5a8afb066a637fbb35144fe7901e3b209d1cd4f524bd0e9deee997"}, - {file = "urllib3-1.26.12.tar.gz", hash = "sha256:3fa96cf423e6987997fc326ae8df396db2a8b7c667747d47ddd8ecba91f4a74e"}, + {file = "urllib3-1.26.17-py2.py3-none-any.whl", hash = "sha256:94a757d178c9be92ef5539b8840d48dc9cf1b2709c9d6b588232a055c524458b"}, + {file = "urllib3-1.26.17.tar.gz", hash = "sha256:24d6a242c28d29af46c3fae832c36db3bbebcc533dd1bb549172cd739c82df21"}, ] [package.extras] -brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"] +brotli = ["brotli (==1.0.9)", "brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"] secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] From 3bb9df7495b31fff60e14e60ee7a7f93fad3a496 Mon Sep 17 00:00:00 2001 From: aceforeverd Date: Tue, 17 Oct 2023 17:50:28 +0800 Subject: [PATCH 62/63] ci: reduce disk usage for java jobs (#3556) * build(java): dont pack resouces for source-jars Github runner has the limitation of disk usage of 14G, this should help reduce the disk usage on CI. * ci(sdk): reduce cache size for maven 1. don't include local installed openmldb jars, they are not dependency 2. don't fallback cache if key miss-match * fix: rm if dir not exists --- .github/workflows/sdk.yml | 9 +++++---- java/pom.xml | 5 ++++- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/.github/workflows/sdk.yml b/.github/workflows/sdk.yml index ed78524a9f6..8f4dc6bd628 100644 --- a/.github/workflows/sdk.yml +++ b/.github/workflows/sdk.yml @@ -68,8 +68,6 @@ jobs: with: path: ~/.m2/repository key: ${{ runner.os }}-maven-${{ hashFiles('java/**/pom.xml') }} - restore-keys: | - ${{ runner.os }}-maven- - name: prepare release if: github.event_name == 'push' @@ -124,6 +122,7 @@ jobs: - name: maven coverage working-directory: java run: | + rm -rfv ~/.m2/repository/com/4paradigm/ ./mvnw --batch-mode prepare-package ./mvnw --batch-mode scoverage:report @@ -160,8 +159,6 @@ jobs: with: path: ~/.m2/repository key: ${{ runner.os }}-maven-${{ hashFiles('java/**/pom.xml') }} - restore-keys: | - ${{ runner.os }}-maven- - name: Cache thirdparty uses: actions/cache@v3 @@ -236,6 +233,10 @@ jobs: MAVEN_USERNAME: ${{ secrets.OSSRH_USERNAME }} MAVEN_TOKEN: ${{ secrets.OSSRH_TOKEN }} GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} + - name: cleanup + run: | + rm -rfv ~/.m2/repository/com/4paradigm/ + python-sdk: runs-on: ubuntu-latest diff --git a/java/pom.xml b/java/pom.xml index 00c893a4201..42ba84c34b8 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -355,7 +355,7 @@ org.apache.maven.plugins maven-source-plugin - 2.2.1 + 3.3.0 attach-sources @@ -364,6 +364,9 @@ + + true + org.apache.maven.plugins From 190992d3ce02067baab962c5c5bdd0d673503581 Mon Sep 17 00:00:00 2001 From: dl239 Date: Fri, 20 Oct 2023 14:57:55 +0800 Subject: [PATCH 63/63] fix: python mac sdk run failed (#3518) * fix: python mac * fix: fix comment --- onebox/stop_all.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/onebox/stop_all.sh b/onebox/stop_all.sh index 747adcdf929..7ba340228f3 100755 --- a/onebox/stop_all.sh +++ b/onebox/stop_all.sh @@ -17,7 +17,7 @@ set -x -e if [[ "$OSTYPE" = "darwin"* ]]; then - pkill -9 -x -l openmldb + pkill -9 -x -l openmldb || exit 0 else pgrep -a -f "openmldb.*onebox.*" | awk '{print $1}' | xargs -I {} kill -9 {} fi