From ee66fb1c602e17563606c6f6eecc225dac5455cc Mon Sep 17 00:00:00 2001 From: Daniel Lemire Date: Thu, 1 Aug 2019 16:23:30 -0400 Subject: [PATCH] Version 0.2.0. --- CMakeLists.txt | 8 +- include/simdjson/simdjson_version.h | 8 +- singleheader/amalgamation_demo.cpp | 7 +- singleheader/simdjson.cpp | 2167 +++++++++------- singleheader/simdjson.h | 3681 +++++++++++++++------------ tools/release.py | 23 +- 6 files changed, 3215 insertions(+), 2679 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d2da0f6a8..fa3dccf3d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,7 +7,7 @@ endif() # usage: cmake -DSIMDJSON_DISABLE_AVX=on .. option(SIMDJSON_DISABLE_AVX "Forcefully disable AVX even if hardware supports it" OFF) - + set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_MACOSX_RPATH OFF) @@ -19,9 +19,9 @@ endif() project(simdjson) set(SIMDJSON_LIB_NAME simdjson) set(PROJECT_VERSION_MAJOR 0) -set(PROJECT_VERSION_MINOR 1) -set(PROJECT_VERSION_PATCH 2) -set(SIMDJSON_LIB_VERSION "0.1.2" CACHE STRING "simdjson library version") +set(PROJECT_VERSION_MINOR 2) +set(PROJECT_VERSION_PATCH 0) +set(SIMDJSON_LIB_VERSION "0.2.0" CACHE STRING "simdjson library version") set(SIMDJSON_LIB_SOVERSION "0" CACHE STRING "simdjson library soversion") if(NOT MSVC) diff --git a/include/simdjson/simdjson_version.h b/include/simdjson/simdjson_version.h index dc55781e0..60aa32fc5 100644 --- a/include/simdjson/simdjson_version.h +++ b/include/simdjson/simdjson_version.h @@ -2,12 +2,12 @@ // do not change by hand #ifndef SIMDJSON_INCLUDE_SIMDJSON_VERSION #define SIMDJSON_INCLUDE_SIMDJSON_VERSION -#define SIMDJSON_VERSION 0.1.2 +#define SIMDJSON_VERSION 0.2.0 namespace simdjson { enum { - SIMDJSON_VERSION_MAJOR = 0, - SIMDJSON_VERSION_MINOR = 1, - SIMDJSON_VERSION_REVISION = 2 + SIMDJSON_VERSION_MAJOR = 0, + SIMDJSON_VERSION_MINOR = 2, + SIMDJSON_VERSION_REVISION = 0 }; } #endif // SIMDJSON_INCLUDE_SIMDJSON_VERSION diff --git a/singleheader/amalgamation_demo.cpp b/singleheader/amalgamation_demo.cpp index afe83364e..6bd75870c 100644 --- a/singleheader/amalgamation_demo.cpp +++ b/singleheader/amalgamation_demo.cpp @@ -1,13 +1,16 @@ -/* auto-generated on Sun 28 Jul 2019 18:08:49 EDT. Do not edit! */ +/* auto-generated on Thu 1 Aug 2019 16:18:07 EDT. Do not edit! */ #include #include "simdjson.h" #include "simdjson.cpp" int main(int argc, char *argv[]) { + if(argc < 2) { + std::cerr << "Please specify a filename " << std::endl; + } const char * filename = argv[1]; simdjson::padded_string p = simdjson::get_corpus(filename); simdjson::ParsedJson pj = simdjson::build_parsed_json(p); // do the parsing - if( ! pj.isValid() ) { + if( ! pj.is_valid() ) { std::cout << "not valid" << std::endl; } else { std::cout << "valid" << std::endl; diff --git a/singleheader/simdjson.cpp b/singleheader/simdjson.cpp index 2f708d4cd..3ea37a970 100644 --- a/singleheader/simdjson.cpp +++ b/singleheader/simdjson.cpp @@ -1,4 +1,4 @@ -/* auto-generated on Sun 28 Jul 2019 18:08:49 EDT. Do not edit! */ +/* auto-generated on Thu 1 Aug 2019 16:18:07 EDT. Do not edit! */ #include "simdjson.h" /* used for http://dmalloc.com/ Dmalloc - Debug Malloc Library */ @@ -10,63 +10,68 @@ #include namespace simdjson { -const std::map errorStrings = { +const std::map error_strings = { {SUCCESS, "No errors"}, {CAPACITY, "This ParsedJson can't support a document that big"}, {MEMALLOC, "Error allocating memory, we're most likely out of memory"}, {TAPE_ERROR, "Something went wrong while writing to the tape"}, {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'"}, + {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"}, {UNITIALIZED, "Unitialized"}, {EMPTY, "Empty"}, - {UNESCAPED_CHARS, "Within strings, some characters must be escapted, we found unescapted characters"}, - {UNEXPECTED_ERROR, "Unexpected error, consider reporting this problem as you may have found a bug in simdjson"}, + {UNESCAPED_CHARS, "Within strings, some characters must be escapted, we " + "found unescapted characters"}, + {UNEXPECTED_ERROR, "Unexpected error, consider reporting this problem as " + "you may have found a bug in simdjson"}, }; -const std::string& errorMsg(const int errorCode) { - return errorStrings.at(errorCode); -} +const std::string &error_message(const int error_code) { + return error_strings.at(error_code); } +} // namespace simdjson /* end file src/simdjson.cpp */ /* begin file src/jsonioutil.cpp */ -#include #include +#include namespace simdjson { -char * allocate_padded_buffer(size_t length) { - // we could do a simple malloc - //return (char *) malloc(length + SIMDJSON_PADDING); - // However, we might as well align to cache lines... - size_t totalpaddedlength = length + SIMDJSON_PADDING; - char *padded_buffer = aligned_malloc_char(64, totalpaddedlength); - return padded_buffer; +char *allocate_padded_buffer(size_t length) { + // we could do a simple malloc + // return (char *) malloc(length + SIMDJSON_PADDING); + // However, we might as well align to cache lines... + size_t totalpaddedlength = length + SIMDJSON_PADDING; + char *padded_buffer = aligned_malloc_char(64, totalpaddedlength); + return padded_buffer; } -padded_string get_corpus(const std::string& filename) { +padded_string get_corpus(const std::string &filename) { std::FILE *fp = std::fopen(filename.c_str(), "rb"); if (fp != nullptr) { std::fseek(fp, 0, SEEK_END); size_t len = std::ftell(fp); padded_string s(len); - if(s.data() == nullptr) { + if (s.data() == nullptr) { std::fclose(fp); - throw std::runtime_error("could not allocate memory"); + throw std::runtime_error("could not allocate memory"); } std::rewind(fp); size_t readb = std::fread(s.data(), 1, len, fp); std::fclose(fp); - if(readb != len) { - throw std::runtime_error("could not read the data"); + if (readb != len) { + throw std::runtime_error("could not read the data"); } return s; } - throw std::runtime_error("could not load corpus"); -} + throw std::runtime_error("could not load corpus"); } +} // namespace simdjson /* end file src/jsonioutil.cpp */ /* begin file src/jsonminifier.cpp */ #include @@ -108,13 +113,13 @@ 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, }; -size_t jsonminify(const unsigned char *bytes, size_t howmany, - unsigned char *out) { +size_t json_minify(const unsigned char *bytes, size_t how_many, + unsigned char *out) { size_t i = 0, pos = 0; uint8_t quote = 0; uint8_t nonescape = 1; - while (i < howmany) { + while (i < how_many) { unsigned char c = bytes[i]; uint8_t *meta = jump_table + 3 * c; @@ -133,7 +138,6 @@ size_t jsonminify(const unsigned char *bytes, size_t howmany, namespace simdjson { - // some intrinsics are missing under GCC? #ifndef __clang__ #ifndef _MSC_VER @@ -154,8 +158,6 @@ static inline void _mm256_storeu2_m128i(__m128i *__addr_hi, __m128i *__addr_lo, #endif #endif - - // a straightforward comparison of a mask against input. static uint64_t cmp_mask_against_input_mini(__m256i input_lo, __m256i input_hi, __m256i mask) { @@ -167,8 +169,9 @@ static uint64_t cmp_mask_against_input_mini(__m256i input_lo, __m256i input_hi, } // take input from buf and remove useless whitespace, input and output can be -// the same, result is null terminated, return the string length (minus the null termination) -size_t jsonminify(const uint8_t *buf, size_t len, uint8_t *out) { +// the same, result is null terminated, return the string length (minus the null +// termination) +size_t json_minify(const uint8_t *buf, size_t len, uint8_t *out) { // Useful constant masks const uint64_t even_bits = 0x5555555555555555ULL; const uint64_t odd_bits = ~even_bits; @@ -178,11 +181,13 @@ size_t jsonminify(const uint8_t *buf, size_t len, uint8_t *out) { uint64_t prev_iter_inside_quote = 0ULL; // either all zeros or all ones size_t idx = 0; if (len >= 64) { - size_t avxlen = len - 63; + size_t avx_len = len - 63; - for (; idx < avxlen; idx += 64) { - __m256i input_lo = _mm256_loadu_si256(reinterpret_cast(buf + idx + 0)); - __m256i input_hi = _mm256_loadu_si256(reinterpret_cast(buf + idx + 32)); + for (; idx < avx_len; idx += 64) { + __m256i input_lo = + _mm256_loadu_si256(reinterpret_cast(buf + idx + 0)); + __m256i input_hi = + _mm256_loadu_si256(reinterpret_cast(buf + idx + 32)); uint64_t bs_bits = cmp_mask_against_input_mini(input_lo, input_hi, _mm256_set1_epi8('\\')); uint64_t start_edges = bs_bits & ~(bs_bits << 1); @@ -191,8 +196,8 @@ size_t jsonminify(const uint8_t *buf, size_t len, uint8_t *out) { uint64_t odd_starts = start_edges & ~even_start_mask; uint64_t even_carries = bs_bits + even_starts; uint64_t odd_carries; - bool iter_ends_odd_backslash = add_overflow( - bs_bits, odd_starts, &odd_carries); + bool iter_ends_odd_backslash = + add_overflow(bs_bits, odd_starts, &odd_carries); odd_carries |= prev_iter_ends_odd_backslash; prev_iter_ends_odd_backslash = iter_ends_odd_backslash ? 0x1ULL : 0x0ULL; uint64_t even_carry_ends = even_carries & ~bs_bits; @@ -206,7 +211,10 @@ size_t jsonminify(const uint8_t *buf, size_t len, uint8_t *out) { uint64_t quote_mask = _mm_cvtsi128_si64(_mm_clmulepi64_si128( _mm_set_epi64x(0ULL, quote_bits), _mm_set1_epi8(0xFF), 0)); quote_mask ^= prev_iter_inside_quote; - prev_iter_inside_quote = static_cast(static_cast(quote_mask) >> 63);// might be undefined behavior, should be fully defined in C++20, ok according to John Regher from Utah University + prev_iter_inside_quote = static_cast( + static_cast(quote_mask) >> + 63); // might be undefined behavior, should be fully defined in C++20, + // ok according to John Regher from Utah University const __m256i low_nibble_mask = _mm256_setr_epi8( // 0 9 a b c d 16, 0, 0, 0, 0, 0, 0, 0, 0, 8, 12, 1, 2, 9, 0, 0, 16, 0, 0, 0, 0, 0, @@ -232,7 +240,8 @@ size_t jsonminify(const uint8_t *buf, size_t len, uint8_t *out) { __m256i tmp_ws_hi = _mm256_cmpeq_epi8( _mm256_and_si256(v_hi, whitespace_shufti_mask), _mm256_set1_epi8(0)); - uint64_t ws_res_0 = static_cast(_mm256_movemask_epi8(tmp_ws_lo)); + uint64_t ws_res_0 = + static_cast(_mm256_movemask_epi8(tmp_ws_lo)); uint64_t ws_res_1 = _mm256_movemask_epi8(tmp_ws_hi); uint64_t whitespace = ~(ws_res_0 | (ws_res_1 << 32)); whitespace &= ~quote_mask; @@ -244,17 +253,18 @@ size_t jsonminify(const uint8_t *buf, size_t len, uint8_t *out) { int pop2 = hamming((~whitespace) & UINT64_C(0xFFFFFFFF)); int pop3 = hamming((~whitespace) & UINT64_C(0xFFFFFFFFFFFF)); int pop4 = hamming((~whitespace)); - __m256i vmask1 = - _mm256_loadu2_m128i(reinterpret_cast(mask128_epi8) + (mask2 & 0x7FFF), - reinterpret_cast(mask128_epi8) + (mask1 & 0x7FFF)); - __m256i vmask2 = - _mm256_loadu2_m128i(reinterpret_cast(mask128_epi8) + (mask4 & 0x7FFF), - reinterpret_cast(mask128_epi8) + (mask3 & 0x7FFF)); + __m256i vmask1 = _mm256_loadu2_m128i( + reinterpret_cast(mask128_epi8) + (mask2 & 0x7FFF), + reinterpret_cast(mask128_epi8) + (mask1 & 0x7FFF)); + __m256i vmask2 = _mm256_loadu2_m128i( + reinterpret_cast(mask128_epi8) + (mask4 & 0x7FFF), + reinterpret_cast(mask128_epi8) + (mask3 & 0x7FFF)); __m256i result1 = _mm256_shuffle_epi8(input_lo, vmask1); __m256i result2 = _mm256_shuffle_epi8(input_hi, vmask2); - _mm256_storeu2_m128i(reinterpret_cast<__m128i *>(out + pop1), reinterpret_cast<__m128i *>(out), result1); - _mm256_storeu2_m128i(reinterpret_cast<__m128i *>(out + pop3), reinterpret_cast<__m128i *>(out + pop2), - result2); + _mm256_storeu2_m128i(reinterpret_cast<__m128i *>(out + pop1), + reinterpret_cast<__m128i *>(out), result1); + _mm256_storeu2_m128i(reinterpret_cast<__m128i *>(out + pop3), + reinterpret_cast<__m128i *>(out + pop2), result2); out += pop4; } } @@ -264,8 +274,10 @@ size_t jsonminify(const uint8_t *buf, size_t len, uint8_t *out) { uint8_t buffer[64]; memset(buffer, 0, 64); memcpy(buffer, buf + idx, len - idx); - __m256i input_lo = _mm256_loadu_si256(reinterpret_cast(buffer)); - __m256i input_hi = _mm256_loadu_si256(reinterpret_cast(buffer + 32)); + __m256i input_lo = + _mm256_loadu_si256(reinterpret_cast(buffer)); + __m256i input_hi = + _mm256_loadu_si256(reinterpret_cast(buffer + 32)); uint64_t bs_bits = cmp_mask_against_input_mini(input_lo, input_hi, _mm256_set1_epi8('\\')); uint64_t start_edges = bs_bits & ~(bs_bits << 1); @@ -274,10 +286,11 @@ size_t jsonminify(const uint8_t *buf, size_t len, uint8_t *out) { uint64_t odd_starts = start_edges & ~even_start_mask; uint64_t even_carries = bs_bits + even_starts; uint64_t odd_carries; - //bool iter_ends_odd_backslash = - add_overflow( bs_bits, odd_starts, &odd_carries); + // bool iter_ends_odd_backslash = + add_overflow(bs_bits, odd_starts, &odd_carries); odd_carries |= prev_iter_ends_odd_backslash; - //prev_iter_ends_odd_backslash = iter_ends_odd_backslash ? 0x1ULL : 0x0ULL; // we never use it + // prev_iter_ends_odd_backslash = iter_ends_odd_backslash ? 0x1ULL : 0x0ULL; + // // we never use it uint64_t even_carry_ends = even_carries & ~bs_bits; uint64_t odd_carry_ends = odd_carries & ~bs_bits; uint64_t even_start_odd_end = even_carry_ends & odd_bits; @@ -289,7 +302,8 @@ size_t jsonminify(const uint8_t *buf, size_t len, uint8_t *out) { uint64_t quote_mask = _mm_cvtsi128_si64(_mm_clmulepi64_si128( _mm_set_epi64x(0ULL, quote_bits), _mm_set1_epi8(0xFF), 0)); quote_mask ^= prev_iter_inside_quote; - // prev_iter_inside_quote = (uint64_t)((int64_t)quote_mask >> 63);// we don't need this anymore + // prev_iter_inside_quote = (uint64_t)((int64_t)quote_mask >> 63);// we + // don't need this anymore __m256i mask_20 = _mm256_set1_epi8(0x20); // c==32 __m256i mask_70 = @@ -323,129 +337,130 @@ size_t jsonminify(const uint8_t *buf, size_t len, uint8_t *out) { int pop2 = hamming((~whitespace) & UINT64_C(0xFFFFFFFF)); int pop3 = hamming((~whitespace) & UINT64_C(0xFFFFFFFFFFFF)); int pop4 = hamming((~whitespace)); - __m256i vmask1 = - _mm256_loadu2_m128i(reinterpret_cast(mask128_epi8) + (mask2 & 0x7FFF), - reinterpret_cast(mask128_epi8) + (mask1 & 0x7FFF)); - __m256i vmask2 = - _mm256_loadu2_m128i(reinterpret_cast(mask128_epi8) + (mask4 & 0x7FFF), - reinterpret_cast(mask128_epi8) + (mask3 & 0x7FFF)); + __m256i vmask1 = _mm256_loadu2_m128i( + reinterpret_cast(mask128_epi8) + (mask2 & 0x7FFF), + reinterpret_cast(mask128_epi8) + (mask1 & 0x7FFF)); + __m256i vmask2 = _mm256_loadu2_m128i( + reinterpret_cast(mask128_epi8) + (mask4 & 0x7FFF), + reinterpret_cast(mask128_epi8) + (mask3 & 0x7FFF)); __m256i result1 = _mm256_shuffle_epi8(input_lo, vmask1); __m256i result2 = _mm256_shuffle_epi8(input_hi, vmask2); - _mm256_storeu2_m128i(reinterpret_cast<__m128i *>(buffer + pop1), reinterpret_cast<__m128i *>(buffer), - result1); - _mm256_storeu2_m128i(reinterpret_cast<__m128i *>(buffer + pop3), reinterpret_cast<__m128i *>(buffer + pop2), - result2); + _mm256_storeu2_m128i(reinterpret_cast<__m128i *>(buffer + pop1), + reinterpret_cast<__m128i *>(buffer), result1); + _mm256_storeu2_m128i(reinterpret_cast<__m128i *>(buffer + pop3), + reinterpret_cast<__m128i *>(buffer + pop2), result2); memcpy(out, buffer, pop4); out += pop4; } - *out = '\0';// NULL termination + *out = '\0'; // NULL termination return out - initout; } -} +} // namespace simdjson #endif /* end file src/jsonminifier.cpp */ /* begin file src/jsonparser.cpp */ -#ifdef _MSC_VER -#include -#include -#else -#include -#endif namespace simdjson { -architecture find_best_supported_implementation() { - constexpr uint32_t haswell_flags = SIMDExtensions::AVX2 | SIMDExtensions::PCLMULQDQ - | SIMDExtensions::BMI1 | SIMDExtensions::BMI2; - constexpr uint32_t westmere_flags = SIMDExtensions::SSE42 | SIMDExtensions::PCLMULQDQ; +Architecture find_best_supported_implementation() { + constexpr uint32_t haswell_flags = + instruction_set::AVX2 | instruction_set::PCLMULQDQ | + instruction_set::BMI1 | instruction_set::BMI2; + constexpr uint32_t westmere_flags = + instruction_set::SSE42 | instruction_set::PCLMULQDQ; uint32_t supports = detect_supported_architectures(); // Order from best to worst (within architecture) - if ((haswell_flags & supports) == haswell_flags) return architecture::haswell; - if ((westmere_flags & supports) == westmere_flags) return architecture::westmere; - if (SIMDExtensions::NEON & supports) return architecture::arm64; + if ((haswell_flags & supports) == haswell_flags) + return Architecture::HASWELL; + if ((westmere_flags & supports) == westmere_flags) + return Architecture::WESTMERE; + if (instruction_set::NEON) + return Architecture::ARM64; - return architecture::none; + return Architecture::NONE; } // Responsible to select the best json_parse implementation -int json_parse_dispatch(const uint8_t *buf, size_t len, ParsedJson &pj, bool reallocifneeded) { - architecture best_implementation = find_best_supported_implementation(); +int json_parse_dispatch(const uint8_t *buf, size_t len, ParsedJson &pj, + bool realloc_if_needed) { + Architecture best_implementation = find_best_supported_implementation(); // Selecting the best implementation switch (best_implementation) { #ifdef IS_X86_64 - case architecture::haswell: - json_parse_ptr = &json_parse_implementation; + case Architecture::HASWELL: + json_parse_ptr = &json_parse_implementation; break; - case architecture::westmere: - json_parse_ptr = &json_parse_implementation; + case Architecture::WESTMERE: + json_parse_ptr = &json_parse_implementation; break; #endif #ifdef IS_ARM64 - case architecture::arm64: - json_parse_ptr = &json_parse_implementation; + case Architecture::ARM64: + json_parse_ptr = &json_parse_implementation; break; #endif - default : + default: std::cerr << "The processor is not supported by simdjson." << std::endl; return simdjson::UNEXPECTED_ERROR; } - return json_parse_ptr(buf, len, pj, reallocifneeded); + return json_parse_ptr(buf, len, pj, realloc_if_needed); } json_parse_functype *json_parse_ptr = &json_parse_dispatch; WARN_UNUSED -ParsedJson build_parsed_json(const uint8_t *buf, size_t len, bool reallocifneeded) { +ParsedJson build_parsed_json(const uint8_t *buf, size_t len, + bool realloc_if_needed) { ParsedJson pj; - bool ok = pj.allocateCapacity(len); - if(ok) { - json_parse(buf, len, pj, reallocifneeded); + bool ok = pj.allocate_capacity(len); + if (ok) { + json_parse(buf, len, pj, realloc_if_needed); } else { std::cerr << "failure during memory allocation " << std::endl; } return pj; } -} +} // namespace simdjson /* end file src/jsonparser.cpp */ /* begin file src/stage1_find_marks.cpp */ - #ifdef IS_X86_64 TARGET_HASWELL namespace simdjson { -template<> -int find_structural_bits(const uint8_t *buf, size_t len, ParsedJson &pj) { - FIND_STRUCTURAL_BITS(architecture::haswell, buf, len, pj); -} - +template <> +int find_structural_bits(const uint8_t *buf, size_t len, + ParsedJson &pj) { + FIND_STRUCTURAL_BITS(Architecture::HASWELL, buf, len, pj, + simdjson::haswell::flatten_bits); } +} // namespace simdjson UNTARGET_REGION TARGET_WESTMERE namespace simdjson { -template<> -int find_structural_bits(const uint8_t *buf, size_t len, ParsedJson &pj) { - FIND_STRUCTURAL_BITS(architecture::westmere, buf, len, pj); -} - +template <> +int find_structural_bits(const uint8_t *buf, size_t len, + ParsedJson &pj) { + FIND_STRUCTURAL_BITS(Architecture::WESTMERE, buf, len, pj, + simdjson::flatten_bits); } +} // namespace simdjson UNTARGET_REGION #endif - #ifdef IS_ARM64 namespace simdjson { -template<> -inline int find_structural_bits(const uint8_t *buf, size_t len, ParsedJson &pj) { - FIND_STRUCTURAL_BITS(architecture::arm64, buf, len, pj); -} - - +template <> +int find_structural_bits(const uint8_t *buf, size_t len, + ParsedJson &pj) { + FIND_STRUCTURAL_BITS(Architecture::ARM64, buf, len, pj, + simdjson::flatten_bits); } +} // namespace simdjson #endif /* end file src/stage1_find_marks.cpp */ /* begin file src/stage2_build_tape.cpp */ @@ -454,13 +469,13 @@ namespace simdjson { // this macro reads the next structural character, updating idx, i and c. #define UPDATE_CHAR() \ -{ \ - idx = pj.structural_indexes[i++]; \ - c = buf[idx]; \ -} + { \ + idx = pj.structural_indexes[i++]; \ + c = buf[idx]; \ + } #ifdef SIMDJSON_USE_COMPUTED_GOTO -#define SET_GOTO_ARRAY_CONTINUE() pj.ret_address[depth] = &&array_continue; +#define SET_GOTO_ARRAY_CONTINUE() pj.ret_address[depth] = &&array_continue; #define SET_GOTO_OBJECT_CONTINUE() pj.ret_address[depth] = &&object_continue; #define SET_GOTO_START_CONTINUE() pj.ret_address[depth] = &&start_continue; #define GOTO_CONTINUE() goto *pj.ret_address[depth]; @@ -468,937 +483,1147 @@ namespace simdjson { #define SET_GOTO_ARRAY_CONTINUE() pj.ret_address[depth] = 'a'; #define SET_GOTO_OBJECT_CONTINUE() pj.ret_address[depth] = 'o'; #define SET_GOTO_START_CONTINUE() pj.ret_address[depth] = 's'; -#define GOTO_CONTINUE() { \ - if(pj.ret_address[depth] == 'a') { \ - goto array_continue; \ - } else if (pj.ret_address[depth] == 'o') { \ - goto object_continue; \ - } else goto { \ - start_continue; \ - } \ -} -#endif +#define GOTO_CONTINUE() \ + { \ + if (pj.ret_address[depth] == 'a') { \ + goto array_continue; \ + } else if (pj.ret_address[depth] == 'o') { \ + goto object_continue; \ + } else { \ + goto start_continue; \ + } \ + } +#endif /************ * The JSON is parsed to a tape, see the accompanying tape.md file * for documentation. ***********/ -// We need to compile that code for multiple architectures. However, target attributes can be used -// only once by function definition. Huge macro seemed better than huge code duplication. -// int UNIFIED_MACHINE(const uint8_t *buf, size_t len, ParsedJson &pj) -#define UNIFIED_MACHINE(T, buf, len, pj) { \ - if (ALLOW_SAME_PAGE_BUFFER_OVERRUN) { \ - memset((uint8_t*)buf + len, 0, SIMDJSON_PADDING); /* to please valgrind */ \ - } \ - uint32_t i = 0; /* index of the structural character (0,1,2,3...) */ \ - uint32_t idx; /* location of the structural character in the input (buf) */ \ - uint8_t c; /* used to track the (structural) character we are looking at, updated */ \ - /* by UPDATE_CHAR macro */ \ - uint32_t depth = 0; /* could have an arbitrary starting depth */ \ - pj.init(); /* sets isvalid to false */ \ - if(pj.bytecapacity < len) { \ - pj.errorcode = simdjson::CAPACITY; \ - return pj.errorcode; \ - } \ - \ - /*//////////////////////////// START STATE ///////////////////////////// */ \ - SET_GOTO_START_CONTINUE() \ - pj.containing_scope_offset[depth] = pj.get_current_loc(); \ - pj.write_tape(0, 'r'); /* r for root, 0 is going to get overwritten */ \ - /* the root is used, if nothing else, to capture the size of the tape */ \ - depth++; /* everything starts at depth = 1, depth = 0 is just for the root, the root may contain an object, an array or something else. */ \ - if (depth >= pj.depthcapacity) { \ - goto fail; \ - } \ - \ - UPDATE_CHAR(); \ - switch (c) { \ - case '{': \ - pj.containing_scope_offset[depth] = pj.get_current_loc(); \ - SET_GOTO_START_CONTINUE(); \ - depth++; \ - if (depth >= pj.depthcapacity) { \ - goto fail; \ - } \ - pj.write_tape(0, c); /* strangely, moving this to object_begin slows things down */ \ - goto object_begin; \ - case '[': \ - pj.containing_scope_offset[depth] = pj.get_current_loc(); \ - SET_GOTO_START_CONTINUE(); \ - depth++; \ - if (depth >= pj.depthcapacity) { \ - goto fail; \ - } \ - pj.write_tape(0, c); \ - goto array_begin; \ -/* #define SIMDJSON_ALLOWANYTHINGINROOT */ \ - /* A JSON text is a serialized value. Note that certain previous */ \ - /* specifications of JSON constrained a JSON text to be an object or an */ \ - /* array. Implementations that generate only objects or arrays where a */ \ - /* JSON text is called for will be interoperable in the sense that all */ \ - /* implementations will accept these as conforming JSON texts. */ \ - /* https://tools.ietf.org/html/rfc8259 */ \ -/* #ifdef SIMDJSON_ALLOWANYTHINGINROOT */ \ - case '"': { \ - if (!parse_string(buf, len, pj, depth, idx)) { \ - goto fail; \ - } \ - break; \ - } \ - case 't': { \ - /* we need to make a copy to make sure that the string is space terminated. */ \ - /* this only applies to the JSON document made solely of the true value. */ \ - /* this will almost never be called in practice */ \ - char * copy = static_cast(malloc(len + SIMDJSON_PADDING)); \ - if(copy == nullptr) { \ - goto fail; \ - } \ - memcpy(copy, buf, len); \ - copy[len] = ' '; \ - if (!is_valid_true_atom(reinterpret_cast(copy) + idx)) { \ - free(copy); \ - goto fail; \ - } \ - free(copy); \ - pj.write_tape(0, c); \ - break; \ - } \ - case 'f': { \ - /* we need to make a copy to make sure that the string is space terminated. */ \ - /* this only applies to the JSON document made solely of the false value. */ \ - /* this will almost never be called in practice */ \ - char * copy = static_cast(malloc(len + SIMDJSON_PADDING)); \ - if(copy == nullptr) { \ - goto fail; \ - } \ - memcpy(copy, buf, len); \ - copy[len] = ' '; \ - if (!is_valid_false_atom(reinterpret_cast(copy) + idx)) { \ - free(copy); \ - goto fail; \ - } \ - free(copy); \ - pj.write_tape(0, c); \ - break; \ - } \ - case 'n': { \ - /* we need to make a copy to make sure that the string is space terminated. */ \ - /* this only applies to the JSON document made solely of the null value. */ \ - /* this will almost never be called in practice */ \ - char * copy = static_cast(malloc(len + SIMDJSON_PADDING)); \ - if(copy == nullptr) { \ - goto fail; \ - } \ - memcpy(copy, buf, len); \ - copy[len] = ' '; \ - if (!is_valid_null_atom(reinterpret_cast(copy) + idx)) { \ - free(copy); \ - goto fail; \ - } \ - free(copy); \ - pj.write_tape(0, c); \ - break; \ - } \ - case '0': \ - case '1': \ - case '2': \ - case '3': \ - case '4': \ - case '5': \ - case '6': \ - case '7': \ - case '8': \ - case '9': { \ - /* we need to make a copy to make sure that the string is space terminated. */ \ - /* this is done only for JSON documents made of a sole number */ \ - /* this will almost never be called in practice. We terminate with a space */ \ - /* because we do not want to allow NULLs in the middle of a number (whereas a */ \ - /* space in the middle of a number would be identified in stage 1). */ \ - char * copy = static_cast(malloc(len + SIMDJSON_PADDING)); \ - if(copy == nullptr) { \ - goto fail; \ - } \ - memcpy(copy, buf, len); \ - copy[len] = ' '; \ - if (!parse_number(reinterpret_cast(copy), pj, idx, false)) { \ - free(copy); \ - goto fail; \ - } \ - free(copy); \ - break; \ - } \ - case '-': { \ - /* we need to make a copy to make sure that the string is NULL terminated. */ \ - /* this is done only for JSON documents made of a sole number */ \ - /* this will almost never be called in practice */ \ - char * copy = static_cast(malloc(len + SIMDJSON_PADDING)); \ - if(copy == nullptr) { \ - goto fail; \ - } \ - memcpy(copy, buf, len); \ - copy[len] = ' '; \ - if (!parse_number(reinterpret_cast(copy), pj, idx, true)) { \ - free(copy); \ - goto fail; \ - } \ - free(copy); \ - break; \ - } \ -/* #endif // ALLOWANYTHINGINROOT */ \ - default: \ - goto fail; \ - } \ -start_continue: \ - /* the string might not be NULL terminated. */ \ - if(i + 1 == pj.n_structural_indexes) { \ - goto succeed; \ - } else { \ - goto fail; \ - } \ - /*//////////////////////////// OBJECT STATES ///////////////////////////// */ \ - \ -object_begin: \ - UPDATE_CHAR(); \ - switch (c) { \ - case '"': { \ - if (!parse_string(buf, len, pj, depth, idx)) { \ - goto fail; \ - } \ - goto object_key_state; \ - } \ - case '}': \ - goto scope_end; /* could also go to object_continue */ \ - default: \ - goto fail; \ - } \ - \ -object_key_state: \ - UPDATE_CHAR(); \ - if (c != ':') { \ - goto fail; \ - } \ - UPDATE_CHAR(); \ - switch (c) { \ - case '"': { \ - if (!parse_string(buf, len, pj, depth, idx)) { \ - goto fail; \ - } \ - break; \ - } \ - case 't': \ - if (!is_valid_true_atom(buf + idx)) { \ - goto fail; \ - } \ - pj.write_tape(0, c); \ - break; \ - case 'f': \ - if (!is_valid_false_atom(buf + idx)) { \ - goto fail; \ - } \ - pj.write_tape(0, c); \ - break; \ - case 'n': \ - if (!is_valid_null_atom(buf + idx)) { \ - goto fail; \ - } \ - pj.write_tape(0, c); \ - break; \ - case '0': \ - case '1': \ - case '2': \ - case '3': \ - case '4': \ - case '5': \ - case '6': \ - case '7': \ - case '8': \ - case '9': { \ - if (!parse_number(buf, pj, idx, false)) { \ - goto fail; \ - } \ - break; \ - } \ - case '-': { \ - if (!parse_number(buf, pj, idx, true)) { \ - goto fail; \ - } \ - break; \ - } \ - case '{': { \ - pj.containing_scope_offset[depth] = pj.get_current_loc(); \ - pj.write_tape(0, c); /* here the compilers knows what c is so this gets optimized */ \ - /* we have not yet encountered } so we need to come back for it */ \ - SET_GOTO_OBJECT_CONTINUE() \ - /* we found an object inside an object, so we need to increment the depth */ \ - depth++; \ - if (depth >= pj.depthcapacity) { \ - goto fail; \ - } \ - \ - goto object_begin; \ - } \ - case '[': { \ - pj.containing_scope_offset[depth] = pj.get_current_loc(); \ - pj.write_tape(0, c); /* here the compilers knows what c is so this gets optimized */ \ - /* we have not yet encountered } so we need to come back for it */ \ - SET_GOTO_OBJECT_CONTINUE() \ - /* we found an array inside an object, so we need to increment the depth */ \ - depth++; \ - if (depth >= pj.depthcapacity) { \ - goto fail; \ - } \ - goto array_begin; \ - } \ - default: \ - goto fail; \ - } \ - \ -object_continue: \ - UPDATE_CHAR(); \ - switch (c) { \ - case ',': \ - UPDATE_CHAR(); \ - if (c != '"') { \ - goto fail; \ - } else { \ - if (!parse_string(buf, len, pj, depth, idx)) { \ - goto fail; \ - } \ - goto object_key_state; \ - } \ - case '}': \ - goto scope_end; \ - default: \ - goto fail; \ - } \ - \ - /*//////////////////////////// COMMON STATE ///////////////////////////// */ \ - \ -scope_end: \ - /* write our tape location to the header scope */ \ - depth--; \ - pj.write_tape(pj.containing_scope_offset[depth], c); \ - pj.annotate_previousloc(pj.containing_scope_offset[depth], \ - pj.get_current_loc()); \ - /* goto saved_state */ \ - GOTO_CONTINUE() \ - \ - /*//////////////////////////// ARRAY STATES ///////////////////////////// */ \ -array_begin: \ - UPDATE_CHAR(); \ - if (c == ']') { \ - goto scope_end; /* could also go to array_continue */ \ - } \ - \ -main_array_switch: \ - /* we call update char on all paths in, so we can peek at c on the */ \ - /* on paths that can accept a close square brace (post-, and at start) */ \ - switch (c) { \ - case '"': { \ - if (!parse_string(buf, len, pj, depth, idx)) { \ - goto fail; \ - } \ - break; \ - } \ - case 't': \ - if (!is_valid_true_atom(buf + idx)) { \ - goto fail; \ - } \ - pj.write_tape(0, c); \ - break; \ - case 'f': \ - if (!is_valid_false_atom(buf + idx)) { \ - goto fail; \ - } \ - pj.write_tape(0, c); \ - break; \ - case 'n': \ - if (!is_valid_null_atom(buf + idx)) { \ - goto fail; \ - } \ - pj.write_tape(0, c); \ - break; /* goto array_continue; */ \ - \ - case '0': \ - case '1': \ - case '2': \ - case '3': \ - case '4': \ - case '5': \ - case '6': \ - case '7': \ - case '8': \ - case '9': { \ - if (!parse_number(buf, pj, idx, false)) { \ - goto fail; \ - } \ - break; /* goto array_continue; */ \ - } \ - case '-': { \ - if (!parse_number(buf, pj, idx, true)) { \ - goto fail; \ - } \ - break; /* goto array_continue; */ \ - } \ - case '{': { \ - /* we have not yet encountered ] so we need to come back for it */ \ - pj.containing_scope_offset[depth] = pj.get_current_loc(); \ - pj.write_tape(0, c); /* here the compilers knows what c is so this gets optimized */ \ - SET_GOTO_ARRAY_CONTINUE() \ - /* we found an object inside an array, so we need to increment the depth */ \ - depth++; \ - if (depth >= pj.depthcapacity) { \ - goto fail; \ - } \ - \ - goto object_begin; \ - } \ - case '[': { \ - /* we have not yet encountered ] so we need to come back for it */ \ - pj.containing_scope_offset[depth] = pj.get_current_loc(); \ - pj.write_tape(0, c); /* here the compilers knows what c is so this gets optimized */ \ - SET_GOTO_ARRAY_CONTINUE() \ - /* we found an array inside an array, so we need to increment the depth */ \ - depth++; \ - if (depth >= pj.depthcapacity) { \ - goto fail; \ - } \ - goto array_begin; \ - } \ - default: \ - goto fail; \ - } \ - \ -array_continue: \ - UPDATE_CHAR(); \ - switch (c) { \ - case ',': \ - UPDATE_CHAR(); \ - goto main_array_switch; \ - case ']': \ - goto scope_end; \ - default: \ - goto fail; \ - } \ - \ - /*//////////////////////////// FINAL STATES ///////////////////////////// */ \ - \ -succeed: \ - depth --; \ - if(depth != 0) { \ - fprintf(stderr, "internal bug\n"); \ - abort(); \ - } \ - if(pj.containing_scope_offset[depth] != 0) { \ - fprintf(stderr, "internal bug\n"); \ - abort(); \ - } \ - pj.annotate_previousloc(pj.containing_scope_offset[depth], \ - pj.get_current_loc()); \ - pj.write_tape(pj.containing_scope_offset[depth], 'r'); /* r is root */ \ - \ - pj.isvalid = true; \ - pj.errorcode = simdjson::SUCCESS; \ - return pj.errorcode; \ -fail: \ - /* we do not need the next line because this is done by pj.init(), pessimistically. */ \ - /* pj.isvalid = false; */ \ - /* At this point in the code, we have all the time in the world. */ \ - /* Note that we know exactly where we are in the document so we could, */ \ - /* without any overhead on the processing code, report a specific location. */ \ - /* We could even trigger special code paths to assess what happened carefully, */ \ - /* all without any added cost. */ \ - if (depth >= pj.depthcapacity) { \ - pj.errorcode = simdjson::DEPTH_ERROR; \ - return pj.errorcode; \ - } \ - switch(c) { \ - case '"': \ - pj.errorcode = simdjson::STRING_ERROR; \ - return pj.errorcode; \ - case '0': \ - case '1': \ - case '2': \ - case '3': \ - case '4': \ - case '5': \ - case '6': \ - case '7': \ - case '8': \ - case '9': \ - case '-': \ - pj.errorcode = simdjson::NUMBER_ERROR; \ - return pj.errorcode; \ - case 't': \ - pj.errorcode = simdjson::T_ATOM_ERROR; \ - return pj.errorcode; \ - case 'n': \ - pj.errorcode = simdjson::N_ATOM_ERROR; \ - return pj.errorcode; \ - case 'f': \ - pj.errorcode = simdjson::F_ATOM_ERROR; \ - return pj.errorcode; \ - default: \ - break; \ - } \ - pj.errorcode = simdjson::TAPE_ERROR; \ - return pj.errorcode; \ -} \ - - -} +// We need to compile that code for multiple architectures. However, target +// attributes can be used only once by function definition. Huge macro seemed +// better than huge code duplication. int UNIFIED_MACHINE(const uint8_t *buf, +// size_t len, ParsedJson &pj) +#define UNIFIED_MACHINE(T, buf, len, pj) \ + { \ + if (ALLOW_SAME_PAGE_BUFFER_OVERRUN) { \ + memset((uint8_t *)buf + len, 0, \ + SIMDJSON_PADDING); /* to please valgrind */ \ + } \ + uint32_t i = 0; /* index of the structural character (0,1,2,3...) */ \ + uint32_t \ + idx; /* location of the structural character in the input (buf) */ \ + uint8_t c; /* used to track the (structural) character we are looking at, \ + updated */ \ + /* by UPDATE_CHAR macro */ \ + uint32_t depth = 0; /* could have an arbitrary starting depth */ \ + pj.init(); /* sets is_valid to false */ \ + if (pj.byte_capacity < len) { \ + pj.error_code = simdjson::CAPACITY; \ + return pj.error_code; \ + } \ + \ + /*//////////////////////////// START STATE ///////////////////////////// \ + */ \ + SET_GOTO_START_CONTINUE() \ + pj.containing_scope_offset[depth] = pj.get_current_loc(); \ + pj.write_tape(0, 'r'); /* r for root, 0 is going to get overwritten */ \ + /* the root is used, if nothing else, to capture the size of the tape */ \ + depth++; /* everything starts at depth = 1, depth = 0 is just for the \ + root, the root may contain an object, an array or something \ + else. */ \ + if (depth >= pj.depth_capacity) { \ + goto fail; \ + } \ + \ + UPDATE_CHAR(); \ + switch (c) { \ + case '{': \ + pj.containing_scope_offset[depth] = pj.get_current_loc(); \ + SET_GOTO_START_CONTINUE(); \ + depth++; \ + if (depth >= pj.depth_capacity) { \ + goto fail; \ + } \ + pj.write_tape( \ + 0, \ + c); /* strangely, moving this to object_begin slows things down */ \ + goto object_begin; \ + case '[': \ + pj.containing_scope_offset[depth] = pj.get_current_loc(); \ + SET_GOTO_START_CONTINUE(); \ + depth++; \ + if (depth >= pj.depth_capacity) { \ + goto fail; \ + } \ + pj.write_tape(0, c); \ + goto array_begin; \ + /* #define SIMDJSON_ALLOWANYTHINGINROOT \ + * A JSON text is a serialized value. Note that certain previous \ + * specifications of JSON constrained a JSON text to be an object or an \ + * array. Implementations that generate only objects or arrays where a \ + * JSON text is called for will be interoperable in the sense that all \ + * implementations will accept these as conforming JSON texts. \ + * https://tools.ietf.org/html/rfc8259 \ + * #ifdef SIMDJSON_ALLOWANYTHINGINROOT */ \ + case '"': { \ + if (!parse_string(buf, len, pj, depth, idx)) { \ + goto fail; \ + } \ + break; \ + } \ + case 't': { \ + /* we need to make a copy to make sure that the string is space \ + * terminated. \ + * this only applies to the JSON document made solely of the true value. \ + * this will almost never be called in practice */ \ + char *copy = static_cast(malloc(len + SIMDJSON_PADDING)); \ + if (copy == nullptr) { \ + goto fail; \ + } \ + memcpy(copy, buf, len); \ + copy[len] = ' '; \ + if (!is_valid_true_atom(reinterpret_cast(copy) + \ + idx)) { \ + free(copy); \ + goto fail; \ + } \ + free(copy); \ + pj.write_tape(0, c); \ + break; \ + } \ + case 'f': { \ + /* we need to make a copy to make sure that the string is space \ + * terminated. \ + * this only applies to the JSON document made solely of the false \ + * value. \ + * this will almost never be called in practice */ \ + char *copy = static_cast(malloc(len + SIMDJSON_PADDING)); \ + if (copy == nullptr) { \ + goto fail; \ + } \ + memcpy(copy, buf, len); \ + copy[len] = ' '; \ + if (!is_valid_false_atom(reinterpret_cast(copy) + \ + idx)) { \ + free(copy); \ + goto fail; \ + } \ + free(copy); \ + pj.write_tape(0, c); \ + break; \ + } \ + case 'n': { \ + /* we need to make a copy to make sure that the string is space \ + * terminated. \ + * this only applies to the JSON document made solely of the null value. \ + * this will almost never be called in practice */ \ + char *copy = static_cast(malloc(len + SIMDJSON_PADDING)); \ + if (copy == nullptr) { \ + goto fail; \ + } \ + memcpy(copy, buf, len); \ + copy[len] = ' '; \ + if (!is_valid_null_atom(reinterpret_cast(copy) + \ + idx)) { \ + free(copy); \ + goto fail; \ + } \ + free(copy); \ + pj.write_tape(0, c); \ + break; \ + } \ + case '0': \ + case '1': \ + case '2': \ + case '3': \ + case '4': \ + case '5': \ + case '6': \ + case '7': \ + case '8': \ + case '9': { \ + /* we need to make a copy to make sure that the string is space \ + * terminated. \ + * this is done only for JSON documents made of a sole number \ + * this will almost never be called in practice. We terminate with a \ + * space \ + * because we do not want to allow NULLs in the middle of a number \ + * (whereas a \ + * space in the middle of a number would be identified in stage 1). */ \ + char *copy = static_cast(malloc(len + SIMDJSON_PADDING)); \ + if (copy == nullptr) { \ + goto fail; \ + } \ + memcpy(copy, buf, len); \ + copy[len] = ' '; \ + if (!parse_number(reinterpret_cast(copy), pj, idx, \ + false)) { \ + free(copy); \ + goto fail; \ + } \ + free(copy); \ + break; \ + } \ + case '-': { \ + /* we need to make a copy to make sure that the string is NULL \ + * terminated. \ + * this is done only for JSON documents made of a sole number \ + * this will almost never be called in practice */ \ + char *copy = static_cast(malloc(len + SIMDJSON_PADDING)); \ + if (copy == nullptr) { \ + goto fail; \ + } \ + memcpy(copy, buf, len); \ + copy[len] = ' '; \ + if (!parse_number(reinterpret_cast(copy), pj, idx, \ + true)) { \ + free(copy); \ + goto fail; \ + } \ + free(copy); \ + break; \ + } \ + default: \ + goto fail; \ + } \ + start_continue: \ + /* the string might not be NULL terminated. */ \ + if (i + 1 == pj.n_structural_indexes) { \ + goto succeed; \ + } else { \ + goto fail; \ + } \ + /*//////////////////////////// OBJECT STATES ///////////////////////////*/ \ + \ + object_begin: \ + UPDATE_CHAR(); \ + switch (c) { \ + case '"': { \ + if (!parse_string(buf, len, pj, depth, idx)) { \ + goto fail; \ + } \ + goto object_key_state; \ + } \ + case '}': \ + goto scope_end; /* could also go to object_continue */ \ + default: \ + goto fail; \ + } \ + \ + object_key_state: \ + UPDATE_CHAR(); \ + if (c != ':') { \ + goto fail; \ + } \ + UPDATE_CHAR(); \ + switch (c) { \ + case '"': { \ + if (!parse_string(buf, len, pj, depth, idx)) { \ + goto fail; \ + } \ + break; \ + } \ + case 't': \ + if (!is_valid_true_atom(buf + idx)) { \ + goto fail; \ + } \ + pj.write_tape(0, c); \ + break; \ + case 'f': \ + if (!is_valid_false_atom(buf + idx)) { \ + goto fail; \ + } \ + pj.write_tape(0, c); \ + break; \ + case 'n': \ + if (!is_valid_null_atom(buf + idx)) { \ + goto fail; \ + } \ + pj.write_tape(0, c); \ + break; \ + case '0': \ + case '1': \ + case '2': \ + case '3': \ + case '4': \ + case '5': \ + case '6': \ + case '7': \ + case '8': \ + case '9': { \ + if (!parse_number(buf, pj, idx, false)) { \ + goto fail; \ + } \ + break; \ + } \ + case '-': { \ + if (!parse_number(buf, pj, idx, true)) { \ + goto fail; \ + } \ + break; \ + } \ + case '{': { \ + pj.containing_scope_offset[depth] = pj.get_current_loc(); \ + pj.write_tape(0, c); /* here the compilers knows what c is so this gets \ + optimized */ \ + /* we have not yet encountered } so we need to come back for it */ \ + SET_GOTO_OBJECT_CONTINUE() \ + /* we found an object inside an object, so we need to increment the \ + * depth */ \ + depth++; \ + if (depth >= pj.depth_capacity) { \ + goto fail; \ + } \ + \ + goto object_begin; \ + } \ + case '[': { \ + pj.containing_scope_offset[depth] = pj.get_current_loc(); \ + pj.write_tape(0, c); /* here the compilers knows what c is so this gets \ + optimized */ \ + /* we have not yet encountered } so we need to come back for it */ \ + SET_GOTO_OBJECT_CONTINUE() \ + /* we found an array inside an object, so we need to increment the depth \ + */ \ + depth++; \ + if (depth >= pj.depth_capacity) { \ + goto fail; \ + } \ + goto array_begin; \ + } \ + default: \ + goto fail; \ + } \ + \ + object_continue: \ + UPDATE_CHAR(); \ + switch (c) { \ + case ',': \ + UPDATE_CHAR(); \ + if (c != '"') { \ + goto fail; \ + } else { \ + if (!parse_string(buf, len, pj, depth, idx)) { \ + goto fail; \ + } \ + goto object_key_state; \ + } \ + case '}': \ + goto scope_end; \ + default: \ + goto fail; \ + } \ + \ + /*//////////////////////////// COMMON STATE ///////////////////////////*/ \ + \ + scope_end: \ + /* write our tape location to the header scope */ \ + depth--; \ + pj.write_tape(pj.containing_scope_offset[depth], c); \ + pj.annotate_previous_loc(pj.containing_scope_offset[depth], \ + pj.get_current_loc()); \ + /* goto saved_state */ \ + GOTO_CONTINUE() \ + \ + /*//////////////////////////// ARRAY STATES ///////////////////////////*/ \ + array_begin: \ + UPDATE_CHAR(); \ + if (c == ']') { \ + goto scope_end; /* could also go to array_continue */ \ + } \ + \ + main_array_switch: \ + /* we call update char on all paths in, so we can peek at c on the \ + * on paths that can accept a close square brace (post-, and at start) */ \ + switch (c) { \ + case '"': { \ + if (!parse_string(buf, len, pj, depth, idx)) { \ + goto fail; \ + } \ + break; \ + } \ + case 't': \ + if (!is_valid_true_atom(buf + idx)) { \ + goto fail; \ + } \ + pj.write_tape(0, c); \ + break; \ + case 'f': \ + if (!is_valid_false_atom(buf + idx)) { \ + goto fail; \ + } \ + pj.write_tape(0, c); \ + break; \ + case 'n': \ + if (!is_valid_null_atom(buf + idx)) { \ + goto fail; \ + } \ + pj.write_tape(0, c); \ + break; /* goto array_continue; */ \ + \ + case '0': \ + case '1': \ + case '2': \ + case '3': \ + case '4': \ + case '5': \ + case '6': \ + case '7': \ + case '8': \ + case '9': { \ + if (!parse_number(buf, pj, idx, false)) { \ + goto fail; \ + } \ + break; /* goto array_continue; */ \ + } \ + case '-': { \ + if (!parse_number(buf, pj, idx, true)) { \ + goto fail; \ + } \ + break; /* goto array_continue; */ \ + } \ + case '{': { \ + /* we have not yet encountered ] so we need to come back for it */ \ + pj.containing_scope_offset[depth] = pj.get_current_loc(); \ + pj.write_tape(0, c); /* here the compilers knows what c is so this gets \ + optimized */ \ + SET_GOTO_ARRAY_CONTINUE() \ + /* we found an object inside an array, so we need to increment the depth \ + */ \ + depth++; \ + if (depth >= pj.depth_capacity) { \ + goto fail; \ + } \ + \ + goto object_begin; \ + } \ + case '[': { \ + /* we have not yet encountered ] so we need to come back for it */ \ + pj.containing_scope_offset[depth] = pj.get_current_loc(); \ + pj.write_tape(0, c); /* here the compilers knows what c is so this gets \ + optimized */ \ + SET_GOTO_ARRAY_CONTINUE() \ + /* we found an array inside an array, so we need to increment the depth \ + */ \ + depth++; \ + if (depth >= pj.depth_capacity) { \ + goto fail; \ + } \ + goto array_begin; \ + } \ + default: \ + goto fail; \ + } \ + \ + array_continue: \ + UPDATE_CHAR(); \ + switch (c) { \ + case ',': \ + UPDATE_CHAR(); \ + goto main_array_switch; \ + case ']': \ + goto scope_end; \ + default: \ + goto fail; \ + } \ + \ + /*//////////////////////////// FINAL STATES ///////////////////////////*/ \ + \ + succeed: \ + depth--; \ + if (depth != 0) { \ + fprintf(stderr, "internal bug\n"); \ + abort(); \ + } \ + if (pj.containing_scope_offset[depth] != 0) { \ + fprintf(stderr, "internal bug\n"); \ + abort(); \ + } \ + pj.annotate_previous_loc(pj.containing_scope_offset[depth], \ + pj.get_current_loc()); \ + pj.write_tape(pj.containing_scope_offset[depth], 'r'); /* r is root */ \ + \ + pj.valid = true; \ + pj.error_code = simdjson::SUCCESS; \ + return pj.error_code; \ + fail: \ + /* we do not need the next line because this is done by pj.init(), \ + * pessimistically. \ + * pj.is_valid = false; \ + * At this point in the code, we have all the time in the world. \ + * Note that we know exactly where we are in the document so we could, \ + * without any overhead on the processing code, report a specific \ + * location. \ + * We could even trigger special code paths to assess what happened \ + * carefully, \ + * all without any added cost. */ \ + if (depth >= pj.depth_capacity) { \ + pj.error_code = simdjson::DEPTH_ERROR; \ + return pj.error_code; \ + } \ + switch (c) { \ + case '"': \ + pj.error_code = simdjson::STRING_ERROR; \ + return pj.error_code; \ + case '0': \ + case '1': \ + case '2': \ + case '3': \ + case '4': \ + case '5': \ + case '6': \ + case '7': \ + case '8': \ + case '9': \ + case '-': \ + pj.error_code = simdjson::NUMBER_ERROR; \ + return pj.error_code; \ + case 't': \ + pj.error_code = simdjson::T_ATOM_ERROR; \ + return pj.error_code; \ + case 'n': \ + pj.error_code = simdjson::N_ATOM_ERROR; \ + return pj.error_code; \ + case 'f': \ + pj.error_code = simdjson::F_ATOM_ERROR; \ + return pj.error_code; \ + default: \ + break; \ + } \ + pj.error_code = simdjson::TAPE_ERROR; \ + return pj.error_code; \ + } +} // namespace simdjson #ifdef IS_X86_64 TARGET_HASWELL namespace simdjson { -template<> -WARN_UNUSED ALLOW_SAME_PAGE_BUFFER_OVERRUN_QUALIFIER LENIENT_MEM_SANITIZER -int unified_machine(const uint8_t *buf, size_t len, ParsedJson &pj) { - UNIFIED_MACHINE(architecture::haswell, buf, len, pj); -} +template <> +WARN_UNUSED ALLOW_SAME_PAGE_BUFFER_OVERRUN_QUALIFIER LENIENT_MEM_SANITIZER int +unified_machine(const uint8_t *buf, size_t len, + ParsedJson &pj) { + UNIFIED_MACHINE(Architecture::HASWELL, buf, len, pj); } +} // namespace simdjson UNTARGET_REGION TARGET_WESTMERE namespace simdjson { -template<> -WARN_UNUSED ALLOW_SAME_PAGE_BUFFER_OVERRUN_QUALIFIER LENIENT_MEM_SANITIZER -int unified_machine(const uint8_t *buf, size_t len, ParsedJson &pj) { - UNIFIED_MACHINE(architecture::westmere, buf, len, pj); -} +template <> +WARN_UNUSED ALLOW_SAME_PAGE_BUFFER_OVERRUN_QUALIFIER LENIENT_MEM_SANITIZER int +unified_machine(const uint8_t *buf, size_t len, + ParsedJson &pj) { + UNIFIED_MACHINE(Architecture::WESTMERE, buf, len, pj); } +} // namespace simdjson UNTARGET_REGION #endif // IS_X86_64 #ifdef IS_ARM64 namespace simdjson { -template<> -WARN_UNUSED ALLOW_SAME_PAGE_BUFFER_OVERRUN_QUALIFIER LENIENT_MEM_SANITIZER -int unified_machine(const uint8_t *buf, size_t len, ParsedJson &pj) { - UNIFIED_MACHINE(architecture::arm64, buf, len, pj); -} +template <> +WARN_UNUSED ALLOW_SAME_PAGE_BUFFER_OVERRUN_QUALIFIER LENIENT_MEM_SANITIZER int +unified_machine(const uint8_t *buf, size_t len, + ParsedJson &pj) { + UNIFIED_MACHINE(Architecture::ARM64, buf, len, pj); } +} // namespace simdjson #endif - - /* end file src/stage2_build_tape.cpp */ /* begin file src/parsedjson.cpp */ namespace simdjson { -ParsedJson::ParsedJson() : - structural_indexes(nullptr), tape(nullptr), containing_scope_offset(nullptr), - ret_address(nullptr), string_buf(nullptr), current_string_buf_loc(nullptr) {} +ParsedJson::ParsedJson() + : structural_indexes(nullptr), tape(nullptr), + containing_scope_offset(nullptr), ret_address(nullptr), + string_buf(nullptr), current_string_buf_loc(nullptr) {} -ParsedJson::~ParsedJson() { - deallocate(); +ParsedJson::~ParsedJson() { deallocate(); } + +ParsedJson::ParsedJson(ParsedJson &&p) + : byte_capacity(p.byte_capacity), depth_capacity(p.depth_capacity), + tape_capacity(p.tape_capacity), string_capacity(p.string_capacity), + current_loc(p.current_loc), n_structural_indexes(p.n_structural_indexes), + structural_indexes(p.structural_indexes), tape(p.tape), + containing_scope_offset(p.containing_scope_offset), + ret_address(p.ret_address), string_buf(p.string_buf), + current_string_buf_loc(p.current_string_buf_loc), valid(p.valid) { + p.structural_indexes = nullptr; + p.tape = nullptr; + p.containing_scope_offset = nullptr; + p.ret_address = nullptr; + p.string_buf = nullptr; + p.current_string_buf_loc = nullptr; } -ParsedJson::ParsedJson(ParsedJson && p) - : bytecapacity(p.bytecapacity), - depthcapacity(p.depthcapacity), - tapecapacity(p.tapecapacity), - stringcapacity(p.stringcapacity), - current_loc(p.current_loc), - n_structural_indexes(p.n_structural_indexes), - structural_indexes(p.structural_indexes), - tape(p.tape), - containing_scope_offset(p.containing_scope_offset), - ret_address(p.ret_address), - string_buf(p.string_buf), - current_string_buf_loc(p.current_string_buf_loc), - isvalid(p.isvalid) { - p.structural_indexes=nullptr; - p.tape=nullptr; - p.containing_scope_offset=nullptr; - p.ret_address=nullptr; - p.string_buf=nullptr; - p.current_string_buf_loc=nullptr; - } - - - WARN_UNUSED -bool ParsedJson::allocateCapacity(size_t len, size_t maxdepth) { - if ((maxdepth == 0) || (len == 0)) { - std::cerr << "capacities must be non-zero " << std::endl; - return false; - } - if(len > SIMDJSON_MAXSIZE_BYTES) { - return false; - } - if ((len <= bytecapacity) && (depthcapacity < maxdepth)) { - return true; - } - deallocate(); - isvalid = false; - bytecapacity = 0; // will only set it to len after allocations are a success - n_structural_indexes = 0; - uint32_t max_structures = ROUNDUP_N(len, 64) + 2 + 7; - structural_indexes = new (std::nothrow) uint32_t[max_structures]; - // a pathological input like "[[[[..." would generate len tape elements, so need a capacity of len + 1 - size_t localtapecapacity = ROUNDUP_N(len + 1, 64); - // a document with only zero-length strings... could have len/3 string - // and we would need len/3 * 5 bytes on the string buffer - size_t localstringcapacity = ROUNDUP_N(5 * len / 3 + 32, 64); - string_buf = new (std::nothrow) uint8_t[localstringcapacity]; - tape = new (std::nothrow) uint64_t[localtapecapacity]; - containing_scope_offset = new (std::nothrow) uint32_t[maxdepth]; -#ifdef SIMDJSON_USE_COMPUTED_GOTO - ret_address = new (std::nothrow) void *[maxdepth]; -#else - ret_address = new (std::nothrow) char[maxdepth]; -#endif - if ((string_buf == nullptr) || (tape == nullptr) || - (containing_scope_offset == nullptr) || (ret_address == nullptr) || (structural_indexes == nullptr)) { - std::cerr << "Could not allocate memory" << std::endl; - delete[] ret_address; - delete[] containing_scope_offset; - delete[] tape; - delete[] string_buf; - delete[] structural_indexes; - - return false; - } - /* - // We do not need to initialize this content for parsing, though we could - // need to initialize it for safety. - memset(string_buf, 0 , localstringcapacity); - memset(structural_indexes, 0, max_structures * sizeof(uint32_t)); - memset(tape, 0, localtapecapacity * sizeof(uint64_t)); - */ - bytecapacity = len; - depthcapacity = maxdepth; - tapecapacity = localtapecapacity; - stringcapacity = localstringcapacity; +bool ParsedJson::allocate_capacity(size_t len, size_t max_depth) { + if (max_depth <= 0) { + max_depth = 1; // don't let the user allocate nothing + } + if (len <= 0) { + len = 64; // allocating 0 bytes is wasteful. + } + if (len > SIMDJSON_MAXSIZE_BYTES) { + return false; + } + if ((len <= byte_capacity) && (depth_capacity < max_depth)) { return true; -} - -bool ParsedJson::isValid() const { - return isvalid; -} - -int ParsedJson::getErrorCode() const { - return errorcode; -} - -std::string ParsedJson::getErrorMsg() const { - return errorMsg(errorcode); -} - -void ParsedJson::deallocate() { - bytecapacity = 0; - depthcapacity = 0; - tapecapacity = 0; - stringcapacity = 0; + } + deallocate(); + valid = false; + byte_capacity = 0; // will only set it to len after allocations are a success + n_structural_indexes = 0; + uint32_t max_structures = ROUNDUP_N(len, 64) + 2 + 7; + structural_indexes = new (std::nothrow) uint32_t[max_structures]; + // a pathological input like "[[[[..." would generate len tape elements, so + // need a capacity of len + 1 + size_t local_tape_capacity = ROUNDUP_N(len + 1, 64); + // a document with only zero-length strings... could have len/3 string + // and we would need len/3 * 5 bytes on the string buffer + size_t local_string_capacity = ROUNDUP_N(5 * len / 3 + 32, 64); + string_buf = new (std::nothrow) uint8_t[local_string_capacity]; + tape = new (std::nothrow) uint64_t[local_tape_capacity]; + containing_scope_offset = new (std::nothrow) uint32_t[max_depth]; +#ifdef SIMDJSON_USE_COMPUTED_GOTO + ret_address = new (std::nothrow) void *[max_depth]; +#else + ret_address = new (std::nothrow) char[max_depth]; +#endif + if ((string_buf == nullptr) || (tape == nullptr) || + (containing_scope_offset == nullptr) || (ret_address == nullptr) || + (structural_indexes == nullptr)) { + std::cerr << "Could not allocate memory" << std::endl; delete[] ret_address; delete[] containing_scope_offset; delete[] tape; delete[] string_buf; delete[] structural_indexes; - isvalid = false; + + return false; + } + /* + // We do not need to initialize this content for parsing, though we could + // need to initialize it for safety. + memset(string_buf, 0 , local_string_capacity); + memset(structural_indexes, 0, max_structures * sizeof(uint32_t)); + memset(tape, 0, local_tape_capacity * sizeof(uint64_t)); + */ + byte_capacity = len; + depth_capacity = max_depth; + tape_capacity = local_tape_capacity; + string_capacity = local_string_capacity; + return true; +} + +bool ParsedJson::is_valid() const { return valid; } + +int ParsedJson::get_error_code() const { return error_code; } + +std::string ParsedJson::get_error_message() const { + return error_message(error_code); +} + +void ParsedJson::deallocate() { + byte_capacity = 0; + depth_capacity = 0; + tape_capacity = 0; + string_capacity = 0; + delete[] ret_address; + delete[] containing_scope_offset; + delete[] tape; + delete[] string_buf; + delete[] structural_indexes; + valid = false; } void ParsedJson::init() { - current_string_buf_loc = string_buf; - current_loc = 0; - isvalid = false; + current_string_buf_loc = string_buf; + current_loc = 0; + valid = false; } WARN_UNUSED -bool ParsedJson::printjson(std::ostream &os) { - if(!isvalid) { - return false; - } - uint32_t string_length; - size_t tapeidx = 0; - uint64_t tape_val = tape[tapeidx]; - uint8_t type = (tape_val >> 56); - size_t howmany = 0; - if (type == 'r') { - howmany = tape_val & JSONVALUEMASK; - } else { - fprintf(stderr, "Error: no starting root node?"); - return false; - } - if (howmany > tapecapacity) { - fprintf(stderr, - "We may be exceeding the tape capacity. Is this a valid document?\n"); - return false; - } - tapeidx++; - bool *inobject = new bool[depthcapacity]; - auto *inobjectidx = new size_t[depthcapacity]; - int depth = 1; // only root at level 0 - inobjectidx[depth] = 0; - inobject[depth] = false; - for (; tapeidx < howmany; tapeidx++) { - tape_val = tape[tapeidx]; - uint64_t payload = tape_val & JSONVALUEMASK; - type = (tape_val >> 56); - if (!inobject[depth]) { - if ((inobjectidx[depth] > 0) && (type != ']')) { - os << ","; - } - inobjectidx[depth]++; - } else { // if (inobject) { - if ((inobjectidx[depth] > 0) && ((inobjectidx[depth] & 1) == 0) && - (type != '}')) { - os << ","; - } - if (((inobjectidx[depth] & 1) == 1)) { - os << ":"; - } - inobjectidx[depth]++; +bool ParsedJson::print_json(std::ostream &os) { + if (!valid) { + return false; + } + uint32_t string_length; + size_t tape_idx = 0; + uint64_t tape_val = tape[tape_idx]; + uint8_t type = (tape_val >> 56); + size_t how_many = 0; + if (type == 'r') { + how_many = tape_val & JSON_VALUE_MASK; + } else { + fprintf(stderr, "Error: no starting root node?"); + return false; + } + if (how_many > tape_capacity) { + fprintf( + stderr, + "We may be exceeding the tape capacity. Is this a valid document?\n"); + return false; + } + tape_idx++; + bool *in_object = new bool[depth_capacity]; + auto *in_object_idx = new size_t[depth_capacity]; + int depth = 1; // only root at level 0 + in_object_idx[depth] = 0; + in_object[depth] = false; + for (; tape_idx < how_many; tape_idx++) { + tape_val = tape[tape_idx]; + uint64_t payload = tape_val & JSON_VALUE_MASK; + type = (tape_val >> 56); + if (!in_object[depth]) { + if ((in_object_idx[depth] > 0) && (type != ']')) { + os << ","; } - switch (type) { - case '"': // we have a string - os << '"'; - memcpy(&string_length,string_buf + payload, sizeof(uint32_t)); - print_with_escapes((const unsigned char *)(string_buf + payload + sizeof(uint32_t)), string_length); - os << '"'; - break; - case 'l': // we have a long int - if (tapeidx + 1 >= howmany) { - delete[] inobject; - delete[] inobjectidx; - return false; - } - os << static_cast(tape[++tapeidx]); - break; - case 'd': // we have a double - if (tapeidx + 1 >= howmany){ - delete[] inobject; - delete[] inobjectidx; - return false; - } - double answer; - memcpy(&answer, &tape[++tapeidx], sizeof(answer)); - os << answer; - 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 - os << '{'; - depth++; - inobject[depth] = true; - inobjectidx[depth] = 0; - break; - case '}': // we end an object - depth--; - os << '}'; - break; - case '[': // we start an array - os << '['; - depth++; - inobject[depth] = false; - inobjectidx[depth] = 0; - break; - case ']': // we end an array - depth--; - os << ']'; - break; - case 'r': // we start and end with the root node - fprintf(stderr, "should we be hitting the root node?\n"); - delete[] inobject; - delete[] inobjectidx; - return false; - default: - fprintf(stderr, "bug %c\n", type); - delete[] inobject; - delete[] inobjectidx; + in_object_idx[depth]++; + } else { // if (in_object) { + if ((in_object_idx[depth] > 0) && ((in_object_idx[depth] & 1) == 0) && + (type != '}')) { + os << ","; + } + if (((in_object_idx[depth] & 1) == 1)) { + os << ":"; + } + in_object_idx[depth]++; + } + switch (type) { + case '"': // we have a string + os << '"'; + memcpy(&string_length, string_buf + payload, sizeof(uint32_t)); + print_with_escapes( + (const unsigned char *)(string_buf + payload + sizeof(uint32_t)), + string_length); + os << '"'; + break; + case 'l': // we have a long int + if (tape_idx + 1 >= how_many) { + delete[] in_object; + delete[] in_object_idx; return false; } + os << static_cast(tape[++tape_idx]); + break; + case 'd': // we have a double + if (tape_idx + 1 >= how_many) { + delete[] in_object; + delete[] in_object_idx; + return false; + } + double answer; + memcpy(&answer, &tape[++tape_idx], sizeof(answer)); + os << answer; + 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 + os << '{'; + depth++; + in_object[depth] = true; + in_object_idx[depth] = 0; + break; + case '}': // we end an object + depth--; + os << '}'; + break; + case '[': // we start an array + os << '['; + depth++; + in_object[depth] = false; + in_object_idx[depth] = 0; + break; + case ']': // we end an array + depth--; + os << ']'; + break; + case 'r': // we start and end with the root node + fprintf(stderr, "should we be hitting the root node?\n"); + delete[] in_object; + delete[] in_object_idx; + return false; + default: + fprintf(stderr, "bug %c\n", type); + delete[] in_object; + delete[] in_object_idx; + return false; } - delete[] inobject; - delete[] inobjectidx; - return true; + } + delete[] in_object; + delete[] in_object_idx; + return true; } WARN_UNUSED bool ParsedJson::dump_raw_tape(std::ostream &os) { - if(!isvalid) { - return false; - } - uint32_t string_length; - size_t tapeidx = 0; - uint64_t tape_val = tape[tapeidx]; - uint8_t type = (tape_val >> 56); - os << tapeidx << " : " << type; - tapeidx++; - size_t howmany = 0; - if (type == 'r') { - howmany = tape_val & JSONVALUEMASK; - } else { - fprintf(stderr, "Error: no starting root node?"); - return false; - } - os << "\t// pointing to " << howmany <<" (right after last node)\n"; - uint64_t payload; - for (; tapeidx < howmany; tapeidx++) { - os << tapeidx << " : "; - tape_val = tape[tapeidx]; - payload = tape_val & JSONVALUEMASK; - type = (tape_val >> 56); - switch (type) { - case '"': // we have a string - os << "string \""; - memcpy(&string_length,string_buf + payload, sizeof(uint32_t)); - print_with_escapes((const unsigned char *)(string_buf + payload + sizeof(uint32_t)), string_length); - os << '"'; - os << '\n'; - break; - case 'l': // we have a long int - if (tapeidx + 1 >= howmany) { - return false; - } - os << "integer " << static_cast(tape[++tapeidx]) << "\n"; - break; - case 'd': // we have a double - os << "float "; - if (tapeidx + 1 >= howmany) { - return false; - } - double answer; - memcpy(&answer, &tape[++tapeidx], 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 " << payload << " (first node after the scope) \n"; - break; - case '}': // we end an object - os << "}\t// pointing to previous tape location " << payload << " (start of the scope) \n"; - break; - case '[': // we start an array - os << "[\t// pointing to next tape location " << payload << " (first node after the scope) \n"; - break; - case ']': // we end an array - os << "]\t// pointing to previous tape location " << payload << " (start of the scope) \n"; - break; - case 'r': // we start and end with the root node - printf("end of root\n"); - return false; - default: + if (!valid) { + return false; + } + uint32_t string_length; + size_t tape_idx = 0; + uint64_t tape_val = tape[tape_idx]; + uint8_t type = (tape_val >> 56); + os << tape_idx << " : " << type; + tape_idx++; + size_t how_many = 0; + if (type == 'r') { + how_many = tape_val & JSON_VALUE_MASK; + } else { + fprintf(stderr, "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 & JSON_VALUE_MASK; + type = (tape_val >> 56); + switch (type) { + case '"': // we have a string + os << "string \""; + memcpy(&string_length, string_buf + payload, sizeof(uint32_t)); + print_with_escapes( + (const unsigned char *)(string_buf + 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 'd': // we have a double + os << "float "; + if (tape_idx + 1 >= how_many) { + return false; + } + double answer; + 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 " << payload + << " (first node after the scope) \n"; + break; + case '}': // we end an object + os << "}\t// pointing to previous tape location " << payload + << " (start of the scope) \n"; + break; + case '[': // we start an array + os << "[\t// pointing to next tape location " << payload + << " (first node after the scope) \n"; + break; + case ']': // we end an array + os << "]\t// pointing to previous tape location " << payload + << " (start of the scope) \n"; + break; + case 'r': // we start and end with the root node + printf("end of root\n"); + return false; + default: + return false; } - tape_val = tape[tapeidx]; - payload = tape_val & JSONVALUEMASK; - type = (tape_val >> 56); - os << tapeidx << " : "<< type <<"\t// pointing to " << payload <<" (start root)\n"; - return true; -} + } + tape_val = tape[tape_idx]; + payload = tape_val & JSON_VALUE_MASK; + type = (tape_val >> 56); + os << tape_idx << " : " << type << "\t// pointing to " << payload + << " (start root)\n"; + return true; } +} // namespace simdjson /* end file src/parsedjson.cpp */ /* begin file src/parsedjsoniterator.cpp */ #include namespace simdjson { -ParsedJson::iterator::iterator(ParsedJson &pj_) : pj(pj_), depth(0), location(0), tape_length(0), depthindex(nullptr) { - if(!pj.isValid()) { - throw InvalidJSON(); - } - depthindex = new scopeindex_t[pj.depthcapacity]; - // memory allocation would throw - //if(depthindex == nullptr) { - // return; - //} - depthindex[0].start_of_scope = location; - current_val = pj.tape[location++]; - current_type = (current_val >> 56); - depthindex[0].scope_type = current_type; - if (current_type == 'r') { - tape_length = current_val & JSONVALUEMASK; - if(location < tape_length) { - current_val = pj.tape[location]; - current_type = (current_val >> 56); - depth++; - depthindex[depth].start_of_scope = location; - depthindex[depth].scope_type = current_type; - } - } else { - // should never happen - throw InvalidJSON(); - } -} - -ParsedJson::iterator::~iterator() { - delete[] depthindex; -} - -ParsedJson::iterator::iterator(const iterator &o): - pj(o.pj), depth(o.depth), location(o.location), - tape_length(0), current_type(o.current_type), - current_val(o.current_val), depthindex(nullptr) { - depthindex = new scopeindex_t[pj.depthcapacity]; - // allocation might throw - memcpy(depthindex, o.depthindex, pj.depthcapacity * sizeof(depthindex[0])); - tape_length = o.tape_length; -} - -ParsedJson::iterator::iterator(iterator &&o): - pj(o.pj), depth(o.depth), location(o.location), - tape_length(o.tape_length), current_type(o.current_type), - current_val(o.current_val), depthindex(o.depthindex) { - o.depthindex = nullptr;// we take ownership -} - -bool ParsedJson::iterator::print(std::ostream &os, bool escape_strings) const { - if(!isOk()) { - return false; +ParsedJson::Iterator::Iterator(ParsedJson &pj_) + : pj(pj_), depth(0), location(0), tape_length(0), depth_index(nullptr) { + if (!pj.is_valid()) { + throw InvalidJSON(); + } + // we overallocate by "1" to silence a warning in Visual Studio + depth_index = new scopeindex_t[pj.depth_capacity + 1]; + // memory allocation would throw + // if(depth_index == nullptr) { + // return; + //} + depth_index[0].start_of_scope = location; + current_val = pj.tape[location++]; + current_type = (current_val >> 56); + depth_index[0].scope_type = current_type; + if (current_type == 'r') { + tape_length = current_val & 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 = pj.tape[location]; + current_type = (current_val >> 56); + depth++; + depth_index[depth].start_of_scope = location; + depth_index[depth].scope_type = current_type; } - switch (current_type) { - case '"': // we have a string + } else { + // should never happen + throw InvalidJSON(); + } +} + +ParsedJson::Iterator::~Iterator() { delete[] depth_index; } + +ParsedJson::Iterator::Iterator(const Iterator &o) noexcept + : pj(o.pj), depth(o.depth), location(o.location), tape_length(0), + current_type(o.current_type), current_val(o.current_val), + depth_index(nullptr) { + depth_index = new scopeindex_t[pj.depth_capacity]; + // allocation might throw + memcpy(depth_index, o.depth_index, + pj.depth_capacity * sizeof(depth_index[0])); + tape_length = o.tape_length; +} + +ParsedJson::Iterator::Iterator(Iterator &&o) noexcept + : pj(o.pj), depth(o.depth), location(o.location), + tape_length(o.tape_length), current_type(o.current_type), + current_val(o.current_val), depth_index(o.depth_index) { + o.depth_index = nullptr; // we take ownership +} + +bool ParsedJson::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) { - print_with_escapes(get_string(), os, get_string_length()); + if (escape_strings) { + print_with_escapes(get_string(), os, 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)); + // 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 + case 'l': // we have a long int os << get_integer(); break; - case 'd': + case 'd': os << get_double(); break; - case 'n': // we have a null + case 'n': // we have a null os << "null"; break; - case 't': // we have a true + case 't': // we have a true os << "true"; break; - case 'f': // we have a false + 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 + case '{': // we have an object + case '}': // we end an object + case '[': // we start an array + case ']': // we end an array os << static_cast(current_type); break; - default: + default: return false; + } + return true; +} + +bool ParsedJson::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') { + try { + 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] = fragment; + i += 3; + } catch (std::invalid_argument &) { + delete[] new_pointer; + return false; // the fragment is invalid + } + } 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; + scopeindex_t *depth_index_s = depth_index; + + 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; + depth_index = depth_index_s; + } + + return found; +} + +bool ParsedJson::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(), 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 = (current_val & JSON_VALUE_MASK); + } else { + npos = + location + ((current_type == 'd' || current_type == 'l') ? 2 : 1); + } + location = npos; + current_val = pj.tape[npos]; + current_type = (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 simdjson /* end file src/parsedjsoniterator.cpp */ diff --git a/singleheader/simdjson.h b/singleheader/simdjson.h index aa5e759bc..ff8d6d381 100644 --- a/singleheader/simdjson.h +++ b/singleheader/simdjson.h @@ -1,16 +1,14 @@ -/* auto-generated on Sun 28 Jul 2019 18:08:49 EDT. Do not edit! */ +/* auto-generated on Thu 1 Aug 2019 16:18:07 EDT. Do not edit! */ /* begin file include/simdjson/simdjson_version.h */ // /include/simdjson/simdjson_version.h automatically generated by release.py, do not change by hand #ifndef SIMDJSON_INCLUDE_SIMDJSON_VERSION #define SIMDJSON_INCLUDE_SIMDJSON_VERSION -#define SIMDJSON_VERSION 0.1.2 -namespace simdjson { +#define SIMDJSON_VERSION 0.2.0 enum { SIMDJSON_VERSION_MAJOR = 0, - SIMDJSON_VERSION_MINOR = 1, - SIMDJSON_VERSION_REVISION = 2 + SIMDJSON_VERSION_MINOR = 2, + SIMDJSON_VERSION_REVISION = 0 }; -} #endif // SIMDJSON_INCLUDE_SIMDJSON_VERSION /* end file include/simdjson/simdjson_version.h */ /* begin file include/simdjson/portability.h */ @@ -18,95 +16,86 @@ enum { #define SIMDJSON_PORTABILITY_H #if defined(__x86_64__) || defined(_M_AMD64) -# define IS_X86_64 1 +#define IS_X86_64 1 #endif #if defined(__aarch64__) || defined(_M_ARM64) -# define IS_ARM64 1 +#define IS_ARM64 1 #endif - - +// this is almost standard? +#define STRINGIFY(a) #a // we are going to use runtime dispatch #ifdef IS_X86_64 -#if defined(_MSC_VER) -// under visual studio, nothing needs to be done -// under ARM, we don't want these macros -#define TARGET_HASWELL -#define TARGET_WESTMERE -#else -/////// -// under clang OR gcc, we need do do extra work -/////// - #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 TARGET_REGION and -// UNTARGET_REGION must be *outside* of a namespace. -#define STRINGIFY(a) #a -#define TARGET_REGION(T) _Pragma(STRINGIFY(clang attribute push(__attribute__((target(T))), apply_to=function))) +// warning: clang attribute push can't be used within a namespace in clang up +// til 8.0 so TARGET_REGION and UNTARGET_REGION must be *outside* of a +// namespace. +#define TARGET_REGION(T) \ + _Pragma(STRINGIFY( \ + clang attribute push(__attribute__((target(T))), apply_to = function))) #define UNTARGET_REGION _Pragma("clang attribute pop") -#undef STINGIFY #elif defined(__GNUC__) // GCC is easier -#define TARGET_REGION(T) \ -_Pragma("GCC push_options") \ -_Pragma(STRINGIFY(GCC target(T))) -#define UNTARGET_REGION \ -_Pragma("GCC pop_options") -#endif +#define TARGET_REGION(T) \ + _Pragma("GCC push_options") _Pragma(STRINGIFY(GCC target(T))) +#define UNTARGET_REGION _Pragma("GCC pop_options") +#else +#define TARGET_REGION(T) +#define UNTARGET_REGION +#endif // clang then gcc // under GCC and CLANG, we use these two macros #define TARGET_HASWELL TARGET_REGION("avx2,bmi,pclmul") #define TARGET_WESTMERE TARGET_REGION("sse4.2,pclmul") - -#endif // msc_ver #endif // x86 - - #ifdef _MSC_VER -# include +#include #else -# if IS_X86_64 -# include -# elif IS_ARM64 -# include -# endif +#if IS_X86_64 +#include +#elif IS_ARM64 +#include +#endif #endif #ifdef _MSC_VER /* Microsoft C/C++-compatible compiler */ -#include #include +#include namespace simdjson { -static inline bool add_overflow(uint64_t value1, uint64_t value2, uint64_t *result) { - return _addcarry_u64(0, value1, value2, reinterpret_cast(result)); +static inline bool add_overflow(uint64_t value1, uint64_t value2, + uint64_t *result) { + return _addcarry_u64(0, value1, value2, + reinterpret_cast(result)); } -# pragma intrinsic(_umul128) -static inline bool mul_overflow(uint64_t value1, uint64_t value2, uint64_t *result) { - uint64_t high; - *result = _umul128(value1, value2, &high); - return high; +#pragma intrinsic(_umul128) +static inline bool mul_overflow(uint64_t value1, uint64_t value2, + uint64_t *result) { + uint64_t high; + *result = _umul128(value1, value2, &high); + return high; } -static inline int trailingzeroes(uint64_t input_num) { - return static_cast(_tzcnt_u64(input_num)); +static inline int trailing_zeroes(uint64_t input_num) { + return static_cast(_tzcnt_u64(input_num)); } -static inline int leadingzeroes(uint64_t input_num) { - return static_cast(_lzcnt_u64(input_num)); +static inline int leading_zeroes(uint64_t input_num) { + return static_cast(_lzcnt_u64(input_num)); } static inline int hamming(uint64_t input_num) { -#ifdef _WIN64 // highly recommended!!! - return (int)__popcnt64(input_num); -#else // if we must support 32-bit Windows - return (int)(__popcnt((uint32_t)input_num) + - __popcnt((uint32_t)(input_num >> 32))); +#ifdef _WIN64 // highly recommended!!! + return (int)__popcnt64(input_num); +#else // if we must support 32-bit Windows + return (int)(__popcnt((uint32_t)input_num) + + __popcnt((uint32_t)(input_num >> 32))); #endif } } // namespace simdjson @@ -115,85 +104,91 @@ static inline int hamming(uint64_t input_num) { #include namespace simdjson { -static inline bool add_overflow(uint64_t value1, uint64_t value2, uint64_t *result) { - return __builtin_uaddll_overflow(value1, value2, (unsigned long long*)result); +static inline bool add_overflow(uint64_t value1, uint64_t value2, + uint64_t *result) { + return __builtin_uaddll_overflow(value1, value2, + (unsigned long long *)result); } -static inline bool mul_overflow(uint64_t value1, uint64_t value2, uint64_t *result) { - return __builtin_umulll_overflow(value1, value2, (unsigned long long *)result); +static inline bool mul_overflow(uint64_t value1, uint64_t value2, + uint64_t *result) { + return __builtin_umulll_overflow(value1, value2, + (unsigned long long *)result); } /* result might be undefined when input_num is zero */ -static inline int trailingzeroes(uint64_t input_num) { -#ifdef __BMI2__ - return _tzcnt_u64(input_num); +static inline int trailing_zeroes(uint64_t input_num) { +#ifdef __BMI__ // tzcnt is BMI1 + return _tzcnt_u64(input_num); #else - return __builtin_ctzll(input_num); + return __builtin_ctzll(input_num); #endif } /* result might be undefined when input_num is zero */ -static inline int leadingzeroes(uint64_t input_num) { +static inline int leading_zeroes(uint64_t input_num) { #ifdef __BMI2__ - return _lzcnt_u64(input_num); + return _lzcnt_u64(input_num); #else - return __builtin_clzll(input_num); + return __builtin_clzll(input_num); #endif } /* result might be undefined when input_num is zero */ static inline int hamming(uint64_t input_num) { #ifdef __POPCOUNT__ - return _popcnt64(input_num); + return _popcnt64(input_num); #else - return __builtin_popcountll(input_num); + return __builtin_popcountll(input_num); #endif } -} +} // namespace simdjson #endif // _MSC_VER - namespace simdjson { // portable version of posix_memalign static inline void *aligned_malloc(size_t alignment, size_t size) { - void *p; + void *p; #ifdef _MSC_VER - p = _aligned_malloc(size, alignment); + p = _aligned_malloc(size, alignment); #elif defined(__MINGW32__) || defined(__MINGW64__) - p = __mingw_aligned_malloc(size, alignment); + p = __mingw_aligned_malloc(size, alignment); #else - // somehow, if this is used before including "x86intrin.h", it creates an - // implicit defined warning. - if (posix_memalign(&p, alignment, size) != 0) { return nullptr; } + // somehow, if this is used before including "x86intrin.h", it creates an + // implicit defined warning. + if (posix_memalign(&p, alignment, size) != 0) { + return nullptr; + } #endif - return p; + return p; } static inline char *aligned_malloc_char(size_t alignment, size_t size) { - return (char*)aligned_malloc(alignment, size); + return (char *)aligned_malloc(alignment, size); } -static inline void aligned_free(void *memblock) { - if(memblock == nullptr) { return; } +static inline void aligned_free(void *mem_block) { + if (mem_block == nullptr) { + return; + } #ifdef _MSC_VER - _aligned_free(memblock); + _aligned_free(mem_block); #elif defined(__MINGW32__) || defined(__MINGW64__) - __mingw_aligned_free(memblock); + __mingw_aligned_free(mem_block); #else - free(memblock); + free(mem_block); #endif } - - -static inline void aligned_free_char(char *memblock) { - aligned_free((void*)memblock); -} +static inline void aligned_free_char(char *mem_block) { + aligned_free((void *)mem_block); } +} // namespace simdjson #endif // SIMDJSON_PORTABILITY_H /* end file include/simdjson/portability.h */ -/* begin file include/simdjson/simddetection.h */ -/* From https://github.com/endorno/pytorch/blob/master/torch/lib/TH/generic/simd/simd.h -Highly modified. +/* begin file include/simdjson/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) @@ -201,9 +196,10 @@ 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) +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. @@ -217,8 +213,8 @@ modification, are permitted provided that the following conditions are met: 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 +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. @@ -235,8 +231,8 @@ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef ISADETECTION_H -#define ISADETECTION_H +#ifndef SIMDJSON_ISADETECTION_H +#define SIMDJSON_ISADETECTION_H #include #include @@ -254,51 +250,48 @@ constexpr uint32_t cpuid_bmi2_bit = 1 << 8; // bit 8 of EBX for EAX=0x7 constexpr uint32_t cpuid_sse42_bit = 1 << 20; // bit 20 of ECX for EAX=0x1 constexpr uint32_t cpuid_pclmulqdq_bit = 1 << 1; // bit 1 of ECX for EAX=0x1 -enum SIMDExtensions { - DEFAULT = 0x0, - NEON = 0x1, - AVX2 = 0x4, - SSE42 = 0x8, +enum instruction_set { + DEFAULT = 0x0, + NEON = 0x1, + AVX2 = 0x4, + SSE42 = 0x8, PCLMULQDQ = 0x10, - BMI1 = 0x20, - BMI2 = 0x40 + BMI1 = 0x20, + BMI2 = 0x40 }; #if defined(__arm__) || defined(__aarch64__) // incl. armel, armhf, arm64 - #if defined(__NEON__) +#if defined(__NEON__) -static inline uint32_t detect_supported_architectures() -{ - return SIMDExtensions::NEON; +static inline uint32_t detect_supported_architectures() { + return instruction_set::NEON; } - #else //ARM without NEON +#else // ARM without NEON -static inline uint32_t detect_supported_architectures() -{ - return SIMDExtensions::DEFAULT; +static inline uint32_t detect_supported_architectures() { + return instruction_set::DEFAULT; } - #endif - -#else // x86 -static inline void cpuid(uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx) -{ +#endif + +#else // x86 +static inline void cpuid(uint32_t *eax, uint32_t *ebx, uint32_t *ecx, + uint32_t *edx) { #if defined(_MSC_VER) - uint32_t cpuInfo[4]; - __cpuid(cpuInfo, *eax); - *eax = cpuInfo[0]; - *ebx = cpuInfo[1]; - *ecx = cpuInfo[2]; - *edx = cpuInfo[3]; + int cpu_info[4]; + __cpuid(cpu_info, *eax); + *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); + __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) ); + asm volatile("cpuid\n\t" : "+a"(a), "=b"(b), "+c"(c), "=d"(d)); *eax = a; *ebx = b; *ecx = c; @@ -306,47 +299,46 @@ static inline void cpuid(uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t * #endif } -static inline uint32_t detect_supported_architectures() -{ +static inline uint32_t detect_supported_architectures() { uint32_t eax, ebx, ecx, edx; - uint32_t hostSimdExts = 0x0; + uint32_t host_isa = 0x0; - // ECX for EAX=0x1 + // ECX for EAX=0x7 eax = 0x7; ecx = 0x0; cpuid(&eax, &ebx, &ecx, &edx); if (ebx & cpuid_avx2_bit) { - hostSimdExts |= SIMDExtensions::AVX2; + host_isa |= instruction_set::AVX2; } if (ebx & cpuid_bmi1_bit) { - hostSimdExts |= SIMDExtensions::BMI1; + host_isa |= instruction_set::BMI1; } if (ebx & cpuid_bmi2_bit) { - hostSimdExts |= SIMDExtensions::BMI2; + host_isa |= instruction_set::BMI2; } - // EBX for EAX=0x7 + // EBX for EAX=0x1 eax = 0x1; cpuid(&eax, &ebx, &ecx, &edx); if (ecx & cpuid_sse42_bit) { - hostSimdExts |= SIMDExtensions::SSE42; + host_isa |= instruction_set::SSE42; } if (ecx & cpuid_pclmulqdq_bit) { - hostSimdExts |= SIMDExtensions::PCLMULQDQ; + host_isa |= instruction_set::PCLMULQDQ; } - return hostSimdExts; + return host_isa; } #endif // end SIMD extension detection code -} +} // namespace simdjson #endif -/* end file include/simdjson/simddetection.h */ +/* end file include/simdjson/isadetection.h */ /* begin file include/simdjson/simdjson.h */ #ifndef SIMDJSON_ERR_H #define SIMDJSON_ERR_H @@ -355,40 +347,42 @@ static inline uint32_t detect_supported_architectures() namespace simdjson { // Represents the minimal architecture that would support an implementation -enum class architecture { - westmere, - haswell, - arm64, - none, - // TODO remove 'native' in favor of runtime dispatch? - // the 'native' enum class value should point at a good default on the current machine +enum class Architecture { + WESTMERE, + HASWELL, + ARM64, + NONE, +// TODO remove 'native' in favor of runtime dispatch? +// the 'native' enum class value should point at a good default on the current +// machine #ifdef IS_X86_64 - native = westmere + NATIVE = WESTMERE #elif defined(IS_ARM64) - native = arm64 + NATIVE = ARM64 #endif }; -enum errorValues { +enum ErrorValues { SUCCESS = 0, - CAPACITY, // This ParsedJson can't support a document that big - MEMALLOC, // Error allocating memory, most likely out of memory - TAPE_ERROR, // Something went wrong while writing to the tape (stage 2), this is a generic error + CAPACITY, // This ParsedJson can't support a document that big + MEMALLOC, // Error allocating memory, most likely out of memory + TAPE_ERROR, // Something went wrong while writing to the tape (stage 2), 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 - UNITIALIZED, // unknown error, or uninitialized document - EMPTY, // no structural document found + 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 + UNITIALIZED, // unknown error, or uninitialized document + EMPTY, // no structural document found UNESCAPED_CHARS, // found unescaped characters in a string. UNCLOSED_STRING, // missing quote at the end UNEXPECTED_ERROR // indicative of a bug in simdjson }; -const std::string& errorMsg(const int); -} +const std::string &error_message(const int); +} // namespace simdjson #endif /* end file include/simdjson/simdjson.h */ /* begin file include/simdjson/common_defs.h */ @@ -403,11 +397,11 @@ const std::string& errorMsg(const int); // the input buf should be readable up to buf + SIMDJSON_PADDING #ifdef __AVX2__ -#define SIMDJSON_PADDING sizeof(__m256i) +#define SIMDJSON_PADDING sizeof(__m256i) #else // this is a stopgap; there should be a better description of the // main loop and its behavior that abstracts over this -#define SIMDJSON_PADDING 32 +#define SIMDJSON_PADDING 32 #endif #ifndef _MSC_VER @@ -416,7 +410,6 @@ const std::string& errorMsg(const int); #define SIMDJSON_USE_COMPUTED_GOTO #endif - // Align to N-byte boundary #define ROUNDUP_N(a, n) (((a) + ((n)-1)) & ~((n)-1)) #define ROUNDDOWN_N(a, n) ((a) & ~((n)-1)) @@ -424,7 +417,7 @@ const std::string& errorMsg(const int); #define ISALIGNED_N(ptr, n) (((uintptr_t)(ptr) & ((n)-1)) == 0) #ifdef _MSC_VER -#define really_inline inline +#define really_inline __forceinline #define never_inline __declspec(noinline) #define UNUSED @@ -437,31 +430,37 @@ const std::string& errorMsg(const int); #define unlikely(x) x #endif +// For Visual Studio compilers, same-page buffer overrun is not fine. +#define ALLOW_SAME_PAGE_BUFFER_OVERRUN false + #else -// For non-Visual Studio compilers, we may assume that same-page buffer overrun is fine. -// However, it will make it difficult to be "valgrind clean". +// For non-Visual Studio compilers, we may assume that same-page buffer overrun +// is fine. However, it will make it difficult to be "valgrind clean". //#ifndef ALLOW_SAME_PAGE_BUFFER_OVERRUN //#define ALLOW_SAME_PAGE_BUFFER_OVERRUN true //#else #define ALLOW_SAME_PAGE_BUFFER_OVERRUN false -//#endif +//#endif // The following is likely unnecessarily complex. #ifdef __SANITIZE_ADDRESS__ // we have GCC, stuck with https://gcc.gnu.org/bugzilla/show_bug.cgi?id=67368 -#undef ALLOW_SAME_PAGE_BUFFER_OVERRUN +#define ALLOW_SAME_PAGE_BUFFER_OVERRUN false #elif defined(__has_feature) // we have CLANG? -# if (__has_feature(address_sanitizer)) -#define ALLOW_SAME_PAGE_BUFFER_OVERRUN_QUALIFIER __attribute__((no_sanitize("address"))) -# endif -#endif +// todo: if we're setting ALLOW_SAME_PAGE_BUFFER_OVERRUN to false, why do we +// have a non-empty qualifier? +#if (__has_feature(address_sanitizer)) +#define ALLOW_SAME_PAGE_BUFFER_OVERRUN_QUALIFIER \ + __attribute__((no_sanitize("address"))) +#endif +#endif #if defined(__has_feature) -# if (__has_feature(memory_sanitizer)) +#if (__has_feature(memory_sanitizer)) #define LENIENT_MEM_SANITIZER __attribute__((no_sanitize("memory"))) -# endif +#endif #endif #define really_inline inline __attribute__((always_inline, unused)) @@ -477,7 +476,7 @@ const std::string& errorMsg(const int); #define unlikely(x) __builtin_expect(!!(x), 0) #endif -#endif // MSC_VER +#endif // MSC_VER // if it does not apply, make it an empty macro #ifndef ALLOW_SAME_PAGE_BUFFER_OVERRUN_QUALIFIER @@ -492,8 +491,8 @@ const std::string& errorMsg(const int); /* begin file include/simdjson/padded_string.h */ #ifndef SIMDJSON_PADDING_STRING_H #define SIMDJSON_PADDING_STRING_H -#include #include +#include namespace simdjson { // low-level function to allocate memory with padding so we can read passed the @@ -556,7 +555,7 @@ private: size_t viable_size; char *data_ptr; }; -} +} // namespace simdjson #endif /* end file include/simdjson/padded_string.h */ @@ -596,7 +595,6 @@ really_inline uint32_t is_not_structural_or_whitespace_or_null(uint8_t c) { return structural_or_whitespace_or_null_negated[c]; } - const uint32_t 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, @@ -637,7 +635,6 @@ really_inline uint32_t is_structural_or_whitespace_or_null(uint8_t c) { return structural_or_whitespace_or_null[c]; } - const uint32_t 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, @@ -655,7 +652,7 @@ really_inline uint32_t is_structural_or_whitespace(uint8_t c) { return structural_or_whitespace[c]; } -const uint32_t digittoval32[886] = { +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, @@ -664,7 +661,7 @@ const uint32_t digittoval32[886] = { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0xFFFFFFFF, @@ -699,7 +696,7 @@ const uint32_t digittoval32[886] = { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0x0, 0x10, 0x20, 0x30, 0x40, 0x50, + 0x0, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x90, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0, 0xFFFFFFFF, @@ -734,7 +731,7 @@ const uint32_t digittoval32[886] = { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0x0, 0x100, 0x200, 0x300, 0x400, 0x500, + 0x0, 0x100, 0x200, 0x300, 0x400, 0x500, 0x600, 0x700, 0x800, 0x900, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xa00, 0xb00, 0xc00, 0xd00, 0xe00, 0xf00, 0xFFFFFFFF, @@ -769,7 +766,7 @@ const uint32_t digittoval32[886] = { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0x0, 0x1000, 0x2000, 0x3000, 0x4000, 0x5000, + 0x0, 0x1000, 0x2000, 0x3000, 0x4000, 0x5000, 0x6000, 0x7000, 0x8000, 0x9000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xa000, 0xb000, 0xc000, 0xd000, 0xe000, 0xf000, 0xFFFFFFFF, @@ -805,15 +802,17 @@ const uint32_t digittoval32[886] = { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF}; // 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 +// 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 = digittoval32[630 + src[0]]; - uint32_t v2 = digittoval32[420 + src[1]]; - uint32_t v3 = digittoval32[210 + src[2]]; - uint32_t v4 = digittoval32[0 + src[3]]; +// 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 = digit_to_val32[630 + src[0]]; + uint32_t v2 = digit_to_val32[420 + src[1]]; + uint32_t v3 = digit_to_val32[210 + src[2]]; + uint32_t v4 = digit_to_val32[0 + src[3]]; return v1 | v2 | v3 | v4; } @@ -833,19 +832,21 @@ inline size_t codepoint_to_utf8(uint32_t cp, uint8_t *c) { if (cp <= 0x7F) { c[0] = cp; return 1; // ascii - } if (cp <= 0x7FF) { + } + if (cp <= 0x7FF) { c[0] = (cp >> 6) + 192; c[1] = (cp & 63) + 128; return 2; // universal plane - // Surrogates are treated elsewhere... - //} //else if (0xd800 <= cp && cp <= 0xdfff) { - // return 0; // surrogates // could put assert here + // Surrogates are treated elsewhere... + //} //else if (0xd800 <= cp && cp <= 0xdfff) { + // return 0; // surrogates // could put assert here } else if (cp <= 0xFFFF) { c[0] = (cp >> 12) + 224; c[1] = ((cp >> 6) & 63) + 128; c[2] = (cp & 63) + 128; return 3; - } else if (cp <= 0x10FFFF) { // if you know you have a valid code point, this is not needed + } else if (cp <= 0x10FFFF) { // if you know you have a valid code point, this + // is not needed c[0] = (cp >> 18) + 240; c[1] = ((cp >> 12) & 63) + 128; c[2] = ((cp >> 6) & 63) + 128; @@ -855,7 +856,7 @@ inline size_t codepoint_to_utf8(uint32_t cp, uint8_t *c) { // will return 0 when the code point was too large. return 0; // bad r } -} +} // namespace simdjson #endif /* end file include/simdjson/jsoncharutils.h */ @@ -872,40 +873,40 @@ namespace simdjson { static inline void print_with_escapes(const unsigned char *src) { while (*src) { switch (*src) { - case '\b': - putchar('\\'); - putchar('b'); - break; - case '\f': - putchar('\\'); - putchar('f'); - break; - case '\n': - putchar('\\'); - putchar('n'); - break; - case '\r': - putchar('\\'); - putchar('r'); - break; - case '\"': - putchar('\\'); - putchar('"'); - break; - case '\t': - putchar('\\'); - putchar('t'); - break; - case '\\': - putchar('\\'); - putchar('\\'); - break; - default: - if (*src <= 0x1F) { - printf("\\u%04x", *src); - } else { - putchar(*src); - } + case '\b': + putchar('\\'); + putchar('b'); + break; + case '\f': + putchar('\\'); + putchar('f'); + break; + case '\n': + putchar('\\'); + putchar('n'); + break; + case '\r': + putchar('\\'); + putchar('r'); + break; + case '\"': + putchar('\\'); + putchar('"'); + break; + case '\t': + putchar('\\'); + putchar('t'); + break; + case '\\': + putchar('\\'); + putchar('\\'); + break; + default: + if (*src <= 0x1F) { + printf("\\u%04x", *src); + } else { + putchar(*src); + } } src++; } @@ -916,43 +917,43 @@ static inline void print_with_escapes(const unsigned char *src, std::ostream &os) { while (*src) { switch (*src) { - case '\b': - os << '\\'; - os << 'b'; - break; - case '\f': - os << '\\'; - os << 'f'; - break; - case '\n': - os << '\\'; - os << 'n'; - break; - case '\r': - os << '\\'; - os << 'r'; - break; - case '\"': - os << '\\'; - os << '"'; - break; - case '\t': - os << '\\'; - os << 't'; - break; - case '\\': - os << '\\'; - os << '\\'; - break; - default: - if (*src <= 0x1F) { - std::ios::fmtflags f(os.flags()); - os << std::hex << std::setw(4) << std::setfill('0') - << static_cast(*src); - os.flags(f); - } else { - os << *src; - } + case '\b': + os << '\\'; + os << 'b'; + break; + case '\f': + os << '\\'; + os << 'f'; + break; + case '\n': + os << '\\'; + os << 'n'; + break; + case '\r': + os << '\\'; + os << 'r'; + break; + case '\"': + os << '\\'; + os << '"'; + break; + case '\t': + os << '\\'; + os << 't'; + break; + case '\\': + os << '\\'; + os << '\\'; + break; + default: + if (*src <= 0x1F) { + std::ios::fmtflags f(os.flags()); + os << std::hex << std::setw(4) << std::setfill('0') + << static_cast(*src); + os.flags(f); + } else { + os << *src; + } } src++; } @@ -963,40 +964,40 @@ static inline void print_with_escapes(const unsigned char *src, size_t len) { const unsigned char *finalsrc = src + len; while (src < finalsrc) { switch (*src) { - case '\b': - putchar('\\'); - putchar('b'); - break; - case '\f': - putchar('\\'); - putchar('f'); - break; - case '\n': - putchar('\\'); - putchar('n'); - break; - case '\r': - putchar('\\'); - putchar('r'); - break; - case '\"': - putchar('\\'); - putchar('"'); - break; - case '\t': - putchar('\\'); - putchar('t'); - break; - case '\\': - putchar('\\'); - putchar('\\'); - break; - default: - if (*src <= 0x1F) { - printf("\\u%04x", *src); - } else { - putchar(*src); - } + case '\b': + putchar('\\'); + putchar('b'); + break; + case '\f': + putchar('\\'); + putchar('f'); + break; + case '\n': + putchar('\\'); + putchar('n'); + break; + case '\r': + putchar('\\'); + putchar('r'); + break; + case '\"': + putchar('\\'); + putchar('"'); + break; + case '\t': + putchar('\\'); + putchar('t'); + break; + case '\\': + putchar('\\'); + putchar('\\'); + break; + default: + if (*src <= 0x1F) { + printf("\\u%04x", *src); + } else { + putchar(*src); + } } src++; } @@ -1008,43 +1009,43 @@ static inline void print_with_escapes(const unsigned char *src, const unsigned char *finalsrc = src + len; while (src < finalsrc) { switch (*src) { - case '\b': - os << '\\'; - os << 'b'; - break; - case '\f': - os << '\\'; - os << 'f'; - break; - case '\n': - os << '\\'; - os << 'n'; - break; - case '\r': - os << '\\'; - os << 'r'; - break; - case '\"': - os << '\\'; - os << '"'; - break; - case '\t': - os << '\\'; - os << 't'; - break; - case '\\': - os << '\\'; - os << '\\'; - break; - default: - if (*src <= 0x1F) { - std::ios::fmtflags f(os.flags()); - os << std::hex << std::setw(4) << std::setfill('0') - << static_cast(*src); - os.flags(f); - } else { - os << *src; - } + case '\b': + os << '\\'; + os << 'b'; + break; + case '\f': + os << '\\'; + os << 'f'; + break; + case '\n': + os << '\\'; + os << 'n'; + break; + case '\r': + os << '\\'; + os << 'r'; + break; + case '\"': + os << '\\'; + os << '"'; + break; + case '\t': + os << '\\'; + os << 't'; + break; + case '\\': + os << '\\'; + os << '\\'; + break; + default: + if (*src <= 0x1F) { + std::ios::fmtflags f(os.flags()); + os << std::hex << std::setw(4) << std::setfill('0') + << static_cast(*src); + os.flags(f); + } else { + os << *src; + } } src++; } @@ -1058,7 +1059,7 @@ static inline void print_with_escapes(const char *src, std::ostream &os, size_t len) { print_with_escapes(reinterpret_cast(src), os, len); } -} +} // namespace simdjson # #endif @@ -1074,8 +1075,6 @@ static inline void print_with_escapes(const char *src, std::ostream &os, #include - - namespace simdjson { // load a file in memory... @@ -1084,16 +1083,16 @@ namespace simdjson { // first element of the pair is a string (null terminated) // whereas the second element is the length. // caller is responsible to free (aligned_free((void*)result.data()))) -// +// // throws an exception if the file cannot be opened, use try/catch // try { // p = get_corpus(filename); -// } catch (const std::exception& e) { +// } catch (const std::exception& e) { // aligned_free((void*)p.data()); // std::cout << "Could not load the file " << filename << std::endl; // } -padded_string get_corpus(const std::string& filename); -} +padded_string get_corpus(const std::string &filename); +} // namespace simdjson #endif /* end file include/simdjson/jsonioutil.h */ @@ -36176,7 +36175,6 @@ static const uint32_t mask256_epi32[] = { #ifndef SIMDJSON_SIMDUTF8CHECK_HASWELL_H #define SIMDJSON_SIMDUTF8CHECK_HASWELL_H - #include #include #include @@ -36212,14 +36210,14 @@ static inline __m256i push_last_2bytes_of_a_to_b(__m256i a, __m256i b) { } // all byte values must be no larger than 0xF4 -static inline void avxcheckSmallerThan0xF4(__m256i current_bytes, - __m256i *has_error) { +static inline void avx_check_smaller_than_0xF4(__m256i current_bytes, + __m256i *has_error) { // unsigned, saturates to 0 below max *has_error = _mm256_or_si256( - *has_error, _mm256_subs_epu8(current_bytes, _mm256_set1_epi8(0xF4))); + *has_error, _mm256_subs_epu8(current_bytes, _mm256_set1_epi8(0xF4u))); } -static inline __m256i avxcontinuationLengths(__m256i high_nibbles) { +static inline __m256i avx_continuation_lengths(__m256i high_nibbles) { return _mm256_shuffle_epi8( _mm256_setr_epi8(1, 1, 1, 1, 1, 1, 1, 1, // 0xxx (ASCII) 0, 0, 0, 0, // 10xx (continuation) @@ -36235,8 +36233,8 @@ static inline __m256i avxcontinuationLengths(__m256i high_nibbles) { high_nibbles); } -static inline __m256i avxcarryContinuations(__m256i initial_lengths, - __m256i previous_carries) { +static inline __m256i avx_carry_continuations(__m256i initial_lengths, + __m256i previous_carries) { __m256i right1 = _mm256_subs_epu8( push_last_byte_of_a_to_b(previous_carries, initial_lengths), @@ -36248,8 +36246,9 @@ static inline __m256i avxcarryContinuations(__m256i initial_lengths, return _mm256_add_epi8(sum, right2); } -static inline void avxcheckContinuations(__m256i initial_lengths, - __m256i carries, __m256i *has_error) { +static inline void avx_check_continuations(__m256i initial_lengths, + __m256i carries, + __m256i *has_error) { // overlap || underlap // carry > length && length > 0 || !(carry > length) && !(length > 0) @@ -36264,18 +36263,18 @@ static inline void avxcheckContinuations(__m256i initial_lengths, // when 0xED is found, next byte must be no larger than 0x9F // when 0xF4 is found, next byte must be no larger than 0x8F // next byte must be continuation, ie sign bit is set, so signed < is ok -static inline void avxcheckFirstContinuationMax(__m256i current_bytes, - __m256i off1_current_bytes, - __m256i *has_error) { +static inline void avx_check_first_continuation_max(__m256i current_bytes, + __m256i off1_current_bytes, + __m256i *has_error) { __m256i maskED = - _mm256_cmpeq_epi8(off1_current_bytes, _mm256_set1_epi8(0xED)); + _mm256_cmpeq_epi8(off1_current_bytes, _mm256_set1_epi8(0xEDu)); __m256i maskF4 = - _mm256_cmpeq_epi8(off1_current_bytes, _mm256_set1_epi8(0xF4)); + _mm256_cmpeq_epi8(off1_current_bytes, _mm256_set1_epi8(0xF4u)); __m256i badfollowED = _mm256_and_si256( - _mm256_cmpgt_epi8(current_bytes, _mm256_set1_epi8(0x9F)), maskED); + _mm256_cmpgt_epi8(current_bytes, _mm256_set1_epi8(0x9Fu)), maskED); __m256i badfollowF4 = _mm256_and_si256( - _mm256_cmpgt_epi8(current_bytes, _mm256_set1_epi8(0x8F)), maskF4); + _mm256_cmpgt_epi8(current_bytes, _mm256_set1_epi8(0x8Fu)), maskF4); *has_error = _mm256_or_si256(*has_error, _mm256_or_si256(badfollowED, badfollowF4)); @@ -36287,37 +36286,37 @@ static inline void avxcheckFirstContinuationMax(__m256i current_bytes, // E => < E1 && < A0 // F => < F1 && < 90 // else false && false -static inline void avxcheckOverlong(__m256i current_bytes, - __m256i off1_current_bytes, __m256i hibits, - __m256i previous_hibits, - __m256i *has_error) { +static inline void avx_check_overlong(__m256i current_bytes, + __m256i off1_current_bytes, + __m256i hibits, __m256i previous_hibits, + __m256i *has_error) { __m256i off1_hibits = push_last_byte_of_a_to_b(previous_hibits, hibits); __m256i initial_mins = _mm256_shuffle_epi8( - _mm256_setr_epi8(-128, -128, -128, -128, -128, -128, -128, -128, - -128, -128, -128, -128, // 10xx => false - 0xC2, -128, // 110x - 0xE1, // 1110 - 0xF1, // 1111 - -128, -128, -128, -128, -128, -128, -128, -128, - -128, -128, -128, -128, // 10xx => false - 0xC2, -128, // 110x - 0xE1, // 1110 - 0xF1), // 1111 + _mm256_setr_epi8(-128, -128, -128, -128, -128, -128, -128, -128, -128, + -128, -128, -128, // 10xx => false + 0xC2u, -128, // 110x + 0xE1u, // 1110 + 0xF1u, // 1111 + -128, -128, -128, -128, -128, -128, -128, -128, -128, + -128, -128, -128, // 10xx => false + 0xC2u, -128, // 110x + 0xE1u, // 1110 + 0xF1u), // 1111 off1_hibits); __m256i initial_under = _mm256_cmpgt_epi8(initial_mins, off1_current_bytes); __m256i second_mins = _mm256_shuffle_epi8( - _mm256_setr_epi8(-128, -128, -128, -128, -128, -128, -128, -128, - -128, -128, -128, -128, // 10xx => false - 127, 127, // 110x => true - 0xA0, // 1110 - 0x90, // 1111 - -128, -128, -128, -128, -128, -128, -128, -128, - -128, -128, -128, -128, // 10xx => false - 127, 127, // 110x => true - 0xA0, // 1110 - 0x90), // 1111 + _mm256_setr_epi8(-128, -128, -128, -128, -128, -128, -128, -128, -128, + -128, -128, -128, // 10xx => false + 127, 127, // 110x => true + 0xA0u, // 1110 + 0x90u, // 1111 + -128, -128, -128, -128, -128, -128, -128, -128, -128, + -128, -128, -128, // 10xx => false + 127, 127, // 110x => true + 0xA0u, // 1110 + 0x90u), // 1111 off1_hibits); __m256i second_under = _mm256_cmpgt_epi8(second_mins, current_bytes); *has_error = _mm256_or_si256(*has_error, @@ -36325,14 +36324,14 @@ static inline void avxcheckOverlong(__m256i current_bytes, } struct avx_processed_utf_bytes { - __m256i rawbytes; + __m256i raw_bytes; __m256i high_nibbles; __m256i carried_continuations; }; static inline void avx_count_nibbles(__m256i bytes, struct avx_processed_utf_bytes *answer) { - answer->rawbytes = bytes; + answer->raw_bytes = bytes; answer->high_nibbles = _mm256_and_si256(_mm256_srli_epi16(bytes, 4), _mm256_set1_epi8(0x0F)); } @@ -36340,33 +36339,33 @@ static inline void avx_count_nibbles(__m256i bytes, // check whether the current bytes are valid UTF-8 // at the end of the function, previous gets updated static inline struct avx_processed_utf_bytes -avxcheckUTF8Bytes(__m256i current_bytes, - struct avx_processed_utf_bytes *previous, - __m256i *has_error) { - struct avx_processed_utf_bytes pb{}; +avx_check_utf8_bytes(__m256i current_bytes, + struct avx_processed_utf_bytes *previous, + __m256i *has_error) { + struct avx_processed_utf_bytes pb {}; avx_count_nibbles(current_bytes, &pb); - avxcheckSmallerThan0xF4(current_bytes, has_error); + avx_check_smaller_than_0xF4(current_bytes, has_error); - __m256i initial_lengths = avxcontinuationLengths(pb.high_nibbles); + __m256i initial_lengths = avx_continuation_lengths(pb.high_nibbles); pb.carried_continuations = - avxcarryContinuations(initial_lengths, previous->carried_continuations); + avx_carry_continuations(initial_lengths, previous->carried_continuations); - avxcheckContinuations(initial_lengths, pb.carried_continuations, has_error); + avx_check_continuations(initial_lengths, pb.carried_continuations, has_error); __m256i off1_current_bytes = - push_last_byte_of_a_to_b(previous->rawbytes, pb.rawbytes); - avxcheckFirstContinuationMax(current_bytes, off1_current_bytes, has_error); + push_last_byte_of_a_to_b(previous->raw_bytes, pb.raw_bytes); + avx_check_first_continuation_max(current_bytes, off1_current_bytes, + has_error); - avxcheckOverlong(current_bytes, off1_current_bytes, pb.high_nibbles, - previous->high_nibbles, has_error); + avx_check_overlong(current_bytes, off1_current_bytes, pb.high_nibbles, + previous->high_nibbles, has_error); return pb; } -}// simdjson +} // namespace simdjson UNTARGET_REGION // haswell - #endif // IS_X86_64 #endif @@ -36402,16 +36401,16 @@ UNTARGET_REGION // haswell /********** sse code **********/ TARGET_WESTMERE -namespace simdjson{ +namespace simdjson { // all byte values must be no larger than 0xF4 -static inline void checkSmallerThan0xF4(__m128i current_bytes, - __m128i *has_error) { +static inline void check_smaller_than_0xF4(__m128i current_bytes, + __m128i *has_error) { // unsigned, saturates to 0 below max *has_error = _mm_or_si128(*has_error, - _mm_subs_epu8(current_bytes, _mm_set1_epi8(0xF4))); + _mm_subs_epu8(current_bytes, _mm_set1_epi8(0xF4u))); } -static inline __m128i continuationLengths(__m128i high_nibbles) { +static inline __m128i continuation_lengths(__m128i high_nibbles) { return _mm_shuffle_epi8( _mm_setr_epi8(1, 1, 1, 1, 1, 1, 1, 1, // 0xxx (ASCII) 0, 0, 0, 0, // 10xx (continuation) @@ -36421,8 +36420,8 @@ static inline __m128i continuationLengths(__m128i high_nibbles) { high_nibbles); } -static inline __m128i carryContinuations(__m128i initial_lengths, - __m128i previous_carries) { +static inline __m128i carry_continuations(__m128i initial_lengths, + __m128i previous_carries) { __m128i right1 = _mm_subs_epu8(_mm_alignr_epi8(initial_lengths, previous_carries, 16 - 1), @@ -36434,8 +36433,8 @@ static inline __m128i carryContinuations(__m128i initial_lengths, return _mm_add_epi8(sum, right2); } -static inline void checkContinuations(__m128i initial_lengths, __m128i carries, - __m128i *has_error) { +static inline void check_continuations(__m128i initial_lengths, __m128i carries, + __m128i *has_error) { // overlap || underlap // carry > length && length > 0 || !(carry > length) && !(length > 0) @@ -36450,16 +36449,16 @@ static inline void checkContinuations(__m128i initial_lengths, __m128i carries, // when 0xED is found, next byte must be no larger than 0x9F // when 0xF4 is found, next byte must be no larger than 0x8F // next byte must be continuation, ie sign bit is set, so signed < is ok -static inline void checkFirstContinuationMax(__m128i current_bytes, - __m128i off1_current_bytes, - __m128i *has_error) { - __m128i maskED = _mm_cmpeq_epi8(off1_current_bytes, _mm_set1_epi8(0xED)); - __m128i maskF4 = _mm_cmpeq_epi8(off1_current_bytes, _mm_set1_epi8(0xF4)); +static inline void check_first_continuation_max(__m128i current_bytes, + __m128i off1_current_bytes, + __m128i *has_error) { + __m128i maskED = _mm_cmpeq_epi8(off1_current_bytes, _mm_set1_epi8(0xEDu)); + __m128i maskF4 = _mm_cmpeq_epi8(off1_current_bytes, _mm_set1_epi8(0xF4u)); - __m128i badfollowED = - _mm_and_si128(_mm_cmpgt_epi8(current_bytes, _mm_set1_epi8(0x9F)), maskED); - __m128i badfollowF4 = - _mm_and_si128(_mm_cmpgt_epi8(current_bytes, _mm_set1_epi8(0x8F)), maskF4); + __m128i badfollowED = _mm_and_si128( + _mm_cmpgt_epi8(current_bytes, _mm_set1_epi8(0x9Fu)), maskED); + __m128i badfollowF4 = _mm_and_si128( + _mm_cmpgt_epi8(current_bytes, _mm_set1_epi8(0x8Fu)), maskF4); *has_error = _mm_or_si128(*has_error, _mm_or_si128(badfollowED, badfollowF4)); } @@ -36470,16 +36469,16 @@ static inline void checkFirstContinuationMax(__m128i current_bytes, // E => < E1 && < A0 // F => < F1 && < 90 // else false && false -static inline void checkOverlong(__m128i current_bytes, - __m128i off1_current_bytes, __m128i hibits, - __m128i previous_hibits, __m128i *has_error) { +static inline void check_overlong(__m128i current_bytes, + __m128i off1_current_bytes, __m128i hibits, + __m128i previous_hibits, __m128i *has_error) { __m128i off1_hibits = _mm_alignr_epi8(hibits, previous_hibits, 16 - 1); __m128i initial_mins = _mm_shuffle_epi8( _mm_setr_epi8(-128, -128, -128, -128, -128, -128, -128, -128, -128, -128, - -128, -128, // 10xx => false - 0xC2, -128, // 110x - 0xE1, // 1110 - 0xF1), + -128, -128, // 10xx => false + 0xC2u, -128, // 110x + 0xE1u, // 1110 + 0xF1u), off1_hibits); __m128i initial_under = _mm_cmpgt_epi8(initial_mins, off1_current_bytes); @@ -36488,8 +36487,8 @@ static inline void checkOverlong(__m128i current_bytes, _mm_setr_epi8(-128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, // 10xx => false 127, 127, // 110x => true - 0xA0, // 1110 - 0x90), + 0xA0u, // 1110 + 0x90u), off1_hibits); __m128i second_under = _mm_cmpgt_epi8(second_mins, current_bytes); *has_error = @@ -36497,14 +36496,14 @@ static inline void checkOverlong(__m128i current_bytes, } struct processed_utf_bytes { - __m128i rawbytes; + __m128i raw_bytes; __m128i high_nibbles; __m128i carried_continuations; }; static inline void count_nibbles(__m128i bytes, struct processed_utf_bytes *answer) { - answer->rawbytes = bytes; + answer->raw_bytes = bytes; answer->high_nibbles = _mm_and_si128(_mm_srli_epi16(bytes, 4), _mm_set1_epi8(0x0F)); } @@ -36512,32 +36511,31 @@ static inline void count_nibbles(__m128i bytes, // check whether the current bytes are valid UTF-8 // at the end of the function, previous gets updated static struct processed_utf_bytes -checkUTF8Bytes(__m128i current_bytes, struct processed_utf_bytes *previous, - __m128i *has_error) { +check_utf8_bytes(__m128i current_bytes, struct processed_utf_bytes *previous, + __m128i *has_error) { struct processed_utf_bytes pb; count_nibbles(current_bytes, &pb); - checkSmallerThan0xF4(current_bytes, has_error); + check_smaller_than_0xF4(current_bytes, has_error); - __m128i initial_lengths = continuationLengths(pb.high_nibbles); + __m128i initial_lengths = continuation_lengths(pb.high_nibbles); pb.carried_continuations = - carryContinuations(initial_lengths, previous->carried_continuations); + carry_continuations(initial_lengths, previous->carried_continuations); - checkContinuations(initial_lengths, pb.carried_continuations, has_error); + check_continuations(initial_lengths, pb.carried_continuations, has_error); __m128i off1_current_bytes = - _mm_alignr_epi8(pb.rawbytes, previous->rawbytes, 16 - 1); - checkFirstContinuationMax(current_bytes, off1_current_bytes, has_error); + _mm_alignr_epi8(pb.raw_bytes, previous->raw_bytes, 16 - 1); + check_first_continuation_max(current_bytes, off1_current_bytes, has_error); - checkOverlong(current_bytes, off1_current_bytes, pb.high_nibbles, - previous->high_nibbles, has_error); + check_overlong(current_bytes, off1_current_bytes, pb.high_nibbles, + previous->high_nibbles, has_error); return pb; } -}//simdjson +} // namespace simdjson UNTARGET_REGION // westmere - #endif // IS_X86_64 #endif @@ -36549,14 +36547,15 @@ UNTARGET_REGION // westmere #ifndef SIMDJSON_SIMDUTF8CHECK_ARM64_H #define SIMDJSON_SIMDUTF8CHECK_ARM64_H -#if defined(_ARM_NEON) || defined(__aarch64__) || (defined(_MSC_VER) && defined(_M_ARM64)) +#if defined(_ARM_NEON) || defined(__aarch64__) || \ + (defined(_MSC_VER) && defined(_M_ARM64)) -#include +#include +#include #include #include +#include #include -#include -#include /* * legal utf-8 byte sequence @@ -36577,47 +36576,49 @@ UNTARGET_REGION // westmere namespace simdjson { // all byte values must be no larger than 0xF4 -static inline void checkSmallerThan0xF4(int8x16_t current_bytes, - int8x16_t *has_error) { +static inline void check_smaller_than_0xF4(int8x16_t current_bytes, + int8x16_t *has_error) { // unsigned, saturates to 0 below max - *has_error = vorrq_s8(*has_error, - vreinterpretq_s8_u8(vqsubq_u8(vreinterpretq_u8_s8(current_bytes), vdupq_n_u8(0xF4)))); + *has_error = vorrq_s8( + *has_error, vreinterpretq_s8_u8(vqsubq_u8( + vreinterpretq_u8_s8(current_bytes), vdupq_n_u8(0xF4)))); } static const int8_t _nibbles[] = { - 1, 1, 1, 1, 1, 1, 1, 1, // 0xxx (ASCII) - 0, 0, 0, 0, // 10xx (continuation) - 2, 2, // 110x - 3, // 1110 - 4, // 1111, next should be 0 (not checked here) + 1, 1, 1, 1, 1, 1, 1, 1, // 0xxx (ASCII) + 0, 0, 0, 0, // 10xx (continuation) + 2, 2, // 110x + 3, // 1110 + 4, // 1111, next should be 0 (not checked here) }; -static inline int8x16_t continuationLengths(int8x16_t high_nibbles) { +static inline int8x16_t continuation_lengths(int8x16_t high_nibbles) { return vqtbl1q_s8(vld1q_s8(_nibbles), vreinterpretq_u8_s8(high_nibbles)); } -static inline int8x16_t carryContinuations(int8x16_t initial_lengths, - int8x16_t previous_carries) { +static inline int8x16_t carry_continuations(int8x16_t initial_lengths, + int8x16_t previous_carries) { - int8x16_t right1 = - vreinterpretq_s8_u8(vqsubq_u8(vreinterpretq_u8_s8(vextq_s8(previous_carries, initial_lengths, 16 - 1)), - vdupq_n_u8(1))); + int8x16_t right1 = vreinterpretq_s8_u8(vqsubq_u8( + vreinterpretq_u8_s8(vextq_s8(previous_carries, initial_lengths, 16 - 1)), + vdupq_n_u8(1))); int8x16_t sum = vaddq_s8(initial_lengths, right1); - int8x16_t right2 = vreinterpretq_s8_u8(vqsubq_u8(vreinterpretq_u8_s8(vextq_s8(previous_carries, sum, 16 - 2)), - vdupq_n_u8(2))); + int8x16_t right2 = vreinterpretq_s8_u8( + vqsubq_u8(vreinterpretq_u8_s8(vextq_s8(previous_carries, sum, 16 - 2)), + vdupq_n_u8(2))); return vaddq_s8(sum, right2); } -static inline void checkContinuations(int8x16_t initial_lengths, int8x16_t carries, - int8x16_t *has_error) { +static inline void check_continuations(int8x16_t initial_lengths, + int8x16_t carries, + int8x16_t *has_error) { // overlap || underlap // carry > length && length > 0 || !(carry > length) && !(length > 0) // (carries > length) == (lengths > 0) - uint8x16_t overunder = - vceqq_u8(vcgtq_s8(carries, initial_lengths), - vcgtq_s8(initial_lengths, vdupq_n_s8(0))); + uint8x16_t overunder = vceqq_u8(vcgtq_s8(carries, initial_lengths), + vcgtq_s8(initial_lengths, vdupq_n_s8(0))); *has_error = vorrq_s8(*has_error, vreinterpretq_s8_u8(overunder)); } @@ -36625,9 +36626,9 @@ static inline void checkContinuations(int8x16_t initial_lengths, int8x16_t carri // when 0xED is found, next byte must be no larger than 0x9F // when 0xF4 is found, next byte must be no larger than 0x8F // next byte must be continuation, ie sign bit is set, so signed < is ok -static inline void checkFirstContinuationMax(int8x16_t current_bytes, - int8x16_t off1_current_bytes, - int8x16_t *has_error) { +static inline void check_first_continuation_max(int8x16_t current_bytes, + int8x16_t off1_current_bytes, + int8x16_t *has_error) { uint8x16_t maskED = vceqq_s8(off1_current_bytes, vdupq_n_s8(0xED)); uint8x16_t maskF4 = vceqq_s8(off1_current_bytes, vdupq_n_s8(0xF4)); @@ -36636,23 +36637,24 @@ static inline void checkFirstContinuationMax(int8x16_t current_bytes, uint8x16_t badfollowF4 = vandq_u8(vcgtq_s8(current_bytes, vdupq_n_s8(0x8F)), maskF4); - *has_error = vorrq_s8(*has_error, vreinterpretq_s8_u8(vorrq_u8(badfollowED, badfollowF4))); + *has_error = vorrq_s8( + *has_error, vreinterpretq_s8_u8(vorrq_u8(badfollowED, badfollowF4))); } static const int8_t _initial_mins[] = { - -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, - -128, -128, // 10xx => false - (int8_t) 0xC2, -128, // 110x - (int8_t) 0xE1, // 1110 - (int8_t) 0xF1, + -128, -128, -128, -128, -128, -128, + -128, -128, -128, -128, -128, -128, // 10xx => false + (int8_t)0xC2, -128, // 110x + (int8_t)0xE1, // 1110 + (int8_t)0xF1, }; static const int8_t _second_mins[] = { - -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, - -128, -128, // 10xx => false - 127, 127, // 110x => true - (int8_t) 0xA0, // 1110 - (int8_t) 0x90, + -128, -128, -128, -128, -128, -128, + -128, -128, -128, -128, -128, -128, // 10xx => false + 127, 127, // 110x => true + (int8_t)0xA0, // 1110 + (int8_t)0x90, }; // map off1_hibits => error condition @@ -36661,59 +36663,62 @@ static const int8_t _second_mins[] = { // E => < E1 && < A0 // F => < F1 && < 90 // else false && false -static inline void checkOverlong(int8x16_t current_bytes, - int8x16_t off1_current_bytes, int8x16_t hibits, - int8x16_t previous_hibits, int8x16_t *has_error) { +static inline void check_overlong(int8x16_t current_bytes, + int8x16_t off1_current_bytes, + int8x16_t hibits, int8x16_t previous_hibits, + int8x16_t *has_error) { int8x16_t off1_hibits = vextq_s8(previous_hibits, hibits, 16 - 1); - int8x16_t initial_mins = vqtbl1q_s8(vld1q_s8(_initial_mins), vreinterpretq_u8_s8(off1_hibits)); + int8x16_t initial_mins = + vqtbl1q_s8(vld1q_s8(_initial_mins), vreinterpretq_u8_s8(off1_hibits)); uint8x16_t initial_under = vcgtq_s8(initial_mins, off1_current_bytes); - int8x16_t second_mins = vqtbl1q_s8(vld1q_s8(_second_mins), vreinterpretq_u8_s8(off1_hibits)); + int8x16_t second_mins = + vqtbl1q_s8(vld1q_s8(_second_mins), vreinterpretq_u8_s8(off1_hibits)); uint8x16_t second_under = vcgtq_s8(second_mins, current_bytes); - *has_error = - vorrq_s8(*has_error, vreinterpretq_s8_u8(vandq_u8(initial_under, second_under))); + *has_error = vorrq_s8( + *has_error, vreinterpretq_s8_u8(vandq_u8(initial_under, second_under))); } struct processed_utf_bytes { - int8x16_t rawbytes; + int8x16_t raw_bytes; int8x16_t high_nibbles; int8x16_t carried_continuations; }; static inline void count_nibbles(int8x16_t bytes, struct processed_utf_bytes *answer) { - answer->rawbytes = bytes; + answer->raw_bytes = bytes; answer->high_nibbles = - vreinterpretq_s8_u8(vshrq_n_u8(vreinterpretq_u8_s8(bytes), 4)); + vreinterpretq_s8_u8(vshrq_n_u8(vreinterpretq_u8_s8(bytes), 4)); } // check whether the current bytes are valid UTF-8 // at the end of the function, previous gets updated static inline struct processed_utf_bytes -checkUTF8Bytes(int8x16_t current_bytes, struct processed_utf_bytes *previous, - int8x16_t *has_error) { +check_utf8_bytes(int8x16_t current_bytes, struct processed_utf_bytes *previous, + int8x16_t *has_error) { struct processed_utf_bytes pb; count_nibbles(current_bytes, &pb); - checkSmallerThan0xF4(current_bytes, has_error); + check_smaller_than_0xF4(current_bytes, has_error); - int8x16_t initial_lengths = continuationLengths(pb.high_nibbles); + int8x16_t initial_lengths = continuation_lengths(pb.high_nibbles); pb.carried_continuations = - carryContinuations(initial_lengths, previous->carried_continuations); + carry_continuations(initial_lengths, previous->carried_continuations); - checkContinuations(initial_lengths, pb.carried_continuations, has_error); + check_continuations(initial_lengths, pb.carried_continuations, has_error); int8x16_t off1_current_bytes = - vextq_s8(previous->rawbytes, pb.rawbytes, 16 - 1); - checkFirstContinuationMax(current_bytes, off1_current_bytes, has_error); + vextq_s8(previous->raw_bytes, pb.raw_bytes, 16 - 1); + check_first_continuation_max(current_bytes, off1_current_bytes, has_error); - checkOverlong(current_bytes, off1_current_bytes, pb.high_nibbles, - previous->high_nibbles, has_error); + check_overlong(current_bytes, off1_current_bytes, pb.high_nibbles, + previous->high_nibbles, has_error); return pb; } -}// simdjson +} // namespace simdjson #endif #endif /* end file include/simdjson/simdutf8check_arm64.h */ @@ -36731,22 +36736,21 @@ namespace simdjson { // out can be the same pointer. Result is null terminated, // return the string length (minus the null termination). // The accelerated version of this function only runs on AVX2 hardware. -size_t jsonminify(const uint8_t *buf, size_t len, uint8_t *out); +size_t json_minify(const uint8_t *buf, size_t len, uint8_t *out); - -static inline size_t jsonminify(const char *buf, size_t len, char *out) { - return jsonminify(reinterpret_cast(buf), len, reinterpret_cast(out)); +static inline size_t json_minify(const char *buf, size_t len, char *out) { + return json_minify(reinterpret_cast(buf), len, + reinterpret_cast(out)); } - -static inline size_t jsonminify(const std::string_view & p, char *out) { - return jsonminify(p.data(), p.size(), out); +static inline size_t json_minify(const std::string_view &p, char *out) { + return json_minify(p.data(), p.size(), out); } -static inline size_t jsonminify(const padded_string & p, char *out) { - return jsonminify(p.data(), p.size(), out); -} +static inline size_t json_minify(const padded_string &p, char *out) { + return json_minify(p.data(), p.size(), out); } +} // namespace simdjson #endif /* end file include/simdjson/jsonminifier.h */ /* begin file include/simdjson/parsedjson.h */ @@ -36758,39 +36762,41 @@ static inline size_t jsonminify(const padded_string & p, char *out) { #include #include #include +#include -#define JSONVALUEMASK 0xFFFFFFFFFFFFFF +#define JSON_VALUE_MASK 0xFFFFFFFFFFFFFF -#define DEFAULTMAXDEPTH 1024// a JSON document with a depth exceeding 1024 is probably de facto invalid +#define DEFAULT_MAX_DEPTH \ + 1024 // a JSON document with a depth exceeding 1024 is probably de facto + // invalid namespace simdjson { /************ * The JSON is parsed to a tape, see the accompanying tape.md file * for documentation. ***********/ -struct ParsedJson { +class ParsedJson { public: - - // create a ParsedJson container with zero capacity, call allocateCapacity to + // create a ParsedJson container with zero capacity, call allocate_capacity to // allocate memory ParsedJson(); ~ParsedJson(); - ParsedJson(ParsedJson && p); + ParsedJson(ParsedJson &&p); // if needed, allocate memory so that the object is able to process JSON - // documents having up to len bytes and maxdepth "depth" + // documents having up to len bytes and max_depth "depth" WARN_UNUSED - bool allocateCapacity(size_t len, size_t maxdepth = DEFAULTMAXDEPTH); + bool allocate_capacity(size_t len, size_t max_depth = DEFAULT_MAX_DEPTH); // returns true if the document parsed was valid - bool isValid() const; + bool is_valid() const; - // return an error code corresponding to the last parsing attempt, see simdjson.h - // will return simdjson::UNITIALIZED if no parsing was attempted - int getErrorCode() const; + // return an error code corresponding to the last parsing attempt, see + // simdjson.h will return simdjson::UNITIALIZED if no parsing was attempted + int get_error_code() const; - // return the string equivalent of "getErrorCode" - std::string getErrorMsg() const; + // return the string equivalent of "get_error_code" + std::string get_error_message() const; // deallocate memory and set capacity to zero, called automatically by the // destructor @@ -36803,11 +36809,10 @@ public: // return false if the tape is likely wrong (e.g., you did not parse a valid // JSON). WARN_UNUSED - bool printjson(std::ostream &os); + bool print_json(std::ostream &os); WARN_UNUSED bool dump_raw_tape(std::ostream &os); - // all nodes are stored on the tape using a 64-bit word. // // strings, double and ints are stored as @@ -36824,43 +36829,42 @@ public: // this should be considered a private function really_inline void write_tape(uint64_t val, uint8_t c) { - tape[current_loc++] = val | ((static_cast(c)) << 56); + tape[current_loc++] = val | ((static_cast(c)) << 56); } really_inline void write_tape_s64(int64_t i) { - write_tape(0, 'l'); - tape[current_loc++] = *(reinterpret_cast(&i)); + write_tape(0, 'l'); + tape[current_loc++] = *(reinterpret_cast(&i)); } really_inline void write_tape_double(double d) { write_tape(0, 'd'); static_assert(sizeof(d) == sizeof(tape[current_loc]), "mismatch size"); - memcpy(& tape[current_loc++], &d, sizeof(double)); - //tape[current_loc++] = *((uint64_t *)&d); + memcpy(&tape[current_loc++], &d, sizeof(double)); + // tape[current_loc++] = *((uint64_t *)&d); } really_inline uint32_t get_current_loc() { return current_loc; } - really_inline void annotate_previousloc(uint32_t saved_loc, uint64_t val) { - tape[saved_loc] |= val; + really_inline void annotate_previous_loc(uint32_t saved_loc, uint64_t val) { + tape[saved_loc] |= val; } - struct InvalidJSON : public std::exception { - const char * what () const throw () { - return "JSON document is invalid"; - } + class InvalidJSON : public std::exception { + const char *what() const throw() { return "JSON document is invalid"; } }; - struct iterator { + class Iterator { // might throw InvalidJSON if ParsedJson is invalid - explicit iterator(ParsedJson &pj_); - ~iterator(); + public: + explicit Iterator(ParsedJson &pj_); + ~Iterator(); - iterator(const iterator &o); + Iterator(const Iterator &o) noexcept; - iterator(iterator &&o); + Iterator(Iterator &&o) noexcept; - inline bool isOk() const; + inline bool is_ok() const; // useful for debuging purposes inline size_t get_tape_location() const; @@ -36868,11 +36872,12 @@ public: // useful for debuging purposes inline size_t get_tape_length() const; - // returns the current depth (start at 1 with 0 reserved for the fictitious root node) + // 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'. + // 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 @@ -36880,81 +36885,66 @@ public: // retrieve the character code of what we're looking at: // [{"sltfn are the possibilities - inline uint8_t get_type() const { - return current_type; // short functions should be inlined! + 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 we're at "l" - inline int64_t get_integer() const { - if(location + 1 >= tape_length) { - return 0;// default value in case of error - } - return static_cast(pj.tape[location + 1]); + inline int64_t get_integer() const { + if (location + 1 >= tape_length) { + return 0; // default value in case of error + } + return static_cast(pj.tape[location + 1]); } // get the string value at this node (NULL ended); valid only if we're at " - // 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(pj.string_buf + (current_val & JSONVALUEMASK) + sizeof(uint32_t)) ; + // 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( + pj.string_buf + (current_val & JSON_VALUE_MASK) + sizeof(uint32_t)); } // return the length of the string in bytes inline uint32_t get_string_length() const { uint32_t answer; - memcpy(&answer, reinterpret_cast(pj.string_buf + (current_val & JSONVALUEMASK)), sizeof(uint32_t)); + memcpy(&answer, + reinterpret_cast(pj.string_buf + + (current_val & JSON_VALUE_MASK)), + sizeof(uint32_t)); return answer; } // get the double value at this node; valid only if // we're at "d" - inline double get_double() const { - if(location + 1 >= tape_length) { - return NAN;// default value in case of error + inline double get_double() const { + if (location + 1 >= tape_length) { + return std::numeric_limits::quiet_NaN(); // default value in + // case of error } double answer; - memcpy(&answer, & pj.tape[location + 1], sizeof(answer)); + memcpy(&answer, &pj.tape[location + 1], sizeof(answer)); return answer; } + inline bool is_object_or_array() const { return is_object() || is_array(); } - inline bool is_object_or_array() const { - return is_object() || is_array(); - } + inline bool is_object() const { return get_type() == '{'; } - inline bool is_object() const { - return get_type() == '{'; - } + inline bool is_array() const { return get_type() == '['; } - inline bool is_array() const { - return get_type() == '['; - } + inline bool is_string() const { return get_type() == '"'; } - inline bool is_string() const { - return get_type() == '"'; - } + inline bool is_integer() const { return get_type() == 'l'; } - inline bool is_integer() const { - return get_type() == 'l'; - } + inline bool is_double() const { return get_type() == 'd'; } - inline bool is_double() const { - return get_type() == 'd'; - } + inline bool is_true() const { return get_type() == 't'; } - inline bool is_true() const { - return get_type() == 't'; - } + inline bool is_false() const { return get_type() == 'f'; } - inline bool is_false() const { - return get_type() == 'f'; - } - - inline bool is_null() const { - return get_type() == 'n'; - } + inline bool is_null() const { return get_type() == 'n'; } static bool is_object_or_array(uint8_t type) { return ((type == '[') || (type == '{')); @@ -36967,33 +36957,76 @@ public: // 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. - inline bool move_to_key(const char * key); + inline bool move_to_key(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. - 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 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 correspoding 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'). + bool move_to(const char *pointer, uint32_t length); + + // Moves the iterator to the value correspoding 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(), pointer.length()); + } + + private: + // Almost the same as move_to(), except it searchs 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(). + bool relative_move_to(const char *pointer, uint32_t length); + + public: // throughout return true if we can do the navigation, false // otherwise // Withing 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). + // 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(); // Withing 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. + // 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. inline bool prev(); // Moves back to either the containing array or object (type { or [) from @@ -37001,42 +37034,47 @@ public: // 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. + // 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()) + ; + } + // void to_end_scope(); // move us to // the start of our current scope; always succeeds // print the thing we're currently pointing at bool print(std::ostream &os, bool escape_strings = true) const; - typedef struct {size_t start_of_scope; uint8_t scope_type;} scopeindex_t; + typedef struct { + size_t start_of_scope; + uint8_t scope_type; + } scopeindex_t; -private: - - iterator& operator=(const iterator& other) = delete ; + private: + Iterator &operator=(const Iterator &other) = delete; ParsedJson &pj; size_t depth; - size_t location; // our current location on a tape + size_t location; // our current location on a tape size_t tape_length; uint8_t current_type; uint64_t current_val; - scopeindex_t *depthindex; + scopeindex_t *depth_index; }; - size_t bytecapacity{0}; // indicates how many bits are meant to be supported + size_t byte_capacity{0}; // indicates how many bits are meant to be supported - size_t depthcapacity{0}; // how deep we can go - size_t tapecapacity{0}; - size_t stringcapacity{0}; + size_t depth_capacity{0}; // how deep we can go + size_t tape_capacity{0}; + size_t string_capacity{0}; uint32_t current_loc{0}; uint32_t n_structural_indexes{0}; @@ -37046,24 +37084,23 @@ private: uint32_t *containing_scope_offset; #ifdef SIMDJSON_USE_COMPUTED_GOTO void **ret_address; -#else +#else char *ret_address; #endif - uint8_t *string_buf; // should be at least bytecapacity + uint8_t *string_buf; // should be at least byte_capacity uint8_t *current_string_buf_loc; - bool isvalid{false}; - int errorcode{simdjson::UNITIALIZED}; + bool valid{false}; + int error_code{simdjson::UNITIALIZED}; -private : - - // we don't want the default constructor to be called - ParsedJson(const ParsedJson & p) = delete; // we don't want the default constructor to be called - // we don't want the assignment to be called - ParsedJson & operator=(const ParsedJson&o) = delete; +private: + // we don't want the default constructor to be called + ParsedJson(const ParsedJson &p) = + delete; // we don't want the default constructor to be called + // we don't want the assignment to be called + ParsedJson &operator=(const ParsedJson &o) = delete; }; - // dump bits low to high inline void dumpbits_always(uint64_t v, const std::string &msg) { for (uint32_t i = 0; i < 64; i++) { @@ -37080,174 +37117,182 @@ inline void dumpbits32_always(uint32_t v, const std::string &msg) { } WARN_UNUSED -bool ParsedJson::iterator::isOk() const { - return location < tape_length; -} +bool ParsedJson::Iterator::is_ok() const { return location < tape_length; } // useful for debuging purposes -size_t ParsedJson::iterator::get_tape_location() const { - return location; -} +size_t ParsedJson::Iterator::get_tape_location() const { return location; } // useful for debuging purposes -size_t ParsedJson::iterator::get_tape_length() const { - return tape_length; +size_t ParsedJson::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 ParsedJson::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 ParsedJson::Iterator::get_scope_type() const { + return depth_index[depth].scope_type; } -// returns the current depth (start at 1 with 0 reserved for the fictitious root node) -size_t ParsedJson::iterator::get_depth() const { - return depth; -} +bool ParsedJson::Iterator::move_forward() { + if (location + 1 >= tape_length) { + return false; // we are at the end! + } -// 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 ParsedJson::iterator::get_scope_type() const { - return depthindex[depth].scope_type; -} - -bool ParsedJson::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++; - depthindex[depth].start_of_scope = location; - depthindex[depth].scope_type = current_type; - } else if ((current_type == ']') || (current_type == '}')) { - // Leaving a scope. - depth--; - } else if ((current_type == 'd') || (current_type == 'l')) { - // d and l types use 2 locations on the tape, not just one. - location += 1; - } - - location += 1; - current_val = pj.tape[location]; - current_type = (current_val >> 56); - return true; -} - -void ParsedJson::iterator::move_to_value() { - // assume that we are on a key, so move by 1. - location += 1; - current_val = pj.tape[location]; - current_type = (current_val >> 56); -} - - -bool ParsedJson::iterator::move_to_key(const char * key) { - if(down()) { - do { - assert(is_string()); - bool rightkey = (strcmp(get_string(),key)==0);// null chars would fool this - move_to_value(); - if(rightkey) { - return true; - } - } while(next()); - assert(up());// not found - } - return false; -} - -bool ParsedJson::iterator::move_to_key(const char * key, uint32_t length) { - if(down()) { - do { - assert(is_string()); - bool rightkey = ((get_string_length() == length) && (memcmp(get_string(),key,length)==0)); - move_to_value(); - if(rightkey) { - return true; - } - } while(next()); - assert(up());// not found - } - return false; -} - - - bool ParsedJson::iterator::prev() { - if(location - 1 < depthindex[depth].start_of_scope) { - return false; - } - location -= 1; - current_val = pj.tape[location]; - current_type = (current_val >> 56); - if ((current_type == ']') || (current_type == '}')){ - // we need to jump - size_t new_location = ( current_val & JSONVALUEMASK); - if(new_location < depthindex[depth].start_of_scope) { - return false; // shoud never happen - } - location = new_location; - current_val = pj.tape[location]; - current_type = (current_val >> 56); - } - return true; -} - - - bool ParsedJson::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 + if ((current_type == '[') || (current_type == '{')) { + // We are entering a new scope + 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--; - location -= 1; - current_val = pj.tape[location]; - current_type = (current_val >> 56); - return true; + } else if ((current_type == 'd') || (current_type == 'l')) { + // d and l types use 2 locations on the tape, not just one. + location += 1; + } + + location += 1; + current_val = pj.tape[location]; + current_type = (current_val >> 56); + return true; } +void ParsedJson::Iterator::move_to_value() { + // assume that we are on a key, so move by 1. + location += 1; + current_val = pj.tape[location]; + current_type = (current_val >> 56); +} - bool ParsedJson::iterator::down() { - if(location + 1 >= tape_length) { - return false; - } - if ((current_type == '[') || (current_type == '{')) { - size_t npos = (current_val & JSONVALUEMASK); - if(npos == location + 2) { - return false; // we have an empty scope +bool ParsedJson::Iterator::move_to_key(const char *key) { + if (down()) { + do { + assert(is_string()); + bool right_key = + (strcmp(get_string(), key) == 0); // null chars would fool this + move_to_value(); + if (right_key) { + return true; } - depth++; - location = location + 1; - depthindex[depth].start_of_scope = location; - depthindex[depth].scope_type = current_type; - current_val = pj.tape[location]; - current_type = (current_val >> 56); + } while (next()); + assert(up()); // not found + } + return false; +} + +bool ParsedJson::Iterator::move_to_key(const char *key, uint32_t length) { + if (down()) { + do { + assert(is_string()); + bool right_key = ((get_string_length() == length) && + (memcmp(get_string(), key, length) == 0)); + move_to_value(); + if (right_key) { + return true; + } + } while (next()); + assert(up()); // not found + } + return false; +} + +bool ParsedJson::Iterator::move_to_index(uint32_t index) { + assert(is_array()); + if (down()) { + uint32_t i = 0; + for (; i < index; i++) { + if (!next()) { + break; + } + } + if (i == index) { return true; } - return false; + assert(up()); + } + return false; } -void ParsedJson::iterator::to_start_scope() { - location = depthindex[depth].start_of_scope; +bool ParsedJson::Iterator::prev() { + if (location - 1 < depth_index[depth].start_of_scope) { + return false; + } + location -= 1; + current_val = pj.tape[location]; + current_type = (current_val >> 56); + if ((current_type == ']') || (current_type == '}')) { + // we need to jump + size_t new_location = (current_val & JSON_VALUE_MASK); + if (new_location < depth_index[depth].start_of_scope) { + return false; // shoud never happen + } + location = new_location; current_val = pj.tape[location]; current_type = (current_val >> 56); + } + return true; } -bool ParsedJson::iterator::next() { - size_t npos; - if ((current_type == '[') || (current_type == '{')){ - // we need to jump - npos = ( current_val & JSONVALUEMASK); - } else { - npos = location + ((current_type == 'd' || current_type == 'l') ? 2 : 1); +bool ParsedJson::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 = pj.tape[location]; + current_type = (current_val >> 56); + return true; +} + +bool ParsedJson::Iterator::down() { + if (location + 1 >= tape_length) { + return false; + } + if ((current_type == '[') || (current_type == '{')) { + size_t npos = (current_val & JSON_VALUE_MASK); + if (npos == location + 2) { + return false; // we have an empty scope } - uint64_t nextval = pj.tape[npos]; - uint8_t nexttype = (nextval >> 56); - if((nexttype == ']') || (nexttype == '}')) { - return false; // we reached the end of the scope - } - location = npos; - current_val = nextval; - current_type = nexttype; + depth++; + location = location + 1; + depth_index[depth].start_of_scope = location; + depth_index[depth].scope_type = current_type; + current_val = pj.tape[location]; + current_type = (current_val >> 56); return true; + } + return false; } + +void ParsedJson::Iterator::to_start_scope() { + location = depth_index[depth].start_of_scope; + current_val = pj.tape[location]; + current_type = (current_val >> 56); } + +bool ParsedJson::Iterator::next() { + size_t npos; + if ((current_type == '[') || (current_type == '{')) { + // we need to jump + npos = (current_val & JSON_VALUE_MASK); + } else { + npos = location + ((current_type == 'd' || current_type == 'l') ? 2 : 1); + } + uint64_t next_val = pj.tape[npos]; + uint8_t next_type = (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; +} +} // namespace simdjson #endif /* end file include/simdjson/parsedjson.h */ /* begin file include/simdjson/stage1_find_marks.h */ @@ -37258,59 +37303,52 @@ bool ParsedJson::iterator::next() { namespace simdjson { -template -struct simd_input; +template struct simd_input; -template -uint64_t compute_quote_mask(uint64_t quote_bits); +template uint64_t compute_quote_mask(uint64_t quote_bits); namespace { - // for when clmul is unavailable - [[maybe_unused]] uint64_t portable_compute_quote_mask(uint64_t quote_bits) { - uint64_t quote_mask = quote_bits ^ (quote_bits << 1); - quote_mask = quote_mask ^ (quote_mask << 2); - quote_mask = quote_mask ^ (quote_mask << 4); - quote_mask = quote_mask ^ (quote_mask << 8); - quote_mask = quote_mask ^ (quote_mask << 16); - quote_mask = quote_mask ^ (quote_mask << 32); - return quote_mask; - } +// for when clmul is unavailable +[[maybe_unused]] uint64_t portable_compute_quote_mask(uint64_t quote_bits) { + uint64_t quote_mask = quote_bits ^ (quote_bits << 1); + quote_mask = quote_mask ^ (quote_mask << 2); + quote_mask = quote_mask ^ (quote_mask << 4); + quote_mask = quote_mask ^ (quote_mask << 8); + quote_mask = quote_mask ^ (quote_mask << 16); + quote_mask = quote_mask ^ (quote_mask << 32); + return quote_mask; } +} // namespace // Holds the state required to perform check_utf8(). -template -struct utf8_checking_state; +template struct utf8_checking_state; - -template -void check_utf8(simd_input in, utf8_checking_state& state); +template +void check_utf8(simd_input in, utf8_checking_state &state); // Checks if the utf8 validation has found any error. -template -errorValues check_utf8_errors(utf8_checking_state& state); +template +ErrorValues check_utf8_errors(utf8_checking_state &state); -// a straightforward comparison of a mask against input. -template +// a straightforward comparison of a mask against input. +template uint64_t cmp_mask_against_input(simd_input in, uint8_t m); - -template -simd_input fill_input(const uint8_t * ptr); +template simd_input fill_input(const uint8_t *ptr); - -// find all values less than or equal than the content of maxval (using unsigned arithmetic) -template +// find all values less than or equal than the content of maxval (using unsigned +// arithmetic) +template uint64_t unsigned_lteq_against_input(simd_input in, uint8_t m); +template +really_inline uint64_t find_odd_backslash_sequences( + simd_input in, uint64_t &prev_iter_ends_odd_backslash); -template really_inline -uint64_t find_odd_backslash_sequences(simd_input in, uint64_t &prev_iter_ends_odd_backslash); - - -template really_inline -uint64_t find_quote_mask_and_bits(simd_input in, uint64_t odd_ends, - uint64_t &prev_iter_inside_quote, uint64_t "e_bits, uint64_t &error_mask); - +template +really_inline uint64_t find_quote_mask_and_bits( + simd_input in, uint64_t odd_ends, uint64_t &prev_iter_inside_quote, + uint64_t "e_bits, uint64_t &error_mask); // do a 'shufti' to detect structural JSON characters // they are { 0x7b } 0x7d : 0x3a [ 0x5b ] 0x5d , 0x2c @@ -37319,9 +37357,8 @@ uint64_t find_quote_mask_and_bits(simd_input in, uint64_t odd_ends, // we are also interested in the four whitespace characters // space 0x20, linefeed 0x0a, horizontal tab 0x09 and carriage return 0x0d // these go into the next 2 buckets of the comparison (8/16) -template -void find_whitespace_and_structurals(simd_input in, - uint64_t &whitespace, +template +void find_whitespace_and_structurals(simd_input in, uint64_t &whitespace, uint64_t &structurals); // return a updated structural bit vector with quoted contents cleared out and @@ -37335,7 +37372,7 @@ really_inline uint64_t finalize_structurals( uint64_t quote_bits, uint64_t &prev_iter_ends_pseudo_pred) { // mask off anything inside quotes structurals &= ~quote_mask; - // add the real quote bits back into our bitmask as well, so we can + // add the real quote bits back into our bit_mask as well, so we can // quickly traverse the strings we've spent all this trouble gathering structurals |= quote_bits; // Now, establish "pseudo-structural characters". These are non-whitespace @@ -37363,23 +37400,18 @@ really_inline uint64_t finalize_structurals( return structurals; } -template -int find_structural_bits(const uint8_t *buf, size_t len, simdjson::ParsedJson &pj); +template +int find_structural_bits(const uint8_t *buf, size_t len, + simdjson::ParsedJson &pj); -template -int find_structural_bits(const char *buf, size_t len, simdjson::ParsedJson &pj) { - return find_structural_bits((const uint8_t*)buf, len, pj); +template +int find_structural_bits(const char *buf, size_t len, + simdjson::ParsedJson &pj) { + return find_structural_bits((const uint8_t *)buf, len, pj); } - } // namespace simdjson - - - - - - #endif /* end file include/simdjson/stage1_find_marks.h */ /* begin file include/simdjson/stage1_find_marks_flatten.h */ @@ -37395,17 +37427,17 @@ namespace simdjson { // again our optimized version. really_inline void flatten_bits(uint32_t *base_ptr, uint32_t &base, uint32_t idx, uint64_t bits) { - uint32_t * out_ptr = base_ptr + base; + uint32_t *out_ptr = base_ptr + base; idx -= 64; - while(bits != 0) { - out_ptr[0] = idx + trailingzeroes(bits); - bits = bits & (bits - 1); - out_ptr++; + while (bits != 0) { + out_ptr[0] = idx + trailing_zeroes(bits); + bits = bits & (bits - 1); + out_ptr++; } base = (out_ptr - base_ptr); } -#else +#else // 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 @@ -37413,69 +37445,165 @@ really_inline void flatten_bits(uint32_t *base_ptr, uint32_t &base, // needs to be large enough to handle this really_inline void flatten_bits(uint32_t *base_ptr, uint32_t &base, uint32_t idx, uint64_t bits) { - // In some instances, the next branch is expensive because it is mispredicted. + // In some instances, the next branch is expensive because it is mispredicted. // Unfortunately, in other cases, // it helps tremendously. - if(bits == 0) return; + if (bits == 0) + return; uint32_t cnt = hamming(bits); uint32_t next_base = base + cnt; idx -= 64; base_ptr += base; - { - base_ptr[0] = idx + trailingzeroes(bits); + { + base_ptr[0] = idx + trailing_zeroes(bits); bits = bits & (bits - 1); - base_ptr[1] = idx + trailingzeroes(bits); + base_ptr[1] = idx + trailing_zeroes(bits); bits = bits & (bits - 1); - base_ptr[2] = idx + trailingzeroes(bits); + base_ptr[2] = idx + trailing_zeroes(bits); bits = bits & (bits - 1); - base_ptr[3] = idx + trailingzeroes(bits); + base_ptr[3] = idx + trailing_zeroes(bits); bits = bits & (bits - 1); - base_ptr[4] = idx + trailingzeroes(bits); + base_ptr[4] = idx + trailing_zeroes(bits); bits = bits & (bits - 1); - base_ptr[5] = idx + trailingzeroes(bits); + base_ptr[5] = idx + trailing_zeroes(bits); bits = bits & (bits - 1); - base_ptr[6] = idx + trailingzeroes(bits); + base_ptr[6] = idx + trailing_zeroes(bits); bits = bits & (bits - 1); - base_ptr[7] = idx + trailingzeroes(bits); + base_ptr[7] = idx + trailing_zeroes(bits); bits = bits & (bits - 1); base_ptr += 8; } // We hope that the next branch is easily predicted. if (cnt > 8) { - base_ptr[0] = idx + trailingzeroes(bits); + base_ptr[0] = idx + trailing_zeroes(bits); bits = bits & (bits - 1); - base_ptr[1] = idx + trailingzeroes(bits); + base_ptr[1] = idx + trailing_zeroes(bits); bits = bits & (bits - 1); - base_ptr[2] = idx + trailingzeroes(bits); + base_ptr[2] = idx + trailing_zeroes(bits); bits = bits & (bits - 1); - base_ptr[3] = idx + trailingzeroes(bits); + base_ptr[3] = idx + trailing_zeroes(bits); bits = bits & (bits - 1); - base_ptr[4] = idx + trailingzeroes(bits); + base_ptr[4] = idx + trailing_zeroes(bits); bits = bits & (bits - 1); - base_ptr[5] = idx + trailingzeroes(bits); + base_ptr[5] = idx + trailing_zeroes(bits); bits = bits & (bits - 1); - base_ptr[6] = idx + trailingzeroes(bits); + base_ptr[6] = idx + trailing_zeroes(bits); bits = bits & (bits - 1); - base_ptr[7] = idx + trailingzeroes(bits); + base_ptr[7] = idx + trailing_zeroes(bits); bits = bits & (bits - 1); base_ptr += 8; } if (cnt > 16) { // unluckly: we rarely get here - // since it means having one structural or pseudo-structral element + // since it means having one structural or pseudo-structral element // every 4 characters (possible with inputs like "","","",...). do { - base_ptr[0] = idx + trailingzeroes(bits); + base_ptr[0] = idx + trailing_zeroes(bits); bits = bits & (bits - 1); base_ptr++; - } while(bits != 0); + } while (bits != 0); } base = next_base; } #endif // SIMDJSON_NAIVE_FLATTEN -} +} // namespace simdjson #endif // SIMDJSON_STAGE1_FIND_MARKS_FLATTEN_H /* end file include/simdjson/stage1_find_marks_flatten.h */ +/* begin file include/simdjson/stage1_find_marks_flatten_haswell.h */ +#ifndef SIMDJSON_STAGE1_FIND_MARKS_FLATTEN_HASWELL_H +#define SIMDJSON_STAGE1_FIND_MARKS_FLATTEN_HASWELL_H + +// This file provides the same function as +// stage1_find_marks_flatten.h, but uses Intel intrinsics. +// This should provide better performance on Visual Studio +// and other compilers that do a conservative optimization. + +// Specifically, on x64 processors with BMI, +// x & (x - 1) should be mapped to +// the blsr instruction. By using the +// _blsr_u64 intrinsic, we +// ensure that this will happen. +///////// + + +#ifdef IS_X86_64 + +TARGET_HASWELL +namespace simdjson { +namespace haswell { + +// 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 +really_inline void flatten_bits(uint32_t *base_ptr, uint32_t &base, + 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; + uint32_t cnt = _mm_popcnt_u64(bits); + uint32_t next_base = base + cnt; + idx -= 64; + base_ptr += base; + { + base_ptr[0] = idx + trailing_zeroes(bits); + bits = _blsr_u64(bits); + base_ptr[1] = idx + trailing_zeroes(bits); + bits = _blsr_u64(bits); + base_ptr[2] = idx + trailing_zeroes(bits); + bits = _blsr_u64(bits); + base_ptr[3] = idx + trailing_zeroes(bits); + bits = _blsr_u64(bits); + base_ptr[4] = idx + trailing_zeroes(bits); + bits = _blsr_u64(bits); + base_ptr[5] = idx + trailing_zeroes(bits); + bits = _blsr_u64(bits); + base_ptr[6] = idx + trailing_zeroes(bits); + bits = _blsr_u64(bits); + base_ptr[7] = idx + trailing_zeroes(bits); + bits = _blsr_u64(bits); + base_ptr += 8; + } + // We hope that the next branch is easily predicted. + if (cnt > 8) { + base_ptr[0] = idx + trailing_zeroes(bits); + bits = _blsr_u64(bits); + base_ptr[1] = idx + trailing_zeroes(bits); + bits = _blsr_u64(bits); + base_ptr[2] = idx + trailing_zeroes(bits); + bits = _blsr_u64(bits); + base_ptr[3] = idx + trailing_zeroes(bits); + bits = _blsr_u64(bits); + base_ptr[4] = idx + trailing_zeroes(bits); + bits = _blsr_u64(bits); + base_ptr[5] = idx + trailing_zeroes(bits); + bits = _blsr_u64(bits); + base_ptr[6] = idx + trailing_zeroes(bits); + bits = _blsr_u64(bits); + base_ptr[7] = idx + trailing_zeroes(bits); + bits = _blsr_u64(bits); + base_ptr += 8; + } + if (cnt > 16) { // unluckly: we rarely get here + // since it means having one structural or pseudo-structral element + // every 4 characters (possible with inputs like "","","",...). + do { + base_ptr[0] = idx + trailing_zeroes(bits); + bits = _blsr_u64(bits); + base_ptr++; + } while (bits != 0); + } + base = next_base; +} +} // namespace haswell +} // namespace simdjson +UNTARGET_REGION +#endif // IS_X86_64 +#endif // SIMDJSON_STAGE1_FIND_MARKS_FLATTEN_H +/* end file include/simdjson/stage1_find_marks_flatten_haswell.h */ /* begin file include/simdjson/stage1_find_marks_macros.h */ #ifndef SIMDJSON_STAGE1_FIND_MARKS_MACROS_H #define SIMDJSON_STAGE1_FIND_MARKS_MACROS_H @@ -37489,41 +37617,44 @@ really_inline void flatten_bits(uint32_t *base_ptr, uint32_t &base, // indicate whether we end an iteration on an odd-length sequence of // backslashes, which modifies our subsequent search for odd-length // sequences of backslashes in an obvious way. -// We need to compile that code for multiple architectures. However, target attributes can be used -// only once by function definition. Huge macro seemed better than huge code duplication. -// uint64_t FIND_ODD_BACKSLASH_SEQUENCES(architecture T, simd_input in, uint64_t &prev_iter_ends_odd_backslash) -#define FIND_ODD_BACKSLASH_SEQUENCES(T, in, prev_iter_ends_odd_backslash) { \ - const uint64_t even_bits = 0x5555555555555555ULL; \ - const uint64_t odd_bits = ~even_bits; \ - uint64_t bs_bits = cmp_mask_against_input(in, '\\'); \ - uint64_t start_edges = bs_bits & ~(bs_bits << 1); \ - /* flip lowest if we have an odd-length run at the end of the prior */ \ - /* iteration */ \ - uint64_t even_start_mask = even_bits ^ prev_iter_ends_odd_backslash; \ - uint64_t even_starts = start_edges & even_start_mask; \ - uint64_t odd_starts = start_edges & ~even_start_mask; \ - uint64_t even_carries = bs_bits + even_starts; \ - \ - uint64_t odd_carries; \ - /* must record the carry-out of our odd-carries out of bit 63; this */ \ - /* indicates whether the sense of any edge going to the next iteration */ \ - /* should be flipped */ \ - bool iter_ends_odd_backslash = \ - add_overflow(bs_bits, odd_starts, &odd_carries); \ - \ - odd_carries |= \ - prev_iter_ends_odd_backslash; /* push in bit zero as a potential end */ \ - /* if we had an odd-numbered run at the */ \ - /* end of the previous iteration */ \ - prev_iter_ends_odd_backslash = iter_ends_odd_backslash ? 0x1ULL : 0x0ULL; \ - uint64_t even_carry_ends = even_carries & ~bs_bits; \ - uint64_t odd_carry_ends = odd_carries & ~bs_bits; \ - uint64_t even_start_odd_end = even_carry_ends & odd_bits; \ - uint64_t odd_start_even_end = odd_carry_ends & even_bits; \ - uint64_t odd_ends = even_start_odd_end | odd_start_even_end; \ - return odd_ends; \ -} - +// We need to compile that code for multiple architectures. However, target +// attributes can be used only once by function definition. Huge macro seemed +// better than huge code duplication. uint64_t +// FIND_ODD_BACKSLASH_SEQUENCES(Architecture T, simd_input in, uint64_t +// &prev_iter_ends_odd_backslash) +#define FIND_ODD_BACKSLASH_SEQUENCES(T, in, prev_iter_ends_odd_backslash) \ + { \ + const uint64_t even_bits = 0x5555555555555555ULL; \ + const uint64_t odd_bits = ~even_bits; \ + uint64_t bs_bits = cmp_mask_against_input(in, '\\'); \ + uint64_t start_edges = bs_bits & ~(bs_bits << 1); \ + /* flip lowest if we have an odd-length run at the end of the prior \ + * iteration */ \ + uint64_t even_start_mask = even_bits ^ prev_iter_ends_odd_backslash; \ + uint64_t even_starts = start_edges & even_start_mask; \ + uint64_t odd_starts = start_edges & ~even_start_mask; \ + uint64_t even_carries = bs_bits + even_starts; \ + \ + uint64_t odd_carries; \ + /* must record the carry-out of our odd-carries out of bit 63; this \ + * indicates whether the sense of any edge going to the next iteration \ + * should be flipped */ \ + bool iter_ends_odd_backslash = \ + add_overflow(bs_bits, odd_starts, &odd_carries); \ + \ + odd_carries |= prev_iter_ends_odd_backslash; /* push in bit zero as a \ + * potential end if we had an \ + * odd-numbered run at the \ + * end of the previous \ + * iteration */ \ + prev_iter_ends_odd_backslash = iter_ends_odd_backslash ? 0x1ULL : 0x0ULL; \ + uint64_t even_carry_ends = even_carries & ~bs_bits; \ + uint64_t odd_carry_ends = odd_carries & ~bs_bits; \ + uint64_t even_start_odd_end = even_carry_ends & odd_bits; \ + uint64_t odd_start_even_end = odd_carry_ends & even_bits; \ + uint64_t odd_ends = even_start_odd_end | odd_start_even_end; \ + return odd_ends; \ + } // return both the quote mask (which is a half-open mask that covers the first // quote @@ -37537,164 +37668,180 @@ really_inline void flatten_bits(uint32_t *base_ptr, uint32_t &base, // Note that we don't do any error checking to see if we have backslash // sequences outside quotes; these // backslash sequences (of any length) will be detected elsewhere. -// We need to compile that code for multiple architectures. However, target attributes can be used -// only once by function definition. Huge macro seemed better than huge code duplication. -// uint64_t FIND_QUOTE_MASK_AND_BITS(architecture T, simd_input in, uint64_t odd_ends, -// uint64_t &prev_iter_inside_quote, uint64_t "e_bits, uint64_t &error_mask) -#define FIND_QUOTE_MASK_AND_BITS(T, in, odd_ends, prev_iter_inside_quote, quote_bits, error_mask) { \ - quote_bits = cmp_mask_against_input(in, '"'); \ - quote_bits = quote_bits & ~odd_ends; \ - uint64_t quote_mask = compute_quote_mask(quote_bits); \ - quote_mask ^= prev_iter_inside_quote; \ - /* All Unicode characters may be placed within the */ \ - /* quotation marks, except for the characters that MUST be escaped: */ \ - /* quotation mark, reverse solidus, and the control characters (U+0000 */ \ - /*through U+001F). */ \ - /* https://tools.ietf.org/html/rfc8259 */ \ - uint64_t unescaped = unsigned_lteq_against_input(in, 0x1F); \ - error_mask |= quote_mask & unescaped; \ - /* 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_iter_inside_quote = \ - static_cast(static_cast(quote_mask) >> 63); \ - return quote_mask; \ -} \ +// We need to compile that code for multiple architectures. However, target +// attributes can be used only once by function definition. Huge macro seemed +// better than huge code duplication. uint64_t +// FIND_QUOTE_MASK_AND_BITS(Architecture T, simd_input in, uint64_t odd_ends, +// uint64_t &prev_iter_inside_quote, uint64_t "e_bits, uint64_t +// &error_mask) +#define FIND_QUOTE_MASK_AND_BITS(T, in, odd_ends, prev_iter_inside_quote, \ + quote_bits, error_mask) \ + { \ + quote_bits = cmp_mask_against_input(in, '"'); \ + quote_bits = quote_bits & ~odd_ends; \ + uint64_t quote_mask = compute_quote_mask(quote_bits); \ + quote_mask ^= prev_iter_inside_quote; \ + /* All Unicode characters may be placed within the \ + * quotation marks, except for the characters that MUST be escaped: \ + * quotation mark, reverse solidus, and the control characters (U+0000 \ + * through U+001F). \ + * https://tools.ietf.org/html/rfc8259 */ \ + uint64_t unescaped = unsigned_lteq_against_input(in, 0x1F); \ + error_mask |= quote_mask & unescaped; \ + /* 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_iter_inside_quote = \ + static_cast(static_cast(quote_mask) >> 63); \ + return quote_mask; \ + } +// Find structural bits in a 64-byte chunk. +// We need to compile that code for multiple architectures. However, target +// attributes can be used only once by function definition. Huge macro seemed +// better than huge code duplication. void FIND_STRUCTURAL_BITS_64( +// Architecture T, +// const uint8_t *buf, +// size_t idx, +// uint32_t *base_ptr, +// uint32_t &base, +// uint64_t &prev_iter_ends_odd_backslash, +// uint64_t &prev_iter_inside_quote, +// uint64_t &prev_iter_ends_pseudo_pred, +// uint64_t &structurals, +// uint64_t &error_mask, +// utf8_checking_state &utf8_state, flatten +// function) +#define FIND_STRUCTURAL_BITS_64( \ + T, buf, idx, base_ptr, base, prev_iter_ends_odd_backslash, \ + prev_iter_inside_quote, prev_iter_ends_pseudo_pred, structurals, \ + error_mask, utf8_state, flat) \ + { \ + simd_input in = fill_input(buf); \ + check_utf8(in, utf8_state); \ + /* detect odd sequences of backslashes */ \ + uint64_t odd_ends = \ + find_odd_backslash_sequences(in, prev_iter_ends_odd_backslash); \ + \ + /* detect insides of quote pairs ("quote_mask") and also our quote_bits \ + * themselves */ \ + uint64_t quote_bits; \ + uint64_t quote_mask = find_quote_mask_and_bits( \ + in, odd_ends, prev_iter_inside_quote, quote_bits, error_mask); \ + \ + /* take the previous iterations structural bits, not our current \ + * iteration, \ + * and flatten */ \ + flat(base_ptr, base, idx, structurals); \ + \ + uint64_t whitespace; \ + find_whitespace_and_structurals(in, whitespace, structurals); \ + \ + /* fixup structurals to reflect quotes and add pseudo-structural \ + * characters */ \ + structurals = \ + finalize_structurals(structurals, whitespace, quote_mask, quote_bits, \ + prev_iter_ends_pseudo_pred); \ + } - -// We need to compile that code for multiple architectures. However, target attributes can be used -// only once by function definition. Huge macro seemed better than huge code duplication. -// FIND_STRUCTURAL_BITS(architecture T, const uint8_t *buf, size_t len, ParsedJson &pj) -#define FIND_STRUCTURAL_BITS(T, buf, len, pj) { \ - if (len > pj.bytecapacity) { \ - std::cerr << "Your ParsedJson object only supports documents up to " \ - << pj.bytecapacity << " bytes but you are trying to process " << len \ - << " bytes" << std::endl; \ - return simdjson::CAPACITY; \ - } \ - uint32_t *base_ptr = pj.structural_indexes; \ - uint32_t base = 0; \ - utf8_checking_state state; \ - \ - /* we have padded the input out to 64 byte multiple with the remainder being */ \ - /* zeros */ \ - \ - /* persistent state across loop */ \ - /* does the last iteration end with an odd-length sequence of backslashes? */ \ - /* either 0 or 1, but a 64-bit value */ \ - uint64_t prev_iter_ends_odd_backslash = 0ULL; \ - /* does the previous iteration end inside a double-quote pair? */ \ - uint64_t prev_iter_inside_quote = 0ULL; /* either all zeros or all ones */ \ - /* does the previous iteration end on something that is a predecessor of a */ \ - /* pseudo-structural character - i.e. whitespace or a structural character */ \ - /* effectively the very first char is considered to follow "whitespace" for */ \ - /* the */ \ - /* purposes of pseudo-structural character detection so we initialize to 1 */ \ - uint64_t prev_iter_ends_pseudo_pred = 1ULL; \ - \ - /* structurals are persistent state across loop as we flatten them on the */ \ - /* subsequent iteration into our array pointed to be base_ptr. */ \ - /* This is harmless on the first iteration as structurals==0 */ \ - /* and is done for performance reasons; we can hide some of the latency of the */ \ - /* expensive carryless multiply in the previous step with this work */ \ - uint64_t structurals = 0; \ - \ - size_t lenminus64 = len < 64 ? 0 : len - 64; \ - size_t idx = 0; \ - uint64_t error_mask = 0; /* for unescaped characters within strings (ASCII code points < 0x20) */ \ - \ - for (; idx < lenminus64; idx += 64) { \ - \ - simd_input in = fill_input(buf+idx); \ - check_utf8(in, state); \ - /* detect odd sequences of backslashes */ \ - uint64_t odd_ends = find_odd_backslash_sequences( \ - in, prev_iter_ends_odd_backslash); \ - \ - /* detect insides of quote pairs ("quote_mask") and also our quote_bits */ \ - /* themselves */ \ - uint64_t quote_bits; \ - uint64_t quote_mask = find_quote_mask_and_bits( \ - in, odd_ends, prev_iter_inside_quote, quote_bits, error_mask); \ - \ - /* take the previous iterations structural bits, not our current iteration, */ \ - /* and flatten */ \ - flatten_bits(base_ptr, base, idx, structurals); \ - \ - uint64_t whitespace; \ - find_whitespace_and_structurals(in, whitespace, structurals); \ - \ - /* fixup structurals to reflect quotes and add pseudo-structural characters */ \ - structurals = finalize_structurals(structurals, whitespace, quote_mask, \ - quote_bits, prev_iter_ends_pseudo_pred); \ - } \ - \ - /*////////////// */ \ - /*/ we use a giant copy-paste which is ugly. */ \ - /*/ but otherwise the string needs to be properly padded or else we */ \ - /*/ risk invalidating the UTF-8 checks. */ \ - /*////////// */ \ - if (idx < len) { \ - uint8_t tmpbuf[64]; \ - memset(tmpbuf, 0x20, 64); \ - memcpy(tmpbuf, buf + idx, len - idx); \ - simd_input in = fill_input(tmpbuf); \ - check_utf8(in, state); \ - \ - /* detect odd sequences of backslashes */ \ - uint64_t odd_ends = find_odd_backslash_sequences( \ - in, prev_iter_ends_odd_backslash); \ - \ - /* detect insides of quote pairs ("quote_mask") and also our quote_bits */ \ - /* themselves */ \ - uint64_t quote_bits; \ - uint64_t quote_mask = find_quote_mask_and_bits( \ - in, odd_ends, prev_iter_inside_quote, quote_bits, error_mask); \ - \ - /* take the previous iterations structural bits, not our current iteration, */ \ - /* and flatten */ \ - flatten_bits(base_ptr, base, idx, structurals); \ - \ - uint64_t whitespace; \ - find_whitespace_and_structurals(in, whitespace, structurals); \ - \ - /* fixup structurals to reflect quotes and add pseudo-structural characters */ \ - structurals = finalize_structurals(structurals, whitespace, quote_mask, \ - quote_bits, prev_iter_ends_pseudo_pred); \ - idx += 64; \ - } \ - \ - /* is last string quote closed? */ \ - if (prev_iter_inside_quote) { \ - return simdjson::UNCLOSED_STRING; \ - } \ - \ - /* finally, flatten out the remaining structurals from the last iteration */ \ - flatten_bits(base_ptr, base, idx, structurals); \ - \ - pj.n_structural_indexes = base; \ - /* a valid JSON file cannot have zero structural indexes - we should have */ \ - /* found something */ \ - if (pj.n_structural_indexes == 0u) { \ - return simdjson::EMPTY; \ - } \ - if (base_ptr[pj.n_structural_indexes - 1] > len) { \ - return simdjson::UNEXPECTED_ERROR; \ - } \ - if (len != base_ptr[pj.n_structural_indexes - 1]) { \ - /* the string might not be NULL terminated, but we add a virtual NULL ending */ \ - /* character. */ \ - base_ptr[pj.n_structural_indexes++] = len; \ - } \ - /* make it safe to dereference one beyond this array */ \ - base_ptr[pj.n_structural_indexes] = 0; \ - if (error_mask) { \ - return simdjson::UNESCAPED_CHARS; \ - } \ - return check_utf8_errors(state); \ -} - +// We need to compile that code for multiple architectures. However, target +// attributes can be used only once by function definition. Huge macro seemed +// better than huge code duplication. ErrorValues +// FIND_STRUCTURAL_BITS(Architecture T, const uint8_t *buf, size_t len, +// ParsedJson &pj, flatten function) +#define FIND_STRUCTURAL_BITS(T, buf, len, pj, flat) \ + { \ + if (len > pj.byte_capacity) { \ + std::cerr << "Your ParsedJson object only supports documents up to " \ + << pj.byte_capacity << " bytes but you are trying to process " \ + << len << " bytes" << std::endl; \ + return simdjson::CAPACITY; \ + } \ + uint32_t *base_ptr = pj.structural_indexes; \ + uint32_t base = 0; \ + utf8_checking_state utf8_state; \ + \ + /* we have padded the input out to 64 byte multiple with the remainder \ + * being zeros persistent state across loop does the last iteration end \ + * with an odd-length sequence of backslashes? */ \ + \ + /* either 0 or 1, but a 64-bit value */ \ + uint64_t prev_iter_ends_odd_backslash = 0ULL; \ + /* does the previous iteration end inside a double-quote pair? */ \ + uint64_t prev_iter_inside_quote = \ + 0ULL; /* either all zeros or all ones \ + * does the previous iteration end on something that is a \ + * predecessor of a pseudo-structural character - i.e. \ + * whitespace or a structural character effectively the very \ + * first char is considered to follow "whitespace" for the \ + * purposes of pseudo-structural character detection so we \ + * initialize to 1 */ \ + uint64_t prev_iter_ends_pseudo_pred = 1ULL; \ + \ + /* structurals are persistent state across loop as we flatten them on the \ + * subsequent iteration into our array pointed to be base_ptr. \ + * This is harmless on the first iteration as structurals==0 \ + * and is done for performance reasons; we can hide some of the latency of \ + * the \ + * expensive carryless multiply in the previous step with this work */ \ + uint64_t structurals = 0; \ + \ + size_t lenminus64 = len < 64 ? 0 : len - 64; \ + size_t idx = 0; \ + uint64_t error_mask = 0; /* for unescaped characters within strings (ASCII \ + code points < 0x20) */ \ + \ + for (; idx < lenminus64; idx += 64) { \ + FIND_STRUCTURAL_BITS_64( \ + T, &buf[idx], idx, base_ptr, base, prev_iter_ends_odd_backslash, \ + prev_iter_inside_quote, prev_iter_ends_pseudo_pred, structurals, \ + error_mask, utf8_state, flat); \ + } \ + /* If we have a final chunk of less than 64 bytes, pad it to 64 with \ + * spaces before processing it (otherwise, we risk invalidating the UTF-8 \ + * checks). */ \ + if (idx < len) { \ + uint8_t tmp_buf[64]; \ + memset(tmp_buf, 0x20, 64); \ + memcpy(tmp_buf, buf + idx, len - idx); \ + FIND_STRUCTURAL_BITS_64( \ + T, &tmp_buf[0], idx, base_ptr, base, prev_iter_ends_odd_backslash, \ + prev_iter_inside_quote, prev_iter_ends_pseudo_pred, structurals, \ + error_mask, utf8_state, flat); \ + idx += 64; \ + } \ + \ + /* is last string quote closed? */ \ + if (prev_iter_inside_quote) { \ + return simdjson::UNCLOSED_STRING; \ + } \ + \ + /* finally, flatten out the remaining structurals from the last iteration \ + */ \ + flat(base_ptr, base, idx, structurals); \ + \ + pj.n_structural_indexes = base; \ + /* a valid JSON file cannot have zero structural indexes - we should have \ + * found something */ \ + if (pj.n_structural_indexes == 0u) { \ + return simdjson::EMPTY; \ + } \ + if (base_ptr[pj.n_structural_indexes - 1] > len) { \ + return simdjson::UNEXPECTED_ERROR; \ + } \ + if (len != base_ptr[pj.n_structural_indexes - 1]) { \ + /* the string might not be NULL terminated, but we add a virtual NULL \ + * ending \ + * character. */ \ + base_ptr[pj.n_structural_indexes++] = len; \ + } \ + /* make it safe to dereference one beyond this array */ \ + base_ptr[pj.n_structural_indexes] = 0; \ + if (error_mask) { \ + return simdjson::UNESCAPED_CHARS; \ + } \ + return check_utf8_errors(utf8_state); \ + } #endif // SIMDJSON_STAGE1_FIND_MARKS_MACROS_H /* end file include/simdjson/stage1_find_marks_macros.h */ @@ -37707,17 +37854,17 @@ really_inline void flatten_bits(uint32_t *base_ptr, uint32_t &base, TARGET_WESTMERE namespace simdjson { -template<> -struct simd_input { +template <> struct simd_input { __m128i v0; __m128i v1; __m128i v2; __m128i v3; }; -template<> really_inline -simd_input fill_input(const uint8_t * ptr) { - struct simd_input in; +template <> +really_inline simd_input +fill_input(const uint8_t *ptr) { + struct simd_input in; in.v0 = _mm_loadu_si128(reinterpret_cast(ptr + 0)); in.v1 = _mm_loadu_si128(reinterpret_cast(ptr + 16)); in.v2 = _mm_loadu_si128(reinterpret_cast(ptr + 32)); @@ -37725,61 +37872,69 @@ simd_input fill_input(const uint return in; } -template<> really_inline -uint64_t compute_quote_mask(uint64_t quote_bits) { +template <> +really_inline uint64_t +compute_quote_mask(uint64_t quote_bits) { return _mm_cvtsi128_si64(_mm_clmulepi64_si128( - _mm_set_epi64x(0ULL, quote_bits), _mm_set1_epi8(0xFF), 0)); + _mm_set_epi64x(0ULL, quote_bits), _mm_set1_epi8(0xFFu), 0)); } -template<> -struct utf8_checking_state -{ +template <> struct utf8_checking_state { __m128i has_error = _mm_setzero_si128(); - processed_utf_bytes previous { - _mm_setzero_si128(), // rawbytes - _mm_setzero_si128(), // high_nibbles - _mm_setzero_si128() // carried_continuations + processed_utf_bytes previous{ + _mm_setzero_si128(), // raw_bytes + _mm_setzero_si128(), // high_nibbles + _mm_setzero_si128() // carried_continuations }; }; -template<> really_inline -void check_utf8(simd_input in, - utf8_checking_state& state) { - __m128i highbit = _mm_set1_epi8(0x80); - if ((_mm_testz_si128(_mm_or_si128(in.v0, in.v1), highbit)) == 1) { +template <> +really_inline void check_utf8( + simd_input in, + utf8_checking_state &state) { + __m128i high_bit = _mm_set1_epi8(0x80u); + if ((_mm_testz_si128(_mm_or_si128(in.v0, in.v1), high_bit)) == 1) { // it is ascii, we just check continuation - state.has_error = _mm_or_si128( - _mm_cmpgt_epi8( - state.previous.carried_continuations, - _mm_setr_epi8(9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 1)), - state.has_error); + state.has_error = + _mm_or_si128(_mm_cmpgt_epi8(state.previous.carried_continuations, + _mm_setr_epi8(9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 1)), + state.has_error); } else { // it is not ascii so we have to do heavy work - state.previous = checkUTF8Bytes(in.v0, &(state.previous), &(state.has_error)); - state.previous = checkUTF8Bytes(in.v1, &(state.previous), &(state.has_error)); + state.previous = + check_utf8_bytes(in.v0, &(state.previous), &(state.has_error)); + state.previous = + check_utf8_bytes(in.v1, &(state.previous), &(state.has_error)); } - if ((_mm_testz_si128(_mm_or_si128(in.v2, in.v3), highbit)) == 1) { + if ((_mm_testz_si128(_mm_or_si128(in.v2, in.v3), high_bit)) == 1) { // it is ascii, we just check continuation - state.has_error = _mm_or_si128( - _mm_cmpgt_epi8( - state.previous.carried_continuations, - _mm_setr_epi8(9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 1)), - state.has_error); + state.has_error = + _mm_or_si128(_mm_cmpgt_epi8(state.previous.carried_continuations, + _mm_setr_epi8(9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 1)), + state.has_error); } else { // it is not ascii so we have to do heavy work - state.previous = checkUTF8Bytes(in.v2, &(state.previous), &(state.has_error)); - state.previous = checkUTF8Bytes(in.v3, &(state.previous), &(state.has_error)); + state.previous = + check_utf8_bytes(in.v2, &(state.previous), &(state.has_error)); + state.previous = + check_utf8_bytes(in.v3, &(state.previous), &(state.has_error)); } } -template<> really_inline -errorValues check_utf8_errors(utf8_checking_state& state) { - return _mm_testz_si128(state.has_error, state.has_error) == 0 ? simdjson::UTF8_ERROR : simdjson::SUCCESS; +template <> +really_inline ErrorValues check_utf8_errors( + utf8_checking_state &state) { + return _mm_testz_si128(state.has_error, state.has_error) == 0 + ? simdjson::UTF8_ERROR + : simdjson::SUCCESS; } -template<> really_inline -uint64_t cmp_mask_against_input(simd_input in, uint8_t m) { +template <> +really_inline uint64_t cmp_mask_against_input( + simd_input in, uint8_t m) { const __m128i mask = _mm_set1_epi8(m); __m128i cmp_res_0 = _mm_cmpeq_epi8(in.v0, mask); uint64_t res_0 = _mm_movemask_epi8(cmp_res_0); @@ -37792,54 +37947,60 @@ uint64_t cmp_mask_against_input(simd_input really_inline -uint64_t unsigned_lteq_against_input(simd_input in, uint8_t m) { +template <> +really_inline uint64_t unsigned_lteq_against_input( + simd_input in, uint8_t m) { const __m128i maxval = _mm_set1_epi8(m); - __m128i cmp_res_0 = _mm_cmpeq_epi8(_mm_max_epu8(maxval,in.v0),maxval); + __m128i cmp_res_0 = _mm_cmpeq_epi8(_mm_max_epu8(maxval, in.v0), maxval); uint64_t res_0 = _mm_movemask_epi8(cmp_res_0); - __m128i cmp_res_1 = _mm_cmpeq_epi8(_mm_max_epu8(maxval,in.v1),maxval); + __m128i cmp_res_1 = _mm_cmpeq_epi8(_mm_max_epu8(maxval, in.v1), maxval); uint64_t res_1 = _mm_movemask_epi8(cmp_res_1); - __m128i cmp_res_2 = _mm_cmpeq_epi8(_mm_max_epu8(maxval,in.v2),maxval); + __m128i cmp_res_2 = _mm_cmpeq_epi8(_mm_max_epu8(maxval, in.v2), maxval); uint64_t res_2 = _mm_movemask_epi8(cmp_res_2); - __m128i cmp_res_3 = _mm_cmpeq_epi8(_mm_max_epu8(maxval,in.v3),maxval); + __m128i cmp_res_3 = _mm_cmpeq_epi8(_mm_max_epu8(maxval, in.v3), maxval); uint64_t res_3 = _mm_movemask_epi8(cmp_res_3); return res_0 | (res_1 << 16) | (res_2 << 32) | (res_3 << 48); } -template<> really_inline -uint64_t find_odd_backslash_sequences(simd_input in, uint64_t &prev_iter_ends_odd_backslash) { - FIND_ODD_BACKSLASH_SEQUENCES(architecture::westmere, in, prev_iter_ends_odd_backslash); +template <> +really_inline uint64_t find_odd_backslash_sequences( + simd_input in, + uint64_t &prev_iter_ends_odd_backslash) { + FIND_ODD_BACKSLASH_SEQUENCES(Architecture::WESTMERE, in, + prev_iter_ends_odd_backslash); } -template<> really_inline -uint64_t find_quote_mask_and_bits(simd_input in, uint64_t odd_ends, - uint64_t &prev_iter_inside_quote, uint64_t "e_bits, uint64_t &error_mask) { - FIND_QUOTE_MASK_AND_BITS(architecture::westmere, in, odd_ends, prev_iter_inside_quote, quote_bits, error_mask) +template <> +really_inline uint64_t find_quote_mask_and_bits( + simd_input in, uint64_t odd_ends, + uint64_t &prev_iter_inside_quote, uint64_t "e_bits, + uint64_t &error_mask) { + FIND_QUOTE_MASK_AND_BITS(Architecture::WESTMERE, in, odd_ends, + prev_iter_inside_quote, quote_bits, error_mask) } -template<> really_inline -void find_whitespace_and_structurals(simd_input in, - uint64_t &whitespace, uint64_t &structurals) { - const __m128i structural_table = _mm_setr_epi8(44, 125, 0, 0, 0xc0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 58, 123); - const __m128i white_table = _mm_setr_epi8( - 32, 100, 100, 100, 17, 100, 113, 2, 100, 9, 10, 112, 100, 13, 100, 100); - const __m128i struct_offset = _mm_set1_epi8(0xd4); +template <> +really_inline void find_whitespace_and_structurals( + simd_input in, uint64_t &whitespace, + uint64_t &structurals) { + const __m128i structural_table = + _mm_setr_epi8(44, 125, 0, 0, 0xc0u, 0, 0, 0, 0, 0, 0, 0, 0, 0, 58, 123); + const __m128i white_table = _mm_setr_epi8(32, 100, 100, 100, 17, 100, 113, 2, + 100, 9, 10, 112, 100, 13, 100, 100); + const __m128i struct_offset = _mm_set1_epi8(0xd4u); const __m128i struct_mask = _mm_set1_epi8(32); - __m128i white0 = _mm_cmpeq_epi8(in.v0, - _mm_shuffle_epi8(white_table, in.v0)); - __m128i white1 = _mm_cmpeq_epi8(in.v1, - _mm_shuffle_epi8(white_table, in.v1)); - __m128i white2 = _mm_cmpeq_epi8(in.v2, - _mm_shuffle_epi8(white_table, in.v2)); - __m128i white3 = _mm_cmpeq_epi8(in.v3, - _mm_shuffle_epi8(white_table, in.v3)); + __m128i white0 = _mm_cmpeq_epi8(in.v0, _mm_shuffle_epi8(white_table, in.v0)); + __m128i white1 = _mm_cmpeq_epi8(in.v1, _mm_shuffle_epi8(white_table, in.v1)); + __m128i white2 = _mm_cmpeq_epi8(in.v2, _mm_shuffle_epi8(white_table, in.v2)); + __m128i white3 = _mm_cmpeq_epi8(in.v3, _mm_shuffle_epi8(white_table, in.v3)); uint64_t ws_res_0 = _mm_movemask_epi8(white0); uint64_t ws_res_1 = _mm_movemask_epi8(white1); uint64_t ws_res_2 = _mm_movemask_epi8(white2); uint64_t ws_res_3 = _mm_movemask_epi8(white3); - whitespace = (ws_res_0 | (ws_res_1 << 16) | (ws_res_2 << 32) | (ws_res_3 << 48)); + whitespace = + (ws_res_0 | (ws_res_1 << 16) | (ws_res_2 << 32) | (ws_res_3 << 48)); __m128i struct1_r1 = _mm_add_epi8(struct_offset, in.v0); __m128i struct2_r1 = _mm_add_epi8(struct_offset, in.v1); @@ -37866,14 +38027,13 @@ void find_whitespace_and_structurals(simd_input -struct simd_input { +template <> struct simd_input { __m256i lo; __m256i hi; }; -template<> really_inline -simd_input fill_input(const uint8_t * ptr) { - struct simd_input in; +template <> +really_inline simd_input +fill_input(const uint8_t *ptr) { + struct simd_input in; in.lo = _mm256_loadu_si256(reinterpret_cast(ptr + 0)); in.hi = _mm256_loadu_si256(reinterpret_cast(ptr + 32)); return in; } -template<> really_inline -uint64_t compute_quote_mask(uint64_t quote_bits) { +template <> +really_inline uint64_t +compute_quote_mask(uint64_t quote_bits) { // There should be no such thing with a processing supporting avx2 // but not clmul. uint64_t quote_mask = _mm_cvtsi128_si64(_mm_clmulepi64_si128( - _mm_set_epi64x(0ULL, quote_bits), _mm_set1_epi8(0xFF), 0)); + _mm_set_epi64x(0ULL, quote_bits), _mm_set1_epi8(0xFFu), 0)); return quote_mask; } -template<> -struct utf8_checking_state { +template <> struct utf8_checking_state { __m256i has_error; avx_processed_utf_bytes previous; utf8_checking_state() { has_error = _mm256_setzero_si256(); - previous.rawbytes = _mm256_setzero_si256(); + previous.raw_bytes = _mm256_setzero_si256(); previous.high_nibbles = _mm256_setzero_si256(); - previous.carried_continuations =_mm256_setzero_si256(); + previous.carried_continuations = _mm256_setzero_si256(); } }; - -template<> really_inline -void check_utf8(simd_input in, - utf8_checking_state& state) { - __m256i highbit = _mm256_set1_epi8(0x80); - if ((_mm256_testz_si256(_mm256_or_si256(in.lo, in.hi), highbit)) == 1) { +template <> +really_inline void check_utf8( + simd_input in, + utf8_checking_state &state) { + __m256i high_bit = _mm256_set1_epi8(0x80u); + if ((_mm256_testz_si256(_mm256_or_si256(in.lo, in.hi), high_bit)) == 1) { // it is ascii, we just check continuation state.has_error = _mm256_or_si256( - _mm256_cmpgt_epi8( - state.previous.carried_continuations, - _mm256_setr_epi8(9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 1)), + _mm256_cmpgt_epi8(state.previous.carried_continuations, + _mm256_setr_epi8(9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 1)), state.has_error); } else { // it is not ascii so we have to do heavy work - state.previous = avxcheckUTF8Bytes(in.lo, &(state.previous), &(state.has_error)); - state.previous = avxcheckUTF8Bytes(in.hi, &(state.previous), &(state.has_error)); + state.previous = + avx_check_utf8_bytes(in.lo, &(state.previous), &(state.has_error)); + state.previous = + avx_check_utf8_bytes(in.hi, &(state.previous), &(state.has_error)); } } -template<> really_inline -errorValues check_utf8_errors(utf8_checking_state& state) { - return _mm256_testz_si256(state.has_error, state.has_error) == 0 ? simdjson::UTF8_ERROR : simdjson::SUCCESS; +template <> +really_inline ErrorValues check_utf8_errors( + utf8_checking_state &state) { + return _mm256_testz_si256(state.has_error, state.has_error) == 0 + ? simdjson::UTF8_ERROR + : simdjson::SUCCESS; } -template<> really_inline -uint64_t cmp_mask_against_input(simd_input in, uint8_t m) { +template <> +really_inline uint64_t cmp_mask_against_input( + simd_input in, uint8_t m) { const __m256i mask = _mm256_set1_epi8(m); __m256i cmp_res_0 = _mm256_cmpeq_epi8(in.lo, mask); uint64_t res_0 = static_cast(_mm256_movemask_epi8(cmp_res_0)); @@ -37956,31 +38122,38 @@ uint64_t cmp_mask_against_input(simd_input really_inline -uint64_t unsigned_lteq_against_input(simd_input in, uint8_t m) { +template <> +really_inline uint64_t unsigned_lteq_against_input( + simd_input in, uint8_t m) { const __m256i maxval = _mm256_set1_epi8(m); - __m256i cmp_res_0 = _mm256_cmpeq_epi8(_mm256_max_epu8(maxval,in.lo),maxval); + __m256i cmp_res_0 = _mm256_cmpeq_epi8(_mm256_max_epu8(maxval, in.lo), maxval); uint64_t res_0 = static_cast(_mm256_movemask_epi8(cmp_res_0)); - __m256i cmp_res_1 = _mm256_cmpeq_epi8(_mm256_max_epu8(maxval,in.hi),maxval); + __m256i cmp_res_1 = _mm256_cmpeq_epi8(_mm256_max_epu8(maxval, in.hi), maxval); uint64_t res_1 = _mm256_movemask_epi8(cmp_res_1); return res_0 | (res_1 << 32); } -template<> really_inline -uint64_t find_odd_backslash_sequences(simd_input in, uint64_t &prev_iter_ends_odd_backslash) { - FIND_ODD_BACKSLASH_SEQUENCES(architecture::haswell, in, prev_iter_ends_odd_backslash); +template <> +really_inline uint64_t find_odd_backslash_sequences( + simd_input in, + uint64_t &prev_iter_ends_odd_backslash) { + FIND_ODD_BACKSLASH_SEQUENCES(Architecture::HASWELL, in, + prev_iter_ends_odd_backslash); } -template<> really_inline -uint64_t find_quote_mask_and_bits(simd_input in, uint64_t odd_ends, - uint64_t &prev_iter_inside_quote, uint64_t "e_bits, uint64_t &error_mask) { - FIND_QUOTE_MASK_AND_BITS(architecture::haswell, in, odd_ends, prev_iter_inside_quote, quote_bits, error_mask) +template <> +really_inline uint64_t find_quote_mask_and_bits( + simd_input in, uint64_t odd_ends, + uint64_t &prev_iter_inside_quote, uint64_t "e_bits, + uint64_t &error_mask) { + FIND_QUOTE_MASK_AND_BITS(Architecture::HASWELL, in, odd_ends, + prev_iter_inside_quote, quote_bits, error_mask) } -template<> really_inline -void find_whitespace_and_structurals(simd_input in, - uint64_t &whitespace, - uint64_t &structurals) { +template <> +really_inline void find_whitespace_and_structurals( + simd_input in, uint64_t &whitespace, + uint64_t &structurals) { #ifdef SIMDJSON_NAIVE_STRUCTURAL // You should never need this naive approach, but it can be useful // for research purposes @@ -37988,21 +38161,28 @@ void find_whitespace_and_structurals(simd_input(_mm256_movemask_epi8(struct_lo)); + struct_lo = _mm256_or_si256(struct_lo, _mm256_cmpeq_epi8(in.lo, mask_comma)); + struct_hi = _mm256_or_si256(struct_hi, _mm256_cmpeq_epi8(in.hi, mask_comma)); + uint64_t structural_res_0 = + static_cast(_mm256_movemask_epi8(struct_lo)); uint64_t structural_res_1 = _mm256_movemask_epi8(struct_hi); structurals = (structural_res_0 | (structural_res_1 << 32)); @@ -38010,34 +38190,36 @@ void find_whitespace_and_structurals(simd_input(_mm256_movemask_epi8(space_lo)); uint64_t ws_res_1 = _mm256_movemask_epi8(space_hi); whitespace = (ws_res_0 | (ws_res_1 << 32)); // end of naive approach -#else // SIMDJSON_NAIVE_STRUCTURAL - const __m256i structural_table = _mm256_setr_epi8( - 44, 125, 0, 0, 0xc0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 58, 123, - 44, 125, 0, 0, 0xc0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 58, 123); +#else // SIMDJSON_NAIVE_STRUCTURAL + // clang-format off + const __m256i structural_table = + _mm256_setr_epi8(44, 125, 0, 0, 0xc0u, 0, 0, 0, 0, 0, 0, 0, 0, 0, 58, 123, + 44, 125, 0, 0, 0xc0u, 0, 0, 0, 0, 0, 0, 0, 0, 0, 58, 123); const __m256i white_table = _mm256_setr_epi8( - 32, 100, 100, 100, 17, 100, 113, 2, 100, 9, 10, 112, 100, 13, 100, 100, - 32, 100, 100, 100, 17, 100, 113, 2, 100, 9, 10, 112, 100, 13, 100, 100); - const __m256i struct_offset = _mm256_set1_epi8(0xd4); + 32, 100, 100, 100, 17, 100, 113, 2, 100, 9, 10, 112, 100, 13, 100, 100, + 32, 100, 100, 100, 17, 100, 113, 2, 100, 9, 10, 112, 100, 13, 100, 100); + // clang-format on + const __m256i struct_offset = _mm256_set1_epi8(0xd4u); const __m256i struct_mask = _mm256_set1_epi8(32); - __m256i lo_white = _mm256_cmpeq_epi8(in.lo, - _mm256_shuffle_epi8(white_table, in.lo)); - __m256i hi_white = _mm256_cmpeq_epi8(in.hi, - _mm256_shuffle_epi8(white_table, in.hi)); + __m256i lo_white = + _mm256_cmpeq_epi8(in.lo, _mm256_shuffle_epi8(white_table, in.lo)); + __m256i hi_white = + _mm256_cmpeq_epi8(in.hi, _mm256_shuffle_epi8(white_table, in.hi)); uint64_t ws_res_0 = static_cast(_mm256_movemask_epi8(lo_white)); uint64_t ws_res_1 = _mm256_movemask_epi8(hi_white); whitespace = (ws_res_0 | (ws_res_1 << 32)); @@ -38049,7 +38231,7 @@ void find_whitespace_and_structurals(simd_input(_mm256_movemask_epi8(lo_struct)); uint64_t structural_res_1 = _mm256_movemask_epi8(hi_struct); @@ -38060,7 +38242,6 @@ void find_whitespace_and_structurals(simd_input struct simd_input { +template <> struct simd_input { uint8x16_t i0; uint8x16_t i1; uint8x16_t i2; uint8x16_t i3; }; -template<> really_inline -simd_input fill_input(const uint8_t * ptr) { - struct simd_input in; +template <> +really_inline simd_input +fill_input(const uint8_t *ptr) { + struct simd_input in; in.i0 = vld1q_u8(ptr + 0); in.i1 = vld1q_u8(ptr + 16); in.i2 = vld1q_u8(ptr + 32); @@ -38088,26 +38270,24 @@ simd_input fill_input(const uint8_t * return in; } - -really_inline -uint16_t neonmovemask(uint8x16_t input) { - const uint8x16_t bitmask = { 0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, +really_inline uint16_t neon_movemask(uint8x16_t input) { + const uint8x16_t bit_mask = {0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, 0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80}; - uint8x16_t minput = vandq_u8(input, bitmask); + uint8x16_t minput = vandq_u8(input, 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); } -really_inline -uint64_t neonmovemask_bulk(uint8x16_t p0, uint8x16_t p1, uint8x16_t p2, uint8x16_t p3) { - const uint8x16_t bitmask = { 0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, +really_inline uint64_t neon_movemask_bulk(uint8x16_t p0, uint8x16_t p1, + uint8x16_t p2, uint8x16_t p3) { + const uint8x16_t bit_mask = {0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, 0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80}; - uint8x16_t t0 = vandq_u8(p0, bitmask); - uint8x16_t t1 = vandq_u8(p1, bitmask); - uint8x16_t t2 = vandq_u8(p2, bitmask); - uint8x16_t t3 = vandq_u8(p3, bitmask); + uint8x16_t t0 = vandq_u8(p0, bit_mask); + uint8x16_t t1 = vandq_u8(p1, bit_mask); + uint8x16_t t2 = vandq_u8(p2, bit_mask); + uint8x16_t t3 = vandq_u8(p3, bit_mask); uint8x16_t sum0 = vpaddq_u8(t0, t1); uint8x16_t sum1 = vpaddq_u8(t2, t3); sum0 = vpaddq_u8(sum0, sum1); @@ -38115,108 +38295,122 @@ uint64_t neonmovemask_bulk(uint8x16_t p0, uint8x16_t p1, uint8x16_t p2, uint8x16 return vgetq_lane_u64(vreinterpretq_u64_u8(sum0), 0); } -template<> really_inline -uint64_t compute_quote_mask(uint64_t quote_bits) { +template <> +really_inline uint64_t +compute_quote_mask(uint64_t quote_bits) { #ifdef __ARM_FEATURE_CRYPTO // some ARM processors lack this extension - return vmull_p64( -1ULL, quote_bits); + return vmull_p64(-1ULL, quote_bits); #else return portable_compute_quote_mask(quote_bits); -#endif +#endif } -template<> -struct utf8_checking_state -{ - int8x16_t has_error {}; - processed_utf_bytes previous {}; +template <> struct utf8_checking_state { + int8x16_t has_error{}; + processed_utf_bytes previous{}; }; // Checks that all bytes are ascii -really_inline -bool check_ascii_neon(simd_input in) { +really_inline bool check_ascii_neon(simd_input in) { // checking if the most significant bit is always equal to 0. - uint8x16_t highbit = vdupq_n_u8(0x80); + uint8x16_t high_bit = vdupq_n_u8(0x80); uint8x16_t t0 = vorrq_u8(in.i0, in.i1); uint8x16_t t1 = vorrq_u8(in.i2, in.i3); uint8x16_t t3 = vorrq_u8(t0, t1); - uint8x16_t t4 = vandq_u8(t3, highbit); + uint8x16_t t4 = vandq_u8(t3, high_bit); uint64x2_t v64 = vreinterpretq_u64_u8(t4); uint32x2_t v32 = vqmovn_u64(v64); uint64x1_t result = vreinterpret_u64_u32(v32); return vget_lane_u64(result, 0) == 0; } -template<> really_inline -void check_utf8(simd_input in, - utf8_checking_state& state) { +template <> +really_inline void check_utf8( + simd_input in, + utf8_checking_state &state) { if (check_ascii_neon(in)) { - // All bytes are ascii. Therefore the byte that was just before must be ascii too. - // We only check the byte that was just before simd_input. Nines are arbitrary values. - const int8x16_t verror = (int8x16_t){9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 1}; + // All bytes are ascii. Therefore the byte that was just before must be + // ascii too. We only check the byte that was just before simd_input. Nines + // are arbitrary values. + const int8x16_t verror = + (int8x16_t){9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 1}; state.has_error = - vorrq_s8(vreinterpretq_s8_u8(vcgtq_s8(state.previous.carried_continuations, - verror)), - state.has_error); + vorrq_s8(vreinterpretq_s8_u8( + vcgtq_s8(state.previous.carried_continuations, verror)), + state.has_error); } else { // it is not ascii so we have to do heavy work - state.previous = checkUTF8Bytes(vreinterpretq_s8_u8(in.i0), &(state.previous), &(state.has_error)); - state.previous = checkUTF8Bytes(vreinterpretq_s8_u8(in.i1), &(state.previous), &(state.has_error)); - state.previous = checkUTF8Bytes(vreinterpretq_s8_u8(in.i2), &(state.previous), &(state.has_error)); - state.previous = checkUTF8Bytes(vreinterpretq_s8_u8(in.i3), &(state.previous), &(state.has_error)); + state.previous = check_utf8_bytes(vreinterpretq_s8_u8(in.i0), + &(state.previous), &(state.has_error)); + state.previous = check_utf8_bytes(vreinterpretq_s8_u8(in.i1), + &(state.previous), &(state.has_error)); + state.previous = check_utf8_bytes(vreinterpretq_s8_u8(in.i2), + &(state.previous), &(state.has_error)); + state.previous = check_utf8_bytes(vreinterpretq_s8_u8(in.i3), + &(state.previous), &(state.has_error)); } } -template<> really_inline -errorValues check_utf8_errors(utf8_checking_state& state) { +template <> +really_inline ErrorValues check_utf8_errors( + utf8_checking_state &state) { uint64x2_t v64 = vreinterpretq_u64_s8(state.has_error); uint32x2_t v32 = vqmovn_u64(v64); uint64x1_t result = vreinterpret_u64_u32(v32); - return vget_lane_u64(result, 0) != 0 ? simdjson::UTF8_ERROR : simdjson::SUCCESS; + return vget_lane_u64(result, 0) != 0 ? simdjson::UTF8_ERROR + : simdjson::SUCCESS; } -template<> really_inline -uint64_t cmp_mask_against_input(simd_input in, uint8_t m) { - const uint8x16_t mask = vmovq_n_u8(m); - uint8x16_t cmp_res_0 = vceqq_u8(in.i0, mask); - uint8x16_t cmp_res_1 = vceqq_u8(in.i1, mask); - uint8x16_t cmp_res_2 = vceqq_u8(in.i2, mask); - uint8x16_t cmp_res_3 = vceqq_u8(in.i3, mask); - return neonmovemask_bulk(cmp_res_0, cmp_res_1, cmp_res_2, cmp_res_3); +template <> +really_inline uint64_t cmp_mask_against_input( + simd_input in, uint8_t m) { + const uint8x16_t mask = vmovq_n_u8(m); + uint8x16_t cmp_res_0 = vceqq_u8(in.i0, mask); + uint8x16_t cmp_res_1 = vceqq_u8(in.i1, mask); + uint8x16_t cmp_res_2 = vceqq_u8(in.i2, mask); + uint8x16_t cmp_res_3 = vceqq_u8(in.i3, mask); + return neon_movemask_bulk(cmp_res_0, cmp_res_1, cmp_res_2, cmp_res_3); } -template<> really_inline -uint64_t unsigned_lteq_against_input(simd_input in, uint8_t m) { - const uint8x16_t mask = vmovq_n_u8(m); - uint8x16_t cmp_res_0 = vcleq_u8(in.i0, mask); - uint8x16_t cmp_res_1 = vcleq_u8(in.i1, mask); - uint8x16_t cmp_res_2 = vcleq_u8(in.i2, mask); - uint8x16_t cmp_res_3 = vcleq_u8(in.i3, mask); - return neonmovemask_bulk(cmp_res_0, cmp_res_1, cmp_res_2, cmp_res_3); +template <> +really_inline uint64_t unsigned_lteq_against_input( + simd_input in, uint8_t m) { + const uint8x16_t mask = vmovq_n_u8(m); + uint8x16_t cmp_res_0 = vcleq_u8(in.i0, mask); + uint8x16_t cmp_res_1 = vcleq_u8(in.i1, mask); + uint8x16_t cmp_res_2 = vcleq_u8(in.i2, mask); + uint8x16_t cmp_res_3 = vcleq_u8(in.i3, mask); + return neon_movemask_bulk(cmp_res_0, cmp_res_1, cmp_res_2, cmp_res_3); } -template<> really_inline -uint64_t find_odd_backslash_sequences(simd_input in, uint64_t &prev_iter_ends_odd_backslash) { - FIND_ODD_BACKSLASH_SEQUENCES(architecture::arm64, in, prev_iter_ends_odd_backslash); +template <> +really_inline uint64_t find_odd_backslash_sequences( + simd_input in, + uint64_t &prev_iter_ends_odd_backslash) { + FIND_ODD_BACKSLASH_SEQUENCES(Architecture::ARM64, in, + prev_iter_ends_odd_backslash); } -template<> really_inline -uint64_t find_quote_mask_and_bits(simd_input in, uint64_t odd_ends, - uint64_t &prev_iter_inside_quote, uint64_t "e_bits, uint64_t &error_mask) { - FIND_QUOTE_MASK_AND_BITS(architecture::arm64, in, odd_ends, prev_iter_inside_quote, quote_bits, error_mask) +template <> +really_inline uint64_t find_quote_mask_and_bits( + simd_input in, uint64_t odd_ends, + uint64_t &prev_iter_inside_quote, uint64_t "e_bits, + uint64_t &error_mask) { + FIND_QUOTE_MASK_AND_BITS(Architecture::ARM64, in, odd_ends, + prev_iter_inside_quote, quote_bits, error_mask) } -template<> really_inline -void find_whitespace_and_structurals( - simd_input in, - uint64_t &whitespace, - uint64_t &structurals) { - const uint8x16_t low_nibble_mask = (uint8x16_t){ - 16, 0, 0, 0, 0, 0, 0, 0, 0, 8, 12, 1, 2, 9, 0, 0}; - const uint8x16_t high_nibble_mask = (uint8x16_t){ - 8, 0, 18, 4, 0, 1, 0, 1, 0, 0, 0, 3, 2, 1, 0, 0}; - const uint8x16_t structural_shufti_mask = vmovq_n_u8(0x7); - const uint8x16_t whitespace_shufti_mask = vmovq_n_u8(0x18); - const uint8x16_t low_nib_and_mask = vmovq_n_u8(0xf); +template <> +really_inline void find_whitespace_and_structurals( + simd_input in, uint64_t &whitespace, + uint64_t &structurals) { + const uint8x16_t low_nibble_mask = + (uint8x16_t){16, 0, 0, 0, 0, 0, 0, 0, 0, 8, 12, 1, 2, 9, 0, 0}; + const uint8x16_t high_nibble_mask = + (uint8x16_t){8, 0, 18, 4, 0, 1, 0, 1, 0, 0, 0, 3, 2, 1, 0, 0}; + const uint8x16_t structural_shufti_mask = vmovq_n_u8(0x7); + const uint8x16_t whitespace_shufti_mask = vmovq_n_u8(0x18); + const uint8x16_t low_nib_and_mask = vmovq_n_u8(0xf); uint8x16_t nib_0_lo = vandq_u8(in.i0, low_nib_and_mask); uint8x16_t nib_0_hi = vshrq_n_u8(in.i0, 4); @@ -38246,15 +38440,15 @@ void find_whitespace_and_structurals( uint8x16_t tmp_1 = vtstq_u8(v_1, structural_shufti_mask); uint8x16_t tmp_2 = vtstq_u8(v_2, structural_shufti_mask); uint8x16_t tmp_3 = vtstq_u8(v_3, structural_shufti_mask); - structurals = neonmovemask_bulk(tmp_0, tmp_1, tmp_2, tmp_3); + structurals = neon_movemask_bulk(tmp_0, tmp_1, tmp_2, tmp_3); uint8x16_t tmp_ws_0 = vtstq_u8(v_0, whitespace_shufti_mask); uint8x16_t tmp_ws_1 = vtstq_u8(v_1, whitespace_shufti_mask); uint8x16_t tmp_ws_2 = vtstq_u8(v_2, whitespace_shufti_mask); uint8x16_t tmp_ws_3 = vtstq_u8(v_3, whitespace_shufti_mask); - whitespace = neonmovemask_bulk(tmp_ws_0, tmp_ws_1, tmp_ws_2, tmp_ws_3); + whitespace = neon_movemask_bulk(tmp_ws_0, tmp_ws_1, tmp_ws_2, tmp_ws_3); } -}// simdjson namespace +} // namespace simdjson #endif // IS_ARM64 #endif // SIMDJSON_STAGE1_FIND_MARKS_ARM64_H @@ -38265,8 +38459,9 @@ void find_whitespace_and_structurals( #ifdef JSON_TEST_STRINGS -void foundString(const uint8_t *buf, const uint8_t *parsed_begin, const uint8_t *parsed_end); -void foundBadString(const uint8_t *buf); +void found_string(const uint8_t *buf, const uint8_t *parsed_begin, + const uint8_t *parsed_end); +void found_bad_string(const uint8_t *buf); #endif namespace simdjson { @@ -38296,7 +38491,6 @@ static const uint8_t escape_map[256] = { 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 @@ -38304,9 +38498,10 @@ static const uint8_t escape_map[256] = { // return true if the unicode codepoint was valid // We work in little-endian then swap at write time WARN_UNUSED -really_inline bool handle_unicode_codepoint(const uint8_t **src_ptr, uint8_t **dst_ptr) { +really_inline bool handle_unicode_codepoint(const uint8_t **src_ptr, + uint8_t **dst_ptr) { // 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 + // conversion isn't valid; we defer the check for this to inside the // multilingual plane check uint32_t code_point = hex_to_u32_nocheck(*src_ptr + 2); *src_ptr += 6; @@ -38317,14 +38512,14 @@ really_inline bool handle_unicode_codepoint(const uint8_t **src_ptr, uint8_t **d return false; } uint32_t code_point_2 = hex_to_u32_nocheck(*src_ptr + 2); - + // if the first code point is invalid we will get here, as we will go past // the check for being outside the Basic Multilingual plane. If we don't - // find a \u immediately afterwards we fail out anyhow, but if we do, + // find a \u immediately afterwards we fail out anyhow, but if we do, // this check catches both the case of the first code point being invalid // or the second code point being invalid. if ((code_point | code_point_2) >> 16) { - return false; + return false; } code_point = @@ -38343,18 +38538,17 @@ struct parse_string_helper { }; // Finds where the backslashes and quotes are located. -template -parse_string_helper find_bs_bits_and_quote_bits(const uint8_t *src, uint8_t *dst); +template +parse_string_helper find_bs_bits_and_quote_bits(const uint8_t *src, + uint8_t *dst); +template +WARN_UNUSED ALLOW_SAME_PAGE_BUFFER_OVERRUN_QUALIFIER LENIENT_MEM_SANITIZER + really_inline bool + parse_string(UNUSED const uint8_t *buf, UNUSED size_t len, ParsedJson &pj, + UNUSED const uint32_t depth, UNUSED uint32_t offset); - -template -WARN_UNUSED ALLOW_SAME_PAGE_BUFFER_OVERRUN_QUALIFIER LENIENT_MEM_SANITIZER really_inline -bool parse_string(UNUSED const uint8_t *buf, UNUSED size_t len, - ParsedJson &pj, UNUSED const uint32_t depth, UNUSED uint32_t offset); - - -} +} // namespace simdjson /// Now include the specializations: @@ -38364,81 +38558,89 @@ bool parse_string(UNUSED const uint8_t *buf, UNUSED size_t len, #ifndef SIMDJSON_STRINGPARSING_MACROS_H #define SIMDJSON_STRINGPARSING_MACROS_H -// We need to compile that code for multiple architectures. However, target attributes can be used -// only once by function definition. Huge macro seemed better than huge code duplication. -// bool PARSE_STRING(architecture T, UNUSED const uint8_t *buf, UNUSED size_t len, -// ParsedJson &pj, UNUSED const uint32_t depth, UNUSED uint32_t offset) -#define PARSE_STRING(T, buf, len, pj, depth, offset) { \ - pj.write_tape(pj.current_string_buf_loc - pj.string_buf, '"'); \ - const uint8_t *src = &buf[offset + 1]; /* we know that buf at offset is a " */ \ - uint8_t *dst = pj.current_string_buf_loc + sizeof(uint32_t); \ - const uint8_t *const start_of_string = dst; \ - while (1) { \ - parse_string_helper helper = find_bs_bits_and_quote_bits(src, dst); \ - if(((helper.bs_bits - 1) & helper.quote_bits) != 0 ) { \ - /* we encountered quotes first. Move dst to point to quotes and exit */ \ - \ - /* find out where the quote is... */ \ - uint32_t quote_dist = trailingzeroes(helper.quote_bits); \ - \ - /* NULL termination is still handy if you expect all your strings to be NULL terminated? */ \ - /* It comes at a small cost */ \ - dst[quote_dist] = 0; \ - \ - uint32_t str_length = (dst - start_of_string) + quote_dist; \ - memcpy(pj.current_string_buf_loc,&str_length, sizeof(uint32_t)); \ - /*///////////////////// */ \ - /* Above, 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. */ \ - /*////////////////////// */ \ - \ - \ - /* we advance the point, accounting for the fact that we have a NULL termination */ \ - pj.current_string_buf_loc = dst + quote_dist + 1; \ - return true; \ - } \ - if(((helper.quote_bits - 1) & helper.bs_bits ) != 0 ) { \ - /* find out where the backspace is */ \ - uint32_t bs_dist = trailingzeroes(helper.bs_bits); \ - 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)) { \ - return false; \ - } \ - } 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 false; /* 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. */ \ - if constexpr(T == architecture::westmere) { \ - src += 16; \ - dst += 16; \ - } else { \ - src += 32; \ - dst += 32; \ - } \ - } \ - } \ - /* can't be reached */ \ - return true; \ -} +// We need to compile that code for multiple architectures. However, target +// attributes can be used only once by function definition. Huge macro seemed +// better than huge code duplication.รง +// bool PARSE_STRING(Architecture T, const uint8_t *buf, size_t len, ParsedJson +// &pj,const uint32_t depth, uint32_t offset) +#define PARSE_STRING(T, buf, len, pj, depth, offset) \ + { \ + pj.write_tape(pj.current_string_buf_loc - pj.string_buf, '"'); \ + const uint8_t *src = \ + &buf[offset + 1]; /* we know that buf at offset is a " */ \ + uint8_t *dst = pj.current_string_buf_loc + sizeof(uint32_t); \ + const uint8_t *const start_of_string = dst; \ + while (1) { \ + parse_string_helper helper = find_bs_bits_and_quote_bits(src, dst); \ + if (((helper.bs_bits - 1) & helper.quote_bits) != 0) { \ + /* we encountered quotes first. Move dst to point to quotes and exit \ + */ \ + \ + /* find out where the quote is... */ \ + uint32_t quote_dist = trailing_zeroes(helper.quote_bits); \ + \ + /* NULL termination is still handy if you expect all your strings to \ + * be NULL terminated? */ \ + /* It comes at a small cost */ \ + dst[quote_dist] = 0; \ + \ + uint32_t str_length = (dst - start_of_string) + quote_dist; \ + memcpy(pj.current_string_buf_loc, &str_length, sizeof(uint32_t)); \ + /***************************** \ + * Above, 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. \ + ****************************/ \ + \ + /* we advance the point, accounting for the fact that we have a NULL \ + * termination */ \ + pj.current_string_buf_loc = dst + quote_dist + 1; \ + return true; \ + } \ + if (((helper.quote_bits - 1) & helper.bs_bits) != 0) { \ + /* find out where the backspace is */ \ + uint32_t bs_dist = trailing_zeroes(helper.bs_bits); \ + 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)) { \ + return false; \ + } \ + } 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 false; /* 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. */ \ + if constexpr (T == Architecture::WESTMERE) { \ + src += 16; \ + dst += 16; \ + } else { \ + src += 32; \ + dst += 32; \ + } \ + } \ + } \ + /* can't be reached */ \ + return true; \ + } #endif /* end file include/simdjson/stringparsing_macros.h */ @@ -38447,32 +38649,37 @@ bool parse_string(UNUSED const uint8_t *buf, UNUSED size_t len, #define SIMDJSON_STRINGPARSING_WESTMERE_H - #ifdef IS_X86_64 TARGET_WESTMERE namespace simdjson { -template<> really_inline -parse_string_helper find_bs_bits_and_quote_bits (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 - __m128i v = _mm_loadu_si128(reinterpret_cast(src)); - // store to dest unconditionally - we can overwrite the bits we don't like - // later - _mm_storeu_si128(reinterpret_cast<__m128i *>(dst), v); - auto quote_mask = _mm_cmpeq_epi8(v, _mm_set1_epi8('"')); - return { - static_cast(_mm_movemask_epi8(_mm_cmpeq_epi8(v, _mm_set1_epi8('\\')))), // bs_bits +template <> +really_inline parse_string_helper +find_bs_bits_and_quote_bits(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 + __m128i v = _mm_loadu_si128(reinterpret_cast(src)); + // store to dest unconditionally - we can overwrite the bits we don't like + // later + _mm_storeu_si128(reinterpret_cast<__m128i *>(dst), v); + auto quote_mask = _mm_cmpeq_epi8(v, _mm_set1_epi8('"')); + return { + static_cast( + _mm_movemask_epi8(_mm_cmpeq_epi8(v, _mm_set1_epi8('\\')))), // bs_bits static_cast(_mm_movemask_epi8(quote_mask)) // quote_bits - }; + }; } -template<> -WARN_UNUSED ALLOW_SAME_PAGE_BUFFER_OVERRUN_QUALIFIER LENIENT_MEM_SANITIZER really_inline -bool parse_string(UNUSED const uint8_t *buf, UNUSED size_t len, - ParsedJson &pj, UNUSED const uint32_t depth, UNUSED uint32_t offset) { - PARSE_STRING(architecture::westmere, buf, len, pj, depth, offset); -} +template <> +WARN_UNUSED ALLOW_SAME_PAGE_BUFFER_OVERRUN_QUALIFIER LENIENT_MEM_SANITIZER + really_inline bool + parse_string(UNUSED const uint8_t *buf, + UNUSED size_t len, ParsedJson &pj, + UNUSED const uint32_t depth, + UNUSED uint32_t offset) { + PARSE_STRING(Architecture::WESTMERE, buf, len, pj, depth, offset); } +} // namespace simdjson UNTARGET_REGION #endif @@ -38483,34 +38690,39 @@ UNTARGET_REGION #define SIMDJSON_STRINGPARSING_HASWELL_H - #ifdef IS_X86_64 TARGET_HASWELL namespace simdjson { -template<> really_inline -parse_string_helper find_bs_bits_and_quote_bits (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(sizeof(__m256i) - 1 <= SIMDJSON_PADDING); - __m256i v = _mm256_loadu_si256(reinterpret_cast(src)); - // store to dest unconditionally - we can overwrite the bits we don't like - // later - _mm256_storeu_si256(reinterpret_cast<__m256i *>(dst), v); - auto quote_mask = _mm256_cmpeq_epi8(v, _mm256_set1_epi8('"')); - return { - static_cast(_mm256_movemask_epi8(_mm256_cmpeq_epi8(v, _mm256_set1_epi8('\\')))), // bs_bits +template <> +really_inline parse_string_helper +find_bs_bits_and_quote_bits(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(sizeof(__m256i) - 1 <= SIMDJSON_PADDING); + __m256i v = _mm256_loadu_si256(reinterpret_cast(src)); + // store to dest unconditionally - we can overwrite the bits we don't like + // later + _mm256_storeu_si256(reinterpret_cast<__m256i *>(dst), v); + auto quote_mask = _mm256_cmpeq_epi8(v, _mm256_set1_epi8('"')); + return { + static_cast(_mm256_movemask_epi8( + _mm256_cmpeq_epi8(v, _mm256_set1_epi8('\\')))), // bs_bits static_cast(_mm256_movemask_epi8(quote_mask)) // quote_bits - }; + }; } -template<> -WARN_UNUSED ALLOW_SAME_PAGE_BUFFER_OVERRUN_QUALIFIER LENIENT_MEM_SANITIZER really_inline -bool parse_string(UNUSED const uint8_t *buf, UNUSED size_t len, - ParsedJson &pj, UNUSED const uint32_t depth, UNUSED uint32_t offset) { - PARSE_STRING(architecture::haswell, buf, len, pj, depth, offset); +template <> +WARN_UNUSED ALLOW_SAME_PAGE_BUFFER_OVERRUN_QUALIFIER LENIENT_MEM_SANITIZER + really_inline bool + parse_string(UNUSED const uint8_t *buf, + UNUSED size_t len, ParsedJson &pj, + UNUSED const uint32_t depth, + UNUSED uint32_t offset) { + PARSE_STRING(Architecture::HASWELL, buf, len, pj, depth, offset); } -} +} // namespace simdjson UNTARGET_REGION #endif @@ -38523,47 +38735,52 @@ UNTARGET_REGION #ifdef IS_ARM64 namespace simdjson { -template<> really_inline -parse_string_helper find_bs_bits_and_quote_bits (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(2 * sizeof(uint8x16_t) - 1 <= SIMDJSON_PADDING); - uint8x16_t v0 = vld1q_u8(src); - uint8x16_t v1 = vld1q_u8(src+16); - vst1q_u8(dst, v0); - vst1q_u8(dst+16, v1); - - uint8x16_t bs_mask = vmovq_n_u8('\\'); - uint8x16_t qt_mask = vmovq_n_u8('"'); - const uint8x16_t bitmask = { 0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, - 0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80}; - uint8x16_t cmp_bs_0 = vceqq_u8(v0, bs_mask); - uint8x16_t cmp_bs_1 = vceqq_u8(v1, bs_mask); - uint8x16_t cmp_qt_0 = vceqq_u8(v0, qt_mask); - uint8x16_t cmp_qt_1 = vceqq_u8(v1, qt_mask); - - cmp_bs_0 = vandq_u8(cmp_bs_0, bitmask); - cmp_bs_1 = vandq_u8(cmp_bs_1, bitmask); - cmp_qt_0 = vandq_u8(cmp_qt_0, bitmask); - cmp_qt_1 = vandq_u8(cmp_qt_1, bitmask); +template <> +really_inline parse_string_helper +find_bs_bits_and_quote_bits(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(2 * sizeof(uint8x16_t) - 1 <= SIMDJSON_PADDING); + uint8x16_t v0 = vld1q_u8(src); + uint8x16_t v1 = vld1q_u8(src + 16); + vst1q_u8(dst, v0); + vst1q_u8(dst + 16, v1); - uint8x16_t sum0 = vpaddq_u8(cmp_bs_0, cmp_bs_1); - uint8x16_t sum1 = vpaddq_u8(cmp_qt_0, cmp_qt_1); - sum0 = vpaddq_u8(sum0, sum1); - sum0 = vpaddq_u8(sum0, sum0); - return { + uint8x16_t bs_mask = vmovq_n_u8('\\'); + uint8x16_t qt_mask = vmovq_n_u8('"'); + const uint8x16_t bit_mask = {0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, + 0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80}; + uint8x16_t cmp_bs_0 = vceqq_u8(v0, bs_mask); + uint8x16_t cmp_bs_1 = vceqq_u8(v1, bs_mask); + uint8x16_t cmp_qt_0 = vceqq_u8(v0, qt_mask); + uint8x16_t cmp_qt_1 = vceqq_u8(v1, qt_mask); + + cmp_bs_0 = vandq_u8(cmp_bs_0, bit_mask); + cmp_bs_1 = vandq_u8(cmp_bs_1, bit_mask); + cmp_qt_0 = vandq_u8(cmp_qt_0, bit_mask); + cmp_qt_1 = vandq_u8(cmp_qt_1, bit_mask); + + uint8x16_t sum0 = vpaddq_u8(cmp_bs_0, cmp_bs_1); + uint8x16_t sum1 = vpaddq_u8(cmp_qt_0, cmp_qt_1); + sum0 = vpaddq_u8(sum0, sum1); + sum0 = vpaddq_u8(sum0, sum0); + return { vgetq_lane_u32(vreinterpretq_u32_u8(sum0), 0), // bs_bits - vgetq_lane_u32(vreinterpretq_u32_u8(sum0), 1) // quote_bits - }; + vgetq_lane_u32(vreinterpretq_u32_u8(sum0), 1) // quote_bits + }; } -template<> -WARN_UNUSED ALLOW_SAME_PAGE_BUFFER_OVERRUN_QUALIFIER LENIENT_MEM_SANITIZER really_inline -bool parse_string(UNUSED const uint8_t *buf, UNUSED size_t len, - ParsedJson &pj, UNUSED const uint32_t depth, UNUSED uint32_t offset) { - PARSE_STRING(architecture::arm64, buf, len, pj, depth, offset); -} +template <> +WARN_UNUSED ALLOW_SAME_PAGE_BUFFER_OVERRUN_QUALIFIER LENIENT_MEM_SANITIZER + really_inline bool + parse_string(UNUSED const uint8_t *buf, + UNUSED size_t len, ParsedJson &pj, + UNUSED const uint32_t depth, + UNUSED uint32_t offset) { + PARSE_STRING(Architecture::ARM64, buf, len, pj, depth, offset); } +} // namespace simdjson #endif #endif /* end file include/simdjson/stringparsing_arm64.h */ @@ -38573,16 +38790,17 @@ bool parse_string(UNUSED const uint8_t *buf, UNUSED size_t #ifdef JSON_TEST_NUMBERS // for unit testing -void foundInvalidNumber(const uint8_t *buf); -void foundInteger(int64_t result, const uint8_t *buf); -void foundFloat(double result, const uint8_t *buf); +void found_invalid_number(const uint8_t *buf); +void found_integer(int64_t result, const uint8_t *buf); +void found_float(double result, const uint8_t *buf); #endif namespace simdjson { -// Allowable floating-point values range from std::numeric_limits::lowest() -// to std::numeric_limits::max(), so from -// -1.7976e308 all the way to 1.7975e308 in binary64. The lowest non-zero -// normal values is std::numeric_limits::min() or about 2.225074e-308. +// Allowable floating-point values range from +// std::numeric_limits::lowest() to std::numeric_limits::max(), +// so from -1.7976e308 all the way to 1.7975e308 in binary64. The lowest +// non-zero normal values is std::numeric_limits::min() or +// about 2.225074e-308. static const double power_of_ten[] = { 1e-308, 1e-307, 1e-306, 1e-305, 1e-304, 1e-303, 1e-302, 1e-301, 1e-300, 1e-299, 1e-298, 1e-297, 1e-296, 1e-295, 1e-294, 1e-293, 1e-292, 1e-291, @@ -38679,7 +38897,7 @@ really_inline bool is_not_structural_or_whitespace_or_exponent_or_decimal(unsigned char c) { return structural_or_whitespace_or_exponent_or_decimal_negated[c]; } -}// simdjson +} // namespace simdjson #ifndef SIMDJSON_DISABLE_SWAR_NUMBER_PARSING #define SWAR_NUMBER_PARSING #endif @@ -38692,7 +38910,7 @@ namespace simdjson { // http://0x80.pl/articles/swar-digits-validate.html static inline bool is_made_of_eight_digits_fast(const char *chars) { uint64_t val; - // this can read up to 7 bytes beyond the buffer size, but we require + // this can read up to 7 bytes beyond the buffer size, but we require // SIMDJSON_PADDING of padding static_assert(7 <= SIMDJSON_PADDING); memcpy(&val, chars, 8); @@ -38704,7 +38922,7 @@ static inline bool is_made_of_eight_digits_fast(const char *chars) { (((val + 0x0606060606060606) & 0xF0F0F0F0F0F0F0F0) >> 4)) == 0x3333333333333333); } -} +} // namespace simdjson #ifdef IS_X86_64 TARGET_WESTMERE namespace simdjson { @@ -38716,7 +38934,8 @@ static inline uint32_t parse_eight_digits_unrolled(const char *chars) { 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 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); @@ -38724,7 +38943,7 @@ static inline uint32_t parse_eight_digits_unrolled(const char *chars) { return _mm_cvtsi128_si32( t4); // only captures the sum of the first 8 digits, drop the rest } -} +} // namespace simdjson UNTARGET_REGION #endif @@ -38733,15 +38952,14 @@ namespace simdjson { // we don't have SSE, so let us use a scalar function // credit: https://johnnylee-sde.github.io/Fast-numeric-string-to-int/ static 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 (val & 0x0000FFFF0000FFFF) * 42949672960001 >> 32; + uint64_t val; + memcpy(&val, chars, sizeof(uint64_t)); + val = (val & 0x0F0F0F0F0F0F0F0F) * 2561 >> 8; + val = (val & 0x00FF00FF00FF00FF) * 6553601 >> 16; + return (val & 0x0000FFFF0000FFFF) * 42949672960001 >> 32; } #endif - #endif // @@ -38749,10 +38967,9 @@ static inline uint32_t parse_eight_digits_unrolled(const char *chars) { // It is only even going to be used when negative_exponent is tiny. static double subnormal_power10(double base, int negative_exponent) { // this is probably not going to be fast - return base * 1e-308 * pow(10, negative_exponent + 308); + return base * 1e-308 * pow(10, negative_exponent + 308); } - // called by parse_number when we know that the output is a float, // but where there might be some integer overflow. The trick here is to // parse using floats from the start. @@ -38763,17 +38980,15 @@ static double subnormal_power10(double base, int negative_exponent) { // // Note: a redesign could avoid this function entirely. // -static never_inline bool -parse_float(const uint8_t *const buf, - ParsedJson &pj, const uint32_t offset, - bool found_minus) { +static never_inline bool parse_float(const uint8_t *const buf, ParsedJson &pj, + const uint32_t offset, bool found_minus) { const char *p = reinterpret_cast(buf + offset); bool negative = false; if (found_minus) { ++p; negative = true; } - double i; + long double i; if (*p == '0') { // 0 cannot be followed by an integer ++p; i = 0; @@ -38789,94 +39004,102 @@ parse_float(const uint8_t *const buf, } if ('.' == *p) { ++p; - double fractionalweight = 1; - if(is_integer(*p)) { + int fractional_weight = 308; + if (is_integer(*p)) { unsigned char digit = *p - '0'; ++p; - fractionalweight *= 0.1; - i = i + digit * fractionalweight; + + fractional_weight--; + i = i + digit * (fractional_weight >= 0 ? power_of_ten[fractional_weight] + : 0); } else { #ifdef JSON_TEST_NUMBERS // for unit testing - foundInvalidNumber(buf + offset); + found_invalid_number(buf + offset); #endif return false; } while (is_integer(*p)) { unsigned char digit = *p - '0'; ++p; - fractionalweight *= 0.1; - i = i + digit * fractionalweight; + fractional_weight--; + i = i + digit * (fractional_weight >= 0 ? power_of_ten[fractional_weight] + : 0); } } if (('e' == *p) || ('E' == *p)) { ++p; - bool negexp = false; + bool neg_exp = false; if ('-' == *p) { - negexp = true; + neg_exp = true; ++p; } else if ('+' == *p) { ++p; } if (!is_integer(*p)) { #ifdef JSON_TEST_NUMBERS // for unit testing - foundInvalidNumber(buf + offset); + found_invalid_number(buf + offset); #endif return false; } unsigned char digit = *p - '0'; - int64_t expnumber = digit; // exponential part + int64_t exp_number = digit; // exponential part p++; if (is_integer(*p)) { digit = *p - '0'; - expnumber = 10 * expnumber + digit; + exp_number = 10 * exp_number + digit; ++p; } if (is_integer(*p)) { digit = *p - '0'; - expnumber = 10 * expnumber + digit; + exp_number = 10 * exp_number + digit; ++p; } if (is_integer(*p)) { digit = *p - '0'; - expnumber = 10 * expnumber + digit; + exp_number = 10 * exp_number + digit; ++p; } - if (is_integer(*p)) { + while (is_integer(*p)) { + if (exp_number > 0x100000000) { // we need to check for overflows // we refuse to parse this #ifdef JSON_TEST_NUMBERS // for unit testing - foundInvalidNumber(buf + offset); + found_invalid_number(buf + offset); #endif - return false; + return false; + } + digit = *p - '0'; + exp_number = 10 * exp_number + digit; + ++p; } - if (unlikely(expnumber > 308)) { + if (unlikely(exp_number > 308)) { // this path is unlikely - if(negexp) { - // We either have zero or a subnormal. + if (neg_exp) { + // We either have zero or a subnormal. // We expect this to be uncommon so we go through a slow path. - i = subnormal_power10(i, - expnumber); + i = subnormal_power10(i, -exp_number); } else { // We know for sure that we have a number that is too large, // we refuse to parse this #ifdef JSON_TEST_NUMBERS // for unit testing - foundInvalidNumber(buf + offset); + found_invalid_number(buf + offset); #endif return false; } } else { - int exponent = (negexp ? -expnumber : expnumber); - // we have that expnumber is [0,308] so that - // exponent is [-308,308] so that + int exponent = (neg_exp ? -exp_number : exp_number); + // we have that exp_number is [0,308] so that + // exponent is [-308,308] so that // 308 + exponent is in [0, 2 * 308] i *= power_of_ten[308 + exponent]; - } + } } - if(is_not_structural_or_whitespace(*p)) { + if (is_not_structural_or_whitespace(*p)) { return false; } double d = negative ? -i : i; pj.write_tape_double(d); #ifdef JSON_TEST_NUMBERS // for unit testing - foundFloat(d, buf + offset); + found_float(d, buf + offset); #endif return is_structural_or_whitespace(*p); } @@ -38914,13 +39137,13 @@ static never_inline bool parse_large_integer(const uint8_t *const buf, digit = *p - '0'; if (mul_overflow(i, 10, &i)) { #ifdef JSON_TEST_NUMBERS // for unit testing - foundInvalidNumber(buf + offset); + found_invalid_number(buf + offset); #endif return false; // overflow } if (add_overflow(i, digit, &i)) { #ifdef JSON_TEST_NUMBERS // for unit testing - foundInvalidNumber(buf + offset); + found_invalid_number(buf + offset); #endif return false; // overflow } @@ -38931,7 +39154,7 @@ static never_inline bool parse_large_integer(const uint8_t *const buf, if (i > 0x8000000000000000) { // overflows! #ifdef JSON_TEST_NUMBERS // for unit testing - foundInvalidNumber(buf + offset); + found_invalid_number(buf + offset); #endif return false; // overflow } @@ -38939,15 +39162,16 @@ static never_inline bool parse_large_integer(const uint8_t *const buf, if (i >= 0x8000000000000000) { // overflows! #ifdef JSON_TEST_NUMBERS // for unit testing - foundInvalidNumber(buf + offset); + found_invalid_number(buf + offset); #endif return false; // overflow } } - int64_t signed_answer = negative ? -static_cast(i) : static_cast(i); + int64_t signed_answer = + negative ? -static_cast(i) : static_cast(i); pj.write_tape_s64(signed_answer); #ifdef JSON_TEST_NUMBERS // for unit testing - foundInteger(signed_answer, buf + offset); + found_integer(signed_answer, buf + offset); #endif return is_structural_or_whitespace(*p); } @@ -38956,17 +39180,18 @@ static never_inline bool parse_large_integer(const uint8_t *const buf, // 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. +// 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. // -static really_inline bool parse_number(const uint8_t *const buf, - ParsedJson &pj, +// Our objective is accurate parsing (ULP of 0 or 1) at high speed. +static really_inline bool parse_number(const uint8_t *const buf, ParsedJson &pj, const uint32_t offset, bool found_minus) { -#ifdef SIMDJSON_SKIPNUMBERPARSING // for performance analysis, it is sometimes useful to skip parsing - pj.write_tape_s64(0); // always write zero - return true; // always succeeds +#ifdef SIMDJSON_SKIPNUMBERPARSING // for performance analysis, it is sometimes + // useful to skip parsing + pj.write_tape_s64(0); // always write zero + return true; // always succeeds #else const char *p = reinterpret_cast(buf + offset); bool negative = false; @@ -38974,28 +39199,28 @@ static really_inline bool parse_number(const uint8_t *const buf, ++p; negative = true; if (!is_integer(*p)) { // a negative sign must be followed by an integer -#ifdef JSON_TEST_NUMBERS // for unit testing - foundInvalidNumber(buf + offset); +#ifdef JSON_TEST_NUMBERS // for unit testing + found_invalid_number(buf + offset); #endif return false; } } - const char *const startdigits = p; + const char *const start_digits = p; - uint64_t i; // an unsigned int avoids signed overflows (which are bad) + uint64_t i; // an unsigned int avoids signed overflows (which are bad) if (*p == '0') { // 0 cannot be followed by an integer ++p; if (is_not_structural_or_whitespace_or_exponent_or_decimal(*p)) { #ifdef JSON_TEST_NUMBERS // for unit testing - foundInvalidNumber(buf + offset); + found_invalid_number(buf + offset); #endif return false; } i = 0; } else { if (!(is_integer(*p))) { // must start with an integer -#ifdef JSON_TEST_NUMBERS // for unit testing - foundInvalidNumber(buf + offset); +#ifdef JSON_TEST_NUMBERS // for unit testing + found_invalid_number(buf + offset); #endif return false; } @@ -39006,25 +39231,33 @@ static really_inline bool parse_number(const uint8_t *const buf, // we rarely see large integer parts like 123456789 while (is_integer(*p)) { digit = *p - '0'; - i = 10 * i + digit; // might overflow + // a multiplication by 10 is cheaper than an arbitrary integer + // multiplication + i = 10 * i + digit; // might overflow, we will handle the overflow later ++p; } } int64_t exponent = 0; bool is_float = false; if ('.' == *p) { - is_float = true; + is_float = true; // At this point we know that we have a float + // 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. ++p; - const char *const firstafterperiod = p; - if(is_integer(*p)) { + const char *const first_after_period = p; + if (is_integer(*p)) { unsigned char digit = *p - '0'; ++p; - i = i * 10 + digit; + i = i * 10 + digit; // might overflow + multiplication by 10 is likely + // cheaper than arbitrary mult. + // we will handle the overflow later } else { #ifdef JSON_TEST_NUMBERS // for unit testing - foundInvalidNumber(buf + offset); + found_invalid_number(buf + offset); #endif - return false; + return false; } #ifdef SWAR_NUMBER_PARSING // this helps if we have lots of decimals! @@ -39037,97 +39270,103 @@ static really_inline bool parse_number(const uint8_t *const buf, while (is_integer(*p)) { unsigned char digit = *p - '0'; ++p; - i = i * 10 + digit; // in rare cases, this will overflow, but that's ok because we have parse_highprecision_float later. + i = i * 10 + digit; // in rare cases, this will overflow, but that's ok + // because we have parse_highprecision_float later. } - exponent = firstafterperiod - p; + exponent = first_after_period - p; } - int digitcount = p - startdigits - 1; - int64_t expnumber = 0; // exponential part + int digit_count = + p - start_digits - 1; // used later to guard against overflows + int64_t exp_number = 0; // exponential part if (('e' == *p) || ('E' == *p)) { is_float = true; ++p; - bool negexp = false; + bool neg_exp = false; if ('-' == *p) { - negexp = true; + neg_exp = true; ++p; } else if ('+' == *p) { ++p; } if (!is_integer(*p)) { #ifdef JSON_TEST_NUMBERS // for unit testing - foundInvalidNumber(buf + offset); + found_invalid_number(buf + offset); #endif return false; } unsigned char digit = *p - '0'; - expnumber = digit; + exp_number = digit; p++; if (is_integer(*p)) { digit = *p - '0'; - expnumber = 10 * expnumber + digit; + exp_number = 10 * exp_number + digit; ++p; } if (is_integer(*p)) { digit = *p - '0'; - expnumber = 10 * expnumber + digit; + exp_number = 10 * exp_number + digit; ++p; } - if (is_integer(*p)) { -// we refuse to parse this + while (is_integer(*p)) { + if (exp_number > 0x100000000) { // we need to check for overflows + // we refuse to parse this #ifdef JSON_TEST_NUMBERS // for unit testing - foundInvalidNumber(buf + offset); + found_invalid_number(buf + offset); #endif - return false; + return false; + } + digit = *p - '0'; + exp_number = 10 * exp_number + digit; + ++p; } - exponent += (negexp ? -expnumber : expnumber); + exponent += (neg_exp ? -exp_number : exp_number); } if (is_float) { - if (unlikely(digitcount >= 19)) { // this is uncommon!!! + uint64_t power_index = 308 + exponent; + if (unlikely((digit_count >= 19))) { // this is uncommon + // It is possible that the integer had an overflow. + // We have to handle the case where we have 0.0000somenumber. + const char *start = start_digits; + while ((*start == '0') || (*start == '.')) { + start++; + } + // we over-decrement by one when there is a '.' + digit_count -= (start - start_digits); + if (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!!! + return parse_float(buf, pj, offset, found_minus); + } + } + if (unlikely((power_index > 2 * 308))) { // this is uncommon!!! // this is almost never going to get called!!! // we start anew, going slowly!!! - return parse_float(buf, pj, offset, - found_minus); + return parse_float(buf, pj, offset, found_minus); } - /////////// - // We want 0.1e1 to be a float. - ////////// - if (i == 0) { - pj.write_tape_double(0.0); + double factor = power_of_ten[power_index]; + factor = negative ? -factor : factor; + double d = i * factor; + pj.write_tape_double(d); #ifdef JSON_TEST_NUMBERS // for unit testing - foundFloat(0.0, buf + offset); + found_float(d, buf + offset); #endif - } else { - double d = i; - d = negative ? -d : d; - uint64_t powerindex = 308 + exponent; - if(likely(powerindex <= 2 * 308)) { - // common case - d *= power_of_ten[powerindex]; - } else { - // this is uncommon so let us move this special case out - // of the main loop - return parse_float(buf, pj, offset,found_minus); - } - pj.write_tape_double(d); -#ifdef JSON_TEST_NUMBERS // for unit testing - foundFloat(d, buf + offset); -#endif - } } else { - if (unlikely(digitcount >= 18)) { // this is uncommon!!! - return parse_large_integer(buf, pj, offset, - found_minus); + if (unlikely(digit_count >= 18)) { // this is uncommon!!! + // there is a good chance that we had an overflow, so we need + // need to recover: we parse the whole thing again. + return parse_large_integer(buf, pj, offset, found_minus); } - i = negative ? 0-i : i; + i = negative ? 0 - i : i; pj.write_tape_s64(i); #ifdef JSON_TEST_NUMBERS // for unit testing - foundInteger(i, buf + offset); + found_integer(i, buf + offset); #endif } - return is_structural_or_whitespace(*p); + return is_structural_or_whitespace(*p); #endif // SIMDJSON_SKIPNUMBERPARSING } -}//simdjson +} // simdjson #endif /* end file include/simdjson/numberparsing.h */ /* begin file include/simdjson/stage2_build_tape.h */ @@ -39147,7 +39386,8 @@ really_inline bool is_valid_true_atom(const uint8_t *loc) { uint64_t tv = *reinterpret_cast("true "); uint64_t mask4 = 0x00000000ffffffff; uint32_t error = 0; - uint64_t locval; // we want to avoid unaligned 64-bit loads (undefined in C/C++) + uint64_t + locval; // we want to avoid unaligned 64-bit loads (undefined in C/C++) // this can read up to 7 bytes beyond the buffer size, but we require // SIMDJSON_PADDING of padding static_assert(sizeof(uint64_t) - 1 <= SIMDJSON_PADDING); @@ -39170,8 +39410,9 @@ really_inline bool is_valid_false_atom(const uint8_t *loc) { // the last character of false (it being 5 byte long!) would be // ignored uint64_t error = 0; - uint64_t locval; // we want to avoid unaligned 64-bit loads (undefined in C/C++) - // this can read up to 7 bytes beyond the buffer size, but we require + uint64_t + locval; // we want to avoid unaligned 64-bit loads (undefined in C/C++) + // this can read up to 7 bytes beyond the buffer size, but we require // SIMDJSON_PADDING of padding static_assert(sizeof(uint64_t) - 1 <= SIMDJSON_PADDING); std::memcpy(&locval, loc, sizeof(uint64_t)); @@ -39185,8 +39426,9 @@ really_inline bool is_valid_null_atom(const uint8_t *loc) { uint64_t nv = *reinterpret_cast("null "); uint64_t mask4 = 0x00000000ffffffff; uint32_t error = 0; - uint64_t locval; // we want to avoid unaligned 64-bit loads (undefined in C/C++) - // this can read up to 7 bytes beyond the buffer size, but we require + uint64_t + locval; // we want to avoid unaligned 64-bit loads (undefined in C/C++) + // this can read up to 7 bytes beyond the buffer size, but we require // SIMDJSON_PADDING of padding static_assert(sizeof(uint64_t) - 1 <= SIMDJSON_PADDING); std::memcpy(&locval, loc, sizeof(uint64_t)); @@ -39195,16 +39437,16 @@ really_inline bool is_valid_null_atom(const uint8_t *loc) { return error == 0; } -template -WARN_UNUSED ALLOW_SAME_PAGE_BUFFER_OVERRUN_QUALIFIER LENIENT_MEM_SANITIZER -int unified_machine(const uint8_t *buf, size_t len, ParsedJson &pj); +template +WARN_UNUSED ALLOW_SAME_PAGE_BUFFER_OVERRUN_QUALIFIER LENIENT_MEM_SANITIZER int +unified_machine(const uint8_t *buf, size_t len, ParsedJson &pj); -template +template int unified_machine(const char *buf, size_t len, ParsedJson &pj) { - return unified_machine(reinterpret_cast(buf), len, pj); + return unified_machine(reinterpret_cast(buf), len, pj); } -} +} // namespace simdjson #endif /* end file include/simdjson/stage2_build_tape.h */ @@ -39214,6 +39456,7 @@ int unified_machine(const char *buf, size_t len, ParsedJson &pj) { #include #ifdef _MSC_VER #include +// must be included after windows.h #include #else #include @@ -39221,112 +39464,145 @@ int unified_machine(const char *buf, size_t len, ParsedJson &pj) { namespace simdjson { // The function that users are expected to call is json_parse. -// We have more than one such function because we want to support several +// We have more than one such function because we want to support several // instruction sets. // function pointer type for json_parse -using json_parse_functype = int (const uint8_t *buf, size_t len, ParsedJson &pj, bool reallocifneeded); +using json_parse_functype = int(const uint8_t *buf, size_t len, ParsedJson &pj, + bool realloc_if_needed); -// Pointer that holds the json_parse implementation corresponding to the available SIMD instruction set +// Pointer that holds the json_parse implementation corresponding to the +// available SIMD instruction set extern json_parse_functype *json_parse_ptr; -// json_parse_implementation is the generic function, it is specialized for various -// architectures, e.g., as json_parse_implementation -// or json_parse_implementation -template -int json_parse_implementation(const uint8_t *buf, size_t len, ParsedJson &pj, bool reallocifneeded = true) { - if (pj.bytecapacity < len) { +// json_parse_implementation is the generic function, it is specialized for +// various architectures, e.g., as +// json_parse_implementation or +// json_parse_implementation +template +int json_parse_implementation(const uint8_t *buf, size_t len, ParsedJson &pj, + bool realloc_if_needed = true) { + if (pj.byte_capacity < len) { return simdjson::CAPACITY; } bool reallocated = false; - if(reallocifneeded) { -#ifdef ALLOW_SAME_PAGE_BUFFER_OVERRUN + if (realloc_if_needed) { +#if ALLOW_SAME_PAGE_BUFFER_OVERRUN // realloc is needed if the end of the memory crosses a page #ifdef _MSC_VER - SYSTEM_INFO sysInfo; - GetSystemInfo(&sysInfo); - long pagesize = sysInfo.dwPageSize; + SYSTEM_INFO sysInfo; + GetSystemInfo(&sysInfo); + long page_size = sysInfo.dwPageSize; #else - long pagesize = sysconf (_SC_PAGESIZE); + long page_size = sysconf(_SC_PAGESIZE); #endif ////////////// // We want to check that buf + len - 1 and buf + len - 1 + SIMDJSON_PADDING // are in the same page. - // That is, we want to check that - // (buf + len - 1) / pagesize == (buf + len - 1 + SIMDJSON_PADDING) / pagesize - // That's true if (buf + len - 1) % pagesize + SIMDJSON_PADDING < pagesize. + // That is, we want to check that + // (buf + len - 1) / page_size == (buf + len - 1 + SIMDJSON_PADDING) / + // page_size That's true if (buf + len - 1) % page_size + SIMDJSON_PADDING < + // page_size. /////////// - if ( (reinterpret_cast(buf + len - 1) % pagesize ) + SIMDJSON_PADDING < static_cast(pagesize) ) { + if ((reinterpret_cast(buf + len - 1) % page_size) + + SIMDJSON_PADDING < + static_cast(page_size)) { #else // SIMDJSON_SAFE_SAME_PAGE_READ_OVERRUN - if(true) { // if not SIMDJSON_SAFE_SAME_PAGE_READ_OVERRUN, we always reallocate + if (true) { // if not SIMDJSON_SAFE_SAME_PAGE_READ_OVERRUN, we always + // reallocate #endif - const uint8_t *tmpbuf = buf; - buf = (uint8_t *) allocate_padded_buffer(len); - if(buf == NULL) return simdjson::MEMALLOC; - memcpy((void*)buf,tmpbuf,len); + const uint8_t *tmp_buf = buf; + buf = (uint8_t *)allocate_padded_buffer(len); + if (buf == NULL) + return simdjson::MEMALLOC; + memcpy((void *)buf, tmp_buf, len); reallocated = true; - } // if (true) OR if ( (reinterpret_cast(buf + len - 1) % pagesize ) + SIMDJSON_PADDING < static_cast(pagesize) ) { - } // if(reallocifneeded) { + } // if (true) OR if ( (reinterpret_cast(buf + len - 1) % + // page_size ) + SIMDJSON_PADDING < static_cast(page_size) ) { + } // if(realloc_if_needed) { int stage1_is_ok = simdjson::find_structural_bits(buf, len, pj); - if(stage1_is_ok != simdjson::SUCCESS) { - pj.errorcode = stage1_is_ok; - return pj.errorcode; - } + if (stage1_is_ok != simdjson::SUCCESS) { + pj.error_code = stage1_is_ok; + return pj.error_code; + } int res = unified_machine(buf, len, pj); - if(reallocated) { aligned_free((void*)buf);} + if (reallocated) { + aligned_free((void *)buf); + } return res; } -// Parse a document found in buf. -// You need to preallocate ParsedJson with a capacity of len (e.g., pj.allocateCapacity(len)). +// Parse a document found in buf. // -// The function returns simdjson::SUCCESS (an integer = 0) in case of a success or 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::errorMsg function converts these error codes -// into a string). +// 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 can also check validity by calling pj.isValid(). The same ParsedJson can be reused for other documents. +// You need to preallocate ParsedJson with a capacity of len (e.g., +// pj.allocate_capacity(len)). // -// If reallocifneeded is true (default) then a temporary buffer is created when needed during processing -// (a copy of the input string is made). -// The input buf should be readable up to buf + len + SIMDJSON_PADDING if reallocifneeded is false, -// all bytes at and after buf + len are ignored (can be garbage). -// The ParsedJson object can be reused. +// The function returns simdjson::SUCCESS (an integer = 0) in case of a success +// or 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 pj.is_valid(). The same ParsedJson can +// be reused for other documents. +// +// If realloc_if_needed is true (default) then a temporary buffer is created +// when needed during processing (a copy of the input string is made). The input +// buf should be readable up to buf + len + SIMDJSON_PADDING if +// realloc_if_needed is false, all bytes at and after buf + len are ignored +// (can be garbage). The ParsedJson object can be reused. -inline int json_parse(const uint8_t *buf, size_t len, ParsedJson &pj, bool reallocifneeded = true) { - return json_parse_ptr(buf, len, pj, reallocifneeded); +inline int json_parse(const uint8_t *buf, size_t len, ParsedJson &pj, + bool realloc_if_needed = true) { + return json_parse_ptr(buf, len, pj, realloc_if_needed); } // Parse a document found in buf. -// You need to preallocate ParsedJson with a capacity of len (e.g., pj.allocateCapacity(len)). // -// The function returns simdjson::SUCCESS (an integer = 0) in case of a success or 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::errorMsg function converts these error codes -// into a string). +// 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 need to preallocate ParsedJson with a capacity of len (e.g., +// pj.allocate_capacity(len)). +// +// The function returns simdjson::SUCCESS (an integer = 0) in case of a success +// or 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 pj.isValid(). The same ParsedJson can be reused for other documents. +// by calling pj.is_valid(). The same ParsedJson can be reused for other +// documents. // -// If reallocifneeded is true (default) then a temporary buffer is created when needed during processing -// (a copy of the input string is made). -// The input buf should be readable up to buf + len + SIMDJSON_PADDING if reallocifneeded is false, -// all bytes at and after buf + len are ignored (can be garbage). -// The ParsedJson object can be reused. -inline int json_parse(const char * buf, size_t len, ParsedJson &pj, bool reallocifneeded = true) { - return json_parse_ptr(reinterpret_cast(buf), len, pj, reallocifneeded); +// If realloc_if_needed is true (default) then a temporary buffer is created +// when needed during processing (a copy of the input string is made). The input +// buf should be readable up to buf + len + SIMDJSON_PADDING if +// realloc_if_needed is false, all bytes at and after buf + len are ignored +// (can be garbage). The ParsedJson object can be reused. +inline int json_parse(const char *buf, size_t len, ParsedJson &pj, + bool realloc_if_needed = true) { + return json_parse_ptr(reinterpret_cast(buf), len, pj, + realloc_if_needed); } // We do not want to allow implicit conversion from C string to std::string. -int json_parse(const char * buf, ParsedJson &pj) = delete; +int json_parse(const char *buf, ParsedJson &pj) = delete; // Parse a document found in in string s. -// You need to preallocate ParsedJson with a capacity of len (e.g., pj.allocateCapacity(len)). +// You need to preallocate ParsedJson with a capacity of len (e.g., +// pj.allocate_capacity(len)). // -// The function returns simdjson::SUCCESS (an integer = 0) in case of a success or 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::errorMsg function converts these error codes -// into a string). +// The function returns simdjson::SUCCESS (an integer = 0) in case of a success +// or 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). // // A temporary buffer is created when needed during processing // (a copy of the input string is made). @@ -39335,75 +39611,104 @@ inline int json_parse(const std::string &s, ParsedJson &pj) { } // Parse a document found in in string s. -// You need to preallocate ParsedJson with a capacity of len (e.g., pj.allocateCapacity(len)). // -// The function returns simdjson::SUCCESS (an integer = 0) in case of a success or 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::errorMsg function converts these error codes -// into a string). +// 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 need to preallocate ParsedJson with a capacity of len (e.g., +// pj.allocate_capacity(len)). +// +// The function returns simdjson::SUCCESS (an integer = 0) in case of a success +// or 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 pj.isValid(). The same ParsedJson can be reused for other documents. +// by calling pj.is_valid(). The same ParsedJson can be reused for other +// documents. inline int json_parse(const padded_string &s, ParsedJson &pj) { return json_parse(s.data(), s.length(), pj, false); } - // Build a ParsedJson object. You can check validity -// by calling pj.isValid(). This does the memory allocation needed for ParsedJson. -// If reallocifneeded is true (default) then a temporary buffer is created when needed during processing -// (a copy of the input string is made). +// by calling pj.is_valid(). This does the memory allocation needed for +// ParsedJson. If realloc_if_needed is true (default) then a temporary buffer is +// created when needed during processing (a copy of the input string is made). // -// the input buf should be readable up to buf + len + SIMDJSON_PADDING if reallocifneeded is false, -// all bytes at and after buf + len are ignored (can be garbage). +// The input buf should be readable up to buf + len + SIMDJSON_PADDING if +// realloc_if_needed is false, all bytes at and after buf + len are ignored +// (can be garbage). +// +// 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. // // This is a convenience function which calls json_parse. WARN_UNUSED -ParsedJson build_parsed_json(const uint8_t *buf, size_t len, bool reallocifneeded = true); +ParsedJson build_parsed_json(const uint8_t *buf, size_t len, + bool realloc_if_needed = true); WARN_UNUSED // Build a ParsedJson object. You can check validity -// by calling pj.isValid(). This does the memory allocation needed for ParsedJson. -// If reallocifneeded is true (default) then a temporary buffer is created when needed during processing -// (a copy of the input string is made). -// The input buf should be readable up to buf + len + SIMDJSON_PADDING if reallocifneeded is false, -// all bytes at and after buf + len are ignored (can be garbage). +// by calling pj.is_valid(). This does the memory allocation needed for +// ParsedJson. If realloc_if_needed is true (default) then a temporary buffer is +// created when needed during processing (a copy of the input string is made). +// +// The input buf should be readable up to buf + len + SIMDJSON_PADDING if +// realloc_if_needed is false, all bytes at and after buf + len are ignored +// (can be garbage). +// +// +// 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. // // This is a convenience function which calls json_parse. -inline ParsedJson build_parsed_json(const char * buf, size_t len, bool reallocifneeded = true) { - return build_parsed_json(reinterpret_cast(buf), len, reallocifneeded); +inline ParsedJson build_parsed_json(const char *buf, size_t len, + bool realloc_if_needed = true) { + return build_parsed_json(reinterpret_cast(buf), len, + realloc_if_needed); } - // We do not want to allow implicit conversion from C string to std::string. ParsedJson build_parsed_json(const char *buf) = delete; - // Parse a document found in in string s. -// You need to preallocate ParsedJson with a capacity of len (e.g., pj.allocateCapacity(len)). -// Return SUCCESS (an integer = 0) in case of a success. You can also check validity -// by calling pj.isValid(). The same ParsedJson can be reused for other documents. +// You need to preallocate ParsedJson with a capacity of len (e.g., +// pj.allocate_capacity(len)). Return SUCCESS (an integer = 0) in case of a +// success. You can also check validity by calling pj.is_valid(). The same +// ParsedJson can be reused for other documents. // // A temporary buffer is created when needed during processing // (a copy of the input string is made). // +// 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. +// // This is a convenience function which calls json_parse. WARN_UNUSED inline ParsedJson build_parsed_json(const std::string &s) { return build_parsed_json(s.data(), s.length(), true); } - // Parse a document found in in string s. -// You need to preallocate ParsedJson with a capacity of len (e.g., pj.allocateCapacity(len)). -// Return SUCCESS (an integer = 0) in case of a success. You can also check validity -// by calling pj.isValid(). The same ParsedJson can be reused for other documents. +// You need to preallocate ParsedJson with a capacity of len (e.g., +// pj.allocate_capacity(len)). Return SUCCESS (an integer = 0) in case of a +// success. You can also check validity by calling pj.is_valid(). The same +// ParsedJson can be reused for other documents. +// +// 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. // // This is a convenience function which calls json_parse. WARN_UNUSED inline ParsedJson build_parsed_json(const padded_string &s) { return build_parsed_json(s.data(), s.length(), false); } -} +} // namespace simdjson #endif /* end file include/simdjson/jsonparser.h */ diff --git a/tools/release.py b/tools/release.py index a5a4105d3..45a4dec00 100755 --- a/tools/release.py +++ b/tools/release.py @@ -82,16 +82,19 @@ versionfilerel = os.sep + "include" + os.sep + "simdjson" + os.sep + "simdjson_v versionfile = maindir + versionfilerel with open(versionfile, 'w') as file: - file.write("// "+versionfilerel+" automatically generated by release.py, do not change by hand \n") - file.write("#ifndef SIMDJSON_INCLUDE_SIMDJSON_VERSION \n") - file.write("#define SIMDJSON_INCLUDE_SIMDJSON_VERSION \n") - file.write("#define SIMDJSON_VERSION "+toversionstring(*newversion)+" \n") - file.write("enum { \n") - file.write(" SIMDJSON_VERSION_MAJOR = "+str(newversion[0])+", \n") - file.write(" SIMDJSON_VERSION_MINOR = "+str(newversion[1])+", \n") - file.write(" SIMDJSON_VERSION_REVISION = "+str(newversion[2])+" \n") - file.write("}; \n") - file.write("#endif // SIMDJSON_INCLUDE_SIMDJSON_VERSION \n") + file.write("// "+versionfilerel+" automatically generated by release.py,\n") + file.write("// do not change by hand\n") + file.write("#ifndef SIMDJSON_INCLUDE_SIMDJSON_VERSION\n") + file.write("#define SIMDJSON_INCLUDE_SIMDJSON_VERSION\n") + file.write("#define SIMDJSON_VERSION "+toversionstring(*newversion)+"\n") + file.write("namespace simdjson {\n") + file.write("enum {\n") + file.write(" SIMDJSON_VERSION_MAJOR = "+str(newversion[0])+",\n") + file.write(" SIMDJSON_VERSION_MINOR = "+str(newversion[1])+",\n") + file.write(" SIMDJSON_VERSION_REVISION = "+str(newversion[2])+"\n") + file.write("};\n") + file.write("}\n") + file.write("#endif // SIMDJSON_INCLUDE_SIMDJSON_VERSION\n") print(versionfile + " modified")