From c924aaede9e104f3dda897395056ab1e15bedb98 Mon Sep 17 00:00:00 2001 From: Daniel Lemire Date: Thu, 30 Jan 2020 17:16:41 -0500 Subject: [PATCH] Fix issue472: make JsonStream a template. (#473) * Fix issue472: make JsonStream a template. * Adding missing include. * Tweaking headers and some minor formatting. * Removing file from aggregation. * Moving jsoncharutils * Adding new header. * Trying another header. * Let us try to route around Visual Studio's nonesense. --- Makefile | 6 +- amalgamation.sh | 3 +- include/simdjson/common_defs.h | 3 +- include/simdjson/jsonioutil.h | 3 +- include/simdjson/jsonstream.h | 541 +++++++++++----- include/simdjson/padded_string.h | 2 + include/simdjson/parsedjson.h | 4 +- include/simdjson/parsedjsoniterator.h | 6 +- singleheader/amalgamation_demo.cpp | 2 +- singleheader/simdjson.cpp | 570 +---------------- singleheader/simdjson.h | 867 +++++++++++++++++++++----- src/CMakeLists.txt | 1 - src/jsonminifier.cpp | 16 +- src/jsonstream.cpp | 226 ------- tests/basictests.cpp | 6 +- 15 files changed, 1147 insertions(+), 1109 deletions(-) delete mode 100755 src/jsonstream.cpp diff --git a/Makefile b/Makefile index eca887d58..aa15d2e1f 100644 --- a/Makefile +++ b/Makefile @@ -58,8 +58,8 @@ endif # ifeq ($(DEBUG),1) endif # ifeq ($(SANITIZE),1) endif # ifeq ($(MEMSANITIZE),1) -MAINEXECUTABLES=parse minify json2json jsonstats statisticalmodel jsonpointer -TESTEXECUTABLES=jsoncheck jsoncheck_noavx integer_tests numberparsingcheck stringparsingcheck pointercheck jsonstream_test +MAINEXECUTABLES=parse minify json2json jsonstats statisticalmodel jsonpointer get_corpus_benchmark +TESTEXECUTABLES=jsoncheck jsoncheck_noavx integer_tests numberparsingcheck stringparsingcheck pointercheck jsonstream_test basictests COMPARISONEXECUTABLES=minifiercompetition parsingcompetition parseandstatcompetition distinctuseridcompetition allparserscheckfile allparsingcompetition SUPPLEMENTARYEXECUTABLES=parse_noutf8validation parse_nonumberparsing parse_nostringparsing @@ -73,7 +73,7 @@ LIBHEADERS=src/jsoncharutils.h src/simdprune_tables.h $(LIBHEADERS_GENERIC) $(LI PUBHEADERS=include/simdjson/common_defs.h include/simdjson/isadetection.h include/simdjson/jsonformatutils.h include/simdjson/jsonioutil.h include/simdjson/jsonminifier.h include/simdjson/jsonparser.h include/simdjson/padded_string.h include/simdjson/parsedjson.h include/simdjson/parsedjsoniterator.h include/simdjson/portability.h include/simdjson/simdjson.h include/simdjson/simdjson_version.h include/simdjson/stage1_find_marks.h include/simdjson/stage2_build_tape.h HEADERS=$(PUBHEADERS) $(LIBHEADERS) -LIBFILES=src/jsonioutil.cpp src/jsonparser.cpp src/jsonstream.cpp src/simdjson.cpp src/stage1_find_marks.cpp src/stage2_build_tape.cpp src/parsedjson.cpp src/parsedjsoniterator.cpp +LIBFILES=src/jsonioutil.cpp src/jsonparser.cpp src/simdjson.cpp src/stage1_find_marks.cpp src/stage2_build_tape.cpp src/parsedjson.cpp src/parsedjsoniterator.cpp MINIFIERHEADERS=include/simdjson/jsonminifier.h MINIFIERLIBFILES=src/jsonminifier.cpp diff --git a/amalgamation.sh b/amalgamation.sh index e7b2e4a1e..5d58a6508 100755 --- a/amalgamation.sh +++ b/amalgamation.sh @@ -16,13 +16,11 @@ ALLCFILES=" $SCRIPTPATH/src/arm64/intrinsics.h $SCRIPTPATH/src/haswell/intrinsics.h $SCRIPTPATH/src/westmere/intrinsics.h -$SCRIPTPATH/src/jsoncharutils.h $SCRIPTPATH/src/simdprune_tables.h $SCRIPTPATH/src/simdjson.cpp $SCRIPTPATH/src/jsonioutil.cpp $SCRIPTPATH/src/jsonminifier.cpp $SCRIPTPATH/src/jsonparser.cpp -$SCRIPTPATH/src/jsonstream.cpp $SCRIPTPATH/src/arm64/bitmanipulation.h $SCRIPTPATH/src/haswell/bitmanipulation.h $SCRIPTPATH/src/westmere/bitmanipulation.h @@ -66,6 +64,7 @@ $SCRIPTPATH/include/simdjson/parsedjsoniterator.h $SCRIPTPATH/include/simdjson/stage1_find_marks.h $SCRIPTPATH/include/simdjson/stage2_build_tape.h $SCRIPTPATH/include/simdjson/jsonparser.h +$SCRIPTPATH/src/jsoncharutils.h $SCRIPTPATH/include/simdjson/jsonstream.h " diff --git a/include/simdjson/common_defs.h b/include/simdjson/common_defs.h index b3475710e..676280862 100644 --- a/include/simdjson/common_defs.h +++ b/include/simdjson/common_defs.h @@ -1,9 +1,8 @@ #ifndef SIMDJSON_COMMON_DEFS_H #define SIMDJSON_COMMON_DEFS_H -#include "simdjson/portability.h" - #include +#include "simdjson/portability.h" // we support documents up to 4GB #define SIMDJSON_MAXSIZE_BYTES 0xFFFFFFFF diff --git a/include/simdjson/jsonioutil.h b/include/simdjson/jsonioutil.h index 07b163d2c..7071e06c7 100644 --- a/include/simdjson/jsonioutil.h +++ b/include/simdjson/jsonioutil.h @@ -1,13 +1,14 @@ #ifndef SIMDJSON_JSONIOUTIL_H #define SIMDJSON_JSONIOUTIL_H -#include "simdjson/common_defs.h" #include #include #include #include +#include #include +#include "simdjson/common_defs.h" #include "simdjson/padded_string.h" namespace simdjson { diff --git a/include/simdjson/jsonstream.h b/include/simdjson/jsonstream.h index 0bc03893d..a8de4d69d 100644 --- a/include/simdjson/jsonstream.h +++ b/include/simdjson/jsonstream.h @@ -1,129 +1,155 @@ #ifndef SIMDJSON_JSONSTREAM_H #define SIMDJSON_JSONSTREAM_H - #include +#include +#include #include +#include "simdjson/isadetection.h" +#include "simdjson/padded_string.h" +#include "simdjson/simdjson.h" #include "simdjson/stage1_find_marks.h" #include "simdjson/stage2_build_tape.h" -#include "simdjson/simdjson.h" -#include "simdjson/padded_string.h" +#include "jsoncharutils.h" + namespace simdjson { - /************************************************************************************* - * The main motivation for this piece of software is to achieve maximum speed and offer - * good quality of life while parsing files containing multiple JSON documents. - * - * Since we want to offer flexibility and not restrict ourselves to a specific file - * format, we support any file that contains any valid JSON documents separated by one - * or more character that is considered a whitespace by the JSON spec. - * Namely: space, nothing, linefeed, carriage return, horizontal tab. - * Anything that is not whitespace will be parsed as a JSON document and could lead - * to failure. - * - * To offer maximum parsing speed, our implementation processes the data inside the - * buffer by batches and their size is defined by the parameter "batch_size". - * By loading data in batches, we can optimize the time spent allocating data in the - * ParsedJson and can also open the possibility of multi-threading. - * The batch_size must be at least as large as the biggest document in the file, but - * not too large in order to submerge the chached memory. We found that 1MB is - * somewhat a sweet spot for now. Eventually, this batch_size could be fully - * automated and be optimal at all times. - ************************************************************************************/ - class JsonStream { - public: - /* Create a JsonStream object that can be used to parse sequentially the valid - * JSON documents found in the buffer "buf". - * - * The batch_size must be at least as large as the biggest document in the file, but - * not too large to submerge the cached memory. We found that 1MB is - * somewhat a sweet spot for now. - * - * The user is expected to call the following json_parse method to parse the next - * valid JSON document found in the buffer. This method can and is expected to be - * called in a loop. - * - * Various methods are offered to keep track of the status, like get_current_buffer_loc, - * get_n_parsed_docs, get_n_bytes_parsed, etc. - * - * */ - JsonStream(const padded_string &s, size_t batch_size = 1000000); +/************************************************************************************* + * The main motivation for this piece of software is to achieve maximum speed + *and offer + * good quality of life while parsing files containing multiple JSON documents. + * + * Since we want to offer flexibility and not restrict ourselves to a specific + *file + * format, we support any file that contains any valid JSON documents separated + *by one + * or more character that is considered a whitespace by the JSON spec. + * Namely: space, nothing, linefeed, carriage return, horizontal tab. + * Anything that is not whitespace will be parsed as a JSON document and could + *lead + * to failure. + * + * To offer maximum parsing speed, our implementation processes the data inside + *the + * buffer by batches and their size is defined by the parameter "batch_size". + * By loading data in batches, we can optimize the time spent allocating data in + *the + * ParsedJson and can also open the possibility of multi-threading. + * The batch_size must be at least as large as the biggest document in the file, + *but + * not too large in order to submerge the chached memory. We found that 1MB is + * somewhat a sweet spot for now. Eventually, this batch_size could be fully + * automated and be optimal at all times. + ************************************************************************************/ +/** +* The template parameter (string_container) must +* support the data() and size() methods, returning a pointer +* to a char* and to the number of bytes respectively. +* The simdjson parser may read up to SIMDJSON_PADDING bytes beyond the end +* of the string, so if you do not use a padded_string container, +* you have the responsability to overallocated. If you fail to +* do so, your software may crash if you cross a page boundary, +* and you should expect memory checkers to object. +* Most users should use a simdjson::padded_string. +*/ +template class JsonStream { +public: + /* Create a JsonStream object that can be used to parse sequentially the valid + * JSON documents found in the buffer "buf". + * + * The batch_size must be at least as large as the biggest document in the + * file, but + * not too large to submerge the cached memory. We found that 1MB is + * somewhat a sweet spot for now. + * + * The user is expected to call the following json_parse method to parse the + * next + * valid JSON document found in the buffer. This method can and is expected + * to be + * called in a loop. + * + * Various methods are offered to keep track of the status, like + * get_current_buffer_loc, + * get_n_parsed_docs, get_n_bytes_parsed, etc. + * + * */ + JsonStream(const string_container &s, size_t batch_size = 1000000); - ~JsonStream(); + ~JsonStream(); - /* Parse the next document found in the buffer previously given to JsonStream. + /* Parse the next document found in the buffer previously given to JsonStream. - * The content should be a valid JSON document encoded as UTF-8. If there is a - * UTF-8 BOM, the caller is responsible for omitting it, UTF-8 BOM are - * discouraged. - * - * You do NOT need to pre-allocate ParsedJson. This function takes care of - * pre-allocating a capacity defined by the batch_size defined when creating the - * JsonStream object. - * - * The function returns simdjson::SUCCESS_AND_HAS_MORE (an integer = 1) in case - * of success and indicates that the buffer still contains more data to be parsed, - * meaning this function can be called again to return the next JSON document - * after this one. - * - * The function returns simdjson::SUCCESS (as integer = 0) in case of success - * and indicates that the buffer has successfully been parsed to the end. - * Every document it contained has been parsed without error. - * - * The function returns an error code from simdjson/simdjson.h in case of failure - * such as simdjson::CAPACITY, simdjson::MEMALLOC, simdjson::DEPTH_ERROR and so forth; - * the simdjson::error_message function converts these error codes into a - * string). - * - * You can also check validity by calling pj.is_valid(). The same ParsedJson can - * and should be reused for the other documents in the buffer. */ - int json_parse(ParsedJson &pj); + * The content should be a valid JSON document encoded as UTF-8. If there is a + * UTF-8 BOM, the caller is responsible for omitting it, UTF-8 BOM are + * discouraged. + * + * You do NOT need to pre-allocate ParsedJson. This function takes care of + * pre-allocating a capacity defined by the batch_size defined when creating + the + * JsonStream object. + * + * The function returns simdjson::SUCCESS_AND_HAS_MORE (an integer = 1) in + case + * of success and indicates that the buffer still contains more data to be + parsed, + * meaning this function can be called again to return the next JSON document + * after this one. + * + * The function returns simdjson::SUCCESS (as integer = 0) in case of success + * and indicates that the buffer has successfully been parsed to the end. + * Every document it contained has been parsed without error. + * + * The function returns an error code from simdjson/simdjson.h in case of + failure + * such as simdjson::CAPACITY, simdjson::MEMALLOC, simdjson::DEPTH_ERROR and + so forth; + * the simdjson::error_message function converts these error codes into a + * string). + * + * You can also check validity by calling pj.is_valid(). The same ParsedJson + can + * and should be reused for the other documents in the buffer. */ + int json_parse(ParsedJson &pj); - /* Returns the location (index) of where the next document should be in the buffer. - * Can be used for debugging, it tells the user the position of the end of the last - * valid JSON document parsed*/ - size_t get_current_buffer_loc() const; + /* Returns the location (index) of where the next document should be in the + * buffer. + * Can be used for debugging, it tells the user the position of the end of the + * last + * valid JSON document parsed*/ + inline size_t get_current_buffer_loc() const { return current_buffer_loc; } - /* Returns the total amount of complete documents parsed by the JsonStream, - * in the current buffer, at the given time.*/ - size_t get_n_parsed_docs() const; + /* Returns the total amount of complete documents parsed by the JsonStream, + * in the current buffer, at the given time.*/ + inline size_t get_n_parsed_docs() const { return n_parsed_docs; } - /* Returns the total amount of data (in bytes) parsed by the JsonStream, - * in the current buffer, at the given time.*/ - size_t get_n_bytes_parsed() const; + /* Returns the total amount of data (in bytes) parsed by the JsonStream, + * in the current buffer, at the given time.*/ + inline size_t get_n_bytes_parsed() const { return n_bytes_parsed; } - private: +private: + inline const char *buf() const { return str.data() + str_start; } - inline const char * buf() const { - return str.data() + str_start; - } - inline void advance(size_t offset) { - str_start += offset; - } + inline void advance(size_t offset) { str_start += offset; } - inline size_t remaining() const { - return str.size() - str_start; - } - const simdjson::padded_string & str; - size_t _batch_size; - size_t str_start{0}; - size_t next_json{0}; - bool load_next_batch{true}; - size_t current_buffer_loc{0}; + inline size_t remaining() const { return str.size() - str_start; } + + const string_container &str; + size_t _batch_size; // this is actually variable! + size_t str_start{0}; + size_t next_json{0}; + bool load_next_batch{true}; + size_t current_buffer_loc{0}; #ifdef SIMDJSON_THREADS_ENABLED - size_t last_json_buffer_loc{0}; + size_t last_json_buffer_loc{0}; #endif - size_t n_parsed_docs{0}; - size_t n_bytes_parsed{0}; + size_t n_parsed_docs{0}; + size_t n_bytes_parsed{0}; #ifdef SIMDJSON_THREADS_ENABLED - int stage1_is_ok_thread{0}; - std::thread stage_1_thread; - simdjson::ParsedJson pj_thread; + int stage1_is_ok_thread{0}; + std::thread stage_1_thread; + simdjson::ParsedJson pj_thread; #endif - - }; - - +}; // end of class JsonStream /* This algorithm is used to quickly identify the buffer position of * the last JSON document inside the current batch. @@ -143,55 +169,274 @@ namespace simdjson { * the pair, and means that we cannot identify the last document. To fix this * issue, we keep a count of the open and closed curly/square braces we found * while searching for the pair. When we find a pair AND the count of open and - * closed curly/square braces is the same, we know that we just passed a complete + * closed curly/square braces is the same, we know that we just passed a + * complete * document, therefore the last json buffer location is the end of the batch * */ -inline size_t find_last_json_buf_idx(const char * buf, size_t size, const ParsedJson &pj) { - // this function can be generally useful - if(pj.n_structural_indexes == 0) return 0; - auto last_i = pj.n_structural_indexes - 1; - if (pj.structural_indexes[last_i] == size) { - if(last_i == 0) return 0; - last_i = pj.n_structural_indexes - 2; - } - auto arr_cnt = 0; - auto obj_cnt = 0; - for (auto i = last_i; i > 0; i--) { - auto idxb = pj.structural_indexes[i]; - switch (buf[idxb]) { - case ':': - case ',': - continue; - case '}': - obj_cnt--; - continue; - case ']': - arr_cnt--; - continue; - case '{': - obj_cnt++; - break; - case '[': - arr_cnt++; - break; - } - auto idxa = pj.structural_indexes[i - 1]; - switch (buf[idxa]) { - case '{': - case '[': - case ':': - case ',': - continue; - } - if (!arr_cnt && !obj_cnt) { - return last_i+1; - } - return i; - } +inline size_t find_last_json_buf_idx(const char *buf, size_t size, + const ParsedJson &pj) { + // this function can be generally useful + if (pj.n_structural_indexes == 0) return 0; + auto last_i = pj.n_structural_indexes - 1; + if (pj.structural_indexes[last_i] == size) { + if (last_i == 0) + return 0; + last_i = pj.n_structural_indexes - 2; + } + auto arr_cnt = 0; + auto obj_cnt = 0; + for (auto i = last_i; i > 0; i--) { + auto idxb = pj.structural_indexes[i]; + switch (buf[idxb]) { + case ':': + case ',': + continue; + case '}': + obj_cnt--; + continue; + case ']': + arr_cnt--; + continue; + case '{': + obj_cnt++; + break; + case '[': + arr_cnt++; + break; + } + auto idxa = pj.structural_indexes[i - 1]; + switch (buf[idxa]) { + case '{': + case '[': + case ':': + case ',': + continue; + } + if (!arr_cnt && !obj_cnt) { + return last_i + 1; + } + return i; + } + return 0; } +// Everything in the following anonymous namespace should go. +// It is a hack. +namespace { + +typedef int (*stage1_functype)(const char *buf, size_t len, + simdjson::ParsedJson &pj, bool streaming); +typedef int (*stage2_functype)(const char *buf, size_t len, + simdjson::ParsedJson &pj, size_t &next_json); + +stage1_functype best_stage1; +stage2_functype best_stage2; + +//// TODO: generalize this set of functions. We don't want to have a copy in +/// jsonparser.cpp +void find_the_best_supported_implementation() { + uint32_t supports = simdjson::detect_supported_architectures(); +// Order from best to worst (within architecture) +#ifdef IS_X86_64 + constexpr uint32_t haswell_flags = + simdjson::instruction_set::AVX2 | simdjson::instruction_set::PCLMULQDQ | + simdjson::instruction_set::BMI1 | simdjson::instruction_set::BMI2; + constexpr uint32_t westmere_flags = + simdjson::instruction_set::SSE42 | simdjson::instruction_set::PCLMULQDQ; + if ((haswell_flags & supports) == haswell_flags) { + best_stage1 = + simdjson::find_structural_bits; + best_stage2 = simdjson::unified_machine; + return; + } + if ((westmere_flags & supports) == westmere_flags) { + best_stage1 = + simdjson::find_structural_bits; + best_stage2 = simdjson::unified_machine; + return; + } +#endif +#ifdef IS_ARM64 + if (supports & instruction_set::NEON) { + best_stage1 = simdjson::find_structural_bits; + best_stage2 = simdjson::unified_machine; + return; + } +#endif + // we throw an exception since this should not be recoverable + throw new std::runtime_error("unsupported architecture"); +} +} // anonymous namespace + +template +JsonStream::JsonStream(const string_container &s, + size_t batchSize) + : str(s), _batch_size(batchSize) { + find_the_best_supported_implementation(); } +template JsonStream::~JsonStream() { +#ifdef SIMDJSON_THREADS_ENABLED + if (stage_1_thread.joinable()) { + stage_1_thread.join(); + } +#endif +} -#endif //SIMDJSON_JSONSTREAM_H +#ifdef SIMDJSON_THREADS_ENABLED + +// threaded version of json_parse +// todo: simplify this code further +template +int JsonStream::json_parse(ParsedJson &pj) { + if (unlikely(pj.byte_capacity == 0)) { + const bool allocok = pj.allocate_capacity(_batch_size); + if (!allocok) { + pj.error_code = simdjson::MEMALLOC; + return pj.error_code; + } + } else if (unlikely(pj.byte_capacity < _batch_size)) { + pj.error_code = simdjson::CAPACITY; + return pj.error_code; + } + if (unlikely(pj_thread.byte_capacity < _batch_size)) { + const bool allocok_thread = pj_thread.allocate_capacity(_batch_size); + if (!allocok_thread) { + pj.error_code = simdjson::MEMALLOC; + return pj.error_code; + } + } + if (unlikely(load_next_batch)) { + // First time loading + if (!stage_1_thread.joinable()) { + _batch_size = (std::min)(_batch_size, remaining()); + _batch_size = trimmed_length_safe_utf8((const char *)buf(), _batch_size); + if (_batch_size == 0) { + pj.error_code = simdjson::UTF8_ERROR; + return pj.error_code; + } + int stage1_is_ok = best_stage1(buf(), _batch_size, pj, true); + if (stage1_is_ok != simdjson::SUCCESS) { + pj.error_code = stage1_is_ok; + return pj.error_code; + } + size_t last_index = find_last_json_buf_idx(buf(), _batch_size, pj); + if (last_index == 0) { + if (pj.n_structural_indexes == 0) { + pj.error_code = simdjson::EMPTY; + return pj.error_code; + } + } else { + pj.n_structural_indexes = last_index + 1; + } + } + // the second thread is running or done. + else { + stage_1_thread.join(); + if (stage1_is_ok_thread != simdjson::SUCCESS) { + pj.error_code = stage1_is_ok_thread; + return pj.error_code; + } + std::swap(pj.structural_indexes, pj_thread.structural_indexes); + pj.n_structural_indexes = pj_thread.n_structural_indexes; + advance(last_json_buffer_loc); + n_bytes_parsed += last_json_buffer_loc; + } + // let us decide whether we will start a new thread + if (remaining() - _batch_size > 0) { + last_json_buffer_loc = + pj.structural_indexes[find_last_json_buf_idx(buf(), _batch_size, pj)]; + _batch_size = (std::min)(_batch_size, remaining() - last_json_buffer_loc); + if (_batch_size > 0) { + _batch_size = trimmed_length_safe_utf8( + (const char *)(buf() + last_json_buffer_loc), _batch_size); + if (_batch_size == 0) { + pj.error_code = simdjson::UTF8_ERROR; + return pj.error_code; + } + // let us capture read-only variables + const char *const b = buf() + last_json_buffer_loc; + const size_t bs = _batch_size; + // we call the thread on a lambda that will update + // this->stage1_is_ok_thread + // there is only one thread that may write to this value + stage_1_thread = std::thread([this, b, bs] { + this->stage1_is_ok_thread = best_stage1(b, bs, this->pj_thread, true); + }); + } + } + next_json = 0; + load_next_batch = false; + } // load_next_batch + int res = best_stage2(buf(), remaining(), pj, next_json); + if (res == simdjson::SUCCESS_AND_HAS_MORE) { + n_parsed_docs++; + current_buffer_loc = pj.structural_indexes[next_json]; + load_next_batch = (current_buffer_loc == last_json_buffer_loc); + } else if (res == simdjson::SUCCESS) { + n_parsed_docs++; + if (remaining() > _batch_size) { + current_buffer_loc = pj.structural_indexes[next_json - 1]; + load_next_batch = true; + res = simdjson::SUCCESS_AND_HAS_MORE; + } + } + return res; +} + +#else // SIMDJSON_THREADS_ENABLED + +// single-threaded version of json_parse +template +int JsonStream::json_parse(ParsedJson &pj) { + if (unlikely(pj.byte_capacity == 0)) { + const bool allocok = pj.allocate_capacity(_batch_size); + if (!allocok) { + pj.error_code = simdjson::MEMALLOC; + return pj.error_code; + } + } else if (unlikely(pj.byte_capacity < _batch_size)) { + pj.error_code = simdjson::CAPACITY; + return pj.error_code; + } + if (unlikely(load_next_batch)) { + advance(current_buffer_loc); + n_bytes_parsed += current_buffer_loc; + _batch_size = (std::min)(_batch_size, remaining()); + _batch_size = trimmed_length_safe_utf8((const char *)buf(), _batch_size); + int stage1_is_ok = best_stage1(buf(), _batch_size, pj, true); + if (stage1_is_ok != simdjson::SUCCESS) { + pj.error_code = stage1_is_ok; + return pj.error_code; + } + size_t last_index = find_last_json_buf_idx(buf(), _batch_size, pj); + if (last_index == 0) { + if (pj.n_structural_indexes == 0) { + pj.error_code = simdjson::EMPTY; + return pj.error_code; + } + } else { + pj.n_structural_indexes = last_index + 1; + } + load_next_batch = false; + } // load_next_batch + int res = best_stage2(buf(), remaining(), pj, next_json); + if (likely(res == simdjson::SUCCESS_AND_HAS_MORE)) { + n_parsed_docs++; + current_buffer_loc = pj.structural_indexes[next_json]; + } else if (res == simdjson::SUCCESS) { + n_parsed_docs++; + if (remaining() > _batch_size) { + current_buffer_loc = pj.structural_indexes[next_json - 1]; + next_json = 1; + load_next_batch = true; + res = simdjson::SUCCESS_AND_HAS_MORE; + } + } + return res; +} +#endif // SIMDJSON_THREADS_ENABLED + +} // end of namespace simdjson +#endif // SIMDJSON_JSONSTREAM_H diff --git a/include/simdjson/padded_string.h b/include/simdjson/padded_string.h index 0fb2dec77..e5a826cf0 100644 --- a/include/simdjson/padded_string.h +++ b/include/simdjson/padded_string.h @@ -1,6 +1,8 @@ #ifndef SIMDJSON_PADDING_STRING_H #define SIMDJSON_PADDING_STRING_H #include "simdjson/portability.h" +#include "simdjson/common_defs.h" // for SIMDJSON_PADDING + #include #include #include diff --git a/include/simdjson/parsedjson.h b/include/simdjson/parsedjson.h index 12fcbe722..238da3a9d 100644 --- a/include/simdjson/parsedjson.h +++ b/include/simdjson/parsedjson.h @@ -1,10 +1,10 @@ #ifndef SIMDJSON_PARSEDJSON_H #define SIMDJSON_PARSEDJSON_H -#include "simdjson/common_defs.h" -#include "simdjson/simdjson.h" #include #include +#include "simdjson/common_defs.h" +#include "simdjson/simdjson.h" #define JSON_VALUE_MASK 0xFFFFFFFFFFFFFF diff --git a/include/simdjson/parsedjsoniterator.h b/include/simdjson/parsedjsoniterator.h index 79b8fc7c1..f075be4da 100644 --- a/include/simdjson/parsedjsoniterator.h +++ b/include/simdjson/parsedjsoniterator.h @@ -1,12 +1,14 @@ #ifndef SIMDJSON_PARSEDJSONITERATOR_H #define SIMDJSON_PARSEDJSONITERATOR_H -#include "simdjson/parsedjson.h" -#include "simdjson/jsonformatutils.h" #include #include #include #include +#include + +#include "simdjson/parsedjson.h" +#include "simdjson/jsonformatutils.h" namespace simdjson { template class ParsedJson::BasicIterator { diff --git a/singleheader/amalgamation_demo.cpp b/singleheader/amalgamation_demo.cpp index 461e6149c..eded2e5f7 100755 --- a/singleheader/amalgamation_demo.cpp +++ b/singleheader/amalgamation_demo.cpp @@ -1,4 +1,4 @@ -/* auto-generated on Wed Jan 29 17:53:53 EST 2020. Do not edit! */ +/* auto-generated on Thu Jan 30 10:52:58 EST 2020. Do not edit! */ #include #include "simdjson.h" diff --git a/singleheader/simdjson.cpp b/singleheader/simdjson.cpp index aa567960c..212594650 100755 --- a/singleheader/simdjson.cpp +++ b/singleheader/simdjson.cpp @@ -1,4 +1,4 @@ -/* auto-generated on Wed Jan 29 17:53:53 EST 2020. Do not edit! */ +/* auto-generated on Thu Jan 30 10:52:58 EST 2020. Do not edit! */ #include "simdjson.h" /* used for http://dmalloc.com/ Dmalloc - Debug Malloc Library */ @@ -44,331 +44,6 @@ #endif // IS_X86_64 #endif // SIMDJSON_WESTMERE_INTRINSICS_H /* end file src/westmere/intrinsics.h */ -/* begin file src/jsoncharutils.h */ -#ifndef SIMDJSON_JSONCHARUTILS_H -#define SIMDJSON_JSONCHARUTILS_H - - -namespace simdjson { -// structural chars here are -// they are { 0x7b } 0x7d : 0x3a [ 0x5b ] 0x5d , 0x2c (and NULL) -// we are also interested in the four whitespace characters -// space 0x20, linefeed 0x0a, horizontal tab 0x09 and carriage return 0x0d - -// these are the chars that can follow a true/false/null or number atom -// and nothing else -const uint32_t structural_or_whitespace_or_null_negated[256] = { - 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, - - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, - - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; - -// return non-zero if not a structural or whitespace char -// zero otherwise -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, - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, - - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, - - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; - -// return non-zero if not a structural or whitespace char -// zero otherwise -really_inline uint32_t is_not_structural_or_whitespace(uint8_t c) { - return structural_or_whitespace_negated[c]; -} - -const uint32_t structural_or_whitespace_or_null[256] = { - 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - -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, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - -really_inline uint32_t is_structural_or_whitespace(uint8_t c) { - return structural_or_whitespace[c]; -} - -const uint32_t digit_to_val32[886] = { - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, - 0x6, 0x7, 0x8, 0x9, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xa, - 0xb, 0xc, 0xd, 0xe, 0xf, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xa, 0xb, 0xc, 0xd, 0xe, - 0xf, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0x0, 0x10, 0x20, 0x30, 0x40, 0x50, - 0x60, 0x70, 0x80, 0x90, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xa0, - 0xb0, 0xc0, 0xd0, 0xe0, 0xf0, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, - 0xf0, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0x0, 0x100, 0x200, 0x300, 0x400, 0x500, - 0x600, 0x700, 0x800, 0x900, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xa00, - 0xb00, 0xc00, 0xd00, 0xe00, 0xf00, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xa00, 0xb00, 0xc00, 0xd00, 0xe00, - 0xf00, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0x0, 0x1000, 0x2000, 0x3000, 0x4000, 0x5000, - 0x6000, 0x7000, 0x8000, 0x9000, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xa000, - 0xb000, 0xc000, 0xd000, 0xe000, 0xf000, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xa000, 0xb000, 0xc000, 0xd000, 0xe000, - 0xf000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF}; -// returns a value with the high 16 bits set if not valid -// otherwise returns the conversion of the 4 hex digits at src into the bottom -// 16 bits of the 32-bit return register -// -// see -// https://lemire.me/blog/2019/04/17/parsing-short-hexadecimal-strings-efficiently/ -static inline uint32_t hex_to_u32_nocheck( - const uint8_t *src) { // strictly speaking, static inline is a C-ism - uint32_t v1 = 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; -} - -// returns true if the provided byte value is a -// "continuing" UTF-8 value, that is, if it starts with -// 0b10... -static inline bool is_utf8_continuing(char c) { - // in 2 complement's notation, values start at 0b10000 (-128)... and - // go up to 0b11111 (-1)... so we want all values from -128 to -65 (which is 0b10111111) - return ((signed char)c) <= -65; -} -// returns true if the provided byte value is an ASCII character -static inline bool is_ascii(char c) { - return ((unsigned char)c) <= 127; -} - -// if the string ends with UTF-8 values, backtrack -// up to the first ASCII character. May return 0. -static inline size_t trimmed_length_safe_utf8(const char * c, size_t len) { - while ((len > 0) and (not is_ascii(c[len - 1]))) { - len--; - } - return len; -} - - - -// given a code point cp, writes to c -// the utf-8 code, outputting the length in -// bytes, if the length is zero, the code point -// is invalid -// -// This can possibly be made faster using pdep -// and clz and table lookups, but JSON documents -// have few escaped code points, and the following -// function looks cheap. -// -// Note: we assume that surrogates are treated separately -// -inline size_t codepoint_to_utf8(uint32_t cp, uint8_t *c) { - if (cp <= 0x7F) { - c[0] = cp; - return 1; // ascii - } - 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 - } 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 - c[0] = (cp >> 18) + 240; - c[1] = ((cp >> 12) & 63) + 128; - c[2] = ((cp >> 6) & 63) + 128; - c[3] = (cp & 63) + 128; - return 4; - } - // will return 0 when the code point was too large. - return 0; // bad r -} -} // namespace simdjson - -#endif -/* end file src/jsoncharutils.h */ /* begin file src/simdprune_tables.h */ #ifndef SIMDJSON_SIMDPRUNE_TABLES_H #define SIMDJSON_SIMDPRUNE_TABLES_H @@ -774,7 +449,7 @@ size_t json_minify(const uint8_t *buf, size_t len, uint8_t *out) { x4 = skinnycleanm128(x4, mask4); int pop1 = hamming(non_whitespace & 0xFFFF); int pop2 = hamming(non_whitespace & UINT64_C(0xFFFFFFFF)); - int pop3 = hamming(non_whitespace) & UINT64_C(0xFFFFFFFFFFFF)); + int pop3 = hamming(non_whitespace & UINT64_C(0xFFFFFFFFFFFF)); int pop4 = hamming(non_whitespace); _mm_storeu_si128(reinterpret_cast<__m128i *>(out), x1); _mm_storeu_si128(reinterpret_cast<__m128i *>(out + pop1), x2); @@ -855,13 +530,13 @@ size_t json_minify(const uint8_t *buf, size_t len, uint8_t *out) { x4 = skinnycleanm128(x4, mask4); int pop1 = hamming(non_whitespace & 0xFFFF); int pop2 = hamming(non_whitespace & UINT64_C(0xFFFFFFFF)); - int pop3 = hamming(non_whitespace) & UINT64_C(0xFFFFFFFFFFFF)); - int pop4 = hamming(non_whitespace); - _mm_storeu_si128(reinterpret_cast<__m128i *>(out), x1); - _mm_storeu_si128(reinterpret_cast<__m128i *>(out + pop1), x2); - _mm_storeu_si128(reinterpret_cast<__m128i *>(out + pop2), x3); - _mm_storeu_si128(reinterpret_cast<__m128i *>(out + pop3), x4); - out += pop4; + int pop3 = hamming(non_whitespace & UINT64_C(0xFFFFFFFFFFFF)); + int pop4 = hamming(non_whitespace); + _mm_storeu_si128(reinterpret_cast<__m128i *>(out), x1); + _mm_storeu_si128(reinterpret_cast<__m128i *>(out + pop1), x2); + _mm_storeu_si128(reinterpret_cast<__m128i *>(out + pop2), x3); + _mm_storeu_si128(reinterpret_cast<__m128i *>(out + pop3), x4); + out += pop4; } *out = '\0'; // NULL termination return out - initout; @@ -1150,233 +825,6 @@ ParsedJson build_parsed_json(const uint8_t *buf, size_t len, } } // namespace simdjson /* end file src/jsonparser.cpp */ -/* begin file src/jsonstream.cpp */ -#include - - -using namespace simdjson; - - - -typedef int (*stage1_functype)(const char *buf, size_t len, ParsedJson &pj, bool streaming); -typedef int (*stage2_functype)(const char *buf, size_t len, ParsedJson &pj, size_t &next_json); - -stage1_functype best_stage1; -stage2_functype best_stage2; - - -namespace { -//// TODO: generalize this set of functions. We don't want to have a copy in jsonparser.cpp -void find_the_best_supported_implementation() { - uint32_t supports = detect_supported_architectures(); - // Order from best to worst (within architecture) -#ifdef IS_X86_64 - 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; - if ((haswell_flags & supports) == haswell_flags) { - best_stage1 = simdjson::find_structural_bits; - best_stage2 = simdjson::unified_machine; - return; - } - if ((westmere_flags & supports) == westmere_flags) { - best_stage1 = simdjson::find_structural_bits; - best_stage2 = simdjson::unified_machine; - return; - } -#endif -#ifdef IS_ARM64 - if (supports & instruction_set::NEON) { - best_stage1 = simdjson::find_structural_bits; - best_stage2 = simdjson::unified_machine; - return; - } -#endif - // we throw an exception since this should not be recoverable - throw new std::runtime_error("unsupported architecture"); -} -} - -JsonStream::JsonStream(const padded_string& s, size_t batchSize) - : str(s), _batch_size(batchSize) { - find_the_best_supported_implementation(); -} - -JsonStream::~JsonStream() { -#ifdef SIMDJSON_THREADS_ENABLED - if(stage_1_thread.joinable()) { - stage_1_thread.join(); - } -#endif -} - - -#ifdef SIMDJSON_THREADS_ENABLED - -// threaded version of json_parse -// todo: simplify this code further -int JsonStream::json_parse(ParsedJson &pj) { - if (unlikely(pj.byte_capacity == 0)) { - const bool allocok = pj.allocate_capacity(_batch_size); - if (!allocok) { - pj.error_code = simdjson::MEMALLOC; - return pj.error_code; - } - } else if (unlikely(pj.byte_capacity < _batch_size)) { - pj.error_code = simdjson::CAPACITY; - return pj.error_code; - } - if(unlikely(pj_thread.byte_capacity < _batch_size)) { - const bool allocok_thread = pj_thread.allocate_capacity(_batch_size); - if (!allocok_thread) { - pj.error_code = simdjson::MEMALLOC; - return pj.error_code; - } - } - if (unlikely(load_next_batch)) { - //First time loading - if(!stage_1_thread.joinable()) { - _batch_size = std::min(_batch_size, remaining()); - _batch_size = trimmed_length_safe_utf8((const char*)buf(), _batch_size); - if(_batch_size == 0) { - pj.error_code = simdjson::UTF8_ERROR; - return pj.error_code; - } - int stage1_is_ok = best_stage1(buf(), _batch_size, pj, true); - if (stage1_is_ok != simdjson::SUCCESS) { - pj.error_code = stage1_is_ok; - return pj.error_code; - } - size_t last_index = find_last_json_buf_idx(buf(), _batch_size, pj); - if(last_index == 0) { - if(pj.n_structural_indexes == 0) { - pj.error_code = simdjson::EMPTY; - return pj.error_code; - } - } else { - pj.n_structural_indexes = last_index + 1; - } - } - // the second thread is running or done. - else { - stage_1_thread.join(); - if (stage1_is_ok_thread != simdjson::SUCCESS) { - pj.error_code = stage1_is_ok_thread; - return pj.error_code; - } - std::swap(pj.structural_indexes, pj_thread.structural_indexes); - pj.n_structural_indexes = pj_thread.n_structural_indexes; - advance(last_json_buffer_loc); - n_bytes_parsed += last_json_buffer_loc; - } - // let us decide whether we will start a new thread - if(remaining() - _batch_size > 0) { - last_json_buffer_loc = pj.structural_indexes[find_last_json_buf_idx(buf(),_batch_size,pj)]; - _batch_size = std::min(_batch_size, remaining() - last_json_buffer_loc); - if(_batch_size > 0) { - _batch_size = trimmed_length_safe_utf8((const char*)(buf() + last_json_buffer_loc), _batch_size); - if(_batch_size == 0) { - pj.error_code = simdjson::UTF8_ERROR; - return pj.error_code; - } - // let us capture read-only variables - const char * const b = buf() + last_json_buffer_loc; - const size_t bs = _batch_size; - // we call the thread on a lambda that will update this->stage1_is_ok_thread - // there is only one thread that may write to this value - stage_1_thread = std::thread( - [this, b, bs] { - this->stage1_is_ok_thread = best_stage1(b, bs, this->pj_thread, true); - }); - } - } - next_json = 0; - load_next_batch = false; - } // load_next_batch - int res = best_stage2(buf(), remaining(), pj, next_json); - if (res == simdjson::SUCCESS_AND_HAS_MORE) { - n_parsed_docs++; - current_buffer_loc = pj.structural_indexes[next_json]; - load_next_batch = (current_buffer_loc == last_json_buffer_loc); - } else if (res == simdjson::SUCCESS) { - n_parsed_docs++; - if(remaining() > _batch_size) { - current_buffer_loc = pj.structural_indexes[next_json - 1]; - load_next_batch = true; - res = simdjson::SUCCESS_AND_HAS_MORE; - } - } - return res; -} - -#else // SIMDJSON_THREADS_ENABLED - -// single-threaded version of json_parse -int JsonStream::json_parse(ParsedJson &pj) { - if (unlikely(pj.byte_capacity == 0)) { - const bool allocok = pj.allocate_capacity(_batch_size); - if (!allocok) { - pj.error_code = simdjson::MEMALLOC; - return pj.error_code; - } - } else if (unlikely(pj.byte_capacity < _batch_size)) { - pj.error_code = simdjson::CAPACITY; - return pj.error_code; - } - if (unlikely(load_next_batch)) { - advance(current_buffer_loc); - n_bytes_parsed += current_buffer_loc; - _batch_size = std::min(_batch_size, remaining()); - _batch_size = trimmed_length_safe_utf8((const char*)buf(), _batch_size); - int stage1_is_ok = best_stage1(buf(), _batch_size, pj, true); - if (stage1_is_ok != simdjson::SUCCESS) { - pj.error_code = stage1_is_ok; - return pj.error_code; - } - size_t last_index = find_last_json_buf_idx(buf(), _batch_size, pj); - if(last_index == 0) { - if(pj.n_structural_indexes == 0) { - pj.error_code = simdjson::EMPTY; - return pj.error_code; - } - } else { - pj.n_structural_indexes = last_index + 1; - } - load_next_batch = false; - } // load_next_batch - int res = best_stage2(buf(), remaining(), pj, next_json); - if (likely(res == simdjson::SUCCESS_AND_HAS_MORE)) { - n_parsed_docs++; - current_buffer_loc = pj.structural_indexes[next_json]; - } else if (res == simdjson::SUCCESS) { - n_parsed_docs++; - if(remaining() > _batch_size) { - current_buffer_loc = pj.structural_indexes[next_json - 1]; - next_json = 1; - load_next_batch = true; - res = simdjson::SUCCESS_AND_HAS_MORE; - } - } - return res; -} - -#endif // SIMDJSON_THREADS_ENABLED - - -size_t JsonStream::get_current_buffer_loc() const { - return current_buffer_loc; -} - -size_t JsonStream::get_n_parsed_docs() const { - return n_parsed_docs; -} - -size_t JsonStream::get_n_bytes_parsed() const { - return n_bytes_parsed; -} -/* end file src/jsonstream.cpp */ /* begin file src/arm64/bitmanipulation.h */ #ifndef SIMDJSON_ARM64_BITMANIPULATION_H #define SIMDJSON_ARM64_BITMANIPULATION_H diff --git a/singleheader/simdjson.h b/singleheader/simdjson.h index f62319d52..5018b2132 100755 --- a/singleheader/simdjson.h +++ b/singleheader/simdjson.h @@ -1,4 +1,4 @@ -/* auto-generated on Wed Jan 29 17:53:53 EST 2020. Do not edit! */ +/* auto-generated on Thu Jan 30 10:52:58 EST 2020. 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 @@ -477,7 +477,6 @@ const std::string &error_message(const int); #ifndef SIMDJSON_COMMON_DEFS_H #define SIMDJSON_COMMON_DEFS_H - #include // we support documents up to 4GB @@ -550,6 +549,7 @@ const std::string &error_message(const int); /* begin file include/simdjson/padded_string.h */ #ifndef SIMDJSON_PADDING_STRING_H #define SIMDJSON_PADDING_STRING_H + #include #include #include @@ -670,6 +670,7 @@ private: #include #include #include +#include #include @@ -874,6 +875,8 @@ public: #include #include #include +#include + namespace simdjson { template class ParsedJson::BasicIterator { @@ -1901,129 +1904,476 @@ inline ParsedJson build_parsed_json(const padded_string &s) { } // namespace simdjson #endif /* end file include/simdjson/jsonparser.h */ +/* begin file src/jsoncharutils.h */ +#ifndef SIMDJSON_JSONCHARUTILS_H +#define SIMDJSON_JSONCHARUTILS_H + + +namespace simdjson { +// structural chars here are +// they are { 0x7b } 0x7d : 0x3a [ 0x5b ] 0x5d , 0x2c (and NULL) +// we are also interested in the four whitespace characters +// space 0x20, linefeed 0x0a, horizontal tab 0x09 and carriage return 0x0d + +// these are the chars that can follow a true/false/null or number atom +// and nothing else +const uint32_t structural_or_whitespace_or_null_negated[256] = { + 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; + +// return non-zero if not a structural or whitespace char +// zero otherwise +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, + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; + +// return non-zero if not a structural or whitespace char +// zero otherwise +really_inline uint32_t is_not_structural_or_whitespace(uint8_t c) { + return structural_or_whitespace_negated[c]; +} + +const uint32_t structural_or_whitespace_or_null[256] = { + 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + +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, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + +really_inline uint32_t is_structural_or_whitespace(uint8_t c) { + return structural_or_whitespace[c]; +} + +const uint32_t digit_to_val32[886] = { + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, + 0x6, 0x7, 0x8, 0x9, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xa, + 0xb, 0xc, 0xd, 0xe, 0xf, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xa, 0xb, 0xc, 0xd, 0xe, + 0xf, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0x0, 0x10, 0x20, 0x30, 0x40, 0x50, + 0x60, 0x70, 0x80, 0x90, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xa0, + 0xb0, 0xc0, 0xd0, 0xe0, 0xf0, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, + 0xf0, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0x0, 0x100, 0x200, 0x300, 0x400, 0x500, + 0x600, 0x700, 0x800, 0x900, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xa00, + 0xb00, 0xc00, 0xd00, 0xe00, 0xf00, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xa00, 0xb00, 0xc00, 0xd00, 0xe00, + 0xf00, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0x0, 0x1000, 0x2000, 0x3000, 0x4000, 0x5000, + 0x6000, 0x7000, 0x8000, 0x9000, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xa000, + 0xb000, 0xc000, 0xd000, 0xe000, 0xf000, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xa000, 0xb000, 0xc000, 0xd000, 0xe000, + 0xf000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF}; +// returns a value with the high 16 bits set if not valid +// otherwise returns the conversion of the 4 hex digits at src into the bottom +// 16 bits of the 32-bit return register +// +// see +// https://lemire.me/blog/2019/04/17/parsing-short-hexadecimal-strings-efficiently/ +static inline uint32_t hex_to_u32_nocheck( + const uint8_t *src) { // strictly speaking, static inline is a C-ism + uint32_t v1 = 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; +} + +// returns true if the provided byte value is a +// "continuing" UTF-8 value, that is, if it starts with +// 0b10... +static inline bool is_utf8_continuing(char c) { + // in 2 complement's notation, values start at 0b10000 (-128)... and + // go up to 0b11111 (-1)... so we want all values from -128 to -65 (which is 0b10111111) + return ((signed char)c) <= -65; +} +// returns true if the provided byte value is an ASCII character +static inline bool is_ascii(char c) { + return ((unsigned char)c) <= 127; +} + +// if the string ends with UTF-8 values, backtrack +// up to the first ASCII character. May return 0. +static inline size_t trimmed_length_safe_utf8(const char * c, size_t len) { + while ((len > 0) and (not is_ascii(c[len - 1]))) { + len--; + } + return len; +} + + + +// given a code point cp, writes to c +// the utf-8 code, outputting the length in +// bytes, if the length is zero, the code point +// is invalid +// +// This can possibly be made faster using pdep +// and clz and table lookups, but JSON documents +// have few escaped code points, and the following +// function looks cheap. +// +// Note: we assume that surrogates are treated separately +// +inline size_t codepoint_to_utf8(uint32_t cp, uint8_t *c) { + if (cp <= 0x7F) { + c[0] = cp; + return 1; // ascii + } + 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 + } 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 + c[0] = (cp >> 18) + 240; + c[1] = ((cp >> 12) & 63) + 128; + c[2] = ((cp >> 6) & 63) + 128; + c[3] = (cp & 63) + 128; + return 4; + } + // will return 0 when the code point was too large. + return 0; // bad r +} +} // namespace simdjson + +#endif +/* end file src/jsoncharutils.h */ /* begin file include/simdjson/jsonstream.h */ #ifndef SIMDJSON_JSONSTREAM_H #define SIMDJSON_JSONSTREAM_H - -#include +#include #include + namespace simdjson { - /************************************************************************************* - * The main motivation for this piece of software is to achieve maximum speed and offer - * good quality of life while parsing files containing multiple JSON documents. - * - * Since we want to offer flexibility and not restrict ourselves to a specific file - * format, we support any file that contains any valid JSON documents separated by one - * or more character that is considered a whitespace by the JSON spec. - * Namely: space, nothing, linefeed, carriage return, horizontal tab. - * Anything that is not whitespace will be parsed as a JSON document and could lead - * to failure. - * - * To offer maximum parsing speed, our implementation processes the data inside the - * buffer by batches and their size is defined by the parameter "batch_size". - * By loading data in batches, we can optimize the time spent allocating data in the - * ParsedJson and can also open the possibility of multi-threading. - * The batch_size must be at least as large as the biggest document in the file, but - * not too large in order to submerge the chached memory. We found that 1MB is - * somewhat a sweet spot for now. Eventually, this batch_size could be fully - * automated and be optimal at all times. - ************************************************************************************/ - class JsonStream { - public: - /* Create a JsonStream object that can be used to parse sequentially the valid - * JSON documents found in the buffer "buf". - * - * The batch_size must be at least as large as the biggest document in the file, but - * not too large to submerge the cached memory. We found that 1MB is - * somewhat a sweet spot for now. - * - * The user is expected to call the following json_parse method to parse the next - * valid JSON document found in the buffer. This method can and is expected to be - * called in a loop. - * - * Various methods are offered to keep track of the status, like get_current_buffer_loc, - * get_n_parsed_docs, get_n_bytes_parsed, etc. - * - * */ - JsonStream(const padded_string &s, size_t batch_size = 1000000); +/************************************************************************************* + * The main motivation for this piece of software is to achieve maximum speed + *and offer + * good quality of life while parsing files containing multiple JSON documents. + * + * Since we want to offer flexibility and not restrict ourselves to a specific + *file + * format, we support any file that contains any valid JSON documents separated + *by one + * or more character that is considered a whitespace by the JSON spec. + * Namely: space, nothing, linefeed, carriage return, horizontal tab. + * Anything that is not whitespace will be parsed as a JSON document and could + *lead + * to failure. + * + * To offer maximum parsing speed, our implementation processes the data inside + *the + * buffer by batches and their size is defined by the parameter "batch_size". + * By loading data in batches, we can optimize the time spent allocating data in + *the + * ParsedJson and can also open the possibility of multi-threading. + * The batch_size must be at least as large as the biggest document in the file, + *but + * not too large in order to submerge the chached memory. We found that 1MB is + * somewhat a sweet spot for now. Eventually, this batch_size could be fully + * automated and be optimal at all times. + ************************************************************************************/ +/** +* The template parameter (string_container) must +* support the data() and size() methods, returning a pointer +* to a char* and to the number of bytes respectively. +* The simdjson parser may read up to SIMDJSON_PADDING bytes beyond the end +* of the string, so if you do not use a padded_string container, +* you have the responsability to overallocated. If you fail to +* do so, your software may crash if you cross a page boundary, +* and you should expect memory checkers to object. +* Most users should use a simdjson::padded_string. +*/ +template class JsonStream { +public: + /* Create a JsonStream object that can be used to parse sequentially the valid + * JSON documents found in the buffer "buf". + * + * The batch_size must be at least as large as the biggest document in the + * file, but + * not too large to submerge the cached memory. We found that 1MB is + * somewhat a sweet spot for now. + * + * The user is expected to call the following json_parse method to parse the + * next + * valid JSON document found in the buffer. This method can and is expected + * to be + * called in a loop. + * + * Various methods are offered to keep track of the status, like + * get_current_buffer_loc, + * get_n_parsed_docs, get_n_bytes_parsed, etc. + * + * */ + JsonStream(const string_container &s, size_t batch_size = 1000000); - ~JsonStream(); + ~JsonStream(); - /* Parse the next document found in the buffer previously given to JsonStream. + /* Parse the next document found in the buffer previously given to JsonStream. - * The content should be a valid JSON document encoded as UTF-8. If there is a - * UTF-8 BOM, the caller is responsible for omitting it, UTF-8 BOM are - * discouraged. - * - * You do NOT need to pre-allocate ParsedJson. This function takes care of - * pre-allocating a capacity defined by the batch_size defined when creating the - * JsonStream object. - * - * The function returns simdjson::SUCCESS_AND_HAS_MORE (an integer = 1) in case - * of success and indicates that the buffer still contains more data to be parsed, - * meaning this function can be called again to return the next JSON document - * after this one. - * - * The function returns simdjson::SUCCESS (as integer = 0) in case of success - * and indicates that the buffer has successfully been parsed to the end. - * Every document it contained has been parsed without error. - * - * The function returns an error code from simdjson/simdjson.h in case of failure - * such as simdjson::CAPACITY, simdjson::MEMALLOC, simdjson::DEPTH_ERROR and so forth; - * the simdjson::error_message function converts these error codes into a - * string). - * - * You can also check validity by calling pj.is_valid(). The same ParsedJson can - * and should be reused for the other documents in the buffer. */ - int json_parse(ParsedJson &pj); + * The content should be a valid JSON document encoded as UTF-8. If there is a + * UTF-8 BOM, the caller is responsible for omitting it, UTF-8 BOM are + * discouraged. + * + * You do NOT need to pre-allocate ParsedJson. This function takes care of + * pre-allocating a capacity defined by the batch_size defined when creating + the + * JsonStream object. + * + * The function returns simdjson::SUCCESS_AND_HAS_MORE (an integer = 1) in + case + * of success and indicates that the buffer still contains more data to be + parsed, + * meaning this function can be called again to return the next JSON document + * after this one. + * + * The function returns simdjson::SUCCESS (as integer = 0) in case of success + * and indicates that the buffer has successfully been parsed to the end. + * Every document it contained has been parsed without error. + * + * The function returns an error code from simdjson/simdjson.h in case of + failure + * such as simdjson::CAPACITY, simdjson::MEMALLOC, simdjson::DEPTH_ERROR and + so forth; + * the simdjson::error_message function converts these error codes into a + * string). + * + * You can also check validity by calling pj.is_valid(). The same ParsedJson + can + * and should be reused for the other documents in the buffer. */ + int json_parse(ParsedJson &pj); - /* Returns the location (index) of where the next document should be in the buffer. - * Can be used for debugging, it tells the user the position of the end of the last - * valid JSON document parsed*/ - size_t get_current_buffer_loc() const; + /* Returns the location (index) of where the next document should be in the + * buffer. + * Can be used for debugging, it tells the user the position of the end of the + * last + * valid JSON document parsed*/ + inline size_t get_current_buffer_loc() const { return current_buffer_loc; } - /* Returns the total amount of complete documents parsed by the JsonStream, - * in the current buffer, at the given time.*/ - size_t get_n_parsed_docs() const; + /* Returns the total amount of complete documents parsed by the JsonStream, + * in the current buffer, at the given time.*/ + inline size_t get_n_parsed_docs() const { return n_parsed_docs; } - /* Returns the total amount of data (in bytes) parsed by the JsonStream, - * in the current buffer, at the given time.*/ - size_t get_n_bytes_parsed() const; + /* Returns the total amount of data (in bytes) parsed by the JsonStream, + * in the current buffer, at the given time.*/ + inline size_t get_n_bytes_parsed() const { return n_bytes_parsed; } - private: +private: + inline const char *buf() const { return str.data() + str_start; } - inline const char * buf() const { - return str.data() + str_start; - } - inline void advance(size_t offset) { - str_start += offset; - } + inline void advance(size_t offset) { str_start += offset; } - inline size_t remaining() const { - return str.size() - str_start; - } - const simdjson::padded_string & str; - size_t _batch_size; - size_t str_start{0}; - size_t next_json{0}; - bool load_next_batch{true}; - size_t current_buffer_loc{0}; + inline size_t remaining() const { return str.size() - str_start; } + + const string_container &str; + size_t _batch_size; + size_t str_start{0}; + size_t next_json{0}; + bool load_next_batch{true}; + size_t current_buffer_loc{0}; #ifdef SIMDJSON_THREADS_ENABLED - size_t last_json_buffer_loc{0}; + size_t last_json_buffer_loc{0}; #endif - size_t n_parsed_docs{0}; - size_t n_bytes_parsed{0}; + size_t n_parsed_docs{0}; + size_t n_bytes_parsed{0}; #ifdef SIMDJSON_THREADS_ENABLED - int stage1_is_ok_thread{0}; - std::thread stage_1_thread; - simdjson::ParsedJson pj_thread; + int stage1_is_ok_thread{0}; + std::thread stage_1_thread; + simdjson::ParsedJson pj_thread; #endif - - }; - - +}; // end of class JsonStream /* This algorithm is used to quickly identify the buffer position of * the last JSON document inside the current batch. @@ -2043,56 +2393,275 @@ namespace simdjson { * the pair, and means that we cannot identify the last document. To fix this * issue, we keep a count of the open and closed curly/square braces we found * while searching for the pair. When we find a pair AND the count of open and - * closed curly/square braces is the same, we know that we just passed a complete + * closed curly/square braces is the same, we know that we just passed a + * complete * document, therefore the last json buffer location is the end of the batch * */ -inline size_t find_last_json_buf_idx(const char * buf, size_t size, const ParsedJson &pj) { - // this function can be generally useful - if(pj.n_structural_indexes == 0) return 0; - auto last_i = pj.n_structural_indexes - 1; - if (pj.structural_indexes[last_i] == size) { - if(last_i == 0) return 0; - last_i = pj.n_structural_indexes - 2; - } - auto arr_cnt = 0; - auto obj_cnt = 0; - for (auto i = last_i; i > 0; i--) { - auto idxb = pj.structural_indexes[i]; - switch (buf[idxb]) { - case ':': - case ',': - continue; - case '}': - obj_cnt--; - continue; - case ']': - arr_cnt--; - continue; - case '{': - obj_cnt++; - break; - case '[': - arr_cnt++; - break; - } - auto idxa = pj.structural_indexes[i - 1]; - switch (buf[idxa]) { - case '{': - case '[': - case ':': - case ',': - continue; - } - if (!arr_cnt && !obj_cnt) { - return last_i+1; - } - return i; - } +inline size_t find_last_json_buf_idx(const char *buf, size_t size, + const ParsedJson &pj) { + // this function can be generally useful + if (pj.n_structural_indexes == 0) return 0; + auto last_i = pj.n_structural_indexes - 1; + if (pj.structural_indexes[last_i] == size) { + if (last_i == 0) + return 0; + last_i = pj.n_structural_indexes - 2; + } + auto arr_cnt = 0; + auto obj_cnt = 0; + for (auto i = last_i; i > 0; i--) { + auto idxb = pj.structural_indexes[i]; + switch (buf[idxb]) { + case ':': + case ',': + continue; + case '}': + obj_cnt--; + continue; + case ']': + arr_cnt--; + continue; + case '{': + obj_cnt++; + break; + case '[': + arr_cnt++; + break; + } + auto idxa = pj.structural_indexes[i - 1]; + switch (buf[idxa]) { + case '{': + case '[': + case ':': + case ',': + continue; + } + if (!arr_cnt && !obj_cnt) { + return last_i + 1; + } + return i; + } + return 0; } +// Everything in the following anonymous namespace should go. +// It is a hack. +namespace { + +typedef int (*stage1_functype)(const char *buf, size_t len, + simdjson::ParsedJson &pj, bool streaming); +typedef int (*stage2_functype)(const char *buf, size_t len, + simdjson::ParsedJson &pj, size_t &next_json); + +stage1_functype best_stage1; +stage2_functype best_stage2; + +//// TODO: generalize this set of functions. We don't want to have a copy in +/// jsonparser.cpp +void find_the_best_supported_implementation() { + uint32_t supports = simdjson::detect_supported_architectures(); +// Order from best to worst (within architecture) +#ifdef IS_X86_64 + constexpr uint32_t haswell_flags = + simdjson::instruction_set::AVX2 | simdjson::instruction_set::PCLMULQDQ | + simdjson::instruction_set::BMI1 | simdjson::instruction_set::BMI2; + constexpr uint32_t westmere_flags = + simdjson::instruction_set::SSE42 | simdjson::instruction_set::PCLMULQDQ; + if ((haswell_flags & supports) == haswell_flags) { + best_stage1 = + simdjson::find_structural_bits; + best_stage2 = simdjson::unified_machine; + return; + } + if ((westmere_flags & supports) == westmere_flags) { + best_stage1 = + simdjson::find_structural_bits; + best_stage2 = simdjson::unified_machine; + return; + } +#endif +#ifdef IS_ARM64 + if (supports & instruction_set::NEON) { + best_stage1 = simdjson::find_structural_bits; + best_stage2 = simdjson::unified_machine; + return; + } +#endif + // we throw an exception since this should not be recoverable + throw new std::runtime_error("unsupported architecture"); +} +} // anonymous namespace + +template +JsonStream::JsonStream(const string_container &s, + size_t batchSize) + : str(s), _batch_size(batchSize) { + find_the_best_supported_implementation(); } +template JsonStream::~JsonStream() { +#ifdef SIMDJSON_THREADS_ENABLED + if (stage_1_thread.joinable()) { + stage_1_thread.join(); + } +#endif +} -#endif //SIMDJSON_JSONSTREAM_H +#ifdef SIMDJSON_THREADS_ENABLED + +// threaded version of json_parse +// todo: simplify this code further +template +int JsonStream::json_parse(ParsedJson &pj) { + if (unlikely(pj.byte_capacity == 0)) { + const bool allocok = pj.allocate_capacity(_batch_size); + if (!allocok) { + pj.error_code = simdjson::MEMALLOC; + return pj.error_code; + } + } else if (unlikely(pj.byte_capacity < _batch_size)) { + pj.error_code = simdjson::CAPACITY; + return pj.error_code; + } + if (unlikely(pj_thread.byte_capacity < _batch_size)) { + const bool allocok_thread = pj_thread.allocate_capacity(_batch_size); + if (!allocok_thread) { + pj.error_code = simdjson::MEMALLOC; + return pj.error_code; + } + } + if (unlikely(load_next_batch)) { + // First time loading + if (!stage_1_thread.joinable()) { + _batch_size = std::min(_batch_size, remaining()); + _batch_size = trimmed_length_safe_utf8((const char *)buf(), _batch_size); + if (_batch_size == 0) { + pj.error_code = simdjson::UTF8_ERROR; + return pj.error_code; + } + int stage1_is_ok = best_stage1(buf(), _batch_size, pj, true); + if (stage1_is_ok != simdjson::SUCCESS) { + pj.error_code = stage1_is_ok; + return pj.error_code; + } + size_t last_index = find_last_json_buf_idx(buf(), _batch_size, pj); + if (last_index == 0) { + if (pj.n_structural_indexes == 0) { + pj.error_code = simdjson::EMPTY; + return pj.error_code; + } + } else { + pj.n_structural_indexes = last_index + 1; + } + } + // the second thread is running or done. + else { + stage_1_thread.join(); + if (stage1_is_ok_thread != simdjson::SUCCESS) { + pj.error_code = stage1_is_ok_thread; + return pj.error_code; + } + std::swap(pj.structural_indexes, pj_thread.structural_indexes); + pj.n_structural_indexes = pj_thread.n_structural_indexes; + advance(last_json_buffer_loc); + n_bytes_parsed += last_json_buffer_loc; + } + // let us decide whether we will start a new thread + if (remaining() - _batch_size > 0) { + last_json_buffer_loc = + pj.structural_indexes[find_last_json_buf_idx(buf(), _batch_size, pj)]; + _batch_size = std::min(_batch_size, remaining() - last_json_buffer_loc); + if (_batch_size > 0) { + _batch_size = trimmed_length_safe_utf8( + (const char *)(buf() + last_json_buffer_loc), _batch_size); + if (_batch_size == 0) { + pj.error_code = simdjson::UTF8_ERROR; + return pj.error_code; + } + // let us capture read-only variables + const char *const b = buf() + last_json_buffer_loc; + const size_t bs = _batch_size; + // we call the thread on a lambda that will update + // this->stage1_is_ok_thread + // there is only one thread that may write to this value + stage_1_thread = std::thread([this, b, bs] { + this->stage1_is_ok_thread = best_stage1(b, bs, this->pj_thread, true); + }); + } + } + next_json = 0; + load_next_batch = false; + } // load_next_batch + int res = best_stage2(buf(), remaining(), pj, next_json); + if (res == simdjson::SUCCESS_AND_HAS_MORE) { + n_parsed_docs++; + current_buffer_loc = pj.structural_indexes[next_json]; + load_next_batch = (current_buffer_loc == last_json_buffer_loc); + } else if (res == simdjson::SUCCESS) { + n_parsed_docs++; + if (remaining() > _batch_size) { + current_buffer_loc = pj.structural_indexes[next_json - 1]; + load_next_batch = true; + res = simdjson::SUCCESS_AND_HAS_MORE; + } + } + return res; +} + +#else // SIMDJSON_THREADS_ENABLED + +// single-threaded version of json_parse +template +int JsonStream::json_parse(ParsedJson &pj) { + if (unlikely(pj.byte_capacity == 0)) { + const bool allocok = pj.allocate_capacity(_batch_size); + if (!allocok) { + pj.error_code = simdjson::MEMALLOC; + return pj.error_code; + } + } else if (unlikely(pj.byte_capacity < _batch_size)) { + pj.error_code = simdjson::CAPACITY; + return pj.error_code; + } + if (unlikely(load_next_batch)) { + advance(current_buffer_loc); + n_bytes_parsed += current_buffer_loc; + _batch_size = std::min(_batch_size, remaining()); + _batch_size = trimmed_length_safe_utf8((const char *)buf(), _batch_size); + int stage1_is_ok = best_stage1(buf(), _batch_size, pj, true); + if (stage1_is_ok != simdjson::SUCCESS) { + pj.error_code = stage1_is_ok; + return pj.error_code; + } + size_t last_index = find_last_json_buf_idx(buf(), _batch_size, pj); + if (last_index == 0) { + if (pj.n_structural_indexes == 0) { + pj.error_code = simdjson::EMPTY; + return pj.error_code; + } + } else { + pj.n_structural_indexes = last_index + 1; + } + load_next_batch = false; + } // load_next_batch + int res = best_stage2(buf(), remaining(), pj, next_json); + if (likely(res == simdjson::SUCCESS_AND_HAS_MORE)) { + n_parsed_docs++; + current_buffer_loc = pj.structural_indexes[next_json]; + } else if (res == simdjson::SUCCESS) { + n_parsed_docs++; + if (remaining() > _batch_size) { + current_buffer_loc = pj.structural_indexes[next_json - 1]; + next_json = 1; + load_next_batch = true; + res = simdjson::SUCCESS_AND_HAS_MORE; + } + } + return res; +} +#endif // SIMDJSON_THREADS_ENABLED + +} // end of namespace simdjson +#endif // SIMDJSON_JSONSTREAM_H /* end file include/simdjson/jsonstream.h */ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 831f53d04..5bbfe4d54 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -25,7 +25,6 @@ set(SIMDJSON_SRC jsonioutil.cpp jsonminifier.cpp jsonparser.cpp - jsonstream.cpp stage1_find_marks.cpp stage2_build_tape.cpp parsedjson.cpp diff --git a/src/jsonminifier.cpp b/src/jsonminifier.cpp index 52f4995a6..47802d98f 100644 --- a/src/jsonminifier.cpp +++ b/src/jsonminifier.cpp @@ -194,7 +194,7 @@ size_t json_minify(const uint8_t *buf, size_t len, uint8_t *out) { x4 = skinnycleanm128(x4, mask4); int pop1 = hamming(non_whitespace & 0xFFFF); int pop2 = hamming(non_whitespace & UINT64_C(0xFFFFFFFF)); - int pop3 = hamming(non_whitespace) & UINT64_C(0xFFFFFFFFFFFF)); + int pop3 = hamming(non_whitespace & UINT64_C(0xFFFFFFFFFFFF)); int pop4 = hamming(non_whitespace); _mm_storeu_si128(reinterpret_cast<__m128i *>(out), x1); _mm_storeu_si128(reinterpret_cast<__m128i *>(out + pop1), x2); @@ -275,13 +275,13 @@ size_t json_minify(const uint8_t *buf, size_t len, uint8_t *out) { x4 = skinnycleanm128(x4, mask4); int pop1 = hamming(non_whitespace & 0xFFFF); int pop2 = hamming(non_whitespace & UINT64_C(0xFFFFFFFF)); - int pop3 = hamming(non_whitespace) & UINT64_C(0xFFFFFFFFFFFF)); - int pop4 = hamming(non_whitespace); - _mm_storeu_si128(reinterpret_cast<__m128i *>(out), x1); - _mm_storeu_si128(reinterpret_cast<__m128i *>(out + pop1), x2); - _mm_storeu_si128(reinterpret_cast<__m128i *>(out + pop2), x3); - _mm_storeu_si128(reinterpret_cast<__m128i *>(out + pop3), x4); - out += pop4; + int pop3 = hamming(non_whitespace & UINT64_C(0xFFFFFFFFFFFF)); + int pop4 = hamming(non_whitespace); + _mm_storeu_si128(reinterpret_cast<__m128i *>(out), x1); + _mm_storeu_si128(reinterpret_cast<__m128i *>(out + pop1), x2); + _mm_storeu_si128(reinterpret_cast<__m128i *>(out + pop2), x3); + _mm_storeu_si128(reinterpret_cast<__m128i *>(out + pop3), x4); + out += pop4; } *out = '\0'; // NULL termination return out - initout; diff --git a/src/jsonstream.cpp b/src/jsonstream.cpp deleted file mode 100755 index 2b5759f9a..000000000 --- a/src/jsonstream.cpp +++ /dev/null @@ -1,226 +0,0 @@ -#include -#include "simdjson/jsonstream.h" -#include "simdjson/isadetection.h" -#include "jsoncharutils.h" - - -using namespace simdjson; - -typedef int (*stage1_functype)(const char *buf, size_t len, ParsedJson &pj, bool streaming); -typedef int (*stage2_functype)(const char *buf, size_t len, ParsedJson &pj, size_t &next_json); - -stage1_functype best_stage1; -stage2_functype best_stage2; - - -namespace { -//// TODO: generalize this set of functions. We don't want to have a copy in jsonparser.cpp -void find_the_best_supported_implementation() { - uint32_t supports = detect_supported_architectures(); - // Order from best to worst (within architecture) -#ifdef IS_X86_64 - 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; - if ((haswell_flags & supports) == haswell_flags) { - best_stage1 = simdjson::find_structural_bits; - best_stage2 = simdjson::unified_machine; - return; - } - if ((westmere_flags & supports) == westmere_flags) { - best_stage1 = simdjson::find_structural_bits; - best_stage2 = simdjson::unified_machine; - return; - } -#endif -#ifdef IS_ARM64 - if (supports & instruction_set::NEON) { - best_stage1 = simdjson::find_structural_bits; - best_stage2 = simdjson::unified_machine; - return; - } -#endif - // we throw an exception since this should not be recoverable - throw new std::runtime_error("unsupported architecture"); -} -} - -JsonStream::JsonStream(const padded_string& s, size_t batchSize) - : str(s), _batch_size(batchSize) { - find_the_best_supported_implementation(); -} - -JsonStream::~JsonStream() { -#ifdef SIMDJSON_THREADS_ENABLED - if(stage_1_thread.joinable()) { - stage_1_thread.join(); - } -#endif -} - - -#ifdef SIMDJSON_THREADS_ENABLED - -// threaded version of json_parse -// todo: simplify this code further -int JsonStream::json_parse(ParsedJson &pj) { - if (unlikely(pj.byte_capacity == 0)) { - const bool allocok = pj.allocate_capacity(_batch_size); - if (!allocok) { - pj.error_code = simdjson::MEMALLOC; - return pj.error_code; - } - } else if (unlikely(pj.byte_capacity < _batch_size)) { - pj.error_code = simdjson::CAPACITY; - return pj.error_code; - } - if(unlikely(pj_thread.byte_capacity < _batch_size)) { - const bool allocok_thread = pj_thread.allocate_capacity(_batch_size); - if (!allocok_thread) { - pj.error_code = simdjson::MEMALLOC; - return pj.error_code; - } - } - if (unlikely(load_next_batch)) { - //First time loading - if(!stage_1_thread.joinable()) { - _batch_size = std::min(_batch_size, remaining()); - _batch_size = trimmed_length_safe_utf8((const char*)buf(), _batch_size); - if(_batch_size == 0) { - pj.error_code = simdjson::UTF8_ERROR; - return pj.error_code; - } - int stage1_is_ok = best_stage1(buf(), _batch_size, pj, true); - if (stage1_is_ok != simdjson::SUCCESS) { - pj.error_code = stage1_is_ok; - return pj.error_code; - } - size_t last_index = find_last_json_buf_idx(buf(), _batch_size, pj); - if(last_index == 0) { - if(pj.n_structural_indexes == 0) { - pj.error_code = simdjson::EMPTY; - return pj.error_code; - } - } else { - pj.n_structural_indexes = last_index + 1; - } - } - // the second thread is running or done. - else { - stage_1_thread.join(); - if (stage1_is_ok_thread != simdjson::SUCCESS) { - pj.error_code = stage1_is_ok_thread; - return pj.error_code; - } - std::swap(pj.structural_indexes, pj_thread.structural_indexes); - pj.n_structural_indexes = pj_thread.n_structural_indexes; - advance(last_json_buffer_loc); - n_bytes_parsed += last_json_buffer_loc; - } - // let us decide whether we will start a new thread - if(remaining() - _batch_size > 0) { - last_json_buffer_loc = pj.structural_indexes[find_last_json_buf_idx(buf(),_batch_size,pj)]; - _batch_size = std::min(_batch_size, remaining() - last_json_buffer_loc); - if(_batch_size > 0) { - _batch_size = trimmed_length_safe_utf8((const char*)(buf() + last_json_buffer_loc), _batch_size); - if(_batch_size == 0) { - pj.error_code = simdjson::UTF8_ERROR; - return pj.error_code; - } - // let us capture read-only variables - const char * const b = buf() + last_json_buffer_loc; - const size_t bs = _batch_size; - // we call the thread on a lambda that will update this->stage1_is_ok_thread - // there is only one thread that may write to this value - stage_1_thread = std::thread( - [this, b, bs] { - this->stage1_is_ok_thread = best_stage1(b, bs, this->pj_thread, true); - }); - } - } - next_json = 0; - load_next_batch = false; - } // load_next_batch - int res = best_stage2(buf(), remaining(), pj, next_json); - if (res == simdjson::SUCCESS_AND_HAS_MORE) { - n_parsed_docs++; - current_buffer_loc = pj.structural_indexes[next_json]; - load_next_batch = (current_buffer_loc == last_json_buffer_loc); - } else if (res == simdjson::SUCCESS) { - n_parsed_docs++; - if(remaining() > _batch_size) { - current_buffer_loc = pj.structural_indexes[next_json - 1]; - load_next_batch = true; - res = simdjson::SUCCESS_AND_HAS_MORE; - } - } - return res; -} - -#else // SIMDJSON_THREADS_ENABLED - -// single-threaded version of json_parse -int JsonStream::json_parse(ParsedJson &pj) { - if (unlikely(pj.byte_capacity == 0)) { - const bool allocok = pj.allocate_capacity(_batch_size); - if (!allocok) { - pj.error_code = simdjson::MEMALLOC; - return pj.error_code; - } - } else if (unlikely(pj.byte_capacity < _batch_size)) { - pj.error_code = simdjson::CAPACITY; - return pj.error_code; - } - if (unlikely(load_next_batch)) { - advance(current_buffer_loc); - n_bytes_parsed += current_buffer_loc; - _batch_size = std::min(_batch_size, remaining()); - _batch_size = trimmed_length_safe_utf8((const char*)buf(), _batch_size); - int stage1_is_ok = best_stage1(buf(), _batch_size, pj, true); - if (stage1_is_ok != simdjson::SUCCESS) { - pj.error_code = stage1_is_ok; - return pj.error_code; - } - size_t last_index = find_last_json_buf_idx(buf(), _batch_size, pj); - if(last_index == 0) { - if(pj.n_structural_indexes == 0) { - pj.error_code = simdjson::EMPTY; - return pj.error_code; - } - } else { - pj.n_structural_indexes = last_index + 1; - } - load_next_batch = false; - } // load_next_batch - int res = best_stage2(buf(), remaining(), pj, next_json); - if (likely(res == simdjson::SUCCESS_AND_HAS_MORE)) { - n_parsed_docs++; - current_buffer_loc = pj.structural_indexes[next_json]; - } else if (res == simdjson::SUCCESS) { - n_parsed_docs++; - if(remaining() > _batch_size) { - current_buffer_loc = pj.structural_indexes[next_json - 1]; - next_json = 1; - load_next_batch = true; - res = simdjson::SUCCESS_AND_HAS_MORE; - } - } - return res; -} - -#endif // SIMDJSON_THREADS_ENABLED - - -size_t JsonStream::get_current_buffer_loc() const { - return current_buffer_loc; -} - -size_t JsonStream::get_n_parsed_docs() const { - return n_parsed_docs; -} - -size_t JsonStream::get_n_bytes_parsed() const { - return n_bytes_parsed; -} diff --git a/tests/basictests.cpp b/tests/basictests.cpp index 4bb7f53de..d2db732a7 100644 --- a/tests/basictests.cpp +++ b/tests/basictests.cpp @@ -248,7 +248,7 @@ static bool parse_json_message_issue467(char const* message, std::size_t len, si } int res; simdjson::padded_string str(message,len); - simdjson::JsonStream js(str, pj.byte_capacity); + simdjson::JsonStream js(str, pj.byte_capacity); do { res = js.json_parse(pj); count++; @@ -405,7 +405,7 @@ bool stream_utf8_test() { printf("."); fflush(NULL); simdjson::padded_string str(data); - simdjson::JsonStream js{str, i}; + simdjson::JsonStream js{str, i}; int parse_res = simdjson::SUCCESS_AND_HAS_MORE; size_t count = 0; simdjson::ParsedJson pj; @@ -467,7 +467,7 @@ bool stream_test() { printf("."); fflush(NULL); simdjson::padded_string str(data); - simdjson::JsonStream js{str, i}; + simdjson::JsonStream js{str, i}; int parse_res = simdjson::SUCCESS_AND_HAS_MORE; size_t count = 0; simdjson::ParsedJson pj;