Commit 73953d5c0df for nodejs
commit 73953d5c0dfeb67de1d98831e617257b924581ac
Author: Node.js GitHub Bot <github-bot@iojs.org>
Date: Mon Nov 2 19:53:13 2026 -0500
deps: update simdjson to 5.1.0
PR-URL: https://github.com/nodejs/node/pull/67172
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Richard Lau <richard.lau@ibm.com>
Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
diff --git a/deps/simdjson/simdjson.cpp b/deps/simdjson/simdjson.cpp
index e8a2a24a04b..08b0de35294 100644
--- a/deps/simdjson/simdjson.cpp
+++ b/deps/simdjson/simdjson.cpp
@@ -1,4 +1,4 @@
-/* auto-generated on 2026-10-14 15:49:28 -0400. version 5.0.5 Do not edit! */
+/* auto-generated on 2026-10-29 18:49:29 -0400. version 5.1.0 Do not edit! */
/* including simdjson.cpp: */
/* begin file simdjson.cpp */
#define SIMDJSON_SRC_SIMDJSON_CPP
@@ -12378,6 +12378,23 @@ public:
*/
simdjson_warn_unused virtual error_code stage2_next(dom::document &doc) noexcept = 0;
+ /** @private A stage 2 entry point for parser::parse_many: see stage2_next_function. */
+ typedef error_code (*stage2_next_function_t)(dom_parser_implementation &impl, dom::document &doc);
+
+ /**
+ * @private For internal implementation use
+ *
+ * Get a function that does what stage2_next() does. dom::document_stream picks
+ * it once per batch and calls it for each document of the batch, so that the
+ * choice between the bounded and the unchecked walk costs nothing per document.
+ *
+ * Overridden by each implementation.
+ *
+ * @param bounded Whether the documents must be bounded to the structural
+ * indexes of the batch (see _bounded_batch).
+ */
+ virtual stage2_next_function_t stage2_next_function(bool bounded) const noexcept = 0;
+
/**
* Unescape a valid UTF-8 string from src to dst, stopping at a final unescaped quote. There
* must be an unescaped quote terminating the string. It returns the final output
@@ -12502,6 +12519,14 @@ public:
*/
bool _unpadded{false};
+ /**
+ * Whether stage 2 must bound the documents of the batch indexed by the last
+ * stage 1 run to its structural indexes (dom::document_stream sets it, see
+ * document_stream::run_stage1, and passes it to stage2_next_function). Other
+ * batches keep the unchecked walk.
+ */
+ bool _bounded_batch{false};
+
protected:
// Declaring these so that subclasses can use them to implement their constructors.
@@ -14960,31 +14985,6 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin
};
}
-struct escaping {
- static constexpr uint32_t BYTES_PROCESSED = 16;
- simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
-
- simdjson_inline bool has_escape() { return escape_bits != 0; }
- simdjson_inline int escape_index() { return trailing_zeroes(escape_bits) / 4; }
-
- uint64_t escape_bits;
-}; // struct escaping
-
-
-
-simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
- static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes");
- simd8<uint8_t> v(src);
- v.store(dst);
- simd8<bool> is_quote = (v == '"');
- simd8<bool> is_backslash = (v == '\\');
- simd8<bool> is_control = (v < 32);
- return {
- (is_backslash | is_quote | is_control).to_bitmask64()
- };
-}
-
-
} // unnamed namespace
} // namespace arm64
@@ -15378,6 +15378,7 @@ public:
simdjson_warn_unused error_code stage1(const uint8_t *buf, size_t len, stage1_mode partial) noexcept final;
simdjson_warn_unused error_code stage2(dom::document &doc) noexcept final;
simdjson_warn_unused error_code stage2_next(dom::document &doc) noexcept final;
+ stage2_next_function_t stage2_next_function(bool bounded) const noexcept final;
simdjson_warn_unused uint8_t *parse_string(const uint8_t *src, uint8_t *dst, bool allow_replacement) const noexcept final;
simdjson_warn_unused uint8_t *parse_wobbly_string(const uint8_t *src, uint8_t *dst) const noexcept final;
inline simdjson_warn_unused error_code set_capacity(size_t capacity) noexcept final;
@@ -16139,21 +16140,6 @@ static bool parse_float_fallback(const uint8_t *ptr, double *outDouble) {
return !(*outDouble > (std::numeric_limits<double>::max)() || *outDouble < std::numeric_limits<double>::lowest());
}
-static bool parse_float_fallback(const uint8_t *ptr, const uint8_t *end_ptr, double *outDouble) {
- *outDouble = simdjson::internal::from_chars(reinterpret_cast<const char *>(ptr), reinterpret_cast<const char *>(end_ptr));
- // We do not accept infinite values.
-
- // Detecting finite values in a portable manner is ridiculously hard, ideally
- // we would want to do:
- // return !std::isfinite(*outDouble);
- // but that mysteriously fails under legacy/old libc++ libraries, see
- // https://github.com/simdjson/simdjson/issues/1286
- //
- // Therefore, fall back to this solution (the extra parens are there
- // to handle that max may be a macro on windows).
- return !(*outDouble > (std::numeric_limits<double>::max)() || *outDouble < std::numeric_limits<double>::lowest());
-}
-
// Same as parse_float_fallback, but for binary32 (float) values. Going through
// the binary64 fallback and then rounding to binary32 would be subject to
// double rounding, so we run the fallback algorithm directly on binary32.
@@ -16699,57 +16685,6 @@ simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned(const u
return i;
}
-
-
-// Parse any number from 0 to 18,446,744,073,709,551,615
-// Never read at src_end or beyond
-simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned(const uint8_t * const src, const uint8_t * const src_end) noexcept {
- const uint8_t *p = src;
- //
- // Parse the integer part.
- //
- // PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
- const uint8_t *const start_digits = p;
- uint64_t i = 0;
- while ((p != src_end) && parse_digit(*p, i)) { p++; }
-
- // If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
- // Optimization note: size_t is expected to be unsigned.
- size_t digit_count = size_t(p - start_digits);
- // The longest positive 64-bit number is 20 digits.
- // We do it this way so we don't trigger this branch unless we must.
- // Optimization note: the compiler can probably merge
- // ((digit_count == 0) || (digit_count > 20))
- // into a single branch since digit_count is unsigned.
- if ((digit_count == 0) || (digit_count > 20)) { return INCORRECT_TYPE; }
- // Here digit_count > 0.
- if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
- // We can do the following...
- // if (!jsoncharutils::is_structural_or_whitespace(*p)) {
- // return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
- // }
- // as a single table lookup:
- if ((p != src_end) && integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); }
-
- if (digit_count == 20) {
- // Positive overflow check:
- // - A 20 digit number starting with 2-9 is overflow, because 18,446,744,073,709,551,615 is the
- // biggest uint64_t.
- // - A 20 digit number starting with 1 is overflow if it is less than INT64_MAX.
- // If we got here, it's a 20 digit number starting with the digit "1".
- // - If a 20 digit number starting with 1 overflowed (i*10+digit), the result will be smaller
- // than 1,553,255,926,290,448,384.
- // - That is smaller than the smallest possible 20-digit number the user could write:
- // 10,000,000,000,000,000,000.
- // - Therefore, if the number is positive and lower than that, it's overflow.
- // - The value we are looking at is less than or equal to INT64_MAX.
- //
- if (src[0] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INCORRECT_TYPE; }
- }
-
- return i;
-}
-
// Parse any number from 0 to 18,446,744,073,709,551,615
simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned_in_string(const uint8_t * const src) noexcept {
const uint8_t *p = src + 1;
@@ -16842,50 +16777,6 @@ simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer(const uin
return negative ? (~i+1) : i;
}
-// Parse any number from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
-// Never read at src_end or beyond
-simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer(const uint8_t * const src, const uint8_t * const src_end) noexcept {
- //
- // Check for minus sign
- //
- if(src == src_end) { return NUMBER_ERROR; }
- bool negative = (*src == '-');
- const uint8_t *p = src + uint8_t(negative);
-
- //
- // Parse the integer part.
- //
- // PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
- const uint8_t *const start_digits = p;
- uint64_t i = 0;
- while ((p != src_end) && parse_digit(*p, i)) { p++; }
-
- // If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
- // Optimization note: size_t is expected to be unsigned.
- size_t digit_count = size_t(p - start_digits);
- // We go from
- // -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
- // so we can never represent numbers that have more than 19 digits.
- size_t longest_digit_count = 19;
- // Optimization note: the compiler can probably merge
- // ((digit_count == 0) || (digit_count > longest_digit_count))
- // into a single branch since digit_count is unsigned.
- if ((digit_count == 0) || (digit_count > longest_digit_count)) { return INCORRECT_TYPE; }
- // Here digit_count > 0.
- if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
- // We can do the following...
- // if (!jsoncharutils::is_structural_or_whitespace(*p)) {
- // return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
- // }
- // as a single table lookup:
- if((p != src_end) && integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); }
- // Negative numbers have can go down to - INT64_MAX - 1 whereas positive numbers are limited to INT64_MAX.
- // Performance note: This check is only needed when digit_count == longest_digit_count but it is
- // so cheap that we might as well always make it.
- if(i > uint64_t(INT64_MAX) + uint64_t(negative)) { return INCORRECT_TYPE; }
- return negative ? (~i+1) : i;
-}
-
// Parse any number from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer_in_string(const uint8_t *src) noexcept {
//
@@ -16991,8 +16882,11 @@ simdjson_unused simdjson_inline simdjson_result<double> parse_double(const uint8
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
+ // no exp digits
+ if (p-start_exp_digits == 0) { return NUMBER_ERROR; }
+ // 20+ exp digits (e.g., 0e9999999999999999999999) are valid, but exp may
+ // have wrapped around: leave such numbers to the fallback parser.
+ overflow = overflow || p-start_exp_digits > 19;
exponent += exp_neg ? 0-exp : exp;
}
@@ -17082,8 +16976,11 @@ simdjson_unused simdjson_inline simdjson_result<float> parse_float(const uint8_t
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
+ // no exp digits
+ if (p-start_exp_digits == 0) { return NUMBER_ERROR; }
+ // 20+ exp digits (e.g., 0e9999999999999999999999) are valid, but exp may
+ // have wrapped around: leave such numbers to the fallback parser.
+ overflow = overflow || p-start_exp_digits > 19;
exponent += exp_neg ? 0-exp : exp;
}
@@ -17163,88 +17060,6 @@ simdjson_unused simdjson_inline simdjson_result<number_type> get_number_type(con
return number_type::floating_point_number;
}
-// Never read at src_end or beyond
-simdjson_unused simdjson_inline simdjson_result<double> parse_double(const uint8_t * src, const uint8_t * const src_end) noexcept {
- if(src == src_end) { return NUMBER_ERROR; }
- //
- // Check for minus sign
- //
- bool negative = (*src == '-');
- src += uint8_t(negative);
-
- //
- // Parse the integer part.
- //
- uint64_t i = 0;
- const uint8_t *p = src;
- if(p == src_end) { return NUMBER_ERROR; }
- p += parse_digit(*p, i);
- bool leading_zero = (i == 0);
- while ((p != src_end) && parse_digit(*p, i)) { p++; }
- // no integer digits, or 0123 (zero must be solo)
- if ( p == src ) { return INCORRECT_TYPE; }
- if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; }
-
- //
- // Parse the decimal part.
- //
- int64_t exponent = 0;
- bool overflow;
- if (simdjson_likely((p != src_end) && (*p == '.'))) {
- p++;
- const uint8_t *start_decimal_digits = p;
- if ((p == src_end) || !parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits
- p++;
- while ((p != src_end) && parse_digit(*p, i)) { p++; }
- exponent = -(p - start_decimal_digits);
-
- // Overflow check. More than 19 digits (minus the decimal) may be overflow.
- overflow = p-src-1 > 19;
- if (simdjson_unlikely(overflow && leading_zero)) {
- // Skip leading 0.00000 and see if it still overflows
- const uint8_t *start_digits = src + 2;
- while (*start_digits == '0') { start_digits++; }
- overflow = start_digits-src > 19;
- }
- } else {
- overflow = p-src > 19;
- }
-
- //
- // Parse the exponent
- //
- if ((p != src_end) && (*p == 'e' || *p == 'E')) {
- p++;
- if(p == src_end) { return NUMBER_ERROR; }
- bool exp_neg = *p == '-';
- p += exp_neg || *p == '+';
-
- uint64_t exp = 0;
- const uint8_t *start_exp_digits = p;
- while ((p != src_end) && parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
-
- exponent += exp_neg ? 0-exp : exp;
- }
-
- if ((p != src_end) && jsoncharutils::is_not_structural_or_whitespace(*p)) { return NUMBER_ERROR; }
-
- overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power;
-
- //
- // Assemble (or slow-parse) the float
- //
- double d;
- if (simdjson_likely(!overflow)) {
- if (compute_float_64(exponent, i, negative, d)) { return d; }
- }
- if (!parse_float_fallback(src - uint8_t(negative), src_end, &d)) {
- return NUMBER_ERROR;
- }
- return d;
-}
-
simdjson_unused simdjson_inline simdjson_result<double> parse_double_in_string(const uint8_t * src) noexcept {
//
// Check for minus sign
@@ -17313,8 +17128,11 @@ simdjson_unused simdjson_inline simdjson_result<double> parse_double_in_string(c
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
+ // no exp digits
+ if (p-start_exp_digits == 0) { return NUMBER_ERROR; }
+ // 20+ exp digits (e.g., 0e9999999999999999999999) are valid, but exp may
+ // have wrapped around: leave such numbers to the fallback parser.
+ overflow = overflow || p-start_exp_digits > 19;
exponent += exp_neg ? 0-exp : exp;
}
@@ -17406,8 +17224,11 @@ simdjson_unused simdjson_inline simdjson_result<float> parse_float_in_string(con
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
+ // no exp digits
+ if (p-start_exp_digits == 0) { return NUMBER_ERROR; }
+ // 20+ exp digits (e.g., 0e9999999999999999999999) are valid, but exp may
+ // have wrapped around: leave such numbers to the fallback parser.
+ overflow = overflow || p-start_exp_digits > 19;
exponent += exp_neg ? 0-exp : exp;
}
@@ -18463,31 +18284,6 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin
};
}
-struct escaping {
- static constexpr uint32_t BYTES_PROCESSED = 16;
- simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
-
- simdjson_inline bool has_escape() { return escape_bits != 0; }
- simdjson_inline int escape_index() { return trailing_zeroes(escape_bits) / 4; }
-
- uint64_t escape_bits;
-}; // struct escaping
-
-
-
-simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
- static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes");
- simd8<uint8_t> v(src);
- v.store(dst);
- simd8<bool> is_quote = (v == '"');
- simd8<bool> is_backslash = (v == '\\');
- simd8<bool> is_control = (v < 32);
- return {
- (is_backslash | is_quote | is_control).to_bitmask64()
- };
-}
-
-
} // unnamed namespace
} // namespace arm64
@@ -18656,6 +18452,17 @@ public:
* @return the number of effective characters in the last block.
*/
simdjson_inline size_t get_remainder(uint8_t *dst) const;
+ /**
+ * Like get_remainder, for input followed by SIMDJSON_PADDING readable bytes.
+ *
+ * get_remainder writes the block with a memset and a memcpy of the remaining
+ * length, and the vector loads that read it back cannot be forwarded from
+ * those narrower stores: they wait until the stores reach the cache, which
+ * costs tens of cycles on every document. Here the block is put together in
+ * vector registers, from loads that may read into the padding, and written
+ * with full-width stores that the loads can forward from.
+ */
+ simdjson_inline size_t get_remainder_padded(uint8_t *dst) const;
simdjson_inline void advance();
private:
const uint8_t *buf;
@@ -18664,47 +18471,6 @@ private:
size_t idx;
};
-// Routines to print masks and text for debugging bitmask operations
-simdjson_unused static char * format_input_text_64(const uint8_t *text) {
- static char buf[sizeof(simd8x64<uint8_t>) + 1];
- for (size_t i=0; i<sizeof(simd8x64<uint8_t>); i++) {
- buf[i] = int8_t(text[i]) < ' ' ? '_' : int8_t(text[i]);
- }
- buf[sizeof(simd8x64<uint8_t>)] = '\0';
- return buf;
-}
-
-// Routines to print masks and text for debugging bitmask operations
-simdjson_unused static char * format_input_text(const simd8x64<uint8_t>& in) {
- static char buf[sizeof(simd8x64<uint8_t>) + 1];
- in.store(reinterpret_cast<uint8_t*>(buf));
- for (size_t i=0; i<sizeof(simd8x64<uint8_t>); i++) {
- if (buf[i] < ' ') { buf[i] = '_'; }
- }
- buf[sizeof(simd8x64<uint8_t>)] = '\0';
- return buf;
-}
-
-simdjson_unused static char * format_input_text(const simd8x64<uint8_t>& in, uint64_t mask) {
- static char buf[sizeof(simd8x64<uint8_t>) + 1];
- in.store(reinterpret_cast<uint8_t*>(buf));
- for (size_t i=0; i<sizeof(simd8x64<uint8_t>); i++) {
- if (buf[i] <= ' ') { buf[i] = '_'; }
- if (!(mask & (size_t(1) << i))) { buf[i] = ' '; }
- }
- buf[sizeof(simd8x64<uint8_t>)] = '\0';
- return buf;
-}
-
-simdjson_unused static char * format_mask(uint64_t mask) {
- static char buf[sizeof(simd8x64<uint8_t>) + 1];
- for (size_t i=0; i<64; i++) {
- buf[i] = (mask & (size_t(1) << i)) ? 'X' : ' ';
- }
- buf[64] = '\0';
- return buf;
-}
-
template<size_t STEP_SIZE>
simdjson_inline buf_block_reader<STEP_SIZE>::buf_block_reader(const uint8_t *_buf, size_t _len) : buf{_buf}, len{_len}, lenminusstep{len < STEP_SIZE ? 0 : len - STEP_SIZE}, idx{0} {}
@@ -18729,6 +18495,38 @@ simdjson_inline size_t buf_block_reader<STEP_SIZE>::get_remainder(uint8_t *dst)
return len - idx;
}
+// keep_mask[128 - n + i] is 0xFF for i < n and 0 otherwise.
+alignas(64) static const uint8_t keep_mask[256] = {
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+template<size_t STEP_SIZE>
+simdjson_inline size_t buf_block_reader<STEP_SIZE>::get_remainder_padded(uint8_t *dst) const {
+ static_assert(STEP_SIZE <= 128, "keep_mask covers blocks of up to 128 bytes");
+ static_assert(sizeof(simd::simd8<uint8_t>) <= SIMDJSON_PADDING, "a vector load at the last byte must stay in the padding");
+ const size_t remaining = len - idx;
+ if (remaining == 0) { return 0; }
+ const simd::simd8<uint8_t> spaces = simd::simd8<uint8_t>::splat(0x20);
+ for (size_t off = 0; off < STEP_SIZE; off += sizeof(simd::simd8<uint8_t>)) {
+ simd::simd8<uint8_t> v = spaces;
+ if (off < remaining) {
+ // Starts before len, so it ends within the padding.
+ const simd::simd8<uint8_t> in = simd::simd8<uint8_t>::load(buf + idx + off);
+ const simd::simd8<uint8_t> keep = simd::simd8<uint8_t>::load(keep_mask + 128 - remaining + off);
+ v = spaces ^ ((in ^ spaces) & keep);
+ }
+ v.store(dst + off);
+ }
+ return remaining;
+}
+
template<size_t STEP_SIZE>
simdjson_inline void buf_block_reader<STEP_SIZE>::advance() {
idx += STEP_SIZE;
@@ -19429,6 +19227,20 @@ simdjson_inline bool ends_with_partial_scalar(dom_parser_implementation &parser,
e != ' ' && e != '\t' && e != '\n' && e != '\r';
}
+/**
+ * The structural index past the last one of a partial batch is the end of this
+ * window, not a token of it: the byte there is the first byte of the next
+ * window. A finished value must not be rejected because that byte is ',', ':',
+ * ']' or '}'. A last document that itself ends with '{', '[', ',' or ':' is
+ * invalid JSON, yet its brackets may balance: walking it, the On-Demand API
+ * could read the next batch as a value. We leave such a document to the next
+ * batch, so that the batch ends between two documents.
+ */
+simdjson_inline bool ends_with_unfinished_document(dom_parser_implementation &parser) {
+ const uint8_t last = parser.buf[parser.structural_indexes[parser.n_structural_indexes - 1]];
+ return last == '{' || last == '[' || last == ',' || last == ':';
+}
+
simdjson_inline uint32_t find_next_document_index(dom_parser_implementation &parser, bool defer_last = false) {
// Variant: do not count separately, just figure out depth
if(parser.n_structural_indexes == 0) { return 0; }
@@ -20110,7 +19922,8 @@ error_code json_structural_indexer::index(const uint8_t *buf, size_t len, dom_pa
// Take care of the last block (will always be there unless file is empty which is
// not supposed to happen.)
uint8_t block[STEP_SIZE];
- if (simdjson_unlikely(reader.get_remainder(block) == 0)) { return UNEXPECTED_ERROR; }
+ const size_t remaining = parser._unpadded ? reader.get_remainder(block) : reader.get_remainder_padded(block);
+ if (simdjson_unlikely(remaining == 0)) { return UNEXPECTED_ERROR; }
indexer.step<STEP_SIZE>(block, reader);
return indexer.finish(parser, reader.block_index(), len, partial);
}
@@ -20144,6 +19957,20 @@ simdjson_inline void json_structural_indexer::next(const simd::simd8x64<uint8_t>
unescaped_chars_error |= block.non_quote_inside_string(unescaped);
}
+// Drops the unclosed string that ends a json_sequence batch and leaves its
+// start in structural_indexes[n_structural_indexes]. The last index is its
+// opening quote, unless the quote directly follows an RS: the scanner reads
+// RS as a scalar byte, so it indexes the RS and not the quote. Keep the RS.
+simdjson_inline void drop_json_sequence_unclosed_string(dom_parser_implementation &parser, size_t len) noexcept {
+ uint32_t quote = parser.structural_indexes[parser.n_structural_indexes - 1];
+ if (parser.buf[quote] == '"') {
+ parser.n_structural_indexes--;
+ return;
+ }
+ while (quote < len && parser.buf[quote] != '"') { quote++; }
+ parser.structural_indexes[parser.n_structural_indexes] = quote;
+}
+
simdjson_inline error_code json_structural_indexer::finish(dom_parser_implementation &parser, size_t idx, size_t len, stage1_mode partial) {
// Write out the final iteration's structurals
indexer.write(uint32_t(idx-64), prev_structurals);
@@ -20200,8 +20027,13 @@ simdjson_inline error_code json_structural_indexer::finish(dom_parser_implementa
if (simdjson_unlikely(parser.n_structural_indexes == 0u)) { return CAPACITY; }
}
// We truncate the input to the end of the last complete document (or zero).
- auto new_structural_indexes = find_next_document_index(parser, !have_unclosed_string && ends_with_partial_scalar(parser, len));
+ const bool partial_scalar = !have_unclosed_string && ends_with_partial_scalar(parser, len);
+ const bool unfinished = ends_with_unfinished_document(parser);
+ auto new_structural_indexes = find_next_document_index(parser, partial_scalar || unfinished);
if (new_structural_indexes == 0 && parser.n_structural_indexes > 0) {
+ // The batch holds a single document, whose brackets balance but which
+ // cannot be valid JSON: it is not too big, it is invalid.
+ if (unfinished && find_next_document_index(parser, partial_scalar) != 0) { return TAPE_ERROR; }
if(parser.structural_indexes[0] == 0) {
// If the buffer is partial and we started at index 0 but the document is
// incomplete, it's too big to parse.
@@ -20245,7 +20077,7 @@ simdjson_inline error_code json_structural_indexer::finish(dom_parser_implementa
// A discarded unclosed string also caps how far the filter may scan.
size_t scan_len = len;
if(have_unclosed_string) {
- parser.n_structural_indexes--;
+ drop_json_sequence_unclosed_string(parser, len);
if (simdjson_unlikely(parser.n_structural_indexes == 0u)) { return CAPACITY; }
scan_len = parser.structural_indexes[parser.n_structural_indexes];
}
@@ -20264,12 +20096,17 @@ simdjson_inline error_code json_structural_indexer::finish(dom_parser_implementa
}
parser.n_structural_indexes = new_structural_indexes;
parser.structural_indexes[parser.n_structural_indexes] = next_batch_start;
+ // The next batch starts at an RS, which the On-Demand API never reads as a
+ // string, a container or a separator. But an invalid last document may make
+ // it skip that RS as a value: repeat it, so that the index past it is not
+ // the next record, which can be a string that does not close in this batch.
+ parser.structural_indexes[parser.n_structural_indexes + 1] = next_batch_start;
} else if (partial == stage1_mode::json_sequence_final) {
// RFC 7464: final batch, last document extends to EOF
// As above: a discarded unclosed string caps how far the filter may scan.
size_t scan_len = len;
if(have_unclosed_string) {
- parser.n_structural_indexes--;
+ drop_json_sequence_unclosed_string(parser, len);
scan_len = parser.structural_indexes[parser.n_structural_indexes];
}
uint32_t next_batch_start = uint32_t(len);
@@ -20687,7 +20524,7 @@ public:
*
* - increment_count(iter) - each time a value is found in an array or object.
*/
- template<bool STREAMING, bool UNPADDED, typename V>
+ template<bool STREAMING, bool UNPADDED, bool BOUNDED = false, typename V>
simdjson_warn_unused simdjson_inline error_code walk_document(V &visitor) noexcept;
/**
@@ -20704,8 +20541,8 @@ public:
*
* They may include invalid JSON as well (such as `1.2.3` or `ture`).
*/
- template<bool UNPADDED>
- simdjson_inline const uint8_t *peek() const noexcept;
+ template<bool UNPADDED, bool BOUNDED = false>
+ simdjson_inline const uint8_t *peek(const uint32_t *batch_end = nullptr) const noexcept;
/**
* Advance to the next token.
*
@@ -20713,8 +20550,8 @@ public:
*
* They may include invalid JSON as well (such as `1.2.3` or `ture`).
*/
- template<bool UNPADDED>
- simdjson_inline const uint8_t *advance() noexcept;
+ template<bool UNPADDED, bool BOUNDED = false>
+ simdjson_inline const uint8_t *advance(const uint32_t *batch_end = nullptr) noexcept;
/**
* Get the remaining length of the document, from the start of the current token.
*/
@@ -20762,7 +20599,7 @@ public:
simdjson_warn_unused simdjson_inline error_code visit_primitive(V &visitor, const uint8_t *value) noexcept;
};
-template<bool STREAMING, bool UNPADDED, typename V>
+template<bool STREAMING, bool UNPADDED, bool BOUNDED, typename V>
simdjson_warn_unused simdjson_inline error_code json_iterator::walk_document(V &visitor) noexcept {
logger::log_start();
@@ -20770,6 +20607,9 @@ simdjson_warn_unused simdjson_inline error_code json_iterator::walk_document(V &
// Start the document
//
if (at_eof()) { return EMPTY; }
+ // Only used when BOUNDED (see advance()); a local, so that it can stay in a
+ // register, and vanishes from the other walks.
+ const uint32_t *const batch_end = &dom_parser.structural_indexes[dom_parser.n_structural_indexes];
log_start_value("document");
SIMDJSON_TRY( visitor.visit_document_start(*this) );
@@ -20777,7 +20617,7 @@ simdjson_warn_unused simdjson_inline error_code json_iterator::walk_document(V &
// Read first value
//
{
- auto value = advance<UNPADDED>();
+ auto value = advance<UNPADDED, BOUNDED>(batch_end);
// Make sure the outer object or array is closed before continuing; otherwise, there are ways we
// could get into memory corruption. See https://github.com/simdjson/simdjson/issues/906
@@ -20789,8 +20629,8 @@ simdjson_warn_unused simdjson_inline error_code json_iterator::walk_document(V &
}
switch (*value) {
- case '{': if (*peek<UNPADDED>() == '}') { advance<UNPADDED>(); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin;
- case '[': if (*peek<UNPADDED>() == ']') { advance<UNPADDED>(); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin;
+ case '{': if (*peek<UNPADDED, BOUNDED>(batch_end) == '}') { advance<UNPADDED, BOUNDED>(batch_end); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin;
+ case '[': if (*peek<UNPADDED, BOUNDED>(batch_end) == ']') { advance<UNPADDED, BOUNDED>(batch_end); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin;
default: SIMDJSON_TRY( visitor.visit_root_primitive(*this, value) ); break;
}
}
@@ -20807,29 +20647,29 @@ object_begin:
SIMDJSON_TRY( visitor.visit_object_start(*this) );
{
- auto key = advance<UNPADDED>();
+ auto key = advance<UNPADDED, BOUNDED>(batch_end);
if (*key != '"') { log_error("Object does not start with a key"); return TAPE_ERROR; }
SIMDJSON_TRY( visitor.increment_count(*this) );
SIMDJSON_TRY( visitor.visit_key(*this, key) );
}
object_field:
- if (simdjson_unlikely( *advance<UNPADDED>() != ':' )) { log_error("Missing colon after key in object"); return TAPE_ERROR; }
+ if (simdjson_unlikely( (*advance<UNPADDED, BOUNDED>(batch_end) != ':') )) { log_error("Missing colon after key in object"); return TAPE_ERROR; }
{
- auto value = advance<UNPADDED>();
+ auto value = advance<UNPADDED, BOUNDED>(batch_end);
switch (*value) {
- case '{': if (*peek<UNPADDED>() == '}') { advance<UNPADDED>(); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin;
- case '[': if (*peek<UNPADDED>() == ']') { advance<UNPADDED>(); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin;
+ case '{': if (*peek<UNPADDED, BOUNDED>(batch_end) == '}') { advance<UNPADDED, BOUNDED>(batch_end); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin;
+ case '[': if (*peek<UNPADDED, BOUNDED>(batch_end) == ']') { advance<UNPADDED, BOUNDED>(batch_end); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin;
default: SIMDJSON_TRY( visitor.visit_primitive(*this, value) ); break;
}
}
object_continue:
- switch (*advance<UNPADDED>()) {
+ switch (*advance<UNPADDED, BOUNDED>(batch_end)) {
case ',':
SIMDJSON_TRY( visitor.increment_count(*this) );
{
- auto key = advance<UNPADDED>();
+ auto key = advance<UNPADDED, BOUNDED>(batch_end);
if (simdjson_unlikely( *key != '"' )) { log_error("Key string missing at beginning of field in object"); return TAPE_ERROR; }
SIMDJSON_TRY( visitor.visit_key(*this, key) );
}
@@ -20857,16 +20697,16 @@ array_begin:
array_value:
{
- auto value = advance<UNPADDED>();
+ auto value = advance<UNPADDED, BOUNDED>(batch_end);
switch (*value) {
- case '{': if (*peek<UNPADDED>() == '}') { advance<UNPADDED>(); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin;
- case '[': if (*peek<UNPADDED>() == ']') { advance<UNPADDED>(); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin;
+ case '{': if (*peek<UNPADDED, BOUNDED>(batch_end) == '}') { advance<UNPADDED, BOUNDED>(batch_end); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin;
+ case '[': if (*peek<UNPADDED, BOUNDED>(batch_end) == ']') { advance<UNPADDED, BOUNDED>(batch_end); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin;
default: SIMDJSON_TRY( visitor.visit_primitive(*this, value) ); break;
}
}
array_continue:
- switch (*advance<UNPADDED>()) {
+ switch (*advance<UNPADDED, BOUNDED>(batch_end)) {
case ',': SIMDJSON_TRY( visitor.increment_count(*this) ); goto array_value;
case ']': log_end_value("array"); SIMDJSON_TRY( visitor.visit_array_end(*this) ); goto scope_end;
default: log_error("Missing comma between array values"); return TAPE_ERROR;
@@ -20896,8 +20736,18 @@ simdjson_inline json_iterator::json_iterator(dom_parser_implementation &_dom_par
// Stage 1 leaves a sentinel index of value `len`; reading buf[len] requires
// padding. For UNPADDED, return a dummy so walk_document can fail cleanly (#2815).
-template<bool UNPADDED>
-simdjson_inline const uint8_t *json_iterator::peek() const noexcept {
+//
+// When BOUNDED (see dom_parser_implementation::_bounded_batch), a document may
+// not run past batch_end, one past the last structural index of the batch:
+// past it, we return the same dummy, and walk_document fails cleanly.
+template<bool UNPADDED, bool BOUNDED>
+simdjson_inline const uint8_t *json_iterator::peek(const uint32_t *batch_end) const noexcept {
+ SIMDJSON_IF_CONSTEXPR (BOUNDED) {
+ if (simdjson_unlikely(next_structural >= batch_end)) {
+ static constexpr uint8_t streaming_eof_sentinel = 0;
+ return &streaming_eof_sentinel;
+ }
+ }
const uint32_t idx = *(next_structural);
SIMDJSON_IF_CONSTEXPR (UNPADDED) {
if (simdjson_unlikely(idx >= dom_parser.len)) {
@@ -20907,8 +20757,14 @@ simdjson_inline const uint8_t *json_iterator::peek() const noexcept {
}
return &buf[idx];
}
-template<bool UNPADDED>
-simdjson_inline const uint8_t *json_iterator::advance() noexcept {
+template<bool UNPADDED, bool BOUNDED>
+simdjson_inline const uint8_t *json_iterator::advance(const uint32_t *batch_end) noexcept {
+ SIMDJSON_IF_CONSTEXPR (BOUNDED) {
+ if (simdjson_unlikely(next_structural >= batch_end)) {
+ static constexpr uint8_t streaming_eof_sentinel = 0;
+ return &streaming_eof_sentinel;
+ }
+ }
const uint32_t idx = *(next_structural++);
SIMDJSON_IF_CONSTEXPR (UNPADDED) {
if (simdjson_unlikely(idx >= dom_parser.len)) {
@@ -21467,6 +21323,30 @@ struct tape_builder {
return iter.walk_document<STREAMING, false>(builder);
}
}
+
+ // The stage 2 entry points of dom::document_stream, which picks one per batch
+ // (see internal::dom_parser_implementation::stage2_next_function). Out of
+ // line, so that stage2_next() can share the code.
+ simdjson_warn_unused static simdjson_never_inline error_code parse_next_document(
+ internal::dom_parser_implementation &dom_parser, dom::document &doc) noexcept {
+ return parse_document<true>(static_cast<dom_parser_implementation &>(dom_parser), doc);
+ }
+
+ // A document of a stream batch that stage 2 must bound to its structural
+ // indexes (see internal::dom_parser_implementation::_bounded_batch).
+ simdjson_warn_unused static simdjson_never_inline error_code parse_next_bounded_document(
+ internal::dom_parser_implementation &_dom_parser, dom::document &doc) noexcept {
+ dom_parser_implementation &dom_parser = static_cast<dom_parser_implementation &>(_dom_parser);
+ dom_parser.doc = &doc;
+ json_iterator iter(dom_parser, dom_parser.next_structural_index);
+ if (dom_parser._unpadded) {
+ tape_builder_impl<true> builder(doc);
+ return iter.walk_document<true, true, true>(builder);
+ } else {
+ tape_builder_impl<false> builder(doc);
+ return iter.walk_document<true, false, true>(builder);
+ }
+ }
};
template <bool UNPADDED>
@@ -21896,18 +21776,6 @@ simdjson_inline bool is_ascii(const simd8x64<uint8_t>& input) {
return bits.max_val() < 0x80u;
}
-simdjson_unused simdjson_inline simd8<bool> must_be_continuation(const simd8<uint8_t> prev1, const simd8<uint8_t> prev2, const simd8<uint8_t> prev3) {
- simd8<bool> is_second_byte = prev1 >= uint8_t(0xc0u);
- simd8<bool> is_third_byte = prev2 >= uint8_t(0xe0u);
- simd8<bool> is_fourth_byte = prev3 >= uint8_t(0xf0u);
- // Use ^ instead of | for is_*_byte, because ^ is commutative, and the caller is using ^ as well.
- // This will work fine because we only have to report errors for cases with 0-1 lead bytes.
- // Multiple lead bytes implies 2 overlapping multibyte characters, and if that happens, there is
- // guaranteed to be at least *one* lead byte that is part of only 1 other multibyte character.
- // The error will be detected there.
- return is_second_byte ^ is_third_byte ^ is_fourth_byte;
-}
-
simdjson_inline simd8<uint8_t> must_be_2_3_continuation(const simd8<uint8_t> prev2, const simd8<uint8_t> prev3) {
simd8<uint8_t> is_third_byte = prev2.saturating_sub(0xe0u-0x80); // Only 111_____ will be >= 0x80
simd8<uint8_t> is_fourth_byte = prev3.saturating_sub(0xf0u-0x80); // Only 1111____ will be >= 0x80
@@ -21947,7 +21815,11 @@ simdjson_warn_unused error_code dom_parser_implementation::stage2(dom::document
}
simdjson_warn_unused error_code dom_parser_implementation::stage2_next(dom::document &_doc) noexcept {
- return stage2::tape_builder::parse_document<true>(*this, _doc);
+ return stage2::tape_builder::parse_next_document(*this, _doc);
+}
+
+internal::dom_parser_implementation::stage2_next_function_t dom_parser_implementation::stage2_next_function(bool bounded) const noexcept {
+ return bounded ? stage2::tape_builder::parse_next_bounded_document : stage2::tape_builder::parse_next_document;
}
SIMDJSON_NO_SANITIZE_MEMORY
@@ -22681,30 +22553,6 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin
}
-struct escaping {
- static constexpr uint32_t BYTES_PROCESSED = 32;
- simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
-
- simdjson_inline bool has_escape() { return escape_bits != 0; }
- simdjson_inline int escape_index() { return trailing_zeroes(escape_bits); }
-
- uint64_t escape_bits;
-}; // struct escaping
-
-
-
-simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
- static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes");
- simd8<uint8_t> v(src);
- v.store(dst);
- simd8<bool> is_quote = (v == '"');
- simd8<bool> is_backslash = (v == '\\');
- simd8<bool> is_control = (v < 32);
- return {
- uint64_t((is_backslash | is_quote | is_control).to_bitmask())
- };
-}
-
} // unnamed namespace
} // namespace haswell
} // namespace simdjson
@@ -23095,6 +22943,7 @@ public:
simdjson_warn_unused error_code stage1(const uint8_t *buf, size_t len, stage1_mode partial) noexcept final;
simdjson_warn_unused error_code stage2(dom::document &doc) noexcept final;
simdjson_warn_unused error_code stage2_next(dom::document &doc) noexcept final;
+ stage2_next_function_t stage2_next_function(bool bounded) const noexcept final;
simdjson_warn_unused uint8_t *parse_string(const uint8_t *src, uint8_t *dst, bool allow_replacement) const noexcept final;
simdjson_warn_unused uint8_t *parse_wobbly_string(const uint8_t *src, uint8_t *dst) const noexcept final;
inline simdjson_warn_unused error_code set_capacity(size_t capacity) noexcept final;
@@ -23856,21 +23705,6 @@ static bool parse_float_fallback(const uint8_t *ptr, double *outDouble) {
return !(*outDouble > (std::numeric_limits<double>::max)() || *outDouble < std::numeric_limits<double>::lowest());
}
-static bool parse_float_fallback(const uint8_t *ptr, const uint8_t *end_ptr, double *outDouble) {
- *outDouble = simdjson::internal::from_chars(reinterpret_cast<const char *>(ptr), reinterpret_cast<const char *>(end_ptr));
- // We do not accept infinite values.
-
- // Detecting finite values in a portable manner is ridiculously hard, ideally
- // we would want to do:
- // return !std::isfinite(*outDouble);
- // but that mysteriously fails under legacy/old libc++ libraries, see
- // https://github.com/simdjson/simdjson/issues/1286
- //
- // Therefore, fall back to this solution (the extra parens are there
- // to handle that max may be a macro on windows).
- return !(*outDouble > (std::numeric_limits<double>::max)() || *outDouble < std::numeric_limits<double>::lowest());
-}
-
// Same as parse_float_fallback, but for binary32 (float) values. Going through
// the binary64 fallback and then rounding to binary32 would be subject to
// double rounding, so we run the fallback algorithm directly on binary32.
@@ -24416,57 +24250,6 @@ simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned(const u
return i;
}
-
-
-// Parse any number from 0 to 18,446,744,073,709,551,615
-// Never read at src_end or beyond
-simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned(const uint8_t * const src, const uint8_t * const src_end) noexcept {
- const uint8_t *p = src;
- //
- // Parse the integer part.
- //
- // PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
- const uint8_t *const start_digits = p;
- uint64_t i = 0;
- while ((p != src_end) && parse_digit(*p, i)) { p++; }
-
- // If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
- // Optimization note: size_t is expected to be unsigned.
- size_t digit_count = size_t(p - start_digits);
- // The longest positive 64-bit number is 20 digits.
- // We do it this way so we don't trigger this branch unless we must.
- // Optimization note: the compiler can probably merge
- // ((digit_count == 0) || (digit_count > 20))
- // into a single branch since digit_count is unsigned.
- if ((digit_count == 0) || (digit_count > 20)) { return INCORRECT_TYPE; }
- // Here digit_count > 0.
- if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
- // We can do the following...
- // if (!jsoncharutils::is_structural_or_whitespace(*p)) {
- // return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
- // }
- // as a single table lookup:
- if ((p != src_end) && integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); }
-
- if (digit_count == 20) {
- // Positive overflow check:
- // - A 20 digit number starting with 2-9 is overflow, because 18,446,744,073,709,551,615 is the
- // biggest uint64_t.
- // - A 20 digit number starting with 1 is overflow if it is less than INT64_MAX.
- // If we got here, it's a 20 digit number starting with the digit "1".
- // - If a 20 digit number starting with 1 overflowed (i*10+digit), the result will be smaller
- // than 1,553,255,926,290,448,384.
- // - That is smaller than the smallest possible 20-digit number the user could write:
- // 10,000,000,000,000,000,000.
- // - Therefore, if the number is positive and lower than that, it's overflow.
- // - The value we are looking at is less than or equal to INT64_MAX.
- //
- if (src[0] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INCORRECT_TYPE; }
- }
-
- return i;
-}
-
// Parse any number from 0 to 18,446,744,073,709,551,615
simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned_in_string(const uint8_t * const src) noexcept {
const uint8_t *p = src + 1;
@@ -24559,50 +24342,6 @@ simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer(const uin
return negative ? (~i+1) : i;
}
-// Parse any number from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
-// Never read at src_end or beyond
-simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer(const uint8_t * const src, const uint8_t * const src_end) noexcept {
- //
- // Check for minus sign
- //
- if(src == src_end) { return NUMBER_ERROR; }
- bool negative = (*src == '-');
- const uint8_t *p = src + uint8_t(negative);
-
- //
- // Parse the integer part.
- //
- // PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
- const uint8_t *const start_digits = p;
- uint64_t i = 0;
- while ((p != src_end) && parse_digit(*p, i)) { p++; }
-
- // If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
- // Optimization note: size_t is expected to be unsigned.
- size_t digit_count = size_t(p - start_digits);
- // We go from
- // -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
- // so we can never represent numbers that have more than 19 digits.
- size_t longest_digit_count = 19;
- // Optimization note: the compiler can probably merge
- // ((digit_count == 0) || (digit_count > longest_digit_count))
- // into a single branch since digit_count is unsigned.
- if ((digit_count == 0) || (digit_count > longest_digit_count)) { return INCORRECT_TYPE; }
- // Here digit_count > 0.
- if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
- // We can do the following...
- // if (!jsoncharutils::is_structural_or_whitespace(*p)) {
- // return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
- // }
- // as a single table lookup:
- if((p != src_end) && integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); }
- // Negative numbers have can go down to - INT64_MAX - 1 whereas positive numbers are limited to INT64_MAX.
- // Performance note: This check is only needed when digit_count == longest_digit_count but it is
- // so cheap that we might as well always make it.
- if(i > uint64_t(INT64_MAX) + uint64_t(negative)) { return INCORRECT_TYPE; }
- return negative ? (~i+1) : i;
-}
-
// Parse any number from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer_in_string(const uint8_t *src) noexcept {
//
@@ -24708,8 +24447,11 @@ simdjson_unused simdjson_inline simdjson_result<double> parse_double(const uint8
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
+ // no exp digits
+ if (p-start_exp_digits == 0) { return NUMBER_ERROR; }
+ // 20+ exp digits (e.g., 0e9999999999999999999999) are valid, but exp may
+ // have wrapped around: leave such numbers to the fallback parser.
+ overflow = overflow || p-start_exp_digits > 19;
exponent += exp_neg ? 0-exp : exp;
}
@@ -24799,8 +24541,11 @@ simdjson_unused simdjson_inline simdjson_result<float> parse_float(const uint8_t
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
+ // no exp digits
+ if (p-start_exp_digits == 0) { return NUMBER_ERROR; }
+ // 20+ exp digits (e.g., 0e9999999999999999999999) are valid, but exp may
+ // have wrapped around: leave such numbers to the fallback parser.
+ overflow = overflow || p-start_exp_digits > 19;
exponent += exp_neg ? 0-exp : exp;
}
@@ -24880,88 +24625,6 @@ simdjson_unused simdjson_inline simdjson_result<number_type> get_number_type(con
return number_type::floating_point_number;
}
-// Never read at src_end or beyond
-simdjson_unused simdjson_inline simdjson_result<double> parse_double(const uint8_t * src, const uint8_t * const src_end) noexcept {
- if(src == src_end) { return NUMBER_ERROR; }
- //
- // Check for minus sign
- //
- bool negative = (*src == '-');
- src += uint8_t(negative);
-
- //
- // Parse the integer part.
- //
- uint64_t i = 0;
- const uint8_t *p = src;
- if(p == src_end) { return NUMBER_ERROR; }
- p += parse_digit(*p, i);
- bool leading_zero = (i == 0);
- while ((p != src_end) && parse_digit(*p, i)) { p++; }
- // no integer digits, or 0123 (zero must be solo)
- if ( p == src ) { return INCORRECT_TYPE; }
- if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; }
-
- //
- // Parse the decimal part.
- //
- int64_t exponent = 0;
- bool overflow;
- if (simdjson_likely((p != src_end) && (*p == '.'))) {
- p++;
- const uint8_t *start_decimal_digits = p;
- if ((p == src_end) || !parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits
- p++;
- while ((p != src_end) && parse_digit(*p, i)) { p++; }
- exponent = -(p - start_decimal_digits);
-
- // Overflow check. More than 19 digits (minus the decimal) may be overflow.
- overflow = p-src-1 > 19;
- if (simdjson_unlikely(overflow && leading_zero)) {
- // Skip leading 0.00000 and see if it still overflows
- const uint8_t *start_digits = src + 2;
- while (*start_digits == '0') { start_digits++; }
- overflow = start_digits-src > 19;
- }
- } else {
- overflow = p-src > 19;
- }
-
- //
- // Parse the exponent
- //
- if ((p != src_end) && (*p == 'e' || *p == 'E')) {
- p++;
- if(p == src_end) { return NUMBER_ERROR; }
- bool exp_neg = *p == '-';
- p += exp_neg || *p == '+';
-
- uint64_t exp = 0;
- const uint8_t *start_exp_digits = p;
- while ((p != src_end) && parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
-
- exponent += exp_neg ? 0-exp : exp;
- }
-
- if ((p != src_end) && jsoncharutils::is_not_structural_or_whitespace(*p)) { return NUMBER_ERROR; }
-
- overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power;
-
- //
- // Assemble (or slow-parse) the float
- //
- double d;
- if (simdjson_likely(!overflow)) {
- if (compute_float_64(exponent, i, negative, d)) { return d; }
- }
- if (!parse_float_fallback(src - uint8_t(negative), src_end, &d)) {
- return NUMBER_ERROR;
- }
- return d;
-}
-
simdjson_unused simdjson_inline simdjson_result<double> parse_double_in_string(const uint8_t * src) noexcept {
//
// Check for minus sign
@@ -25030,8 +24693,11 @@ simdjson_unused simdjson_inline simdjson_result<double> parse_double_in_string(c
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
+ // no exp digits
+ if (p-start_exp_digits == 0) { return NUMBER_ERROR; }
+ // 20+ exp digits (e.g., 0e9999999999999999999999) are valid, but exp may
+ // have wrapped around: leave such numbers to the fallback parser.
+ overflow = overflow || p-start_exp_digits > 19;
exponent += exp_neg ? 0-exp : exp;
}
@@ -25123,8 +24789,11 @@ simdjson_unused simdjson_inline simdjson_result<float> parse_float_in_string(con
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
+ // no exp digits
+ if (p-start_exp_digits == 0) { return NUMBER_ERROR; }
+ // 20+ exp digits (e.g., 0e9999999999999999999999) are valid, but exp may
+ // have wrapped around: leave such numbers to the fallback parser.
+ overflow = overflow || p-start_exp_digits > 19;
exponent += exp_neg ? 0-exp : exp;
}
@@ -26031,30 +25700,6 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin
}
-struct escaping {
- static constexpr uint32_t BYTES_PROCESSED = 32;
- simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
-
- simdjson_inline bool has_escape() { return escape_bits != 0; }
- simdjson_inline int escape_index() { return trailing_zeroes(escape_bits); }
-
- uint64_t escape_bits;
-}; // struct escaping
-
-
-
-simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
- static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes");
- simd8<uint8_t> v(src);
- v.store(dst);
- simd8<bool> is_quote = (v == '"');
- simd8<bool> is_backslash = (v == '\\');
- simd8<bool> is_control = (v < 32);
- return {
- uint64_t((is_backslash | is_quote | is_control).to_bitmask())
- };
-}
-
} // unnamed namespace
} // namespace haswell
} // namespace simdjson
@@ -26220,6 +25865,17 @@ public:
* @return the number of effective characters in the last block.
*/
simdjson_inline size_t get_remainder(uint8_t *dst) const;
+ /**
+ * Like get_remainder, for input followed by SIMDJSON_PADDING readable bytes.
+ *
+ * get_remainder writes the block with a memset and a memcpy of the remaining
+ * length, and the vector loads that read it back cannot be forwarded from
+ * those narrower stores: they wait until the stores reach the cache, which
+ * costs tens of cycles on every document. Here the block is put together in
+ * vector registers, from loads that may read into the padding, and written
+ * with full-width stores that the loads can forward from.
+ */
+ simdjson_inline size_t get_remainder_padded(uint8_t *dst) const;
simdjson_inline void advance();
private:
const uint8_t *buf;
@@ -26228,47 +25884,6 @@ private:
size_t idx;
};
-// Routines to print masks and text for debugging bitmask operations
-simdjson_unused static char * format_input_text_64(const uint8_t *text) {
- static char buf[sizeof(simd8x64<uint8_t>) + 1];
- for (size_t i=0; i<sizeof(simd8x64<uint8_t>); i++) {
- buf[i] = int8_t(text[i]) < ' ' ? '_' : int8_t(text[i]);
- }
- buf[sizeof(simd8x64<uint8_t>)] = '\0';
- return buf;
-}
-
-// Routines to print masks and text for debugging bitmask operations
-simdjson_unused static char * format_input_text(const simd8x64<uint8_t>& in) {
- static char buf[sizeof(simd8x64<uint8_t>) + 1];
- in.store(reinterpret_cast<uint8_t*>(buf));
- for (size_t i=0; i<sizeof(simd8x64<uint8_t>); i++) {
- if (buf[i] < ' ') { buf[i] = '_'; }
- }
- buf[sizeof(simd8x64<uint8_t>)] = '\0';
- return buf;
-}
-
-simdjson_unused static char * format_input_text(const simd8x64<uint8_t>& in, uint64_t mask) {
- static char buf[sizeof(simd8x64<uint8_t>) + 1];
- in.store(reinterpret_cast<uint8_t*>(buf));
- for (size_t i=0; i<sizeof(simd8x64<uint8_t>); i++) {
- if (buf[i] <= ' ') { buf[i] = '_'; }
- if (!(mask & (size_t(1) << i))) { buf[i] = ' '; }
- }
- buf[sizeof(simd8x64<uint8_t>)] = '\0';
- return buf;
-}
-
-simdjson_unused static char * format_mask(uint64_t mask) {
- static char buf[sizeof(simd8x64<uint8_t>) + 1];
- for (size_t i=0; i<64; i++) {
- buf[i] = (mask & (size_t(1) << i)) ? 'X' : ' ';
- }
- buf[64] = '\0';
- return buf;
-}
-
template<size_t STEP_SIZE>
simdjson_inline buf_block_reader<STEP_SIZE>::buf_block_reader(const uint8_t *_buf, size_t _len) : buf{_buf}, len{_len}, lenminusstep{len < STEP_SIZE ? 0 : len - STEP_SIZE}, idx{0} {}
@@ -26293,6 +25908,38 @@ simdjson_inline size_t buf_block_reader<STEP_SIZE>::get_remainder(uint8_t *dst)
return len - idx;
}
+// keep_mask[128 - n + i] is 0xFF for i < n and 0 otherwise.
+alignas(64) static const uint8_t keep_mask[256] = {
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+template<size_t STEP_SIZE>
+simdjson_inline size_t buf_block_reader<STEP_SIZE>::get_remainder_padded(uint8_t *dst) const {
+ static_assert(STEP_SIZE <= 128, "keep_mask covers blocks of up to 128 bytes");
+ static_assert(sizeof(simd::simd8<uint8_t>) <= SIMDJSON_PADDING, "a vector load at the last byte must stay in the padding");
+ const size_t remaining = len - idx;
+ if (remaining == 0) { return 0; }
+ const simd::simd8<uint8_t> spaces = simd::simd8<uint8_t>::splat(0x20);
+ for (size_t off = 0; off < STEP_SIZE; off += sizeof(simd::simd8<uint8_t>)) {
+ simd::simd8<uint8_t> v = spaces;
+ if (off < remaining) {
+ // Starts before len, so it ends within the padding.
+ const simd::simd8<uint8_t> in = simd::simd8<uint8_t>::load(buf + idx + off);
+ const simd::simd8<uint8_t> keep = simd::simd8<uint8_t>::load(keep_mask + 128 - remaining + off);
+ v = spaces ^ ((in ^ spaces) & keep);
+ }
+ v.store(dst + off);
+ }
+ return remaining;
+}
+
template<size_t STEP_SIZE>
simdjson_inline void buf_block_reader<STEP_SIZE>::advance() {
idx += STEP_SIZE;
@@ -26993,6 +26640,20 @@ simdjson_inline bool ends_with_partial_scalar(dom_parser_implementation &parser,
e != ' ' && e != '\t' && e != '\n' && e != '\r';
}
+/**
+ * The structural index past the last one of a partial batch is the end of this
+ * window, not a token of it: the byte there is the first byte of the next
+ * window. A finished value must not be rejected because that byte is ',', ':',
+ * ']' or '}'. A last document that itself ends with '{', '[', ',' or ':' is
+ * invalid JSON, yet its brackets may balance: walking it, the On-Demand API
+ * could read the next batch as a value. We leave such a document to the next
+ * batch, so that the batch ends between two documents.
+ */
+simdjson_inline bool ends_with_unfinished_document(dom_parser_implementation &parser) {
+ const uint8_t last = parser.buf[parser.structural_indexes[parser.n_structural_indexes - 1]];
+ return last == '{' || last == '[' || last == ',' || last == ':';
+}
+
simdjson_inline uint32_t find_next_document_index(dom_parser_implementation &parser, bool defer_last = false) {
// Variant: do not count separately, just figure out depth
if(parser.n_structural_indexes == 0) { return 0; }
@@ -27674,7 +27335,8 @@ error_code json_structural_indexer::index(const uint8_t *buf, size_t len, dom_pa
// Take care of the last block (will always be there unless file is empty which is
// not supposed to happen.)
uint8_t block[STEP_SIZE];
- if (simdjson_unlikely(reader.get_remainder(block) == 0)) { return UNEXPECTED_ERROR; }
+ const size_t remaining = parser._unpadded ? reader.get_remainder(block) : reader.get_remainder_padded(block);
+ if (simdjson_unlikely(remaining == 0)) { return UNEXPECTED_ERROR; }
indexer.step<STEP_SIZE>(block, reader);
return indexer.finish(parser, reader.block_index(), len, partial);
}
@@ -27708,6 +27370,20 @@ simdjson_inline void json_structural_indexer::next(const simd::simd8x64<uint8_t>
unescaped_chars_error |= block.non_quote_inside_string(unescaped);
}
+// Drops the unclosed string that ends a json_sequence batch and leaves its
+// start in structural_indexes[n_structural_indexes]. The last index is its
+// opening quote, unless the quote directly follows an RS: the scanner reads
+// RS as a scalar byte, so it indexes the RS and not the quote. Keep the RS.
+simdjson_inline void drop_json_sequence_unclosed_string(dom_parser_implementation &parser, size_t len) noexcept {
+ uint32_t quote = parser.structural_indexes[parser.n_structural_indexes - 1];
+ if (parser.buf[quote] == '"') {
+ parser.n_structural_indexes--;
+ return;
+ }
+ while (quote < len && parser.buf[quote] != '"') { quote++; }
+ parser.structural_indexes[parser.n_structural_indexes] = quote;
+}
+
simdjson_inline error_code json_structural_indexer::finish(dom_parser_implementation &parser, size_t idx, size_t len, stage1_mode partial) {
// Write out the final iteration's structurals
indexer.write(uint32_t(idx-64), prev_structurals);
@@ -27764,8 +27440,13 @@ simdjson_inline error_code json_structural_indexer::finish(dom_parser_implementa
if (simdjson_unlikely(parser.n_structural_indexes == 0u)) { return CAPACITY; }
}
// We truncate the input to the end of the last complete document (or zero).
- auto new_structural_indexes = find_next_document_index(parser, !have_unclosed_string && ends_with_partial_scalar(parser, len));
+ const bool partial_scalar = !have_unclosed_string && ends_with_partial_scalar(parser, len);
+ const bool unfinished = ends_with_unfinished_document(parser);
+ auto new_structural_indexes = find_next_document_index(parser, partial_scalar || unfinished);
if (new_structural_indexes == 0 && parser.n_structural_indexes > 0) {
+ // The batch holds a single document, whose brackets balance but which
+ // cannot be valid JSON: it is not too big, it is invalid.
+ if (unfinished && find_next_document_index(parser, partial_scalar) != 0) { return TAPE_ERROR; }
if(parser.structural_indexes[0] == 0) {
// If the buffer is partial and we started at index 0 but the document is
// incomplete, it's too big to parse.
@@ -27809,7 +27490,7 @@ simdjson_inline error_code json_structural_indexer::finish(dom_parser_implementa
// A discarded unclosed string also caps how far the filter may scan.
size_t scan_len = len;
if(have_unclosed_string) {
- parser.n_structural_indexes--;
+ drop_json_sequence_unclosed_string(parser, len);
if (simdjson_unlikely(parser.n_structural_indexes == 0u)) { return CAPACITY; }
scan_len = parser.structural_indexes[parser.n_structural_indexes];
}
@@ -27828,12 +27509,17 @@ simdjson_inline error_code json_structural_indexer::finish(dom_parser_implementa
}
parser.n_structural_indexes = new_structural_indexes;
parser.structural_indexes[parser.n_structural_indexes] = next_batch_start;
+ // The next batch starts at an RS, which the On-Demand API never reads as a
+ // string, a container or a separator. But an invalid last document may make
+ // it skip that RS as a value: repeat it, so that the index past it is not
+ // the next record, which can be a string that does not close in this batch.
+ parser.structural_indexes[parser.n_structural_indexes + 1] = next_batch_start;
} else if (partial == stage1_mode::json_sequence_final) {
// RFC 7464: final batch, last document extends to EOF
// As above: a discarded unclosed string caps how far the filter may scan.
size_t scan_len = len;
if(have_unclosed_string) {
- parser.n_structural_indexes--;
+ drop_json_sequence_unclosed_string(parser, len);
scan_len = parser.structural_indexes[parser.n_structural_indexes];
}
uint32_t next_batch_start = uint32_t(len);
@@ -28251,7 +27937,7 @@ public:
*
* - increment_count(iter) - each time a value is found in an array or object.
*/
- template<bool STREAMING, bool UNPADDED, typename V>
+ template<bool STREAMING, bool UNPADDED, bool BOUNDED = false, typename V>
simdjson_warn_unused simdjson_inline error_code walk_document(V &visitor) noexcept;
/**
@@ -28268,8 +27954,8 @@ public:
*
* They may include invalid JSON as well (such as `1.2.3` or `ture`).
*/
- template<bool UNPADDED>
- simdjson_inline const uint8_t *peek() const noexcept;
+ template<bool UNPADDED, bool BOUNDED = false>
+ simdjson_inline const uint8_t *peek(const uint32_t *batch_end = nullptr) const noexcept;
/**
* Advance to the next token.
*
@@ -28277,8 +27963,8 @@ public:
*
* They may include invalid JSON as well (such as `1.2.3` or `ture`).
*/
- template<bool UNPADDED>
- simdjson_inline const uint8_t *advance() noexcept;
+ template<bool UNPADDED, bool BOUNDED = false>
+ simdjson_inline const uint8_t *advance(const uint32_t *batch_end = nullptr) noexcept;
/**
* Get the remaining length of the document, from the start of the current token.
*/
@@ -28326,7 +28012,7 @@ public:
simdjson_warn_unused simdjson_inline error_code visit_primitive(V &visitor, const uint8_t *value) noexcept;
};
-template<bool STREAMING, bool UNPADDED, typename V>
+template<bool STREAMING, bool UNPADDED, bool BOUNDED, typename V>
simdjson_warn_unused simdjson_inline error_code json_iterator::walk_document(V &visitor) noexcept {
logger::log_start();
@@ -28334,6 +28020,9 @@ simdjson_warn_unused simdjson_inline error_code json_iterator::walk_document(V &
// Start the document
//
if (at_eof()) { return EMPTY; }
+ // Only used when BOUNDED (see advance()); a local, so that it can stay in a
+ // register, and vanishes from the other walks.
+ const uint32_t *const batch_end = &dom_parser.structural_indexes[dom_parser.n_structural_indexes];
log_start_value("document");
SIMDJSON_TRY( visitor.visit_document_start(*this) );
@@ -28341,7 +28030,7 @@ simdjson_warn_unused simdjson_inline error_code json_iterator::walk_document(V &
// Read first value
//
{
- auto value = advance<UNPADDED>();
+ auto value = advance<UNPADDED, BOUNDED>(batch_end);
// Make sure the outer object or array is closed before continuing; otherwise, there are ways we
// could get into memory corruption. See https://github.com/simdjson/simdjson/issues/906
@@ -28353,8 +28042,8 @@ simdjson_warn_unused simdjson_inline error_code json_iterator::walk_document(V &
}
switch (*value) {
- case '{': if (*peek<UNPADDED>() == '}') { advance<UNPADDED>(); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin;
- case '[': if (*peek<UNPADDED>() == ']') { advance<UNPADDED>(); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin;
+ case '{': if (*peek<UNPADDED, BOUNDED>(batch_end) == '}') { advance<UNPADDED, BOUNDED>(batch_end); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin;
+ case '[': if (*peek<UNPADDED, BOUNDED>(batch_end) == ']') { advance<UNPADDED, BOUNDED>(batch_end); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin;
default: SIMDJSON_TRY( visitor.visit_root_primitive(*this, value) ); break;
}
}
@@ -28371,29 +28060,29 @@ object_begin:
SIMDJSON_TRY( visitor.visit_object_start(*this) );
{
- auto key = advance<UNPADDED>();
+ auto key = advance<UNPADDED, BOUNDED>(batch_end);
if (*key != '"') { log_error("Object does not start with a key"); return TAPE_ERROR; }
SIMDJSON_TRY( visitor.increment_count(*this) );
SIMDJSON_TRY( visitor.visit_key(*this, key) );
}
object_field:
- if (simdjson_unlikely( *advance<UNPADDED>() != ':' )) { log_error("Missing colon after key in object"); return TAPE_ERROR; }
+ if (simdjson_unlikely( (*advance<UNPADDED, BOUNDED>(batch_end) != ':') )) { log_error("Missing colon after key in object"); return TAPE_ERROR; }
{
- auto value = advance<UNPADDED>();
+ auto value = advance<UNPADDED, BOUNDED>(batch_end);
switch (*value) {
- case '{': if (*peek<UNPADDED>() == '}') { advance<UNPADDED>(); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin;
- case '[': if (*peek<UNPADDED>() == ']') { advance<UNPADDED>(); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin;
+ case '{': if (*peek<UNPADDED, BOUNDED>(batch_end) == '}') { advance<UNPADDED, BOUNDED>(batch_end); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin;
+ case '[': if (*peek<UNPADDED, BOUNDED>(batch_end) == ']') { advance<UNPADDED, BOUNDED>(batch_end); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin;
default: SIMDJSON_TRY( visitor.visit_primitive(*this, value) ); break;
}
}
object_continue:
- switch (*advance<UNPADDED>()) {
+ switch (*advance<UNPADDED, BOUNDED>(batch_end)) {
case ',':
SIMDJSON_TRY( visitor.increment_count(*this) );
{
- auto key = advance<UNPADDED>();
+ auto key = advance<UNPADDED, BOUNDED>(batch_end);
if (simdjson_unlikely( *key != '"' )) { log_error("Key string missing at beginning of field in object"); return TAPE_ERROR; }
SIMDJSON_TRY( visitor.visit_key(*this, key) );
}
@@ -28421,16 +28110,16 @@ array_begin:
array_value:
{
- auto value = advance<UNPADDED>();
+ auto value = advance<UNPADDED, BOUNDED>(batch_end);
switch (*value) {
- case '{': if (*peek<UNPADDED>() == '}') { advance<UNPADDED>(); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin;
- case '[': if (*peek<UNPADDED>() == ']') { advance<UNPADDED>(); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin;
+ case '{': if (*peek<UNPADDED, BOUNDED>(batch_end) == '}') { advance<UNPADDED, BOUNDED>(batch_end); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin;
+ case '[': if (*peek<UNPADDED, BOUNDED>(batch_end) == ']') { advance<UNPADDED, BOUNDED>(batch_end); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin;
default: SIMDJSON_TRY( visitor.visit_primitive(*this, value) ); break;
}
}
array_continue:
- switch (*advance<UNPADDED>()) {
+ switch (*advance<UNPADDED, BOUNDED>(batch_end)) {
case ',': SIMDJSON_TRY( visitor.increment_count(*this) ); goto array_value;
case ']': log_end_value("array"); SIMDJSON_TRY( visitor.visit_array_end(*this) ); goto scope_end;
default: log_error("Missing comma between array values"); return TAPE_ERROR;
@@ -28460,8 +28149,18 @@ simdjson_inline json_iterator::json_iterator(dom_parser_implementation &_dom_par
// Stage 1 leaves a sentinel index of value `len`; reading buf[len] requires
// padding. For UNPADDED, return a dummy so walk_document can fail cleanly (#2815).
-template<bool UNPADDED>
-simdjson_inline const uint8_t *json_iterator::peek() const noexcept {
+//
+// When BOUNDED (see dom_parser_implementation::_bounded_batch), a document may
+// not run past batch_end, one past the last structural index of the batch:
+// past it, we return the same dummy, and walk_document fails cleanly.
+template<bool UNPADDED, bool BOUNDED>
+simdjson_inline const uint8_t *json_iterator::peek(const uint32_t *batch_end) const noexcept {
+ SIMDJSON_IF_CONSTEXPR (BOUNDED) {
+ if (simdjson_unlikely(next_structural >= batch_end)) {
+ static constexpr uint8_t streaming_eof_sentinel = 0;
+ return &streaming_eof_sentinel;
+ }
+ }
const uint32_t idx = *(next_structural);
SIMDJSON_IF_CONSTEXPR (UNPADDED) {
if (simdjson_unlikely(idx >= dom_parser.len)) {
@@ -28471,8 +28170,14 @@ simdjson_inline const uint8_t *json_iterator::peek() const noexcept {
}
return &buf[idx];
}
-template<bool UNPADDED>
-simdjson_inline const uint8_t *json_iterator::advance() noexcept {
+template<bool UNPADDED, bool BOUNDED>
+simdjson_inline const uint8_t *json_iterator::advance(const uint32_t *batch_end) noexcept {
+ SIMDJSON_IF_CONSTEXPR (BOUNDED) {
+ if (simdjson_unlikely(next_structural >= batch_end)) {
+ static constexpr uint8_t streaming_eof_sentinel = 0;
+ return &streaming_eof_sentinel;
+ }
+ }
const uint32_t idx = *(next_structural++);
SIMDJSON_IF_CONSTEXPR (UNPADDED) {
if (simdjson_unlikely(idx >= dom_parser.len)) {
@@ -29031,6 +28736,30 @@ struct tape_builder {
return iter.walk_document<STREAMING, false>(builder);
}
}
+
+ // The stage 2 entry points of dom::document_stream, which picks one per batch
+ // (see internal::dom_parser_implementation::stage2_next_function). Out of
+ // line, so that stage2_next() can share the code.
+ simdjson_warn_unused static simdjson_never_inline error_code parse_next_document(
+ internal::dom_parser_implementation &dom_parser, dom::document &doc) noexcept {
+ return parse_document<true>(static_cast<dom_parser_implementation &>(dom_parser), doc);
+ }
+
+ // A document of a stream batch that stage 2 must bound to its structural
+ // indexes (see internal::dom_parser_implementation::_bounded_batch).
+ simdjson_warn_unused static simdjson_never_inline error_code parse_next_bounded_document(
+ internal::dom_parser_implementation &_dom_parser, dom::document &doc) noexcept {
+ dom_parser_implementation &dom_parser = static_cast<dom_parser_implementation &>(_dom_parser);
+ dom_parser.doc = &doc;
+ json_iterator iter(dom_parser, dom_parser.next_structural_index);
+ if (dom_parser._unpadded) {
+ tape_builder_impl<true> builder(doc);
+ return iter.walk_document<true, true, true>(builder);
+ } else {
+ tape_builder_impl<false> builder(doc);
+ return iter.walk_document<true, false, true>(builder);
+ }
+ }
};
template <bool UNPADDED>
@@ -29441,14 +29170,6 @@ simdjson_inline bool is_ascii(const simd8x64<uint8_t>& input) {
return input.reduce_or().is_ascii();
}
-simdjson_unused simdjson_inline simd8<bool> must_be_continuation(const simd8<uint8_t> prev1, const simd8<uint8_t> prev2, const simd8<uint8_t> prev3) {
- simd8<uint8_t> is_second_byte = prev1.saturating_sub(0xc0u-1); // Only 11______ will be > 0
- simd8<uint8_t> is_third_byte = prev2.saturating_sub(0xe0u-1); // Only 111_____ will be > 0
- simd8<uint8_t> is_fourth_byte = prev3.saturating_sub(0xf0u-1); // Only 1111____ will be > 0
- // Caller requires a bool (all 1's). All values resulting from the subtraction will be <= 64, so signed comparison is fine.
- return simd8<int8_t>(is_second_byte | is_third_byte | is_fourth_byte) > int8_t(0);
-}
-
simdjson_inline simd8<uint8_t> must_be_2_3_continuation(const simd8<uint8_t> prev2, const simd8<uint8_t> prev3) {
simd8<uint8_t> is_third_byte = prev2.saturating_sub(0xe0u-0x80); // Only 111_____ will be >= 0x80
simd8<uint8_t> is_fourth_byte = prev3.saturating_sub(0xf0u-0x80); // Only 1111____ will be >= 0x80
@@ -29488,7 +29209,11 @@ simdjson_warn_unused error_code dom_parser_implementation::stage2(dom::document
}
simdjson_warn_unused error_code dom_parser_implementation::stage2_next(dom::document &_doc) noexcept {
- return stage2::tape_builder::parse_document<true>(*this, _doc);
+ return stage2::tape_builder::parse_next_document(*this, _doc);
+}
+
+internal::dom_parser_implementation::stage2_next_function_t dom_parser_implementation::stage2_next_function(bool bounded) const noexcept {
+ return bounded ? stage2::tape_builder::parse_next_bounded_document : stage2::tape_builder::parse_next_document;
}
SIMDJSON_NO_SANITIZE_MEMORY
@@ -30157,34 +29882,6 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin
}
-
-struct escaping {
- static constexpr uint32_t BYTES_PROCESSED = 64;
- simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
-
- simdjson_inline bool has_escape() { return escape_bits != 0; }
- simdjson_inline int escape_index() { return trailing_zeroes(uint64_t(escape_bits)); }
-
- __mmask64 escape_bits;
-}; // struct escaping
-
-
-
-simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
- static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes");
- simd8<uint8_t> v(src);
- v.store(dst);
- __mmask64 is_quote = _mm512_cmpeq_epi8_mask(v, _mm512_set1_epi8('"'));
- __mmask64 is_backslash = _mm512_cmpeq_epi8_mask(v, _mm512_set1_epi8('\\'));
- __mmask64 is_control = _mm512_cmplt_epi8_mask(v, _mm512_set1_epi8(32));
- return {
- (is_backslash | is_quote | is_control)
- };
-}
-
-
-
-
} // unnamed namespace
} // namespace icelake
} // namespace simdjson
@@ -30635,6 +30332,7 @@ public:
simdjson_warn_unused error_code stage1(const uint8_t *buf, size_t len, stage1_mode partial) noexcept final;
simdjson_warn_unused error_code stage2(dom::document &doc) noexcept final;
simdjson_warn_unused error_code stage2_next(dom::document &doc) noexcept final;
+ stage2_next_function_t stage2_next_function(bool bounded) const noexcept final;
simdjson_warn_unused uint8_t *parse_string(const uint8_t *src, uint8_t *dst, bool allow_replacement) const noexcept final;
simdjson_warn_unused uint8_t *parse_wobbly_string(const uint8_t *src, uint8_t *dst) const noexcept final;
inline simdjson_warn_unused error_code set_capacity(size_t capacity) noexcept final;
@@ -31396,21 +31094,6 @@ static bool parse_float_fallback(const uint8_t *ptr, double *outDouble) {
return !(*outDouble > (std::numeric_limits<double>::max)() || *outDouble < std::numeric_limits<double>::lowest());
}
-static bool parse_float_fallback(const uint8_t *ptr, const uint8_t *end_ptr, double *outDouble) {
- *outDouble = simdjson::internal::from_chars(reinterpret_cast<const char *>(ptr), reinterpret_cast<const char *>(end_ptr));
- // We do not accept infinite values.
-
- // Detecting finite values in a portable manner is ridiculously hard, ideally
- // we would want to do:
- // return !std::isfinite(*outDouble);
- // but that mysteriously fails under legacy/old libc++ libraries, see
- // https://github.com/simdjson/simdjson/issues/1286
- //
- // Therefore, fall back to this solution (the extra parens are there
- // to handle that max may be a macro on windows).
- return !(*outDouble > (std::numeric_limits<double>::max)() || *outDouble < std::numeric_limits<double>::lowest());
-}
-
// Same as parse_float_fallback, but for binary32 (float) values. Going through
// the binary64 fallback and then rounding to binary32 would be subject to
// double rounding, so we run the fallback algorithm directly on binary32.
@@ -31956,57 +31639,6 @@ simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned(const u
return i;
}
-
-
-// Parse any number from 0 to 18,446,744,073,709,551,615
-// Never read at src_end or beyond
-simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned(const uint8_t * const src, const uint8_t * const src_end) noexcept {
- const uint8_t *p = src;
- //
- // Parse the integer part.
- //
- // PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
- const uint8_t *const start_digits = p;
- uint64_t i = 0;
- while ((p != src_end) && parse_digit(*p, i)) { p++; }
-
- // If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
- // Optimization note: size_t is expected to be unsigned.
- size_t digit_count = size_t(p - start_digits);
- // The longest positive 64-bit number is 20 digits.
- // We do it this way so we don't trigger this branch unless we must.
- // Optimization note: the compiler can probably merge
- // ((digit_count == 0) || (digit_count > 20))
- // into a single branch since digit_count is unsigned.
- if ((digit_count == 0) || (digit_count > 20)) { return INCORRECT_TYPE; }
- // Here digit_count > 0.
- if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
- // We can do the following...
- // if (!jsoncharutils::is_structural_or_whitespace(*p)) {
- // return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
- // }
- // as a single table lookup:
- if ((p != src_end) && integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); }
-
- if (digit_count == 20) {
- // Positive overflow check:
- // - A 20 digit number starting with 2-9 is overflow, because 18,446,744,073,709,551,615 is the
- // biggest uint64_t.
- // - A 20 digit number starting with 1 is overflow if it is less than INT64_MAX.
- // If we got here, it's a 20 digit number starting with the digit "1".
- // - If a 20 digit number starting with 1 overflowed (i*10+digit), the result will be smaller
- // than 1,553,255,926,290,448,384.
- // - That is smaller than the smallest possible 20-digit number the user could write:
- // 10,000,000,000,000,000,000.
- // - Therefore, if the number is positive and lower than that, it's overflow.
- // - The value we are looking at is less than or equal to INT64_MAX.
- //
- if (src[0] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INCORRECT_TYPE; }
- }
-
- return i;
-}
-
// Parse any number from 0 to 18,446,744,073,709,551,615
simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned_in_string(const uint8_t * const src) noexcept {
const uint8_t *p = src + 1;
@@ -32099,50 +31731,6 @@ simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer(const uin
return negative ? (~i+1) : i;
}
-// Parse any number from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
-// Never read at src_end or beyond
-simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer(const uint8_t * const src, const uint8_t * const src_end) noexcept {
- //
- // Check for minus sign
- //
- if(src == src_end) { return NUMBER_ERROR; }
- bool negative = (*src == '-');
- const uint8_t *p = src + uint8_t(negative);
-
- //
- // Parse the integer part.
- //
- // PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
- const uint8_t *const start_digits = p;
- uint64_t i = 0;
- while ((p != src_end) && parse_digit(*p, i)) { p++; }
-
- // If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
- // Optimization note: size_t is expected to be unsigned.
- size_t digit_count = size_t(p - start_digits);
- // We go from
- // -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
- // so we can never represent numbers that have more than 19 digits.
- size_t longest_digit_count = 19;
- // Optimization note: the compiler can probably merge
- // ((digit_count == 0) || (digit_count > longest_digit_count))
- // into a single branch since digit_count is unsigned.
- if ((digit_count == 0) || (digit_count > longest_digit_count)) { return INCORRECT_TYPE; }
- // Here digit_count > 0.
- if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
- // We can do the following...
- // if (!jsoncharutils::is_structural_or_whitespace(*p)) {
- // return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
- // }
- // as a single table lookup:
- if((p != src_end) && integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); }
- // Negative numbers have can go down to - INT64_MAX - 1 whereas positive numbers are limited to INT64_MAX.
- // Performance note: This check is only needed when digit_count == longest_digit_count but it is
- // so cheap that we might as well always make it.
- if(i > uint64_t(INT64_MAX) + uint64_t(negative)) { return INCORRECT_TYPE; }
- return negative ? (~i+1) : i;
-}
-
// Parse any number from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer_in_string(const uint8_t *src) noexcept {
//
@@ -32248,8 +31836,11 @@ simdjson_unused simdjson_inline simdjson_result<double> parse_double(const uint8
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
+ // no exp digits
+ if (p-start_exp_digits == 0) { return NUMBER_ERROR; }
+ // 20+ exp digits (e.g., 0e9999999999999999999999) are valid, but exp may
+ // have wrapped around: leave such numbers to the fallback parser.
+ overflow = overflow || p-start_exp_digits > 19;
exponent += exp_neg ? 0-exp : exp;
}
@@ -32339,8 +31930,11 @@ simdjson_unused simdjson_inline simdjson_result<float> parse_float(const uint8_t
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
+ // no exp digits
+ if (p-start_exp_digits == 0) { return NUMBER_ERROR; }
+ // 20+ exp digits (e.g., 0e9999999999999999999999) are valid, but exp may
+ // have wrapped around: leave such numbers to the fallback parser.
+ overflow = overflow || p-start_exp_digits > 19;
exponent += exp_neg ? 0-exp : exp;
}
@@ -32420,88 +32014,6 @@ simdjson_unused simdjson_inline simdjson_result<number_type> get_number_type(con
return number_type::floating_point_number;
}
-// Never read at src_end or beyond
-simdjson_unused simdjson_inline simdjson_result<double> parse_double(const uint8_t * src, const uint8_t * const src_end) noexcept {
- if(src == src_end) { return NUMBER_ERROR; }
- //
- // Check for minus sign
- //
- bool negative = (*src == '-');
- src += uint8_t(negative);
-
- //
- // Parse the integer part.
- //
- uint64_t i = 0;
- const uint8_t *p = src;
- if(p == src_end) { return NUMBER_ERROR; }
- p += parse_digit(*p, i);
- bool leading_zero = (i == 0);
- while ((p != src_end) && parse_digit(*p, i)) { p++; }
- // no integer digits, or 0123 (zero must be solo)
- if ( p == src ) { return INCORRECT_TYPE; }
- if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; }
-
- //
- // Parse the decimal part.
- //
- int64_t exponent = 0;
- bool overflow;
- if (simdjson_likely((p != src_end) && (*p == '.'))) {
- p++;
- const uint8_t *start_decimal_digits = p;
- if ((p == src_end) || !parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits
- p++;
- while ((p != src_end) && parse_digit(*p, i)) { p++; }
- exponent = -(p - start_decimal_digits);
-
- // Overflow check. More than 19 digits (minus the decimal) may be overflow.
- overflow = p-src-1 > 19;
- if (simdjson_unlikely(overflow && leading_zero)) {
- // Skip leading 0.00000 and see if it still overflows
- const uint8_t *start_digits = src + 2;
- while (*start_digits == '0') { start_digits++; }
- overflow = start_digits-src > 19;
- }
- } else {
- overflow = p-src > 19;
- }
-
- //
- // Parse the exponent
- //
- if ((p != src_end) && (*p == 'e' || *p == 'E')) {
- p++;
- if(p == src_end) { return NUMBER_ERROR; }
- bool exp_neg = *p == '-';
- p += exp_neg || *p == '+';
-
- uint64_t exp = 0;
- const uint8_t *start_exp_digits = p;
- while ((p != src_end) && parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
-
- exponent += exp_neg ? 0-exp : exp;
- }
-
- if ((p != src_end) && jsoncharutils::is_not_structural_or_whitespace(*p)) { return NUMBER_ERROR; }
-
- overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power;
-
- //
- // Assemble (or slow-parse) the float
- //
- double d;
- if (simdjson_likely(!overflow)) {
- if (compute_float_64(exponent, i, negative, d)) { return d; }
- }
- if (!parse_float_fallback(src - uint8_t(negative), src_end, &d)) {
- return NUMBER_ERROR;
- }
- return d;
-}
-
simdjson_unused simdjson_inline simdjson_result<double> parse_double_in_string(const uint8_t * src) noexcept {
//
// Check for minus sign
@@ -32570,8 +32082,11 @@ simdjson_unused simdjson_inline simdjson_result<double> parse_double_in_string(c
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
+ // no exp digits
+ if (p-start_exp_digits == 0) { return NUMBER_ERROR; }
+ // 20+ exp digits (e.g., 0e9999999999999999999999) are valid, but exp may
+ // have wrapped around: leave such numbers to the fallback parser.
+ overflow = overflow || p-start_exp_digits > 19;
exponent += exp_neg ? 0-exp : exp;
}
@@ -32663,8 +32178,11 @@ simdjson_unused simdjson_inline simdjson_result<float> parse_float_in_string(con
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
+ // no exp digits
+ if (p-start_exp_digits == 0) { return NUMBER_ERROR; }
+ // 20+ exp digits (e.g., 0e9999999999999999999999) are valid, but exp may
+ // have wrapped around: leave such numbers to the fallback parser.
+ overflow = overflow || p-start_exp_digits > 19;
exponent += exp_neg ? 0-exp : exp;
}
@@ -33506,34 +33024,6 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin
}
-
-struct escaping {
- static constexpr uint32_t BYTES_PROCESSED = 64;
- simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
-
- simdjson_inline bool has_escape() { return escape_bits != 0; }
- simdjson_inline int escape_index() { return trailing_zeroes(uint64_t(escape_bits)); }
-
- __mmask64 escape_bits;
-}; // struct escaping
-
-
-
-simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
- static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes");
- simd8<uint8_t> v(src);
- v.store(dst);
- __mmask64 is_quote = _mm512_cmpeq_epi8_mask(v, _mm512_set1_epi8('"'));
- __mmask64 is_backslash = _mm512_cmpeq_epi8_mask(v, _mm512_set1_epi8('\\'));
- __mmask64 is_control = _mm512_cmplt_epi8_mask(v, _mm512_set1_epi8(32));
- return {
- (is_backslash | is_quote | is_control)
- };
-}
-
-
-
-
} // unnamed namespace
} // namespace icelake
} // namespace simdjson
@@ -33759,6 +33249,17 @@ public:
* @return the number of effective characters in the last block.
*/
simdjson_inline size_t get_remainder(uint8_t *dst) const;
+ /**
+ * Like get_remainder, for input followed by SIMDJSON_PADDING readable bytes.
+ *
+ * get_remainder writes the block with a memset and a memcpy of the remaining
+ * length, and the vector loads that read it back cannot be forwarded from
+ * those narrower stores: they wait until the stores reach the cache, which
+ * costs tens of cycles on every document. Here the block is put together in
+ * vector registers, from loads that may read into the padding, and written
+ * with full-width stores that the loads can forward from.
+ */
+ simdjson_inline size_t get_remainder_padded(uint8_t *dst) const;
simdjson_inline void advance();
private:
const uint8_t *buf;
@@ -33767,47 +33268,6 @@ private:
size_t idx;
};
-// Routines to print masks and text for debugging bitmask operations
-simdjson_unused static char * format_input_text_64(const uint8_t *text) {
- static char buf[sizeof(simd8x64<uint8_t>) + 1];
- for (size_t i=0; i<sizeof(simd8x64<uint8_t>); i++) {
- buf[i] = int8_t(text[i]) < ' ' ? '_' : int8_t(text[i]);
- }
- buf[sizeof(simd8x64<uint8_t>)] = '\0';
- return buf;
-}
-
-// Routines to print masks and text for debugging bitmask operations
-simdjson_unused static char * format_input_text(const simd8x64<uint8_t>& in) {
- static char buf[sizeof(simd8x64<uint8_t>) + 1];
- in.store(reinterpret_cast<uint8_t*>(buf));
- for (size_t i=0; i<sizeof(simd8x64<uint8_t>); i++) {
- if (buf[i] < ' ') { buf[i] = '_'; }
- }
- buf[sizeof(simd8x64<uint8_t>)] = '\0';
- return buf;
-}
-
-simdjson_unused static char * format_input_text(const simd8x64<uint8_t>& in, uint64_t mask) {
- static char buf[sizeof(simd8x64<uint8_t>) + 1];
- in.store(reinterpret_cast<uint8_t*>(buf));
- for (size_t i=0; i<sizeof(simd8x64<uint8_t>); i++) {
- if (buf[i] <= ' ') { buf[i] = '_'; }
- if (!(mask & (size_t(1) << i))) { buf[i] = ' '; }
- }
- buf[sizeof(simd8x64<uint8_t>)] = '\0';
- return buf;
-}
-
-simdjson_unused static char * format_mask(uint64_t mask) {
- static char buf[sizeof(simd8x64<uint8_t>) + 1];
- for (size_t i=0; i<64; i++) {
- buf[i] = (mask & (size_t(1) << i)) ? 'X' : ' ';
- }
- buf[64] = '\0';
- return buf;
-}
-
template<size_t STEP_SIZE>
simdjson_inline buf_block_reader<STEP_SIZE>::buf_block_reader(const uint8_t *_buf, size_t _len) : buf{_buf}, len{_len}, lenminusstep{len < STEP_SIZE ? 0 : len - STEP_SIZE}, idx{0} {}
@@ -33832,6 +33292,38 @@ simdjson_inline size_t buf_block_reader<STEP_SIZE>::get_remainder(uint8_t *dst)
return len - idx;
}
+// keep_mask[128 - n + i] is 0xFF for i < n and 0 otherwise.
+alignas(64) static const uint8_t keep_mask[256] = {
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+template<size_t STEP_SIZE>
+simdjson_inline size_t buf_block_reader<STEP_SIZE>::get_remainder_padded(uint8_t *dst) const {
+ static_assert(STEP_SIZE <= 128, "keep_mask covers blocks of up to 128 bytes");
+ static_assert(sizeof(simd::simd8<uint8_t>) <= SIMDJSON_PADDING, "a vector load at the last byte must stay in the padding");
+ const size_t remaining = len - idx;
+ if (remaining == 0) { return 0; }
+ const simd::simd8<uint8_t> spaces = simd::simd8<uint8_t>::splat(0x20);
+ for (size_t off = 0; off < STEP_SIZE; off += sizeof(simd::simd8<uint8_t>)) {
+ simd::simd8<uint8_t> v = spaces;
+ if (off < remaining) {
+ // Starts before len, so it ends within the padding.
+ const simd::simd8<uint8_t> in = simd::simd8<uint8_t>::load(buf + idx + off);
+ const simd::simd8<uint8_t> keep = simd::simd8<uint8_t>::load(keep_mask + 128 - remaining + off);
+ v = spaces ^ ((in ^ spaces) & keep);
+ }
+ v.store(dst + off);
+ }
+ return remaining;
+}
+
template<size_t STEP_SIZE>
simdjson_inline void buf_block_reader<STEP_SIZE>::advance() {
idx += STEP_SIZE;
@@ -34532,6 +34024,20 @@ simdjson_inline bool ends_with_partial_scalar(dom_parser_implementation &parser,
e != ' ' && e != '\t' && e != '\n' && e != '\r';
}
+/**
+ * The structural index past the last one of a partial batch is the end of this
+ * window, not a token of it: the byte there is the first byte of the next
+ * window. A finished value must not be rejected because that byte is ',', ':',
+ * ']' or '}'. A last document that itself ends with '{', '[', ',' or ':' is
+ * invalid JSON, yet its brackets may balance: walking it, the On-Demand API
+ * could read the next batch as a value. We leave such a document to the next
+ * batch, so that the batch ends between two documents.
+ */
+simdjson_inline bool ends_with_unfinished_document(dom_parser_implementation &parser) {
+ const uint8_t last = parser.buf[parser.structural_indexes[parser.n_structural_indexes - 1]];
+ return last == '{' || last == '[' || last == ',' || last == ':';
+}
+
simdjson_inline uint32_t find_next_document_index(dom_parser_implementation &parser, bool defer_last = false) {
// Variant: do not count separately, just figure out depth
if(parser.n_structural_indexes == 0) { return 0; }
@@ -35213,7 +34719,8 @@ error_code json_structural_indexer::index(const uint8_t *buf, size_t len, dom_pa
// Take care of the last block (will always be there unless file is empty which is
// not supposed to happen.)
uint8_t block[STEP_SIZE];
- if (simdjson_unlikely(reader.get_remainder(block) == 0)) { return UNEXPECTED_ERROR; }
+ const size_t remaining = parser._unpadded ? reader.get_remainder(block) : reader.get_remainder_padded(block);
+ if (simdjson_unlikely(remaining == 0)) { return UNEXPECTED_ERROR; }
indexer.step<STEP_SIZE>(block, reader);
return indexer.finish(parser, reader.block_index(), len, partial);
}
@@ -35247,6 +34754,20 @@ simdjson_inline void json_structural_indexer::next(const simd::simd8x64<uint8_t>
unescaped_chars_error |= block.non_quote_inside_string(unescaped);
}
+// Drops the unclosed string that ends a json_sequence batch and leaves its
+// start in structural_indexes[n_structural_indexes]. The last index is its
+// opening quote, unless the quote directly follows an RS: the scanner reads
+// RS as a scalar byte, so it indexes the RS and not the quote. Keep the RS.
+simdjson_inline void drop_json_sequence_unclosed_string(dom_parser_implementation &parser, size_t len) noexcept {
+ uint32_t quote = parser.structural_indexes[parser.n_structural_indexes - 1];
+ if (parser.buf[quote] == '"') {
+ parser.n_structural_indexes--;
+ return;
+ }
+ while (quote < len && parser.buf[quote] != '"') { quote++; }
+ parser.structural_indexes[parser.n_structural_indexes] = quote;
+}
+
simdjson_inline error_code json_structural_indexer::finish(dom_parser_implementation &parser, size_t idx, size_t len, stage1_mode partial) {
// Write out the final iteration's structurals
indexer.write(uint32_t(idx-64), prev_structurals);
@@ -35303,8 +34824,13 @@ simdjson_inline error_code json_structural_indexer::finish(dom_parser_implementa
if (simdjson_unlikely(parser.n_structural_indexes == 0u)) { return CAPACITY; }
}
// We truncate the input to the end of the last complete document (or zero).
- auto new_structural_indexes = find_next_document_index(parser, !have_unclosed_string && ends_with_partial_scalar(parser, len));
+ const bool partial_scalar = !have_unclosed_string && ends_with_partial_scalar(parser, len);
+ const bool unfinished = ends_with_unfinished_document(parser);
+ auto new_structural_indexes = find_next_document_index(parser, partial_scalar || unfinished);
if (new_structural_indexes == 0 && parser.n_structural_indexes > 0) {
+ // The batch holds a single document, whose brackets balance but which
+ // cannot be valid JSON: it is not too big, it is invalid.
+ if (unfinished && find_next_document_index(parser, partial_scalar) != 0) { return TAPE_ERROR; }
if(parser.structural_indexes[0] == 0) {
// If the buffer is partial and we started at index 0 but the document is
// incomplete, it's too big to parse.
@@ -35348,7 +34874,7 @@ simdjson_inline error_code json_structural_indexer::finish(dom_parser_implementa
// A discarded unclosed string also caps how far the filter may scan.
size_t scan_len = len;
if(have_unclosed_string) {
- parser.n_structural_indexes--;
+ drop_json_sequence_unclosed_string(parser, len);
if (simdjson_unlikely(parser.n_structural_indexes == 0u)) { return CAPACITY; }
scan_len = parser.structural_indexes[parser.n_structural_indexes];
}
@@ -35367,12 +34893,17 @@ simdjson_inline error_code json_structural_indexer::finish(dom_parser_implementa
}
parser.n_structural_indexes = new_structural_indexes;
parser.structural_indexes[parser.n_structural_indexes] = next_batch_start;
+ // The next batch starts at an RS, which the On-Demand API never reads as a
+ // string, a container or a separator. But an invalid last document may make
+ // it skip that RS as a value: repeat it, so that the index past it is not
+ // the next record, which can be a string that does not close in this batch.
+ parser.structural_indexes[parser.n_structural_indexes + 1] = next_batch_start;
} else if (partial == stage1_mode::json_sequence_final) {
// RFC 7464: final batch, last document extends to EOF
// As above: a discarded unclosed string caps how far the filter may scan.
size_t scan_len = len;
if(have_unclosed_string) {
- parser.n_structural_indexes--;
+ drop_json_sequence_unclosed_string(parser, len);
scan_len = parser.structural_indexes[parser.n_structural_indexes];
}
uint32_t next_batch_start = uint32_t(len);
@@ -35790,7 +35321,7 @@ public:
*
* - increment_count(iter) - each time a value is found in an array or object.
*/
- template<bool STREAMING, bool UNPADDED, typename V>
+ template<bool STREAMING, bool UNPADDED, bool BOUNDED = false, typename V>
simdjson_warn_unused simdjson_inline error_code walk_document(V &visitor) noexcept;
/**
@@ -35807,8 +35338,8 @@ public:
*
* They may include invalid JSON as well (such as `1.2.3` or `ture`).
*/
- template<bool UNPADDED>
- simdjson_inline const uint8_t *peek() const noexcept;
+ template<bool UNPADDED, bool BOUNDED = false>
+ simdjson_inline const uint8_t *peek(const uint32_t *batch_end = nullptr) const noexcept;
/**
* Advance to the next token.
*
@@ -35816,8 +35347,8 @@ public:
*
* They may include invalid JSON as well (such as `1.2.3` or `ture`).
*/
- template<bool UNPADDED>
- simdjson_inline const uint8_t *advance() noexcept;
+ template<bool UNPADDED, bool BOUNDED = false>
+ simdjson_inline const uint8_t *advance(const uint32_t *batch_end = nullptr) noexcept;
/**
* Get the remaining length of the document, from the start of the current token.
*/
@@ -35865,7 +35396,7 @@ public:
simdjson_warn_unused simdjson_inline error_code visit_primitive(V &visitor, const uint8_t *value) noexcept;
};
-template<bool STREAMING, bool UNPADDED, typename V>
+template<bool STREAMING, bool UNPADDED, bool BOUNDED, typename V>
simdjson_warn_unused simdjson_inline error_code json_iterator::walk_document(V &visitor) noexcept {
logger::log_start();
@@ -35873,6 +35404,9 @@ simdjson_warn_unused simdjson_inline error_code json_iterator::walk_document(V &
// Start the document
//
if (at_eof()) { return EMPTY; }
+ // Only used when BOUNDED (see advance()); a local, so that it can stay in a
+ // register, and vanishes from the other walks.
+ const uint32_t *const batch_end = &dom_parser.structural_indexes[dom_parser.n_structural_indexes];
log_start_value("document");
SIMDJSON_TRY( visitor.visit_document_start(*this) );
@@ -35880,7 +35414,7 @@ simdjson_warn_unused simdjson_inline error_code json_iterator::walk_document(V &
// Read first value
//
{
- auto value = advance<UNPADDED>();
+ auto value = advance<UNPADDED, BOUNDED>(batch_end);
// Make sure the outer object or array is closed before continuing; otherwise, there are ways we
// could get into memory corruption. See https://github.com/simdjson/simdjson/issues/906
@@ -35892,8 +35426,8 @@ simdjson_warn_unused simdjson_inline error_code json_iterator::walk_document(V &
}
switch (*value) {
- case '{': if (*peek<UNPADDED>() == '}') { advance<UNPADDED>(); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin;
- case '[': if (*peek<UNPADDED>() == ']') { advance<UNPADDED>(); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin;
+ case '{': if (*peek<UNPADDED, BOUNDED>(batch_end) == '}') { advance<UNPADDED, BOUNDED>(batch_end); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin;
+ case '[': if (*peek<UNPADDED, BOUNDED>(batch_end) == ']') { advance<UNPADDED, BOUNDED>(batch_end); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin;
default: SIMDJSON_TRY( visitor.visit_root_primitive(*this, value) ); break;
}
}
@@ -35910,29 +35444,29 @@ object_begin:
SIMDJSON_TRY( visitor.visit_object_start(*this) );
{
- auto key = advance<UNPADDED>();
+ auto key = advance<UNPADDED, BOUNDED>(batch_end);
if (*key != '"') { log_error("Object does not start with a key"); return TAPE_ERROR; }
SIMDJSON_TRY( visitor.increment_count(*this) );
SIMDJSON_TRY( visitor.visit_key(*this, key) );
}
object_field:
- if (simdjson_unlikely( *advance<UNPADDED>() != ':' )) { log_error("Missing colon after key in object"); return TAPE_ERROR; }
+ if (simdjson_unlikely( (*advance<UNPADDED, BOUNDED>(batch_end) != ':') )) { log_error("Missing colon after key in object"); return TAPE_ERROR; }
{
- auto value = advance<UNPADDED>();
+ auto value = advance<UNPADDED, BOUNDED>(batch_end);
switch (*value) {
- case '{': if (*peek<UNPADDED>() == '}') { advance<UNPADDED>(); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin;
- case '[': if (*peek<UNPADDED>() == ']') { advance<UNPADDED>(); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin;
+ case '{': if (*peek<UNPADDED, BOUNDED>(batch_end) == '}') { advance<UNPADDED, BOUNDED>(batch_end); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin;
+ case '[': if (*peek<UNPADDED, BOUNDED>(batch_end) == ']') { advance<UNPADDED, BOUNDED>(batch_end); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin;
default: SIMDJSON_TRY( visitor.visit_primitive(*this, value) ); break;
}
}
object_continue:
- switch (*advance<UNPADDED>()) {
+ switch (*advance<UNPADDED, BOUNDED>(batch_end)) {
case ',':
SIMDJSON_TRY( visitor.increment_count(*this) );
{
- auto key = advance<UNPADDED>();
+ auto key = advance<UNPADDED, BOUNDED>(batch_end);
if (simdjson_unlikely( *key != '"' )) { log_error("Key string missing at beginning of field in object"); return TAPE_ERROR; }
SIMDJSON_TRY( visitor.visit_key(*this, key) );
}
@@ -35960,16 +35494,16 @@ array_begin:
array_value:
{
- auto value = advance<UNPADDED>();
+ auto value = advance<UNPADDED, BOUNDED>(batch_end);
switch (*value) {
- case '{': if (*peek<UNPADDED>() == '}') { advance<UNPADDED>(); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin;
- case '[': if (*peek<UNPADDED>() == ']') { advance<UNPADDED>(); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin;
+ case '{': if (*peek<UNPADDED, BOUNDED>(batch_end) == '}') { advance<UNPADDED, BOUNDED>(batch_end); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin;
+ case '[': if (*peek<UNPADDED, BOUNDED>(batch_end) == ']') { advance<UNPADDED, BOUNDED>(batch_end); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin;
default: SIMDJSON_TRY( visitor.visit_primitive(*this, value) ); break;
}
}
array_continue:
- switch (*advance<UNPADDED>()) {
+ switch (*advance<UNPADDED, BOUNDED>(batch_end)) {
case ',': SIMDJSON_TRY( visitor.increment_count(*this) ); goto array_value;
case ']': log_end_value("array"); SIMDJSON_TRY( visitor.visit_array_end(*this) ); goto scope_end;
default: log_error("Missing comma between array values"); return TAPE_ERROR;
@@ -35999,8 +35533,18 @@ simdjson_inline json_iterator::json_iterator(dom_parser_implementation &_dom_par
// Stage 1 leaves a sentinel index of value `len`; reading buf[len] requires
// padding. For UNPADDED, return a dummy so walk_document can fail cleanly (#2815).
-template<bool UNPADDED>
-simdjson_inline const uint8_t *json_iterator::peek() const noexcept {
+//
+// When BOUNDED (see dom_parser_implementation::_bounded_batch), a document may
+// not run past batch_end, one past the last structural index of the batch:
+// past it, we return the same dummy, and walk_document fails cleanly.
+template<bool UNPADDED, bool BOUNDED>
+simdjson_inline const uint8_t *json_iterator::peek(const uint32_t *batch_end) const noexcept {
+ SIMDJSON_IF_CONSTEXPR (BOUNDED) {
+ if (simdjson_unlikely(next_structural >= batch_end)) {
+ static constexpr uint8_t streaming_eof_sentinel = 0;
+ return &streaming_eof_sentinel;
+ }
+ }
const uint32_t idx = *(next_structural);
SIMDJSON_IF_CONSTEXPR (UNPADDED) {
if (simdjson_unlikely(idx >= dom_parser.len)) {
@@ -36010,8 +35554,14 @@ simdjson_inline const uint8_t *json_iterator::peek() const noexcept {
}
return &buf[idx];
}
-template<bool UNPADDED>
-simdjson_inline const uint8_t *json_iterator::advance() noexcept {
+template<bool UNPADDED, bool BOUNDED>
+simdjson_inline const uint8_t *json_iterator::advance(const uint32_t *batch_end) noexcept {
+ SIMDJSON_IF_CONSTEXPR (BOUNDED) {
+ if (simdjson_unlikely(next_structural >= batch_end)) {
+ static constexpr uint8_t streaming_eof_sentinel = 0;
+ return &streaming_eof_sentinel;
+ }
+ }
const uint32_t idx = *(next_structural++);
SIMDJSON_IF_CONSTEXPR (UNPADDED) {
if (simdjson_unlikely(idx >= dom_parser.len)) {
@@ -36570,6 +36120,30 @@ struct tape_builder {
return iter.walk_document<STREAMING, false>(builder);
}
}
+
+ // The stage 2 entry points of dom::document_stream, which picks one per batch
+ // (see internal::dom_parser_implementation::stage2_next_function). Out of
+ // line, so that stage2_next() can share the code.
+ simdjson_warn_unused static simdjson_never_inline error_code parse_next_document(
+ internal::dom_parser_implementation &dom_parser, dom::document &doc) noexcept {
+ return parse_document<true>(static_cast<dom_parser_implementation &>(dom_parser), doc);
+ }
+
+ // A document of a stream batch that stage 2 must bound to its structural
+ // indexes (see internal::dom_parser_implementation::_bounded_batch).
+ simdjson_warn_unused static simdjson_never_inline error_code parse_next_bounded_document(
+ internal::dom_parser_implementation &_dom_parser, dom::document &doc) noexcept {
+ dom_parser_implementation &dom_parser = static_cast<dom_parser_implementation &>(_dom_parser);
+ dom_parser.doc = &doc;
+ json_iterator iter(dom_parser, dom_parser.next_structural_index);
+ if (dom_parser._unpadded) {
+ tape_builder_impl<true> builder(doc);
+ return iter.walk_document<true, true, true>(builder);
+ } else {
+ tape_builder_impl<false> builder(doc);
+ return iter.walk_document<true, false, true>(builder);
+ }
+ }
};
template <bool UNPADDED>
@@ -36979,14 +36553,6 @@ simdjson_inline bool is_ascii(const simd8x64<uint8_t>& input) {
return input.reduce_or().is_ascii();
}
-simdjson_unused simdjson_inline simd8<bool> must_be_continuation(const simd8<uint8_t> prev1, const simd8<uint8_t> prev2, const simd8<uint8_t> prev3) {
- simd8<uint8_t> is_second_byte = prev1.saturating_sub(0xc0u-1); // Only 11______ will be > 0
- simd8<uint8_t> is_third_byte = prev2.saturating_sub(0xe0u-1); // Only 111_____ will be > 0
- simd8<uint8_t> is_fourth_byte = prev3.saturating_sub(0xf0u-1); // Only 1111____ will be > 0
- // Caller requires a bool (all 1's). All values resulting from the subtraction will be <= 64, so signed comparison is fine.
- return simd8<int8_t>(is_second_byte | is_third_byte | is_fourth_byte) > int8_t(0);
-}
-
simdjson_inline simd8<uint8_t> must_be_2_3_continuation(const simd8<uint8_t> prev2, const simd8<uint8_t> prev3) {
simd8<uint8_t> is_third_byte = prev2.saturating_sub(0xe0u-0x80); // Only 111_____ will be >= 0x80
simd8<uint8_t> is_fourth_byte = prev3.saturating_sub(0xf0u-0x80); // Only 1111____ will be >= 0x80
@@ -37070,7 +36636,11 @@ simdjson_warn_unused error_code dom_parser_implementation::stage2(dom::document
}
simdjson_warn_unused error_code dom_parser_implementation::stage2_next(dom::document &_doc) noexcept {
- return stage2::tape_builder::parse_document<true>(*this, _doc);
+ return stage2::tape_builder::parse_next_document(*this, _doc);
+}
+
+internal::dom_parser_implementation::stage2_next_function_t dom_parser_implementation::stage2_next_function(bool bounded) const noexcept {
+ return bounded ? stage2::tape_builder::parse_next_bounded_document : stage2::tape_builder::parse_next_document;
}
SIMDJSON_NO_SANITIZE_MEMORY
@@ -37915,31 +37485,6 @@ backslash_and_quote::copy_and_find(const uint8_t *src, uint8_t *dst) {
}
-struct escaping {
- static constexpr uint32_t BYTES_PROCESSED = 16;
- simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
-
- simdjson_inline bool has_escape() { return escape_bits != 0; }
- simdjson_inline int escape_index() { return trailing_zeroes(escape_bits); }
-
- uint64_t escape_bits;
-}; // struct escaping
-
-
-
-simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
- static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes");
- simd8<uint8_t> v(src);
- v.store(dst);
- simd8<bool> is_quote = (v == '"');
- simd8<bool> is_backslash = (v == '\\');
- simd8<bool> is_control = (v < 32);
- return {
- // We store it as a 64-bit bitmask even though we only need 16 bits.
- uint64_t((is_backslash | is_quote | is_control).to_bitmask())
- };
-}
-
} // unnamed namespace
} // namespace ppc64
} // namespace simdjson
@@ -38332,6 +37877,7 @@ public:
simdjson_warn_unused error_code stage1(const uint8_t *buf, size_t len, stage1_mode partial) noexcept final;
simdjson_warn_unused error_code stage2(dom::document &doc) noexcept final;
simdjson_warn_unused error_code stage2_next(dom::document &doc) noexcept final;
+ stage2_next_function_t stage2_next_function(bool bounded) const noexcept final;
simdjson_warn_unused uint8_t *parse_string(const uint8_t *src, uint8_t *dst, bool allow_replacement) const noexcept final;
simdjson_warn_unused uint8_t *parse_wobbly_string(const uint8_t *src, uint8_t *dst) const noexcept final;
inline simdjson_warn_unused error_code set_capacity(size_t capacity) noexcept final;
@@ -39093,21 +38639,6 @@ static bool parse_float_fallback(const uint8_t *ptr, double *outDouble) {
return !(*outDouble > (std::numeric_limits<double>::max)() || *outDouble < std::numeric_limits<double>::lowest());
}
-static bool parse_float_fallback(const uint8_t *ptr, const uint8_t *end_ptr, double *outDouble) {
- *outDouble = simdjson::internal::from_chars(reinterpret_cast<const char *>(ptr), reinterpret_cast<const char *>(end_ptr));
- // We do not accept infinite values.
-
- // Detecting finite values in a portable manner is ridiculously hard, ideally
- // we would want to do:
- // return !std::isfinite(*outDouble);
- // but that mysteriously fails under legacy/old libc++ libraries, see
- // https://github.com/simdjson/simdjson/issues/1286
- //
- // Therefore, fall back to this solution (the extra parens are there
- // to handle that max may be a macro on windows).
- return !(*outDouble > (std::numeric_limits<double>::max)() || *outDouble < std::numeric_limits<double>::lowest());
-}
-
// Same as parse_float_fallback, but for binary32 (float) values. Going through
// the binary64 fallback and then rounding to binary32 would be subject to
// double rounding, so we run the fallback algorithm directly on binary32.
@@ -39653,57 +39184,6 @@ simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned(const u
return i;
}
-
-
-// Parse any number from 0 to 18,446,744,073,709,551,615
-// Never read at src_end or beyond
-simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned(const uint8_t * const src, const uint8_t * const src_end) noexcept {
- const uint8_t *p = src;
- //
- // Parse the integer part.
- //
- // PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
- const uint8_t *const start_digits = p;
- uint64_t i = 0;
- while ((p != src_end) && parse_digit(*p, i)) { p++; }
-
- // If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
- // Optimization note: size_t is expected to be unsigned.
- size_t digit_count = size_t(p - start_digits);
- // The longest positive 64-bit number is 20 digits.
- // We do it this way so we don't trigger this branch unless we must.
- // Optimization note: the compiler can probably merge
- // ((digit_count == 0) || (digit_count > 20))
- // into a single branch since digit_count is unsigned.
- if ((digit_count == 0) || (digit_count > 20)) { return INCORRECT_TYPE; }
- // Here digit_count > 0.
- if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
- // We can do the following...
- // if (!jsoncharutils::is_structural_or_whitespace(*p)) {
- // return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
- // }
- // as a single table lookup:
- if ((p != src_end) && integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); }
-
- if (digit_count == 20) {
- // Positive overflow check:
- // - A 20 digit number starting with 2-9 is overflow, because 18,446,744,073,709,551,615 is the
- // biggest uint64_t.
- // - A 20 digit number starting with 1 is overflow if it is less than INT64_MAX.
- // If we got here, it's a 20 digit number starting with the digit "1".
- // - If a 20 digit number starting with 1 overflowed (i*10+digit), the result will be smaller
- // than 1,553,255,926,290,448,384.
- // - That is smaller than the smallest possible 20-digit number the user could write:
- // 10,000,000,000,000,000,000.
- // - Therefore, if the number is positive and lower than that, it's overflow.
- // - The value we are looking at is less than or equal to INT64_MAX.
- //
- if (src[0] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INCORRECT_TYPE; }
- }
-
- return i;
-}
-
// Parse any number from 0 to 18,446,744,073,709,551,615
simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned_in_string(const uint8_t * const src) noexcept {
const uint8_t *p = src + 1;
@@ -39796,50 +39276,6 @@ simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer(const uin
return negative ? (~i+1) : i;
}
-// Parse any number from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
-// Never read at src_end or beyond
-simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer(const uint8_t * const src, const uint8_t * const src_end) noexcept {
- //
- // Check for minus sign
- //
- if(src == src_end) { return NUMBER_ERROR; }
- bool negative = (*src == '-');
- const uint8_t *p = src + uint8_t(negative);
-
- //
- // Parse the integer part.
- //
- // PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
- const uint8_t *const start_digits = p;
- uint64_t i = 0;
- while ((p != src_end) && parse_digit(*p, i)) { p++; }
-
- // If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
- // Optimization note: size_t is expected to be unsigned.
- size_t digit_count = size_t(p - start_digits);
- // We go from
- // -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
- // so we can never represent numbers that have more than 19 digits.
- size_t longest_digit_count = 19;
- // Optimization note: the compiler can probably merge
- // ((digit_count == 0) || (digit_count > longest_digit_count))
- // into a single branch since digit_count is unsigned.
- if ((digit_count == 0) || (digit_count > longest_digit_count)) { return INCORRECT_TYPE; }
- // Here digit_count > 0.
- if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
- // We can do the following...
- // if (!jsoncharutils::is_structural_or_whitespace(*p)) {
- // return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
- // }
- // as a single table lookup:
- if((p != src_end) && integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); }
- // Negative numbers have can go down to - INT64_MAX - 1 whereas positive numbers are limited to INT64_MAX.
- // Performance note: This check is only needed when digit_count == longest_digit_count but it is
- // so cheap that we might as well always make it.
- if(i > uint64_t(INT64_MAX) + uint64_t(negative)) { return INCORRECT_TYPE; }
- return negative ? (~i+1) : i;
-}
-
// Parse any number from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer_in_string(const uint8_t *src) noexcept {
//
@@ -39945,8 +39381,11 @@ simdjson_unused simdjson_inline simdjson_result<double> parse_double(const uint8
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
+ // no exp digits
+ if (p-start_exp_digits == 0) { return NUMBER_ERROR; }
+ // 20+ exp digits (e.g., 0e9999999999999999999999) are valid, but exp may
+ // have wrapped around: leave such numbers to the fallback parser.
+ overflow = overflow || p-start_exp_digits > 19;
exponent += exp_neg ? 0-exp : exp;
}
@@ -40036,8 +39475,11 @@ simdjson_unused simdjson_inline simdjson_result<float> parse_float(const uint8_t
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
+ // no exp digits
+ if (p-start_exp_digits == 0) { return NUMBER_ERROR; }
+ // 20+ exp digits (e.g., 0e9999999999999999999999) are valid, but exp may
+ // have wrapped around: leave such numbers to the fallback parser.
+ overflow = overflow || p-start_exp_digits > 19;
exponent += exp_neg ? 0-exp : exp;
}
@@ -40117,88 +39559,6 @@ simdjson_unused simdjson_inline simdjson_result<number_type> get_number_type(con
return number_type::floating_point_number;
}
-// Never read at src_end or beyond
-simdjson_unused simdjson_inline simdjson_result<double> parse_double(const uint8_t * src, const uint8_t * const src_end) noexcept {
- if(src == src_end) { return NUMBER_ERROR; }
- //
- // Check for minus sign
- //
- bool negative = (*src == '-');
- src += uint8_t(negative);
-
- //
- // Parse the integer part.
- //
- uint64_t i = 0;
- const uint8_t *p = src;
- if(p == src_end) { return NUMBER_ERROR; }
- p += parse_digit(*p, i);
- bool leading_zero = (i == 0);
- while ((p != src_end) && parse_digit(*p, i)) { p++; }
- // no integer digits, or 0123 (zero must be solo)
- if ( p == src ) { return INCORRECT_TYPE; }
- if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; }
-
- //
- // Parse the decimal part.
- //
- int64_t exponent = 0;
- bool overflow;
- if (simdjson_likely((p != src_end) && (*p == '.'))) {
- p++;
- const uint8_t *start_decimal_digits = p;
- if ((p == src_end) || !parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits
- p++;
- while ((p != src_end) && parse_digit(*p, i)) { p++; }
- exponent = -(p - start_decimal_digits);
-
- // Overflow check. More than 19 digits (minus the decimal) may be overflow.
- overflow = p-src-1 > 19;
- if (simdjson_unlikely(overflow && leading_zero)) {
- // Skip leading 0.00000 and see if it still overflows
- const uint8_t *start_digits = src + 2;
- while (*start_digits == '0') { start_digits++; }
- overflow = start_digits-src > 19;
- }
- } else {
- overflow = p-src > 19;
- }
-
- //
- // Parse the exponent
- //
- if ((p != src_end) && (*p == 'e' || *p == 'E')) {
- p++;
- if(p == src_end) { return NUMBER_ERROR; }
- bool exp_neg = *p == '-';
- p += exp_neg || *p == '+';
-
- uint64_t exp = 0;
- const uint8_t *start_exp_digits = p;
- while ((p != src_end) && parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
-
- exponent += exp_neg ? 0-exp : exp;
- }
-
- if ((p != src_end) && jsoncharutils::is_not_structural_or_whitespace(*p)) { return NUMBER_ERROR; }
-
- overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power;
-
- //
- // Assemble (or slow-parse) the float
- //
- double d;
- if (simdjson_likely(!overflow)) {
- if (compute_float_64(exponent, i, negative, d)) { return d; }
- }
- if (!parse_float_fallback(src - uint8_t(negative), src_end, &d)) {
- return NUMBER_ERROR;
- }
- return d;
-}
-
simdjson_unused simdjson_inline simdjson_result<double> parse_double_in_string(const uint8_t * src) noexcept {
//
// Check for minus sign
@@ -40267,8 +39627,11 @@ simdjson_unused simdjson_inline simdjson_result<double> parse_double_in_string(c
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
+ // no exp digits
+ if (p-start_exp_digits == 0) { return NUMBER_ERROR; }
+ // 20+ exp digits (e.g., 0e9999999999999999999999) are valid, but exp may
+ // have wrapped around: leave such numbers to the fallback parser.
+ overflow = overflow || p-start_exp_digits > 19;
exponent += exp_neg ? 0-exp : exp;
}
@@ -40360,8 +39723,11 @@ simdjson_unused simdjson_inline simdjson_result<float> parse_float_in_string(con
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
+ // no exp digits
+ if (p-start_exp_digits == 0) { return NUMBER_ERROR; }
+ // 20+ exp digits (e.g., 0e9999999999999999999999) are valid, but exp may
+ // have wrapped around: leave such numbers to the fallback parser.
+ overflow = overflow || p-start_exp_digits > 19;
exponent += exp_neg ? 0-exp : exp;
}
@@ -41377,31 +40743,6 @@ backslash_and_quote::copy_and_find(const uint8_t *src, uint8_t *dst) {
}
-struct escaping {
- static constexpr uint32_t BYTES_PROCESSED = 16;
- simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
-
- simdjson_inline bool has_escape() { return escape_bits != 0; }
- simdjson_inline int escape_index() { return trailing_zeroes(escape_bits); }
-
- uint64_t escape_bits;
-}; // struct escaping
-
-
-
-simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
- static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes");
- simd8<uint8_t> v(src);
- v.store(dst);
- simd8<bool> is_quote = (v == '"');
- simd8<bool> is_backslash = (v == '\\');
- simd8<bool> is_control = (v < 32);
- return {
- // We store it as a 64-bit bitmask even though we only need 16 bits.
- uint64_t((is_backslash | is_quote | is_control).to_bitmask())
- };
-}
-
} // unnamed namespace
} // namespace ppc64
} // namespace simdjson
@@ -41569,6 +40910,17 @@ public:
* @return the number of effective characters in the last block.
*/
simdjson_inline size_t get_remainder(uint8_t *dst) const;
+ /**
+ * Like get_remainder, for input followed by SIMDJSON_PADDING readable bytes.
+ *
+ * get_remainder writes the block with a memset and a memcpy of the remaining
+ * length, and the vector loads that read it back cannot be forwarded from
+ * those narrower stores: they wait until the stores reach the cache, which
+ * costs tens of cycles on every document. Here the block is put together in
+ * vector registers, from loads that may read into the padding, and written
+ * with full-width stores that the loads can forward from.
+ */
+ simdjson_inline size_t get_remainder_padded(uint8_t *dst) const;
simdjson_inline void advance();
private:
const uint8_t *buf;
@@ -41577,47 +40929,6 @@ private:
size_t idx;
};
-// Routines to print masks and text for debugging bitmask operations
-simdjson_unused static char * format_input_text_64(const uint8_t *text) {
- static char buf[sizeof(simd8x64<uint8_t>) + 1];
- for (size_t i=0; i<sizeof(simd8x64<uint8_t>); i++) {
- buf[i] = int8_t(text[i]) < ' ' ? '_' : int8_t(text[i]);
- }
- buf[sizeof(simd8x64<uint8_t>)] = '\0';
- return buf;
-}
-
-// Routines to print masks and text for debugging bitmask operations
-simdjson_unused static char * format_input_text(const simd8x64<uint8_t>& in) {
- static char buf[sizeof(simd8x64<uint8_t>) + 1];
- in.store(reinterpret_cast<uint8_t*>(buf));
- for (size_t i=0; i<sizeof(simd8x64<uint8_t>); i++) {
- if (buf[i] < ' ') { buf[i] = '_'; }
- }
- buf[sizeof(simd8x64<uint8_t>)] = '\0';
- return buf;
-}
-
-simdjson_unused static char * format_input_text(const simd8x64<uint8_t>& in, uint64_t mask) {
- static char buf[sizeof(simd8x64<uint8_t>) + 1];
- in.store(reinterpret_cast<uint8_t*>(buf));
- for (size_t i=0; i<sizeof(simd8x64<uint8_t>); i++) {
- if (buf[i] <= ' ') { buf[i] = '_'; }
- if (!(mask & (size_t(1) << i))) { buf[i] = ' '; }
- }
- buf[sizeof(simd8x64<uint8_t>)] = '\0';
- return buf;
-}
-
-simdjson_unused static char * format_mask(uint64_t mask) {
- static char buf[sizeof(simd8x64<uint8_t>) + 1];
- for (size_t i=0; i<64; i++) {
- buf[i] = (mask & (size_t(1) << i)) ? 'X' : ' ';
- }
- buf[64] = '\0';
- return buf;
-}
-
template<size_t STEP_SIZE>
simdjson_inline buf_block_reader<STEP_SIZE>::buf_block_reader(const uint8_t *_buf, size_t _len) : buf{_buf}, len{_len}, lenminusstep{len < STEP_SIZE ? 0 : len - STEP_SIZE}, idx{0} {}
@@ -41642,6 +40953,38 @@ simdjson_inline size_t buf_block_reader<STEP_SIZE>::get_remainder(uint8_t *dst)
return len - idx;
}
+// keep_mask[128 - n + i] is 0xFF for i < n and 0 otherwise.
+alignas(64) static const uint8_t keep_mask[256] = {
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+template<size_t STEP_SIZE>
+simdjson_inline size_t buf_block_reader<STEP_SIZE>::get_remainder_padded(uint8_t *dst) const {
+ static_assert(STEP_SIZE <= 128, "keep_mask covers blocks of up to 128 bytes");
+ static_assert(sizeof(simd::simd8<uint8_t>) <= SIMDJSON_PADDING, "a vector load at the last byte must stay in the padding");
+ const size_t remaining = len - idx;
+ if (remaining == 0) { return 0; }
+ const simd::simd8<uint8_t> spaces = simd::simd8<uint8_t>::splat(0x20);
+ for (size_t off = 0; off < STEP_SIZE; off += sizeof(simd::simd8<uint8_t>)) {
+ simd::simd8<uint8_t> v = spaces;
+ if (off < remaining) {
+ // Starts before len, so it ends within the padding.
+ const simd::simd8<uint8_t> in = simd::simd8<uint8_t>::load(buf + idx + off);
+ const simd::simd8<uint8_t> keep = simd::simd8<uint8_t>::load(keep_mask + 128 - remaining + off);
+ v = spaces ^ ((in ^ spaces) & keep);
+ }
+ v.store(dst + off);
+ }
+ return remaining;
+}
+
template<size_t STEP_SIZE>
simdjson_inline void buf_block_reader<STEP_SIZE>::advance() {
idx += STEP_SIZE;
@@ -42342,6 +41685,20 @@ simdjson_inline bool ends_with_partial_scalar(dom_parser_implementation &parser,
e != ' ' && e != '\t' && e != '\n' && e != '\r';
}
+/**
+ * The structural index past the last one of a partial batch is the end of this
+ * window, not a token of it: the byte there is the first byte of the next
+ * window. A finished value must not be rejected because that byte is ',', ':',
+ * ']' or '}'. A last document that itself ends with '{', '[', ',' or ':' is
+ * invalid JSON, yet its brackets may balance: walking it, the On-Demand API
+ * could read the next batch as a value. We leave such a document to the next
+ * batch, so that the batch ends between two documents.
+ */
+simdjson_inline bool ends_with_unfinished_document(dom_parser_implementation &parser) {
+ const uint8_t last = parser.buf[parser.structural_indexes[parser.n_structural_indexes - 1]];
+ return last == '{' || last == '[' || last == ',' || last == ':';
+}
+
simdjson_inline uint32_t find_next_document_index(dom_parser_implementation &parser, bool defer_last = false) {
// Variant: do not count separately, just figure out depth
if(parser.n_structural_indexes == 0) { return 0; }
@@ -43023,7 +42380,8 @@ error_code json_structural_indexer::index(const uint8_t *buf, size_t len, dom_pa
// Take care of the last block (will always be there unless file is empty which is
// not supposed to happen.)
uint8_t block[STEP_SIZE];
- if (simdjson_unlikely(reader.get_remainder(block) == 0)) { return UNEXPECTED_ERROR; }
+ const size_t remaining = parser._unpadded ? reader.get_remainder(block) : reader.get_remainder_padded(block);
+ if (simdjson_unlikely(remaining == 0)) { return UNEXPECTED_ERROR; }
indexer.step<STEP_SIZE>(block, reader);
return indexer.finish(parser, reader.block_index(), len, partial);
}
@@ -43057,6 +42415,20 @@ simdjson_inline void json_structural_indexer::next(const simd::simd8x64<uint8_t>
unescaped_chars_error |= block.non_quote_inside_string(unescaped);
}
+// Drops the unclosed string that ends a json_sequence batch and leaves its
+// start in structural_indexes[n_structural_indexes]. The last index is its
+// opening quote, unless the quote directly follows an RS: the scanner reads
+// RS as a scalar byte, so it indexes the RS and not the quote. Keep the RS.
+simdjson_inline void drop_json_sequence_unclosed_string(dom_parser_implementation &parser, size_t len) noexcept {
+ uint32_t quote = parser.structural_indexes[parser.n_structural_indexes - 1];
+ if (parser.buf[quote] == '"') {
+ parser.n_structural_indexes--;
+ return;
+ }
+ while (quote < len && parser.buf[quote] != '"') { quote++; }
+ parser.structural_indexes[parser.n_structural_indexes] = quote;
+}
+
simdjson_inline error_code json_structural_indexer::finish(dom_parser_implementation &parser, size_t idx, size_t len, stage1_mode partial) {
// Write out the final iteration's structurals
indexer.write(uint32_t(idx-64), prev_structurals);
@@ -43113,8 +42485,13 @@ simdjson_inline error_code json_structural_indexer::finish(dom_parser_implementa
if (simdjson_unlikely(parser.n_structural_indexes == 0u)) { return CAPACITY; }
}
// We truncate the input to the end of the last complete document (or zero).
- auto new_structural_indexes = find_next_document_index(parser, !have_unclosed_string && ends_with_partial_scalar(parser, len));
+ const bool partial_scalar = !have_unclosed_string && ends_with_partial_scalar(parser, len);
+ const bool unfinished = ends_with_unfinished_document(parser);
+ auto new_structural_indexes = find_next_document_index(parser, partial_scalar || unfinished);
if (new_structural_indexes == 0 && parser.n_structural_indexes > 0) {
+ // The batch holds a single document, whose brackets balance but which
+ // cannot be valid JSON: it is not too big, it is invalid.
+ if (unfinished && find_next_document_index(parser, partial_scalar) != 0) { return TAPE_ERROR; }
if(parser.structural_indexes[0] == 0) {
// If the buffer is partial and we started at index 0 but the document is
// incomplete, it's too big to parse.
@@ -43158,7 +42535,7 @@ simdjson_inline error_code json_structural_indexer::finish(dom_parser_implementa
// A discarded unclosed string also caps how far the filter may scan.
size_t scan_len = len;
if(have_unclosed_string) {
- parser.n_structural_indexes--;
+ drop_json_sequence_unclosed_string(parser, len);
if (simdjson_unlikely(parser.n_structural_indexes == 0u)) { return CAPACITY; }
scan_len = parser.structural_indexes[parser.n_structural_indexes];
}
@@ -43177,12 +42554,17 @@ simdjson_inline error_code json_structural_indexer::finish(dom_parser_implementa
}
parser.n_structural_indexes = new_structural_indexes;
parser.structural_indexes[parser.n_structural_indexes] = next_batch_start;
+ // The next batch starts at an RS, which the On-Demand API never reads as a
+ // string, a container or a separator. But an invalid last document may make
+ // it skip that RS as a value: repeat it, so that the index past it is not
+ // the next record, which can be a string that does not close in this batch.
+ parser.structural_indexes[parser.n_structural_indexes + 1] = next_batch_start;
} else if (partial == stage1_mode::json_sequence_final) {
// RFC 7464: final batch, last document extends to EOF
// As above: a discarded unclosed string caps how far the filter may scan.
size_t scan_len = len;
if(have_unclosed_string) {
- parser.n_structural_indexes--;
+ drop_json_sequence_unclosed_string(parser, len);
scan_len = parser.structural_indexes[parser.n_structural_indexes];
}
uint32_t next_batch_start = uint32_t(len);
@@ -43600,7 +42982,7 @@ public:
*
* - increment_count(iter) - each time a value is found in an array or object.
*/
- template<bool STREAMING, bool UNPADDED, typename V>
+ template<bool STREAMING, bool UNPADDED, bool BOUNDED = false, typename V>
simdjson_warn_unused simdjson_inline error_code walk_document(V &visitor) noexcept;
/**
@@ -43617,8 +42999,8 @@ public:
*
* They may include invalid JSON as well (such as `1.2.3` or `ture`).
*/
- template<bool UNPADDED>
- simdjson_inline const uint8_t *peek() const noexcept;
+ template<bool UNPADDED, bool BOUNDED = false>
+ simdjson_inline const uint8_t *peek(const uint32_t *batch_end = nullptr) const noexcept;
/**
* Advance to the next token.
*
@@ -43626,8 +43008,8 @@ public:
*
* They may include invalid JSON as well (such as `1.2.3` or `ture`).
*/
- template<bool UNPADDED>
- simdjson_inline const uint8_t *advance() noexcept;
+ template<bool UNPADDED, bool BOUNDED = false>
+ simdjson_inline const uint8_t *advance(const uint32_t *batch_end = nullptr) noexcept;
/**
* Get the remaining length of the document, from the start of the current token.
*/
@@ -43675,7 +43057,7 @@ public:
simdjson_warn_unused simdjson_inline error_code visit_primitive(V &visitor, const uint8_t *value) noexcept;
};
-template<bool STREAMING, bool UNPADDED, typename V>
+template<bool STREAMING, bool UNPADDED, bool BOUNDED, typename V>
simdjson_warn_unused simdjson_inline error_code json_iterator::walk_document(V &visitor) noexcept {
logger::log_start();
@@ -43683,6 +43065,9 @@ simdjson_warn_unused simdjson_inline error_code json_iterator::walk_document(V &
// Start the document
//
if (at_eof()) { return EMPTY; }
+ // Only used when BOUNDED (see advance()); a local, so that it can stay in a
+ // register, and vanishes from the other walks.
+ const uint32_t *const batch_end = &dom_parser.structural_indexes[dom_parser.n_structural_indexes];
log_start_value("document");
SIMDJSON_TRY( visitor.visit_document_start(*this) );
@@ -43690,7 +43075,7 @@ simdjson_warn_unused simdjson_inline error_code json_iterator::walk_document(V &
// Read first value
//
{
- auto value = advance<UNPADDED>();
+ auto value = advance<UNPADDED, BOUNDED>(batch_end);
// Make sure the outer object or array is closed before continuing; otherwise, there are ways we
// could get into memory corruption. See https://github.com/simdjson/simdjson/issues/906
@@ -43702,8 +43087,8 @@ simdjson_warn_unused simdjson_inline error_code json_iterator::walk_document(V &
}
switch (*value) {
- case '{': if (*peek<UNPADDED>() == '}') { advance<UNPADDED>(); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin;
- case '[': if (*peek<UNPADDED>() == ']') { advance<UNPADDED>(); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin;
+ case '{': if (*peek<UNPADDED, BOUNDED>(batch_end) == '}') { advance<UNPADDED, BOUNDED>(batch_end); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin;
+ case '[': if (*peek<UNPADDED, BOUNDED>(batch_end) == ']') { advance<UNPADDED, BOUNDED>(batch_end); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin;
default: SIMDJSON_TRY( visitor.visit_root_primitive(*this, value) ); break;
}
}
@@ -43720,29 +43105,29 @@ object_begin:
SIMDJSON_TRY( visitor.visit_object_start(*this) );
{
- auto key = advance<UNPADDED>();
+ auto key = advance<UNPADDED, BOUNDED>(batch_end);
if (*key != '"') { log_error("Object does not start with a key"); return TAPE_ERROR; }
SIMDJSON_TRY( visitor.increment_count(*this) );
SIMDJSON_TRY( visitor.visit_key(*this, key) );
}
object_field:
- if (simdjson_unlikely( *advance<UNPADDED>() != ':' )) { log_error("Missing colon after key in object"); return TAPE_ERROR; }
+ if (simdjson_unlikely( (*advance<UNPADDED, BOUNDED>(batch_end) != ':') )) { log_error("Missing colon after key in object"); return TAPE_ERROR; }
{
- auto value = advance<UNPADDED>();
+ auto value = advance<UNPADDED, BOUNDED>(batch_end);
switch (*value) {
- case '{': if (*peek<UNPADDED>() == '}') { advance<UNPADDED>(); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin;
- case '[': if (*peek<UNPADDED>() == ']') { advance<UNPADDED>(); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin;
+ case '{': if (*peek<UNPADDED, BOUNDED>(batch_end) == '}') { advance<UNPADDED, BOUNDED>(batch_end); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin;
+ case '[': if (*peek<UNPADDED, BOUNDED>(batch_end) == ']') { advance<UNPADDED, BOUNDED>(batch_end); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin;
default: SIMDJSON_TRY( visitor.visit_primitive(*this, value) ); break;
}
}
object_continue:
- switch (*advance<UNPADDED>()) {
+ switch (*advance<UNPADDED, BOUNDED>(batch_end)) {
case ',':
SIMDJSON_TRY( visitor.increment_count(*this) );
{
- auto key = advance<UNPADDED>();
+ auto key = advance<UNPADDED, BOUNDED>(batch_end);
if (simdjson_unlikely( *key != '"' )) { log_error("Key string missing at beginning of field in object"); return TAPE_ERROR; }
SIMDJSON_TRY( visitor.visit_key(*this, key) );
}
@@ -43770,16 +43155,16 @@ array_begin:
array_value:
{
- auto value = advance<UNPADDED>();
+ auto value = advance<UNPADDED, BOUNDED>(batch_end);
switch (*value) {
- case '{': if (*peek<UNPADDED>() == '}') { advance<UNPADDED>(); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin;
- case '[': if (*peek<UNPADDED>() == ']') { advance<UNPADDED>(); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin;
+ case '{': if (*peek<UNPADDED, BOUNDED>(batch_end) == '}') { advance<UNPADDED, BOUNDED>(batch_end); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin;
+ case '[': if (*peek<UNPADDED, BOUNDED>(batch_end) == ']') { advance<UNPADDED, BOUNDED>(batch_end); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin;
default: SIMDJSON_TRY( visitor.visit_primitive(*this, value) ); break;
}
}
array_continue:
- switch (*advance<UNPADDED>()) {
+ switch (*advance<UNPADDED, BOUNDED>(batch_end)) {
case ',': SIMDJSON_TRY( visitor.increment_count(*this) ); goto array_value;
case ']': log_end_value("array"); SIMDJSON_TRY( visitor.visit_array_end(*this) ); goto scope_end;
default: log_error("Missing comma between array values"); return TAPE_ERROR;
@@ -43809,8 +43194,18 @@ simdjson_inline json_iterator::json_iterator(dom_parser_implementation &_dom_par
// Stage 1 leaves a sentinel index of value `len`; reading buf[len] requires
// padding. For UNPADDED, return a dummy so walk_document can fail cleanly (#2815).
-template<bool UNPADDED>
-simdjson_inline const uint8_t *json_iterator::peek() const noexcept {
+//
+// When BOUNDED (see dom_parser_implementation::_bounded_batch), a document may
+// not run past batch_end, one past the last structural index of the batch:
+// past it, we return the same dummy, and walk_document fails cleanly.
+template<bool UNPADDED, bool BOUNDED>
+simdjson_inline const uint8_t *json_iterator::peek(const uint32_t *batch_end) const noexcept {
+ SIMDJSON_IF_CONSTEXPR (BOUNDED) {
+ if (simdjson_unlikely(next_structural >= batch_end)) {
+ static constexpr uint8_t streaming_eof_sentinel = 0;
+ return &streaming_eof_sentinel;
+ }
+ }
const uint32_t idx = *(next_structural);
SIMDJSON_IF_CONSTEXPR (UNPADDED) {
if (simdjson_unlikely(idx >= dom_parser.len)) {
@@ -43820,8 +43215,14 @@ simdjson_inline const uint8_t *json_iterator::peek() const noexcept {
}
return &buf[idx];
}
-template<bool UNPADDED>
-simdjson_inline const uint8_t *json_iterator::advance() noexcept {
+template<bool UNPADDED, bool BOUNDED>
+simdjson_inline const uint8_t *json_iterator::advance(const uint32_t *batch_end) noexcept {
+ SIMDJSON_IF_CONSTEXPR (BOUNDED) {
+ if (simdjson_unlikely(next_structural >= batch_end)) {
+ static constexpr uint8_t streaming_eof_sentinel = 0;
+ return &streaming_eof_sentinel;
+ }
+ }
const uint32_t idx = *(next_structural++);
SIMDJSON_IF_CONSTEXPR (UNPADDED) {
if (simdjson_unlikely(idx >= dom_parser.len)) {
@@ -44380,6 +43781,30 @@ struct tape_builder {
return iter.walk_document<STREAMING, false>(builder);
}
}
+
+ // The stage 2 entry points of dom::document_stream, which picks one per batch
+ // (see internal::dom_parser_implementation::stage2_next_function). Out of
+ // line, so that stage2_next() can share the code.
+ simdjson_warn_unused static simdjson_never_inline error_code parse_next_document(
+ internal::dom_parser_implementation &dom_parser, dom::document &doc) noexcept {
+ return parse_document<true>(static_cast<dom_parser_implementation &>(dom_parser), doc);
+ }
+
+ // A document of a stream batch that stage 2 must bound to its structural
+ // indexes (see internal::dom_parser_implementation::_bounded_batch).
+ simdjson_warn_unused static simdjson_never_inline error_code parse_next_bounded_document(
+ internal::dom_parser_implementation &_dom_parser, dom::document &doc) noexcept {
+ dom_parser_implementation &dom_parser = static_cast<dom_parser_implementation &>(_dom_parser);
+ dom_parser.doc = &doc;
+ json_iterator iter(dom_parser, dom_parser.next_structural_index);
+ if (dom_parser._unpadded) {
+ tape_builder_impl<true> builder(doc);
+ return iter.walk_document<true, true, true>(builder);
+ } else {
+ tape_builder_impl<false> builder(doc);
+ return iter.walk_document<true, false, true>(builder);
+ }
+ }
};
template <bool UNPADDED>
@@ -44763,14 +44188,6 @@ simdjson_inline bool is_ascii(const simd8x64<uint8_t>& input) {
return input.reduce_or().saturating_sub(0x7fu).bits_not_set_anywhere();
}
-simdjson_unused simdjson_inline simd8<bool> must_be_continuation(const simd8<uint8_t> prev1, const simd8<uint8_t> prev2, const simd8<uint8_t> prev3) {
- simd8<uint8_t> is_second_byte = prev1.saturating_sub(0xc0u-1); // Only 11______ will be > 0
- simd8<uint8_t> is_third_byte = prev2.saturating_sub(0xe0u-1); // Only 111_____ will be > 0
- simd8<uint8_t> is_fourth_byte = prev3.saturating_sub(0xf0u-1); // Only 1111____ will be > 0
- // Caller requires a bool (all 1's). All values resulting from the subtraction will be <= 64, so signed comparison is fine.
- return simd8<int8_t>(is_second_byte | is_third_byte | is_fourth_byte) > int8_t(0);
-}
-
simdjson_inline simd8<uint8_t> must_be_2_3_continuation(const simd8<uint8_t> prev2, const simd8<uint8_t> prev3) {
simd8<uint8_t> is_third_byte = prev2.saturating_sub(0xe0u-0x80); // Only 111_____ will be >= 0x80
simd8<uint8_t> is_fourth_byte = prev3.saturating_sub(0xf0u-0x80); // Only 1111____ will be >= 0x80
@@ -44810,7 +44227,11 @@ simdjson_warn_unused error_code dom_parser_implementation::stage2(dom::document
}
simdjson_warn_unused error_code dom_parser_implementation::stage2_next(dom::document &_doc) noexcept {
- return stage2::tape_builder::parse_document<true>(*this, _doc);
+ return stage2::tape_builder::parse_next_document(*this, _doc);
+}
+
+internal::dom_parser_implementation::stage2_next_function_t dom_parser_implementation::stage2_next_function(bool bounded) const noexcept {
+ return bounded ? stage2::tape_builder::parse_next_bounded_document : stage2::tape_builder::parse_next_document;
}
SIMDJSON_NO_SANITIZE_MEMORY
@@ -45965,30 +45386,6 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin
}
-struct escaping {
- static constexpr uint32_t BYTES_PROCESSED = 16;
- simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
-
- simdjson_inline bool has_escape() { return escape_bits != 0; }
- simdjson_inline int escape_index() { return trailing_zeroes(escape_bits); }
-
- uint64_t escape_bits;
-}; // struct escaping
-
-
-
-simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
- static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes");
- simd8<uint8_t> v(src);
- v.store(dst);
- simd8<bool> is_quote = (v == '"');
- simd8<bool> is_backslash = (v == '\\');
- simd8<bool> is_control = (v < 32);
- return {
- uint64_t((is_backslash | is_quote | is_control).to_bitmask())
- };
-}
-
} // unnamed namespace
} // namespace westmere
} // namespace simdjson
@@ -46379,6 +45776,7 @@ public:
simdjson_warn_unused error_code stage1(const uint8_t *buf, size_t len, stage1_mode partial) noexcept final;
simdjson_warn_unused error_code stage2(dom::document &doc) noexcept final;
simdjson_warn_unused error_code stage2_next(dom::document &doc) noexcept final;
+ stage2_next_function_t stage2_next_function(bool bounded) const noexcept final;
simdjson_warn_unused uint8_t *parse_string(const uint8_t *src, uint8_t *dst, bool allow_replacement) const noexcept final;
simdjson_warn_unused uint8_t *parse_wobbly_string(const uint8_t *src, uint8_t *dst) const noexcept final;
inline simdjson_warn_unused error_code set_capacity(size_t capacity) noexcept final;
@@ -47140,21 +46538,6 @@ static bool parse_float_fallback(const uint8_t *ptr, double *outDouble) {
return !(*outDouble > (std::numeric_limits<double>::max)() || *outDouble < std::numeric_limits<double>::lowest());
}
-static bool parse_float_fallback(const uint8_t *ptr, const uint8_t *end_ptr, double *outDouble) {
- *outDouble = simdjson::internal::from_chars(reinterpret_cast<const char *>(ptr), reinterpret_cast<const char *>(end_ptr));
- // We do not accept infinite values.
-
- // Detecting finite values in a portable manner is ridiculously hard, ideally
- // we would want to do:
- // return !std::isfinite(*outDouble);
- // but that mysteriously fails under legacy/old libc++ libraries, see
- // https://github.com/simdjson/simdjson/issues/1286
- //
- // Therefore, fall back to this solution (the extra parens are there
- // to handle that max may be a macro on windows).
- return !(*outDouble > (std::numeric_limits<double>::max)() || *outDouble < std::numeric_limits<double>::lowest());
-}
-
// Same as parse_float_fallback, but for binary32 (float) values. Going through
// the binary64 fallback and then rounding to binary32 would be subject to
// double rounding, so we run the fallback algorithm directly on binary32.
@@ -47700,57 +47083,6 @@ simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned(const u
return i;
}
-
-
-// Parse any number from 0 to 18,446,744,073,709,551,615
-// Never read at src_end or beyond
-simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned(const uint8_t * const src, const uint8_t * const src_end) noexcept {
- const uint8_t *p = src;
- //
- // Parse the integer part.
- //
- // PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
- const uint8_t *const start_digits = p;
- uint64_t i = 0;
- while ((p != src_end) && parse_digit(*p, i)) { p++; }
-
- // If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
- // Optimization note: size_t is expected to be unsigned.
- size_t digit_count = size_t(p - start_digits);
- // The longest positive 64-bit number is 20 digits.
- // We do it this way so we don't trigger this branch unless we must.
- // Optimization note: the compiler can probably merge
- // ((digit_count == 0) || (digit_count > 20))
- // into a single branch since digit_count is unsigned.
- if ((digit_count == 0) || (digit_count > 20)) { return INCORRECT_TYPE; }
- // Here digit_count > 0.
- if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
- // We can do the following...
- // if (!jsoncharutils::is_structural_or_whitespace(*p)) {
- // return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
- // }
- // as a single table lookup:
- if ((p != src_end) && integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); }
-
- if (digit_count == 20) {
- // Positive overflow check:
- // - A 20 digit number starting with 2-9 is overflow, because 18,446,744,073,709,551,615 is the
- // biggest uint64_t.
- // - A 20 digit number starting with 1 is overflow if it is less than INT64_MAX.
- // If we got here, it's a 20 digit number starting with the digit "1".
- // - If a 20 digit number starting with 1 overflowed (i*10+digit), the result will be smaller
- // than 1,553,255,926,290,448,384.
- // - That is smaller than the smallest possible 20-digit number the user could write:
- // 10,000,000,000,000,000,000.
- // - Therefore, if the number is positive and lower than that, it's overflow.
- // - The value we are looking at is less than or equal to INT64_MAX.
- //
- if (src[0] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INCORRECT_TYPE; }
- }
-
- return i;
-}
-
// Parse any number from 0 to 18,446,744,073,709,551,615
simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned_in_string(const uint8_t * const src) noexcept {
const uint8_t *p = src + 1;
@@ -47843,50 +47175,6 @@ simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer(const uin
return negative ? (~i+1) : i;
}
-// Parse any number from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
-// Never read at src_end or beyond
-simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer(const uint8_t * const src, const uint8_t * const src_end) noexcept {
- //
- // Check for minus sign
- //
- if(src == src_end) { return NUMBER_ERROR; }
- bool negative = (*src == '-');
- const uint8_t *p = src + uint8_t(negative);
-
- //
- // Parse the integer part.
- //
- // PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
- const uint8_t *const start_digits = p;
- uint64_t i = 0;
- while ((p != src_end) && parse_digit(*p, i)) { p++; }
-
- // If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
- // Optimization note: size_t is expected to be unsigned.
- size_t digit_count = size_t(p - start_digits);
- // We go from
- // -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
- // so we can never represent numbers that have more than 19 digits.
- size_t longest_digit_count = 19;
- // Optimization note: the compiler can probably merge
- // ((digit_count == 0) || (digit_count > longest_digit_count))
- // into a single branch since digit_count is unsigned.
- if ((digit_count == 0) || (digit_count > longest_digit_count)) { return INCORRECT_TYPE; }
- // Here digit_count > 0.
- if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
- // We can do the following...
- // if (!jsoncharutils::is_structural_or_whitespace(*p)) {
- // return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
- // }
- // as a single table lookup:
- if((p != src_end) && integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); }
- // Negative numbers have can go down to - INT64_MAX - 1 whereas positive numbers are limited to INT64_MAX.
- // Performance note: This check is only needed when digit_count == longest_digit_count but it is
- // so cheap that we might as well always make it.
- if(i > uint64_t(INT64_MAX) + uint64_t(negative)) { return INCORRECT_TYPE; }
- return negative ? (~i+1) : i;
-}
-
// Parse any number from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer_in_string(const uint8_t *src) noexcept {
//
@@ -47992,8 +47280,11 @@ simdjson_unused simdjson_inline simdjson_result<double> parse_double(const uint8
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
+ // no exp digits
+ if (p-start_exp_digits == 0) { return NUMBER_ERROR; }
+ // 20+ exp digits (e.g., 0e9999999999999999999999) are valid, but exp may
+ // have wrapped around: leave such numbers to the fallback parser.
+ overflow = overflow || p-start_exp_digits > 19;
exponent += exp_neg ? 0-exp : exp;
}
@@ -48083,8 +47374,11 @@ simdjson_unused simdjson_inline simdjson_result<float> parse_float(const uint8_t
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
+ // no exp digits
+ if (p-start_exp_digits == 0) { return NUMBER_ERROR; }
+ // 20+ exp digits (e.g., 0e9999999999999999999999) are valid, but exp may
+ // have wrapped around: leave such numbers to the fallback parser.
+ overflow = overflow || p-start_exp_digits > 19;
exponent += exp_neg ? 0-exp : exp;
}
@@ -48164,88 +47458,6 @@ simdjson_unused simdjson_inline simdjson_result<number_type> get_number_type(con
return number_type::floating_point_number;
}
-// Never read at src_end or beyond
-simdjson_unused simdjson_inline simdjson_result<double> parse_double(const uint8_t * src, const uint8_t * const src_end) noexcept {
- if(src == src_end) { return NUMBER_ERROR; }
- //
- // Check for minus sign
- //
- bool negative = (*src == '-');
- src += uint8_t(negative);
-
- //
- // Parse the integer part.
- //
- uint64_t i = 0;
- const uint8_t *p = src;
- if(p == src_end) { return NUMBER_ERROR; }
- p += parse_digit(*p, i);
- bool leading_zero = (i == 0);
- while ((p != src_end) && parse_digit(*p, i)) { p++; }
- // no integer digits, or 0123 (zero must be solo)
- if ( p == src ) { return INCORRECT_TYPE; }
- if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; }
-
- //
- // Parse the decimal part.
- //
- int64_t exponent = 0;
- bool overflow;
- if (simdjson_likely((p != src_end) && (*p == '.'))) {
- p++;
- const uint8_t *start_decimal_digits = p;
- if ((p == src_end) || !parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits
- p++;
- while ((p != src_end) && parse_digit(*p, i)) { p++; }
- exponent = -(p - start_decimal_digits);
-
- // Overflow check. More than 19 digits (minus the decimal) may be overflow.
- overflow = p-src-1 > 19;
- if (simdjson_unlikely(overflow && leading_zero)) {
- // Skip leading 0.00000 and see if it still overflows
- const uint8_t *start_digits = src + 2;
- while (*start_digits == '0') { start_digits++; }
- overflow = start_digits-src > 19;
- }
- } else {
- overflow = p-src > 19;
- }
-
- //
- // Parse the exponent
- //
- if ((p != src_end) && (*p == 'e' || *p == 'E')) {
- p++;
- if(p == src_end) { return NUMBER_ERROR; }
- bool exp_neg = *p == '-';
- p += exp_neg || *p == '+';
-
- uint64_t exp = 0;
- const uint8_t *start_exp_digits = p;
- while ((p != src_end) && parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
-
- exponent += exp_neg ? 0-exp : exp;
- }
-
- if ((p != src_end) && jsoncharutils::is_not_structural_or_whitespace(*p)) { return NUMBER_ERROR; }
-
- overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power;
-
- //
- // Assemble (or slow-parse) the float
- //
- double d;
- if (simdjson_likely(!overflow)) {
- if (compute_float_64(exponent, i, negative, d)) { return d; }
- }
- if (!parse_float_fallback(src - uint8_t(negative), src_end, &d)) {
- return NUMBER_ERROR;
- }
- return d;
-}
-
simdjson_unused simdjson_inline simdjson_result<double> parse_double_in_string(const uint8_t * src) noexcept {
//
// Check for minus sign
@@ -48314,8 +47526,11 @@ simdjson_unused simdjson_inline simdjson_result<double> parse_double_in_string(c
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
+ // no exp digits
+ if (p-start_exp_digits == 0) { return NUMBER_ERROR; }
+ // 20+ exp digits (e.g., 0e9999999999999999999999) are valid, but exp may
+ // have wrapped around: leave such numbers to the fallback parser.
+ overflow = overflow || p-start_exp_digits > 19;
exponent += exp_neg ? 0-exp : exp;
}
@@ -48407,8 +47622,11 @@ simdjson_unused simdjson_inline simdjson_result<float> parse_float_in_string(con
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
+ // no exp digits
+ if (p-start_exp_digits == 0) { return NUMBER_ERROR; }
+ // 20+ exp digits (e.g., 0e9999999999999999999999) are valid, but exp may
+ // have wrapped around: leave such numbers to the fallback parser.
+ overflow = overflow || p-start_exp_digits > 19;
exponent += exp_neg ? 0-exp : exp;
}
@@ -49732,30 +48950,6 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin
}
-struct escaping {
- static constexpr uint32_t BYTES_PROCESSED = 16;
- simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
-
- simdjson_inline bool has_escape() { return escape_bits != 0; }
- simdjson_inline int escape_index() { return trailing_zeroes(escape_bits); }
-
- uint64_t escape_bits;
-}; // struct escaping
-
-
-
-simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
- static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes");
- simd8<uint8_t> v(src);
- v.store(dst);
- simd8<bool> is_quote = (v == '"');
- simd8<bool> is_backslash = (v == '\\');
- simd8<bool> is_control = (v < 32);
- return {
- uint64_t((is_backslash | is_quote | is_control).to_bitmask())
- };
-}
-
} // unnamed namespace
} // namespace westmere
} // namespace simdjson
@@ -49921,6 +49115,17 @@ public:
* @return the number of effective characters in the last block.
*/
simdjson_inline size_t get_remainder(uint8_t *dst) const;
+ /**
+ * Like get_remainder, for input followed by SIMDJSON_PADDING readable bytes.
+ *
+ * get_remainder writes the block with a memset and a memcpy of the remaining
+ * length, and the vector loads that read it back cannot be forwarded from
+ * those narrower stores: they wait until the stores reach the cache, which
+ * costs tens of cycles on every document. Here the block is put together in
+ * vector registers, from loads that may read into the padding, and written
+ * with full-width stores that the loads can forward from.
+ */
+ simdjson_inline size_t get_remainder_padded(uint8_t *dst) const;
simdjson_inline void advance();
private:
const uint8_t *buf;
@@ -49929,47 +49134,6 @@ private:
size_t idx;
};
-// Routines to print masks and text for debugging bitmask operations
-simdjson_unused static char * format_input_text_64(const uint8_t *text) {
- static char buf[sizeof(simd8x64<uint8_t>) + 1];
- for (size_t i=0; i<sizeof(simd8x64<uint8_t>); i++) {
- buf[i] = int8_t(text[i]) < ' ' ? '_' : int8_t(text[i]);
- }
- buf[sizeof(simd8x64<uint8_t>)] = '\0';
- return buf;
-}
-
-// Routines to print masks and text for debugging bitmask operations
-simdjson_unused static char * format_input_text(const simd8x64<uint8_t>& in) {
- static char buf[sizeof(simd8x64<uint8_t>) + 1];
- in.store(reinterpret_cast<uint8_t*>(buf));
- for (size_t i=0; i<sizeof(simd8x64<uint8_t>); i++) {
- if (buf[i] < ' ') { buf[i] = '_'; }
- }
- buf[sizeof(simd8x64<uint8_t>)] = '\0';
- return buf;
-}
-
-simdjson_unused static char * format_input_text(const simd8x64<uint8_t>& in, uint64_t mask) {
- static char buf[sizeof(simd8x64<uint8_t>) + 1];
- in.store(reinterpret_cast<uint8_t*>(buf));
- for (size_t i=0; i<sizeof(simd8x64<uint8_t>); i++) {
- if (buf[i] <= ' ') { buf[i] = '_'; }
- if (!(mask & (size_t(1) << i))) { buf[i] = ' '; }
- }
- buf[sizeof(simd8x64<uint8_t>)] = '\0';
- return buf;
-}
-
-simdjson_unused static char * format_mask(uint64_t mask) {
- static char buf[sizeof(simd8x64<uint8_t>) + 1];
- for (size_t i=0; i<64; i++) {
- buf[i] = (mask & (size_t(1) << i)) ? 'X' : ' ';
- }
- buf[64] = '\0';
- return buf;
-}
-
template<size_t STEP_SIZE>
simdjson_inline buf_block_reader<STEP_SIZE>::buf_block_reader(const uint8_t *_buf, size_t _len) : buf{_buf}, len{_len}, lenminusstep{len < STEP_SIZE ? 0 : len - STEP_SIZE}, idx{0} {}
@@ -49994,6 +49158,38 @@ simdjson_inline size_t buf_block_reader<STEP_SIZE>::get_remainder(uint8_t *dst)
return len - idx;
}
+// keep_mask[128 - n + i] is 0xFF for i < n and 0 otherwise.
+alignas(64) static const uint8_t keep_mask[256] = {
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+template<size_t STEP_SIZE>
+simdjson_inline size_t buf_block_reader<STEP_SIZE>::get_remainder_padded(uint8_t *dst) const {
+ static_assert(STEP_SIZE <= 128, "keep_mask covers blocks of up to 128 bytes");
+ static_assert(sizeof(simd::simd8<uint8_t>) <= SIMDJSON_PADDING, "a vector load at the last byte must stay in the padding");
+ const size_t remaining = len - idx;
+ if (remaining == 0) { return 0; }
+ const simd::simd8<uint8_t> spaces = simd::simd8<uint8_t>::splat(0x20);
+ for (size_t off = 0; off < STEP_SIZE; off += sizeof(simd::simd8<uint8_t>)) {
+ simd::simd8<uint8_t> v = spaces;
+ if (off < remaining) {
+ // Starts before len, so it ends within the padding.
+ const simd::simd8<uint8_t> in = simd::simd8<uint8_t>::load(buf + idx + off);
+ const simd::simd8<uint8_t> keep = simd::simd8<uint8_t>::load(keep_mask + 128 - remaining + off);
+ v = spaces ^ ((in ^ spaces) & keep);
+ }
+ v.store(dst + off);
+ }
+ return remaining;
+}
+
template<size_t STEP_SIZE>
simdjson_inline void buf_block_reader<STEP_SIZE>::advance() {
idx += STEP_SIZE;
@@ -50694,6 +49890,20 @@ simdjson_inline bool ends_with_partial_scalar(dom_parser_implementation &parser,
e != ' ' && e != '\t' && e != '\n' && e != '\r';
}
+/**
+ * The structural index past the last one of a partial batch is the end of this
+ * window, not a token of it: the byte there is the first byte of the next
+ * window. A finished value must not be rejected because that byte is ',', ':',
+ * ']' or '}'. A last document that itself ends with '{', '[', ',' or ':' is
+ * invalid JSON, yet its brackets may balance: walking it, the On-Demand API
+ * could read the next batch as a value. We leave such a document to the next
+ * batch, so that the batch ends between two documents.
+ */
+simdjson_inline bool ends_with_unfinished_document(dom_parser_implementation &parser) {
+ const uint8_t last = parser.buf[parser.structural_indexes[parser.n_structural_indexes - 1]];
+ return last == '{' || last == '[' || last == ',' || last == ':';
+}
+
simdjson_inline uint32_t find_next_document_index(dom_parser_implementation &parser, bool defer_last = false) {
// Variant: do not count separately, just figure out depth
if(parser.n_structural_indexes == 0) { return 0; }
@@ -51375,7 +50585,8 @@ error_code json_structural_indexer::index(const uint8_t *buf, size_t len, dom_pa
// Take care of the last block (will always be there unless file is empty which is
// not supposed to happen.)
uint8_t block[STEP_SIZE];
- if (simdjson_unlikely(reader.get_remainder(block) == 0)) { return UNEXPECTED_ERROR; }
+ const size_t remaining = parser._unpadded ? reader.get_remainder(block) : reader.get_remainder_padded(block);
+ if (simdjson_unlikely(remaining == 0)) { return UNEXPECTED_ERROR; }
indexer.step<STEP_SIZE>(block, reader);
return indexer.finish(parser, reader.block_index(), len, partial);
}
@@ -51409,6 +50620,20 @@ simdjson_inline void json_structural_indexer::next(const simd::simd8x64<uint8_t>
unescaped_chars_error |= block.non_quote_inside_string(unescaped);
}
+// Drops the unclosed string that ends a json_sequence batch and leaves its
+// start in structural_indexes[n_structural_indexes]. The last index is its
+// opening quote, unless the quote directly follows an RS: the scanner reads
+// RS as a scalar byte, so it indexes the RS and not the quote. Keep the RS.
+simdjson_inline void drop_json_sequence_unclosed_string(dom_parser_implementation &parser, size_t len) noexcept {
+ uint32_t quote = parser.structural_indexes[parser.n_structural_indexes - 1];
+ if (parser.buf[quote] == '"') {
+ parser.n_structural_indexes--;
+ return;
+ }
+ while (quote < len && parser.buf[quote] != '"') { quote++; }
+ parser.structural_indexes[parser.n_structural_indexes] = quote;
+}
+
simdjson_inline error_code json_structural_indexer::finish(dom_parser_implementation &parser, size_t idx, size_t len, stage1_mode partial) {
// Write out the final iteration's structurals
indexer.write(uint32_t(idx-64), prev_structurals);
@@ -51465,8 +50690,13 @@ simdjson_inline error_code json_structural_indexer::finish(dom_parser_implementa
if (simdjson_unlikely(parser.n_structural_indexes == 0u)) { return CAPACITY; }
}
// We truncate the input to the end of the last complete document (or zero).
- auto new_structural_indexes = find_next_document_index(parser, !have_unclosed_string && ends_with_partial_scalar(parser, len));
+ const bool partial_scalar = !have_unclosed_string && ends_with_partial_scalar(parser, len);
+ const bool unfinished = ends_with_unfinished_document(parser);
+ auto new_structural_indexes = find_next_document_index(parser, partial_scalar || unfinished);
if (new_structural_indexes == 0 && parser.n_structural_indexes > 0) {
+ // The batch holds a single document, whose brackets balance but which
+ // cannot be valid JSON: it is not too big, it is invalid.
+ if (unfinished && find_next_document_index(parser, partial_scalar) != 0) { return TAPE_ERROR; }
if(parser.structural_indexes[0] == 0) {
// If the buffer is partial and we started at index 0 but the document is
// incomplete, it's too big to parse.
@@ -51510,7 +50740,7 @@ simdjson_inline error_code json_structural_indexer::finish(dom_parser_implementa
// A discarded unclosed string also caps how far the filter may scan.
size_t scan_len = len;
if(have_unclosed_string) {
- parser.n_structural_indexes--;
+ drop_json_sequence_unclosed_string(parser, len);
if (simdjson_unlikely(parser.n_structural_indexes == 0u)) { return CAPACITY; }
scan_len = parser.structural_indexes[parser.n_structural_indexes];
}
@@ -51529,12 +50759,17 @@ simdjson_inline error_code json_structural_indexer::finish(dom_parser_implementa
}
parser.n_structural_indexes = new_structural_indexes;
parser.structural_indexes[parser.n_structural_indexes] = next_batch_start;
+ // The next batch starts at an RS, which the On-Demand API never reads as a
+ // string, a container or a separator. But an invalid last document may make
+ // it skip that RS as a value: repeat it, so that the index past it is not
+ // the next record, which can be a string that does not close in this batch.
+ parser.structural_indexes[parser.n_structural_indexes + 1] = next_batch_start;
} else if (partial == stage1_mode::json_sequence_final) {
// RFC 7464: final batch, last document extends to EOF
// As above: a discarded unclosed string caps how far the filter may scan.
size_t scan_len = len;
if(have_unclosed_string) {
- parser.n_structural_indexes--;
+ drop_json_sequence_unclosed_string(parser, len);
scan_len = parser.structural_indexes[parser.n_structural_indexes];
}
uint32_t next_batch_start = uint32_t(len);
@@ -51952,7 +51187,7 @@ public:
*
* - increment_count(iter) - each time a value is found in an array or object.
*/
- template<bool STREAMING, bool UNPADDED, typename V>
+ template<bool STREAMING, bool UNPADDED, bool BOUNDED = false, typename V>
simdjson_warn_unused simdjson_inline error_code walk_document(V &visitor) noexcept;
/**
@@ -51969,8 +51204,8 @@ public:
*
* They may include invalid JSON as well (such as `1.2.3` or `ture`).
*/
- template<bool UNPADDED>
- simdjson_inline const uint8_t *peek() const noexcept;
+ template<bool UNPADDED, bool BOUNDED = false>
+ simdjson_inline const uint8_t *peek(const uint32_t *batch_end = nullptr) const noexcept;
/**
* Advance to the next token.
*
@@ -51978,8 +51213,8 @@ public:
*
* They may include invalid JSON as well (such as `1.2.3` or `ture`).
*/
- template<bool UNPADDED>
- simdjson_inline const uint8_t *advance() noexcept;
+ template<bool UNPADDED, bool BOUNDED = false>
+ simdjson_inline const uint8_t *advance(const uint32_t *batch_end = nullptr) noexcept;
/**
* Get the remaining length of the document, from the start of the current token.
*/
@@ -52027,7 +51262,7 @@ public:
simdjson_warn_unused simdjson_inline error_code visit_primitive(V &visitor, const uint8_t *value) noexcept;
};
-template<bool STREAMING, bool UNPADDED, typename V>
+template<bool STREAMING, bool UNPADDED, bool BOUNDED, typename V>
simdjson_warn_unused simdjson_inline error_code json_iterator::walk_document(V &visitor) noexcept {
logger::log_start();
@@ -52035,6 +51270,9 @@ simdjson_warn_unused simdjson_inline error_code json_iterator::walk_document(V &
// Start the document
//
if (at_eof()) { return EMPTY; }
+ // Only used when BOUNDED (see advance()); a local, so that it can stay in a
+ // register, and vanishes from the other walks.
+ const uint32_t *const batch_end = &dom_parser.structural_indexes[dom_parser.n_structural_indexes];
log_start_value("document");
SIMDJSON_TRY( visitor.visit_document_start(*this) );
@@ -52042,7 +51280,7 @@ simdjson_warn_unused simdjson_inline error_code json_iterator::walk_document(V &
// Read first value
//
{
- auto value = advance<UNPADDED>();
+ auto value = advance<UNPADDED, BOUNDED>(batch_end);
// Make sure the outer object or array is closed before continuing; otherwise, there are ways we
// could get into memory corruption. See https://github.com/simdjson/simdjson/issues/906
@@ -52054,8 +51292,8 @@ simdjson_warn_unused simdjson_inline error_code json_iterator::walk_document(V &
}
switch (*value) {
- case '{': if (*peek<UNPADDED>() == '}') { advance<UNPADDED>(); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin;
- case '[': if (*peek<UNPADDED>() == ']') { advance<UNPADDED>(); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin;
+ case '{': if (*peek<UNPADDED, BOUNDED>(batch_end) == '}') { advance<UNPADDED, BOUNDED>(batch_end); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin;
+ case '[': if (*peek<UNPADDED, BOUNDED>(batch_end) == ']') { advance<UNPADDED, BOUNDED>(batch_end); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin;
default: SIMDJSON_TRY( visitor.visit_root_primitive(*this, value) ); break;
}
}
@@ -52072,29 +51310,29 @@ object_begin:
SIMDJSON_TRY( visitor.visit_object_start(*this) );
{
- auto key = advance<UNPADDED>();
+ auto key = advance<UNPADDED, BOUNDED>(batch_end);
if (*key != '"') { log_error("Object does not start with a key"); return TAPE_ERROR; }
SIMDJSON_TRY( visitor.increment_count(*this) );
SIMDJSON_TRY( visitor.visit_key(*this, key) );
}
object_field:
- if (simdjson_unlikely( *advance<UNPADDED>() != ':' )) { log_error("Missing colon after key in object"); return TAPE_ERROR; }
+ if (simdjson_unlikely( (*advance<UNPADDED, BOUNDED>(batch_end) != ':') )) { log_error("Missing colon after key in object"); return TAPE_ERROR; }
{
- auto value = advance<UNPADDED>();
+ auto value = advance<UNPADDED, BOUNDED>(batch_end);
switch (*value) {
- case '{': if (*peek<UNPADDED>() == '}') { advance<UNPADDED>(); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin;
- case '[': if (*peek<UNPADDED>() == ']') { advance<UNPADDED>(); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin;
+ case '{': if (*peek<UNPADDED, BOUNDED>(batch_end) == '}') { advance<UNPADDED, BOUNDED>(batch_end); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin;
+ case '[': if (*peek<UNPADDED, BOUNDED>(batch_end) == ']') { advance<UNPADDED, BOUNDED>(batch_end); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin;
default: SIMDJSON_TRY( visitor.visit_primitive(*this, value) ); break;
}
}
object_continue:
- switch (*advance<UNPADDED>()) {
+ switch (*advance<UNPADDED, BOUNDED>(batch_end)) {
case ',':
SIMDJSON_TRY( visitor.increment_count(*this) );
{
- auto key = advance<UNPADDED>();
+ auto key = advance<UNPADDED, BOUNDED>(batch_end);
if (simdjson_unlikely( *key != '"' )) { log_error("Key string missing at beginning of field in object"); return TAPE_ERROR; }
SIMDJSON_TRY( visitor.visit_key(*this, key) );
}
@@ -52122,16 +51360,16 @@ array_begin:
array_value:
{
- auto value = advance<UNPADDED>();
+ auto value = advance<UNPADDED, BOUNDED>(batch_end);
switch (*value) {
- case '{': if (*peek<UNPADDED>() == '}') { advance<UNPADDED>(); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin;
- case '[': if (*peek<UNPADDED>() == ']') { advance<UNPADDED>(); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin;
+ case '{': if (*peek<UNPADDED, BOUNDED>(batch_end) == '}') { advance<UNPADDED, BOUNDED>(batch_end); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin;
+ case '[': if (*peek<UNPADDED, BOUNDED>(batch_end) == ']') { advance<UNPADDED, BOUNDED>(batch_end); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin;
default: SIMDJSON_TRY( visitor.visit_primitive(*this, value) ); break;
}
}
array_continue:
- switch (*advance<UNPADDED>()) {
+ switch (*advance<UNPADDED, BOUNDED>(batch_end)) {
case ',': SIMDJSON_TRY( visitor.increment_count(*this) ); goto array_value;
case ']': log_end_value("array"); SIMDJSON_TRY( visitor.visit_array_end(*this) ); goto scope_end;
default: log_error("Missing comma between array values"); return TAPE_ERROR;
@@ -52161,8 +51399,18 @@ simdjson_inline json_iterator::json_iterator(dom_parser_implementation &_dom_par
// Stage 1 leaves a sentinel index of value `len`; reading buf[len] requires
// padding. For UNPADDED, return a dummy so walk_document can fail cleanly (#2815).
-template<bool UNPADDED>
-simdjson_inline const uint8_t *json_iterator::peek() const noexcept {
+//
+// When BOUNDED (see dom_parser_implementation::_bounded_batch), a document may
+// not run past batch_end, one past the last structural index of the batch:
+// past it, we return the same dummy, and walk_document fails cleanly.
+template<bool UNPADDED, bool BOUNDED>
+simdjson_inline const uint8_t *json_iterator::peek(const uint32_t *batch_end) const noexcept {
+ SIMDJSON_IF_CONSTEXPR (BOUNDED) {
+ if (simdjson_unlikely(next_structural >= batch_end)) {
+ static constexpr uint8_t streaming_eof_sentinel = 0;
+ return &streaming_eof_sentinel;
+ }
+ }
const uint32_t idx = *(next_structural);
SIMDJSON_IF_CONSTEXPR (UNPADDED) {
if (simdjson_unlikely(idx >= dom_parser.len)) {
@@ -52172,8 +51420,14 @@ simdjson_inline const uint8_t *json_iterator::peek() const noexcept {
}
return &buf[idx];
}
-template<bool UNPADDED>
-simdjson_inline const uint8_t *json_iterator::advance() noexcept {
+template<bool UNPADDED, bool BOUNDED>
+simdjson_inline const uint8_t *json_iterator::advance(const uint32_t *batch_end) noexcept {
+ SIMDJSON_IF_CONSTEXPR (BOUNDED) {
+ if (simdjson_unlikely(next_structural >= batch_end)) {
+ static constexpr uint8_t streaming_eof_sentinel = 0;
+ return &streaming_eof_sentinel;
+ }
+ }
const uint32_t idx = *(next_structural++);
SIMDJSON_IF_CONSTEXPR (UNPADDED) {
if (simdjson_unlikely(idx >= dom_parser.len)) {
@@ -52732,6 +51986,30 @@ struct tape_builder {
return iter.walk_document<STREAMING, false>(builder);
}
}
+
+ // The stage 2 entry points of dom::document_stream, which picks one per batch
+ // (see internal::dom_parser_implementation::stage2_next_function). Out of
+ // line, so that stage2_next() can share the code.
+ simdjson_warn_unused static simdjson_never_inline error_code parse_next_document(
+ internal::dom_parser_implementation &dom_parser, dom::document &doc) noexcept {
+ return parse_document<true>(static_cast<dom_parser_implementation &>(dom_parser), doc);
+ }
+
+ // A document of a stream batch that stage 2 must bound to its structural
+ // indexes (see internal::dom_parser_implementation::_bounded_batch).
+ simdjson_warn_unused static simdjson_never_inline error_code parse_next_bounded_document(
+ internal::dom_parser_implementation &_dom_parser, dom::document &doc) noexcept {
+ dom_parser_implementation &dom_parser = static_cast<dom_parser_implementation &>(_dom_parser);
+ dom_parser.doc = &doc;
+ json_iterator iter(dom_parser, dom_parser.next_structural_index);
+ if (dom_parser._unpadded) {
+ tape_builder_impl<true> builder(doc);
+ return iter.walk_document<true, true, true>(builder);
+ } else {
+ tape_builder_impl<false> builder(doc);
+ return iter.walk_document<true, false, true>(builder);
+ }
+ }
};
template <bool UNPADDED>
@@ -53146,14 +52424,6 @@ simdjson_inline bool is_ascii(const simd8x64<uint8_t>& input) {
return input.reduce_or().is_ascii();
}
-simdjson_unused simdjson_inline simd8<bool> must_be_continuation(const simd8<uint8_t> prev1, const simd8<uint8_t> prev2, const simd8<uint8_t> prev3) {
- simd8<uint8_t> is_second_byte = prev1.saturating_sub(0xc0u-1); // Only 11______ will be > 0
- simd8<uint8_t> is_third_byte = prev2.saturating_sub(0xe0u-1); // Only 111_____ will be > 0
- simd8<uint8_t> is_fourth_byte = prev3.saturating_sub(0xf0u-1); // Only 1111____ will be > 0
- // Caller requires a bool (all 1's). All values resulting from the subtraction will be <= 64, so signed comparison is fine.
- return simd8<int8_t>(is_second_byte | is_third_byte | is_fourth_byte) > int8_t(0);
-}
-
simdjson_inline simd8<uint8_t> must_be_2_3_continuation(const simd8<uint8_t> prev2, const simd8<uint8_t> prev3) {
simd8<uint8_t> is_third_byte = prev2.saturating_sub(0xe0u-0x80); // Only 111_____ will be >= 0x80
simd8<uint8_t> is_fourth_byte = prev3.saturating_sub(0xf0u-0x80); // Only 1111____ will be >= 0x80
@@ -53194,7 +52464,11 @@ simdjson_warn_unused error_code dom_parser_implementation::stage2(dom::document
}
simdjson_warn_unused error_code dom_parser_implementation::stage2_next(dom::document &_doc) noexcept {
- return stage2::tape_builder::parse_document<true>(*this, _doc);
+ return stage2::tape_builder::parse_next_document(*this, _doc);
+}
+
+internal::dom_parser_implementation::stage2_next_function_t dom_parser_implementation::stage2_next_function(bool bounded) const noexcept {
+ return bounded ? stage2::tape_builder::parse_next_bounded_document : stage2::tape_builder::parse_next_document;
}
SIMDJSON_NO_SANITIZE_MEMORY
@@ -53860,30 +53134,6 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin
}
-struct escaping {
- static constexpr uint32_t BYTES_PROCESSED = 16;
- simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
-
- simdjson_inline bool has_escape() { return escape_bits != 0; }
- simdjson_inline int escape_index() { return trailing_zeroes(escape_bits); }
-
- uint64_t escape_bits;
-}; // struct escaping
-
-
-
-simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
- static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes");
- simd8<uint8_t> v(src);
- v.store(dst);
- simd8<bool> is_quote = (v == '"');
- simd8<bool> is_backslash = (v == '\\');
- simd8<bool> is_control = (v < 32);
- return {
- static_cast<uint64_t>((is_backslash | is_quote | is_control).to_bitmask())
- };
-}
-
} // unnamed namespace
} // namespace lasx
} // namespace simdjson
@@ -54278,6 +53528,7 @@ public:
simdjson_warn_unused error_code stage1(const uint8_t *buf, size_t len, stage1_mode partial) noexcept final;
simdjson_warn_unused error_code stage2(dom::document &doc) noexcept final;
simdjson_warn_unused error_code stage2_next(dom::document &doc) noexcept final;
+ stage2_next_function_t stage2_next_function(bool bounded) const noexcept final;
simdjson_warn_unused uint8_t *parse_string(const uint8_t *src, uint8_t *dst, bool allow_replacement) const noexcept final;
simdjson_warn_unused uint8_t *parse_wobbly_string(const uint8_t *src, uint8_t *dst) const noexcept final;
inline simdjson_warn_unused error_code set_capacity(size_t capacity) noexcept final;
@@ -55039,21 +54290,6 @@ static bool parse_float_fallback(const uint8_t *ptr, double *outDouble) {
return !(*outDouble > (std::numeric_limits<double>::max)() || *outDouble < std::numeric_limits<double>::lowest());
}
-static bool parse_float_fallback(const uint8_t *ptr, const uint8_t *end_ptr, double *outDouble) {
- *outDouble = simdjson::internal::from_chars(reinterpret_cast<const char *>(ptr), reinterpret_cast<const char *>(end_ptr));
- // We do not accept infinite values.
-
- // Detecting finite values in a portable manner is ridiculously hard, ideally
- // we would want to do:
- // return !std::isfinite(*outDouble);
- // but that mysteriously fails under legacy/old libc++ libraries, see
- // https://github.com/simdjson/simdjson/issues/1286
- //
- // Therefore, fall back to this solution (the extra parens are there
- // to handle that max may be a macro on windows).
- return !(*outDouble > (std::numeric_limits<double>::max)() || *outDouble < std::numeric_limits<double>::lowest());
-}
-
// Same as parse_float_fallback, but for binary32 (float) values. Going through
// the binary64 fallback and then rounding to binary32 would be subject to
// double rounding, so we run the fallback algorithm directly on binary32.
@@ -55599,57 +54835,6 @@ simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned(const u
return i;
}
-
-
-// Parse any number from 0 to 18,446,744,073,709,551,615
-// Never read at src_end or beyond
-simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned(const uint8_t * const src, const uint8_t * const src_end) noexcept {
- const uint8_t *p = src;
- //
- // Parse the integer part.
- //
- // PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
- const uint8_t *const start_digits = p;
- uint64_t i = 0;
- while ((p != src_end) && parse_digit(*p, i)) { p++; }
-
- // If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
- // Optimization note: size_t is expected to be unsigned.
- size_t digit_count = size_t(p - start_digits);
- // The longest positive 64-bit number is 20 digits.
- // We do it this way so we don't trigger this branch unless we must.
- // Optimization note: the compiler can probably merge
- // ((digit_count == 0) || (digit_count > 20))
- // into a single branch since digit_count is unsigned.
- if ((digit_count == 0) || (digit_count > 20)) { return INCORRECT_TYPE; }
- // Here digit_count > 0.
- if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
- // We can do the following...
- // if (!jsoncharutils::is_structural_or_whitespace(*p)) {
- // return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
- // }
- // as a single table lookup:
- if ((p != src_end) && integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); }
-
- if (digit_count == 20) {
- // Positive overflow check:
- // - A 20 digit number starting with 2-9 is overflow, because 18,446,744,073,709,551,615 is the
- // biggest uint64_t.
- // - A 20 digit number starting with 1 is overflow if it is less than INT64_MAX.
- // If we got here, it's a 20 digit number starting with the digit "1".
- // - If a 20 digit number starting with 1 overflowed (i*10+digit), the result will be smaller
- // than 1,553,255,926,290,448,384.
- // - That is smaller than the smallest possible 20-digit number the user could write:
- // 10,000,000,000,000,000,000.
- // - Therefore, if the number is positive and lower than that, it's overflow.
- // - The value we are looking at is less than or equal to INT64_MAX.
- //
- if (src[0] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INCORRECT_TYPE; }
- }
-
- return i;
-}
-
// Parse any number from 0 to 18,446,744,073,709,551,615
simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned_in_string(const uint8_t * const src) noexcept {
const uint8_t *p = src + 1;
@@ -55742,50 +54927,6 @@ simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer(const uin
return negative ? (~i+1) : i;
}
-// Parse any number from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
-// Never read at src_end or beyond
-simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer(const uint8_t * const src, const uint8_t * const src_end) noexcept {
- //
- // Check for minus sign
- //
- if(src == src_end) { return NUMBER_ERROR; }
- bool negative = (*src == '-');
- const uint8_t *p = src + uint8_t(negative);
-
- //
- // Parse the integer part.
- //
- // PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
- const uint8_t *const start_digits = p;
- uint64_t i = 0;
- while ((p != src_end) && parse_digit(*p, i)) { p++; }
-
- // If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
- // Optimization note: size_t is expected to be unsigned.
- size_t digit_count = size_t(p - start_digits);
- // We go from
- // -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
- // so we can never represent numbers that have more than 19 digits.
- size_t longest_digit_count = 19;
- // Optimization note: the compiler can probably merge
- // ((digit_count == 0) || (digit_count > longest_digit_count))
- // into a single branch since digit_count is unsigned.
- if ((digit_count == 0) || (digit_count > longest_digit_count)) { return INCORRECT_TYPE; }
- // Here digit_count > 0.
- if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
- // We can do the following...
- // if (!jsoncharutils::is_structural_or_whitespace(*p)) {
- // return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
- // }
- // as a single table lookup:
- if((p != src_end) && integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); }
- // Negative numbers have can go down to - INT64_MAX - 1 whereas positive numbers are limited to INT64_MAX.
- // Performance note: This check is only needed when digit_count == longest_digit_count but it is
- // so cheap that we might as well always make it.
- if(i > uint64_t(INT64_MAX) + uint64_t(negative)) { return INCORRECT_TYPE; }
- return negative ? (~i+1) : i;
-}
-
// Parse any number from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer_in_string(const uint8_t *src) noexcept {
//
@@ -55891,8 +55032,11 @@ simdjson_unused simdjson_inline simdjson_result<double> parse_double(const uint8
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
+ // no exp digits
+ if (p-start_exp_digits == 0) { return NUMBER_ERROR; }
+ // 20+ exp digits (e.g., 0e9999999999999999999999) are valid, but exp may
+ // have wrapped around: leave such numbers to the fallback parser.
+ overflow = overflow || p-start_exp_digits > 19;
exponent += exp_neg ? 0-exp : exp;
}
@@ -55982,8 +55126,11 @@ simdjson_unused simdjson_inline simdjson_result<float> parse_float(const uint8_t
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
+ // no exp digits
+ if (p-start_exp_digits == 0) { return NUMBER_ERROR; }
+ // 20+ exp digits (e.g., 0e9999999999999999999999) are valid, but exp may
+ // have wrapped around: leave such numbers to the fallback parser.
+ overflow = overflow || p-start_exp_digits > 19;
exponent += exp_neg ? 0-exp : exp;
}
@@ -56063,26 +55210,37 @@ simdjson_unused simdjson_inline simdjson_result<number_type> get_number_type(con
return number_type::floating_point_number;
}
-// Never read at src_end or beyond
-simdjson_unused simdjson_inline simdjson_result<double> parse_double(const uint8_t * src, const uint8_t * const src_end) noexcept {
- if(src == src_end) { return NUMBER_ERROR; }
+simdjson_unused simdjson_inline simdjson_result<double> parse_double_in_string(const uint8_t * src) noexcept {
//
// Check for minus sign
//
- bool negative = (*src == '-');
- src += uint8_t(negative);
+ bool negative = (*(src + 1) == '-');
+ src += uint8_t(negative) + 1;
//
// Parse the integer part.
//
uint64_t i = 0;
const uint8_t *p = src;
- if(p == src_end) { return NUMBER_ERROR; }
- p += parse_digit(*p, i);
- bool leading_zero = (i == 0);
- while ((p != src_end) && parse_digit(*p, i)) { p++; }
+ bool leading_zero;
+ parse_float_integer_digits(p, i, leading_zero);
// no integer digits, or 0123 (zero must be solo)
- if ( p == src ) { return INCORRECT_TYPE; }
+ if ( p == src ) {
+#if SIMDJSON_ENABLE_NAN_INF
+ // If there are no leading digits, attempt to parse numbers that are either
+ // NaN or Infinity
+ if (atomparsing::is_valid_inf_in_string(src)) {
+ double inf = std::numeric_limits<double>::infinity();
+ return negative ? -inf : inf;
+ }
+
+ if (atomparsing::is_valid_nan_in_string(src)) {
+ return std::numeric_limits<double>::quiet_NaN();
+ }
+#endif
+
+ return INCORRECT_TYPE;
+ }
if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; }
//
@@ -56090,12 +55248,11 @@ simdjson_unused simdjson_inline simdjson_result<double> parse_double(const uint8
//
int64_t exponent = 0;
bool overflow;
- if (simdjson_likely((p != src_end) && (*p == '.'))) {
+ if (simdjson_likely(*p == '.')) {
p++;
const uint8_t *start_decimal_digits = p;
- if ((p == src_end) || !parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits
- p++;
- while ((p != src_end) && parse_digit(*p, i)) { p++; }
+ parse_fraction_digits(p, i);
+ if (p == start_decimal_digits) { return NUMBER_ERROR; } // no decimal digits
exponent = -(p - start_decimal_digits);
// Overflow check. More than 19 digits (minus the decimal) may be overflow.
@@ -56104,7 +55261,7 @@ simdjson_unused simdjson_inline simdjson_result<double> parse_double(const uint8
// Skip leading 0.00000 and see if it still overflows
const uint8_t *start_digits = src + 2;
while (*start_digits == '0') { start_digits++; }
- overflow = start_digits-src > 19;
+ overflow = p-start_digits > 19;
}
} else {
overflow = p-src > 19;
@@ -56113,22 +55270,24 @@ simdjson_unused simdjson_inline simdjson_result<double> parse_double(const uint8
//
// Parse the exponent
//
- if ((p != src_end) && (*p == 'e' || *p == 'E')) {
+ if (*p == 'e' || *p == 'E') {
p++;
- if(p == src_end) { return NUMBER_ERROR; }
bool exp_neg = *p == '-';
p += exp_neg || *p == '+';
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
- while ((p != src_end) && parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
+ while (parse_digit(*p, exp)) { p++; }
+ // no exp digits
+ if (p-start_exp_digits == 0) { return NUMBER_ERROR; }
+ // 20+ exp digits (e.g., 0e9999999999999999999999) are valid, but exp may
+ // have wrapped around: leave such numbers to the fallback parser.
+ overflow = overflow || p-start_exp_digits > 19;
exponent += exp_neg ? 0-exp : exp;
}
- if ((p != src_end) && jsoncharutils::is_not_structural_or_whitespace(*p)) { return NUMBER_ERROR; }
+ if (*p != '"') { return NUMBER_ERROR; }
overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power;
@@ -56139,13 +55298,15 @@ simdjson_unused simdjson_inline simdjson_result<double> parse_double(const uint8
if (simdjson_likely(!overflow)) {
if (compute_float_64(exponent, i, negative, d)) { return d; }
}
- if (!parse_float_fallback(src - uint8_t(negative), src_end, &d)) {
+ if (!parse_float_fallback(src - uint8_t(negative), &d)) {
return NUMBER_ERROR;
}
return d;
}
-simdjson_unused simdjson_inline simdjson_result<double> parse_double_in_string(const uint8_t * src) noexcept {
+// Parse a JSON number held inside a JSON string into a binary32 (float) value.
+// See parse_float for why we do not simply round parse_double_in_string.
+simdjson_unused simdjson_inline simdjson_result<float> parse_float_in_string(const uint8_t * src) noexcept {
//
// Check for minus sign
//
@@ -56165,12 +55326,12 @@ simdjson_unused simdjson_inline simdjson_result<double> parse_double_in_string(c
// If there are no leading digits, attempt to parse numbers that are either
// NaN or Infinity
if (atomparsing::is_valid_inf_in_string(src)) {
- double inf = std::numeric_limits<double>::infinity();
+ float inf = std::numeric_limits<float>::infinity();
return negative ? -inf : inf;
}
if (atomparsing::is_valid_nan_in_string(src)) {
- return std::numeric_limits<double>::quiet_NaN();
+ return std::numeric_limits<float>::quiet_NaN();
}
#endif
@@ -56213,101 +55374,11 @@ simdjson_unused simdjson_inline simdjson_result<double> parse_double_in_string(c
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
-
- exponent += exp_neg ? 0-exp : exp;
- }
-
- if (*p != '"') { return NUMBER_ERROR; }
-
- overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power;
-
- //
- // Assemble (or slow-parse) the float
- //
- double d;
- if (simdjson_likely(!overflow)) {
- if (compute_float_64(exponent, i, negative, d)) { return d; }
- }
- if (!parse_float_fallback(src - uint8_t(negative), &d)) {
- return NUMBER_ERROR;
- }
- return d;
-}
-
-// Parse a JSON number held inside a JSON string into a binary32 (float) value.
-// See parse_float for why we do not simply round parse_double_in_string.
-simdjson_unused simdjson_inline simdjson_result<float> parse_float_in_string(const uint8_t * src) noexcept {
- //
- // Check for minus sign
- //
- bool negative = (*(src + 1) == '-');
- src += uint8_t(negative) + 1;
-
- //
- // Parse the integer part.
- //
- uint64_t i = 0;
- const uint8_t *p = src;
- bool leading_zero;
- parse_float_integer_digits(p, i, leading_zero);
- // no integer digits, or 0123 (zero must be solo)
- if ( p == src ) {
-#if SIMDJSON_ENABLE_NAN_INF
- // If there are no leading digits, attempt to parse numbers that are either
- // NaN or Infinity
- if (atomparsing::is_valid_inf_in_string(src)) {
- float inf = std::numeric_limits<float>::infinity();
- return negative ? -inf : inf;
- }
-
- if (atomparsing::is_valid_nan_in_string(src)) {
- return std::numeric_limits<float>::quiet_NaN();
- }
-#endif
-
- return INCORRECT_TYPE;
- }
- if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; }
-
- //
- // Parse the decimal part.
- //
- int64_t exponent = 0;
- bool overflow;
- if (simdjson_likely(*p == '.')) {
- p++;
- const uint8_t *start_decimal_digits = p;
- parse_fraction_digits(p, i);
- if (p == start_decimal_digits) { return NUMBER_ERROR; } // no decimal digits
- exponent = -(p - start_decimal_digits);
-
- // Overflow check. More than 19 digits (minus the decimal) may be overflow.
- overflow = p-src-1 > 19;
- if (simdjson_unlikely(overflow && leading_zero)) {
- // Skip leading 0.00000 and see if it still overflows
- const uint8_t *start_digits = src + 2;
- while (*start_digits == '0') { start_digits++; }
- overflow = p-start_digits > 19;
- }
- } else {
- overflow = p-src > 19;
- }
-
- //
- // Parse the exponent
- //
- if (*p == 'e' || *p == 'E') {
- p++;
- bool exp_neg = *p == '-';
- p += exp_neg || *p == '+';
-
- uint64_t exp = 0;
- const uint8_t *start_exp_digits = p;
- while (parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
+ // no exp digits
+ if (p-start_exp_digits == 0) { return NUMBER_ERROR; }
+ // 20+ exp digits (e.g., 0e9999999999999999999999) are valid, but exp may
+ // have wrapped around: leave such numbers to the fallback parser.
+ overflow = overflow || p-start_exp_digits > 19;
exponent += exp_neg ? 0-exp : exp;
}
@@ -57143,30 +56214,6 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin
}
-struct escaping {
- static constexpr uint32_t BYTES_PROCESSED = 16;
- simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
-
- simdjson_inline bool has_escape() { return escape_bits != 0; }
- simdjson_inline int escape_index() { return trailing_zeroes(escape_bits); }
-
- uint64_t escape_bits;
-}; // struct escaping
-
-
-
-simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
- static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes");
- simd8<uint8_t> v(src);
- v.store(dst);
- simd8<bool> is_quote = (v == '"');
- simd8<bool> is_backslash = (v == '\\');
- simd8<bool> is_control = (v < 32);
- return {
- static_cast<uint64_t>((is_backslash | is_quote | is_control).to_bitmask())
- };
-}
-
} // unnamed namespace
} // namespace lasx
} // namespace simdjson
@@ -57336,6 +56383,17 @@ public:
* @return the number of effective characters in the last block.
*/
simdjson_inline size_t get_remainder(uint8_t *dst) const;
+ /**
+ * Like get_remainder, for input followed by SIMDJSON_PADDING readable bytes.
+ *
+ * get_remainder writes the block with a memset and a memcpy of the remaining
+ * length, and the vector loads that read it back cannot be forwarded from
+ * those narrower stores: they wait until the stores reach the cache, which
+ * costs tens of cycles on every document. Here the block is put together in
+ * vector registers, from loads that may read into the padding, and written
+ * with full-width stores that the loads can forward from.
+ */
+ simdjson_inline size_t get_remainder_padded(uint8_t *dst) const;
simdjson_inline void advance();
private:
const uint8_t *buf;
@@ -57344,47 +56402,6 @@ private:
size_t idx;
};
-// Routines to print masks and text for debugging bitmask operations
-simdjson_unused static char * format_input_text_64(const uint8_t *text) {
- static char buf[sizeof(simd8x64<uint8_t>) + 1];
- for (size_t i=0; i<sizeof(simd8x64<uint8_t>); i++) {
- buf[i] = int8_t(text[i]) < ' ' ? '_' : int8_t(text[i]);
- }
- buf[sizeof(simd8x64<uint8_t>)] = '\0';
- return buf;
-}
-
-// Routines to print masks and text for debugging bitmask operations
-simdjson_unused static char * format_input_text(const simd8x64<uint8_t>& in) {
- static char buf[sizeof(simd8x64<uint8_t>) + 1];
- in.store(reinterpret_cast<uint8_t*>(buf));
- for (size_t i=0; i<sizeof(simd8x64<uint8_t>); i++) {
- if (buf[i] < ' ') { buf[i] = '_'; }
- }
- buf[sizeof(simd8x64<uint8_t>)] = '\0';
- return buf;
-}
-
-simdjson_unused static char * format_input_text(const simd8x64<uint8_t>& in, uint64_t mask) {
- static char buf[sizeof(simd8x64<uint8_t>) + 1];
- in.store(reinterpret_cast<uint8_t*>(buf));
- for (size_t i=0; i<sizeof(simd8x64<uint8_t>); i++) {
- if (buf[i] <= ' ') { buf[i] = '_'; }
- if (!(mask & (size_t(1) << i))) { buf[i] = ' '; }
- }
- buf[sizeof(simd8x64<uint8_t>)] = '\0';
- return buf;
-}
-
-simdjson_unused static char * format_mask(uint64_t mask) {
- static char buf[sizeof(simd8x64<uint8_t>) + 1];
- for (size_t i=0; i<64; i++) {
- buf[i] = (mask & (size_t(1) << i)) ? 'X' : ' ';
- }
- buf[64] = '\0';
- return buf;
-}
-
template<size_t STEP_SIZE>
simdjson_inline buf_block_reader<STEP_SIZE>::buf_block_reader(const uint8_t *_buf, size_t _len) : buf{_buf}, len{_len}, lenminusstep{len < STEP_SIZE ? 0 : len - STEP_SIZE}, idx{0} {}
@@ -57409,6 +56426,38 @@ simdjson_inline size_t buf_block_reader<STEP_SIZE>::get_remainder(uint8_t *dst)
return len - idx;
}
+// keep_mask[128 - n + i] is 0xFF for i < n and 0 otherwise.
+alignas(64) static const uint8_t keep_mask[256] = {
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+template<size_t STEP_SIZE>
+simdjson_inline size_t buf_block_reader<STEP_SIZE>::get_remainder_padded(uint8_t *dst) const {
+ static_assert(STEP_SIZE <= 128, "keep_mask covers blocks of up to 128 bytes");
+ static_assert(sizeof(simd::simd8<uint8_t>) <= SIMDJSON_PADDING, "a vector load at the last byte must stay in the padding");
+ const size_t remaining = len - idx;
+ if (remaining == 0) { return 0; }
+ const simd::simd8<uint8_t> spaces = simd::simd8<uint8_t>::splat(0x20);
+ for (size_t off = 0; off < STEP_SIZE; off += sizeof(simd::simd8<uint8_t>)) {
+ simd::simd8<uint8_t> v = spaces;
+ if (off < remaining) {
+ // Starts before len, so it ends within the padding.
+ const simd::simd8<uint8_t> in = simd::simd8<uint8_t>::load(buf + idx + off);
+ const simd::simd8<uint8_t> keep = simd::simd8<uint8_t>::load(keep_mask + 128 - remaining + off);
+ v = spaces ^ ((in ^ spaces) & keep);
+ }
+ v.store(dst + off);
+ }
+ return remaining;
+}
+
template<size_t STEP_SIZE>
simdjson_inline void buf_block_reader<STEP_SIZE>::advance() {
idx += STEP_SIZE;
@@ -58109,6 +57158,20 @@ simdjson_inline bool ends_with_partial_scalar(dom_parser_implementation &parser,
e != ' ' && e != '\t' && e != '\n' && e != '\r';
}
+/**
+ * The structural index past the last one of a partial batch is the end of this
+ * window, not a token of it: the byte there is the first byte of the next
+ * window. A finished value must not be rejected because that byte is ',', ':',
+ * ']' or '}'. A last document that itself ends with '{', '[', ',' or ':' is
+ * invalid JSON, yet its brackets may balance: walking it, the On-Demand API
+ * could read the next batch as a value. We leave such a document to the next
+ * batch, so that the batch ends between two documents.
+ */
+simdjson_inline bool ends_with_unfinished_document(dom_parser_implementation &parser) {
+ const uint8_t last = parser.buf[parser.structural_indexes[parser.n_structural_indexes - 1]];
+ return last == '{' || last == '[' || last == ',' || last == ':';
+}
+
simdjson_inline uint32_t find_next_document_index(dom_parser_implementation &parser, bool defer_last = false) {
// Variant: do not count separately, just figure out depth
if(parser.n_structural_indexes == 0) { return 0; }
@@ -58790,7 +57853,8 @@ error_code json_structural_indexer::index(const uint8_t *buf, size_t len, dom_pa
// Take care of the last block (will always be there unless file is empty which is
// not supposed to happen.)
uint8_t block[STEP_SIZE];
- if (simdjson_unlikely(reader.get_remainder(block) == 0)) { return UNEXPECTED_ERROR; }
+ const size_t remaining = parser._unpadded ? reader.get_remainder(block) : reader.get_remainder_padded(block);
+ if (simdjson_unlikely(remaining == 0)) { return UNEXPECTED_ERROR; }
indexer.step<STEP_SIZE>(block, reader);
return indexer.finish(parser, reader.block_index(), len, partial);
}
@@ -58824,6 +57888,20 @@ simdjson_inline void json_structural_indexer::next(const simd::simd8x64<uint8_t>
unescaped_chars_error |= block.non_quote_inside_string(unescaped);
}
+// Drops the unclosed string that ends a json_sequence batch and leaves its
+// start in structural_indexes[n_structural_indexes]. The last index is its
+// opening quote, unless the quote directly follows an RS: the scanner reads
+// RS as a scalar byte, so it indexes the RS and not the quote. Keep the RS.
+simdjson_inline void drop_json_sequence_unclosed_string(dom_parser_implementation &parser, size_t len) noexcept {
+ uint32_t quote = parser.structural_indexes[parser.n_structural_indexes - 1];
+ if (parser.buf[quote] == '"') {
+ parser.n_structural_indexes--;
+ return;
+ }
+ while (quote < len && parser.buf[quote] != '"') { quote++; }
+ parser.structural_indexes[parser.n_structural_indexes] = quote;
+}
+
simdjson_inline error_code json_structural_indexer::finish(dom_parser_implementation &parser, size_t idx, size_t len, stage1_mode partial) {
// Write out the final iteration's structurals
indexer.write(uint32_t(idx-64), prev_structurals);
@@ -58880,8 +57958,13 @@ simdjson_inline error_code json_structural_indexer::finish(dom_parser_implementa
if (simdjson_unlikely(parser.n_structural_indexes == 0u)) { return CAPACITY; }
}
// We truncate the input to the end of the last complete document (or zero).
- auto new_structural_indexes = find_next_document_index(parser, !have_unclosed_string && ends_with_partial_scalar(parser, len));
+ const bool partial_scalar = !have_unclosed_string && ends_with_partial_scalar(parser, len);
+ const bool unfinished = ends_with_unfinished_document(parser);
+ auto new_structural_indexes = find_next_document_index(parser, partial_scalar || unfinished);
if (new_structural_indexes == 0 && parser.n_structural_indexes > 0) {
+ // The batch holds a single document, whose brackets balance but which
+ // cannot be valid JSON: it is not too big, it is invalid.
+ if (unfinished && find_next_document_index(parser, partial_scalar) != 0) { return TAPE_ERROR; }
if(parser.structural_indexes[0] == 0) {
// If the buffer is partial and we started at index 0 but the document is
// incomplete, it's too big to parse.
@@ -58925,7 +58008,7 @@ simdjson_inline error_code json_structural_indexer::finish(dom_parser_implementa
// A discarded unclosed string also caps how far the filter may scan.
size_t scan_len = len;
if(have_unclosed_string) {
- parser.n_structural_indexes--;
+ drop_json_sequence_unclosed_string(parser, len);
if (simdjson_unlikely(parser.n_structural_indexes == 0u)) { return CAPACITY; }
scan_len = parser.structural_indexes[parser.n_structural_indexes];
}
@@ -58944,12 +58027,17 @@ simdjson_inline error_code json_structural_indexer::finish(dom_parser_implementa
}
parser.n_structural_indexes = new_structural_indexes;
parser.structural_indexes[parser.n_structural_indexes] = next_batch_start;
+ // The next batch starts at an RS, which the On-Demand API never reads as a
+ // string, a container or a separator. But an invalid last document may make
+ // it skip that RS as a value: repeat it, so that the index past it is not
+ // the next record, which can be a string that does not close in this batch.
+ parser.structural_indexes[parser.n_structural_indexes + 1] = next_batch_start;
} else if (partial == stage1_mode::json_sequence_final) {
// RFC 7464: final batch, last document extends to EOF
// As above: a discarded unclosed string caps how far the filter may scan.
size_t scan_len = len;
if(have_unclosed_string) {
- parser.n_structural_indexes--;
+ drop_json_sequence_unclosed_string(parser, len);
scan_len = parser.structural_indexes[parser.n_structural_indexes];
}
uint32_t next_batch_start = uint32_t(len);
@@ -59367,7 +58455,7 @@ public:
*
* - increment_count(iter) - each time a value is found in an array or object.
*/
- template<bool STREAMING, bool UNPADDED, typename V>
+ template<bool STREAMING, bool UNPADDED, bool BOUNDED = false, typename V>
simdjson_warn_unused simdjson_inline error_code walk_document(V &visitor) noexcept;
/**
@@ -59384,8 +58472,8 @@ public:
*
* They may include invalid JSON as well (such as `1.2.3` or `ture`).
*/
- template<bool UNPADDED>
- simdjson_inline const uint8_t *peek() const noexcept;
+ template<bool UNPADDED, bool BOUNDED = false>
+ simdjson_inline const uint8_t *peek(const uint32_t *batch_end = nullptr) const noexcept;
/**
* Advance to the next token.
*
@@ -59393,8 +58481,8 @@ public:
*
* They may include invalid JSON as well (such as `1.2.3` or `ture`).
*/
- template<bool UNPADDED>
- simdjson_inline const uint8_t *advance() noexcept;
+ template<bool UNPADDED, bool BOUNDED = false>
+ simdjson_inline const uint8_t *advance(const uint32_t *batch_end = nullptr) noexcept;
/**
* Get the remaining length of the document, from the start of the current token.
*/
@@ -59442,7 +58530,7 @@ public:
simdjson_warn_unused simdjson_inline error_code visit_primitive(V &visitor, const uint8_t *value) noexcept;
};
-template<bool STREAMING, bool UNPADDED, typename V>
+template<bool STREAMING, bool UNPADDED, bool BOUNDED, typename V>
simdjson_warn_unused simdjson_inline error_code json_iterator::walk_document(V &visitor) noexcept {
logger::log_start();
@@ -59450,6 +58538,9 @@ simdjson_warn_unused simdjson_inline error_code json_iterator::walk_document(V &
// Start the document
//
if (at_eof()) { return EMPTY; }
+ // Only used when BOUNDED (see advance()); a local, so that it can stay in a
+ // register, and vanishes from the other walks.
+ const uint32_t *const batch_end = &dom_parser.structural_indexes[dom_parser.n_structural_indexes];
log_start_value("document");
SIMDJSON_TRY( visitor.visit_document_start(*this) );
@@ -59457,7 +58548,7 @@ simdjson_warn_unused simdjson_inline error_code json_iterator::walk_document(V &
// Read first value
//
{
- auto value = advance<UNPADDED>();
+ auto value = advance<UNPADDED, BOUNDED>(batch_end);
// Make sure the outer object or array is closed before continuing; otherwise, there are ways we
// could get into memory corruption. See https://github.com/simdjson/simdjson/issues/906
@@ -59469,8 +58560,8 @@ simdjson_warn_unused simdjson_inline error_code json_iterator::walk_document(V &
}
switch (*value) {
- case '{': if (*peek<UNPADDED>() == '}') { advance<UNPADDED>(); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin;
- case '[': if (*peek<UNPADDED>() == ']') { advance<UNPADDED>(); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin;
+ case '{': if (*peek<UNPADDED, BOUNDED>(batch_end) == '}') { advance<UNPADDED, BOUNDED>(batch_end); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin;
+ case '[': if (*peek<UNPADDED, BOUNDED>(batch_end) == ']') { advance<UNPADDED, BOUNDED>(batch_end); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin;
default: SIMDJSON_TRY( visitor.visit_root_primitive(*this, value) ); break;
}
}
@@ -59487,29 +58578,29 @@ object_begin:
SIMDJSON_TRY( visitor.visit_object_start(*this) );
{
- auto key = advance<UNPADDED>();
+ auto key = advance<UNPADDED, BOUNDED>(batch_end);
if (*key != '"') { log_error("Object does not start with a key"); return TAPE_ERROR; }
SIMDJSON_TRY( visitor.increment_count(*this) );
SIMDJSON_TRY( visitor.visit_key(*this, key) );
}
object_field:
- if (simdjson_unlikely( *advance<UNPADDED>() != ':' )) { log_error("Missing colon after key in object"); return TAPE_ERROR; }
+ if (simdjson_unlikely( (*advance<UNPADDED, BOUNDED>(batch_end) != ':') )) { log_error("Missing colon after key in object"); return TAPE_ERROR; }
{
- auto value = advance<UNPADDED>();
+ auto value = advance<UNPADDED, BOUNDED>(batch_end);
switch (*value) {
- case '{': if (*peek<UNPADDED>() == '}') { advance<UNPADDED>(); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin;
- case '[': if (*peek<UNPADDED>() == ']') { advance<UNPADDED>(); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin;
+ case '{': if (*peek<UNPADDED, BOUNDED>(batch_end) == '}') { advance<UNPADDED, BOUNDED>(batch_end); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin;
+ case '[': if (*peek<UNPADDED, BOUNDED>(batch_end) == ']') { advance<UNPADDED, BOUNDED>(batch_end); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin;
default: SIMDJSON_TRY( visitor.visit_primitive(*this, value) ); break;
}
}
object_continue:
- switch (*advance<UNPADDED>()) {
+ switch (*advance<UNPADDED, BOUNDED>(batch_end)) {
case ',':
SIMDJSON_TRY( visitor.increment_count(*this) );
{
- auto key = advance<UNPADDED>();
+ auto key = advance<UNPADDED, BOUNDED>(batch_end);
if (simdjson_unlikely( *key != '"' )) { log_error("Key string missing at beginning of field in object"); return TAPE_ERROR; }
SIMDJSON_TRY( visitor.visit_key(*this, key) );
}
@@ -59537,16 +58628,16 @@ array_begin:
array_value:
{
- auto value = advance<UNPADDED>();
+ auto value = advance<UNPADDED, BOUNDED>(batch_end);
switch (*value) {
- case '{': if (*peek<UNPADDED>() == '}') { advance<UNPADDED>(); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin;
- case '[': if (*peek<UNPADDED>() == ']') { advance<UNPADDED>(); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin;
+ case '{': if (*peek<UNPADDED, BOUNDED>(batch_end) == '}') { advance<UNPADDED, BOUNDED>(batch_end); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin;
+ case '[': if (*peek<UNPADDED, BOUNDED>(batch_end) == ']') { advance<UNPADDED, BOUNDED>(batch_end); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin;
default: SIMDJSON_TRY( visitor.visit_primitive(*this, value) ); break;
}
}
array_continue:
- switch (*advance<UNPADDED>()) {
+ switch (*advance<UNPADDED, BOUNDED>(batch_end)) {
case ',': SIMDJSON_TRY( visitor.increment_count(*this) ); goto array_value;
case ']': log_end_value("array"); SIMDJSON_TRY( visitor.visit_array_end(*this) ); goto scope_end;
default: log_error("Missing comma between array values"); return TAPE_ERROR;
@@ -59576,8 +58667,18 @@ simdjson_inline json_iterator::json_iterator(dom_parser_implementation &_dom_par
// Stage 1 leaves a sentinel index of value `len`; reading buf[len] requires
// padding. For UNPADDED, return a dummy so walk_document can fail cleanly (#2815).
-template<bool UNPADDED>
-simdjson_inline const uint8_t *json_iterator::peek() const noexcept {
+//
+// When BOUNDED (see dom_parser_implementation::_bounded_batch), a document may
+// not run past batch_end, one past the last structural index of the batch:
+// past it, we return the same dummy, and walk_document fails cleanly.
+template<bool UNPADDED, bool BOUNDED>
+simdjson_inline const uint8_t *json_iterator::peek(const uint32_t *batch_end) const noexcept {
+ SIMDJSON_IF_CONSTEXPR (BOUNDED) {
+ if (simdjson_unlikely(next_structural >= batch_end)) {
+ static constexpr uint8_t streaming_eof_sentinel = 0;
+ return &streaming_eof_sentinel;
+ }
+ }
const uint32_t idx = *(next_structural);
SIMDJSON_IF_CONSTEXPR (UNPADDED) {
if (simdjson_unlikely(idx >= dom_parser.len)) {
@@ -59587,8 +58688,14 @@ simdjson_inline const uint8_t *json_iterator::peek() const noexcept {
}
return &buf[idx];
}
-template<bool UNPADDED>
-simdjson_inline const uint8_t *json_iterator::advance() noexcept {
+template<bool UNPADDED, bool BOUNDED>
+simdjson_inline const uint8_t *json_iterator::advance(const uint32_t *batch_end) noexcept {
+ SIMDJSON_IF_CONSTEXPR (BOUNDED) {
+ if (simdjson_unlikely(next_structural >= batch_end)) {
+ static constexpr uint8_t streaming_eof_sentinel = 0;
+ return &streaming_eof_sentinel;
+ }
+ }
const uint32_t idx = *(next_structural++);
SIMDJSON_IF_CONSTEXPR (UNPADDED) {
if (simdjson_unlikely(idx >= dom_parser.len)) {
@@ -60147,6 +59254,30 @@ struct tape_builder {
return iter.walk_document<STREAMING, false>(builder);
}
}
+
+ // The stage 2 entry points of dom::document_stream, which picks one per batch
+ // (see internal::dom_parser_implementation::stage2_next_function). Out of
+ // line, so that stage2_next() can share the code.
+ simdjson_warn_unused static simdjson_never_inline error_code parse_next_document(
+ internal::dom_parser_implementation &dom_parser, dom::document &doc) noexcept {
+ return parse_document<true>(static_cast<dom_parser_implementation &>(dom_parser), doc);
+ }
+
+ // A document of a stream batch that stage 2 must bound to its structural
+ // indexes (see internal::dom_parser_implementation::_bounded_batch).
+ simdjson_warn_unused static simdjson_never_inline error_code parse_next_bounded_document(
+ internal::dom_parser_implementation &_dom_parser, dom::document &doc) noexcept {
+ dom_parser_implementation &dom_parser = static_cast<dom_parser_implementation &>(_dom_parser);
+ dom_parser.doc = &doc;
+ json_iterator iter(dom_parser, dom_parser.next_structural_index);
+ if (dom_parser._unpadded) {
+ tape_builder_impl<true> builder(doc);
+ return iter.walk_document<true, true, true>(builder);
+ } else {
+ tape_builder_impl<false> builder(doc);
+ return iter.walk_document<true, false, true>(builder);
+ }
+ }
};
template <bool UNPADDED>
@@ -60567,7 +59698,11 @@ simdjson_warn_unused error_code dom_parser_implementation::stage2(dom::document
}
simdjson_warn_unused error_code dom_parser_implementation::stage2_next(dom::document &_doc) noexcept {
- return stage2::tape_builder::parse_document<true>(*this, _doc);
+ return stage2::tape_builder::parse_next_document(*this, _doc);
+}
+
+internal::dom_parser_implementation::stage2_next_function_t dom_parser_implementation::stage2_next_function(bool bounded) const noexcept {
+ return bounded ? stage2::tape_builder::parse_next_bounded_document : stage2::tape_builder::parse_next_document;
}
SIMDJSON_NO_SANITIZE_MEMORY
@@ -61214,30 +60349,6 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin
}
-struct escaping {
- static constexpr uint32_t BYTES_PROCESSED = 16;
- simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
-
- simdjson_inline bool has_escape() { return escape_bits != 0; }
- simdjson_inline int escape_index() { return trailing_zeroes(escape_bits); }
-
- uint64_t escape_bits;
-}; // struct escaping
-
-
-
-simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
- static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes");
- simd8<uint8_t> v(src);
- v.store(dst);
- simd8<bool> is_quote = (v == '"');
- simd8<bool> is_backslash = (v == '\\');
- simd8<bool> is_control = (v < 32);
- return {
- static_cast<uint64_t>((is_backslash | is_quote | is_control).to_bitmask())
- };
-}
-
} // unnamed namespace
} // namespace lsx
} // namespace simdjson
@@ -61630,6 +60741,7 @@ public:
simdjson_warn_unused error_code stage1(const uint8_t *buf, size_t len, stage1_mode partial) noexcept final;
simdjson_warn_unused error_code stage2(dom::document &doc) noexcept final;
simdjson_warn_unused error_code stage2_next(dom::document &doc) noexcept final;
+ stage2_next_function_t stage2_next_function(bool bounded) const noexcept final;
simdjson_warn_unused uint8_t *parse_string(const uint8_t *src, uint8_t *dst, bool allow_replacement) const noexcept final;
simdjson_warn_unused uint8_t *parse_wobbly_string(const uint8_t *src, uint8_t *dst) const noexcept final;
inline simdjson_warn_unused error_code set_capacity(size_t capacity) noexcept final;
@@ -62391,21 +61503,6 @@ static bool parse_float_fallback(const uint8_t *ptr, double *outDouble) {
return !(*outDouble > (std::numeric_limits<double>::max)() || *outDouble < std::numeric_limits<double>::lowest());
}
-static bool parse_float_fallback(const uint8_t *ptr, const uint8_t *end_ptr, double *outDouble) {
- *outDouble = simdjson::internal::from_chars(reinterpret_cast<const char *>(ptr), reinterpret_cast<const char *>(end_ptr));
- // We do not accept infinite values.
-
- // Detecting finite values in a portable manner is ridiculously hard, ideally
- // we would want to do:
- // return !std::isfinite(*outDouble);
- // but that mysteriously fails under legacy/old libc++ libraries, see
- // https://github.com/simdjson/simdjson/issues/1286
- //
- // Therefore, fall back to this solution (the extra parens are there
- // to handle that max may be a macro on windows).
- return !(*outDouble > (std::numeric_limits<double>::max)() || *outDouble < std::numeric_limits<double>::lowest());
-}
-
// Same as parse_float_fallback, but for binary32 (float) values. Going through
// the binary64 fallback and then rounding to binary32 would be subject to
// double rounding, so we run the fallback algorithm directly on binary32.
@@ -62951,57 +62048,6 @@ simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned(const u
return i;
}
-
-
-// Parse any number from 0 to 18,446,744,073,709,551,615
-// Never read at src_end or beyond
-simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned(const uint8_t * const src, const uint8_t * const src_end) noexcept {
- const uint8_t *p = src;
- //
- // Parse the integer part.
- //
- // PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
- const uint8_t *const start_digits = p;
- uint64_t i = 0;
- while ((p != src_end) && parse_digit(*p, i)) { p++; }
-
- // If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
- // Optimization note: size_t is expected to be unsigned.
- size_t digit_count = size_t(p - start_digits);
- // The longest positive 64-bit number is 20 digits.
- // We do it this way so we don't trigger this branch unless we must.
- // Optimization note: the compiler can probably merge
- // ((digit_count == 0) || (digit_count > 20))
- // into a single branch since digit_count is unsigned.
- if ((digit_count == 0) || (digit_count > 20)) { return INCORRECT_TYPE; }
- // Here digit_count > 0.
- if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
- // We can do the following...
- // if (!jsoncharutils::is_structural_or_whitespace(*p)) {
- // return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
- // }
- // as a single table lookup:
- if ((p != src_end) && integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); }
-
- if (digit_count == 20) {
- // Positive overflow check:
- // - A 20 digit number starting with 2-9 is overflow, because 18,446,744,073,709,551,615 is the
- // biggest uint64_t.
- // - A 20 digit number starting with 1 is overflow if it is less than INT64_MAX.
- // If we got here, it's a 20 digit number starting with the digit "1".
- // - If a 20 digit number starting with 1 overflowed (i*10+digit), the result will be smaller
- // than 1,553,255,926,290,448,384.
- // - That is smaller than the smallest possible 20-digit number the user could write:
- // 10,000,000,000,000,000,000.
- // - Therefore, if the number is positive and lower than that, it's overflow.
- // - The value we are looking at is less than or equal to INT64_MAX.
- //
- if (src[0] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INCORRECT_TYPE; }
- }
-
- return i;
-}
-
// Parse any number from 0 to 18,446,744,073,709,551,615
simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned_in_string(const uint8_t * const src) noexcept {
const uint8_t *p = src + 1;
@@ -63094,50 +62140,6 @@ simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer(const uin
return negative ? (~i+1) : i;
}
-// Parse any number from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
-// Never read at src_end or beyond
-simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer(const uint8_t * const src, const uint8_t * const src_end) noexcept {
- //
- // Check for minus sign
- //
- if(src == src_end) { return NUMBER_ERROR; }
- bool negative = (*src == '-');
- const uint8_t *p = src + uint8_t(negative);
-
- //
- // Parse the integer part.
- //
- // PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
- const uint8_t *const start_digits = p;
- uint64_t i = 0;
- while ((p != src_end) && parse_digit(*p, i)) { p++; }
-
- // If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
- // Optimization note: size_t is expected to be unsigned.
- size_t digit_count = size_t(p - start_digits);
- // We go from
- // -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
- // so we can never represent numbers that have more than 19 digits.
- size_t longest_digit_count = 19;
- // Optimization note: the compiler can probably merge
- // ((digit_count == 0) || (digit_count > longest_digit_count))
- // into a single branch since digit_count is unsigned.
- if ((digit_count == 0) || (digit_count > longest_digit_count)) { return INCORRECT_TYPE; }
- // Here digit_count > 0.
- if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
- // We can do the following...
- // if (!jsoncharutils::is_structural_or_whitespace(*p)) {
- // return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
- // }
- // as a single table lookup:
- if((p != src_end) && integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); }
- // Negative numbers have can go down to - INT64_MAX - 1 whereas positive numbers are limited to INT64_MAX.
- // Performance note: This check is only needed when digit_count == longest_digit_count but it is
- // so cheap that we might as well always make it.
- if(i > uint64_t(INT64_MAX) + uint64_t(negative)) { return INCORRECT_TYPE; }
- return negative ? (~i+1) : i;
-}
-
// Parse any number from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer_in_string(const uint8_t *src) noexcept {
//
@@ -63243,8 +62245,11 @@ simdjson_unused simdjson_inline simdjson_result<double> parse_double(const uint8
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
+ // no exp digits
+ if (p-start_exp_digits == 0) { return NUMBER_ERROR; }
+ // 20+ exp digits (e.g., 0e9999999999999999999999) are valid, but exp may
+ // have wrapped around: leave such numbers to the fallback parser.
+ overflow = overflow || p-start_exp_digits > 19;
exponent += exp_neg ? 0-exp : exp;
}
@@ -63334,8 +62339,11 @@ simdjson_unused simdjson_inline simdjson_result<float> parse_float(const uint8_t
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
+ // no exp digits
+ if (p-start_exp_digits == 0) { return NUMBER_ERROR; }
+ // 20+ exp digits (e.g., 0e9999999999999999999999) are valid, but exp may
+ // have wrapped around: leave such numbers to the fallback parser.
+ overflow = overflow || p-start_exp_digits > 19;
exponent += exp_neg ? 0-exp : exp;
}
@@ -63415,88 +62423,6 @@ simdjson_unused simdjson_inline simdjson_result<number_type> get_number_type(con
return number_type::floating_point_number;
}
-// Never read at src_end or beyond
-simdjson_unused simdjson_inline simdjson_result<double> parse_double(const uint8_t * src, const uint8_t * const src_end) noexcept {
- if(src == src_end) { return NUMBER_ERROR; }
- //
- // Check for minus sign
- //
- bool negative = (*src == '-');
- src += uint8_t(negative);
-
- //
- // Parse the integer part.
- //
- uint64_t i = 0;
- const uint8_t *p = src;
- if(p == src_end) { return NUMBER_ERROR; }
- p += parse_digit(*p, i);
- bool leading_zero = (i == 0);
- while ((p != src_end) && parse_digit(*p, i)) { p++; }
- // no integer digits, or 0123 (zero must be solo)
- if ( p == src ) { return INCORRECT_TYPE; }
- if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; }
-
- //
- // Parse the decimal part.
- //
- int64_t exponent = 0;
- bool overflow;
- if (simdjson_likely((p != src_end) && (*p == '.'))) {
- p++;
- const uint8_t *start_decimal_digits = p;
- if ((p == src_end) || !parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits
- p++;
- while ((p != src_end) && parse_digit(*p, i)) { p++; }
- exponent = -(p - start_decimal_digits);
-
- // Overflow check. More than 19 digits (minus the decimal) may be overflow.
- overflow = p-src-1 > 19;
- if (simdjson_unlikely(overflow && leading_zero)) {
- // Skip leading 0.00000 and see if it still overflows
- const uint8_t *start_digits = src + 2;
- while (*start_digits == '0') { start_digits++; }
- overflow = start_digits-src > 19;
- }
- } else {
- overflow = p-src > 19;
- }
-
- //
- // Parse the exponent
- //
- if ((p != src_end) && (*p == 'e' || *p == 'E')) {
- p++;
- if(p == src_end) { return NUMBER_ERROR; }
- bool exp_neg = *p == '-';
- p += exp_neg || *p == '+';
-
- uint64_t exp = 0;
- const uint8_t *start_exp_digits = p;
- while ((p != src_end) && parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
-
- exponent += exp_neg ? 0-exp : exp;
- }
-
- if ((p != src_end) && jsoncharutils::is_not_structural_or_whitespace(*p)) { return NUMBER_ERROR; }
-
- overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power;
-
- //
- // Assemble (or slow-parse) the float
- //
- double d;
- if (simdjson_likely(!overflow)) {
- if (compute_float_64(exponent, i, negative, d)) { return d; }
- }
- if (!parse_float_fallback(src - uint8_t(negative), src_end, &d)) {
- return NUMBER_ERROR;
- }
- return d;
-}
-
simdjson_unused simdjson_inline simdjson_result<double> parse_double_in_string(const uint8_t * src) noexcept {
//
// Check for minus sign
@@ -63565,8 +62491,11 @@ simdjson_unused simdjson_inline simdjson_result<double> parse_double_in_string(c
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
+ // no exp digits
+ if (p-start_exp_digits == 0) { return NUMBER_ERROR; }
+ // 20+ exp digits (e.g., 0e9999999999999999999999) are valid, but exp may
+ // have wrapped around: leave such numbers to the fallback parser.
+ overflow = overflow || p-start_exp_digits > 19;
exponent += exp_neg ? 0-exp : exp;
}
@@ -63658,8 +62587,11 @@ simdjson_unused simdjson_inline simdjson_result<float> parse_float_in_string(con
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
+ // no exp digits
+ if (p-start_exp_digits == 0) { return NUMBER_ERROR; }
+ // 20+ exp digits (e.g., 0e9999999999999999999999) are valid, but exp may
+ // have wrapped around: leave such numbers to the fallback parser.
+ overflow = overflow || p-start_exp_digits > 19;
exponent += exp_neg ? 0-exp : exp;
}
@@ -64464,30 +63396,6 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin
}
-struct escaping {
- static constexpr uint32_t BYTES_PROCESSED = 16;
- simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
-
- simdjson_inline bool has_escape() { return escape_bits != 0; }
- simdjson_inline int escape_index() { return trailing_zeroes(escape_bits); }
-
- uint64_t escape_bits;
-}; // struct escaping
-
-
-
-simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
- static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes");
- simd8<uint8_t> v(src);
- v.store(dst);
- simd8<bool> is_quote = (v == '"');
- simd8<bool> is_backslash = (v == '\\');
- simd8<bool> is_control = (v < 32);
- return {
- static_cast<uint64_t>((is_backslash | is_quote | is_control).to_bitmask())
- };
-}
-
} // unnamed namespace
} // namespace lsx
} // namespace simdjson
@@ -64655,6 +63563,17 @@ public:
* @return the number of effective characters in the last block.
*/
simdjson_inline size_t get_remainder(uint8_t *dst) const;
+ /**
+ * Like get_remainder, for input followed by SIMDJSON_PADDING readable bytes.
+ *
+ * get_remainder writes the block with a memset and a memcpy of the remaining
+ * length, and the vector loads that read it back cannot be forwarded from
+ * those narrower stores: they wait until the stores reach the cache, which
+ * costs tens of cycles on every document. Here the block is put together in
+ * vector registers, from loads that may read into the padding, and written
+ * with full-width stores that the loads can forward from.
+ */
+ simdjson_inline size_t get_remainder_padded(uint8_t *dst) const;
simdjson_inline void advance();
private:
const uint8_t *buf;
@@ -64663,47 +63582,6 @@ private:
size_t idx;
};
-// Routines to print masks and text for debugging bitmask operations
-simdjson_unused static char * format_input_text_64(const uint8_t *text) {
- static char buf[sizeof(simd8x64<uint8_t>) + 1];
- for (size_t i=0; i<sizeof(simd8x64<uint8_t>); i++) {
- buf[i] = int8_t(text[i]) < ' ' ? '_' : int8_t(text[i]);
- }
- buf[sizeof(simd8x64<uint8_t>)] = '\0';
- return buf;
-}
-
-// Routines to print masks and text for debugging bitmask operations
-simdjson_unused static char * format_input_text(const simd8x64<uint8_t>& in) {
- static char buf[sizeof(simd8x64<uint8_t>) + 1];
- in.store(reinterpret_cast<uint8_t*>(buf));
- for (size_t i=0; i<sizeof(simd8x64<uint8_t>); i++) {
- if (buf[i] < ' ') { buf[i] = '_'; }
- }
- buf[sizeof(simd8x64<uint8_t>)] = '\0';
- return buf;
-}
-
-simdjson_unused static char * format_input_text(const simd8x64<uint8_t>& in, uint64_t mask) {
- static char buf[sizeof(simd8x64<uint8_t>) + 1];
- in.store(reinterpret_cast<uint8_t*>(buf));
- for (size_t i=0; i<sizeof(simd8x64<uint8_t>); i++) {
- if (buf[i] <= ' ') { buf[i] = '_'; }
- if (!(mask & (size_t(1) << i))) { buf[i] = ' '; }
- }
- buf[sizeof(simd8x64<uint8_t>)] = '\0';
- return buf;
-}
-
-simdjson_unused static char * format_mask(uint64_t mask) {
- static char buf[sizeof(simd8x64<uint8_t>) + 1];
- for (size_t i=0; i<64; i++) {
- buf[i] = (mask & (size_t(1) << i)) ? 'X' : ' ';
- }
- buf[64] = '\0';
- return buf;
-}
-
template<size_t STEP_SIZE>
simdjson_inline buf_block_reader<STEP_SIZE>::buf_block_reader(const uint8_t *_buf, size_t _len) : buf{_buf}, len{_len}, lenminusstep{len < STEP_SIZE ? 0 : len - STEP_SIZE}, idx{0} {}
@@ -64728,6 +63606,38 @@ simdjson_inline size_t buf_block_reader<STEP_SIZE>::get_remainder(uint8_t *dst)
return len - idx;
}
+// keep_mask[128 - n + i] is 0xFF for i < n and 0 otherwise.
+alignas(64) static const uint8_t keep_mask[256] = {
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+template<size_t STEP_SIZE>
+simdjson_inline size_t buf_block_reader<STEP_SIZE>::get_remainder_padded(uint8_t *dst) const {
+ static_assert(STEP_SIZE <= 128, "keep_mask covers blocks of up to 128 bytes");
+ static_assert(sizeof(simd::simd8<uint8_t>) <= SIMDJSON_PADDING, "a vector load at the last byte must stay in the padding");
+ const size_t remaining = len - idx;
+ if (remaining == 0) { return 0; }
+ const simd::simd8<uint8_t> spaces = simd::simd8<uint8_t>::splat(0x20);
+ for (size_t off = 0; off < STEP_SIZE; off += sizeof(simd::simd8<uint8_t>)) {
+ simd::simd8<uint8_t> v = spaces;
+ if (off < remaining) {
+ // Starts before len, so it ends within the padding.
+ const simd::simd8<uint8_t> in = simd::simd8<uint8_t>::load(buf + idx + off);
+ const simd::simd8<uint8_t> keep = simd::simd8<uint8_t>::load(keep_mask + 128 - remaining + off);
+ v = spaces ^ ((in ^ spaces) & keep);
+ }
+ v.store(dst + off);
+ }
+ return remaining;
+}
+
template<size_t STEP_SIZE>
simdjson_inline void buf_block_reader<STEP_SIZE>::advance() {
idx += STEP_SIZE;
@@ -65428,6 +64338,20 @@ simdjson_inline bool ends_with_partial_scalar(dom_parser_implementation &parser,
e != ' ' && e != '\t' && e != '\n' && e != '\r';
}
+/**
+ * The structural index past the last one of a partial batch is the end of this
+ * window, not a token of it: the byte there is the first byte of the next
+ * window. A finished value must not be rejected because that byte is ',', ':',
+ * ']' or '}'. A last document that itself ends with '{', '[', ',' or ':' is
+ * invalid JSON, yet its brackets may balance: walking it, the On-Demand API
+ * could read the next batch as a value. We leave such a document to the next
+ * batch, so that the batch ends between two documents.
+ */
+simdjson_inline bool ends_with_unfinished_document(dom_parser_implementation &parser) {
+ const uint8_t last = parser.buf[parser.structural_indexes[parser.n_structural_indexes - 1]];
+ return last == '{' || last == '[' || last == ',' || last == ':';
+}
+
simdjson_inline uint32_t find_next_document_index(dom_parser_implementation &parser, bool defer_last = false) {
// Variant: do not count separately, just figure out depth
if(parser.n_structural_indexes == 0) { return 0; }
@@ -66109,7 +65033,8 @@ error_code json_structural_indexer::index(const uint8_t *buf, size_t len, dom_pa
// Take care of the last block (will always be there unless file is empty which is
// not supposed to happen.)
uint8_t block[STEP_SIZE];
- if (simdjson_unlikely(reader.get_remainder(block) == 0)) { return UNEXPECTED_ERROR; }
+ const size_t remaining = parser._unpadded ? reader.get_remainder(block) : reader.get_remainder_padded(block);
+ if (simdjson_unlikely(remaining == 0)) { return UNEXPECTED_ERROR; }
indexer.step<STEP_SIZE>(block, reader);
return indexer.finish(parser, reader.block_index(), len, partial);
}
@@ -66143,6 +65068,20 @@ simdjson_inline void json_structural_indexer::next(const simd::simd8x64<uint8_t>
unescaped_chars_error |= block.non_quote_inside_string(unescaped);
}
+// Drops the unclosed string that ends a json_sequence batch and leaves its
+// start in structural_indexes[n_structural_indexes]. The last index is its
+// opening quote, unless the quote directly follows an RS: the scanner reads
+// RS as a scalar byte, so it indexes the RS and not the quote. Keep the RS.
+simdjson_inline void drop_json_sequence_unclosed_string(dom_parser_implementation &parser, size_t len) noexcept {
+ uint32_t quote = parser.structural_indexes[parser.n_structural_indexes - 1];
+ if (parser.buf[quote] == '"') {
+ parser.n_structural_indexes--;
+ return;
+ }
+ while (quote < len && parser.buf[quote] != '"') { quote++; }
+ parser.structural_indexes[parser.n_structural_indexes] = quote;
+}
+
simdjson_inline error_code json_structural_indexer::finish(dom_parser_implementation &parser, size_t idx, size_t len, stage1_mode partial) {
// Write out the final iteration's structurals
indexer.write(uint32_t(idx-64), prev_structurals);
@@ -66199,8 +65138,13 @@ simdjson_inline error_code json_structural_indexer::finish(dom_parser_implementa
if (simdjson_unlikely(parser.n_structural_indexes == 0u)) { return CAPACITY; }
}
// We truncate the input to the end of the last complete document (or zero).
- auto new_structural_indexes = find_next_document_index(parser, !have_unclosed_string && ends_with_partial_scalar(parser, len));
+ const bool partial_scalar = !have_unclosed_string && ends_with_partial_scalar(parser, len);
+ const bool unfinished = ends_with_unfinished_document(parser);
+ auto new_structural_indexes = find_next_document_index(parser, partial_scalar || unfinished);
if (new_structural_indexes == 0 && parser.n_structural_indexes > 0) {
+ // The batch holds a single document, whose brackets balance but which
+ // cannot be valid JSON: it is not too big, it is invalid.
+ if (unfinished && find_next_document_index(parser, partial_scalar) != 0) { return TAPE_ERROR; }
if(parser.structural_indexes[0] == 0) {
// If the buffer is partial and we started at index 0 but the document is
// incomplete, it's too big to parse.
@@ -66244,7 +65188,7 @@ simdjson_inline error_code json_structural_indexer::finish(dom_parser_implementa
// A discarded unclosed string also caps how far the filter may scan.
size_t scan_len = len;
if(have_unclosed_string) {
- parser.n_structural_indexes--;
+ drop_json_sequence_unclosed_string(parser, len);
if (simdjson_unlikely(parser.n_structural_indexes == 0u)) { return CAPACITY; }
scan_len = parser.structural_indexes[parser.n_structural_indexes];
}
@@ -66263,12 +65207,17 @@ simdjson_inline error_code json_structural_indexer::finish(dom_parser_implementa
}
parser.n_structural_indexes = new_structural_indexes;
parser.structural_indexes[parser.n_structural_indexes] = next_batch_start;
+ // The next batch starts at an RS, which the On-Demand API never reads as a
+ // string, a container or a separator. But an invalid last document may make
+ // it skip that RS as a value: repeat it, so that the index past it is not
+ // the next record, which can be a string that does not close in this batch.
+ parser.structural_indexes[parser.n_structural_indexes + 1] = next_batch_start;
} else if (partial == stage1_mode::json_sequence_final) {
// RFC 7464: final batch, last document extends to EOF
// As above: a discarded unclosed string caps how far the filter may scan.
size_t scan_len = len;
if(have_unclosed_string) {
- parser.n_structural_indexes--;
+ drop_json_sequence_unclosed_string(parser, len);
scan_len = parser.structural_indexes[parser.n_structural_indexes];
}
uint32_t next_batch_start = uint32_t(len);
@@ -66686,7 +65635,7 @@ public:
*
* - increment_count(iter) - each time a value is found in an array or object.
*/
- template<bool STREAMING, bool UNPADDED, typename V>
+ template<bool STREAMING, bool UNPADDED, bool BOUNDED = false, typename V>
simdjson_warn_unused simdjson_inline error_code walk_document(V &visitor) noexcept;
/**
@@ -66703,8 +65652,8 @@ public:
*
* They may include invalid JSON as well (such as `1.2.3` or `ture`).
*/
- template<bool UNPADDED>
- simdjson_inline const uint8_t *peek() const noexcept;
+ template<bool UNPADDED, bool BOUNDED = false>
+ simdjson_inline const uint8_t *peek(const uint32_t *batch_end = nullptr) const noexcept;
/**
* Advance to the next token.
*
@@ -66712,8 +65661,8 @@ public:
*
* They may include invalid JSON as well (such as `1.2.3` or `ture`).
*/
- template<bool UNPADDED>
- simdjson_inline const uint8_t *advance() noexcept;
+ template<bool UNPADDED, bool BOUNDED = false>
+ simdjson_inline const uint8_t *advance(const uint32_t *batch_end = nullptr) noexcept;
/**
* Get the remaining length of the document, from the start of the current token.
*/
@@ -66761,7 +65710,7 @@ public:
simdjson_warn_unused simdjson_inline error_code visit_primitive(V &visitor, const uint8_t *value) noexcept;
};
-template<bool STREAMING, bool UNPADDED, typename V>
+template<bool STREAMING, bool UNPADDED, bool BOUNDED, typename V>
simdjson_warn_unused simdjson_inline error_code json_iterator::walk_document(V &visitor) noexcept {
logger::log_start();
@@ -66769,6 +65718,9 @@ simdjson_warn_unused simdjson_inline error_code json_iterator::walk_document(V &
// Start the document
//
if (at_eof()) { return EMPTY; }
+ // Only used when BOUNDED (see advance()); a local, so that it can stay in a
+ // register, and vanishes from the other walks.
+ const uint32_t *const batch_end = &dom_parser.structural_indexes[dom_parser.n_structural_indexes];
log_start_value("document");
SIMDJSON_TRY( visitor.visit_document_start(*this) );
@@ -66776,7 +65728,7 @@ simdjson_warn_unused simdjson_inline error_code json_iterator::walk_document(V &
// Read first value
//
{
- auto value = advance<UNPADDED>();
+ auto value = advance<UNPADDED, BOUNDED>(batch_end);
// Make sure the outer object or array is closed before continuing; otherwise, there are ways we
// could get into memory corruption. See https://github.com/simdjson/simdjson/issues/906
@@ -66788,8 +65740,8 @@ simdjson_warn_unused simdjson_inline error_code json_iterator::walk_document(V &
}
switch (*value) {
- case '{': if (*peek<UNPADDED>() == '}') { advance<UNPADDED>(); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin;
- case '[': if (*peek<UNPADDED>() == ']') { advance<UNPADDED>(); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin;
+ case '{': if (*peek<UNPADDED, BOUNDED>(batch_end) == '}') { advance<UNPADDED, BOUNDED>(batch_end); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin;
+ case '[': if (*peek<UNPADDED, BOUNDED>(batch_end) == ']') { advance<UNPADDED, BOUNDED>(batch_end); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin;
default: SIMDJSON_TRY( visitor.visit_root_primitive(*this, value) ); break;
}
}
@@ -66806,29 +65758,29 @@ object_begin:
SIMDJSON_TRY( visitor.visit_object_start(*this) );
{
- auto key = advance<UNPADDED>();
+ auto key = advance<UNPADDED, BOUNDED>(batch_end);
if (*key != '"') { log_error("Object does not start with a key"); return TAPE_ERROR; }
SIMDJSON_TRY( visitor.increment_count(*this) );
SIMDJSON_TRY( visitor.visit_key(*this, key) );
}
object_field:
- if (simdjson_unlikely( *advance<UNPADDED>() != ':' )) { log_error("Missing colon after key in object"); return TAPE_ERROR; }
+ if (simdjson_unlikely( (*advance<UNPADDED, BOUNDED>(batch_end) != ':') )) { log_error("Missing colon after key in object"); return TAPE_ERROR; }
{
- auto value = advance<UNPADDED>();
+ auto value = advance<UNPADDED, BOUNDED>(batch_end);
switch (*value) {
- case '{': if (*peek<UNPADDED>() == '}') { advance<UNPADDED>(); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin;
- case '[': if (*peek<UNPADDED>() == ']') { advance<UNPADDED>(); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin;
+ case '{': if (*peek<UNPADDED, BOUNDED>(batch_end) == '}') { advance<UNPADDED, BOUNDED>(batch_end); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin;
+ case '[': if (*peek<UNPADDED, BOUNDED>(batch_end) == ']') { advance<UNPADDED, BOUNDED>(batch_end); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin;
default: SIMDJSON_TRY( visitor.visit_primitive(*this, value) ); break;
}
}
object_continue:
- switch (*advance<UNPADDED>()) {
+ switch (*advance<UNPADDED, BOUNDED>(batch_end)) {
case ',':
SIMDJSON_TRY( visitor.increment_count(*this) );
{
- auto key = advance<UNPADDED>();
+ auto key = advance<UNPADDED, BOUNDED>(batch_end);
if (simdjson_unlikely( *key != '"' )) { log_error("Key string missing at beginning of field in object"); return TAPE_ERROR; }
SIMDJSON_TRY( visitor.visit_key(*this, key) );
}
@@ -66856,16 +65808,16 @@ array_begin:
array_value:
{
- auto value = advance<UNPADDED>();
+ auto value = advance<UNPADDED, BOUNDED>(batch_end);
switch (*value) {
- case '{': if (*peek<UNPADDED>() == '}') { advance<UNPADDED>(); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin;
- case '[': if (*peek<UNPADDED>() == ']') { advance<UNPADDED>(); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin;
+ case '{': if (*peek<UNPADDED, BOUNDED>(batch_end) == '}') { advance<UNPADDED, BOUNDED>(batch_end); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin;
+ case '[': if (*peek<UNPADDED, BOUNDED>(batch_end) == ']') { advance<UNPADDED, BOUNDED>(batch_end); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin;
default: SIMDJSON_TRY( visitor.visit_primitive(*this, value) ); break;
}
}
array_continue:
- switch (*advance<UNPADDED>()) {
+ switch (*advance<UNPADDED, BOUNDED>(batch_end)) {
case ',': SIMDJSON_TRY( visitor.increment_count(*this) ); goto array_value;
case ']': log_end_value("array"); SIMDJSON_TRY( visitor.visit_array_end(*this) ); goto scope_end;
default: log_error("Missing comma between array values"); return TAPE_ERROR;
@@ -66895,8 +65847,18 @@ simdjson_inline json_iterator::json_iterator(dom_parser_implementation &_dom_par
// Stage 1 leaves a sentinel index of value `len`; reading buf[len] requires
// padding. For UNPADDED, return a dummy so walk_document can fail cleanly (#2815).
-template<bool UNPADDED>
-simdjson_inline const uint8_t *json_iterator::peek() const noexcept {
+//
+// When BOUNDED (see dom_parser_implementation::_bounded_batch), a document may
+// not run past batch_end, one past the last structural index of the batch:
+// past it, we return the same dummy, and walk_document fails cleanly.
+template<bool UNPADDED, bool BOUNDED>
+simdjson_inline const uint8_t *json_iterator::peek(const uint32_t *batch_end) const noexcept {
+ SIMDJSON_IF_CONSTEXPR (BOUNDED) {
+ if (simdjson_unlikely(next_structural >= batch_end)) {
+ static constexpr uint8_t streaming_eof_sentinel = 0;
+ return &streaming_eof_sentinel;
+ }
+ }
const uint32_t idx = *(next_structural);
SIMDJSON_IF_CONSTEXPR (UNPADDED) {
if (simdjson_unlikely(idx >= dom_parser.len)) {
@@ -66906,8 +65868,14 @@ simdjson_inline const uint8_t *json_iterator::peek() const noexcept {
}
return &buf[idx];
}
-template<bool UNPADDED>
-simdjson_inline const uint8_t *json_iterator::advance() noexcept {
+template<bool UNPADDED, bool BOUNDED>
+simdjson_inline const uint8_t *json_iterator::advance(const uint32_t *batch_end) noexcept {
+ SIMDJSON_IF_CONSTEXPR (BOUNDED) {
+ if (simdjson_unlikely(next_structural >= batch_end)) {
+ static constexpr uint8_t streaming_eof_sentinel = 0;
+ return &streaming_eof_sentinel;
+ }
+ }
const uint32_t idx = *(next_structural++);
SIMDJSON_IF_CONSTEXPR (UNPADDED) {
if (simdjson_unlikely(idx >= dom_parser.len)) {
@@ -67466,6 +66434,30 @@ struct tape_builder {
return iter.walk_document<STREAMING, false>(builder);
}
}
+
+ // The stage 2 entry points of dom::document_stream, which picks one per batch
+ // (see internal::dom_parser_implementation::stage2_next_function). Out of
+ // line, so that stage2_next() can share the code.
+ simdjson_warn_unused static simdjson_never_inline error_code parse_next_document(
+ internal::dom_parser_implementation &dom_parser, dom::document &doc) noexcept {
+ return parse_document<true>(static_cast<dom_parser_implementation &>(dom_parser), doc);
+ }
+
+ // A document of a stream batch that stage 2 must bound to its structural
+ // indexes (see internal::dom_parser_implementation::_bounded_batch).
+ simdjson_warn_unused static simdjson_never_inline error_code parse_next_bounded_document(
+ internal::dom_parser_implementation &_dom_parser, dom::document &doc) noexcept {
+ dom_parser_implementation &dom_parser = static_cast<dom_parser_implementation &>(_dom_parser);
+ dom_parser.doc = &doc;
+ json_iterator iter(dom_parser, dom_parser.next_structural_index);
+ if (dom_parser._unpadded) {
+ tape_builder_impl<true> builder(doc);
+ return iter.walk_document<true, true, true>(builder);
+ } else {
+ tape_builder_impl<false> builder(doc);
+ return iter.walk_document<true, false, true>(builder);
+ }
+ }
};
template <bool UNPADDED>
@@ -67890,7 +66882,11 @@ simdjson_warn_unused error_code dom_parser_implementation::stage2(dom::document
}
simdjson_warn_unused error_code dom_parser_implementation::stage2_next(dom::document &_doc) noexcept {
- return stage2::tape_builder::parse_document<true>(*this, _doc);
+ return stage2::tape_builder::parse_next_document(*this, _doc);
+}
+
+internal::dom_parser_implementation::stage2_next_function_t dom_parser_implementation::stage2_next_function(bool bounded) const noexcept {
+ return bounded ? stage2::tape_builder::parse_next_bounded_document : stage2::tape_builder::parse_next_document;
}
SIMDJSON_NO_SANITIZE_MEMORY
@@ -68500,23 +67496,6 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin
return { (v == '\\').to_bitmask(), (v == '"').to_bitmask() };
}
-struct escaping {
- static constexpr uint64_t BYTES_PROCESSED = sizeof(simd8<uint8_t>);
- simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
-
- simdjson_inline bool has_escape() { return escape_bits != 0; }
- simdjson_inline int escape_index() { return trailing_zeroes(escape_bits) / 4; }
-
- uint64_t escape_bits;
-}; // struct escaping
-
-simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
- static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes");
- simd8<uint8_t> v(src);
- v.store(dst);
- return { ((v == '"') | (v == '\\') | (v == 32)).to_bitmask() };
-}
-
} // unnamed namespace
} // namespace rvv_vls
@@ -68969,6 +67948,7 @@ public:
simdjson_warn_unused error_code stage1(const uint8_t *buf, size_t len, stage1_mode partial) noexcept final;
simdjson_warn_unused error_code stage2(dom::document &doc) noexcept final;
simdjson_warn_unused error_code stage2_next(dom::document &doc) noexcept final;
+ stage2_next_function_t stage2_next_function(bool bounded) const noexcept final;
simdjson_warn_unused uint8_t *parse_string(const uint8_t *src, uint8_t *dst, bool allow_replacement) const noexcept final;
simdjson_warn_unused uint8_t *parse_wobbly_string(const uint8_t *src, uint8_t *dst) const noexcept final;
inline simdjson_warn_unused error_code set_capacity(size_t capacity) noexcept final;
@@ -69730,21 +68710,6 @@ static bool parse_float_fallback(const uint8_t *ptr, double *outDouble) {
return !(*outDouble > (std::numeric_limits<double>::max)() || *outDouble < std::numeric_limits<double>::lowest());
}
-static bool parse_float_fallback(const uint8_t *ptr, const uint8_t *end_ptr, double *outDouble) {
- *outDouble = simdjson::internal::from_chars(reinterpret_cast<const char *>(ptr), reinterpret_cast<const char *>(end_ptr));
- // We do not accept infinite values.
-
- // Detecting finite values in a portable manner is ridiculously hard, ideally
- // we would want to do:
- // return !std::isfinite(*outDouble);
- // but that mysteriously fails under legacy/old libc++ libraries, see
- // https://github.com/simdjson/simdjson/issues/1286
- //
- // Therefore, fall back to this solution (the extra parens are there
- // to handle that max may be a macro on windows).
- return !(*outDouble > (std::numeric_limits<double>::max)() || *outDouble < std::numeric_limits<double>::lowest());
-}
-
// Same as parse_float_fallback, but for binary32 (float) values. Going through
// the binary64 fallback and then rounding to binary32 would be subject to
// double rounding, so we run the fallback algorithm directly on binary32.
@@ -70290,57 +69255,6 @@ simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned(const u
return i;
}
-
-
-// Parse any number from 0 to 18,446,744,073,709,551,615
-// Never read at src_end or beyond
-simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned(const uint8_t * const src, const uint8_t * const src_end) noexcept {
- const uint8_t *p = src;
- //
- // Parse the integer part.
- //
- // PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
- const uint8_t *const start_digits = p;
- uint64_t i = 0;
- while ((p != src_end) && parse_digit(*p, i)) { p++; }
-
- // If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
- // Optimization note: size_t is expected to be unsigned.
- size_t digit_count = size_t(p - start_digits);
- // The longest positive 64-bit number is 20 digits.
- // We do it this way so we don't trigger this branch unless we must.
- // Optimization note: the compiler can probably merge
- // ((digit_count == 0) || (digit_count > 20))
- // into a single branch since digit_count is unsigned.
- if ((digit_count == 0) || (digit_count > 20)) { return INCORRECT_TYPE; }
- // Here digit_count > 0.
- if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
- // We can do the following...
- // if (!jsoncharutils::is_structural_or_whitespace(*p)) {
- // return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
- // }
- // as a single table lookup:
- if ((p != src_end) && integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); }
-
- if (digit_count == 20) {
- // Positive overflow check:
- // - A 20 digit number starting with 2-9 is overflow, because 18,446,744,073,709,551,615 is the
- // biggest uint64_t.
- // - A 20 digit number starting with 1 is overflow if it is less than INT64_MAX.
- // If we got here, it's a 20 digit number starting with the digit "1".
- // - If a 20 digit number starting with 1 overflowed (i*10+digit), the result will be smaller
- // than 1,553,255,926,290,448,384.
- // - That is smaller than the smallest possible 20-digit number the user could write:
- // 10,000,000,000,000,000,000.
- // - Therefore, if the number is positive and lower than that, it's overflow.
- // - The value we are looking at is less than or equal to INT64_MAX.
- //
- if (src[0] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INCORRECT_TYPE; }
- }
-
- return i;
-}
-
// Parse any number from 0 to 18,446,744,073,709,551,615
simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned_in_string(const uint8_t * const src) noexcept {
const uint8_t *p = src + 1;
@@ -70433,50 +69347,6 @@ simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer(const uin
return negative ? (~i+1) : i;
}
-// Parse any number from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
-// Never read at src_end or beyond
-simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer(const uint8_t * const src, const uint8_t * const src_end) noexcept {
- //
- // Check for minus sign
- //
- if(src == src_end) { return NUMBER_ERROR; }
- bool negative = (*src == '-');
- const uint8_t *p = src + uint8_t(negative);
-
- //
- // Parse the integer part.
- //
- // PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
- const uint8_t *const start_digits = p;
- uint64_t i = 0;
- while ((p != src_end) && parse_digit(*p, i)) { p++; }
-
- // If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
- // Optimization note: size_t is expected to be unsigned.
- size_t digit_count = size_t(p - start_digits);
- // We go from
- // -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
- // so we can never represent numbers that have more than 19 digits.
- size_t longest_digit_count = 19;
- // Optimization note: the compiler can probably merge
- // ((digit_count == 0) || (digit_count > longest_digit_count))
- // into a single branch since digit_count is unsigned.
- if ((digit_count == 0) || (digit_count > longest_digit_count)) { return INCORRECT_TYPE; }
- // Here digit_count > 0.
- if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
- // We can do the following...
- // if (!jsoncharutils::is_structural_or_whitespace(*p)) {
- // return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
- // }
- // as a single table lookup:
- if((p != src_end) && integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); }
- // Negative numbers have can go down to - INT64_MAX - 1 whereas positive numbers are limited to INT64_MAX.
- // Performance note: This check is only needed when digit_count == longest_digit_count but it is
- // so cheap that we might as well always make it.
- if(i > uint64_t(INT64_MAX) + uint64_t(negative)) { return INCORRECT_TYPE; }
- return negative ? (~i+1) : i;
-}
-
// Parse any number from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer_in_string(const uint8_t *src) noexcept {
//
@@ -70582,8 +69452,11 @@ simdjson_unused simdjson_inline simdjson_result<double> parse_double(const uint8
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
+ // no exp digits
+ if (p-start_exp_digits == 0) { return NUMBER_ERROR; }
+ // 20+ exp digits (e.g., 0e9999999999999999999999) are valid, but exp may
+ // have wrapped around: leave such numbers to the fallback parser.
+ overflow = overflow || p-start_exp_digits > 19;
exponent += exp_neg ? 0-exp : exp;
}
@@ -70673,8 +69546,11 @@ simdjson_unused simdjson_inline simdjson_result<float> parse_float(const uint8_t
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
+ // no exp digits
+ if (p-start_exp_digits == 0) { return NUMBER_ERROR; }
+ // 20+ exp digits (e.g., 0e9999999999999999999999) are valid, but exp may
+ // have wrapped around: leave such numbers to the fallback parser.
+ overflow = overflow || p-start_exp_digits > 19;
exponent += exp_neg ? 0-exp : exp;
}
@@ -70754,88 +69630,6 @@ simdjson_unused simdjson_inline simdjson_result<number_type> get_number_type(con
return number_type::floating_point_number;
}
-// Never read at src_end or beyond
-simdjson_unused simdjson_inline simdjson_result<double> parse_double(const uint8_t * src, const uint8_t * const src_end) noexcept {
- if(src == src_end) { return NUMBER_ERROR; }
- //
- // Check for minus sign
- //
- bool negative = (*src == '-');
- src += uint8_t(negative);
-
- //
- // Parse the integer part.
- //
- uint64_t i = 0;
- const uint8_t *p = src;
- if(p == src_end) { return NUMBER_ERROR; }
- p += parse_digit(*p, i);
- bool leading_zero = (i == 0);
- while ((p != src_end) && parse_digit(*p, i)) { p++; }
- // no integer digits, or 0123 (zero must be solo)
- if ( p == src ) { return INCORRECT_TYPE; }
- if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; }
-
- //
- // Parse the decimal part.
- //
- int64_t exponent = 0;
- bool overflow;
- if (simdjson_likely((p != src_end) && (*p == '.'))) {
- p++;
- const uint8_t *start_decimal_digits = p;
- if ((p == src_end) || !parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits
- p++;
- while ((p != src_end) && parse_digit(*p, i)) { p++; }
- exponent = -(p - start_decimal_digits);
-
- // Overflow check. More than 19 digits (minus the decimal) may be overflow.
- overflow = p-src-1 > 19;
- if (simdjson_unlikely(overflow && leading_zero)) {
- // Skip leading 0.00000 and see if it still overflows
- const uint8_t *start_digits = src + 2;
- while (*start_digits == '0') { start_digits++; }
- overflow = start_digits-src > 19;
- }
- } else {
- overflow = p-src > 19;
- }
-
- //
- // Parse the exponent
- //
- if ((p != src_end) && (*p == 'e' || *p == 'E')) {
- p++;
- if(p == src_end) { return NUMBER_ERROR; }
- bool exp_neg = *p == '-';
- p += exp_neg || *p == '+';
-
- uint64_t exp = 0;
- const uint8_t *start_exp_digits = p;
- while ((p != src_end) && parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
-
- exponent += exp_neg ? 0-exp : exp;
- }
-
- if ((p != src_end) && jsoncharutils::is_not_structural_or_whitespace(*p)) { return NUMBER_ERROR; }
-
- overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power;
-
- //
- // Assemble (or slow-parse) the float
- //
- double d;
- if (simdjson_likely(!overflow)) {
- if (compute_float_64(exponent, i, negative, d)) { return d; }
- }
- if (!parse_float_fallback(src - uint8_t(negative), src_end, &d)) {
- return NUMBER_ERROR;
- }
- return d;
-}
-
simdjson_unused simdjson_inline simdjson_result<double> parse_double_in_string(const uint8_t * src) noexcept {
//
// Check for minus sign
@@ -70904,8 +69698,11 @@ simdjson_unused simdjson_inline simdjson_result<double> parse_double_in_string(c
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
+ // no exp digits
+ if (p-start_exp_digits == 0) { return NUMBER_ERROR; }
+ // 20+ exp digits (e.g., 0e9999999999999999999999) are valid, but exp may
+ // have wrapped around: leave such numbers to the fallback parser.
+ overflow = overflow || p-start_exp_digits > 19;
exponent += exp_neg ? 0-exp : exp;
}
@@ -70997,8 +69794,11 @@ simdjson_unused simdjson_inline simdjson_result<float> parse_float_in_string(con
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
+ // no exp digits
+ if (p-start_exp_digits == 0) { return NUMBER_ERROR; }
+ // 20+ exp digits (e.g., 0e9999999999999999999999) are valid, but exp may
+ // have wrapped around: leave such numbers to the fallback parser.
+ overflow = overflow || p-start_exp_digits > 19;
exponent += exp_neg ? 0-exp : exp;
}
@@ -71774,23 +70574,6 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin
return { (v == '\\').to_bitmask(), (v == '"').to_bitmask() };
}
-struct escaping {
- static constexpr uint64_t BYTES_PROCESSED = sizeof(simd8<uint8_t>);
- simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
-
- simdjson_inline bool has_escape() { return escape_bits != 0; }
- simdjson_inline int escape_index() { return trailing_zeroes(escape_bits) / 4; }
-
- uint64_t escape_bits;
-}; // struct escaping
-
-simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
- static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes");
- simd8<uint8_t> v(src);
- v.store(dst);
- return { ((v == '"') | (v == '\\') | (v == 32)).to_bitmask() };
-}
-
} // unnamed namespace
} // namespace rvv_vls
@@ -72391,6 +71174,17 @@ public:
* @return the number of effective characters in the last block.
*/
simdjson_inline size_t get_remainder(uint8_t *dst) const;
+ /**
+ * Like get_remainder, for input followed by SIMDJSON_PADDING readable bytes.
+ *
+ * get_remainder writes the block with a memset and a memcpy of the remaining
+ * length, and the vector loads that read it back cannot be forwarded from
+ * those narrower stores: they wait until the stores reach the cache, which
+ * costs tens of cycles on every document. Here the block is put together in
+ * vector registers, from loads that may read into the padding, and written
+ * with full-width stores that the loads can forward from.
+ */
+ simdjson_inline size_t get_remainder_padded(uint8_t *dst) const;
simdjson_inline void advance();
private:
const uint8_t *buf;
@@ -72399,47 +71193,6 @@ private:
size_t idx;
};
-// Routines to print masks and text for debugging bitmask operations
-simdjson_unused static char * format_input_text_64(const uint8_t *text) {
- static char buf[sizeof(simd8x64<uint8_t>) + 1];
- for (size_t i=0; i<sizeof(simd8x64<uint8_t>); i++) {
- buf[i] = int8_t(text[i]) < ' ' ? '_' : int8_t(text[i]);
- }
- buf[sizeof(simd8x64<uint8_t>)] = '\0';
- return buf;
-}
-
-// Routines to print masks and text for debugging bitmask operations
-simdjson_unused static char * format_input_text(const simd8x64<uint8_t>& in) {
- static char buf[sizeof(simd8x64<uint8_t>) + 1];
- in.store(reinterpret_cast<uint8_t*>(buf));
- for (size_t i=0; i<sizeof(simd8x64<uint8_t>); i++) {
- if (buf[i] < ' ') { buf[i] = '_'; }
- }
- buf[sizeof(simd8x64<uint8_t>)] = '\0';
- return buf;
-}
-
-simdjson_unused static char * format_input_text(const simd8x64<uint8_t>& in, uint64_t mask) {
- static char buf[sizeof(simd8x64<uint8_t>) + 1];
- in.store(reinterpret_cast<uint8_t*>(buf));
- for (size_t i=0; i<sizeof(simd8x64<uint8_t>); i++) {
- if (buf[i] <= ' ') { buf[i] = '_'; }
- if (!(mask & (size_t(1) << i))) { buf[i] = ' '; }
- }
- buf[sizeof(simd8x64<uint8_t>)] = '\0';
- return buf;
-}
-
-simdjson_unused static char * format_mask(uint64_t mask) {
- static char buf[sizeof(simd8x64<uint8_t>) + 1];
- for (size_t i=0; i<64; i++) {
- buf[i] = (mask & (size_t(1) << i)) ? 'X' : ' ';
- }
- buf[64] = '\0';
- return buf;
-}
-
template<size_t STEP_SIZE>
simdjson_inline buf_block_reader<STEP_SIZE>::buf_block_reader(const uint8_t *_buf, size_t _len) : buf{_buf}, len{_len}, lenminusstep{len < STEP_SIZE ? 0 : len - STEP_SIZE}, idx{0} {}
@@ -72464,6 +71217,38 @@ simdjson_inline size_t buf_block_reader<STEP_SIZE>::get_remainder(uint8_t *dst)
return len - idx;
}
+// keep_mask[128 - n + i] is 0xFF for i < n and 0 otherwise.
+alignas(64) static const uint8_t keep_mask[256] = {
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+template<size_t STEP_SIZE>
+simdjson_inline size_t buf_block_reader<STEP_SIZE>::get_remainder_padded(uint8_t *dst) const {
+ static_assert(STEP_SIZE <= 128, "keep_mask covers blocks of up to 128 bytes");
+ static_assert(sizeof(simd::simd8<uint8_t>) <= SIMDJSON_PADDING, "a vector load at the last byte must stay in the padding");
+ const size_t remaining = len - idx;
+ if (remaining == 0) { return 0; }
+ const simd::simd8<uint8_t> spaces = simd::simd8<uint8_t>::splat(0x20);
+ for (size_t off = 0; off < STEP_SIZE; off += sizeof(simd::simd8<uint8_t>)) {
+ simd::simd8<uint8_t> v = spaces;
+ if (off < remaining) {
+ // Starts before len, so it ends within the padding.
+ const simd::simd8<uint8_t> in = simd::simd8<uint8_t>::load(buf + idx + off);
+ const simd::simd8<uint8_t> keep = simd::simd8<uint8_t>::load(keep_mask + 128 - remaining + off);
+ v = spaces ^ ((in ^ spaces) & keep);
+ }
+ v.store(dst + off);
+ }
+ return remaining;
+}
+
template<size_t STEP_SIZE>
simdjson_inline void buf_block_reader<STEP_SIZE>::advance() {
idx += STEP_SIZE;
@@ -73164,6 +71949,20 @@ simdjson_inline bool ends_with_partial_scalar(dom_parser_implementation &parser,
e != ' ' && e != '\t' && e != '\n' && e != '\r';
}
+/**
+ * The structural index past the last one of a partial batch is the end of this
+ * window, not a token of it: the byte there is the first byte of the next
+ * window. A finished value must not be rejected because that byte is ',', ':',
+ * ']' or '}'. A last document that itself ends with '{', '[', ',' or ':' is
+ * invalid JSON, yet its brackets may balance: walking it, the On-Demand API
+ * could read the next batch as a value. We leave such a document to the next
+ * batch, so that the batch ends between two documents.
+ */
+simdjson_inline bool ends_with_unfinished_document(dom_parser_implementation &parser) {
+ const uint8_t last = parser.buf[parser.structural_indexes[parser.n_structural_indexes - 1]];
+ return last == '{' || last == '[' || last == ',' || last == ':';
+}
+
simdjson_inline uint32_t find_next_document_index(dom_parser_implementation &parser, bool defer_last = false) {
// Variant: do not count separately, just figure out depth
if(parser.n_structural_indexes == 0) { return 0; }
@@ -73845,7 +72644,8 @@ error_code json_structural_indexer::index(const uint8_t *buf, size_t len, dom_pa
// Take care of the last block (will always be there unless file is empty which is
// not supposed to happen.)
uint8_t block[STEP_SIZE];
- if (simdjson_unlikely(reader.get_remainder(block) == 0)) { return UNEXPECTED_ERROR; }
+ const size_t remaining = parser._unpadded ? reader.get_remainder(block) : reader.get_remainder_padded(block);
+ if (simdjson_unlikely(remaining == 0)) { return UNEXPECTED_ERROR; }
indexer.step<STEP_SIZE>(block, reader);
return indexer.finish(parser, reader.block_index(), len, partial);
}
@@ -73879,6 +72679,20 @@ simdjson_inline void json_structural_indexer::next(const simd::simd8x64<uint8_t>
unescaped_chars_error |= block.non_quote_inside_string(unescaped);
}
+// Drops the unclosed string that ends a json_sequence batch and leaves its
+// start in structural_indexes[n_structural_indexes]. The last index is its
+// opening quote, unless the quote directly follows an RS: the scanner reads
+// RS as a scalar byte, so it indexes the RS and not the quote. Keep the RS.
+simdjson_inline void drop_json_sequence_unclosed_string(dom_parser_implementation &parser, size_t len) noexcept {
+ uint32_t quote = parser.structural_indexes[parser.n_structural_indexes - 1];
+ if (parser.buf[quote] == '"') {
+ parser.n_structural_indexes--;
+ return;
+ }
+ while (quote < len && parser.buf[quote] != '"') { quote++; }
+ parser.structural_indexes[parser.n_structural_indexes] = quote;
+}
+
simdjson_inline error_code json_structural_indexer::finish(dom_parser_implementation &parser, size_t idx, size_t len, stage1_mode partial) {
// Write out the final iteration's structurals
indexer.write(uint32_t(idx-64), prev_structurals);
@@ -73935,8 +72749,13 @@ simdjson_inline error_code json_structural_indexer::finish(dom_parser_implementa
if (simdjson_unlikely(parser.n_structural_indexes == 0u)) { return CAPACITY; }
}
// We truncate the input to the end of the last complete document (or zero).
- auto new_structural_indexes = find_next_document_index(parser, !have_unclosed_string && ends_with_partial_scalar(parser, len));
+ const bool partial_scalar = !have_unclosed_string && ends_with_partial_scalar(parser, len);
+ const bool unfinished = ends_with_unfinished_document(parser);
+ auto new_structural_indexes = find_next_document_index(parser, partial_scalar || unfinished);
if (new_structural_indexes == 0 && parser.n_structural_indexes > 0) {
+ // The batch holds a single document, whose brackets balance but which
+ // cannot be valid JSON: it is not too big, it is invalid.
+ if (unfinished && find_next_document_index(parser, partial_scalar) != 0) { return TAPE_ERROR; }
if(parser.structural_indexes[0] == 0) {
// If the buffer is partial and we started at index 0 but the document is
// incomplete, it's too big to parse.
@@ -73980,7 +72799,7 @@ simdjson_inline error_code json_structural_indexer::finish(dom_parser_implementa
// A discarded unclosed string also caps how far the filter may scan.
size_t scan_len = len;
if(have_unclosed_string) {
- parser.n_structural_indexes--;
+ drop_json_sequence_unclosed_string(parser, len);
if (simdjson_unlikely(parser.n_structural_indexes == 0u)) { return CAPACITY; }
scan_len = parser.structural_indexes[parser.n_structural_indexes];
}
@@ -73999,12 +72818,17 @@ simdjson_inline error_code json_structural_indexer::finish(dom_parser_implementa
}
parser.n_structural_indexes = new_structural_indexes;
parser.structural_indexes[parser.n_structural_indexes] = next_batch_start;
+ // The next batch starts at an RS, which the On-Demand API never reads as a
+ // string, a container or a separator. But an invalid last document may make
+ // it skip that RS as a value: repeat it, so that the index past it is not
+ // the next record, which can be a string that does not close in this batch.
+ parser.structural_indexes[parser.n_structural_indexes + 1] = next_batch_start;
} else if (partial == stage1_mode::json_sequence_final) {
// RFC 7464: final batch, last document extends to EOF
// As above: a discarded unclosed string caps how far the filter may scan.
size_t scan_len = len;
if(have_unclosed_string) {
- parser.n_structural_indexes--;
+ drop_json_sequence_unclosed_string(parser, len);
scan_len = parser.structural_indexes[parser.n_structural_indexes];
}
uint32_t next_batch_start = uint32_t(len);
@@ -74422,7 +73246,7 @@ public:
*
* - increment_count(iter) - each time a value is found in an array or object.
*/
- template<bool STREAMING, bool UNPADDED, typename V>
+ template<bool STREAMING, bool UNPADDED, bool BOUNDED = false, typename V>
simdjson_warn_unused simdjson_inline error_code walk_document(V &visitor) noexcept;
/**
@@ -74439,8 +73263,8 @@ public:
*
* They may include invalid JSON as well (such as `1.2.3` or `ture`).
*/
- template<bool UNPADDED>
- simdjson_inline const uint8_t *peek() const noexcept;
+ template<bool UNPADDED, bool BOUNDED = false>
+ simdjson_inline const uint8_t *peek(const uint32_t *batch_end = nullptr) const noexcept;
/**
* Advance to the next token.
*
@@ -74448,8 +73272,8 @@ public:
*
* They may include invalid JSON as well (such as `1.2.3` or `ture`).
*/
- template<bool UNPADDED>
- simdjson_inline const uint8_t *advance() noexcept;
+ template<bool UNPADDED, bool BOUNDED = false>
+ simdjson_inline const uint8_t *advance(const uint32_t *batch_end = nullptr) noexcept;
/**
* Get the remaining length of the document, from the start of the current token.
*/
@@ -74497,7 +73321,7 @@ public:
simdjson_warn_unused simdjson_inline error_code visit_primitive(V &visitor, const uint8_t *value) noexcept;
};
-template<bool STREAMING, bool UNPADDED, typename V>
+template<bool STREAMING, bool UNPADDED, bool BOUNDED, typename V>
simdjson_warn_unused simdjson_inline error_code json_iterator::walk_document(V &visitor) noexcept {
logger::log_start();
@@ -74505,6 +73329,9 @@ simdjson_warn_unused simdjson_inline error_code json_iterator::walk_document(V &
// Start the document
//
if (at_eof()) { return EMPTY; }
+ // Only used when BOUNDED (see advance()); a local, so that it can stay in a
+ // register, and vanishes from the other walks.
+ const uint32_t *const batch_end = &dom_parser.structural_indexes[dom_parser.n_structural_indexes];
log_start_value("document");
SIMDJSON_TRY( visitor.visit_document_start(*this) );
@@ -74512,7 +73339,7 @@ simdjson_warn_unused simdjson_inline error_code json_iterator::walk_document(V &
// Read first value
//
{
- auto value = advance<UNPADDED>();
+ auto value = advance<UNPADDED, BOUNDED>(batch_end);
// Make sure the outer object or array is closed before continuing; otherwise, there are ways we
// could get into memory corruption. See https://github.com/simdjson/simdjson/issues/906
@@ -74524,8 +73351,8 @@ simdjson_warn_unused simdjson_inline error_code json_iterator::walk_document(V &
}
switch (*value) {
- case '{': if (*peek<UNPADDED>() == '}') { advance<UNPADDED>(); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin;
- case '[': if (*peek<UNPADDED>() == ']') { advance<UNPADDED>(); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin;
+ case '{': if (*peek<UNPADDED, BOUNDED>(batch_end) == '}') { advance<UNPADDED, BOUNDED>(batch_end); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin;
+ case '[': if (*peek<UNPADDED, BOUNDED>(batch_end) == ']') { advance<UNPADDED, BOUNDED>(batch_end); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin;
default: SIMDJSON_TRY( visitor.visit_root_primitive(*this, value) ); break;
}
}
@@ -74542,29 +73369,29 @@ object_begin:
SIMDJSON_TRY( visitor.visit_object_start(*this) );
{
- auto key = advance<UNPADDED>();
+ auto key = advance<UNPADDED, BOUNDED>(batch_end);
if (*key != '"') { log_error("Object does not start with a key"); return TAPE_ERROR; }
SIMDJSON_TRY( visitor.increment_count(*this) );
SIMDJSON_TRY( visitor.visit_key(*this, key) );
}
object_field:
- if (simdjson_unlikely( *advance<UNPADDED>() != ':' )) { log_error("Missing colon after key in object"); return TAPE_ERROR; }
+ if (simdjson_unlikely( (*advance<UNPADDED, BOUNDED>(batch_end) != ':') )) { log_error("Missing colon after key in object"); return TAPE_ERROR; }
{
- auto value = advance<UNPADDED>();
+ auto value = advance<UNPADDED, BOUNDED>(batch_end);
switch (*value) {
- case '{': if (*peek<UNPADDED>() == '}') { advance<UNPADDED>(); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin;
- case '[': if (*peek<UNPADDED>() == ']') { advance<UNPADDED>(); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin;
+ case '{': if (*peek<UNPADDED, BOUNDED>(batch_end) == '}') { advance<UNPADDED, BOUNDED>(batch_end); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin;
+ case '[': if (*peek<UNPADDED, BOUNDED>(batch_end) == ']') { advance<UNPADDED, BOUNDED>(batch_end); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin;
default: SIMDJSON_TRY( visitor.visit_primitive(*this, value) ); break;
}
}
object_continue:
- switch (*advance<UNPADDED>()) {
+ switch (*advance<UNPADDED, BOUNDED>(batch_end)) {
case ',':
SIMDJSON_TRY( visitor.increment_count(*this) );
{
- auto key = advance<UNPADDED>();
+ auto key = advance<UNPADDED, BOUNDED>(batch_end);
if (simdjson_unlikely( *key != '"' )) { log_error("Key string missing at beginning of field in object"); return TAPE_ERROR; }
SIMDJSON_TRY( visitor.visit_key(*this, key) );
}
@@ -74592,16 +73419,16 @@ array_begin:
array_value:
{
- auto value = advance<UNPADDED>();
+ auto value = advance<UNPADDED, BOUNDED>(batch_end);
switch (*value) {
- case '{': if (*peek<UNPADDED>() == '}') { advance<UNPADDED>(); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin;
- case '[': if (*peek<UNPADDED>() == ']') { advance<UNPADDED>(); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin;
+ case '{': if (*peek<UNPADDED, BOUNDED>(batch_end) == '}') { advance<UNPADDED, BOUNDED>(batch_end); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin;
+ case '[': if (*peek<UNPADDED, BOUNDED>(batch_end) == ']') { advance<UNPADDED, BOUNDED>(batch_end); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin;
default: SIMDJSON_TRY( visitor.visit_primitive(*this, value) ); break;
}
}
array_continue:
- switch (*advance<UNPADDED>()) {
+ switch (*advance<UNPADDED, BOUNDED>(batch_end)) {
case ',': SIMDJSON_TRY( visitor.increment_count(*this) ); goto array_value;
case ']': log_end_value("array"); SIMDJSON_TRY( visitor.visit_array_end(*this) ); goto scope_end;
default: log_error("Missing comma between array values"); return TAPE_ERROR;
@@ -74631,8 +73458,18 @@ simdjson_inline json_iterator::json_iterator(dom_parser_implementation &_dom_par
// Stage 1 leaves a sentinel index of value `len`; reading buf[len] requires
// padding. For UNPADDED, return a dummy so walk_document can fail cleanly (#2815).
-template<bool UNPADDED>
-simdjson_inline const uint8_t *json_iterator::peek() const noexcept {
+//
+// When BOUNDED (see dom_parser_implementation::_bounded_batch), a document may
+// not run past batch_end, one past the last structural index of the batch:
+// past it, we return the same dummy, and walk_document fails cleanly.
+template<bool UNPADDED, bool BOUNDED>
+simdjson_inline const uint8_t *json_iterator::peek(const uint32_t *batch_end) const noexcept {
+ SIMDJSON_IF_CONSTEXPR (BOUNDED) {
+ if (simdjson_unlikely(next_structural >= batch_end)) {
+ static constexpr uint8_t streaming_eof_sentinel = 0;
+ return &streaming_eof_sentinel;
+ }
+ }
const uint32_t idx = *(next_structural);
SIMDJSON_IF_CONSTEXPR (UNPADDED) {
if (simdjson_unlikely(idx >= dom_parser.len)) {
@@ -74642,8 +73479,14 @@ simdjson_inline const uint8_t *json_iterator::peek() const noexcept {
}
return &buf[idx];
}
-template<bool UNPADDED>
-simdjson_inline const uint8_t *json_iterator::advance() noexcept {
+template<bool UNPADDED, bool BOUNDED>
+simdjson_inline const uint8_t *json_iterator::advance(const uint32_t *batch_end) noexcept {
+ SIMDJSON_IF_CONSTEXPR (BOUNDED) {
+ if (simdjson_unlikely(next_structural >= batch_end)) {
+ static constexpr uint8_t streaming_eof_sentinel = 0;
+ return &streaming_eof_sentinel;
+ }
+ }
const uint32_t idx = *(next_structural++);
SIMDJSON_IF_CONSTEXPR (UNPADDED) {
if (simdjson_unlikely(idx >= dom_parser.len)) {
@@ -75202,6 +74045,30 @@ struct tape_builder {
return iter.walk_document<STREAMING, false>(builder);
}
}
+
+ // The stage 2 entry points of dom::document_stream, which picks one per batch
+ // (see internal::dom_parser_implementation::stage2_next_function). Out of
+ // line, so that stage2_next() can share the code.
+ simdjson_warn_unused static simdjson_never_inline error_code parse_next_document(
+ internal::dom_parser_implementation &dom_parser, dom::document &doc) noexcept {
+ return parse_document<true>(static_cast<dom_parser_implementation &>(dom_parser), doc);
+ }
+
+ // A document of a stream batch that stage 2 must bound to its structural
+ // indexes (see internal::dom_parser_implementation::_bounded_batch).
+ simdjson_warn_unused static simdjson_never_inline error_code parse_next_bounded_document(
+ internal::dom_parser_implementation &_dom_parser, dom::document &doc) noexcept {
+ dom_parser_implementation &dom_parser = static_cast<dom_parser_implementation &>(_dom_parser);
+ dom_parser.doc = &doc;
+ json_iterator iter(dom_parser, dom_parser.next_structural_index);
+ if (dom_parser._unpadded) {
+ tape_builder_impl<true> builder(doc);
+ return iter.walk_document<true, true, true>(builder);
+ } else {
+ tape_builder_impl<false> builder(doc);
+ return iter.walk_document<true, false, true>(builder);
+ }
+ }
};
template <bool UNPADDED>
@@ -75620,7 +74487,11 @@ simdjson_warn_unused error_code dom_parser_implementation::stage2(dom::document
}
simdjson_warn_unused error_code dom_parser_implementation::stage2_next(dom::document &_doc) noexcept {
- return stage2::tape_builder::parse_document<true>(*this, _doc);
+ return stage2::tape_builder::parse_next_document(*this, _doc);
+}
+
+internal::dom_parser_implementation::stage2_next_function_t dom_parser_implementation::stage2_next_function(bool bounded) const noexcept {
+ return bounded ? stage2::tape_builder::parse_next_bounded_document : stage2::tape_builder::parse_next_document;
}
SIMDJSON_NO_SANITIZE_MEMORY
@@ -75793,23 +74664,6 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin
}
-struct escaping {
- static constexpr uint32_t BYTES_PROCESSED = 1;
- simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
-
- simdjson_inline bool has_escape() { return escape_bits; }
- simdjson_inline int escape_index() { return 0; }
-
- bool escape_bits;
-}; // struct escaping
-
-
-
-simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
- dst[0] = src[0];
- return { (src[0] == '\\') || (src[0] == '"') || (src[0] < 32) };
-}
-
} // unnamed namespace
} // namespace fallback
} // namespace simdjson
@@ -76289,6 +75143,7 @@ public:
simdjson_warn_unused error_code stage1(const uint8_t *buf, size_t len, stage1_mode partial) noexcept final;
simdjson_warn_unused error_code stage2(dom::document &doc) noexcept final;
simdjson_warn_unused error_code stage2_next(dom::document &doc) noexcept final;
+ stage2_next_function_t stage2_next_function(bool bounded) const noexcept final;
simdjson_warn_unused uint8_t *parse_string(const uint8_t *src, uint8_t *dst, bool allow_replacement) const noexcept final;
simdjson_warn_unused uint8_t *parse_wobbly_string(const uint8_t *src, uint8_t *dst) const noexcept final;
inline simdjson_warn_unused error_code set_capacity(size_t capacity) noexcept final;
@@ -77050,21 +75905,6 @@ static bool parse_float_fallback(const uint8_t *ptr, double *outDouble) {
return !(*outDouble > (std::numeric_limits<double>::max)() || *outDouble < std::numeric_limits<double>::lowest());
}
-static bool parse_float_fallback(const uint8_t *ptr, const uint8_t *end_ptr, double *outDouble) {
- *outDouble = simdjson::internal::from_chars(reinterpret_cast<const char *>(ptr), reinterpret_cast<const char *>(end_ptr));
- // We do not accept infinite values.
-
- // Detecting finite values in a portable manner is ridiculously hard, ideally
- // we would want to do:
- // return !std::isfinite(*outDouble);
- // but that mysteriously fails under legacy/old libc++ libraries, see
- // https://github.com/simdjson/simdjson/issues/1286
- //
- // Therefore, fall back to this solution (the extra parens are there
- // to handle that max may be a macro on windows).
- return !(*outDouble > (std::numeric_limits<double>::max)() || *outDouble < std::numeric_limits<double>::lowest());
-}
-
// Same as parse_float_fallback, but for binary32 (float) values. Going through
// the binary64 fallback and then rounding to binary32 would be subject to
// double rounding, so we run the fallback algorithm directly on binary32.
@@ -77610,57 +76450,6 @@ simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned(const u
return i;
}
-
-
-// Parse any number from 0 to 18,446,744,073,709,551,615
-// Never read at src_end or beyond
-simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned(const uint8_t * const src, const uint8_t * const src_end) noexcept {
- const uint8_t *p = src;
- //
- // Parse the integer part.
- //
- // PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
- const uint8_t *const start_digits = p;
- uint64_t i = 0;
- while ((p != src_end) && parse_digit(*p, i)) { p++; }
-
- // If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
- // Optimization note: size_t is expected to be unsigned.
- size_t digit_count = size_t(p - start_digits);
- // The longest positive 64-bit number is 20 digits.
- // We do it this way so we don't trigger this branch unless we must.
- // Optimization note: the compiler can probably merge
- // ((digit_count == 0) || (digit_count > 20))
- // into a single branch since digit_count is unsigned.
- if ((digit_count == 0) || (digit_count > 20)) { return INCORRECT_TYPE; }
- // Here digit_count > 0.
- if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
- // We can do the following...
- // if (!jsoncharutils::is_structural_or_whitespace(*p)) {
- // return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
- // }
- // as a single table lookup:
- if ((p != src_end) && integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); }
-
- if (digit_count == 20) {
- // Positive overflow check:
- // - A 20 digit number starting with 2-9 is overflow, because 18,446,744,073,709,551,615 is the
- // biggest uint64_t.
- // - A 20 digit number starting with 1 is overflow if it is less than INT64_MAX.
- // If we got here, it's a 20 digit number starting with the digit "1".
- // - If a 20 digit number starting with 1 overflowed (i*10+digit), the result will be smaller
- // than 1,553,255,926,290,448,384.
- // - That is smaller than the smallest possible 20-digit number the user could write:
- // 10,000,000,000,000,000,000.
- // - Therefore, if the number is positive and lower than that, it's overflow.
- // - The value we are looking at is less than or equal to INT64_MAX.
- //
- if (src[0] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INCORRECT_TYPE; }
- }
-
- return i;
-}
-
// Parse any number from 0 to 18,446,744,073,709,551,615
simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned_in_string(const uint8_t * const src) noexcept {
const uint8_t *p = src + 1;
@@ -77753,50 +76542,6 @@ simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer(const uin
return negative ? (~i+1) : i;
}
-// Parse any number from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
-// Never read at src_end or beyond
-simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer(const uint8_t * const src, const uint8_t * const src_end) noexcept {
- //
- // Check for minus sign
- //
- if(src == src_end) { return NUMBER_ERROR; }
- bool negative = (*src == '-');
- const uint8_t *p = src + uint8_t(negative);
-
- //
- // Parse the integer part.
- //
- // PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
- const uint8_t *const start_digits = p;
- uint64_t i = 0;
- while ((p != src_end) && parse_digit(*p, i)) { p++; }
-
- // If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
- // Optimization note: size_t is expected to be unsigned.
- size_t digit_count = size_t(p - start_digits);
- // We go from
- // -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
- // so we can never represent numbers that have more than 19 digits.
- size_t longest_digit_count = 19;
- // Optimization note: the compiler can probably merge
- // ((digit_count == 0) || (digit_count > longest_digit_count))
- // into a single branch since digit_count is unsigned.
- if ((digit_count == 0) || (digit_count > longest_digit_count)) { return INCORRECT_TYPE; }
- // Here digit_count > 0.
- if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
- // We can do the following...
- // if (!jsoncharutils::is_structural_or_whitespace(*p)) {
- // return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
- // }
- // as a single table lookup:
- if((p != src_end) && integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); }
- // Negative numbers have can go down to - INT64_MAX - 1 whereas positive numbers are limited to INT64_MAX.
- // Performance note: This check is only needed when digit_count == longest_digit_count but it is
- // so cheap that we might as well always make it.
- if(i > uint64_t(INT64_MAX) + uint64_t(negative)) { return INCORRECT_TYPE; }
- return negative ? (~i+1) : i;
-}
-
// Parse any number from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer_in_string(const uint8_t *src) noexcept {
//
@@ -77902,8 +76647,11 @@ simdjson_unused simdjson_inline simdjson_result<double> parse_double(const uint8
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
+ // no exp digits
+ if (p-start_exp_digits == 0) { return NUMBER_ERROR; }
+ // 20+ exp digits (e.g., 0e9999999999999999999999) are valid, but exp may
+ // have wrapped around: leave such numbers to the fallback parser.
+ overflow = overflow || p-start_exp_digits > 19;
exponent += exp_neg ? 0-exp : exp;
}
@@ -77993,8 +76741,11 @@ simdjson_unused simdjson_inline simdjson_result<float> parse_float(const uint8_t
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
+ // no exp digits
+ if (p-start_exp_digits == 0) { return NUMBER_ERROR; }
+ // 20+ exp digits (e.g., 0e9999999999999999999999) are valid, but exp may
+ // have wrapped around: leave such numbers to the fallback parser.
+ overflow = overflow || p-start_exp_digits > 19;
exponent += exp_neg ? 0-exp : exp;
}
@@ -78074,88 +76825,6 @@ simdjson_unused simdjson_inline simdjson_result<number_type> get_number_type(con
return number_type::floating_point_number;
}
-// Never read at src_end or beyond
-simdjson_unused simdjson_inline simdjson_result<double> parse_double(const uint8_t * src, const uint8_t * const src_end) noexcept {
- if(src == src_end) { return NUMBER_ERROR; }
- //
- // Check for minus sign
- //
- bool negative = (*src == '-');
- src += uint8_t(negative);
-
- //
- // Parse the integer part.
- //
- uint64_t i = 0;
- const uint8_t *p = src;
- if(p == src_end) { return NUMBER_ERROR; }
- p += parse_digit(*p, i);
- bool leading_zero = (i == 0);
- while ((p != src_end) && parse_digit(*p, i)) { p++; }
- // no integer digits, or 0123 (zero must be solo)
- if ( p == src ) { return INCORRECT_TYPE; }
- if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; }
-
- //
- // Parse the decimal part.
- //
- int64_t exponent = 0;
- bool overflow;
- if (simdjson_likely((p != src_end) && (*p == '.'))) {
- p++;
- const uint8_t *start_decimal_digits = p;
- if ((p == src_end) || !parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits
- p++;
- while ((p != src_end) && parse_digit(*p, i)) { p++; }
- exponent = -(p - start_decimal_digits);
-
- // Overflow check. More than 19 digits (minus the decimal) may be overflow.
- overflow = p-src-1 > 19;
- if (simdjson_unlikely(overflow && leading_zero)) {
- // Skip leading 0.00000 and see if it still overflows
- const uint8_t *start_digits = src + 2;
- while (*start_digits == '0') { start_digits++; }
- overflow = start_digits-src > 19;
- }
- } else {
- overflow = p-src > 19;
- }
-
- //
- // Parse the exponent
- //
- if ((p != src_end) && (*p == 'e' || *p == 'E')) {
- p++;
- if(p == src_end) { return NUMBER_ERROR; }
- bool exp_neg = *p == '-';
- p += exp_neg || *p == '+';
-
- uint64_t exp = 0;
- const uint8_t *start_exp_digits = p;
- while ((p != src_end) && parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
-
- exponent += exp_neg ? 0-exp : exp;
- }
-
- if ((p != src_end) && jsoncharutils::is_not_structural_or_whitespace(*p)) { return NUMBER_ERROR; }
-
- overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power;
-
- //
- // Assemble (or slow-parse) the float
- //
- double d;
- if (simdjson_likely(!overflow)) {
- if (compute_float_64(exponent, i, negative, d)) { return d; }
- }
- if (!parse_float_fallback(src - uint8_t(negative), src_end, &d)) {
- return NUMBER_ERROR;
- }
- return d;
-}
-
simdjson_unused simdjson_inline simdjson_result<double> parse_double_in_string(const uint8_t * src) noexcept {
//
// Check for minus sign
@@ -78224,8 +76893,11 @@ simdjson_unused simdjson_inline simdjson_result<double> parse_double_in_string(c
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
+ // no exp digits
+ if (p-start_exp_digits == 0) { return NUMBER_ERROR; }
+ // 20+ exp digits (e.g., 0e9999999999999999999999) are valid, but exp may
+ // have wrapped around: leave such numbers to the fallback parser.
+ overflow = overflow || p-start_exp_digits > 19;
exponent += exp_neg ? 0-exp : exp;
}
@@ -78317,8 +76989,11 @@ simdjson_unused simdjson_inline simdjson_result<float> parse_float_in_string(con
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
+ // no exp digits
+ if (p-start_exp_digits == 0) { return NUMBER_ERROR; }
+ // 20+ exp digits (e.g., 0e9999999999999999999999) are valid, but exp may
+ // have wrapped around: leave such numbers to the fallback parser.
+ overflow = overflow || p-start_exp_digits > 19;
exponent += exp_neg ? 0-exp : exp;
}
@@ -78659,23 +77334,6 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin
}
-struct escaping {
- static constexpr uint32_t BYTES_PROCESSED = 1;
- simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
-
- simdjson_inline bool has_escape() { return escape_bits; }
- simdjson_inline int escape_index() { return 0; }
-
- bool escape_bits;
-}; // struct escaping
-
-
-
-simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
- dst[0] = src[0];
- return { (src[0] == '\\') || (src[0] == '"') || (src[0] < 32) };
-}
-
} // unnamed namespace
} // namespace fallback
} // namespace simdjson
@@ -78819,6 +77477,20 @@ simdjson_inline bool ends_with_partial_scalar(dom_parser_implementation &parser,
e != ' ' && e != '\t' && e != '\n' && e != '\r';
}
+/**
+ * The structural index past the last one of a partial batch is the end of this
+ * window, not a token of it: the byte there is the first byte of the next
+ * window. A finished value must not be rejected because that byte is ',', ':',
+ * ']' or '}'. A last document that itself ends with '{', '[', ',' or ':' is
+ * invalid JSON, yet its brackets may balance: walking it, the On-Demand API
+ * could read the next batch as a value. We leave such a document to the next
+ * batch, so that the batch ends between two documents.
+ */
+simdjson_inline bool ends_with_unfinished_document(dom_parser_implementation &parser) {
+ const uint8_t last = parser.buf[parser.structural_indexes[parser.n_structural_indexes - 1]];
+ return last == '{' || last == '[' || last == ',' || last == ':';
+}
+
simdjson_inline uint32_t find_next_document_index(dom_parser_implementation &parser, bool defer_last = false) {
// Variant: do not count separately, just figure out depth
if(parser.n_structural_indexes == 0) { return 0; }
@@ -79652,7 +78324,7 @@ public:
*
* - increment_count(iter) - each time a value is found in an array or object.
*/
- template<bool STREAMING, bool UNPADDED, typename V>
+ template<bool STREAMING, bool UNPADDED, bool BOUNDED = false, typename V>
simdjson_warn_unused simdjson_inline error_code walk_document(V &visitor) noexcept;
/**
@@ -79669,8 +78341,8 @@ public:
*
* They may include invalid JSON as well (such as `1.2.3` or `ture`).
*/
- template<bool UNPADDED>
- simdjson_inline const uint8_t *peek() const noexcept;
+ template<bool UNPADDED, bool BOUNDED = false>
+ simdjson_inline const uint8_t *peek(const uint32_t *batch_end = nullptr) const noexcept;
/**
* Advance to the next token.
*
@@ -79678,8 +78350,8 @@ public:
*
* They may include invalid JSON as well (such as `1.2.3` or `ture`).
*/
- template<bool UNPADDED>
- simdjson_inline const uint8_t *advance() noexcept;
+ template<bool UNPADDED, bool BOUNDED = false>
+ simdjson_inline const uint8_t *advance(const uint32_t *batch_end = nullptr) noexcept;
/**
* Get the remaining length of the document, from the start of the current token.
*/
@@ -79727,7 +78399,7 @@ public:
simdjson_warn_unused simdjson_inline error_code visit_primitive(V &visitor, const uint8_t *value) noexcept;
};
-template<bool STREAMING, bool UNPADDED, typename V>
+template<bool STREAMING, bool UNPADDED, bool BOUNDED, typename V>
simdjson_warn_unused simdjson_inline error_code json_iterator::walk_document(V &visitor) noexcept {
logger::log_start();
@@ -79735,6 +78407,9 @@ simdjson_warn_unused simdjson_inline error_code json_iterator::walk_document(V &
// Start the document
//
if (at_eof()) { return EMPTY; }
+ // Only used when BOUNDED (see advance()); a local, so that it can stay in a
+ // register, and vanishes from the other walks.
+ const uint32_t *const batch_end = &dom_parser.structural_indexes[dom_parser.n_structural_indexes];
log_start_value("document");
SIMDJSON_TRY( visitor.visit_document_start(*this) );
@@ -79742,7 +78417,7 @@ simdjson_warn_unused simdjson_inline error_code json_iterator::walk_document(V &
// Read first value
//
{
- auto value = advance<UNPADDED>();
+ auto value = advance<UNPADDED, BOUNDED>(batch_end);
// Make sure the outer object or array is closed before continuing; otherwise, there are ways we
// could get into memory corruption. See https://github.com/simdjson/simdjson/issues/906
@@ -79754,8 +78429,8 @@ simdjson_warn_unused simdjson_inline error_code json_iterator::walk_document(V &
}
switch (*value) {
- case '{': if (*peek<UNPADDED>() == '}') { advance<UNPADDED>(); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin;
- case '[': if (*peek<UNPADDED>() == ']') { advance<UNPADDED>(); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin;
+ case '{': if (*peek<UNPADDED, BOUNDED>(batch_end) == '}') { advance<UNPADDED, BOUNDED>(batch_end); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin;
+ case '[': if (*peek<UNPADDED, BOUNDED>(batch_end) == ']') { advance<UNPADDED, BOUNDED>(batch_end); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin;
default: SIMDJSON_TRY( visitor.visit_root_primitive(*this, value) ); break;
}
}
@@ -79772,29 +78447,29 @@ object_begin:
SIMDJSON_TRY( visitor.visit_object_start(*this) );
{
- auto key = advance<UNPADDED>();
+ auto key = advance<UNPADDED, BOUNDED>(batch_end);
if (*key != '"') { log_error("Object does not start with a key"); return TAPE_ERROR; }
SIMDJSON_TRY( visitor.increment_count(*this) );
SIMDJSON_TRY( visitor.visit_key(*this, key) );
}
object_field:
- if (simdjson_unlikely( *advance<UNPADDED>() != ':' )) { log_error("Missing colon after key in object"); return TAPE_ERROR; }
+ if (simdjson_unlikely( (*advance<UNPADDED, BOUNDED>(batch_end) != ':') )) { log_error("Missing colon after key in object"); return TAPE_ERROR; }
{
- auto value = advance<UNPADDED>();
+ auto value = advance<UNPADDED, BOUNDED>(batch_end);
switch (*value) {
- case '{': if (*peek<UNPADDED>() == '}') { advance<UNPADDED>(); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin;
- case '[': if (*peek<UNPADDED>() == ']') { advance<UNPADDED>(); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin;
+ case '{': if (*peek<UNPADDED, BOUNDED>(batch_end) == '}') { advance<UNPADDED, BOUNDED>(batch_end); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin;
+ case '[': if (*peek<UNPADDED, BOUNDED>(batch_end) == ']') { advance<UNPADDED, BOUNDED>(batch_end); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin;
default: SIMDJSON_TRY( visitor.visit_primitive(*this, value) ); break;
}
}
object_continue:
- switch (*advance<UNPADDED>()) {
+ switch (*advance<UNPADDED, BOUNDED>(batch_end)) {
case ',':
SIMDJSON_TRY( visitor.increment_count(*this) );
{
- auto key = advance<UNPADDED>();
+ auto key = advance<UNPADDED, BOUNDED>(batch_end);
if (simdjson_unlikely( *key != '"' )) { log_error("Key string missing at beginning of field in object"); return TAPE_ERROR; }
SIMDJSON_TRY( visitor.visit_key(*this, key) );
}
@@ -79822,16 +78497,16 @@ array_begin:
array_value:
{
- auto value = advance<UNPADDED>();
+ auto value = advance<UNPADDED, BOUNDED>(batch_end);
switch (*value) {
- case '{': if (*peek<UNPADDED>() == '}') { advance<UNPADDED>(); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin;
- case '[': if (*peek<UNPADDED>() == ']') { advance<UNPADDED>(); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin;
+ case '{': if (*peek<UNPADDED, BOUNDED>(batch_end) == '}') { advance<UNPADDED, BOUNDED>(batch_end); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin;
+ case '[': if (*peek<UNPADDED, BOUNDED>(batch_end) == ']') { advance<UNPADDED, BOUNDED>(batch_end); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin;
default: SIMDJSON_TRY( visitor.visit_primitive(*this, value) ); break;
}
}
array_continue:
- switch (*advance<UNPADDED>()) {
+ switch (*advance<UNPADDED, BOUNDED>(batch_end)) {
case ',': SIMDJSON_TRY( visitor.increment_count(*this) ); goto array_value;
case ']': log_end_value("array"); SIMDJSON_TRY( visitor.visit_array_end(*this) ); goto scope_end;
default: log_error("Missing comma between array values"); return TAPE_ERROR;
@@ -79861,8 +78536,18 @@ simdjson_inline json_iterator::json_iterator(dom_parser_implementation &_dom_par
// Stage 1 leaves a sentinel index of value `len`; reading buf[len] requires
// padding. For UNPADDED, return a dummy so walk_document can fail cleanly (#2815).
-template<bool UNPADDED>
-simdjson_inline const uint8_t *json_iterator::peek() const noexcept {
+//
+// When BOUNDED (see dom_parser_implementation::_bounded_batch), a document may
+// not run past batch_end, one past the last structural index of the batch:
+// past it, we return the same dummy, and walk_document fails cleanly.
+template<bool UNPADDED, bool BOUNDED>
+simdjson_inline const uint8_t *json_iterator::peek(const uint32_t *batch_end) const noexcept {
+ SIMDJSON_IF_CONSTEXPR (BOUNDED) {
+ if (simdjson_unlikely(next_structural >= batch_end)) {
+ static constexpr uint8_t streaming_eof_sentinel = 0;
+ return &streaming_eof_sentinel;
+ }
+ }
const uint32_t idx = *(next_structural);
SIMDJSON_IF_CONSTEXPR (UNPADDED) {
if (simdjson_unlikely(idx >= dom_parser.len)) {
@@ -79872,8 +78557,14 @@ simdjson_inline const uint8_t *json_iterator::peek() const noexcept {
}
return &buf[idx];
}
-template<bool UNPADDED>
-simdjson_inline const uint8_t *json_iterator::advance() noexcept {
+template<bool UNPADDED, bool BOUNDED>
+simdjson_inline const uint8_t *json_iterator::advance(const uint32_t *batch_end) noexcept {
+ SIMDJSON_IF_CONSTEXPR (BOUNDED) {
+ if (simdjson_unlikely(next_structural >= batch_end)) {
+ static constexpr uint8_t streaming_eof_sentinel = 0;
+ return &streaming_eof_sentinel;
+ }
+ }
const uint32_t idx = *(next_structural++);
SIMDJSON_IF_CONSTEXPR (UNPADDED) {
if (simdjson_unlikely(idx >= dom_parser.len)) {
@@ -80241,6 +78932,30 @@ struct tape_builder {
return iter.walk_document<STREAMING, false>(builder);
}
}
+
+ // The stage 2 entry points of dom::document_stream, which picks one per batch
+ // (see internal::dom_parser_implementation::stage2_next_function). Out of
+ // line, so that stage2_next() can share the code.
+ simdjson_warn_unused static simdjson_never_inline error_code parse_next_document(
+ internal::dom_parser_implementation &dom_parser, dom::document &doc) noexcept {
+ return parse_document<true>(static_cast<dom_parser_implementation &>(dom_parser), doc);
+ }
+
+ // A document of a stream batch that stage 2 must bound to its structural
+ // indexes (see internal::dom_parser_implementation::_bounded_batch).
+ simdjson_warn_unused static simdjson_never_inline error_code parse_next_bounded_document(
+ internal::dom_parser_implementation &_dom_parser, dom::document &doc) noexcept {
+ dom_parser_implementation &dom_parser = static_cast<dom_parser_implementation &>(_dom_parser);
+ dom_parser.doc = &doc;
+ json_iterator iter(dom_parser, dom_parser.next_structural_index);
+ if (dom_parser._unpadded) {
+ tape_builder_impl<true> builder(doc);
+ return iter.walk_document<true, true, true>(builder);
+ } else {
+ tape_builder_impl<false> builder(doc);
+ return iter.walk_document<true, false, true>(builder);
+ }
+ }
};
template <bool UNPADDED>
@@ -80804,8 +79519,14 @@ simdjson_warn_unused simdjson_inline error_code scan() {
if (simdjson_unlikely(parser.n_structural_indexes == 0)) { return CAPACITY; }
}
// We truncate the input to the end of the last complete document (or zero).
- auto new_structural_indexes = find_next_document_index(parser, !unclosed_string && ends_with_partial_scalar(parser, len));
+ // See json_structural_indexer.h for ends_with_unfinished_document().
+ const bool partial_scalar = !unclosed_string && ends_with_partial_scalar(parser, len);
+ const bool unfinished = ends_with_unfinished_document(parser);
+ auto new_structural_indexes = find_next_document_index(parser, partial_scalar || unfinished);
if (new_structural_indexes == 0 && parser.n_structural_indexes > 0) {
+ // The batch holds a single document, whose brackets balance but which
+ // cannot be valid JSON: it is not too big, it is invalid.
+ if (unfinished && find_next_document_index(parser, partial_scalar) != 0) { return TAPE_ERROR; }
if(parser.structural_indexes[0] == 0) {
// If the buffer is partial and we started at index 0 but the document is
// incomplete, it's too big to parse.
@@ -80863,6 +79584,9 @@ simdjson_warn_unused simdjson_inline error_code scan() {
}
parser.n_structural_indexes = new_structural_indexes;
parser.structural_indexes[parser.n_structural_indexes] = next_batch_start;
+ // Repeat the RS that starts the next batch, as in json_structural_indexer.h:
+ // an invalid last document may make the On-Demand API skip it as a value.
+ parser.structural_indexes[parser.n_structural_indexes + 1] = next_batch_start;
} else if (partial == stage1_mode::json_sequence_final) {
// RFC 7464: final batch, last document extends to EOF
// As above: a discarded unclosed string caps how far the filter may scan.
@@ -81066,7 +79790,11 @@ simdjson_warn_unused error_code dom_parser_implementation::stage2(dom::document
}
simdjson_warn_unused error_code dom_parser_implementation::stage2_next(dom::document &_doc) noexcept {
- return stage2::tape_builder::parse_document<true>(*this, _doc);
+ return stage2::tape_builder::parse_next_document(*this, _doc);
+}
+
+internal::dom_parser_implementation::stage2_next_function_t dom_parser_implementation::stage2_next_function(bool bounded) const noexcept {
+ return bounded ? stage2::tape_builder::parse_next_bounded_document : stage2::tape_builder::parse_next_document;
}
SIMDJSON_NO_SANITIZE_MEMORY
diff --git a/deps/simdjson/simdjson.h b/deps/simdjson/simdjson.h
index faf21bdc232..9d79c053f09 100644
--- a/deps/simdjson/simdjson.h
+++ b/deps/simdjson/simdjson.h
@@ -1,4 +1,4 @@
-/* auto-generated on 2026-10-14 15:49:28 -0400. version 5.0.5 Do not edit! */
+/* auto-generated on 2026-10-29 18:49:29 -0400. version 5.1.0 Do not edit! */
/* including simdjson.h: */
/* begin file simdjson.h */
#ifndef SIMDJSON_H
@@ -2715,7 +2715,7 @@ namespace std {
#define SIMDJSON_SIMDJSON_VERSION_H
/** The version of simdjson being used (major.minor.revision) */
-#define SIMDJSON_VERSION "5.0.5"
+#define SIMDJSON_VERSION "5.1.0"
namespace simdjson {
enum {
@@ -2726,11 +2726,11 @@ enum {
/**
* The minor version (major.MINOR.revision) of simdjson being used.
*/
- SIMDJSON_VERSION_MINOR = 0,
+ SIMDJSON_VERSION_MINOR = 1,
/**
* The revision (major.minor.REVISION) of simdjson being used.
*/
- SIMDJSON_VERSION_REVISION = 5
+ SIMDJSON_VERSION_REVISION = 0
};
} // namespace simdjson
@@ -4007,6 +4007,23 @@ public:
*/
simdjson_warn_unused virtual error_code stage2_next(dom::document &doc) noexcept = 0;
+ /** @private A stage 2 entry point for parser::parse_many: see stage2_next_function. */
+ typedef error_code (*stage2_next_function_t)(dom_parser_implementation &impl, dom::document &doc);
+
+ /**
+ * @private For internal implementation use
+ *
+ * Get a function that does what stage2_next() does. dom::document_stream picks
+ * it once per batch and calls it for each document of the batch, so that the
+ * choice between the bounded and the unchecked walk costs nothing per document.
+ *
+ * Overridden by each implementation.
+ *
+ * @param bounded Whether the documents must be bounded to the structural
+ * indexes of the batch (see _bounded_batch).
+ */
+ virtual stage2_next_function_t stage2_next_function(bool bounded) const noexcept = 0;
+
/**
* Unescape a valid UTF-8 string from src to dst, stopping at a final unescaped quote. There
* must be an unescaped quote terminating the string. It returns the final output
@@ -4131,6 +4148,14 @@ public:
*/
bool _unpadded{false};
+ /**
+ * Whether stage 2 must bound the documents of the batch indexed by the last
+ * stage 1 run to its structural indexes (dom::document_stream sets it, see
+ * document_stream::run_stage1, and passes it to stage2_next_function). Other
+ * batches keep the unchecked walk.
+ */
+ bool _bounded_batch{false};
+
protected:
// Declaring these so that subclasses can use them to implement their constructors.
@@ -4886,9 +4911,24 @@ public:
/** The number of allocated bytes. */
inline size_t capacity() const noexcept;
- /** check that the view has sufficient padding */
+ /**
+ * Check that the view has sufficient padding: either SIMDJSON_PADDING bytes of
+ * capacity past the end of the document, or enough trailing white space in
+ * the document to stand in for the missing bytes.
+ */
inline bool has_sufficient_padding() const noexcept;
+ /**
+ * The parser may read up to SIMDJSON_PADDING bytes past the end of the
+ * document. When the capacity falls short of that but the document ends
+ * with enough white space to make up for it (see has_sufficient_padding()),
+ * this moves that white space out of the document and into the padding, so
+ * that padding() >= SIMDJSON_PADDING. It does nothing otherwise.
+ *
+ * @return whether the document was shortened
+ */
+ inline bool use_trailing_whitespace_as_padding() noexcept;
+
/**
* Remove the UTF-8 Byte Order Mark (BOM) if it exists.
*
@@ -5090,6 +5130,14 @@ inline bool padded_string_view::has_sufficient_padding() const noexcept {
return true;
}
+inline bool padded_string_view::use_trailing_whitespace_as_padding() noexcept {
+ if (padding() >= SIMDJSON_PADDING || !has_sufficient_padding()) {
+ return false;
+ }
+ remove_suffix(SIMDJSON_PADDING - padding());
+ return true;
+}
+
inline size_t padded_string_view::capacity() const noexcept { return _capacity; }
inline size_t padded_string_view::padding() const noexcept { return capacity() - length(); }
@@ -6166,7 +6214,6 @@ public:
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
inline simdjson_result<dom::element> at_pointer(std::string_view json_pointer) const noexcept;
- inline void process_json_path_of_child_elements(std::vector<dom::element>::iterator& current, std::vector<dom::element>::iterator& end, const std::string_view& path_suffix, std::vector<dom::element>& accumulator) const noexcept;
inline simdjson_result<std::vector<dom::element>> at_path_with_wildcard(std::string_view json_path) const noexcept;
inline simdjson_result<dom::element> at_path(std::string_view json_path) const noexcept;
inline simdjson_result<dom::element> at(size_t index) const noexcept;
@@ -6845,7 +6892,8 @@ public:
inline simdjson_result<document_stream> parse_many(const uint8_t *buf, size_t len, size_t batch_size = dom::DEFAULT_BATCH_SIZE) noexcept;
/** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */
inline simdjson_result<document_stream> parse_many(const char *buf, size_t len, size_t batch_size = dom::DEFAULT_BATCH_SIZE) noexcept;
- /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */
+ /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size)
+ * Returns INSUFFICIENT_PADDING if s lacks SIMDJSON_PADDING bytes of spare capacity (or trailing whitespace). */
inline simdjson_result<document_stream> parse_many(const std::string &s, size_t batch_size = dom::DEFAULT_BATCH_SIZE) noexcept;
inline simdjson_result<document_stream> parse_many(const std::string &&s, size_t batch_size = dom::DEFAULT_BATCH_SIZE) = delete;// unsafe
/** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */
@@ -7346,6 +7394,9 @@ private:
/** Pass the next batch through stage 1 with the given parser. */
inline error_code run_stage1(dom::parser &p, size_t batch_start) noexcept;
+ /** Pick the stage 2 entry point for the batch that stage 1 just indexed. */
+ inline void load_stage2() noexcept;
+
dom::parser *parser;
const uint8_t *buf;
size_t len;
@@ -7356,6 +7407,8 @@ private:
error_code error;
size_t batch_start{0};
size_t doc_index{};
+ /** The stage 2 entry point for the documents of the current batch (see load_stage2). */
+ internal::dom_parser_implementation::stage2_next_function_t stage2_next_fn{nullptr};
#ifdef SIMDJSON_THREADS_ENABLED
/** Indicates whether we use threads. Note that this needs to be a constant during the execution of the parsing. */
bool use_thread;
@@ -8312,7 +8365,6 @@ public:
inline simdjson_result<dom::element> operator[](const char *key) const noexcept;
simdjson_result<dom::element> operator[](int) const noexcept = delete;
inline simdjson_result<dom::element> at_pointer(std::string_view json_pointer) const noexcept;
- inline void process_json_path_of_child_elements(std::vector<dom::element>::iterator& current, std::vector<dom::element>::iterator& end, const std::string_view& path_suffix, std::vector<dom::element>& accumulator) const noexcept;
inline simdjson_result<std::vector<dom::element>> at_path_with_wildcard(std::string_view json_path_new) const noexcept;
inline simdjson_result<dom::element> at_path(std::string_view json_path) const noexcept;
inline simdjson_result<dom::element> at_key(std::string_view key) const noexcept;
@@ -8532,6 +8584,11 @@ public:
simdjson_inline void append(simdjson::dom::key_value_pair value);
private:
+ /**
+ * Append an element nested base_depth levels deep: the caller has already
+ * written the indentation (and key) before it.
+ **/
+ inline void append(simdjson::dom::element value, size_t base_depth);
formatter format{};
};
@@ -8945,16 +9002,6 @@ simdjson_inline bool is_matching_failure_error(error_code error) noexcept {
return error == NO_SUCH_FIELD || error == INDEX_OUT_OF_BOUNDS ||
error == INCORRECT_TYPE || error == INVALID_JSON_POINTER;
}
-
-/**
- * Returns true if a wildcard follows in the remaining JSONPath. A segment that
- * matches nothing before a wildcard yields an empty result instead of an
- * error, as in DOM's at_path_with_wildcard. Like DOM, this treats a '*' inside
- * a quoted key as a wildcard.
- */
-simdjson_inline bool json_path_has_wildcard(std::string_view json_path) noexcept {
- return json_path.find('*') != std::string_view::npos;
-}
} // namespace internal
/**
@@ -9077,6 +9124,53 @@ inline std::pair<std::string_view, std::string_view> get_next_key_and_json_path(
}
namespace internal {
+/**
+ * One level of a JSONPath as the On-Demand for_each_at_path_with_wildcard()
+ * consumes it.
+ */
+struct json_path_segment {
+ // The key or array index at this level, "*" for a wildcard. Empty if the
+ // path could not be parsed at this level.
+ std::string_view key{};
+ // Whether a '*' occurs in the rest of the path, after this segment. A
+ // segment that matches nothing before a wildcard yields an empty result
+ // instead of an error, as in DOM's at_path_with_wildcard. Like DOM, this
+ // treats a '*' inside a quoted key as a wildcard.
+ bool wildcard_follows{false};
+};
+
+/**
+ * Splits json_path with get_next_key_and_json_path(), one segment per level,
+ * as the On-Demand for_each_at_path_with_wildcard() historically did at each
+ * node. The keys point into json_path, which must outlive the result.
+ *
+ * Every node visited at a given depth sees the same remaining path, so we
+ * split it once up front: re-scanning the remaining path at every node costs
+ * O(nodes * path length), which made a long path after a wildcard take
+ * minutes on a large document.
+ *
+ * The last segment either has an empty key (the path is invalid there) or
+ * is the end of the path.
+ */
+inline std::vector<json_path_segment> split_json_path(std::string_view json_path) {
+ std::vector<json_path_segment> segments;
+ // The remaining path (always a suffix of json_path) contains a '*' if and
+ // only if it starts at or before the last one.
+ const size_t last_star = json_path.rfind('*');
+ std::string_view path = json_path;
+ do {
+ std::pair<std::string_view, std::string_view> key_and_json_path = get_next_key_and_json_path(path);
+ path = key_and_json_path.second;
+ const size_t offset = json_path.size() - path.size();
+ json_path_segment segment;
+ segment.key = key_and_json_path.first;
+ segment.wildcard_follows = last_star != std::string_view::npos && last_star >= offset;
+ segments.push_back(segment);
+ if (segment.key.empty()) { break; }
+ } while (!path.empty());
+ return segments;
+}
+
/**
* A JSONPath as the DOM at_path_with_wildcard() consumes it, parsed once.
*
@@ -9102,6 +9196,22 @@ struct wildcard_json_path {
*/
inline wildcard_json_path parse_wildcard_json_path(std::string_view json_path) {
wildcard_json_path result;
+ // Each key is consumed by at_pointer, which unescapes ~0/~1, so '~' and
+ // '/' in the key must be escaped as json_path_to_pointer_conversion does:
+ // a raw '/' splits the step into two pointer tokens and a raw '~'
+ // invalidates it, either way addressing the wrong element.
+ const auto append_key = [](std::string &pointer, std::string_view key) {
+ pointer += '/';
+ for (const char c : key) {
+ if (c == '~') {
+ pointer += "~0";
+ } else if (c == '/') {
+ pointer += "~1";
+ } else {
+ pointer += c;
+ }
+ }
+ };
// The remaining path (always a suffix of json_path) contains a '*' if and
// only if it starts at or before the last one.
const size_t last_star = json_path.rfind('*');
@@ -9117,7 +9227,24 @@ inline wildcard_json_path parse_wildcard_json_path(std::string_view json_path) {
}
const size_t offset = json_path.size() - path.size();
if (last_star == std::string_view::npos || last_star < offset) {
- result.tail_pointer = json_path_to_pointer_conversion(path);
+ // Split the rest like the steps, so that a bracket-quoted key (['b'],
+ // ["b"]) is the key b, not 'b' with its quotes. What we do not split
+ // (e.g. [b], the key "b") is converted as at_path does.
+ std::string_view rest = path;
+ while (!rest.empty()) {
+ std::pair<std::string_view, std::string_view> key_and_json_path = get_next_key_and_json_path(rest);
+ if (key_and_json_path.first.empty()) {
+ const std::string pointer = json_path_to_pointer_conversion(rest);
+ if (pointer == "-1") {
+ result.tail_pointer = pointer;
+ } else {
+ result.tail_pointer += pointer;
+ }
+ break;
+ }
+ append_key(result.tail_pointer, key_and_json_path.first);
+ rest = key_and_json_path.second;
+ }
result.tail = (result.tail_pointer == "-1")
? wildcard_json_path::tail_type::invalid
: wildcard_json_path::tail_type::pointer;
@@ -9135,22 +9262,18 @@ inline wildcard_json_path parse_wildcard_json_path(std::string_view json_path) {
}
std::string step;
step.reserve(key_and_json_path.first.size() + 1);
- step += '/';
- // The step is consumed by at_pointer, which unescapes ~0/~1, so '~' and
- // '/' in the key must be escaped as json_path_to_pointer_conversion does:
- // a raw '/' splits the step into two pointer tokens and a raw '~'
- // invalidates it, either way addressing the wrong element.
- for (const char c : key_and_json_path.first) {
- if (c == '~') {
- step += "~0";
- } else if (c == '/') {
- step += "~1";
- } else {
- step += c;
- }
- }
+ append_key(step, key_and_json_path.first);
result.steps.push_back(std::move(step));
path = key_and_json_path.second;
+ // The segment above consumed the path. An empty remainder is the node
+ // that segment selected (at_pointer("")), not a parse error. The check
+ // at the top of the loop is for a non-empty remainder that does not
+ // continue with '.' or '['. A '*' inside the final segment ($.a.b*,
+ // $['b*']) belongs to that segment.
+ if (path.empty()) {
+ result.tail = wildcard_json_path::tail_type::pointer;
+ return result;
+ }
}
}
} // namespace internal
@@ -9441,6 +9564,7 @@ inline simdjson_result<dom::element> simdjson_result<dom::array>::at(size_t inde
}
inline std::vector<dom::element>& simdjson_result<dom::array>::get_values(std::vector<dom::element>& out) const noexcept {
+ if (error()) { return out; }
return first.get_values(out);
}
@@ -9690,6 +9814,7 @@ inline simdjson_result<dom::element> simdjson_result<dom::object>::at_key(std::s
return first.at_key(key);
}
inline std::vector<dom::element>& simdjson_result<dom::object>::get_values(std::vector<dom::element>& out) const noexcept {
+ if (error()) { return out; }
return first.get_values(out);
}
inline simdjson_result<dom::element> simdjson_result<dom::object>::at_key_case_insensitive(std::string_view key) const noexcept {
@@ -10176,16 +10301,15 @@ inline void get_container_values(const dom::element &e, std::vector<dom::element
// Walks a pre-parsed wildcard path from e, which is at depth `step` of the
// path. Errors below the starting node are skipped, not reported.
inline simdjson_result<std::vector<dom::element>> at_wildcard_json_path(const dom::element &e, const wildcard_json_path &path, size_t step) noexcept {
- const dom::element_type t = e.type();
- if (t != dom::element_type::ARRAY && t != dom::element_type::OBJECT) {
- return std::vector<dom::element>{};
- }
- std::vector<dom::element> children;
+ // A finished path names this node, even when it is a scalar. Checking the
+ // container type first would drop keys such as "b*" and "foo*".
if (step == path.steps.size()) {
switch (path.tail) {
- case wildcard_json_path::tail_type::all_values:
+ case wildcard_json_path::tail_type::all_values: {
+ std::vector<dom::element> children;
get_container_values(e, children);
return children;
+ }
case wildcard_json_path::tail_type::pointer: {
dom::element result;
auto error = e.at_pointer(path.tail_pointer).get(result);
@@ -10198,6 +10322,11 @@ inline simdjson_result<std::vector<dom::element>> at_wildcard_json_path(const do
return INVALID_JSON_POINTER;
}
}
+ const dom::element_type t = e.type();
+ if (t != dom::element_type::ARRAY && t != dom::element_type::OBJECT) {
+ return std::vector<dom::element>{};
+ }
+ std::vector<dom::element> children;
const std::string &key_pointer = path.steps[step];
if (key_pointer == "/*") {
get_container_values(e, children);
@@ -10477,10 +10606,16 @@ inline simdjson_result<element> element::at_pointer(std::string_view json_pointe
inline simdjson_result<std::vector<element>> element::at_path_with_wildcard(std::string_view json_path) const noexcept {
SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914
+ const internal::wildcard_json_path path = internal::parse_wildcard_json_path(json_path);
+ // A path that cannot be parsed matches nothing, whatever the document: say
+ // so rather than letting the walk skip it as a mismatch below a wildcard.
+ if (path.tail == internal::wildcard_json_path::tail_type::invalid) {
+ return INVALID_JSON_POINTER;
+ }
switch (tape.tape_ref_type()) {
case internal::tape_type::START_OBJECT:
case internal::tape_type::START_ARRAY:
- return internal::at_wildcard_json_path(*this, internal::parse_wildcard_json_path(json_path), 0);
+ return internal::at_wildcard_json_path(*this, path, 0);
default:
return std::vector<element>{};
}
@@ -10720,10 +10855,11 @@ inline simdjson_result<element> parser::parse_into_document(document& provided_d
inline simdjson_result<element> parser::parse_into_document_unpadded(document& provided_doc, const uint8_t *buf, size_t len) & noexcept {
// Like parse_into_document with realloc_if_needed=false (no copy, parse in
- // place), but we tell the implementation the buffer is not padded so stage 2
- // avoids reading past buf+len: string unescaping is bounded, near-the-end
+ // place), but we tell the implementation the buffer is not padded so neither
+ // stage reads past buf+len: stage 1 copies the last block instead of loading
+ // it in place, and in stage 2 string unescaping is bounded, near-the-end
// numbers are parsed from a padded copy, and atoms use length-aware
- // validators (see tape_builder). Stage 1 is already safe for unpadded input.
+ // validators (see tape_builder).
error_code _error = ensure_capacity(provided_doc, len);
if (_error) { return _error; }
@@ -10766,7 +10902,10 @@ simdjson_inline simdjson_result<element> parser::parse(const padded_string &s) &
return parse(s.data(), s.length(), false);
}
simdjson_inline simdjson_result<element> parser::parse(const padded_string_view &v) & noexcept {
- return parse(v.data(), v.length(), false);
+ if (!v.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
+ padded_string_view json(v);
+ json.use_trailing_whitespace_as_padding();
+ return parse(json.data(), json.length(), false);
}
inline simdjson_result<element> parser::parse_unpadded(const uint8_t *buf, size_t len) & noexcept {
@@ -10786,13 +10925,16 @@ inline simdjson_result<document_stream> parser::parse_many(const char *buf, size
return parse_many(reinterpret_cast<const uint8_t *>(buf), len, batch_size);
}
inline simdjson_result<document_stream> parser::parse_many(const std::string &s, size_t batch_size) noexcept {
- return parse_many(s.data(), s.length(), batch_size);
+ return parse_many(padded_string_view(s), batch_size);
}
inline simdjson_result<document_stream> parser::parse_many(const padded_string &s, size_t batch_size) noexcept {
return parse_many(s.data(), s.length(), batch_size);
}
inline simdjson_result<document_stream> parser::parse_many(const padded_string_view &v, size_t batch_size) noexcept {
- return parse_many(v.data(), v.length(), batch_size);
+ if (!v.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
+ padded_string_view json(v);
+ json.use_trailing_whitespace_as_padding();
+ return parse_many(json.data(), json.length(), batch_size);
}
inline simdjson_result<document_stream> parser::parse_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) noexcept {
@@ -10825,13 +10967,16 @@ inline simdjson_result<document_stream> parser::parse_many(const char *buf, size
return parse_many(reinterpret_cast<const uint8_t *>(buf), len, batch_size, format);
}
inline simdjson_result<document_stream> parser::parse_many(const std::string &s, size_t batch_size, stream_format format) noexcept {
- return parse_many(s.data(), s.length(), batch_size, format);
+ return parse_many(padded_string_view(s), batch_size, format);
}
inline simdjson_result<document_stream> parser::parse_many(const padded_string &s, size_t batch_size, stream_format format) noexcept {
return parse_many(s.data(), s.length(), batch_size, format);
}
inline simdjson_result<document_stream> parser::parse_many(const padded_string_view &v, size_t batch_size, stream_format format) noexcept {
- return parse_many(v.data(), v.length(), batch_size, format);
+ if (!v.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
+ padded_string_view json(v);
+ json.use_trailing_whitespace_as_padding();
+ return parse_many(json.data(), json.length(), batch_size, format);
}
simdjson_inline size_t parser::capacity() const noexcept {
@@ -11103,6 +11248,7 @@ inline void document_stream::start() noexcept {
error = run_stage1(*parser, batch_start);
}
if (error) { return; }
+ load_stage2();
#ifdef SIMDJSON_THREADS_ENABLED
if (use_thread && next_batch_start() < len) {
// Kick off the first thread if needed
@@ -11179,7 +11325,7 @@ inline void document_stream::next() noexcept {
// Load the next document from the batch
doc_index = batch_start + parser->implementation->structural_indexes[parser->implementation->next_structural_index];
- error = parser->implementation->stage2_next(parser->doc);
+ error = stage2_next_fn(*parser->implementation, parser->doc);
// If that was the last document in the batch, load another batch (if available)
while (error == EMPTY) {
batch_start = next_batch_start();
@@ -11195,9 +11341,10 @@ inline void document_stream::next() noexcept {
error = run_stage1(*parser, batch_start);
#endif
if (error) { continue; } // If the error was EMPTY, we may want to load another batch.
+ load_stage2();
// Run stage 2 on the first document in the batch
doc_index = batch_start + parser->implementation->structural_indexes[parser->implementation->next_structural_index];
- error = parser->implementation->stage2_next(parser->doc);
+ error = stage2_next_fn(*parser->implementation, parser->doc);
}
}
inline size_t document_stream::size_in_bytes() const noexcept {
@@ -11216,9 +11363,40 @@ inline size_t document_stream::next_batch_start() const noexcept {
return batch_start + parser->implementation->structural_indexes[parser->implementation->n_structural_indexes];
}
+// Stage 1 cuts the batch after the last document that looks complete, and
+// leaves a sentinel structural index past it: stage 2 validates the documents,
+// and stops an invalid one at the sentinel because the byte there cannot
+// continue a document. That byte is not part of the batch, though: it is the
+// start of the next document, padding (which the caller need not initialize)
+// or the `]` that comma_delimited_array strips. When it is ',', ':', ']' or
+// '}', or when the batch ends where a value is expected, an invalid document
+// such as `[2` (before a truncated `[["`) could be accepted and run past the
+// batch, into stale indexes such as the quote of an unclosed string, and so
+// past the buffer. Stage 2 then walks this batch with an explicit bound (see
+// load_stage2). Other batches keep the unchecked walk.
+//
+// set_bounded_batch() makes that decision for the batch stage 1 just indexed.
+// Out of line: it runs once per batch, and inlined it would weigh on next().
+simdjson_never_inline void set_bounded_batch(internal::dom_parser_implementation &impl, const uint8_t *batch) noexcept {
+ bool bounded = false;
+ const uint32_t n = impl.n_structural_indexes;
+ if (n > 0) {
+ const uint8_t next = batch[impl.structural_indexes[n]];
+ const uint8_t last = batch[impl.structural_indexes[n - 1]];
+ bounded = next == ',' || next == ':' || next == ']' || next == '}' ||
+ last == ',' || last == ':' || last == '[' || last == '{';
+ }
+ impl._bounded_batch = bounded;
+}
+
+inline void document_stream::load_stage2() noexcept {
+ stage2_next_fn = parser->implementation->stage2_next_function(parser->implementation->_bounded_batch);
+}
+
inline error_code document_stream::run_stage1(dom::parser &p, size_t _batch_start) noexcept {
size_t remaining = len - _batch_start;
stage1_mode mode;
+ size_t batch_len;
if (remaining <= batch_size) {
// Final batch
switch (format) {
@@ -11232,7 +11410,7 @@ inline error_code document_stream::run_stage1(dom::parser &p, size_t _batch_star
mode = stage1_mode::streaming_final;
break;
}
- return p.implementation->stage1(&buf[_batch_start], remaining, mode);
+ batch_len = remaining;
} else {
// Partial batch
switch (format) {
@@ -11246,8 +11424,11 @@ inline error_code document_stream::run_stage1(dom::parser &p, size_t _batch_star
mode = stage1_mode::streaming_partial;
break;
}
- return p.implementation->stage1(&buf[_batch_start], batch_size, mode);
+ batch_len = batch_size;
}
+ error_code err = p.implementation->stage1(&buf[_batch_start], batch_len, mode);
+ if (!err) { set_bounded_batch(*p.implementation, &buf[_batch_start]); }
+ return err;
}
#ifdef SIMDJSON_THREADS_ENABLED
@@ -11258,7 +11439,12 @@ inline void document_stream::load_from_stage1_thread() noexcept {
// enough memory to swap to, as well.
std::swap(*parser, stage1_thread_parser);
error = stage1_thread_error;
- if (error) { return; }
+ // An EMPTY batch (no complete document in it, e.g. a run of white space at
+ // least batch_size long) is not the end of the stream: next() moves on to
+ // the batch after it, so queue that batch now as we would after a success.
+ // Returning here would leave no job for the next call, which then swaps
+ // the stale previous batch back in and ends the stream early.
+ if (error && error != EMPTY) { return; }
// If there's anything left, start the stage 1 thread!
if (next_batch_start() < len) {
@@ -11575,6 +11761,135 @@ inline bool document::dump_raw_tape(std::ostream &os) const noexcept {
/* skipped duplicate #include "simdjson/dom/base.h" */
/* skipped duplicate #include "simdjson/dom/parser.h" */
/* skipped duplicate #include "simdjson/dom/serialization.h" */
+/* including simdjson/internal/jeaiii.h: #include "simdjson/internal/jeaiii.h" */
+/* begin file simdjson/internal/jeaiii.h */
+#ifndef SIMDJSON_INTERNAL_JEAIII_H
+#define SIMDJSON_INTERNAL_JEAIII_H
+
+/* skipped duplicate #include "simdjson/base.h" */
+#include <cstdint>
+#include <cstring>
+
+namespace simdjson {
+namespace internal {
+
+// Integer to decimal: James Edward Anhalt III's algorithm
+static const char jeaiii_dd[201] =
+ "00010203040506070809101112131415161718192021222324252627282930313233343536373839"
+ "40414243444546474849505152535455565758596061626364656667686970717273747576777879"
+ "8081828384858687888990919293949596979899";
+static const char jeaiii_fd[201] =
+ "0\0" "1\0" "2\0" "3\0" "4\0" "5\0" "6\0" "7\0" "8\0" "9\0"
+ "10111213141516171819202122232425262728293031323334353637383940414243444546474849"
+ "50515253545556575859606162636465666768697071727374757677787980818283848586878889"
+ "90919293949596979899";
+
+simdjson_really_inline void jeaiii_write_dd(char *p, uint64_t k) noexcept {
+ std::memcpy(p, &jeaiii_dd[2 * k], 2);
+}
+simdjson_really_inline void jeaiii_write_fd(char *p, uint64_t k) noexcept {
+ std::memcpy(p, &jeaiii_fd[2 * k], 2);
+}
+
+// Caller guarantees n < 10^8. Writes 1 to 8 digits.
+simdjson_really_inline char *jeaiii_lt1e8(char *b, uint32_t n) noexcept {
+ constexpr uint64_t mask24 = (uint64_t(1) << 24) - 1;
+ constexpr uint64_t mask32 = (uint64_t(1) << 32) - 1;
+ if (n < 100) {
+ jeaiii_write_fd(b, n);
+ return n < 10 ? b + 1 : b + 2;
+ }
+ if (n < 1000000) {
+ if (n < 10000) {
+ const uint32_t f0 = uint32_t(10 * (1 << 24) / 1e3 + 1) * n;
+ jeaiii_write_fd(b, f0 >> 24);
+ b -= n < 1000;
+ const uint32_t f2 = uint32_t(f0 & mask24) * 100;
+ jeaiii_write_dd(b + 2, f2 >> 24);
+ return b + 4;
+ }
+ const uint64_t f0 = uint64_t(10 * (1ull << 32) / 1e5 + 1) * n;
+ jeaiii_write_fd(b, f0 >> 32);
+ b -= n < 100000;
+ const uint64_t f2 = (f0 & mask32) * 100;
+ jeaiii_write_dd(b + 2, f2 >> 32);
+ const uint64_t f4 = (f2 & mask32) * 100;
+ jeaiii_write_dd(b + 4, f4 >> 32);
+ return b + 6;
+ }
+ const uint64_t f0 = uint64_t(10 * (1ull << 48) / 1e7 + 1) * n >> 16;
+ jeaiii_write_fd(b, f0 >> 32);
+ b -= n < 10000000;
+ const uint64_t f2 = (f0 & mask32) * 100;
+ jeaiii_write_dd(b + 2, f2 >> 32);
+ const uint64_t f4 = (f2 & mask32) * 100;
+ jeaiii_write_dd(b + 4, f4 >> 32);
+ const uint64_t f6 = (f4 & mask32) * 100;
+ jeaiii_write_dd(b + 6, f6 >> 32);
+ return b + 8;
+}
+
+// Caller guarantees z < 10^8. Always writes exactly 8 digits.
+simdjson_really_inline char *jeaiii_8_digits(char *b, uint32_t z) noexcept {
+ constexpr uint64_t mask32 = (uint64_t(1) << 32) - 1;
+ const uint64_t f0 = (uint64_t((1ull << 48) / 1e6 + 1) * z >> 16) + 1;
+ jeaiii_write_dd(b, f0 >> 32);
+ const uint64_t f2 = (f0 & mask32) * 100;
+ jeaiii_write_dd(b + 2, f2 >> 32);
+ const uint64_t f4 = (f2 & mask32) * 100;
+ jeaiii_write_dd(b + 4, f4 >> 32);
+ const uint64_t f6 = (f4 & mask32) * 100;
+ jeaiii_write_dd(b + 6, f6 >> 32);
+ return b + 8;
+}
+
+// Caller guarantees 10^8 <= n < 2^32. Writes 9 or 10 digits.
+simdjson_really_inline char *jeaiii_9_or_10(char *b, uint64_t n) noexcept {
+ constexpr uint64_t mask57 = (uint64_t(1) << 57) - 1;
+ const uint64_t f0 = uint64_t(10 * (1ull << 57) / 1e9 + 1) * n;
+ jeaiii_write_fd(b, f0 >> 57);
+ b -= n < 1000000000;
+ const uint64_t f2 = (f0 & mask57) * 100;
+ jeaiii_write_dd(b + 2, f2 >> 57);
+ const uint64_t f4 = (f2 & mask57) * 100;
+ jeaiii_write_dd(b + 4, f4 >> 57);
+ const uint64_t f6 = (f4 & mask57) * 100;
+ jeaiii_write_dd(b + 6, f6 >> 57);
+ const uint64_t f8 = (f6 & mask57) * 100;
+ jeaiii_write_dd(b + 8, f8 >> 57);
+ return b + 10;
+}
+
+simdjson_really_inline char *write_uint_jeaiii(char *b, uint64_t n) noexcept {
+ if (n < 100000000) {
+ return jeaiii_lt1e8(b, uint32_t(n));
+ }
+ if (n < (uint64_t(1) << 32)) {
+ return jeaiii_9_or_10(b, n);
+ }
+ // At least 10 digits: the low 8 digits, and 2 to 12 digits above them.
+ const uint32_t z = uint32_t(n % 100000000);
+ uint64_t u = n / 100000000;
+ if (u < 100000000) {
+ // u has 2 to 8 digits (if u < 10, n would be below 2^32).
+ b = jeaiii_lt1e8(b, uint32_t(u));
+ } else if (u < (uint64_t(1) << 32)) {
+ b = jeaiii_9_or_10(b, u);
+ } else {
+ // u has 11 or 12 digits: split off 8 more.
+ const uint32_t y = uint32_t(u % 100000000);
+ u /= 100000000;
+ b = jeaiii_lt1e8(b, uint32_t(u)); // 3 or 4 digits
+ b = jeaiii_8_digits(b, y);
+ }
+ return jeaiii_8_digits(b, z);
+}
+
+} // namespace internal
+} // namespace simdjson
+
+#endif // SIMDJSON_INTERNAL_JEAIII_H
+/* end file simdjson/internal/jeaiii.h */
/* skipped duplicate #include "simdjson/internal/tape_type.h" */
/* skipped duplicate #include "simdjson/dom/array-inl.h" */
@@ -11660,75 +11975,6 @@ struct escape_sequence {
const char
string[7]; // technically, we only ever need 6 characters, we pad to 8
};
-/**@private
- * This converts a signed integer into a character sequence.
- * The caller is responsible for providing enough memory (at least
- * 20 characters.)
- * Though various runtime libraries provide itoa functions,
- * it is not part of the C++ standard. The C++17 standard
- * adds the to_chars functions which would do as well, but
- * we want to support C++11.
- */
-static char *fast_itoa(char *output, int64_t value) noexcept {
- // This is a standard implementation of itoa.
- char buffer[20];
- uint64_t value_positive;
- // In general, negating a signed integer is unsafe.
- if (value < 0) {
- *output++ = '-';
- // Doing value_positive = -value; while avoiding
- // undefined behavior warnings.
- // It assumes two complement's which is universal at this
- // point in time.
- std::memcpy(&value_positive, &value, sizeof(value));
- value_positive = (~value_positive) + 1; // this is a negation
- } else {
- value_positive = value;
- }
- // We work solely with value_positive. It *might* be easier
- // for an optimizing compiler to deal with an unsigned variable
- // as far as performance goes.
- const char *const end_buffer = buffer + 20;
- char *write_pointer = buffer + 19;
- // A faster approach is possible if we expect large integers:
- // unroll the loop (work in 100s, 1000s) and use some kind of
- // memoization.
- while (value_positive >= 10) {
- *write_pointer-- = char('0' + (value_positive % 10));
- value_positive /= 10;
- }
- *write_pointer = char('0' + value_positive);
- size_t len = end_buffer - write_pointer;
- std::memcpy(output, write_pointer, len);
- return output + len;
-}
-/**@private
- * This converts an unsigned integer into a character sequence.
- * The caller is responsible for providing enough memory (at least
- * 19 characters.)
- * Though various runtime libraries provide itoa functions,
- * it is not part of the C++ standard. The C++17 standard
- * adds the to_chars functions which would do as well, but
- * we want to support C++11.
- */
-static char *fast_itoa(char *output, uint64_t value) noexcept {
- // This is a standard implementation of itoa.
- char buffer[20];
- const char *const end_buffer = buffer + 20;
- char *write_pointer = buffer + 19;
- // A faster approach is possible if we expect large integers:
- // unroll the loop (work in 100s, 1000s) and use some kind of
- // memoization.
- while (value >= 10) {
- *write_pointer-- = char('0' + (value % 10));
- value /= 10;
- };
- *write_pointer = char('0' + value);
- size_t len = end_buffer - write_pointer;
- std::memcpy(output, write_pointer, len);
- return output + len;
-}
-
} // anonymous namespace
namespace internal {
@@ -11738,16 +11984,18 @@ namespace internal {
template <class formatter>
simdjson_inline void base_formatter<formatter>::number(uint64_t x) {
+ // write_uint_jeaiii may store one byte before its output pointer.
char number_buffer[24];
- char *newp = fast_itoa(number_buffer, x);
- chars(number_buffer, newp);
+ chars(number_buffer + 1, write_uint_jeaiii(number_buffer + 1, x));
}
template <class formatter>
simdjson_inline void base_formatter<formatter>::number(int64_t x) {
char number_buffer[24];
- char *newp = fast_itoa(number_buffer, x);
- chars(number_buffer, newp);
+ // 0 - uint64_t(x) is the magnitude, also for INT64_MIN.
+ char *end = write_uint_jeaiii(number_buffer + 1, x < 0 ? 0 - uint64_t(x) : uint64_t(x));
+ number_buffer[0] = '-';
+ chars(number_buffer + (x >= 0), end);
}
template <class formatter>
@@ -11963,6 +12211,12 @@ simdjson_inline void pretty_formatter::print_space() { one_char(' '); }
template <class serializer>
inline void string_builder<serializer>::append(simdjson::dom::element value) {
+ append(value, 0);
+ format.print_newline();
+}
+
+template <class serializer>
+inline void string_builder<serializer>::append(simdjson::dom::element value, size_t base_depth) {
// using tape_type = simdjson::internal::tape_type;
size_t depth = 0;
constexpr size_t MAX_DEPTH = 16;
@@ -11978,7 +12232,10 @@ inline void string_builder<serializer>::append(simdjson::dom::element value) {
format.print_newline();
}
- format.print_indents(depth);
+ // At depth 0 the caller has already indented the value.
+ if (depth != 0) {
+ format.print_indents(base_depth + depth);
+ }
// If we are in an object, print the next key and :, and skip to the next
// value.
@@ -11994,7 +12251,7 @@ inline void string_builder<serializer>::append(simdjson::dom::element value) {
// If we're too deep, we need to recurse to go deeper.
depth++;
if (simdjson_unlikely(depth >= MAX_DEPTH)) {
- append(simdjson::dom::array(iter));
+ append(simdjson::dom::element(iter), base_depth + depth - 1);
iter.json_index = iter.matching_brace_index() - 1; // Jump to the ]
depth--;
break;
@@ -12022,7 +12279,7 @@ inline void string_builder<serializer>::append(simdjson::dom::element value) {
// If we're too deep, we need to recurse to go deeper.
depth++;
if (simdjson_unlikely(depth >= MAX_DEPTH)) {
- append(simdjson::dom::object(iter));
+ append(simdjson::dom::element(iter), base_depth + depth - 1);
iter.json_index = iter.matching_brace_index() - 1; // Jump to the }
depth--;
break;
@@ -12094,7 +12351,7 @@ inline void string_builder<serializer>::append(simdjson::dom::element value) {
iter.tape_ref_type() == tape_type::END_OBJECT)) {
format.print_newline();
depth--;
- format.print_indents(depth);
+ format.print_indents(base_depth + depth);
if (iter.tape_ref_type() == tape_type::END_ARRAY) {
format.end_array();
} else {
@@ -12105,38 +12362,16 @@ inline void string_builder<serializer>::append(simdjson::dom::element value) {
// Stop when we're at depth 0
} while (depth != 0);
-
- format.print_newline();
}
template <class serializer>
inline void string_builder<serializer>::append(simdjson::dom::object value) {
- format.start_object();
- auto pair = value.begin();
- auto end = value.end();
- if (pair != end) {
- append(*pair);
- for (++pair; pair != end; ++pair) {
- format.comma();
- append(*pair);
- }
- }
- format.end_object();
+ append(simdjson::dom::element(value));
}
template <class serializer>
inline void string_builder<serializer>::append(simdjson::dom::array value) {
- format.start_array();
- auto iter = value.begin();
- auto end = value.end();
- if (iter != end) {
- append(*iter);
- for (++iter; iter != end; ++iter) {
- format.comma();
- append(*iter);
- }
- }
- format.end_array();
+ append(simdjson::dom::element(value));
}
template <class serializer>
@@ -12348,11 +12583,17 @@ public:
const fractured_json_options& opts);
/** Decide layout at the given render depth. Kept out of analysis since
- * the same metrics can render inline or expanded at different depths. */
+ * the same metrics can render inline or expanded at different depths.
+ * key_width is the width of the key, colon and colon padding written
+ * before the value on its first line (0 if the value has no key). */
static layout_mode decide_layout(const element_metrics& metrics,
size_t depth,
const fractured_json_options& opts,
- bool has_trailing_comma = false);
+ bool has_trailing_comma = false,
+ size_t key_width = 0);
+
+ /** Estimate inline length for a string (including quotes and escaping) */
+ size_t estimate_string_length(std::string_view s) const;
private:
const fractured_json_options* current_opts_ = nullptr;
@@ -12369,9 +12610,6 @@ private:
/** Analyze an object element */
element_metrics analyze_object(const dom::object& obj, size_t depth) const;
- /** Estimate inline length for a string (including quotes and escaping) */
- size_t estimate_string_length(std::string_view s) const;
-
/** Estimate inline length for a number */
size_t estimate_number_length(double d) const;
size_t estimate_number_length(int64_t i) const;
@@ -12516,11 +12754,14 @@ private:
/** Format an element using pre-computed metrics */
void format_element(const dom::element& elem, const element_metrics& metrics, size_t depth,
- bool has_trailing_comma = false);
+ bool has_trailing_comma = false, size_t key_width = 0);
/** Format an array with the appropriate layout */
void format_array(const dom::array& arr, const element_metrics& metrics, size_t depth,
- bool has_trailing_comma = false);
+ bool has_trailing_comma = false, size_t key_width = 0);
+
+ /** Format an element on a single line, whatever its depth */
+ void format_element_inline(const dom::element& elem, const element_metrics& metrics);
/** Format an array inline: [1, 2, 3] */
void format_array_inline(const dom::array& arr, const element_metrics& metrics);
@@ -12572,7 +12813,7 @@ private:
/** Format an object with the appropriate layout */
void format_object(const dom::object& obj, const element_metrics& metrics, size_t depth,
- bool has_trailing_comma = false);
+ bool has_trailing_comma = false, size_t key_width = 0);
/** Format an object inline: {"a": 1, "b": 2} */
void format_object_inline(const dom::object& obj, const element_metrics& metrics);
@@ -12686,6 +12927,13 @@ inline element_metrics structure_analyzer::analyze_scalar(const dom::element& el
}
break;
}
+ case dom::element_type::BIGINT: {
+ std::string_view digits;
+ if (elem.get_bigint().get(digits) == SUCCESS) {
+ metrics.estimated_inline_len = digits.size();
+ }
+ break;
+ }
case dom::element_type::BOOL: {
bool val;
if (elem.get_bool().get(val) == SUCCESS) {
@@ -12828,10 +13076,14 @@ inline size_t structure_analyzer::estimate_number_length(double d) const {
return 4; // "null" for invalid numbers
#endif
}
- // Rough estimate: up to 17 significant digits + sign + decimal point + exponent
- char buf[32];
- int len = snprintf(buf, sizeof(buf), "%.17g", d);
- return len > 0 ? static_cast<size_t>(len) : 20;
+ // Must match base_formatter::number(double), which serializes finite values
+ // with internal::to_chars (shortest round-trip). snprintf("%.17g") over-counts
+ // (e.g. 0.1 becomes "0.10000000000000001", 19 chars, vs the emitted "0.1"),
+ // which misaligns double columns in table formatting the same way escaped
+ // control characters did before #2929.
+ char buf[simdjson::internal::to_chars_buffer_size];
+ char *end = simdjson::internal::to_chars(buf, buf + sizeof(buf), d);
+ return static_cast<size_t>(end - buf);
}
inline size_t structure_analyzer::estimate_number_length(int64_t i) const {
@@ -12863,7 +13115,8 @@ inline table_column_type structure_analyzer::classify_table_value(dom::element_t
case dom::element_type::ARRAY: return table_column_type::array;
case dom::element_type::INT64:
case dom::element_type::UINT64:
- case dom::element_type::DOUBLE: return table_column_type::number;
+ case dom::element_type::DOUBLE:
+ case dom::element_type::BIGINT: return table_column_type::number;
case dom::element_type::NULL_VALUE: return table_column_type::unknown;
default: return table_column_type::simple; // string, bool
}
@@ -13089,7 +13342,8 @@ inline void structure_analyzer::recompute_column_widths(std::vector<table_column
inline layout_mode structure_analyzer::decide_layout(const element_metrics& metrics,
size_t depth,
const fractured_json_options& opts,
- bool has_trailing_comma) {
+ bool has_trailing_comma,
+ size_t key_width) {
if (metrics.child_count == 0) {
return layout_mode::single_line;
}
@@ -13099,7 +13353,8 @@ inline layout_mode structure_analyzer::decide_layout(const element_metrics& metr
bool depth_allows_table = signed_depth >= opts.always_expand_depth;
// Check inline feasibility
- size_t reserved_width = depth * opts.indent_spaces + (has_trailing_comma ? 1 : 0);
+ size_t reserved_width = depth * opts.indent_spaces + key_width +
+ (has_trailing_comma ? 1 : 0);
if (depth_allows_inline_or_compact && metrics.can_inline &&
metrics.estimated_inline_len + reserved_width <= opts.max_total_line_length) {
return layout_mode::single_line;
@@ -13234,19 +13489,20 @@ simdjson_inline std::string_view fractured_string_builder::str() const {
inline void fractured_string_builder::format_element(const dom::element& elem,
const element_metrics& metrics,
size_t depth,
- bool has_trailing_comma) {
+ bool has_trailing_comma,
+ size_t key_width) {
switch (elem.type()) {
case dom::element_type::ARRAY: {
dom::array arr;
if (elem.get_array().get(arr) == SUCCESS) {
- format_array(arr, metrics, depth, has_trailing_comma);
+ format_array(arr, metrics, depth, has_trailing_comma, key_width);
}
break;
}
case dom::element_type::OBJECT: {
dom::object obj;
if (elem.get_object().get(obj) == SUCCESS) {
- format_object(obj, metrics, depth, has_trailing_comma);
+ format_object(obj, metrics, depth, has_trailing_comma, key_width);
}
break;
}
@@ -13259,8 +13515,10 @@ inline void fractured_string_builder::format_element(const dom::element& elem,
inline void fractured_string_builder::format_array(const dom::array& arr,
const element_metrics& metrics,
size_t depth,
- bool has_trailing_comma) {
- layout_mode layout = structure_analyzer::decide_layout(metrics, depth, options_, has_trailing_comma);
+ bool has_trailing_comma,
+ size_t key_width) {
+ layout_mode layout = structure_analyzer::decide_layout(metrics, depth, options_,
+ has_trailing_comma, key_width);
switch (layout) {
case layout_mode::single_line:
format_array_inline(arr, metrics);
@@ -13278,6 +13536,33 @@ inline void fractured_string_builder::format_array(const dom::array& arr,
}
}
+// The children of an inline value are written inline as well. Going through
+// format_element() would let decide_layout() pick another layout for them
+// (e.g. at a depth not past always_expand_depth), which a single line cannot
+// show: it would only add table padding and drop the comma padding.
+inline void fractured_string_builder::format_element_inline(const dom::element& elem,
+ const element_metrics& metrics) {
+ switch (elem.type()) {
+ case dom::element_type::ARRAY: {
+ dom::array arr;
+ if (elem.get_array().get(arr) == SUCCESS) {
+ format_array_inline(arr, metrics);
+ }
+ break;
+ }
+ case dom::element_type::OBJECT: {
+ dom::object obj;
+ if (elem.get_object().get(obj) == SUCCESS) {
+ format_object_inline(obj, metrics);
+ }
+ break;
+ }
+ default:
+ format_scalar(elem);
+ break;
+ }
+}
+
inline void fractured_string_builder::format_array_inline(const dom::array& arr,
const element_metrics& metrics) {
scoped_single_line_mode single_line(format_);
@@ -13299,7 +13584,7 @@ inline void fractured_string_builder::format_array_inline(const dom::array& arr,
}
first = false;
const element_metrics& child_metrics = child_metrics_at(metrics.children, child_idx);
- format_element(elem, child_metrics, 0);
+ format_element_inline(elem, child_metrics);
child_idx++;
}
@@ -13327,14 +13612,19 @@ inline void fractured_string_builder::format_array_compact_multiline(const dom::
for (dom::element elem : arr) {
const element_metrics& child_metrics = child_metrics_at(metrics.children, child_idx);
+ bool is_last = (child_idx + 1 == metrics.child_count);
if (!first) {
format_.comma();
format_.track_line_length(1);
- // Check if we should break to new line
+ // Check if we should break to new line. Staying on this line also
+ // takes the padding space before the item and the comma after it.
+ size_t upcoming_length = child_metrics.estimated_inline_len +
+ (options_.comma_padding ? 1 : 0) +
+ (is_last ? 0 : 1);
if (prev_item_was_expanded ||
- format_.should_break_line(child_metrics.estimated_inline_len)) {
+ format_.should_break_line(upcoming_length)) {
format_.print_newline();
format_.print_indents(depth + 1);
} else if (options_.comma_padding) {
@@ -13343,7 +13633,6 @@ inline void fractured_string_builder::format_array_compact_multiline(const dom::
}
first = false;
- bool is_last = (child_idx + 1 == metrics.child_count);
layout_mode item_layout = structure_analyzer::decide_layout(child_metrics, depth + 1, options_, !is_last);
bool item_fits = item_layout == layout_mode::single_line;
if (item_fits) {
@@ -13710,8 +13999,10 @@ inline void fractured_string_builder::format_array_expanded(const dom::array& ar
inline void fractured_string_builder::format_object(const dom::object& obj,
const element_metrics& metrics,
size_t depth,
- bool has_trailing_comma) {
- layout_mode layout = structure_analyzer::decide_layout(metrics, depth, options_, has_trailing_comma);
+ bool has_trailing_comma,
+ size_t key_width) {
+ layout_mode layout = structure_analyzer::decide_layout(metrics, depth, options_,
+ has_trailing_comma, key_width);
if (layout == layout_mode::single_line) {
format_object_inline(obj, metrics);
} else {
@@ -13746,7 +14037,7 @@ inline void fractured_string_builder::format_object_inline(const dom::object& ob
format_.print_space();
}
const element_metrics& child_metrics = child_metrics_at(metrics.children, child_idx);
- format_element(field.value, child_metrics, 0);
+ format_element_inline(field.value, child_metrics);
child_idx++;
}
@@ -13780,7 +14071,10 @@ inline void fractured_string_builder::format_object_expanded(const dom::object&
}
const element_metrics& child_metrics = child_metrics_at(metrics.children, child_idx);
bool is_last = (child_idx + 1 == metrics.child_count);
- format_element(field.value, child_metrics, depth + 1, !is_last);
+ // An inline value shares its line with the key, the colon and its padding.
+ size_t key_width = analyzer_.estimate_string_length(field.key) + 1 +
+ (options_.colon_padding ? 1 : 0);
+ format_element(field.value, child_metrics, depth + 1, !is_last, key_width);
child_idx++;
}
@@ -13821,6 +14115,14 @@ inline void fractured_string_builder::format_scalar(const dom::element& elem) {
}
break;
}
+ case dom::element_type::BIGINT: {
+ // Big integer stored as string: output the raw digits (no quotes)
+ std::string_view digits;
+ if (elem.get_bigint().get(digits) == SUCCESS) {
+ format_.chars(digits.data(), digits.data() + digits.size());
+ }
+ break;
+ }
case dom::element_type::BOOL: {
bool val;
if (elem.get_bool().get(val) == SUCCESS) {
@@ -16243,31 +16545,6 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin
};
}
-struct escaping {
- static constexpr uint32_t BYTES_PROCESSED = 16;
- simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
-
- simdjson_inline bool has_escape() { return escape_bits != 0; }
- simdjson_inline int escape_index() { return trailing_zeroes(escape_bits) / 4; }
-
- uint64_t escape_bits;
-}; // struct escaping
-
-
-
-simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
- static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes");
- simd8<uint8_t> v(src);
- v.store(dst);
- simd8<bool> is_quote = (v == '"');
- simd8<bool> is_backslash = (v == '\\');
- simd8<bool> is_control = (v < 32);
- return {
- (is_backslash | is_quote | is_control).to_bitmask64()
- };
-}
-
-
} // unnamed namespace
} // namespace arm64
@@ -16661,6 +16938,7 @@ public:
simdjson_warn_unused error_code stage1(const uint8_t *buf, size_t len, stage1_mode partial) noexcept final;
simdjson_warn_unused error_code stage2(dom::document &doc) noexcept final;
simdjson_warn_unused error_code stage2_next(dom::document &doc) noexcept final;
+ stage2_next_function_t stage2_next_function(bool bounded) const noexcept final;
simdjson_warn_unused uint8_t *parse_string(const uint8_t *src, uint8_t *dst, bool allow_replacement) const noexcept final;
simdjson_warn_unused uint8_t *parse_wobbly_string(const uint8_t *src, uint8_t *dst) const noexcept final;
inline simdjson_warn_unused error_code set_capacity(size_t capacity) noexcept final;
@@ -17422,21 +17700,6 @@ static bool parse_float_fallback(const uint8_t *ptr, double *outDouble) {
return !(*outDouble > (std::numeric_limits<double>::max)() || *outDouble < std::numeric_limits<double>::lowest());
}
-static bool parse_float_fallback(const uint8_t *ptr, const uint8_t *end_ptr, double *outDouble) {
- *outDouble = simdjson::internal::from_chars(reinterpret_cast<const char *>(ptr), reinterpret_cast<const char *>(end_ptr));
- // We do not accept infinite values.
-
- // Detecting finite values in a portable manner is ridiculously hard, ideally
- // we would want to do:
- // return !std::isfinite(*outDouble);
- // but that mysteriously fails under legacy/old libc++ libraries, see
- // https://github.com/simdjson/simdjson/issues/1286
- //
- // Therefore, fall back to this solution (the extra parens are there
- // to handle that max may be a macro on windows).
- return !(*outDouble > (std::numeric_limits<double>::max)() || *outDouble < std::numeric_limits<double>::lowest());
-}
-
// Same as parse_float_fallback, but for binary32 (float) values. Going through
// the binary64 fallback and then rounding to binary32 would be subject to
// double rounding, so we run the fallback algorithm directly on binary32.
@@ -17982,57 +18245,6 @@ simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned(const u
return i;
}
-
-
-// Parse any number from 0 to 18,446,744,073,709,551,615
-// Never read at src_end or beyond
-simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned(const uint8_t * const src, const uint8_t * const src_end) noexcept {
- const uint8_t *p = src;
- //
- // Parse the integer part.
- //
- // PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
- const uint8_t *const start_digits = p;
- uint64_t i = 0;
- while ((p != src_end) && parse_digit(*p, i)) { p++; }
-
- // If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
- // Optimization note: size_t is expected to be unsigned.
- size_t digit_count = size_t(p - start_digits);
- // The longest positive 64-bit number is 20 digits.
- // We do it this way so we don't trigger this branch unless we must.
- // Optimization note: the compiler can probably merge
- // ((digit_count == 0) || (digit_count > 20))
- // into a single branch since digit_count is unsigned.
- if ((digit_count == 0) || (digit_count > 20)) { return INCORRECT_TYPE; }
- // Here digit_count > 0.
- if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
- // We can do the following...
- // if (!jsoncharutils::is_structural_or_whitespace(*p)) {
- // return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
- // }
- // as a single table lookup:
- if ((p != src_end) && integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); }
-
- if (digit_count == 20) {
- // Positive overflow check:
- // - A 20 digit number starting with 2-9 is overflow, because 18,446,744,073,709,551,615 is the
- // biggest uint64_t.
- // - A 20 digit number starting with 1 is overflow if it is less than INT64_MAX.
- // If we got here, it's a 20 digit number starting with the digit "1".
- // - If a 20 digit number starting with 1 overflowed (i*10+digit), the result will be smaller
- // than 1,553,255,926,290,448,384.
- // - That is smaller than the smallest possible 20-digit number the user could write:
- // 10,000,000,000,000,000,000.
- // - Therefore, if the number is positive and lower than that, it's overflow.
- // - The value we are looking at is less than or equal to INT64_MAX.
- //
- if (src[0] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INCORRECT_TYPE; }
- }
-
- return i;
-}
-
// Parse any number from 0 to 18,446,744,073,709,551,615
simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned_in_string(const uint8_t * const src) noexcept {
const uint8_t *p = src + 1;
@@ -18125,50 +18337,6 @@ simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer(const uin
return negative ? (~i+1) : i;
}
-// Parse any number from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
-// Never read at src_end or beyond
-simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer(const uint8_t * const src, const uint8_t * const src_end) noexcept {
- //
- // Check for minus sign
- //
- if(src == src_end) { return NUMBER_ERROR; }
- bool negative = (*src == '-');
- const uint8_t *p = src + uint8_t(negative);
-
- //
- // Parse the integer part.
- //
- // PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
- const uint8_t *const start_digits = p;
- uint64_t i = 0;
- while ((p != src_end) && parse_digit(*p, i)) { p++; }
-
- // If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
- // Optimization note: size_t is expected to be unsigned.
- size_t digit_count = size_t(p - start_digits);
- // We go from
- // -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
- // so we can never represent numbers that have more than 19 digits.
- size_t longest_digit_count = 19;
- // Optimization note: the compiler can probably merge
- // ((digit_count == 0) || (digit_count > longest_digit_count))
- // into a single branch since digit_count is unsigned.
- if ((digit_count == 0) || (digit_count > longest_digit_count)) { return INCORRECT_TYPE; }
- // Here digit_count > 0.
- if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
- // We can do the following...
- // if (!jsoncharutils::is_structural_or_whitespace(*p)) {
- // return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
- // }
- // as a single table lookup:
- if((p != src_end) && integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); }
- // Negative numbers have can go down to - INT64_MAX - 1 whereas positive numbers are limited to INT64_MAX.
- // Performance note: This check is only needed when digit_count == longest_digit_count but it is
- // so cheap that we might as well always make it.
- if(i > uint64_t(INT64_MAX) + uint64_t(negative)) { return INCORRECT_TYPE; }
- return negative ? (~i+1) : i;
-}
-
// Parse any number from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer_in_string(const uint8_t *src) noexcept {
//
@@ -18274,8 +18442,11 @@ simdjson_unused simdjson_inline simdjson_result<double> parse_double(const uint8
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
+ // no exp digits
+ if (p-start_exp_digits == 0) { return NUMBER_ERROR; }
+ // 20+ exp digits (e.g., 0e9999999999999999999999) are valid, but exp may
+ // have wrapped around: leave such numbers to the fallback parser.
+ overflow = overflow || p-start_exp_digits > 19;
exponent += exp_neg ? 0-exp : exp;
}
@@ -18365,8 +18536,11 @@ simdjson_unused simdjson_inline simdjson_result<float> parse_float(const uint8_t
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
+ // no exp digits
+ if (p-start_exp_digits == 0) { return NUMBER_ERROR; }
+ // 20+ exp digits (e.g., 0e9999999999999999999999) are valid, but exp may
+ // have wrapped around: leave such numbers to the fallback parser.
+ overflow = overflow || p-start_exp_digits > 19;
exponent += exp_neg ? 0-exp : exp;
}
@@ -18446,88 +18620,6 @@ simdjson_unused simdjson_inline simdjson_result<number_type> get_number_type(con
return number_type::floating_point_number;
}
-// Never read at src_end or beyond
-simdjson_unused simdjson_inline simdjson_result<double> parse_double(const uint8_t * src, const uint8_t * const src_end) noexcept {
- if(src == src_end) { return NUMBER_ERROR; }
- //
- // Check for minus sign
- //
- bool negative = (*src == '-');
- src += uint8_t(negative);
-
- //
- // Parse the integer part.
- //
- uint64_t i = 0;
- const uint8_t *p = src;
- if(p == src_end) { return NUMBER_ERROR; }
- p += parse_digit(*p, i);
- bool leading_zero = (i == 0);
- while ((p != src_end) && parse_digit(*p, i)) { p++; }
- // no integer digits, or 0123 (zero must be solo)
- if ( p == src ) { return INCORRECT_TYPE; }
- if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; }
-
- //
- // Parse the decimal part.
- //
- int64_t exponent = 0;
- bool overflow;
- if (simdjson_likely((p != src_end) && (*p == '.'))) {
- p++;
- const uint8_t *start_decimal_digits = p;
- if ((p == src_end) || !parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits
- p++;
- while ((p != src_end) && parse_digit(*p, i)) { p++; }
- exponent = -(p - start_decimal_digits);
-
- // Overflow check. More than 19 digits (minus the decimal) may be overflow.
- overflow = p-src-1 > 19;
- if (simdjson_unlikely(overflow && leading_zero)) {
- // Skip leading 0.00000 and see if it still overflows
- const uint8_t *start_digits = src + 2;
- while (*start_digits == '0') { start_digits++; }
- overflow = start_digits-src > 19;
- }
- } else {
- overflow = p-src > 19;
- }
-
- //
- // Parse the exponent
- //
- if ((p != src_end) && (*p == 'e' || *p == 'E')) {
- p++;
- if(p == src_end) { return NUMBER_ERROR; }
- bool exp_neg = *p == '-';
- p += exp_neg || *p == '+';
-
- uint64_t exp = 0;
- const uint8_t *start_exp_digits = p;
- while ((p != src_end) && parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
-
- exponent += exp_neg ? 0-exp : exp;
- }
-
- if ((p != src_end) && jsoncharutils::is_not_structural_or_whitespace(*p)) { return NUMBER_ERROR; }
-
- overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power;
-
- //
- // Assemble (or slow-parse) the float
- //
- double d;
- if (simdjson_likely(!overflow)) {
- if (compute_float_64(exponent, i, negative, d)) { return d; }
- }
- if (!parse_float_fallback(src - uint8_t(negative), src_end, &d)) {
- return NUMBER_ERROR;
- }
- return d;
-}
-
simdjson_unused simdjson_inline simdjson_result<double> parse_double_in_string(const uint8_t * src) noexcept {
//
// Check for minus sign
@@ -18596,8 +18688,11 @@ simdjson_unused simdjson_inline simdjson_result<double> parse_double_in_string(c
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
+ // no exp digits
+ if (p-start_exp_digits == 0) { return NUMBER_ERROR; }
+ // 20+ exp digits (e.g., 0e9999999999999999999999) are valid, but exp may
+ // have wrapped around: leave such numbers to the fallback parser.
+ overflow = overflow || p-start_exp_digits > 19;
exponent += exp_neg ? 0-exp : exp;
}
@@ -18689,8 +18784,11 @@ simdjson_unused simdjson_inline simdjson_result<float> parse_float_in_string(con
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
+ // no exp digits
+ if (p-start_exp_digits == 0) { return NUMBER_ERROR; }
+ // 20+ exp digits (e.g., 0e9999999999999999999999) are valid, but exp may
+ // have wrapped around: leave such numbers to the fallback parser.
+ overflow = overflow || p-start_exp_digits > 19;
exponent += exp_neg ? 0-exp : exp;
}
@@ -19000,23 +19098,6 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin
}
-struct escaping {
- static constexpr uint32_t BYTES_PROCESSED = 1;
- simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
-
- simdjson_inline bool has_escape() { return escape_bits; }
- simdjson_inline int escape_index() { return 0; }
-
- bool escape_bits;
-}; // struct escaping
-
-
-
-simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
- dst[0] = src[0];
- return { (src[0] == '\\') || (src[0] == '"') || (src[0] < 32) };
-}
-
} // unnamed namespace
} // namespace fallback
} // namespace simdjson
@@ -19496,6 +19577,7 @@ public:
simdjson_warn_unused error_code stage1(const uint8_t *buf, size_t len, stage1_mode partial) noexcept final;
simdjson_warn_unused error_code stage2(dom::document &doc) noexcept final;
simdjson_warn_unused error_code stage2_next(dom::document &doc) noexcept final;
+ stage2_next_function_t stage2_next_function(bool bounded) const noexcept final;
simdjson_warn_unused uint8_t *parse_string(const uint8_t *src, uint8_t *dst, bool allow_replacement) const noexcept final;
simdjson_warn_unused uint8_t *parse_wobbly_string(const uint8_t *src, uint8_t *dst) const noexcept final;
inline simdjson_warn_unused error_code set_capacity(size_t capacity) noexcept final;
@@ -20257,21 +20339,6 @@ static bool parse_float_fallback(const uint8_t *ptr, double *outDouble) {
return !(*outDouble > (std::numeric_limits<double>::max)() || *outDouble < std::numeric_limits<double>::lowest());
}
-static bool parse_float_fallback(const uint8_t *ptr, const uint8_t *end_ptr, double *outDouble) {
- *outDouble = simdjson::internal::from_chars(reinterpret_cast<const char *>(ptr), reinterpret_cast<const char *>(end_ptr));
- // We do not accept infinite values.
-
- // Detecting finite values in a portable manner is ridiculously hard, ideally
- // we would want to do:
- // return !std::isfinite(*outDouble);
- // but that mysteriously fails under legacy/old libc++ libraries, see
- // https://github.com/simdjson/simdjson/issues/1286
- //
- // Therefore, fall back to this solution (the extra parens are there
- // to handle that max may be a macro on windows).
- return !(*outDouble > (std::numeric_limits<double>::max)() || *outDouble < std::numeric_limits<double>::lowest());
-}
-
// Same as parse_float_fallback, but for binary32 (float) values. Going through
// the binary64 fallback and then rounding to binary32 would be subject to
// double rounding, so we run the fallback algorithm directly on binary32.
@@ -20817,57 +20884,6 @@ simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned(const u
return i;
}
-
-
-// Parse any number from 0 to 18,446,744,073,709,551,615
-// Never read at src_end or beyond
-simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned(const uint8_t * const src, const uint8_t * const src_end) noexcept {
- const uint8_t *p = src;
- //
- // Parse the integer part.
- //
- // PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
- const uint8_t *const start_digits = p;
- uint64_t i = 0;
- while ((p != src_end) && parse_digit(*p, i)) { p++; }
-
- // If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
- // Optimization note: size_t is expected to be unsigned.
- size_t digit_count = size_t(p - start_digits);
- // The longest positive 64-bit number is 20 digits.
- // We do it this way so we don't trigger this branch unless we must.
- // Optimization note: the compiler can probably merge
- // ((digit_count == 0) || (digit_count > 20))
- // into a single branch since digit_count is unsigned.
- if ((digit_count == 0) || (digit_count > 20)) { return INCORRECT_TYPE; }
- // Here digit_count > 0.
- if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
- // We can do the following...
- // if (!jsoncharutils::is_structural_or_whitespace(*p)) {
- // return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
- // }
- // as a single table lookup:
- if ((p != src_end) && integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); }
-
- if (digit_count == 20) {
- // Positive overflow check:
- // - A 20 digit number starting with 2-9 is overflow, because 18,446,744,073,709,551,615 is the
- // biggest uint64_t.
- // - A 20 digit number starting with 1 is overflow if it is less than INT64_MAX.
- // If we got here, it's a 20 digit number starting with the digit "1".
- // - If a 20 digit number starting with 1 overflowed (i*10+digit), the result will be smaller
- // than 1,553,255,926,290,448,384.
- // - That is smaller than the smallest possible 20-digit number the user could write:
- // 10,000,000,000,000,000,000.
- // - Therefore, if the number is positive and lower than that, it's overflow.
- // - The value we are looking at is less than or equal to INT64_MAX.
- //
- if (src[0] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INCORRECT_TYPE; }
- }
-
- return i;
-}
-
// Parse any number from 0 to 18,446,744,073,709,551,615
simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned_in_string(const uint8_t * const src) noexcept {
const uint8_t *p = src + 1;
@@ -20960,50 +20976,6 @@ simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer(const uin
return negative ? (~i+1) : i;
}
-// Parse any number from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
-// Never read at src_end or beyond
-simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer(const uint8_t * const src, const uint8_t * const src_end) noexcept {
- //
- // Check for minus sign
- //
- if(src == src_end) { return NUMBER_ERROR; }
- bool negative = (*src == '-');
- const uint8_t *p = src + uint8_t(negative);
-
- //
- // Parse the integer part.
- //
- // PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
- const uint8_t *const start_digits = p;
- uint64_t i = 0;
- while ((p != src_end) && parse_digit(*p, i)) { p++; }
-
- // If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
- // Optimization note: size_t is expected to be unsigned.
- size_t digit_count = size_t(p - start_digits);
- // We go from
- // -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
- // so we can never represent numbers that have more than 19 digits.
- size_t longest_digit_count = 19;
- // Optimization note: the compiler can probably merge
- // ((digit_count == 0) || (digit_count > longest_digit_count))
- // into a single branch since digit_count is unsigned.
- if ((digit_count == 0) || (digit_count > longest_digit_count)) { return INCORRECT_TYPE; }
- // Here digit_count > 0.
- if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
- // We can do the following...
- // if (!jsoncharutils::is_structural_or_whitespace(*p)) {
- // return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
- // }
- // as a single table lookup:
- if((p != src_end) && integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); }
- // Negative numbers have can go down to - INT64_MAX - 1 whereas positive numbers are limited to INT64_MAX.
- // Performance note: This check is only needed when digit_count == longest_digit_count but it is
- // so cheap that we might as well always make it.
- if(i > uint64_t(INT64_MAX) + uint64_t(negative)) { return INCORRECT_TYPE; }
- return negative ? (~i+1) : i;
-}
-
// Parse any number from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer_in_string(const uint8_t *src) noexcept {
//
@@ -21109,8 +21081,11 @@ simdjson_unused simdjson_inline simdjson_result<double> parse_double(const uint8
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
+ // no exp digits
+ if (p-start_exp_digits == 0) { return NUMBER_ERROR; }
+ // 20+ exp digits (e.g., 0e9999999999999999999999) are valid, but exp may
+ // have wrapped around: leave such numbers to the fallback parser.
+ overflow = overflow || p-start_exp_digits > 19;
exponent += exp_neg ? 0-exp : exp;
}
@@ -21200,8 +21175,11 @@ simdjson_unused simdjson_inline simdjson_result<float> parse_float(const uint8_t
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
+ // no exp digits
+ if (p-start_exp_digits == 0) { return NUMBER_ERROR; }
+ // 20+ exp digits (e.g., 0e9999999999999999999999) are valid, but exp may
+ // have wrapped around: leave such numbers to the fallback parser.
+ overflow = overflow || p-start_exp_digits > 19;
exponent += exp_neg ? 0-exp : exp;
}
@@ -21281,88 +21259,6 @@ simdjson_unused simdjson_inline simdjson_result<number_type> get_number_type(con
return number_type::floating_point_number;
}
-// Never read at src_end or beyond
-simdjson_unused simdjson_inline simdjson_result<double> parse_double(const uint8_t * src, const uint8_t * const src_end) noexcept {
- if(src == src_end) { return NUMBER_ERROR; }
- //
- // Check for minus sign
- //
- bool negative = (*src == '-');
- src += uint8_t(negative);
-
- //
- // Parse the integer part.
- //
- uint64_t i = 0;
- const uint8_t *p = src;
- if(p == src_end) { return NUMBER_ERROR; }
- p += parse_digit(*p, i);
- bool leading_zero = (i == 0);
- while ((p != src_end) && parse_digit(*p, i)) { p++; }
- // no integer digits, or 0123 (zero must be solo)
- if ( p == src ) { return INCORRECT_TYPE; }
- if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; }
-
- //
- // Parse the decimal part.
- //
- int64_t exponent = 0;
- bool overflow;
- if (simdjson_likely((p != src_end) && (*p == '.'))) {
- p++;
- const uint8_t *start_decimal_digits = p;
- if ((p == src_end) || !parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits
- p++;
- while ((p != src_end) && parse_digit(*p, i)) { p++; }
- exponent = -(p - start_decimal_digits);
-
- // Overflow check. More than 19 digits (minus the decimal) may be overflow.
- overflow = p-src-1 > 19;
- if (simdjson_unlikely(overflow && leading_zero)) {
- // Skip leading 0.00000 and see if it still overflows
- const uint8_t *start_digits = src + 2;
- while (*start_digits == '0') { start_digits++; }
- overflow = start_digits-src > 19;
- }
- } else {
- overflow = p-src > 19;
- }
-
- //
- // Parse the exponent
- //
- if ((p != src_end) && (*p == 'e' || *p == 'E')) {
- p++;
- if(p == src_end) { return NUMBER_ERROR; }
- bool exp_neg = *p == '-';
- p += exp_neg || *p == '+';
-
- uint64_t exp = 0;
- const uint8_t *start_exp_digits = p;
- while ((p != src_end) && parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
-
- exponent += exp_neg ? 0-exp : exp;
- }
-
- if ((p != src_end) && jsoncharutils::is_not_structural_or_whitespace(*p)) { return NUMBER_ERROR; }
-
- overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power;
-
- //
- // Assemble (or slow-parse) the float
- //
- double d;
- if (simdjson_likely(!overflow)) {
- if (compute_float_64(exponent, i, negative, d)) { return d; }
- }
- if (!parse_float_fallback(src - uint8_t(negative), src_end, &d)) {
- return NUMBER_ERROR;
- }
- return d;
-}
-
simdjson_unused simdjson_inline simdjson_result<double> parse_double_in_string(const uint8_t * src) noexcept {
//
// Check for minus sign
@@ -21431,8 +21327,11 @@ simdjson_unused simdjson_inline simdjson_result<double> parse_double_in_string(c
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
+ // no exp digits
+ if (p-start_exp_digits == 0) { return NUMBER_ERROR; }
+ // 20+ exp digits (e.g., 0e9999999999999999999999) are valid, but exp may
+ // have wrapped around: leave such numbers to the fallback parser.
+ overflow = overflow || p-start_exp_digits > 19;
exponent += exp_neg ? 0-exp : exp;
}
@@ -21524,8 +21423,11 @@ simdjson_unused simdjson_inline simdjson_result<float> parse_float_in_string(con
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
+ // no exp digits
+ if (p-start_exp_digits == 0) { return NUMBER_ERROR; }
+ // 20+ exp digits (e.g., 0e9999999999999999999999) are valid, but exp may
+ // have wrapped around: leave such numbers to the fallback parser.
+ overflow = overflow || p-start_exp_digits > 19;
exponent += exp_neg ? 0-exp : exp;
}
@@ -22394,30 +22296,6 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin
}
-struct escaping {
- static constexpr uint32_t BYTES_PROCESSED = 32;
- simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
-
- simdjson_inline bool has_escape() { return escape_bits != 0; }
- simdjson_inline int escape_index() { return trailing_zeroes(escape_bits); }
-
- uint64_t escape_bits;
-}; // struct escaping
-
-
-
-simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
- static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes");
- simd8<uint8_t> v(src);
- v.store(dst);
- simd8<bool> is_quote = (v == '"');
- simd8<bool> is_backslash = (v == '\\');
- simd8<bool> is_control = (v < 32);
- return {
- uint64_t((is_backslash | is_quote | is_control).to_bitmask())
- };
-}
-
} // unnamed namespace
} // namespace haswell
} // namespace simdjson
@@ -22808,6 +22686,7 @@ public:
simdjson_warn_unused error_code stage1(const uint8_t *buf, size_t len, stage1_mode partial) noexcept final;
simdjson_warn_unused error_code stage2(dom::document &doc) noexcept final;
simdjson_warn_unused error_code stage2_next(dom::document &doc) noexcept final;
+ stage2_next_function_t stage2_next_function(bool bounded) const noexcept final;
simdjson_warn_unused uint8_t *parse_string(const uint8_t *src, uint8_t *dst, bool allow_replacement) const noexcept final;
simdjson_warn_unused uint8_t *parse_wobbly_string(const uint8_t *src, uint8_t *dst) const noexcept final;
inline simdjson_warn_unused error_code set_capacity(size_t capacity) noexcept final;
@@ -23569,21 +23448,6 @@ static bool parse_float_fallback(const uint8_t *ptr, double *outDouble) {
return !(*outDouble > (std::numeric_limits<double>::max)() || *outDouble < std::numeric_limits<double>::lowest());
}
-static bool parse_float_fallback(const uint8_t *ptr, const uint8_t *end_ptr, double *outDouble) {
- *outDouble = simdjson::internal::from_chars(reinterpret_cast<const char *>(ptr), reinterpret_cast<const char *>(end_ptr));
- // We do not accept infinite values.
-
- // Detecting finite values in a portable manner is ridiculously hard, ideally
- // we would want to do:
- // return !std::isfinite(*outDouble);
- // but that mysteriously fails under legacy/old libc++ libraries, see
- // https://github.com/simdjson/simdjson/issues/1286
- //
- // Therefore, fall back to this solution (the extra parens are there
- // to handle that max may be a macro on windows).
- return !(*outDouble > (std::numeric_limits<double>::max)() || *outDouble < std::numeric_limits<double>::lowest());
-}
-
// Same as parse_float_fallback, but for binary32 (float) values. Going through
// the binary64 fallback and then rounding to binary32 would be subject to
// double rounding, so we run the fallback algorithm directly on binary32.
@@ -24129,57 +23993,6 @@ simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned(const u
return i;
}
-
-
-// Parse any number from 0 to 18,446,744,073,709,551,615
-// Never read at src_end or beyond
-simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned(const uint8_t * const src, const uint8_t * const src_end) noexcept {
- const uint8_t *p = src;
- //
- // Parse the integer part.
- //
- // PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
- const uint8_t *const start_digits = p;
- uint64_t i = 0;
- while ((p != src_end) && parse_digit(*p, i)) { p++; }
-
- // If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
- // Optimization note: size_t is expected to be unsigned.
- size_t digit_count = size_t(p - start_digits);
- // The longest positive 64-bit number is 20 digits.
- // We do it this way so we don't trigger this branch unless we must.
- // Optimization note: the compiler can probably merge
- // ((digit_count == 0) || (digit_count > 20))
- // into a single branch since digit_count is unsigned.
- if ((digit_count == 0) || (digit_count > 20)) { return INCORRECT_TYPE; }
- // Here digit_count > 0.
- if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
- // We can do the following...
- // if (!jsoncharutils::is_structural_or_whitespace(*p)) {
- // return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
- // }
- // as a single table lookup:
- if ((p != src_end) && integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); }
-
- if (digit_count == 20) {
- // Positive overflow check:
- // - A 20 digit number starting with 2-9 is overflow, because 18,446,744,073,709,551,615 is the
- // biggest uint64_t.
- // - A 20 digit number starting with 1 is overflow if it is less than INT64_MAX.
- // If we got here, it's a 20 digit number starting with the digit "1".
- // - If a 20 digit number starting with 1 overflowed (i*10+digit), the result will be smaller
- // than 1,553,255,926,290,448,384.
- // - That is smaller than the smallest possible 20-digit number the user could write:
- // 10,000,000,000,000,000,000.
- // - Therefore, if the number is positive and lower than that, it's overflow.
- // - The value we are looking at is less than or equal to INT64_MAX.
- //
- if (src[0] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INCORRECT_TYPE; }
- }
-
- return i;
-}
-
// Parse any number from 0 to 18,446,744,073,709,551,615
simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned_in_string(const uint8_t * const src) noexcept {
const uint8_t *p = src + 1;
@@ -24272,50 +24085,6 @@ simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer(const uin
return negative ? (~i+1) : i;
}
-// Parse any number from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
-// Never read at src_end or beyond
-simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer(const uint8_t * const src, const uint8_t * const src_end) noexcept {
- //
- // Check for minus sign
- //
- if(src == src_end) { return NUMBER_ERROR; }
- bool negative = (*src == '-');
- const uint8_t *p = src + uint8_t(negative);
-
- //
- // Parse the integer part.
- //
- // PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
- const uint8_t *const start_digits = p;
- uint64_t i = 0;
- while ((p != src_end) && parse_digit(*p, i)) { p++; }
-
- // If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
- // Optimization note: size_t is expected to be unsigned.
- size_t digit_count = size_t(p - start_digits);
- // We go from
- // -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
- // so we can never represent numbers that have more than 19 digits.
- size_t longest_digit_count = 19;
- // Optimization note: the compiler can probably merge
- // ((digit_count == 0) || (digit_count > longest_digit_count))
- // into a single branch since digit_count is unsigned.
- if ((digit_count == 0) || (digit_count > longest_digit_count)) { return INCORRECT_TYPE; }
- // Here digit_count > 0.
- if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
- // We can do the following...
- // if (!jsoncharutils::is_structural_or_whitespace(*p)) {
- // return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
- // }
- // as a single table lookup:
- if((p != src_end) && integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); }
- // Negative numbers have can go down to - INT64_MAX - 1 whereas positive numbers are limited to INT64_MAX.
- // Performance note: This check is only needed when digit_count == longest_digit_count but it is
- // so cheap that we might as well always make it.
- if(i > uint64_t(INT64_MAX) + uint64_t(negative)) { return INCORRECT_TYPE; }
- return negative ? (~i+1) : i;
-}
-
// Parse any number from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer_in_string(const uint8_t *src) noexcept {
//
@@ -24421,8 +24190,11 @@ simdjson_unused simdjson_inline simdjson_result<double> parse_double(const uint8
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
+ // no exp digits
+ if (p-start_exp_digits == 0) { return NUMBER_ERROR; }
+ // 20+ exp digits (e.g., 0e9999999999999999999999) are valid, but exp may
+ // have wrapped around: leave such numbers to the fallback parser.
+ overflow = overflow || p-start_exp_digits > 19;
exponent += exp_neg ? 0-exp : exp;
}
@@ -24512,8 +24284,11 @@ simdjson_unused simdjson_inline simdjson_result<float> parse_float(const uint8_t
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
+ // no exp digits
+ if (p-start_exp_digits == 0) { return NUMBER_ERROR; }
+ // 20+ exp digits (e.g., 0e9999999999999999999999) are valid, but exp may
+ // have wrapped around: leave such numbers to the fallback parser.
+ overflow = overflow || p-start_exp_digits > 19;
exponent += exp_neg ? 0-exp : exp;
}
@@ -24593,88 +24368,6 @@ simdjson_unused simdjson_inline simdjson_result<number_type> get_number_type(con
return number_type::floating_point_number;
}
-// Never read at src_end or beyond
-simdjson_unused simdjson_inline simdjson_result<double> parse_double(const uint8_t * src, const uint8_t * const src_end) noexcept {
- if(src == src_end) { return NUMBER_ERROR; }
- //
- // Check for minus sign
- //
- bool negative = (*src == '-');
- src += uint8_t(negative);
-
- //
- // Parse the integer part.
- //
- uint64_t i = 0;
- const uint8_t *p = src;
- if(p == src_end) { return NUMBER_ERROR; }
- p += parse_digit(*p, i);
- bool leading_zero = (i == 0);
- while ((p != src_end) && parse_digit(*p, i)) { p++; }
- // no integer digits, or 0123 (zero must be solo)
- if ( p == src ) { return INCORRECT_TYPE; }
- if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; }
-
- //
- // Parse the decimal part.
- //
- int64_t exponent = 0;
- bool overflow;
- if (simdjson_likely((p != src_end) && (*p == '.'))) {
- p++;
- const uint8_t *start_decimal_digits = p;
- if ((p == src_end) || !parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits
- p++;
- while ((p != src_end) && parse_digit(*p, i)) { p++; }
- exponent = -(p - start_decimal_digits);
-
- // Overflow check. More than 19 digits (minus the decimal) may be overflow.
- overflow = p-src-1 > 19;
- if (simdjson_unlikely(overflow && leading_zero)) {
- // Skip leading 0.00000 and see if it still overflows
- const uint8_t *start_digits = src + 2;
- while (*start_digits == '0') { start_digits++; }
- overflow = start_digits-src > 19;
- }
- } else {
- overflow = p-src > 19;
- }
-
- //
- // Parse the exponent
- //
- if ((p != src_end) && (*p == 'e' || *p == 'E')) {
- p++;
- if(p == src_end) { return NUMBER_ERROR; }
- bool exp_neg = *p == '-';
- p += exp_neg || *p == '+';
-
- uint64_t exp = 0;
- const uint8_t *start_exp_digits = p;
- while ((p != src_end) && parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
-
- exponent += exp_neg ? 0-exp : exp;
- }
-
- if ((p != src_end) && jsoncharutils::is_not_structural_or_whitespace(*p)) { return NUMBER_ERROR; }
-
- overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power;
-
- //
- // Assemble (or slow-parse) the float
- //
- double d;
- if (simdjson_likely(!overflow)) {
- if (compute_float_64(exponent, i, negative, d)) { return d; }
- }
- if (!parse_float_fallback(src - uint8_t(negative), src_end, &d)) {
- return NUMBER_ERROR;
- }
- return d;
-}
-
simdjson_unused simdjson_inline simdjson_result<double> parse_double_in_string(const uint8_t * src) noexcept {
//
// Check for minus sign
@@ -24743,8 +24436,11 @@ simdjson_unused simdjson_inline simdjson_result<double> parse_double_in_string(c
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
+ // no exp digits
+ if (p-start_exp_digits == 0) { return NUMBER_ERROR; }
+ // 20+ exp digits (e.g., 0e9999999999999999999999) are valid, but exp may
+ // have wrapped around: leave such numbers to the fallback parser.
+ overflow = overflow || p-start_exp_digits > 19;
exponent += exp_neg ? 0-exp : exp;
}
@@ -24836,8 +24532,11 @@ simdjson_unused simdjson_inline simdjson_result<float> parse_float_in_string(con
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
+ // no exp digits
+ if (p-start_exp_digits == 0) { return NUMBER_ERROR; }
+ // 20+ exp digits (e.g., 0e9999999999999999999999) are valid, but exp may
+ // have wrapped around: leave such numbers to the fallback parser.
+ overflow = overflow || p-start_exp_digits > 19;
exponent += exp_neg ? 0-exp : exp;
}
@@ -25642,34 +25341,6 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin
}
-
-struct escaping {
- static constexpr uint32_t BYTES_PROCESSED = 64;
- simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
-
- simdjson_inline bool has_escape() { return escape_bits != 0; }
- simdjson_inline int escape_index() { return trailing_zeroes(uint64_t(escape_bits)); }
-
- __mmask64 escape_bits;
-}; // struct escaping
-
-
-
-simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
- static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes");
- simd8<uint8_t> v(src);
- v.store(dst);
- __mmask64 is_quote = _mm512_cmpeq_epi8_mask(v, _mm512_set1_epi8('"'));
- __mmask64 is_backslash = _mm512_cmpeq_epi8_mask(v, _mm512_set1_epi8('\\'));
- __mmask64 is_control = _mm512_cmplt_epi8_mask(v, _mm512_set1_epi8(32));
- return {
- (is_backslash | is_quote | is_control)
- };
-}
-
-
-
-
} // unnamed namespace
} // namespace icelake
} // namespace simdjson
@@ -26120,6 +25791,7 @@ public:
simdjson_warn_unused error_code stage1(const uint8_t *buf, size_t len, stage1_mode partial) noexcept final;
simdjson_warn_unused error_code stage2(dom::document &doc) noexcept final;
simdjson_warn_unused error_code stage2_next(dom::document &doc) noexcept final;
+ stage2_next_function_t stage2_next_function(bool bounded) const noexcept final;
simdjson_warn_unused uint8_t *parse_string(const uint8_t *src, uint8_t *dst, bool allow_replacement) const noexcept final;
simdjson_warn_unused uint8_t *parse_wobbly_string(const uint8_t *src, uint8_t *dst) const noexcept final;
inline simdjson_warn_unused error_code set_capacity(size_t capacity) noexcept final;
@@ -26881,21 +26553,6 @@ static bool parse_float_fallback(const uint8_t *ptr, double *outDouble) {
return !(*outDouble > (std::numeric_limits<double>::max)() || *outDouble < std::numeric_limits<double>::lowest());
}
-static bool parse_float_fallback(const uint8_t *ptr, const uint8_t *end_ptr, double *outDouble) {
- *outDouble = simdjson::internal::from_chars(reinterpret_cast<const char *>(ptr), reinterpret_cast<const char *>(end_ptr));
- // We do not accept infinite values.
-
- // Detecting finite values in a portable manner is ridiculously hard, ideally
- // we would want to do:
- // return !std::isfinite(*outDouble);
- // but that mysteriously fails under legacy/old libc++ libraries, see
- // https://github.com/simdjson/simdjson/issues/1286
- //
- // Therefore, fall back to this solution (the extra parens are there
- // to handle that max may be a macro on windows).
- return !(*outDouble > (std::numeric_limits<double>::max)() || *outDouble < std::numeric_limits<double>::lowest());
-}
-
// Same as parse_float_fallback, but for binary32 (float) values. Going through
// the binary64 fallback and then rounding to binary32 would be subject to
// double rounding, so we run the fallback algorithm directly on binary32.
@@ -27441,57 +27098,6 @@ simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned(const u
return i;
}
-
-
-// Parse any number from 0 to 18,446,744,073,709,551,615
-// Never read at src_end or beyond
-simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned(const uint8_t * const src, const uint8_t * const src_end) noexcept {
- const uint8_t *p = src;
- //
- // Parse the integer part.
- //
- // PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
- const uint8_t *const start_digits = p;
- uint64_t i = 0;
- while ((p != src_end) && parse_digit(*p, i)) { p++; }
-
- // If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
- // Optimization note: size_t is expected to be unsigned.
- size_t digit_count = size_t(p - start_digits);
- // The longest positive 64-bit number is 20 digits.
- // We do it this way so we don't trigger this branch unless we must.
- // Optimization note: the compiler can probably merge
- // ((digit_count == 0) || (digit_count > 20))
- // into a single branch since digit_count is unsigned.
- if ((digit_count == 0) || (digit_count > 20)) { return INCORRECT_TYPE; }
- // Here digit_count > 0.
- if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
- // We can do the following...
- // if (!jsoncharutils::is_structural_or_whitespace(*p)) {
- // return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
- // }
- // as a single table lookup:
- if ((p != src_end) && integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); }
-
- if (digit_count == 20) {
- // Positive overflow check:
- // - A 20 digit number starting with 2-9 is overflow, because 18,446,744,073,709,551,615 is the
- // biggest uint64_t.
- // - A 20 digit number starting with 1 is overflow if it is less than INT64_MAX.
- // If we got here, it's a 20 digit number starting with the digit "1".
- // - If a 20 digit number starting with 1 overflowed (i*10+digit), the result will be smaller
- // than 1,553,255,926,290,448,384.
- // - That is smaller than the smallest possible 20-digit number the user could write:
- // 10,000,000,000,000,000,000.
- // - Therefore, if the number is positive and lower than that, it's overflow.
- // - The value we are looking at is less than or equal to INT64_MAX.
- //
- if (src[0] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INCORRECT_TYPE; }
- }
-
- return i;
-}
-
// Parse any number from 0 to 18,446,744,073,709,551,615
simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned_in_string(const uint8_t * const src) noexcept {
const uint8_t *p = src + 1;
@@ -27584,50 +27190,6 @@ simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer(const uin
return negative ? (~i+1) : i;
}
-// Parse any number from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
-// Never read at src_end or beyond
-simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer(const uint8_t * const src, const uint8_t * const src_end) noexcept {
- //
- // Check for minus sign
- //
- if(src == src_end) { return NUMBER_ERROR; }
- bool negative = (*src == '-');
- const uint8_t *p = src + uint8_t(negative);
-
- //
- // Parse the integer part.
- //
- // PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
- const uint8_t *const start_digits = p;
- uint64_t i = 0;
- while ((p != src_end) && parse_digit(*p, i)) { p++; }
-
- // If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
- // Optimization note: size_t is expected to be unsigned.
- size_t digit_count = size_t(p - start_digits);
- // We go from
- // -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
- // so we can never represent numbers that have more than 19 digits.
- size_t longest_digit_count = 19;
- // Optimization note: the compiler can probably merge
- // ((digit_count == 0) || (digit_count > longest_digit_count))
- // into a single branch since digit_count is unsigned.
- if ((digit_count == 0) || (digit_count > longest_digit_count)) { return INCORRECT_TYPE; }
- // Here digit_count > 0.
- if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
- // We can do the following...
- // if (!jsoncharutils::is_structural_or_whitespace(*p)) {
- // return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
- // }
- // as a single table lookup:
- if((p != src_end) && integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); }
- // Negative numbers have can go down to - INT64_MAX - 1 whereas positive numbers are limited to INT64_MAX.
- // Performance note: This check is only needed when digit_count == longest_digit_count but it is
- // so cheap that we might as well always make it.
- if(i > uint64_t(INT64_MAX) + uint64_t(negative)) { return INCORRECT_TYPE; }
- return negative ? (~i+1) : i;
-}
-
// Parse any number from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer_in_string(const uint8_t *src) noexcept {
//
@@ -27733,8 +27295,11 @@ simdjson_unused simdjson_inline simdjson_result<double> parse_double(const uint8
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
+ // no exp digits
+ if (p-start_exp_digits == 0) { return NUMBER_ERROR; }
+ // 20+ exp digits (e.g., 0e9999999999999999999999) are valid, but exp may
+ // have wrapped around: leave such numbers to the fallback parser.
+ overflow = overflow || p-start_exp_digits > 19;
exponent += exp_neg ? 0-exp : exp;
}
@@ -27824,8 +27389,11 @@ simdjson_unused simdjson_inline simdjson_result<float> parse_float(const uint8_t
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
+ // no exp digits
+ if (p-start_exp_digits == 0) { return NUMBER_ERROR; }
+ // 20+ exp digits (e.g., 0e9999999999999999999999) are valid, but exp may
+ // have wrapped around: leave such numbers to the fallback parser.
+ overflow = overflow || p-start_exp_digits > 19;
exponent += exp_neg ? 0-exp : exp;
}
@@ -27905,88 +27473,6 @@ simdjson_unused simdjson_inline simdjson_result<number_type> get_number_type(con
return number_type::floating_point_number;
}
-// Never read at src_end or beyond
-simdjson_unused simdjson_inline simdjson_result<double> parse_double(const uint8_t * src, const uint8_t * const src_end) noexcept {
- if(src == src_end) { return NUMBER_ERROR; }
- //
- // Check for minus sign
- //
- bool negative = (*src == '-');
- src += uint8_t(negative);
-
- //
- // Parse the integer part.
- //
- uint64_t i = 0;
- const uint8_t *p = src;
- if(p == src_end) { return NUMBER_ERROR; }
- p += parse_digit(*p, i);
- bool leading_zero = (i == 0);
- while ((p != src_end) && parse_digit(*p, i)) { p++; }
- // no integer digits, or 0123 (zero must be solo)
- if ( p == src ) { return INCORRECT_TYPE; }
- if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; }
-
- //
- // Parse the decimal part.
- //
- int64_t exponent = 0;
- bool overflow;
- if (simdjson_likely((p != src_end) && (*p == '.'))) {
- p++;
- const uint8_t *start_decimal_digits = p;
- if ((p == src_end) || !parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits
- p++;
- while ((p != src_end) && parse_digit(*p, i)) { p++; }
- exponent = -(p - start_decimal_digits);
-
- // Overflow check. More than 19 digits (minus the decimal) may be overflow.
- overflow = p-src-1 > 19;
- if (simdjson_unlikely(overflow && leading_zero)) {
- // Skip leading 0.00000 and see if it still overflows
- const uint8_t *start_digits = src + 2;
- while (*start_digits == '0') { start_digits++; }
- overflow = start_digits-src > 19;
- }
- } else {
- overflow = p-src > 19;
- }
-
- //
- // Parse the exponent
- //
- if ((p != src_end) && (*p == 'e' || *p == 'E')) {
- p++;
- if(p == src_end) { return NUMBER_ERROR; }
- bool exp_neg = *p == '-';
- p += exp_neg || *p == '+';
-
- uint64_t exp = 0;
- const uint8_t *start_exp_digits = p;
- while ((p != src_end) && parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
-
- exponent += exp_neg ? 0-exp : exp;
- }
-
- if ((p != src_end) && jsoncharutils::is_not_structural_or_whitespace(*p)) { return NUMBER_ERROR; }
-
- overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power;
-
- //
- // Assemble (or slow-parse) the float
- //
- double d;
- if (simdjson_likely(!overflow)) {
- if (compute_float_64(exponent, i, negative, d)) { return d; }
- }
- if (!parse_float_fallback(src - uint8_t(negative), src_end, &d)) {
- return NUMBER_ERROR;
- }
- return d;
-}
-
simdjson_unused simdjson_inline simdjson_result<double> parse_double_in_string(const uint8_t * src) noexcept {
//
// Check for minus sign
@@ -28055,8 +27541,11 @@ simdjson_unused simdjson_inline simdjson_result<double> parse_double_in_string(c
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
+ // no exp digits
+ if (p-start_exp_digits == 0) { return NUMBER_ERROR; }
+ // 20+ exp digits (e.g., 0e9999999999999999999999) are valid, but exp may
+ // have wrapped around: leave such numbers to the fallback parser.
+ overflow = overflow || p-start_exp_digits > 19;
exponent += exp_neg ? 0-exp : exp;
}
@@ -28148,8 +27637,11 @@ simdjson_unused simdjson_inline simdjson_result<float> parse_float_in_string(con
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
+ // no exp digits
+ if (p-start_exp_digits == 0) { return NUMBER_ERROR; }
+ // 20+ exp digits (e.g., 0e9999999999999999999999) are valid, but exp may
+ // have wrapped around: leave such numbers to the fallback parser.
+ overflow = overflow || p-start_exp_digits > 19;
exponent += exp_neg ? 0-exp : exp;
}
@@ -29130,31 +28622,6 @@ backslash_and_quote::copy_and_find(const uint8_t *src, uint8_t *dst) {
}
-struct escaping {
- static constexpr uint32_t BYTES_PROCESSED = 16;
- simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
-
- simdjson_inline bool has_escape() { return escape_bits != 0; }
- simdjson_inline int escape_index() { return trailing_zeroes(escape_bits); }
-
- uint64_t escape_bits;
-}; // struct escaping
-
-
-
-simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
- static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes");
- simd8<uint8_t> v(src);
- v.store(dst);
- simd8<bool> is_quote = (v == '"');
- simd8<bool> is_backslash = (v == '\\');
- simd8<bool> is_control = (v < 32);
- return {
- // We store it as a 64-bit bitmask even though we only need 16 bits.
- uint64_t((is_backslash | is_quote | is_control).to_bitmask())
- };
-}
-
} // unnamed namespace
} // namespace ppc64
} // namespace simdjson
@@ -29547,6 +29014,7 @@ public:
simdjson_warn_unused error_code stage1(const uint8_t *buf, size_t len, stage1_mode partial) noexcept final;
simdjson_warn_unused error_code stage2(dom::document &doc) noexcept final;
simdjson_warn_unused error_code stage2_next(dom::document &doc) noexcept final;
+ stage2_next_function_t stage2_next_function(bool bounded) const noexcept final;
simdjson_warn_unused uint8_t *parse_string(const uint8_t *src, uint8_t *dst, bool allow_replacement) const noexcept final;
simdjson_warn_unused uint8_t *parse_wobbly_string(const uint8_t *src, uint8_t *dst) const noexcept final;
inline simdjson_warn_unused error_code set_capacity(size_t capacity) noexcept final;
@@ -30308,21 +29776,6 @@ static bool parse_float_fallback(const uint8_t *ptr, double *outDouble) {
return !(*outDouble > (std::numeric_limits<double>::max)() || *outDouble < std::numeric_limits<double>::lowest());
}
-static bool parse_float_fallback(const uint8_t *ptr, const uint8_t *end_ptr, double *outDouble) {
- *outDouble = simdjson::internal::from_chars(reinterpret_cast<const char *>(ptr), reinterpret_cast<const char *>(end_ptr));
- // We do not accept infinite values.
-
- // Detecting finite values in a portable manner is ridiculously hard, ideally
- // we would want to do:
- // return !std::isfinite(*outDouble);
- // but that mysteriously fails under legacy/old libc++ libraries, see
- // https://github.com/simdjson/simdjson/issues/1286
- //
- // Therefore, fall back to this solution (the extra parens are there
- // to handle that max may be a macro on windows).
- return !(*outDouble > (std::numeric_limits<double>::max)() || *outDouble < std::numeric_limits<double>::lowest());
-}
-
// Same as parse_float_fallback, but for binary32 (float) values. Going through
// the binary64 fallback and then rounding to binary32 would be subject to
// double rounding, so we run the fallback algorithm directly on binary32.
@@ -30868,57 +30321,6 @@ simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned(const u
return i;
}
-
-
-// Parse any number from 0 to 18,446,744,073,709,551,615
-// Never read at src_end or beyond
-simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned(const uint8_t * const src, const uint8_t * const src_end) noexcept {
- const uint8_t *p = src;
- //
- // Parse the integer part.
- //
- // PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
- const uint8_t *const start_digits = p;
- uint64_t i = 0;
- while ((p != src_end) && parse_digit(*p, i)) { p++; }
-
- // If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
- // Optimization note: size_t is expected to be unsigned.
- size_t digit_count = size_t(p - start_digits);
- // The longest positive 64-bit number is 20 digits.
- // We do it this way so we don't trigger this branch unless we must.
- // Optimization note: the compiler can probably merge
- // ((digit_count == 0) || (digit_count > 20))
- // into a single branch since digit_count is unsigned.
- if ((digit_count == 0) || (digit_count > 20)) { return INCORRECT_TYPE; }
- // Here digit_count > 0.
- if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
- // We can do the following...
- // if (!jsoncharutils::is_structural_or_whitespace(*p)) {
- // return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
- // }
- // as a single table lookup:
- if ((p != src_end) && integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); }
-
- if (digit_count == 20) {
- // Positive overflow check:
- // - A 20 digit number starting with 2-9 is overflow, because 18,446,744,073,709,551,615 is the
- // biggest uint64_t.
- // - A 20 digit number starting with 1 is overflow if it is less than INT64_MAX.
- // If we got here, it's a 20 digit number starting with the digit "1".
- // - If a 20 digit number starting with 1 overflowed (i*10+digit), the result will be smaller
- // than 1,553,255,926,290,448,384.
- // - That is smaller than the smallest possible 20-digit number the user could write:
- // 10,000,000,000,000,000,000.
- // - Therefore, if the number is positive and lower than that, it's overflow.
- // - The value we are looking at is less than or equal to INT64_MAX.
- //
- if (src[0] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INCORRECT_TYPE; }
- }
-
- return i;
-}
-
// Parse any number from 0 to 18,446,744,073,709,551,615
simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned_in_string(const uint8_t * const src) noexcept {
const uint8_t *p = src + 1;
@@ -31011,50 +30413,6 @@ simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer(const uin
return negative ? (~i+1) : i;
}
-// Parse any number from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
-// Never read at src_end or beyond
-simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer(const uint8_t * const src, const uint8_t * const src_end) noexcept {
- //
- // Check for minus sign
- //
- if(src == src_end) { return NUMBER_ERROR; }
- bool negative = (*src == '-');
- const uint8_t *p = src + uint8_t(negative);
-
- //
- // Parse the integer part.
- //
- // PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
- const uint8_t *const start_digits = p;
- uint64_t i = 0;
- while ((p != src_end) && parse_digit(*p, i)) { p++; }
-
- // If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
- // Optimization note: size_t is expected to be unsigned.
- size_t digit_count = size_t(p - start_digits);
- // We go from
- // -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
- // so we can never represent numbers that have more than 19 digits.
- size_t longest_digit_count = 19;
- // Optimization note: the compiler can probably merge
- // ((digit_count == 0) || (digit_count > longest_digit_count))
- // into a single branch since digit_count is unsigned.
- if ((digit_count == 0) || (digit_count > longest_digit_count)) { return INCORRECT_TYPE; }
- // Here digit_count > 0.
- if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
- // We can do the following...
- // if (!jsoncharutils::is_structural_or_whitespace(*p)) {
- // return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
- // }
- // as a single table lookup:
- if((p != src_end) && integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); }
- // Negative numbers have can go down to - INT64_MAX - 1 whereas positive numbers are limited to INT64_MAX.
- // Performance note: This check is only needed when digit_count == longest_digit_count but it is
- // so cheap that we might as well always make it.
- if(i > uint64_t(INT64_MAX) + uint64_t(negative)) { return INCORRECT_TYPE; }
- return negative ? (~i+1) : i;
-}
-
// Parse any number from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer_in_string(const uint8_t *src) noexcept {
//
@@ -31160,8 +30518,11 @@ simdjson_unused simdjson_inline simdjson_result<double> parse_double(const uint8
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
+ // no exp digits
+ if (p-start_exp_digits == 0) { return NUMBER_ERROR; }
+ // 20+ exp digits (e.g., 0e9999999999999999999999) are valid, but exp may
+ // have wrapped around: leave such numbers to the fallback parser.
+ overflow = overflow || p-start_exp_digits > 19;
exponent += exp_neg ? 0-exp : exp;
}
@@ -31251,8 +30612,11 @@ simdjson_unused simdjson_inline simdjson_result<float> parse_float(const uint8_t
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
+ // no exp digits
+ if (p-start_exp_digits == 0) { return NUMBER_ERROR; }
+ // 20+ exp digits (e.g., 0e9999999999999999999999) are valid, but exp may
+ // have wrapped around: leave such numbers to the fallback parser.
+ overflow = overflow || p-start_exp_digits > 19;
exponent += exp_neg ? 0-exp : exp;
}
@@ -31332,88 +30696,6 @@ simdjson_unused simdjson_inline simdjson_result<number_type> get_number_type(con
return number_type::floating_point_number;
}
-// Never read at src_end or beyond
-simdjson_unused simdjson_inline simdjson_result<double> parse_double(const uint8_t * src, const uint8_t * const src_end) noexcept {
- if(src == src_end) { return NUMBER_ERROR; }
- //
- // Check for minus sign
- //
- bool negative = (*src == '-');
- src += uint8_t(negative);
-
- //
- // Parse the integer part.
- //
- uint64_t i = 0;
- const uint8_t *p = src;
- if(p == src_end) { return NUMBER_ERROR; }
- p += parse_digit(*p, i);
- bool leading_zero = (i == 0);
- while ((p != src_end) && parse_digit(*p, i)) { p++; }
- // no integer digits, or 0123 (zero must be solo)
- if ( p == src ) { return INCORRECT_TYPE; }
- if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; }
-
- //
- // Parse the decimal part.
- //
- int64_t exponent = 0;
- bool overflow;
- if (simdjson_likely((p != src_end) && (*p == '.'))) {
- p++;
- const uint8_t *start_decimal_digits = p;
- if ((p == src_end) || !parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits
- p++;
- while ((p != src_end) && parse_digit(*p, i)) { p++; }
- exponent = -(p - start_decimal_digits);
-
- // Overflow check. More than 19 digits (minus the decimal) may be overflow.
- overflow = p-src-1 > 19;
- if (simdjson_unlikely(overflow && leading_zero)) {
- // Skip leading 0.00000 and see if it still overflows
- const uint8_t *start_digits = src + 2;
- while (*start_digits == '0') { start_digits++; }
- overflow = start_digits-src > 19;
- }
- } else {
- overflow = p-src > 19;
- }
-
- //
- // Parse the exponent
- //
- if ((p != src_end) && (*p == 'e' || *p == 'E')) {
- p++;
- if(p == src_end) { return NUMBER_ERROR; }
- bool exp_neg = *p == '-';
- p += exp_neg || *p == '+';
-
- uint64_t exp = 0;
- const uint8_t *start_exp_digits = p;
- while ((p != src_end) && parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
-
- exponent += exp_neg ? 0-exp : exp;
- }
-
- if ((p != src_end) && jsoncharutils::is_not_structural_or_whitespace(*p)) { return NUMBER_ERROR; }
-
- overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power;
-
- //
- // Assemble (or slow-parse) the float
- //
- double d;
- if (simdjson_likely(!overflow)) {
- if (compute_float_64(exponent, i, negative, d)) { return d; }
- }
- if (!parse_float_fallback(src - uint8_t(negative), src_end, &d)) {
- return NUMBER_ERROR;
- }
- return d;
-}
-
simdjson_unused simdjson_inline simdjson_result<double> parse_double_in_string(const uint8_t * src) noexcept {
//
// Check for minus sign
@@ -31482,8 +30764,11 @@ simdjson_unused simdjson_inline simdjson_result<double> parse_double_in_string(c
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
+ // no exp digits
+ if (p-start_exp_digits == 0) { return NUMBER_ERROR; }
+ // 20+ exp digits (e.g., 0e9999999999999999999999) are valid, but exp may
+ // have wrapped around: leave such numbers to the fallback parser.
+ overflow = overflow || p-start_exp_digits > 19;
exponent += exp_neg ? 0-exp : exp;
}
@@ -31575,8 +30860,11 @@ simdjson_unused simdjson_inline simdjson_result<float> parse_float_in_string(con
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
+ // no exp digits
+ if (p-start_exp_digits == 0) { return NUMBER_ERROR; }
+ // 20+ exp digits (e.g., 0e9999999999999999999999) are valid, but exp may
+ // have wrapped around: leave such numbers to the fallback parser.
+ overflow = overflow || p-start_exp_digits > 19;
exponent += exp_neg ? 0-exp : exp;
}
@@ -32867,30 +32155,6 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin
}
-struct escaping {
- static constexpr uint32_t BYTES_PROCESSED = 16;
- simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
-
- simdjson_inline bool has_escape() { return escape_bits != 0; }
- simdjson_inline int escape_index() { return trailing_zeroes(escape_bits); }
-
- uint64_t escape_bits;
-}; // struct escaping
-
-
-
-simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
- static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes");
- simd8<uint8_t> v(src);
- v.store(dst);
- simd8<bool> is_quote = (v == '"');
- simd8<bool> is_backslash = (v == '\\');
- simd8<bool> is_control = (v < 32);
- return {
- uint64_t((is_backslash | is_quote | is_control).to_bitmask())
- };
-}
-
} // unnamed namespace
} // namespace westmere
} // namespace simdjson
@@ -33281,6 +32545,7 @@ public:
simdjson_warn_unused error_code stage1(const uint8_t *buf, size_t len, stage1_mode partial) noexcept final;
simdjson_warn_unused error_code stage2(dom::document &doc) noexcept final;
simdjson_warn_unused error_code stage2_next(dom::document &doc) noexcept final;
+ stage2_next_function_t stage2_next_function(bool bounded) const noexcept final;
simdjson_warn_unused uint8_t *parse_string(const uint8_t *src, uint8_t *dst, bool allow_replacement) const noexcept final;
simdjson_warn_unused uint8_t *parse_wobbly_string(const uint8_t *src, uint8_t *dst) const noexcept final;
inline simdjson_warn_unused error_code set_capacity(size_t capacity) noexcept final;
@@ -34042,21 +33307,6 @@ static bool parse_float_fallback(const uint8_t *ptr, double *outDouble) {
return !(*outDouble > (std::numeric_limits<double>::max)() || *outDouble < std::numeric_limits<double>::lowest());
}
-static bool parse_float_fallback(const uint8_t *ptr, const uint8_t *end_ptr, double *outDouble) {
- *outDouble = simdjson::internal::from_chars(reinterpret_cast<const char *>(ptr), reinterpret_cast<const char *>(end_ptr));
- // We do not accept infinite values.
-
- // Detecting finite values in a portable manner is ridiculously hard, ideally
- // we would want to do:
- // return !std::isfinite(*outDouble);
- // but that mysteriously fails under legacy/old libc++ libraries, see
- // https://github.com/simdjson/simdjson/issues/1286
- //
- // Therefore, fall back to this solution (the extra parens are there
- // to handle that max may be a macro on windows).
- return !(*outDouble > (std::numeric_limits<double>::max)() || *outDouble < std::numeric_limits<double>::lowest());
-}
-
// Same as parse_float_fallback, but for binary32 (float) values. Going through
// the binary64 fallback and then rounding to binary32 would be subject to
// double rounding, so we run the fallback algorithm directly on binary32.
@@ -34602,57 +33852,6 @@ simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned(const u
return i;
}
-
-
-// Parse any number from 0 to 18,446,744,073,709,551,615
-// Never read at src_end or beyond
-simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned(const uint8_t * const src, const uint8_t * const src_end) noexcept {
- const uint8_t *p = src;
- //
- // Parse the integer part.
- //
- // PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
- const uint8_t *const start_digits = p;
- uint64_t i = 0;
- while ((p != src_end) && parse_digit(*p, i)) { p++; }
-
- // If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
- // Optimization note: size_t is expected to be unsigned.
- size_t digit_count = size_t(p - start_digits);
- // The longest positive 64-bit number is 20 digits.
- // We do it this way so we don't trigger this branch unless we must.
- // Optimization note: the compiler can probably merge
- // ((digit_count == 0) || (digit_count > 20))
- // into a single branch since digit_count is unsigned.
- if ((digit_count == 0) || (digit_count > 20)) { return INCORRECT_TYPE; }
- // Here digit_count > 0.
- if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
- // We can do the following...
- // if (!jsoncharutils::is_structural_or_whitespace(*p)) {
- // return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
- // }
- // as a single table lookup:
- if ((p != src_end) && integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); }
-
- if (digit_count == 20) {
- // Positive overflow check:
- // - A 20 digit number starting with 2-9 is overflow, because 18,446,744,073,709,551,615 is the
- // biggest uint64_t.
- // - A 20 digit number starting with 1 is overflow if it is less than INT64_MAX.
- // If we got here, it's a 20 digit number starting with the digit "1".
- // - If a 20 digit number starting with 1 overflowed (i*10+digit), the result will be smaller
- // than 1,553,255,926,290,448,384.
- // - That is smaller than the smallest possible 20-digit number the user could write:
- // 10,000,000,000,000,000,000.
- // - Therefore, if the number is positive and lower than that, it's overflow.
- // - The value we are looking at is less than or equal to INT64_MAX.
- //
- if (src[0] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INCORRECT_TYPE; }
- }
-
- return i;
-}
-
// Parse any number from 0 to 18,446,744,073,709,551,615
simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned_in_string(const uint8_t * const src) noexcept {
const uint8_t *p = src + 1;
@@ -34745,50 +33944,6 @@ simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer(const uin
return negative ? (~i+1) : i;
}
-// Parse any number from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
-// Never read at src_end or beyond
-simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer(const uint8_t * const src, const uint8_t * const src_end) noexcept {
- //
- // Check for minus sign
- //
- if(src == src_end) { return NUMBER_ERROR; }
- bool negative = (*src == '-');
- const uint8_t *p = src + uint8_t(negative);
-
- //
- // Parse the integer part.
- //
- // PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
- const uint8_t *const start_digits = p;
- uint64_t i = 0;
- while ((p != src_end) && parse_digit(*p, i)) { p++; }
-
- // If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
- // Optimization note: size_t is expected to be unsigned.
- size_t digit_count = size_t(p - start_digits);
- // We go from
- // -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
- // so we can never represent numbers that have more than 19 digits.
- size_t longest_digit_count = 19;
- // Optimization note: the compiler can probably merge
- // ((digit_count == 0) || (digit_count > longest_digit_count))
- // into a single branch since digit_count is unsigned.
- if ((digit_count == 0) || (digit_count > longest_digit_count)) { return INCORRECT_TYPE; }
- // Here digit_count > 0.
- if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
- // We can do the following...
- // if (!jsoncharutils::is_structural_or_whitespace(*p)) {
- // return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
- // }
- // as a single table lookup:
- if((p != src_end) && integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); }
- // Negative numbers have can go down to - INT64_MAX - 1 whereas positive numbers are limited to INT64_MAX.
- // Performance note: This check is only needed when digit_count == longest_digit_count but it is
- // so cheap that we might as well always make it.
- if(i > uint64_t(INT64_MAX) + uint64_t(negative)) { return INCORRECT_TYPE; }
- return negative ? (~i+1) : i;
-}
-
// Parse any number from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer_in_string(const uint8_t *src) noexcept {
//
@@ -34894,8 +34049,11 @@ simdjson_unused simdjson_inline simdjson_result<double> parse_double(const uint8
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
+ // no exp digits
+ if (p-start_exp_digits == 0) { return NUMBER_ERROR; }
+ // 20+ exp digits (e.g., 0e9999999999999999999999) are valid, but exp may
+ // have wrapped around: leave such numbers to the fallback parser.
+ overflow = overflow || p-start_exp_digits > 19;
exponent += exp_neg ? 0-exp : exp;
}
@@ -34985,8 +34143,11 @@ simdjson_unused simdjson_inline simdjson_result<float> parse_float(const uint8_t
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
+ // no exp digits
+ if (p-start_exp_digits == 0) { return NUMBER_ERROR; }
+ // 20+ exp digits (e.g., 0e9999999999999999999999) are valid, but exp may
+ // have wrapped around: leave such numbers to the fallback parser.
+ overflow = overflow || p-start_exp_digits > 19;
exponent += exp_neg ? 0-exp : exp;
}
@@ -35066,88 +34227,6 @@ simdjson_unused simdjson_inline simdjson_result<number_type> get_number_type(con
return number_type::floating_point_number;
}
-// Never read at src_end or beyond
-simdjson_unused simdjson_inline simdjson_result<double> parse_double(const uint8_t * src, const uint8_t * const src_end) noexcept {
- if(src == src_end) { return NUMBER_ERROR; }
- //
- // Check for minus sign
- //
- bool negative = (*src == '-');
- src += uint8_t(negative);
-
- //
- // Parse the integer part.
- //
- uint64_t i = 0;
- const uint8_t *p = src;
- if(p == src_end) { return NUMBER_ERROR; }
- p += parse_digit(*p, i);
- bool leading_zero = (i == 0);
- while ((p != src_end) && parse_digit(*p, i)) { p++; }
- // no integer digits, or 0123 (zero must be solo)
- if ( p == src ) { return INCORRECT_TYPE; }
- if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; }
-
- //
- // Parse the decimal part.
- //
- int64_t exponent = 0;
- bool overflow;
- if (simdjson_likely((p != src_end) && (*p == '.'))) {
- p++;
- const uint8_t *start_decimal_digits = p;
- if ((p == src_end) || !parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits
- p++;
- while ((p != src_end) && parse_digit(*p, i)) { p++; }
- exponent = -(p - start_decimal_digits);
-
- // Overflow check. More than 19 digits (minus the decimal) may be overflow.
- overflow = p-src-1 > 19;
- if (simdjson_unlikely(overflow && leading_zero)) {
- // Skip leading 0.00000 and see if it still overflows
- const uint8_t *start_digits = src + 2;
- while (*start_digits == '0') { start_digits++; }
- overflow = start_digits-src > 19;
- }
- } else {
- overflow = p-src > 19;
- }
-
- //
- // Parse the exponent
- //
- if ((p != src_end) && (*p == 'e' || *p == 'E')) {
- p++;
- if(p == src_end) { return NUMBER_ERROR; }
- bool exp_neg = *p == '-';
- p += exp_neg || *p == '+';
-
- uint64_t exp = 0;
- const uint8_t *start_exp_digits = p;
- while ((p != src_end) && parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
-
- exponent += exp_neg ? 0-exp : exp;
- }
-
- if ((p != src_end) && jsoncharutils::is_not_structural_or_whitespace(*p)) { return NUMBER_ERROR; }
-
- overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power;
-
- //
- // Assemble (or slow-parse) the float
- //
- double d;
- if (simdjson_likely(!overflow)) {
- if (compute_float_64(exponent, i, negative, d)) { return d; }
- }
- if (!parse_float_fallback(src - uint8_t(negative), src_end, &d)) {
- return NUMBER_ERROR;
- }
- return d;
-}
-
simdjson_unused simdjson_inline simdjson_result<double> parse_double_in_string(const uint8_t * src) noexcept {
//
// Check for minus sign
@@ -35216,8 +34295,11 @@ simdjson_unused simdjson_inline simdjson_result<double> parse_double_in_string(c
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
+ // no exp digits
+ if (p-start_exp_digits == 0) { return NUMBER_ERROR; }
+ // 20+ exp digits (e.g., 0e9999999999999999999999) are valid, but exp may
+ // have wrapped around: leave such numbers to the fallback parser.
+ overflow = overflow || p-start_exp_digits > 19;
exponent += exp_neg ? 0-exp : exp;
}
@@ -35309,8 +34391,11 @@ simdjson_unused simdjson_inline simdjson_result<float> parse_float_in_string(con
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
+ // no exp digits
+ if (p-start_exp_digits == 0) { return NUMBER_ERROR; }
+ // 20+ exp digits (e.g., 0e9999999999999999999999) are valid, but exp may
+ // have wrapped around: leave such numbers to the fallback parser.
+ overflow = overflow || p-start_exp_digits > 19;
exponent += exp_neg ? 0-exp : exp;
}
@@ -36113,30 +35198,6 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin
}
-struct escaping {
- static constexpr uint32_t BYTES_PROCESSED = 16;
- simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
-
- simdjson_inline bool has_escape() { return escape_bits != 0; }
- simdjson_inline int escape_index() { return trailing_zeroes(escape_bits); }
-
- uint64_t escape_bits;
-}; // struct escaping
-
-
-
-simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
- static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes");
- simd8<uint8_t> v(src);
- v.store(dst);
- simd8<bool> is_quote = (v == '"');
- simd8<bool> is_backslash = (v == '\\');
- simd8<bool> is_control = (v < 32);
- return {
- static_cast<uint64_t>((is_backslash | is_quote | is_control).to_bitmask())
- };
-}
-
} // unnamed namespace
} // namespace lasx
} // namespace simdjson
@@ -36531,6 +35592,7 @@ public:
simdjson_warn_unused error_code stage1(const uint8_t *buf, size_t len, stage1_mode partial) noexcept final;
simdjson_warn_unused error_code stage2(dom::document &doc) noexcept final;
simdjson_warn_unused error_code stage2_next(dom::document &doc) noexcept final;
+ stage2_next_function_t stage2_next_function(bool bounded) const noexcept final;
simdjson_warn_unused uint8_t *parse_string(const uint8_t *src, uint8_t *dst, bool allow_replacement) const noexcept final;
simdjson_warn_unused uint8_t *parse_wobbly_string(const uint8_t *src, uint8_t *dst) const noexcept final;
inline simdjson_warn_unused error_code set_capacity(size_t capacity) noexcept final;
@@ -37292,21 +36354,6 @@ static bool parse_float_fallback(const uint8_t *ptr, double *outDouble) {
return !(*outDouble > (std::numeric_limits<double>::max)() || *outDouble < std::numeric_limits<double>::lowest());
}
-static bool parse_float_fallback(const uint8_t *ptr, const uint8_t *end_ptr, double *outDouble) {
- *outDouble = simdjson::internal::from_chars(reinterpret_cast<const char *>(ptr), reinterpret_cast<const char *>(end_ptr));
- // We do not accept infinite values.
-
- // Detecting finite values in a portable manner is ridiculously hard, ideally
- // we would want to do:
- // return !std::isfinite(*outDouble);
- // but that mysteriously fails under legacy/old libc++ libraries, see
- // https://github.com/simdjson/simdjson/issues/1286
- //
- // Therefore, fall back to this solution (the extra parens are there
- // to handle that max may be a macro on windows).
- return !(*outDouble > (std::numeric_limits<double>::max)() || *outDouble < std::numeric_limits<double>::lowest());
-}
-
// Same as parse_float_fallback, but for binary32 (float) values. Going through
// the binary64 fallback and then rounding to binary32 would be subject to
// double rounding, so we run the fallback algorithm directly on binary32.
@@ -37852,57 +36899,6 @@ simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned(const u
return i;
}
-
-
-// Parse any number from 0 to 18,446,744,073,709,551,615
-// Never read at src_end or beyond
-simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned(const uint8_t * const src, const uint8_t * const src_end) noexcept {
- const uint8_t *p = src;
- //
- // Parse the integer part.
- //
- // PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
- const uint8_t *const start_digits = p;
- uint64_t i = 0;
- while ((p != src_end) && parse_digit(*p, i)) { p++; }
-
- // If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
- // Optimization note: size_t is expected to be unsigned.
- size_t digit_count = size_t(p - start_digits);
- // The longest positive 64-bit number is 20 digits.
- // We do it this way so we don't trigger this branch unless we must.
- // Optimization note: the compiler can probably merge
- // ((digit_count == 0) || (digit_count > 20))
- // into a single branch since digit_count is unsigned.
- if ((digit_count == 0) || (digit_count > 20)) { return INCORRECT_TYPE; }
- // Here digit_count > 0.
- if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
- // We can do the following...
- // if (!jsoncharutils::is_structural_or_whitespace(*p)) {
- // return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
- // }
- // as a single table lookup:
- if ((p != src_end) && integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); }
-
- if (digit_count == 20) {
- // Positive overflow check:
- // - A 20 digit number starting with 2-9 is overflow, because 18,446,744,073,709,551,615 is the
- // biggest uint64_t.
- // - A 20 digit number starting with 1 is overflow if it is less than INT64_MAX.
- // If we got here, it's a 20 digit number starting with the digit "1".
- // - If a 20 digit number starting with 1 overflowed (i*10+digit), the result will be smaller
- // than 1,553,255,926,290,448,384.
- // - That is smaller than the smallest possible 20-digit number the user could write:
- // 10,000,000,000,000,000,000.
- // - Therefore, if the number is positive and lower than that, it's overflow.
- // - The value we are looking at is less than or equal to INT64_MAX.
- //
- if (src[0] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INCORRECT_TYPE; }
- }
-
- return i;
-}
-
// Parse any number from 0 to 18,446,744,073,709,551,615
simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned_in_string(const uint8_t * const src) noexcept {
const uint8_t *p = src + 1;
@@ -37995,50 +36991,6 @@ simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer(const uin
return negative ? (~i+1) : i;
}
-// Parse any number from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
-// Never read at src_end or beyond
-simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer(const uint8_t * const src, const uint8_t * const src_end) noexcept {
- //
- // Check for minus sign
- //
- if(src == src_end) { return NUMBER_ERROR; }
- bool negative = (*src == '-');
- const uint8_t *p = src + uint8_t(negative);
-
- //
- // Parse the integer part.
- //
- // PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
- const uint8_t *const start_digits = p;
- uint64_t i = 0;
- while ((p != src_end) && parse_digit(*p, i)) { p++; }
-
- // If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
- // Optimization note: size_t is expected to be unsigned.
- size_t digit_count = size_t(p - start_digits);
- // We go from
- // -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
- // so we can never represent numbers that have more than 19 digits.
- size_t longest_digit_count = 19;
- // Optimization note: the compiler can probably merge
- // ((digit_count == 0) || (digit_count > longest_digit_count))
- // into a single branch since digit_count is unsigned.
- if ((digit_count == 0) || (digit_count > longest_digit_count)) { return INCORRECT_TYPE; }
- // Here digit_count > 0.
- if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
- // We can do the following...
- // if (!jsoncharutils::is_structural_or_whitespace(*p)) {
- // return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
- // }
- // as a single table lookup:
- if((p != src_end) && integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); }
- // Negative numbers have can go down to - INT64_MAX - 1 whereas positive numbers are limited to INT64_MAX.
- // Performance note: This check is only needed when digit_count == longest_digit_count but it is
- // so cheap that we might as well always make it.
- if(i > uint64_t(INT64_MAX) + uint64_t(negative)) { return INCORRECT_TYPE; }
- return negative ? (~i+1) : i;
-}
-
// Parse any number from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer_in_string(const uint8_t *src) noexcept {
//
@@ -38144,8 +37096,11 @@ simdjson_unused simdjson_inline simdjson_result<double> parse_double(const uint8
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
+ // no exp digits
+ if (p-start_exp_digits == 0) { return NUMBER_ERROR; }
+ // 20+ exp digits (e.g., 0e9999999999999999999999) are valid, but exp may
+ // have wrapped around: leave such numbers to the fallback parser.
+ overflow = overflow || p-start_exp_digits > 19;
exponent += exp_neg ? 0-exp : exp;
}
@@ -38235,8 +37190,11 @@ simdjson_unused simdjson_inline simdjson_result<float> parse_float(const uint8_t
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
+ // no exp digits
+ if (p-start_exp_digits == 0) { return NUMBER_ERROR; }
+ // 20+ exp digits (e.g., 0e9999999999999999999999) are valid, but exp may
+ // have wrapped around: leave such numbers to the fallback parser.
+ overflow = overflow || p-start_exp_digits > 19;
exponent += exp_neg ? 0-exp : exp;
}
@@ -38316,88 +37274,6 @@ simdjson_unused simdjson_inline simdjson_result<number_type> get_number_type(con
return number_type::floating_point_number;
}
-// Never read at src_end or beyond
-simdjson_unused simdjson_inline simdjson_result<double> parse_double(const uint8_t * src, const uint8_t * const src_end) noexcept {
- if(src == src_end) { return NUMBER_ERROR; }
- //
- // Check for minus sign
- //
- bool negative = (*src == '-');
- src += uint8_t(negative);
-
- //
- // Parse the integer part.
- //
- uint64_t i = 0;
- const uint8_t *p = src;
- if(p == src_end) { return NUMBER_ERROR; }
- p += parse_digit(*p, i);
- bool leading_zero = (i == 0);
- while ((p != src_end) && parse_digit(*p, i)) { p++; }
- // no integer digits, or 0123 (zero must be solo)
- if ( p == src ) { return INCORRECT_TYPE; }
- if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; }
-
- //
- // Parse the decimal part.
- //
- int64_t exponent = 0;
- bool overflow;
- if (simdjson_likely((p != src_end) && (*p == '.'))) {
- p++;
- const uint8_t *start_decimal_digits = p;
- if ((p == src_end) || !parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits
- p++;
- while ((p != src_end) && parse_digit(*p, i)) { p++; }
- exponent = -(p - start_decimal_digits);
-
- // Overflow check. More than 19 digits (minus the decimal) may be overflow.
- overflow = p-src-1 > 19;
- if (simdjson_unlikely(overflow && leading_zero)) {
- // Skip leading 0.00000 and see if it still overflows
- const uint8_t *start_digits = src + 2;
- while (*start_digits == '0') { start_digits++; }
- overflow = start_digits-src > 19;
- }
- } else {
- overflow = p-src > 19;
- }
-
- //
- // Parse the exponent
- //
- if ((p != src_end) && (*p == 'e' || *p == 'E')) {
- p++;
- if(p == src_end) { return NUMBER_ERROR; }
- bool exp_neg = *p == '-';
- p += exp_neg || *p == '+';
-
- uint64_t exp = 0;
- const uint8_t *start_exp_digits = p;
- while ((p != src_end) && parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
-
- exponent += exp_neg ? 0-exp : exp;
- }
-
- if ((p != src_end) && jsoncharutils::is_not_structural_or_whitespace(*p)) { return NUMBER_ERROR; }
-
- overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power;
-
- //
- // Assemble (or slow-parse) the float
- //
- double d;
- if (simdjson_likely(!overflow)) {
- if (compute_float_64(exponent, i, negative, d)) { return d; }
- }
- if (!parse_float_fallback(src - uint8_t(negative), src_end, &d)) {
- return NUMBER_ERROR;
- }
- return d;
-}
-
simdjson_unused simdjson_inline simdjson_result<double> parse_double_in_string(const uint8_t * src) noexcept {
//
// Check for minus sign
@@ -38466,8 +37342,11 @@ simdjson_unused simdjson_inline simdjson_result<double> parse_double_in_string(c
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
+ // no exp digits
+ if (p-start_exp_digits == 0) { return NUMBER_ERROR; }
+ // 20+ exp digits (e.g., 0e9999999999999999999999) are valid, but exp may
+ // have wrapped around: leave such numbers to the fallback parser.
+ overflow = overflow || p-start_exp_digits > 19;
exponent += exp_neg ? 0-exp : exp;
}
@@ -38559,8 +37438,11 @@ simdjson_unused simdjson_inline simdjson_result<float> parse_float_in_string(con
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
+ // no exp digits
+ if (p-start_exp_digits == 0) { return NUMBER_ERROR; }
+ // 20+ exp digits (e.g., 0e9999999999999999999999) are valid, but exp may
+ // have wrapped around: leave such numbers to the fallback parser.
+ overflow = overflow || p-start_exp_digits > 19;
exponent += exp_neg ? 0-exp : exp;
}
@@ -39343,30 +38225,6 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin
}
-struct escaping {
- static constexpr uint32_t BYTES_PROCESSED = 16;
- simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
-
- simdjson_inline bool has_escape() { return escape_bits != 0; }
- simdjson_inline int escape_index() { return trailing_zeroes(escape_bits); }
-
- uint64_t escape_bits;
-}; // struct escaping
-
-
-
-simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
- static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes");
- simd8<uint8_t> v(src);
- v.store(dst);
- simd8<bool> is_quote = (v == '"');
- simd8<bool> is_backslash = (v == '\\');
- simd8<bool> is_control = (v < 32);
- return {
- static_cast<uint64_t>((is_backslash | is_quote | is_control).to_bitmask())
- };
-}
-
} // unnamed namespace
} // namespace lsx
} // namespace simdjson
@@ -39759,6 +38617,7 @@ public:
simdjson_warn_unused error_code stage1(const uint8_t *buf, size_t len, stage1_mode partial) noexcept final;
simdjson_warn_unused error_code stage2(dom::document &doc) noexcept final;
simdjson_warn_unused error_code stage2_next(dom::document &doc) noexcept final;
+ stage2_next_function_t stage2_next_function(bool bounded) const noexcept final;
simdjson_warn_unused uint8_t *parse_string(const uint8_t *src, uint8_t *dst, bool allow_replacement) const noexcept final;
simdjson_warn_unused uint8_t *parse_wobbly_string(const uint8_t *src, uint8_t *dst) const noexcept final;
inline simdjson_warn_unused error_code set_capacity(size_t capacity) noexcept final;
@@ -40520,21 +39379,6 @@ static bool parse_float_fallback(const uint8_t *ptr, double *outDouble) {
return !(*outDouble > (std::numeric_limits<double>::max)() || *outDouble < std::numeric_limits<double>::lowest());
}
-static bool parse_float_fallback(const uint8_t *ptr, const uint8_t *end_ptr, double *outDouble) {
- *outDouble = simdjson::internal::from_chars(reinterpret_cast<const char *>(ptr), reinterpret_cast<const char *>(end_ptr));
- // We do not accept infinite values.
-
- // Detecting finite values in a portable manner is ridiculously hard, ideally
- // we would want to do:
- // return !std::isfinite(*outDouble);
- // but that mysteriously fails under legacy/old libc++ libraries, see
- // https://github.com/simdjson/simdjson/issues/1286
- //
- // Therefore, fall back to this solution (the extra parens are there
- // to handle that max may be a macro on windows).
- return !(*outDouble > (std::numeric_limits<double>::max)() || *outDouble < std::numeric_limits<double>::lowest());
-}
-
// Same as parse_float_fallback, but for binary32 (float) values. Going through
// the binary64 fallback and then rounding to binary32 would be subject to
// double rounding, so we run the fallback algorithm directly on binary32.
@@ -41080,57 +39924,6 @@ simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned(const u
return i;
}
-
-
-// Parse any number from 0 to 18,446,744,073,709,551,615
-// Never read at src_end or beyond
-simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned(const uint8_t * const src, const uint8_t * const src_end) noexcept {
- const uint8_t *p = src;
- //
- // Parse the integer part.
- //
- // PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
- const uint8_t *const start_digits = p;
- uint64_t i = 0;
- while ((p != src_end) && parse_digit(*p, i)) { p++; }
-
- // If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
- // Optimization note: size_t is expected to be unsigned.
- size_t digit_count = size_t(p - start_digits);
- // The longest positive 64-bit number is 20 digits.
- // We do it this way so we don't trigger this branch unless we must.
- // Optimization note: the compiler can probably merge
- // ((digit_count == 0) || (digit_count > 20))
- // into a single branch since digit_count is unsigned.
- if ((digit_count == 0) || (digit_count > 20)) { return INCORRECT_TYPE; }
- // Here digit_count > 0.
- if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
- // We can do the following...
- // if (!jsoncharutils::is_structural_or_whitespace(*p)) {
- // return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
- // }
- // as a single table lookup:
- if ((p != src_end) && integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); }
-
- if (digit_count == 20) {
- // Positive overflow check:
- // - A 20 digit number starting with 2-9 is overflow, because 18,446,744,073,709,551,615 is the
- // biggest uint64_t.
- // - A 20 digit number starting with 1 is overflow if it is less than INT64_MAX.
- // If we got here, it's a 20 digit number starting with the digit "1".
- // - If a 20 digit number starting with 1 overflowed (i*10+digit), the result will be smaller
- // than 1,553,255,926,290,448,384.
- // - That is smaller than the smallest possible 20-digit number the user could write:
- // 10,000,000,000,000,000,000.
- // - Therefore, if the number is positive and lower than that, it's overflow.
- // - The value we are looking at is less than or equal to INT64_MAX.
- //
- if (src[0] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INCORRECT_TYPE; }
- }
-
- return i;
-}
-
// Parse any number from 0 to 18,446,744,073,709,551,615
simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned_in_string(const uint8_t * const src) noexcept {
const uint8_t *p = src + 1;
@@ -41223,50 +40016,6 @@ simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer(const uin
return negative ? (~i+1) : i;
}
-// Parse any number from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
-// Never read at src_end or beyond
-simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer(const uint8_t * const src, const uint8_t * const src_end) noexcept {
- //
- // Check for minus sign
- //
- if(src == src_end) { return NUMBER_ERROR; }
- bool negative = (*src == '-');
- const uint8_t *p = src + uint8_t(negative);
-
- //
- // Parse the integer part.
- //
- // PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
- const uint8_t *const start_digits = p;
- uint64_t i = 0;
- while ((p != src_end) && parse_digit(*p, i)) { p++; }
-
- // If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
- // Optimization note: size_t is expected to be unsigned.
- size_t digit_count = size_t(p - start_digits);
- // We go from
- // -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
- // so we can never represent numbers that have more than 19 digits.
- size_t longest_digit_count = 19;
- // Optimization note: the compiler can probably merge
- // ((digit_count == 0) || (digit_count > longest_digit_count))
- // into a single branch since digit_count is unsigned.
- if ((digit_count == 0) || (digit_count > longest_digit_count)) { return INCORRECT_TYPE; }
- // Here digit_count > 0.
- if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
- // We can do the following...
- // if (!jsoncharutils::is_structural_or_whitespace(*p)) {
- // return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
- // }
- // as a single table lookup:
- if((p != src_end) && integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); }
- // Negative numbers have can go down to - INT64_MAX - 1 whereas positive numbers are limited to INT64_MAX.
- // Performance note: This check is only needed when digit_count == longest_digit_count but it is
- // so cheap that we might as well always make it.
- if(i > uint64_t(INT64_MAX) + uint64_t(negative)) { return INCORRECT_TYPE; }
- return negative ? (~i+1) : i;
-}
-
// Parse any number from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer_in_string(const uint8_t *src) noexcept {
//
@@ -41372,8 +40121,11 @@ simdjson_unused simdjson_inline simdjson_result<double> parse_double(const uint8
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
+ // no exp digits
+ if (p-start_exp_digits == 0) { return NUMBER_ERROR; }
+ // 20+ exp digits (e.g., 0e9999999999999999999999) are valid, but exp may
+ // have wrapped around: leave such numbers to the fallback parser.
+ overflow = overflow || p-start_exp_digits > 19;
exponent += exp_neg ? 0-exp : exp;
}
@@ -41463,8 +40215,11 @@ simdjson_unused simdjson_inline simdjson_result<float> parse_float(const uint8_t
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
+ // no exp digits
+ if (p-start_exp_digits == 0) { return NUMBER_ERROR; }
+ // 20+ exp digits (e.g., 0e9999999999999999999999) are valid, but exp may
+ // have wrapped around: leave such numbers to the fallback parser.
+ overflow = overflow || p-start_exp_digits > 19;
exponent += exp_neg ? 0-exp : exp;
}
@@ -41544,88 +40299,6 @@ simdjson_unused simdjson_inline simdjson_result<number_type> get_number_type(con
return number_type::floating_point_number;
}
-// Never read at src_end or beyond
-simdjson_unused simdjson_inline simdjson_result<double> parse_double(const uint8_t * src, const uint8_t * const src_end) noexcept {
- if(src == src_end) { return NUMBER_ERROR; }
- //
- // Check for minus sign
- //
- bool negative = (*src == '-');
- src += uint8_t(negative);
-
- //
- // Parse the integer part.
- //
- uint64_t i = 0;
- const uint8_t *p = src;
- if(p == src_end) { return NUMBER_ERROR; }
- p += parse_digit(*p, i);
- bool leading_zero = (i == 0);
- while ((p != src_end) && parse_digit(*p, i)) { p++; }
- // no integer digits, or 0123 (zero must be solo)
- if ( p == src ) { return INCORRECT_TYPE; }
- if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; }
-
- //
- // Parse the decimal part.
- //
- int64_t exponent = 0;
- bool overflow;
- if (simdjson_likely((p != src_end) && (*p == '.'))) {
- p++;
- const uint8_t *start_decimal_digits = p;
- if ((p == src_end) || !parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits
- p++;
- while ((p != src_end) && parse_digit(*p, i)) { p++; }
- exponent = -(p - start_decimal_digits);
-
- // Overflow check. More than 19 digits (minus the decimal) may be overflow.
- overflow = p-src-1 > 19;
- if (simdjson_unlikely(overflow && leading_zero)) {
- // Skip leading 0.00000 and see if it still overflows
- const uint8_t *start_digits = src + 2;
- while (*start_digits == '0') { start_digits++; }
- overflow = start_digits-src > 19;
- }
- } else {
- overflow = p-src > 19;
- }
-
- //
- // Parse the exponent
- //
- if ((p != src_end) && (*p == 'e' || *p == 'E')) {
- p++;
- if(p == src_end) { return NUMBER_ERROR; }
- bool exp_neg = *p == '-';
- p += exp_neg || *p == '+';
-
- uint64_t exp = 0;
- const uint8_t *start_exp_digits = p;
- while ((p != src_end) && parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
-
- exponent += exp_neg ? 0-exp : exp;
- }
-
- if ((p != src_end) && jsoncharutils::is_not_structural_or_whitespace(*p)) { return NUMBER_ERROR; }
-
- overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power;
-
- //
- // Assemble (or slow-parse) the float
- //
- double d;
- if (simdjson_likely(!overflow)) {
- if (compute_float_64(exponent, i, negative, d)) { return d; }
- }
- if (!parse_float_fallback(src - uint8_t(negative), src_end, &d)) {
- return NUMBER_ERROR;
- }
- return d;
-}
-
simdjson_unused simdjson_inline simdjson_result<double> parse_double_in_string(const uint8_t * src) noexcept {
//
// Check for minus sign
@@ -41694,8 +40367,11 @@ simdjson_unused simdjson_inline simdjson_result<double> parse_double_in_string(c
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
+ // no exp digits
+ if (p-start_exp_digits == 0) { return NUMBER_ERROR; }
+ // 20+ exp digits (e.g., 0e9999999999999999999999) are valid, but exp may
+ // have wrapped around: leave such numbers to the fallback parser.
+ overflow = overflow || p-start_exp_digits > 19;
exponent += exp_neg ? 0-exp : exp;
}
@@ -41787,8 +40463,11 @@ simdjson_unused simdjson_inline simdjson_result<float> parse_float_in_string(con
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
+ // no exp digits
+ if (p-start_exp_digits == 0) { return NUMBER_ERROR; }
+ // 20+ exp digits (e.g., 0e9999999999999999999999) are valid, but exp may
+ // have wrapped around: leave such numbers to the fallback parser.
+ overflow = overflow || p-start_exp_digits > 19;
exponent += exp_neg ? 0-exp : exp;
}
@@ -42534,23 +41213,6 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin
return { (v == '\\').to_bitmask(), (v == '"').to_bitmask() };
}
-struct escaping {
- static constexpr uint64_t BYTES_PROCESSED = sizeof(simd8<uint8_t>);
- simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
-
- simdjson_inline bool has_escape() { return escape_bits != 0; }
- simdjson_inline int escape_index() { return trailing_zeroes(escape_bits) / 4; }
-
- uint64_t escape_bits;
-}; // struct escaping
-
-simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
- static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes");
- simd8<uint8_t> v(src);
- v.store(dst);
- return { ((v == '"') | (v == '\\') | (v == 32)).to_bitmask() };
-}
-
} // unnamed namespace
} // namespace rvv_vls
@@ -43003,6 +41665,7 @@ public:
simdjson_warn_unused error_code stage1(const uint8_t *buf, size_t len, stage1_mode partial) noexcept final;
simdjson_warn_unused error_code stage2(dom::document &doc) noexcept final;
simdjson_warn_unused error_code stage2_next(dom::document &doc) noexcept final;
+ stage2_next_function_t stage2_next_function(bool bounded) const noexcept final;
simdjson_warn_unused uint8_t *parse_string(const uint8_t *src, uint8_t *dst, bool allow_replacement) const noexcept final;
simdjson_warn_unused uint8_t *parse_wobbly_string(const uint8_t *src, uint8_t *dst) const noexcept final;
inline simdjson_warn_unused error_code set_capacity(size_t capacity) noexcept final;
@@ -43764,21 +42427,6 @@ static bool parse_float_fallback(const uint8_t *ptr, double *outDouble) {
return !(*outDouble > (std::numeric_limits<double>::max)() || *outDouble < std::numeric_limits<double>::lowest());
}
-static bool parse_float_fallback(const uint8_t *ptr, const uint8_t *end_ptr, double *outDouble) {
- *outDouble = simdjson::internal::from_chars(reinterpret_cast<const char *>(ptr), reinterpret_cast<const char *>(end_ptr));
- // We do not accept infinite values.
-
- // Detecting finite values in a portable manner is ridiculously hard, ideally
- // we would want to do:
- // return !std::isfinite(*outDouble);
- // but that mysteriously fails under legacy/old libc++ libraries, see
- // https://github.com/simdjson/simdjson/issues/1286
- //
- // Therefore, fall back to this solution (the extra parens are there
- // to handle that max may be a macro on windows).
- return !(*outDouble > (std::numeric_limits<double>::max)() || *outDouble < std::numeric_limits<double>::lowest());
-}
-
// Same as parse_float_fallback, but for binary32 (float) values. Going through
// the binary64 fallback and then rounding to binary32 would be subject to
// double rounding, so we run the fallback algorithm directly on binary32.
@@ -44324,57 +42972,6 @@ simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned(const u
return i;
}
-
-
-// Parse any number from 0 to 18,446,744,073,709,551,615
-// Never read at src_end or beyond
-simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned(const uint8_t * const src, const uint8_t * const src_end) noexcept {
- const uint8_t *p = src;
- //
- // Parse the integer part.
- //
- // PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
- const uint8_t *const start_digits = p;
- uint64_t i = 0;
- while ((p != src_end) && parse_digit(*p, i)) { p++; }
-
- // If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
- // Optimization note: size_t is expected to be unsigned.
- size_t digit_count = size_t(p - start_digits);
- // The longest positive 64-bit number is 20 digits.
- // We do it this way so we don't trigger this branch unless we must.
- // Optimization note: the compiler can probably merge
- // ((digit_count == 0) || (digit_count > 20))
- // into a single branch since digit_count is unsigned.
- if ((digit_count == 0) || (digit_count > 20)) { return INCORRECT_TYPE; }
- // Here digit_count > 0.
- if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
- // We can do the following...
- // if (!jsoncharutils::is_structural_or_whitespace(*p)) {
- // return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
- // }
- // as a single table lookup:
- if ((p != src_end) && integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); }
-
- if (digit_count == 20) {
- // Positive overflow check:
- // - A 20 digit number starting with 2-9 is overflow, because 18,446,744,073,709,551,615 is the
- // biggest uint64_t.
- // - A 20 digit number starting with 1 is overflow if it is less than INT64_MAX.
- // If we got here, it's a 20 digit number starting with the digit "1".
- // - If a 20 digit number starting with 1 overflowed (i*10+digit), the result will be smaller
- // than 1,553,255,926,290,448,384.
- // - That is smaller than the smallest possible 20-digit number the user could write:
- // 10,000,000,000,000,000,000.
- // - Therefore, if the number is positive and lower than that, it's overflow.
- // - The value we are looking at is less than or equal to INT64_MAX.
- //
- if (src[0] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INCORRECT_TYPE; }
- }
-
- return i;
-}
-
// Parse any number from 0 to 18,446,744,073,709,551,615
simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned_in_string(const uint8_t * const src) noexcept {
const uint8_t *p = src + 1;
@@ -44467,50 +43064,6 @@ simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer(const uin
return negative ? (~i+1) : i;
}
-// Parse any number from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
-// Never read at src_end or beyond
-simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer(const uint8_t * const src, const uint8_t * const src_end) noexcept {
- //
- // Check for minus sign
- //
- if(src == src_end) { return NUMBER_ERROR; }
- bool negative = (*src == '-');
- const uint8_t *p = src + uint8_t(negative);
-
- //
- // Parse the integer part.
- //
- // PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
- const uint8_t *const start_digits = p;
- uint64_t i = 0;
- while ((p != src_end) && parse_digit(*p, i)) { p++; }
-
- // If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
- // Optimization note: size_t is expected to be unsigned.
- size_t digit_count = size_t(p - start_digits);
- // We go from
- // -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
- // so we can never represent numbers that have more than 19 digits.
- size_t longest_digit_count = 19;
- // Optimization note: the compiler can probably merge
- // ((digit_count == 0) || (digit_count > longest_digit_count))
- // into a single branch since digit_count is unsigned.
- if ((digit_count == 0) || (digit_count > longest_digit_count)) { return INCORRECT_TYPE; }
- // Here digit_count > 0.
- if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
- // We can do the following...
- // if (!jsoncharutils::is_structural_or_whitespace(*p)) {
- // return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
- // }
- // as a single table lookup:
- if((p != src_end) && integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); }
- // Negative numbers have can go down to - INT64_MAX - 1 whereas positive numbers are limited to INT64_MAX.
- // Performance note: This check is only needed when digit_count == longest_digit_count but it is
- // so cheap that we might as well always make it.
- if(i > uint64_t(INT64_MAX) + uint64_t(negative)) { return INCORRECT_TYPE; }
- return negative ? (~i+1) : i;
-}
-
// Parse any number from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer_in_string(const uint8_t *src) noexcept {
//
@@ -44616,8 +43169,11 @@ simdjson_unused simdjson_inline simdjson_result<double> parse_double(const uint8
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
+ // no exp digits
+ if (p-start_exp_digits == 0) { return NUMBER_ERROR; }
+ // 20+ exp digits (e.g., 0e9999999999999999999999) are valid, but exp may
+ // have wrapped around: leave such numbers to the fallback parser.
+ overflow = overflow || p-start_exp_digits > 19;
exponent += exp_neg ? 0-exp : exp;
}
@@ -44707,8 +43263,11 @@ simdjson_unused simdjson_inline simdjson_result<float> parse_float(const uint8_t
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
+ // no exp digits
+ if (p-start_exp_digits == 0) { return NUMBER_ERROR; }
+ // 20+ exp digits (e.g., 0e9999999999999999999999) are valid, but exp may
+ // have wrapped around: leave such numbers to the fallback parser.
+ overflow = overflow || p-start_exp_digits > 19;
exponent += exp_neg ? 0-exp : exp;
}
@@ -44788,88 +43347,6 @@ simdjson_unused simdjson_inline simdjson_result<number_type> get_number_type(con
return number_type::floating_point_number;
}
-// Never read at src_end or beyond
-simdjson_unused simdjson_inline simdjson_result<double> parse_double(const uint8_t * src, const uint8_t * const src_end) noexcept {
- if(src == src_end) { return NUMBER_ERROR; }
- //
- // Check for minus sign
- //
- bool negative = (*src == '-');
- src += uint8_t(negative);
-
- //
- // Parse the integer part.
- //
- uint64_t i = 0;
- const uint8_t *p = src;
- if(p == src_end) { return NUMBER_ERROR; }
- p += parse_digit(*p, i);
- bool leading_zero = (i == 0);
- while ((p != src_end) && parse_digit(*p, i)) { p++; }
- // no integer digits, or 0123 (zero must be solo)
- if ( p == src ) { return INCORRECT_TYPE; }
- if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; }
-
- //
- // Parse the decimal part.
- //
- int64_t exponent = 0;
- bool overflow;
- if (simdjson_likely((p != src_end) && (*p == '.'))) {
- p++;
- const uint8_t *start_decimal_digits = p;
- if ((p == src_end) || !parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits
- p++;
- while ((p != src_end) && parse_digit(*p, i)) { p++; }
- exponent = -(p - start_decimal_digits);
-
- // Overflow check. More than 19 digits (minus the decimal) may be overflow.
- overflow = p-src-1 > 19;
- if (simdjson_unlikely(overflow && leading_zero)) {
- // Skip leading 0.00000 and see if it still overflows
- const uint8_t *start_digits = src + 2;
- while (*start_digits == '0') { start_digits++; }
- overflow = start_digits-src > 19;
- }
- } else {
- overflow = p-src > 19;
- }
-
- //
- // Parse the exponent
- //
- if ((p != src_end) && (*p == 'e' || *p == 'E')) {
- p++;
- if(p == src_end) { return NUMBER_ERROR; }
- bool exp_neg = *p == '-';
- p += exp_neg || *p == '+';
-
- uint64_t exp = 0;
- const uint8_t *start_exp_digits = p;
- while ((p != src_end) && parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
-
- exponent += exp_neg ? 0-exp : exp;
- }
-
- if ((p != src_end) && jsoncharutils::is_not_structural_or_whitespace(*p)) { return NUMBER_ERROR; }
-
- overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power;
-
- //
- // Assemble (or slow-parse) the float
- //
- double d;
- if (simdjson_likely(!overflow)) {
- if (compute_float_64(exponent, i, negative, d)) { return d; }
- }
- if (!parse_float_fallback(src - uint8_t(negative), src_end, &d)) {
- return NUMBER_ERROR;
- }
- return d;
-}
-
simdjson_unused simdjson_inline simdjson_result<double> parse_double_in_string(const uint8_t * src) noexcept {
//
// Check for minus sign
@@ -44938,8 +43415,11 @@ simdjson_unused simdjson_inline simdjson_result<double> parse_double_in_string(c
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
+ // no exp digits
+ if (p-start_exp_digits == 0) { return NUMBER_ERROR; }
+ // 20+ exp digits (e.g., 0e9999999999999999999999) are valid, but exp may
+ // have wrapped around: leave such numbers to the fallback parser.
+ overflow = overflow || p-start_exp_digits > 19;
exponent += exp_neg ? 0-exp : exp;
}
@@ -45031,8 +43511,11 @@ simdjson_unused simdjson_inline simdjson_result<float> parse_float_in_string(con
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
- // no exp digits, or 20+ exp digits
- if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
+ // no exp digits
+ if (p-start_exp_digits == 0) { return NUMBER_ERROR; }
+ // 20+ exp digits (e.g., 0e9999999999999999999999) are valid, but exp may
+ // have wrapped around: leave such numbers to the fallback parser.
+ overflow = overflow || p-start_exp_digits > 19;
exponent += exp_neg ? 0-exp : exp;
}
@@ -45606,6 +44089,7 @@ consteval const char* get_json_key_name() {
#endif // SIMDJSON_STATIC_REFLECTION
#endif // SIMDJSON_ANNOTATIONS_H
/* end file simdjson/annotations.h */
+/* skipped duplicate #include "simdjson/internal/jeaiii.h" */
#endif // SIMDJSON_GENERIC_BUILDER_DEPENDENCIES_H
/* end file simdjson/generic/builder/dependencies.h */
@@ -46457,31 +44941,6 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin
};
}
-struct escaping {
- static constexpr uint32_t BYTES_PROCESSED = 16;
- simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
-
- simdjson_inline bool has_escape() { return escape_bits != 0; }
- simdjson_inline int escape_index() { return trailing_zeroes(escape_bits) / 4; }
-
- uint64_t escape_bits;
-}; // struct escaping
-
-
-
-simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
- static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes");
- simd8<uint8_t> v(src);
- v.store(dst);
- simd8<bool> is_quote = (v == '"');
- simd8<bool> is_backslash = (v == '\\');
- simd8<bool> is_control = (v < 32);
- return {
- (is_backslash | is_quote | is_control).to_bitmask64()
- };
-}
-
-
} // unnamed namespace
} // namespace arm64
@@ -46553,6 +45012,10 @@ namespace builder {
class string_builder {
public:
simdjson_inline string_builder(size_t initial_capacity = DEFAULT_INITIAL_CAPACITY);
+ string_builder(const string_builder &) = delete;
+ string_builder &operator=(const string_builder &) = delete;
+ string_builder(string_builder &&) noexcept = default;
+ string_builder &operator=(string_builder &&) noexcept = default;
static constexpr size_t DEFAULT_INITIAL_CAPACITY = 1024;
@@ -46566,6 +45029,18 @@ public:
typename = typename std::enable_if<std::is_arithmetic<number_type>::value>::type>
simdjson_inline void append(number_type v) noexcept;
+#ifdef __SIZEOF_INT128__
+ /**
+ * Integers wider than 64 bits are not supported. Without these overloads, a
+ * 128-bit integer would either be truncated to 64 bits by append(number_type)
+ * (when the standard library counts it as arithmetic, as libc++ does) or be
+ * converted to char by append(char) (when it does not, as libstdc++ does in
+ * strict ISO mode).
+ */
+ void append(__int128_t v) noexcept = delete;
+ void append(__uint128_t v) noexcept = delete;
+#endif
+
/**
* Append character c.
*/
@@ -46744,6 +45219,20 @@ requires (!std::is_convertible<R, std::string_view>::value && !concepts::optiona
*/
simdjson_inline simdjson_result<const char *> c_str() noexcept;
+ /**
+ * Moves the written JSON buffer into out, replacing its content, without
+ * copying it. When the content fills at most half of the buffer, it is
+ * copied instead, so that a small result does not keep the whole buffer
+ * allocated. The builder is left empty: it keeps its buffer when the
+ * content was copied, and gives up its memory when the buffer was moved.
+ * Returns an error, and leaves out and the builder unchanged, if memory
+ * allocation failed.
+ *
+ * The result may not be valid UTF-8 if some of your content was not valid UTF-8.
+ * Use validate_unicode() to check the content.
+ */
+ simdjson_warn_unused inline error_code move_to(std::string &out) noexcept;
+
/**
* Return true if the content is valid UTF-8.
*/
@@ -46763,7 +45252,7 @@ requires (!std::is_convertible<R, std::string_view>::value && !concepts::optiona
// forces those reloads when accessed via members of *this). User
// code should NOT call these directly.
// ============================================================
- simdjson_inline char *unsafe_data() noexcept { return buffer.get(); }
+ simdjson_inline char *unsafe_data() noexcept { return buffer_data(); }
simdjson_inline size_t unsafe_position() const noexcept { return position; }
simdjson_inline size_t unsafe_capacity() const noexcept { return capacity; }
simdjson_inline void unsafe_set_position(size_t p) noexcept { position = p; }
@@ -46804,7 +45293,13 @@ private:
*/
simdjson_inline void set_valid(bool valid) noexcept;
- std::unique_ptr<char[]> buffer{};
+ simdjson_inline char *buffer_data() noexcept { return &buffer[0]; }
+ simdjson_inline const char *buffer_data() const noexcept { return buffer.data(); }
+
+ // We own capacity bytes: buffer.size() == capacity. Only the first position
+ // bytes hold JSON. A std::string lets move_to() hand the result over
+ // without a copy.
+ std::string buffer{};
size_t position{0};
size_t capacity{0};
bool is_valid{true};
@@ -46822,20 +45317,16 @@ template <class Z>
simdjson_warn_unused simdjson_result<std::string> to_json(const Z &z, size_t initial_capacity = simdjson::arm64::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
simdjson::arm64::builder::string_builder b(initial_capacity);
b.append(z);
- std::string_view s;
- auto e = b.view().get(s);
+ std::string s;
+ auto e = b.move_to(s);
if(e) { return e; }
- return std::string(s);
+ return s;
}
template <class Z>
simdjson_warn_unused error_code to_json(const Z &z, std::string &s, size_t initial_capacity = simdjson::arm64::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
simdjson::arm64::builder::string_builder b(initial_capacity);
b.append(z);
- std::string_view sv;
- auto e = b.view().get(sv);
- if(e) { return e; }
- s.assign(sv.data(), sv.size());
- return simdjson::SUCCESS;
+ return b.move_to(s);
}
#endif
@@ -46851,7 +45342,7 @@ simdjson_warn_unused error_code to_json(const Z &z, std::string &s, size_t initi
#ifndef SIMDJSON_GENERIC_BUILDER_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
-/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_STRING_BUILDER_H */
+/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_BUILDER_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/builder/json_string_builder.h" */
/* amalgamation skipped (editor-only): #include "simdjson/concepts.h" */
/* amalgamation skipped (editor-only): #include "simdjson/annotations.h" */
@@ -46877,7 +45368,7 @@ namespace builder {
// included at the point this header is parsed; without these forwards,
// name lookup falls back to the wrong outer namespace).
namespace internal {
-simdjson_really_inline char *write_uint_jeaiii(char *p, uint64_t v) noexcept;
+using simdjson::internal::write_uint_jeaiii;
simdjson_inline char *write_double(char *p, double v) noexcept;
} // namespace internal
simdjson_really_inline size_t write_string_escaped(const std::string_view input, char *out);
@@ -47010,6 +45501,51 @@ consteval bool is_serialized_member(std::meta::info dm) {
&& !simdjson::detail::has_annotation(dm, ^^simdjson::detail::skip_serializing_tag);
}
+template <class Target, class T, class... Visiting>
+consteval bool members_reach();
+
+// Whether a value of type T may hold a value of type Target, directly or
+// through optionals, smart pointers, containers and members. Visiting lists
+// the structures being searched, so that the search terminates.
+template <class Target, class T, class... Visiting>
+consteval bool reaches() {
+ if constexpr (std::is_same_v<T, Target>) {
+ return true;
+ } else if constexpr ((std::is_same_v<T, Visiting> || ...) || require_custom_serialization<T> ||
+ std::is_same_v<T, std::string> || std::is_same_v<T, std::string_view>) {
+ return false;
+ } else if constexpr (concepts::optional_type<T> || concepts::smart_pointer<T>) {
+ return reaches<Target, std::remove_cvref_t<decltype(*std::declval<const T &>())>, Visiting...>();
+ } else if constexpr (concepts::string_view_keyed_map<T>) {
+ return reaches<Target, std::remove_cvref_t<typename T::mapped_type>, Visiting...>();
+ } else if constexpr (concepts::container_but_not_string<T> || concepts::appendable_containers<T>) {
+ return reaches<Target, std::remove_cvref_t<std::ranges::range_value_t<T>>, Visiting...>();
+ } else if constexpr (std::is_class_v<T>) {
+ return members_reach<Target, T, Visiting...>();
+ } else {
+ return false;
+ }
+}
+
+template <class Target, class T, class... Visiting>
+consteval bool members_reach() {
+ bool found = false;
+ template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) {
+ if constexpr (is_serialized_member(dm)) {
+ found = found || reaches<Target, std::remove_cvref_t<decltype(std::declval<const T &>().[:dm:])>, T, Visiting...>();
+ }
+ };
+ return found;
+}
+
+// Whether the structure T may contain itself, as in
+// struct Rule { std::unique_ptr<Rule> subrule; }. Serializing such a type
+// recurses at runtime, which simdjson_really_inline functions cannot do.
+template <class T>
+consteval bool is_recursive() {
+ return members_reach<T, T>();
+}
+
// False when the member has a skip_serializing_if<pred> annotation and
// pred(value) is true.
template <auto dm, typename V>
@@ -47193,6 +45729,8 @@ simdjson_really_inline constexpr void atom(W &w, const T &m) {
template<class W, typename number_type,
typename = typename std::enable_if<std::is_arithmetic<number_type>::value && !std::is_same_v<number_type, char>>::type>
simdjson_really_inline constexpr void atom(W &w, const number_type t) {
+ static_assert(sizeof(number_type) <= sizeof(uint64_t),
+ "Integers wider than 64 bits are not supported");
// Booleans / floats: defer to string_builder (rare path; keeps writer hot
// path free of float-formatter machinery). For integers, write directly
// via jeaiii using local pos.
@@ -47231,6 +45769,31 @@ simdjson_really_inline constexpr void atom(W &w, const number_type t) {
}
}
+namespace annotation_detail {
+
+template <class W, class T>
+simdjson_really_inline void atom_object(W &w, const T &t) {
+ if constexpr (simdjson::detail::has_annotation(^^T, ^^simdjson::detail::transparent_tag)) {
+ // A transparent structure is serialized as its single member.
+ constexpr auto dm = simdjson::detail::transparent_member(^^T);
+ atom_member<dm>(w, t.[:dm:]);
+ } else {
+ bool first = true;
+ if (!w.ensure(1)) { return; }
+ w.ptr[w.pos++] = '{';
+ atom_fields(w, t, first);
+ if (!w.ensure(1)) { return; }
+ w.ptr[w.pos++] = '}';
+ }
+}
+
+template <class W, class T>
+void atom_object_outlined(W &w, const T &t) {
+ atom_object(w, t);
+}
+
+} // namespace annotation_detail
+
template <class W, class T>
requires(std::is_class_v<T> && !concepts::container_but_not_string<T> &&
!concepts::string_view_keyed_map<T> &&
@@ -47242,17 +45805,11 @@ template <class W, class T>
!std::is_same_v<T, const char*> &&
!std::is_same_v<T, char> && !require_custom_serialization<T>)
simdjson_really_inline constexpr void atom(W &w, const T &t) {
- if constexpr (simdjson::detail::has_annotation(^^T, ^^simdjson::detail::transparent_tag)) {
- // A transparent structure is serialized as its single member.
- constexpr auto dm = simdjson::detail::transparent_member(^^T);
- annotation_detail::atom_member<dm>(w, t.[:dm:]);
+ if constexpr (annotation_detail::is_recursive<T>()) {
+ // Break the cycle of simdjson_really_inline calls.
+ annotation_detail::atom_object_outlined(w, t);
} else {
- bool first = true;
- if (!w.ensure(1)) { return; }
- w.ptr[w.pos++] = '{';
- annotation_detail::atom_fields(w, t, first);
- if (!w.ensure(1)) { return; }
- w.ptr[w.pos++] = '}';
+ annotation_detail::atom_object(w, t);
}
}
@@ -47352,25 +45909,30 @@ namespace bound_detail {
// Whether size_bound covers everything that atom() writes for T: not when a
// member is serialized by a with<Adapter> serializer, which writes an unknown
// amount through the string_builder.
-template <class T>
+// Visiting lists the structures being checked, so that a recursive type
+// (struct Node { std::unique_ptr<Node> next; }) does not recurse forever: a
+// type already being visited adds nothing, its members get checked once.
+template <class T, class... Visiting>
consteval bool is_bounded() {
- if constexpr (require_custom_serialization<T>) {
+ if constexpr ((std::is_same_v<T, Visiting> || ...)) {
+ return true;
+ } else if constexpr (require_custom_serialization<T>) {
return false;
} else if constexpr (std::is_same_v<T, std::string> || std::is_same_v<T, std::string_view> ||
std::is_same_v<T, const char *> || std::is_arithmetic_v<T> || std::is_enum_v<T>) {
return true;
} else if constexpr (concepts::optional_type<T> || concepts::smart_pointer<T>) {
- return is_bounded<std::remove_cvref_t<decltype(*std::declval<const T &>())>>();
+ return is_bounded<std::remove_cvref_t<decltype(*std::declval<const T &>())>, Visiting...>();
} else if constexpr (concepts::string_view_keyed_map<T>) {
- return is_bounded<std::remove_cvref_t<typename T::mapped_type>>();
+ return is_bounded<std::remove_cvref_t<typename T::mapped_type>, Visiting...>();
} else if constexpr (concepts::container_but_not_string<T> || concepts::appendable_containers<T>) {
- return is_bounded<std::remove_cvref_t<std::ranges::range_value_t<T>>>();
+ return is_bounded<std::remove_cvref_t<std::ranges::range_value_t<T>>, Visiting...>();
} else {
bool bounded = true;
template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) {
if constexpr (annotation_detail::is_serialized_member(dm)) {
bounded = bounded && simdjson::detail::annotation_of_template(dm, ^^simdjson::detail::with_t) == std::meta::info{} &&
- is_bounded<std::remove_cvref_t<decltype(std::declval<const T &>().[:dm:])>>();
+ is_bounded<std::remove_cvref_t<decltype(std::declval<const T &>().[:dm:])>, T, Visiting...>();
}
};
return bounded;
@@ -47408,6 +45970,11 @@ simdjson_really_inline size_t fields_bound(const T &t) noexcept {
return bound;
}
+template <class T>
+size_t fields_bound_outlined(const T &t) noexcept {
+ return fields_bound(t);
+}
+
template <class T>
simdjson_really_inline size_t size_bound([[maybe_unused]] const T &t) noexcept {
if constexpr (std::is_same_v<T, char>) {
@@ -47452,6 +46019,9 @@ simdjson_really_inline size_t size_bound([[maybe_unused]] const T &t) noexcept {
} else if constexpr (simdjson::detail::has_annotation(^^T, ^^simdjson::detail::transparent_tag)) {
constexpr auto dm = simdjson::detail::transparent_member(^^T);
return size_bound(t.[:dm:]);
+ } else if constexpr (annotation_detail::is_recursive<T>()) {
+ // Break the cycle of simdjson_really_inline calls.
+ return 2 + fields_bound_outlined(t);
} else {
return 2 + fields_bound(t);
}
@@ -47586,10 +46156,7 @@ simdjson_warn_unused error_code to_json(const Z &z, std::string &s, size_t initi
} else {
string_builder b(initial_capacity);
append(b, z);
- std::string_view view;
- if(auto e = b.view().get(view); e) { return e; }
- s.assign(view);
- return SUCCESS;
+ return b.move_to(s);
}
}
@@ -47652,9 +46219,9 @@ template<constevalutil::fixed_string... FieldNames, typename T>
simdjson_warn_unused simdjson_result<std::string> extract_from(const T &obj, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) {
string_builder b(initial_capacity);
extract_from<FieldNames...>(b, obj);
- std::string_view s;
- if(auto e = b.view().get(s); e) { return e; }
- return std::string(s);
+ std::string s;
+ if(auto e = b.move_to(s); e) { return e; }
+ return s;
}
SIMDJSON_POP_DISABLE_WARNINGS
@@ -47676,16 +46243,16 @@ template<constevalutil::fixed_string... FieldNames, typename T>
simdjson_warn_unused simdjson_result<std::string> extract_from(const T &obj, size_t initial_capacity = arm64::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
arm64::builder::string_builder b(initial_capacity);
arm64::builder::extract_from<FieldNames...>(b, obj);
- std::string_view s;
- if(auto e = b.view().get(s); e) { return e; }
- return std::string(s);
+ std::string s;
+ if(auto e = b.move_to(s); e) { return e; }
+ return s;
}
} // namespace simdjson
#endif // SIMDJSON_STATIC_REFLECTION
-#endif
+#endif // SIMDJSON_GENERIC_BUILDER_H
/* end file simdjson/generic/builder/json_builder.h for arm64 */
/* including simdjson/generic/builder/fractured_json_builder.h for arm64: #include "simdjson/generic/builder/fractured_json_builder.h" */
/* begin file simdjson/generic/builder/fractured_json_builder.h for arm64 */
@@ -47824,6 +46391,7 @@ simdjson_warn_unused simdjson_result<std::string> extract_fractured_json(
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_STRING_BUILDER_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/builder/json_string_builder.h" */
+/* amalgamation skipped (editor-only): #include "simdjson/internal/jeaiii.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
/*
@@ -47936,6 +46504,12 @@ inline bool has_json_escapable_byte(uint64_t x) {
**/
+#if SIMDJSON_EXPERIMENTAL_HAS_SSE2 || SIMDJSON_EXPERIMENTAL_HAS_NEON
+#define SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE 1
+#endif
+
+// With block escaping (SSE2/NEON), write_string_escaped scans the input itself.
+#if !SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE
// Scalar fallback for finding next quotable character
SIMDJSON_CONSTEXPR_LAMBDA simdjson_inline size_t
find_next_json_quotable_character_scalar(const std::string_view view,
@@ -47951,82 +46525,7 @@ find_next_json_quotable_character_scalar(const std::string_view view,
// SIMD-accelerated position finding that directly locates the first quotable
// character, combining detection and position extraction in a single pass to
// minimize redundant work.
-#if SIMDJSON_EXPERIMENTAL_HAS_NEON
-simdjson_inline size_t
-find_next_json_quotable_character(const std::string_view view,
- size_t location) noexcept {
- const size_t len = view.size();
- const uint8_t *ptr =
- reinterpret_cast<const uint8_t *>(view.data()) + location;
- size_t remaining = len - location;
-
- // SIMD constants for characters requiring escape
- uint8x16_t v34 = vdupq_n_u8(34); // '"'
- uint8x16_t v92 = vdupq_n_u8(92); // '\\'
- uint8x16_t v32 = vdupq_n_u8(32); // control char threshold
-
- while (remaining >= 16) {
- uint8x16_t word = vld1q_u8(ptr);
-
- // Check for quotable characters: '"', '\\', or control chars (< 32)
- uint8x16_t needs_escape = vceqq_u8(word, v34);
- needs_escape = vorrq_u8(needs_escape, vceqq_u8(word, v92));
- needs_escape = vorrq_u8(needs_escape, vcltq_u8(word, v32));
-
- const uint8x8_t res = vshrn_n_u16(vreinterpretq_u16_u8(needs_escape), 4);
- const uint64_t mask = vget_lane_u64(vreinterpret_u64_u8(res), 0);
- if(mask != 0) {
- size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
- auto trailing_zero = trailing_zeroes(mask);
- return offset + (trailing_zero >> 2);
- }
- ptr += 16;
- remaining -= 16;
- }
-
- // Scalar fallback for remaining bytes
- size_t current = len - remaining;
- return find_next_json_quotable_character_scalar(view, current);
-}
-#elif SIMDJSON_EXPERIMENTAL_HAS_SSE2
-simdjson_inline size_t
-find_next_json_quotable_character(const std::string_view view,
- size_t location) noexcept {
- const size_t len = view.size();
- const uint8_t *ptr =
- reinterpret_cast<const uint8_t *>(view.data()) + location;
- size_t remaining = len - location;
-
- // SIMD constants
- __m128i v34 = _mm_set1_epi8(34); // '"'
- __m128i v92 = _mm_set1_epi8(92); // '\\'
- __m128i v31 = _mm_set1_epi8(31); // for control char detection
-
- while (remaining >= 16) {
- __m128i word = _mm_loadu_si128(reinterpret_cast<const __m128i *>(ptr));
-
- // Check for quotable characters
- __m128i needs_escape = _mm_cmpeq_epi8(word, v34);
- needs_escape = _mm_or_si128(needs_escape, _mm_cmpeq_epi8(word, v92));
- needs_escape = _mm_or_si128(
- needs_escape,
- _mm_cmpeq_epi8(_mm_subs_epu8(word, v31), _mm_setzero_si128()));
-
- int mask = _mm_movemask_epi8(needs_escape);
- if (mask != 0) {
- // Found quotable character - use trailing zero count to find position
- size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
- return offset + trailing_zeroes(mask);
- }
- ptr += 16;
- remaining -= 16;
- }
-
- // Scalar fallback for remaining bytes
- size_t current = len - remaining;
- return find_next_json_quotable_character_scalar(view, current);
-}
-#elif SIMDJSON_EXPERIMENTAL_HAS_LASX
+#if SIMDJSON_EXPERIMENTAL_HAS_LASX
simdjson_inline size_t
find_next_json_quotable_character(const std::string_view view,
size_t location) noexcept {
@@ -48199,6 +46698,7 @@ find_next_json_quotable_character(const std::string_view view,
return find_next_json_quotable_character_scalar(view, location);
}
#endif
+#endif // !SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE
SIMDJSON_CONSTEXPR_LAMBDA static std::string_view control_chars[] = {
"\\u0000", "\\u0001", "\\u0002", "\\u0003", "\\u0004", "\\u0005", "\\u0006",
@@ -48226,10 +46726,6 @@ SIMDJSON_CONSTEXPR_LAMBDA simdjson_inline void escape_json_char(char c, char *&o
}
}
-#if SIMDJSON_EXPERIMENTAL_HAS_SSE2 || SIMDJSON_EXPERIMENTAL_HAS_NEON
-#define SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE 1
-#endif
-
#if SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE
#if SIMDJSON_EXPERIMENTAL_HAS_SSE2
@@ -48510,10 +47006,21 @@ inline size_t write_string_escaped(const std::string_view input, char *out) {
#undef SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE
-simdjson_inline string_builder::string_builder(size_t initial_capacity)
- : buffer(new(std::nothrow) char[initial_capacity]), position(0),
- capacity(buffer.get() != nullptr ? initial_capacity : 0),
- is_valid(buffer.get() != nullptr) {}
+namespace internal {
+// Sets the size of s to n without initializing the new bytes when the
+// standard library allows it (C++23 resize_and_overwrite).
+simdjson_inline void resize_uninitialized(std::string &s, size_t n) {
+#if defined(__cpp_lib_string_resize_and_overwrite) && __cpp_lib_string_resize_and_overwrite >= 202110L
+ s.resize_and_overwrite(n, [](char *, size_t len) noexcept { return len; });
+#else
+ s.resize(n);
+#endif
+}
+} // namespace internal
+
+simdjson_inline string_builder::string_builder(size_t initial_capacity) {
+ grow_buffer(initial_capacity);
+}
simdjson_inline bool string_builder::capacity_check(size_t upcoming_bytes) {
// We use the convention that when is_valid is false, then the capacity and
@@ -48536,22 +47043,63 @@ inline void string_builder::grow_buffer(size_t desired_capacity) {
if (!is_valid) {
return;
}
- std::unique_ptr<char[]> new_buffer(new (std::nothrow) char[desired_capacity]);
- if (new_buffer.get() == nullptr) {
+ std::string new_buffer;
+ if (desired_capacity > new_buffer.max_size()) {
set_valid(false);
return;
}
- std::memcpy(new_buffer.get(), buffer.get(), position);
+#if defined(__cpp_exceptions) || defined(_CPPUNWIND)
+ try {
+ internal::resize_uninitialized(new_buffer, desired_capacity);
+ } catch (...) { // std::bad_alloc
+ set_valid(false);
+ return;
+ }
+#else
+ // Without exceptions, a failed allocation terminates the program.
+ internal::resize_uninitialized(new_buffer, desired_capacity);
+#endif
+ std::memcpy(&new_buffer[0], buffer_data(), position);
buffer.swap(new_buffer);
capacity = desired_capacity;
}
+// Not simdjson_inline: MSVC cannot force-inline a function with a try block (C4714).
+inline error_code string_builder::move_to(std::string &out) noexcept {
+ if (!is_valid) {
+ return simdjson::OUT_OF_CAPACITY;
+ }
+ if (position <= buffer.capacity() / 2) {
+ // Most of the buffer is unused: copy the bytes out, so that a small
+ // result does not keep the whole buffer allocated. The builder keeps its
+ // buffer for what it writes next.
+#if defined(__cpp_exceptions) || defined(_CPPUNWIND)
+ try {
+ out.assign(buffer.data(), position);
+ } catch (...) { // std::bad_alloc
+ return simdjson::MEMALLOC;
+ }
+#else
+ // Without exceptions, a failed allocation terminates the program.
+ out.assign(buffer.data(), position);
+#endif
+ position = 0;
+ return simdjson::SUCCESS;
+ }
+ buffer.resize(position); // shrinking never allocates
+ out = std::move(buffer);
+ std::string().swap(buffer);
+ position = 0;
+ capacity = 0;
+ return simdjson::SUCCESS;
+}
+
simdjson_inline void string_builder::set_valid(bool valid) noexcept {
if (!valid) {
is_valid = false;
capacity = 0;
position = 0;
- buffer.reset();
+ std::string().swap(buffer);
} else {
is_valid = true;
}
@@ -48563,7 +47111,7 @@ simdjson_inline size_t string_builder::size() const noexcept {
simdjson_inline void string_builder::append(char c) noexcept {
if (capacity_check(1)) {
- buffer.get()[position++] = c;
+ buffer_data()[position++] = c;
}
}
@@ -48571,7 +47119,7 @@ simdjson_inline void string_builder::append_null() noexcept {
constexpr char null_literal[] = "null";
constexpr size_t null_len = sizeof(null_literal) - 1;
if (capacity_check(null_len)) {
- std::memcpy(buffer.get() + position, null_literal, null_len);
+ std::memcpy(buffer_data() + position, null_literal, null_len);
position += null_len;
}
}
@@ -48581,124 +47129,14 @@ simdjson_inline void string_builder::clear() noexcept {
// if it was invalid, we should try to repair it
if (!is_valid) {
capacity = 0;
- buffer.reset();
+ std::string().swap(buffer);
is_valid = true;
}
}
namespace internal {
-// Integer to decimal: James Edward Anhalt III's algorithm
-static const char jeaiii_dd[201] =
- "00010203040506070809101112131415161718192021222324252627282930313233343536373839"
- "40414243444546474849505152535455565758596061626364656667686970717273747576777879"
- "8081828384858687888990919293949596979899";
-static const char jeaiii_fd[201] =
- "0\0" "1\0" "2\0" "3\0" "4\0" "5\0" "6\0" "7\0" "8\0" "9\0"
- "10111213141516171819202122232425262728293031323334353637383940414243444546474849"
- "50515253545556575859606162636465666768697071727374757677787980818283848586878889"
- "90919293949596979899";
-
-simdjson_really_inline void jeaiii_write_dd(char *p, uint64_t k) noexcept {
- std::memcpy(p, &jeaiii_dd[2 * k], 2);
-}
-simdjson_really_inline void jeaiii_write_fd(char *p, uint64_t k) noexcept {
- std::memcpy(p, &jeaiii_fd[2 * k], 2);
-}
-
-// Caller guarantees n < 10^8. Writes 1 to 8 digits.
-simdjson_really_inline char *jeaiii_lt1e8(char *b, uint32_t n) noexcept {
- constexpr uint64_t mask24 = (uint64_t(1) << 24) - 1;
- constexpr uint64_t mask32 = (uint64_t(1) << 32) - 1;
- if (n < 100) {
- jeaiii_write_fd(b, n);
- return n < 10 ? b + 1 : b + 2;
- }
- if (n < 1000000) {
- if (n < 10000) {
- const uint32_t f0 = uint32_t(10 * (1 << 24) / 1e3 + 1) * n;
- jeaiii_write_fd(b, f0 >> 24);
- b -= n < 1000;
- const uint32_t f2 = uint32_t(f0 & mask24) * 100;
- jeaiii_write_dd(b + 2, f2 >> 24);
- return b + 4;
- }
- const uint64_t f0 = uint64_t(10 * (1ull << 32) / 1e5 + 1) * n;
- jeaiii_write_fd(b, f0 >> 32);
- b -= n < 100000;
- const uint64_t f2 = (f0 & mask32) * 100;
- jeaiii_write_dd(b + 2, f2 >> 32);
- const uint64_t f4 = (f2 & mask32) * 100;
- jeaiii_write_dd(b + 4, f4 >> 32);
- return b + 6;
- }
- const uint64_t f0 = uint64_t(10 * (1ull << 48) / 1e7 + 1) * n >> 16;
- jeaiii_write_fd(b, f0 >> 32);
- b -= n < 10000000;
- const uint64_t f2 = (f0 & mask32) * 100;
- jeaiii_write_dd(b + 2, f2 >> 32);
- const uint64_t f4 = (f2 & mask32) * 100;
- jeaiii_write_dd(b + 4, f4 >> 32);
- const uint64_t f6 = (f4 & mask32) * 100;
- jeaiii_write_dd(b + 6, f6 >> 32);
- return b + 8;
-}
-
-// Caller guarantees z < 10^8. Always writes exactly 8 digits.
-simdjson_really_inline char *jeaiii_8_digits(char *b, uint32_t z) noexcept {
- constexpr uint64_t mask32 = (uint64_t(1) << 32) - 1;
- const uint64_t f0 = (uint64_t((1ull << 48) / 1e6 + 1) * z >> 16) + 1;
- jeaiii_write_dd(b, f0 >> 32);
- const uint64_t f2 = (f0 & mask32) * 100;
- jeaiii_write_dd(b + 2, f2 >> 32);
- const uint64_t f4 = (f2 & mask32) * 100;
- jeaiii_write_dd(b + 4, f4 >> 32);
- const uint64_t f6 = (f4 & mask32) * 100;
- jeaiii_write_dd(b + 6, f6 >> 32);
- return b + 8;
-}
-
-// Caller guarantees 10^8 <= n < 2^32. Writes 9 or 10 digits.
-simdjson_really_inline char *jeaiii_9_or_10(char *b, uint64_t n) noexcept {
- constexpr uint64_t mask57 = (uint64_t(1) << 57) - 1;
- const uint64_t f0 = uint64_t(10 * (1ull << 57) / 1e9 + 1) * n;
- jeaiii_write_fd(b, f0 >> 57);
- b -= n < 1000000000;
- const uint64_t f2 = (f0 & mask57) * 100;
- jeaiii_write_dd(b + 2, f2 >> 57);
- const uint64_t f4 = (f2 & mask57) * 100;
- jeaiii_write_dd(b + 4, f4 >> 57);
- const uint64_t f6 = (f4 & mask57) * 100;
- jeaiii_write_dd(b + 6, f6 >> 57);
- const uint64_t f8 = (f6 & mask57) * 100;
- jeaiii_write_dd(b + 8, f8 >> 57);
- return b + 10;
-}
-
-simdjson_really_inline char *write_uint_jeaiii(char *b, uint64_t n) noexcept {
- if (n < 100000000) {
- return jeaiii_lt1e8(b, uint32_t(n));
- }
- if (n < (uint64_t(1) << 32)) {
- return jeaiii_9_or_10(b, n);
- }
- // At least 10 digits: the low 8 digits, and 2 to 12 digits above them.
- const uint32_t z = uint32_t(n % 100000000);
- uint64_t u = n / 100000000;
- if (u < 100000000) {
- // u has 2 to 8 digits (if u < 10, n would be below 2^32).
- b = jeaiii_lt1e8(b, uint32_t(u));
- } else if (u < (uint64_t(1) << 32)) {
- b = jeaiii_9_or_10(b, u);
- } else {
- // u has 11 or 12 digits: split off 8 more.
- const uint32_t y = uint32_t(u % 100000000);
- u /= 100000000;
- b = jeaiii_lt1e8(b, uint32_t(u)); // 3 or 4 digits
- b = jeaiii_8_digits(b, y);
- }
- return jeaiii_8_digits(b, z);
-}
+using simdjson::internal::write_uint_jeaiii;
// Writes v at p, which must have to_chars_buffer_size bytes available, and
// returns the end of what was written.
@@ -48726,20 +47164,22 @@ simdjson_inline void string_builder::append(number_type v) noexcept {
std::is_integral<number_type>::value ||
std::is_floating_point<number_type>::value,
"Unsupported number type");
+ static_assert(sizeof(number_type) <= sizeof(uint64_t),
+ "Integers wider than 64 bits are not supported");
// If C++17 is available, we can 'if constexpr' here.
SIMDJSON_IF_CONSTEXPR(std::is_same<number_type, bool>::value) {
if (v) {
constexpr char true_literal[] = "true";
constexpr size_t true_len = sizeof(true_literal) - 1;
if (capacity_check(true_len)) {
- std::memcpy(buffer.get() + position, true_literal, true_len);
+ std::memcpy(buffer_data() + position, true_literal, true_len);
position += true_len;
}
} else {
constexpr char false_literal[] = "false";
constexpr size_t false_len = sizeof(false_literal) - 1;
if (capacity_check(false_len)) {
- std::memcpy(buffer.get() + position, false_literal, false_len);
+ std::memcpy(buffer_data() + position, false_literal, false_len);
position += false_len;
}
}
@@ -48749,9 +47189,9 @@ simdjson_inline void string_builder::append(number_type v) noexcept {
if (capacity_check(max_number_size)) {
using unsigned_type = typename std::make_unsigned<number_type>::type;
char* end = internal::write_uint_jeaiii(
- buffer.get() + position,
+ buffer_data() + position,
static_cast<uint64_t>(static_cast<unsigned_type>(v)));
- position = end - buffer.get();
+ position = end - buffer_data();
}
}
else SIMDJSON_IF_CONSTEXPR(std::is_integral<number_type>::value) {
@@ -48765,11 +47205,11 @@ simdjson_inline void string_builder::append(number_type v) noexcept {
? unsigned_type(0) - static_cast<unsigned_type>(v)
: static_cast<unsigned_type>(v);
// Branchless: always write '-', advance only if negative.
- buffer.get()[position] = '-';
+ buffer_data()[position] = '-';
position += negative;
char* end = internal::write_uint_jeaiii(
- buffer.get() + position, static_cast<uint64_t>(pv));
- position = end - buffer.get();
+ buffer_data() + position, static_cast<uint64_t>(pv));
+ position = end - buffer_data();
}
}
else SIMDJSON_IF_CONSTEXPR(std::is_floating_point<number_type>::value) {
@@ -48785,16 +47225,16 @@ simdjson_inline void string_builder::append(number_type v) noexcept {
constexpr char nan_literal[] = "NaN";
constexpr size_t nan_len = sizeof(nan_literal) - 1;
- std::memcpy(buffer.get() + position, nan_literal, nan_len);
+ std::memcpy(buffer_data() + position, nan_literal, nan_len);
position += nan_len;
} else {
constexpr char inf_literal[] = "Infinity";
constexpr size_t inf_len = sizeof(inf_literal) - 1;
if (v < 0) {
- buffer.get()[position] = '-';
+ buffer_data()[position] = '-';
++position;
}
- std::memcpy(buffer.get() + position, inf_literal, inf_len);
+ std::memcpy(buffer_data() + position, inf_literal, inf_len);
position += inf_len;
}
return;
@@ -48802,9 +47242,9 @@ simdjson_inline void string_builder::append(number_type v) noexcept {
#endif
// We could specialize for float.
- char *end = simdjson::internal::to_chars(buffer.get() + position, nullptr,
+ char *end = simdjson::internal::to_chars(buffer_data() + position, nullptr,
double(v));
- position = end - buffer.get();
+ position = end - buffer_data();
}
}
}
@@ -48818,7 +47258,7 @@ string_builder::escape_and_append(std::string_view input) noexcept {
return;
}
if (capacity_check(6 * input.size())) {
- position += write_string_escaped(input, buffer.get() + position);
+ position += write_string_escaped(input, buffer_data() + position);
}
}
@@ -48831,9 +47271,9 @@ string_builder::escape_and_append_with_quotes(std::string_view input) noexcept {
return;
}
if (capacity_check(2 + 6 * input.size())) {
- buffer.get()[position++] = '"';
- position += write_string_escaped(input, buffer.get() + position);
- buffer.get()[position++] = '"';
+ buffer_data()[position++] = '"';
+ position += write_string_escaped(input, buffer_data() + position);
+ buffer_data()[position++] = '"';
}
}
@@ -48841,10 +47281,10 @@ simdjson_inline void
string_builder::escape_and_append_with_quotes(char input) noexcept {
// escaping might turn a control character into \x00xx so 6 characters.
if (capacity_check(2 + 6 * 1)) {
- buffer.get()[position++] = '"';
+ buffer_data()[position++] = '"';
std::string_view cinput(&input, 1);
- position += write_string_escaped(cinput, buffer.get() + position);
- buffer.get()[position++] = '"';
+ position += write_string_escaped(cinput, buffer_data() + position);
+ buffer_data()[position++] = '"';
}
}
@@ -48874,7 +47314,7 @@ simdjson_inline void string_builder::append_raw(const char *c) noexcept {
simdjson_inline void
string_builder::append_raw(std::string_view input) noexcept {
if (capacity_check(input.size())) {
- std::memcpy(buffer.get() + position, input.data(), input.size());
+ std::memcpy(buffer_data() + position, input.data(), input.size());
position += input.size();
}
}
@@ -48882,7 +47322,7 @@ string_builder::append_raw(std::string_view input) noexcept {
simdjson_inline void string_builder::append_raw(const char *str,
size_t len) noexcept {
if (capacity_check(len)) {
- std::memcpy(buffer.get() + position, str, len);
+ std::memcpy(buffer_data() + position, str, len);
position += len;
}
}
@@ -48890,7 +47330,7 @@ simdjson_inline void string_builder::append_raw(const char *str,
template <size_t N>
simdjson_inline void string_builder::append_raw_n(const char *str) noexcept {
if (capacity_check(N)) {
- std::memcpy(buffer.get() + position, str, N);
+ std::memcpy(buffer_data() + position, str, N);
position += N;
}
}
@@ -48900,7 +47340,7 @@ template <typename Text>
simdjson_inline void string_builder::append_static_string() noexcept {
constexpr auto& padded = constevalutil::static_string<Text>;
if (capacity_check(padded.size())) {
- std::memcpy(buffer.get() + position, padded.data(), padded.size());
+ std::memcpy(buffer_data() + position, padded.data(), padded.size());
position += constevalutil::static_string_size<Text>();
}
}
@@ -48994,54 +47434,54 @@ string_builder::view() const noexcept {
if (!is_valid) {
return simdjson::OUT_OF_CAPACITY;
}
- return std::string_view(buffer.get(), position);
+ return std::string_view(buffer_data(), position);
}
simdjson_inline simdjson_result<const char *> string_builder::c_str() noexcept {
if (capacity_check(1)) {
- buffer.get()[position] = '\0';
- return buffer.get();
+ buffer_data()[position] = '\0';
+ return buffer_data();
}
return simdjson::OUT_OF_CAPACITY;
}
simdjson_inline bool string_builder::validate_unicode() const noexcept {
- return simdjson::validate_utf8(buffer.get(), position);
+ return simdjson::validate_utf8(buffer_data(), position);
}
simdjson_inline void string_builder::start_object() noexcept {
if (capacity_check(1)) {
- buffer.get()[position++] = '{';
+ buffer_data()[position++] = '{';
}
}
simdjson_inline void string_builder::end_object() noexcept {
if (capacity_check(1)) {
- buffer.get()[position++] = '}';
+ buffer_data()[position++] = '}';
}
}
simdjson_inline void string_builder::start_array() noexcept {
if (capacity_check(1)) {
- buffer.get()[position++] = '[';
+ buffer_data()[position++] = '[';
}
}
simdjson_inline void string_builder::end_array() noexcept {
if (capacity_check(1)) {
- buffer.get()[position++] = ']';
+ buffer_data()[position++] = ']';
}
}
simdjson_inline void string_builder::append_comma() noexcept {
if (capacity_check(1)) {
- buffer.get()[position++] = ',';
+ buffer_data()[position++] = ',';
}
}
simdjson_inline void string_builder::append_colon() noexcept {
if (capacity_check(1)) {
- buffer.get()[position++] = ':';
+ buffer_data()[position++] = ':';
}
}
@@ -49310,23 +47750,6 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin
}
-struct escaping {
- static constexpr uint32_t BYTES_PROCESSED = 1;
- simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
-
- simdjson_inline bool has_escape() { return escape_bits; }
- simdjson_inline int escape_index() { return 0; }
-
- bool escape_bits;
-}; // struct escaping
-
-
-
-simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
- dst[0] = src[0];
- return { (src[0] == '\\') || (src[0] == '"') || (src[0] < 32) };
-}
-
} // unnamed namespace
} // namespace fallback
} // namespace simdjson
@@ -49484,6 +47907,10 @@ namespace builder {
class string_builder {
public:
simdjson_inline string_builder(size_t initial_capacity = DEFAULT_INITIAL_CAPACITY);
+ string_builder(const string_builder &) = delete;
+ string_builder &operator=(const string_builder &) = delete;
+ string_builder(string_builder &&) noexcept = default;
+ string_builder &operator=(string_builder &&) noexcept = default;
static constexpr size_t DEFAULT_INITIAL_CAPACITY = 1024;
@@ -49497,6 +47924,18 @@ public:
typename = typename std::enable_if<std::is_arithmetic<number_type>::value>::type>
simdjson_inline void append(number_type v) noexcept;
+#ifdef __SIZEOF_INT128__
+ /**
+ * Integers wider than 64 bits are not supported. Without these overloads, a
+ * 128-bit integer would either be truncated to 64 bits by append(number_type)
+ * (when the standard library counts it as arithmetic, as libc++ does) or be
+ * converted to char by append(char) (when it does not, as libstdc++ does in
+ * strict ISO mode).
+ */
+ void append(__int128_t v) noexcept = delete;
+ void append(__uint128_t v) noexcept = delete;
+#endif
+
/**
* Append character c.
*/
@@ -49675,6 +48114,20 @@ requires (!std::is_convertible<R, std::string_view>::value && !concepts::optiona
*/
simdjson_inline simdjson_result<const char *> c_str() noexcept;
+ /**
+ * Moves the written JSON buffer into out, replacing its content, without
+ * copying it. When the content fills at most half of the buffer, it is
+ * copied instead, so that a small result does not keep the whole buffer
+ * allocated. The builder is left empty: it keeps its buffer when the
+ * content was copied, and gives up its memory when the buffer was moved.
+ * Returns an error, and leaves out and the builder unchanged, if memory
+ * allocation failed.
+ *
+ * The result may not be valid UTF-8 if some of your content was not valid UTF-8.
+ * Use validate_unicode() to check the content.
+ */
+ simdjson_warn_unused inline error_code move_to(std::string &out) noexcept;
+
/**
* Return true if the content is valid UTF-8.
*/
@@ -49694,7 +48147,7 @@ requires (!std::is_convertible<R, std::string_view>::value && !concepts::optiona
// forces those reloads when accessed via members of *this). User
// code should NOT call these directly.
// ============================================================
- simdjson_inline char *unsafe_data() noexcept { return buffer.get(); }
+ simdjson_inline char *unsafe_data() noexcept { return buffer_data(); }
simdjson_inline size_t unsafe_position() const noexcept { return position; }
simdjson_inline size_t unsafe_capacity() const noexcept { return capacity; }
simdjson_inline void unsafe_set_position(size_t p) noexcept { position = p; }
@@ -49735,7 +48188,13 @@ private:
*/
simdjson_inline void set_valid(bool valid) noexcept;
- std::unique_ptr<char[]> buffer{};
+ simdjson_inline char *buffer_data() noexcept { return &buffer[0]; }
+ simdjson_inline const char *buffer_data() const noexcept { return buffer.data(); }
+
+ // We own capacity bytes: buffer.size() == capacity. Only the first position
+ // bytes hold JSON. A std::string lets move_to() hand the result over
+ // without a copy.
+ std::string buffer{};
size_t position{0};
size_t capacity{0};
bool is_valid{true};
@@ -49753,20 +48212,16 @@ template <class Z>
simdjson_warn_unused simdjson_result<std::string> to_json(const Z &z, size_t initial_capacity = simdjson::fallback::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
simdjson::fallback::builder::string_builder b(initial_capacity);
b.append(z);
- std::string_view s;
- auto e = b.view().get(s);
+ std::string s;
+ auto e = b.move_to(s);
if(e) { return e; }
- return std::string(s);
+ return s;
}
template <class Z>
simdjson_warn_unused error_code to_json(const Z &z, std::string &s, size_t initial_capacity = simdjson::fallback::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
simdjson::fallback::builder::string_builder b(initial_capacity);
b.append(z);
- std::string_view sv;
- auto e = b.view().get(sv);
- if(e) { return e; }
- s.assign(sv.data(), sv.size());
- return simdjson::SUCCESS;
+ return b.move_to(s);
}
#endif
@@ -49782,7 +48237,7 @@ simdjson_warn_unused error_code to_json(const Z &z, std::string &s, size_t initi
#ifndef SIMDJSON_GENERIC_BUILDER_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
-/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_STRING_BUILDER_H */
+/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_BUILDER_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/builder/json_string_builder.h" */
/* amalgamation skipped (editor-only): #include "simdjson/concepts.h" */
/* amalgamation skipped (editor-only): #include "simdjson/annotations.h" */
@@ -49808,7 +48263,7 @@ namespace builder {
// included at the point this header is parsed; without these forwards,
// name lookup falls back to the wrong outer namespace).
namespace internal {
-simdjson_really_inline char *write_uint_jeaiii(char *p, uint64_t v) noexcept;
+using simdjson::internal::write_uint_jeaiii;
simdjson_inline char *write_double(char *p, double v) noexcept;
} // namespace internal
simdjson_really_inline size_t write_string_escaped(const std::string_view input, char *out);
@@ -49941,6 +48396,51 @@ consteval bool is_serialized_member(std::meta::info dm) {
&& !simdjson::detail::has_annotation(dm, ^^simdjson::detail::skip_serializing_tag);
}
+template <class Target, class T, class... Visiting>
+consteval bool members_reach();
+
+// Whether a value of type T may hold a value of type Target, directly or
+// through optionals, smart pointers, containers and members. Visiting lists
+// the structures being searched, so that the search terminates.
+template <class Target, class T, class... Visiting>
+consteval bool reaches() {
+ if constexpr (std::is_same_v<T, Target>) {
+ return true;
+ } else if constexpr ((std::is_same_v<T, Visiting> || ...) || require_custom_serialization<T> ||
+ std::is_same_v<T, std::string> || std::is_same_v<T, std::string_view>) {
+ return false;
+ } else if constexpr (concepts::optional_type<T> || concepts::smart_pointer<T>) {
+ return reaches<Target, std::remove_cvref_t<decltype(*std::declval<const T &>())>, Visiting...>();
+ } else if constexpr (concepts::string_view_keyed_map<T>) {
+ return reaches<Target, std::remove_cvref_t<typename T::mapped_type>, Visiting...>();
+ } else if constexpr (concepts::container_but_not_string<T> || concepts::appendable_containers<T>) {
+ return reaches<Target, std::remove_cvref_t<std::ranges::range_value_t<T>>, Visiting...>();
+ } else if constexpr (std::is_class_v<T>) {
+ return members_reach<Target, T, Visiting...>();
+ } else {
+ return false;
+ }
+}
+
+template <class Target, class T, class... Visiting>
+consteval bool members_reach() {
+ bool found = false;
+ template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) {
+ if constexpr (is_serialized_member(dm)) {
+ found = found || reaches<Target, std::remove_cvref_t<decltype(std::declval<const T &>().[:dm:])>, T, Visiting...>();
+ }
+ };
+ return found;
+}
+
+// Whether the structure T may contain itself, as in
+// struct Rule { std::unique_ptr<Rule> subrule; }. Serializing such a type
+// recurses at runtime, which simdjson_really_inline functions cannot do.
+template <class T>
+consteval bool is_recursive() {
+ return members_reach<T, T>();
+}
+
// False when the member has a skip_serializing_if<pred> annotation and
// pred(value) is true.
template <auto dm, typename V>
@@ -50124,6 +48624,8 @@ simdjson_really_inline constexpr void atom(W &w, const T &m) {
template<class W, typename number_type,
typename = typename std::enable_if<std::is_arithmetic<number_type>::value && !std::is_same_v<number_type, char>>::type>
simdjson_really_inline constexpr void atom(W &w, const number_type t) {
+ static_assert(sizeof(number_type) <= sizeof(uint64_t),
+ "Integers wider than 64 bits are not supported");
// Booleans / floats: defer to string_builder (rare path; keeps writer hot
// path free of float-formatter machinery). For integers, write directly
// via jeaiii using local pos.
@@ -50162,6 +48664,31 @@ simdjson_really_inline constexpr void atom(W &w, const number_type t) {
}
}
+namespace annotation_detail {
+
+template <class W, class T>
+simdjson_really_inline void atom_object(W &w, const T &t) {
+ if constexpr (simdjson::detail::has_annotation(^^T, ^^simdjson::detail::transparent_tag)) {
+ // A transparent structure is serialized as its single member.
+ constexpr auto dm = simdjson::detail::transparent_member(^^T);
+ atom_member<dm>(w, t.[:dm:]);
+ } else {
+ bool first = true;
+ if (!w.ensure(1)) { return; }
+ w.ptr[w.pos++] = '{';
+ atom_fields(w, t, first);
+ if (!w.ensure(1)) { return; }
+ w.ptr[w.pos++] = '}';
+ }
+}
+
+template <class W, class T>
+void atom_object_outlined(W &w, const T &t) {
+ atom_object(w, t);
+}
+
+} // namespace annotation_detail
+
template <class W, class T>
requires(std::is_class_v<T> && !concepts::container_but_not_string<T> &&
!concepts::string_view_keyed_map<T> &&
@@ -50173,17 +48700,11 @@ template <class W, class T>
!std::is_same_v<T, const char*> &&
!std::is_same_v<T, char> && !require_custom_serialization<T>)
simdjson_really_inline constexpr void atom(W &w, const T &t) {
- if constexpr (simdjson::detail::has_annotation(^^T, ^^simdjson::detail::transparent_tag)) {
- // A transparent structure is serialized as its single member.
- constexpr auto dm = simdjson::detail::transparent_member(^^T);
- annotation_detail::atom_member<dm>(w, t.[:dm:]);
+ if constexpr (annotation_detail::is_recursive<T>()) {
+ // Break the cycle of simdjson_really_inline calls.
+ annotation_detail::atom_object_outlined(w, t);
} else {
- bool first = true;
- if (!w.ensure(1)) { return; }
- w.ptr[w.pos++] = '{';
- annotation_detail::atom_fields(w, t, first);
- if (!w.ensure(1)) { return; }
- w.ptr[w.pos++] = '}';
+ annotation_detail::atom_object(w, t);
}
}
@@ -50283,25 +48804,30 @@ namespace bound_detail {
// Whether size_bound covers everything that atom() writes for T: not when a
// member is serialized by a with<Adapter> serializer, which writes an unknown
// amount through the string_builder.
-template <class T>
+// Visiting lists the structures being checked, so that a recursive type
+// (struct Node { std::unique_ptr<Node> next; }) does not recurse forever: a
+// type already being visited adds nothing, its members get checked once.
+template <class T, class... Visiting>
consteval bool is_bounded() {
- if constexpr (require_custom_serialization<T>) {
+ if constexpr ((std::is_same_v<T, Visiting> || ...)) {
+ return true;
+ } else if constexpr (require_custom_serialization<T>) {
return false;
} else if constexpr (std::is_same_v<T, std::string> || std::is_same_v<T, std::string_view> ||
std::is_same_v<T, const char *> || std::is_arithmetic_v<T> || std::is_enum_v<T>) {
return true;
} else if constexpr (concepts::optional_type<T> || concepts::smart_pointer<T>) {
- return is_bounded<std::remove_cvref_t<decltype(*std::declval<const T &>())>>();
+ return is_bounded<std::remove_cvref_t<decltype(*std::declval<const T &>())>, Visiting...>();
} else if constexpr (concepts::string_view_keyed_map<T>) {
- return is_bounded<std::remove_cvref_t<typename T::mapped_type>>();
+ return is_bounded<std::remove_cvref_t<typename T::mapped_type>, Visiting...>();
} else if constexpr (concepts::container_but_not_string<T> || concepts::appendable_containers<T>) {
- return is_bounded<std::remove_cvref_t<std::ranges::range_value_t<T>>>();
+ return is_bounded<std::remove_cvref_t<std::ranges::range_value_t<T>>, Visiting...>();
} else {
bool bounded = true;
template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) {
if constexpr (annotation_detail::is_serialized_member(dm)) {
bounded = bounded && simdjson::detail::annotation_of_template(dm, ^^simdjson::detail::with_t) == std::meta::info{} &&
- is_bounded<std::remove_cvref_t<decltype(std::declval<const T &>().[:dm:])>>();
+ is_bounded<std::remove_cvref_t<decltype(std::declval<const T &>().[:dm:])>, T, Visiting...>();
}
};
return bounded;
@@ -50339,6 +48865,11 @@ simdjson_really_inline size_t fields_bound(const T &t) noexcept {
return bound;
}
+template <class T>
+size_t fields_bound_outlined(const T &t) noexcept {
+ return fields_bound(t);
+}
+
template <class T>
simdjson_really_inline size_t size_bound([[maybe_unused]] const T &t) noexcept {
if constexpr (std::is_same_v<T, char>) {
@@ -50383,6 +48914,9 @@ simdjson_really_inline size_t size_bound([[maybe_unused]] const T &t) noexcept {
} else if constexpr (simdjson::detail::has_annotation(^^T, ^^simdjson::detail::transparent_tag)) {
constexpr auto dm = simdjson::detail::transparent_member(^^T);
return size_bound(t.[:dm:]);
+ } else if constexpr (annotation_detail::is_recursive<T>()) {
+ // Break the cycle of simdjson_really_inline calls.
+ return 2 + fields_bound_outlined(t);
} else {
return 2 + fields_bound(t);
}
@@ -50517,10 +49051,7 @@ simdjson_warn_unused error_code to_json(const Z &z, std::string &s, size_t initi
} else {
string_builder b(initial_capacity);
append(b, z);
- std::string_view view;
- if(auto e = b.view().get(view); e) { return e; }
- s.assign(view);
- return SUCCESS;
+ return b.move_to(s);
}
}
@@ -50583,9 +49114,9 @@ template<constevalutil::fixed_string... FieldNames, typename T>
simdjson_warn_unused simdjson_result<std::string> extract_from(const T &obj, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) {
string_builder b(initial_capacity);
extract_from<FieldNames...>(b, obj);
- std::string_view s;
- if(auto e = b.view().get(s); e) { return e; }
- return std::string(s);
+ std::string s;
+ if(auto e = b.move_to(s); e) { return e; }
+ return s;
}
SIMDJSON_POP_DISABLE_WARNINGS
@@ -50607,16 +49138,16 @@ template<constevalutil::fixed_string... FieldNames, typename T>
simdjson_warn_unused simdjson_result<std::string> extract_from(const T &obj, size_t initial_capacity = fallback::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
fallback::builder::string_builder b(initial_capacity);
fallback::builder::extract_from<FieldNames...>(b, obj);
- std::string_view s;
- if(auto e = b.view().get(s); e) { return e; }
- return std::string(s);
+ std::string s;
+ if(auto e = b.move_to(s); e) { return e; }
+ return s;
}
} // namespace simdjson
#endif // SIMDJSON_STATIC_REFLECTION
-#endif
+#endif // SIMDJSON_GENERIC_BUILDER_H
/* end file simdjson/generic/builder/json_builder.h for fallback */
/* including simdjson/generic/builder/fractured_json_builder.h for fallback: #include "simdjson/generic/builder/fractured_json_builder.h" */
/* begin file simdjson/generic/builder/fractured_json_builder.h for fallback */
@@ -50755,6 +49286,7 @@ simdjson_warn_unused simdjson_result<std::string> extract_fractured_json(
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_STRING_BUILDER_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/builder/json_string_builder.h" */
+/* amalgamation skipped (editor-only): #include "simdjson/internal/jeaiii.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
/*
@@ -50867,6 +49399,12 @@ inline bool has_json_escapable_byte(uint64_t x) {
**/
+#if SIMDJSON_EXPERIMENTAL_HAS_SSE2 || SIMDJSON_EXPERIMENTAL_HAS_NEON
+#define SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE 1
+#endif
+
+// With block escaping (SSE2/NEON), write_string_escaped scans the input itself.
+#if !SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE
// Scalar fallback for finding next quotable character
SIMDJSON_CONSTEXPR_LAMBDA simdjson_inline size_t
find_next_json_quotable_character_scalar(const std::string_view view,
@@ -50882,82 +49420,7 @@ find_next_json_quotable_character_scalar(const std::string_view view,
// SIMD-accelerated position finding that directly locates the first quotable
// character, combining detection and position extraction in a single pass to
// minimize redundant work.
-#if SIMDJSON_EXPERIMENTAL_HAS_NEON
-simdjson_inline size_t
-find_next_json_quotable_character(const std::string_view view,
- size_t location) noexcept {
- const size_t len = view.size();
- const uint8_t *ptr =
- reinterpret_cast<const uint8_t *>(view.data()) + location;
- size_t remaining = len - location;
-
- // SIMD constants for characters requiring escape
- uint8x16_t v34 = vdupq_n_u8(34); // '"'
- uint8x16_t v92 = vdupq_n_u8(92); // '\\'
- uint8x16_t v32 = vdupq_n_u8(32); // control char threshold
-
- while (remaining >= 16) {
- uint8x16_t word = vld1q_u8(ptr);
-
- // Check for quotable characters: '"', '\\', or control chars (< 32)
- uint8x16_t needs_escape = vceqq_u8(word, v34);
- needs_escape = vorrq_u8(needs_escape, vceqq_u8(word, v92));
- needs_escape = vorrq_u8(needs_escape, vcltq_u8(word, v32));
-
- const uint8x8_t res = vshrn_n_u16(vreinterpretq_u16_u8(needs_escape), 4);
- const uint64_t mask = vget_lane_u64(vreinterpret_u64_u8(res), 0);
- if(mask != 0) {
- size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
- auto trailing_zero = trailing_zeroes(mask);
- return offset + (trailing_zero >> 2);
- }
- ptr += 16;
- remaining -= 16;
- }
-
- // Scalar fallback for remaining bytes
- size_t current = len - remaining;
- return find_next_json_quotable_character_scalar(view, current);
-}
-#elif SIMDJSON_EXPERIMENTAL_HAS_SSE2
-simdjson_inline size_t
-find_next_json_quotable_character(const std::string_view view,
- size_t location) noexcept {
- const size_t len = view.size();
- const uint8_t *ptr =
- reinterpret_cast<const uint8_t *>(view.data()) + location;
- size_t remaining = len - location;
-
- // SIMD constants
- __m128i v34 = _mm_set1_epi8(34); // '"'
- __m128i v92 = _mm_set1_epi8(92); // '\\'
- __m128i v31 = _mm_set1_epi8(31); // for control char detection
-
- while (remaining >= 16) {
- __m128i word = _mm_loadu_si128(reinterpret_cast<const __m128i *>(ptr));
-
- // Check for quotable characters
- __m128i needs_escape = _mm_cmpeq_epi8(word, v34);
- needs_escape = _mm_or_si128(needs_escape, _mm_cmpeq_epi8(word, v92));
- needs_escape = _mm_or_si128(
- needs_escape,
- _mm_cmpeq_epi8(_mm_subs_epu8(word, v31), _mm_setzero_si128()));
-
- int mask = _mm_movemask_epi8(needs_escape);
- if (mask != 0) {
- // Found quotable character - use trailing zero count to find position
- size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
- return offset + trailing_zeroes(mask);
- }
- ptr += 16;
- remaining -= 16;
- }
-
- // Scalar fallback for remaining bytes
- size_t current = len - remaining;
- return find_next_json_quotable_character_scalar(view, current);
-}
-#elif SIMDJSON_EXPERIMENTAL_HAS_LASX
+#if SIMDJSON_EXPERIMENTAL_HAS_LASX
simdjson_inline size_t
find_next_json_quotable_character(const std::string_view view,
size_t location) noexcept {
@@ -51130,6 +49593,7 @@ find_next_json_quotable_character(const std::string_view view,
return find_next_json_quotable_character_scalar(view, location);
}
#endif
+#endif // !SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE
SIMDJSON_CONSTEXPR_LAMBDA static std::string_view control_chars[] = {
"\\u0000", "\\u0001", "\\u0002", "\\u0003", "\\u0004", "\\u0005", "\\u0006",
@@ -51157,10 +49621,6 @@ SIMDJSON_CONSTEXPR_LAMBDA simdjson_inline void escape_json_char(char c, char *&o
}
}
-#if SIMDJSON_EXPERIMENTAL_HAS_SSE2 || SIMDJSON_EXPERIMENTAL_HAS_NEON
-#define SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE 1
-#endif
-
#if SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE
#if SIMDJSON_EXPERIMENTAL_HAS_SSE2
@@ -51441,10 +49901,21 @@ inline size_t write_string_escaped(const std::string_view input, char *out) {
#undef SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE
-simdjson_inline string_builder::string_builder(size_t initial_capacity)
- : buffer(new(std::nothrow) char[initial_capacity]), position(0),
- capacity(buffer.get() != nullptr ? initial_capacity : 0),
- is_valid(buffer.get() != nullptr) {}
+namespace internal {
+// Sets the size of s to n without initializing the new bytes when the
+// standard library allows it (C++23 resize_and_overwrite).
+simdjson_inline void resize_uninitialized(std::string &s, size_t n) {
+#if defined(__cpp_lib_string_resize_and_overwrite) && __cpp_lib_string_resize_and_overwrite >= 202110L
+ s.resize_and_overwrite(n, [](char *, size_t len) noexcept { return len; });
+#else
+ s.resize(n);
+#endif
+}
+} // namespace internal
+
+simdjson_inline string_builder::string_builder(size_t initial_capacity) {
+ grow_buffer(initial_capacity);
+}
simdjson_inline bool string_builder::capacity_check(size_t upcoming_bytes) {
// We use the convention that when is_valid is false, then the capacity and
@@ -51467,22 +49938,63 @@ inline void string_builder::grow_buffer(size_t desired_capacity) {
if (!is_valid) {
return;
}
- std::unique_ptr<char[]> new_buffer(new (std::nothrow) char[desired_capacity]);
- if (new_buffer.get() == nullptr) {
+ std::string new_buffer;
+ if (desired_capacity > new_buffer.max_size()) {
set_valid(false);
return;
}
- std::memcpy(new_buffer.get(), buffer.get(), position);
+#if defined(__cpp_exceptions) || defined(_CPPUNWIND)
+ try {
+ internal::resize_uninitialized(new_buffer, desired_capacity);
+ } catch (...) { // std::bad_alloc
+ set_valid(false);
+ return;
+ }
+#else
+ // Without exceptions, a failed allocation terminates the program.
+ internal::resize_uninitialized(new_buffer, desired_capacity);
+#endif
+ std::memcpy(&new_buffer[0], buffer_data(), position);
buffer.swap(new_buffer);
capacity = desired_capacity;
}
+// Not simdjson_inline: MSVC cannot force-inline a function with a try block (C4714).
+inline error_code string_builder::move_to(std::string &out) noexcept {
+ if (!is_valid) {
+ return simdjson::OUT_OF_CAPACITY;
+ }
+ if (position <= buffer.capacity() / 2) {
+ // Most of the buffer is unused: copy the bytes out, so that a small
+ // result does not keep the whole buffer allocated. The builder keeps its
+ // buffer for what it writes next.
+#if defined(__cpp_exceptions) || defined(_CPPUNWIND)
+ try {
+ out.assign(buffer.data(), position);
+ } catch (...) { // std::bad_alloc
+ return simdjson::MEMALLOC;
+ }
+#else
+ // Without exceptions, a failed allocation terminates the program.
+ out.assign(buffer.data(), position);
+#endif
+ position = 0;
+ return simdjson::SUCCESS;
+ }
+ buffer.resize(position); // shrinking never allocates
+ out = std::move(buffer);
+ std::string().swap(buffer);
+ position = 0;
+ capacity = 0;
+ return simdjson::SUCCESS;
+}
+
simdjson_inline void string_builder::set_valid(bool valid) noexcept {
if (!valid) {
is_valid = false;
capacity = 0;
position = 0;
- buffer.reset();
+ std::string().swap(buffer);
} else {
is_valid = true;
}
@@ -51494,7 +50006,7 @@ simdjson_inline size_t string_builder::size() const noexcept {
simdjson_inline void string_builder::append(char c) noexcept {
if (capacity_check(1)) {
- buffer.get()[position++] = c;
+ buffer_data()[position++] = c;
}
}
@@ -51502,7 +50014,7 @@ simdjson_inline void string_builder::append_null() noexcept {
constexpr char null_literal[] = "null";
constexpr size_t null_len = sizeof(null_literal) - 1;
if (capacity_check(null_len)) {
- std::memcpy(buffer.get() + position, null_literal, null_len);
+ std::memcpy(buffer_data() + position, null_literal, null_len);
position += null_len;
}
}
@@ -51512,124 +50024,14 @@ simdjson_inline void string_builder::clear() noexcept {
// if it was invalid, we should try to repair it
if (!is_valid) {
capacity = 0;
- buffer.reset();
+ std::string().swap(buffer);
is_valid = true;
}
}
namespace internal {
-// Integer to decimal: James Edward Anhalt III's algorithm
-static const char jeaiii_dd[201] =
- "00010203040506070809101112131415161718192021222324252627282930313233343536373839"
- "40414243444546474849505152535455565758596061626364656667686970717273747576777879"
- "8081828384858687888990919293949596979899";
-static const char jeaiii_fd[201] =
- "0\0" "1\0" "2\0" "3\0" "4\0" "5\0" "6\0" "7\0" "8\0" "9\0"
- "10111213141516171819202122232425262728293031323334353637383940414243444546474849"
- "50515253545556575859606162636465666768697071727374757677787980818283848586878889"
- "90919293949596979899";
-
-simdjson_really_inline void jeaiii_write_dd(char *p, uint64_t k) noexcept {
- std::memcpy(p, &jeaiii_dd[2 * k], 2);
-}
-simdjson_really_inline void jeaiii_write_fd(char *p, uint64_t k) noexcept {
- std::memcpy(p, &jeaiii_fd[2 * k], 2);
-}
-
-// Caller guarantees n < 10^8. Writes 1 to 8 digits.
-simdjson_really_inline char *jeaiii_lt1e8(char *b, uint32_t n) noexcept {
- constexpr uint64_t mask24 = (uint64_t(1) << 24) - 1;
- constexpr uint64_t mask32 = (uint64_t(1) << 32) - 1;
- if (n < 100) {
- jeaiii_write_fd(b, n);
- return n < 10 ? b + 1 : b + 2;
- }
- if (n < 1000000) {
- if (n < 10000) {
- const uint32_t f0 = uint32_t(10 * (1 << 24) / 1e3 + 1) * n;
- jeaiii_write_fd(b, f0 >> 24);
- b -= n < 1000;
- const uint32_t f2 = uint32_t(f0 & mask24) * 100;
- jeaiii_write_dd(b + 2, f2 >> 24);
- return b + 4;
- }
- const uint64_t f0 = uint64_t(10 * (1ull << 32) / 1e5 + 1) * n;
- jeaiii_write_fd(b, f0 >> 32);
- b -= n < 100000;
- const uint64_t f2 = (f0 & mask32) * 100;
- jeaiii_write_dd(b + 2, f2 >> 32);
- const uint64_t f4 = (f2 & mask32) * 100;
- jeaiii_write_dd(b + 4, f4 >> 32);
- return b + 6;
- }
- const uint64_t f0 = uint64_t(10 * (1ull << 48) / 1e7 + 1) * n >> 16;
- jeaiii_write_fd(b, f0 >> 32);
- b -= n < 10000000;
- const uint64_t f2 = (f0 & mask32) * 100;
- jeaiii_write_dd(b + 2, f2 >> 32);
- const uint64_t f4 = (f2 & mask32) * 100;
- jeaiii_write_dd(b + 4, f4 >> 32);
- const uint64_t f6 = (f4 & mask32) * 100;
- jeaiii_write_dd(b + 6, f6 >> 32);
- return b + 8;
-}
-
-// Caller guarantees z < 10^8. Always writes exactly 8 digits.
-simdjson_really_inline char *jeaiii_8_digits(char *b, uint32_t z) noexcept {
- constexpr uint64_t mask32 = (uint64_t(1) << 32) - 1;
- const uint64_t f0 = (uint64_t((1ull << 48) / 1e6 + 1) * z >> 16) + 1;
- jeaiii_write_dd(b, f0 >> 32);
- const uint64_t f2 = (f0 & mask32) * 100;
- jeaiii_write_dd(b + 2, f2 >> 32);
- const uint64_t f4 = (f2 & mask32) * 100;
- jeaiii_write_dd(b + 4, f4 >> 32);
- const uint64_t f6 = (f4 & mask32) * 100;
- jeaiii_write_dd(b + 6, f6 >> 32);
- return b + 8;
-}
-
-// Caller guarantees 10^8 <= n < 2^32. Writes 9 or 10 digits.
-simdjson_really_inline char *jeaiii_9_or_10(char *b, uint64_t n) noexcept {
- constexpr uint64_t mask57 = (uint64_t(1) << 57) - 1;
- const uint64_t f0 = uint64_t(10 * (1ull << 57) / 1e9 + 1) * n;
- jeaiii_write_fd(b, f0 >> 57);
- b -= n < 1000000000;
- const uint64_t f2 = (f0 & mask57) * 100;
- jeaiii_write_dd(b + 2, f2 >> 57);
- const uint64_t f4 = (f2 & mask57) * 100;
- jeaiii_write_dd(b + 4, f4 >> 57);
- const uint64_t f6 = (f4 & mask57) * 100;
- jeaiii_write_dd(b + 6, f6 >> 57);
- const uint64_t f8 = (f6 & mask57) * 100;
- jeaiii_write_dd(b + 8, f8 >> 57);
- return b + 10;
-}
-
-simdjson_really_inline char *write_uint_jeaiii(char *b, uint64_t n) noexcept {
- if (n < 100000000) {
- return jeaiii_lt1e8(b, uint32_t(n));
- }
- if (n < (uint64_t(1) << 32)) {
- return jeaiii_9_or_10(b, n);
- }
- // At least 10 digits: the low 8 digits, and 2 to 12 digits above them.
- const uint32_t z = uint32_t(n % 100000000);
- uint64_t u = n / 100000000;
- if (u < 100000000) {
- // u has 2 to 8 digits (if u < 10, n would be below 2^32).
- b = jeaiii_lt1e8(b, uint32_t(u));
- } else if (u < (uint64_t(1) << 32)) {
- b = jeaiii_9_or_10(b, u);
- } else {
- // u has 11 or 12 digits: split off 8 more.
- const uint32_t y = uint32_t(u % 100000000);
- u /= 100000000;
- b = jeaiii_lt1e8(b, uint32_t(u)); // 3 or 4 digits
- b = jeaiii_8_digits(b, y);
- }
- return jeaiii_8_digits(b, z);
-}
+using simdjson::internal::write_uint_jeaiii;
// Writes v at p, which must have to_chars_buffer_size bytes available, and
// returns the end of what was written.
@@ -51657,20 +50059,22 @@ simdjson_inline void string_builder::append(number_type v) noexcept {
std::is_integral<number_type>::value ||
std::is_floating_point<number_type>::value,
"Unsupported number type");
+ static_assert(sizeof(number_type) <= sizeof(uint64_t),
+ "Integers wider than 64 bits are not supported");
// If C++17 is available, we can 'if constexpr' here.
SIMDJSON_IF_CONSTEXPR(std::is_same<number_type, bool>::value) {
if (v) {
constexpr char true_literal[] = "true";
constexpr size_t true_len = sizeof(true_literal) - 1;
if (capacity_check(true_len)) {
- std::memcpy(buffer.get() + position, true_literal, true_len);
+ std::memcpy(buffer_data() + position, true_literal, true_len);
position += true_len;
}
} else {
constexpr char false_literal[] = "false";
constexpr size_t false_len = sizeof(false_literal) - 1;
if (capacity_check(false_len)) {
- std::memcpy(buffer.get() + position, false_literal, false_len);
+ std::memcpy(buffer_data() + position, false_literal, false_len);
position += false_len;
}
}
@@ -51680,9 +50084,9 @@ simdjson_inline void string_builder::append(number_type v) noexcept {
if (capacity_check(max_number_size)) {
using unsigned_type = typename std::make_unsigned<number_type>::type;
char* end = internal::write_uint_jeaiii(
- buffer.get() + position,
+ buffer_data() + position,
static_cast<uint64_t>(static_cast<unsigned_type>(v)));
- position = end - buffer.get();
+ position = end - buffer_data();
}
}
else SIMDJSON_IF_CONSTEXPR(std::is_integral<number_type>::value) {
@@ -51696,11 +50100,11 @@ simdjson_inline void string_builder::append(number_type v) noexcept {
? unsigned_type(0) - static_cast<unsigned_type>(v)
: static_cast<unsigned_type>(v);
// Branchless: always write '-', advance only if negative.
- buffer.get()[position] = '-';
+ buffer_data()[position] = '-';
position += negative;
char* end = internal::write_uint_jeaiii(
- buffer.get() + position, static_cast<uint64_t>(pv));
- position = end - buffer.get();
+ buffer_data() + position, static_cast<uint64_t>(pv));
+ position = end - buffer_data();
}
}
else SIMDJSON_IF_CONSTEXPR(std::is_floating_point<number_type>::value) {
@@ -51716,16 +50120,16 @@ simdjson_inline void string_builder::append(number_type v) noexcept {
constexpr char nan_literal[] = "NaN";
constexpr size_t nan_len = sizeof(nan_literal) - 1;
- std::memcpy(buffer.get() + position, nan_literal, nan_len);
+ std::memcpy(buffer_data() + position, nan_literal, nan_len);
position += nan_len;
} else {
constexpr char inf_literal[] = "Infinity";
constexpr size_t inf_len = sizeof(inf_literal) - 1;
if (v < 0) {
- buffer.get()[position] = '-';
+ buffer_data()[position] = '-';
++position;
}
- std::memcpy(buffer.get() + position, inf_literal, inf_len);
+ std::memcpy(buffer_data() + position, inf_literal, inf_len);
position += inf_len;
}
return;
@@ -51733,9 +50137,9 @@ simdjson_inline void string_builder::append(number_type v) noexcept {
#endif
// We could specialize for float.
- char *end = simdjson::internal::to_chars(buffer.get() + position, nullptr,
+ char *end = simdjson::internal::to_chars(buffer_data() + position, nullptr,
double(v));
- position = end - buffer.get();
+ position = end - buffer_data();
}
}
}
@@ -51749,7 +50153,7 @@ string_builder::escape_and_append(std::string_view input) noexcept {
return;
}
if (capacity_check(6 * input.size())) {
- position += write_string_escaped(input, buffer.get() + position);
+ position += write_string_escaped(input, buffer_data() + position);
}
}
@@ -51762,9 +50166,9 @@ string_builder::escape_and_append_with_quotes(std::string_view input) noexcept {
return;
}
if (capacity_check(2 + 6 * input.size())) {
- buffer.get()[position++] = '"';
- position += write_string_escaped(input, buffer.get() + position);
- buffer.get()[position++] = '"';
+ buffer_data()[position++] = '"';
+ position += write_string_escaped(input, buffer_data() + position);
+ buffer_data()[position++] = '"';
}
}
@@ -51772,10 +50176,10 @@ simdjson_inline void
string_builder::escape_and_append_with_quotes(char input) noexcept {
// escaping might turn a control character into \x00xx so 6 characters.
if (capacity_check(2 + 6 * 1)) {
- buffer.get()[position++] = '"';
+ buffer_data()[position++] = '"';
std::string_view cinput(&input, 1);
- position += write_string_escaped(cinput, buffer.get() + position);
- buffer.get()[position++] = '"';
+ position += write_string_escaped(cinput, buffer_data() + position);
+ buffer_data()[position++] = '"';
}
}
@@ -51805,7 +50209,7 @@ simdjson_inline void string_builder::append_raw(const char *c) noexcept {
simdjson_inline void
string_builder::append_raw(std::string_view input) noexcept {
if (capacity_check(input.size())) {
- std::memcpy(buffer.get() + position, input.data(), input.size());
+ std::memcpy(buffer_data() + position, input.data(), input.size());
position += input.size();
}
}
@@ -51813,7 +50217,7 @@ string_builder::append_raw(std::string_view input) noexcept {
simdjson_inline void string_builder::append_raw(const char *str,
size_t len) noexcept {
if (capacity_check(len)) {
- std::memcpy(buffer.get() + position, str, len);
+ std::memcpy(buffer_data() + position, str, len);
position += len;
}
}
@@ -51821,7 +50225,7 @@ simdjson_inline void string_builder::append_raw(const char *str,
template <size_t N>
simdjson_inline void string_builder::append_raw_n(const char *str) noexcept {
if (capacity_check(N)) {
- std::memcpy(buffer.get() + position, str, N);
+ std::memcpy(buffer_data() + position, str, N);
position += N;
}
}
@@ -51831,7 +50235,7 @@ template <typename Text>
simdjson_inline void string_builder::append_static_string() noexcept {
constexpr auto& padded = constevalutil::static_string<Text>;
if (capacity_check(padded.size())) {
- std::memcpy(buffer.get() + position, padded.data(), padded.size());
+ std::memcpy(buffer_data() + position, padded.data(), padded.size());
position += constevalutil::static_string_size<Text>();
}
}
@@ -51925,54 +50329,54 @@ string_builder::view() const noexcept {
if (!is_valid) {
return simdjson::OUT_OF_CAPACITY;
}
- return std::string_view(buffer.get(), position);
+ return std::string_view(buffer_data(), position);
}
simdjson_inline simdjson_result<const char *> string_builder::c_str() noexcept {
if (capacity_check(1)) {
- buffer.get()[position] = '\0';
- return buffer.get();
+ buffer_data()[position] = '\0';
+ return buffer_data();
}
return simdjson::OUT_OF_CAPACITY;
}
simdjson_inline bool string_builder::validate_unicode() const noexcept {
- return simdjson::validate_utf8(buffer.get(), position);
+ return simdjson::validate_utf8(buffer_data(), position);
}
simdjson_inline void string_builder::start_object() noexcept {
if (capacity_check(1)) {
- buffer.get()[position++] = '{';
+ buffer_data()[position++] = '{';
}
}
simdjson_inline void string_builder::end_object() noexcept {
if (capacity_check(1)) {
- buffer.get()[position++] = '}';
+ buffer_data()[position++] = '}';
}
}
simdjson_inline void string_builder::start_array() noexcept {
if (capacity_check(1)) {
- buffer.get()[position++] = '[';
+ buffer_data()[position++] = '[';
}
}
simdjson_inline void string_builder::end_array() noexcept {
if (capacity_check(1)) {
- buffer.get()[position++] = ']';
+ buffer_data()[position++] = ']';
}
}
simdjson_inline void string_builder::append_comma() noexcept {
if (capacity_check(1)) {
- buffer.get()[position++] = ',';
+ buffer_data()[position++] = ',';
}
}
simdjson_inline void string_builder::append_colon() noexcept {
if (capacity_check(1)) {
- buffer.get()[position++] = ':';
+ buffer_data()[position++] = ':';
}
}
@@ -52800,30 +51204,6 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin
}
-struct escaping {
- static constexpr uint32_t BYTES_PROCESSED = 32;
- simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
-
- simdjson_inline bool has_escape() { return escape_bits != 0; }
- simdjson_inline int escape_index() { return trailing_zeroes(escape_bits); }
-
- uint64_t escape_bits;
-}; // struct escaping
-
-
-
-simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
- static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes");
- simd8<uint8_t> v(src);
- v.store(dst);
- simd8<bool> is_quote = (v == '"');
- simd8<bool> is_backslash = (v == '\\');
- simd8<bool> is_control = (v < 32);
- return {
- uint64_t((is_backslash | is_quote | is_control).to_bitmask())
- };
-}
-
} // unnamed namespace
} // namespace haswell
} // namespace simdjson
@@ -52892,6 +51272,10 @@ namespace builder {
class string_builder {
public:
simdjson_inline string_builder(size_t initial_capacity = DEFAULT_INITIAL_CAPACITY);
+ string_builder(const string_builder &) = delete;
+ string_builder &operator=(const string_builder &) = delete;
+ string_builder(string_builder &&) noexcept = default;
+ string_builder &operator=(string_builder &&) noexcept = default;
static constexpr size_t DEFAULT_INITIAL_CAPACITY = 1024;
@@ -52905,6 +51289,18 @@ public:
typename = typename std::enable_if<std::is_arithmetic<number_type>::value>::type>
simdjson_inline void append(number_type v) noexcept;
+#ifdef __SIZEOF_INT128__
+ /**
+ * Integers wider than 64 bits are not supported. Without these overloads, a
+ * 128-bit integer would either be truncated to 64 bits by append(number_type)
+ * (when the standard library counts it as arithmetic, as libc++ does) or be
+ * converted to char by append(char) (when it does not, as libstdc++ does in
+ * strict ISO mode).
+ */
+ void append(__int128_t v) noexcept = delete;
+ void append(__uint128_t v) noexcept = delete;
+#endif
+
/**
* Append character c.
*/
@@ -53083,6 +51479,20 @@ requires (!std::is_convertible<R, std::string_view>::value && !concepts::optiona
*/
simdjson_inline simdjson_result<const char *> c_str() noexcept;
+ /**
+ * Moves the written JSON buffer into out, replacing its content, without
+ * copying it. When the content fills at most half of the buffer, it is
+ * copied instead, so that a small result does not keep the whole buffer
+ * allocated. The builder is left empty: it keeps its buffer when the
+ * content was copied, and gives up its memory when the buffer was moved.
+ * Returns an error, and leaves out and the builder unchanged, if memory
+ * allocation failed.
+ *
+ * The result may not be valid UTF-8 if some of your content was not valid UTF-8.
+ * Use validate_unicode() to check the content.
+ */
+ simdjson_warn_unused inline error_code move_to(std::string &out) noexcept;
+
/**
* Return true if the content is valid UTF-8.
*/
@@ -53102,7 +51512,7 @@ requires (!std::is_convertible<R, std::string_view>::value && !concepts::optiona
// forces those reloads when accessed via members of *this). User
// code should NOT call these directly.
// ============================================================
- simdjson_inline char *unsafe_data() noexcept { return buffer.get(); }
+ simdjson_inline char *unsafe_data() noexcept { return buffer_data(); }
simdjson_inline size_t unsafe_position() const noexcept { return position; }
simdjson_inline size_t unsafe_capacity() const noexcept { return capacity; }
simdjson_inline void unsafe_set_position(size_t p) noexcept { position = p; }
@@ -53143,7 +51553,13 @@ private:
*/
simdjson_inline void set_valid(bool valid) noexcept;
- std::unique_ptr<char[]> buffer{};
+ simdjson_inline char *buffer_data() noexcept { return &buffer[0]; }
+ simdjson_inline const char *buffer_data() const noexcept { return buffer.data(); }
+
+ // We own capacity bytes: buffer.size() == capacity. Only the first position
+ // bytes hold JSON. A std::string lets move_to() hand the result over
+ // without a copy.
+ std::string buffer{};
size_t position{0};
size_t capacity{0};
bool is_valid{true};
@@ -53161,20 +51577,16 @@ template <class Z>
simdjson_warn_unused simdjson_result<std::string> to_json(const Z &z, size_t initial_capacity = simdjson::haswell::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
simdjson::haswell::builder::string_builder b(initial_capacity);
b.append(z);
- std::string_view s;
- auto e = b.view().get(s);
+ std::string s;
+ auto e = b.move_to(s);
if(e) { return e; }
- return std::string(s);
+ return s;
}
template <class Z>
simdjson_warn_unused error_code to_json(const Z &z, std::string &s, size_t initial_capacity = simdjson::haswell::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
simdjson::haswell::builder::string_builder b(initial_capacity);
b.append(z);
- std::string_view sv;
- auto e = b.view().get(sv);
- if(e) { return e; }
- s.assign(sv.data(), sv.size());
- return simdjson::SUCCESS;
+ return b.move_to(s);
}
#endif
@@ -53190,7 +51602,7 @@ simdjson_warn_unused error_code to_json(const Z &z, std::string &s, size_t initi
#ifndef SIMDJSON_GENERIC_BUILDER_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
-/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_STRING_BUILDER_H */
+/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_BUILDER_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/builder/json_string_builder.h" */
/* amalgamation skipped (editor-only): #include "simdjson/concepts.h" */
/* amalgamation skipped (editor-only): #include "simdjson/annotations.h" */
@@ -53216,7 +51628,7 @@ namespace builder {
// included at the point this header is parsed; without these forwards,
// name lookup falls back to the wrong outer namespace).
namespace internal {
-simdjson_really_inline char *write_uint_jeaiii(char *p, uint64_t v) noexcept;
+using simdjson::internal::write_uint_jeaiii;
simdjson_inline char *write_double(char *p, double v) noexcept;
} // namespace internal
simdjson_really_inline size_t write_string_escaped(const std::string_view input, char *out);
@@ -53349,6 +51761,51 @@ consteval bool is_serialized_member(std::meta::info dm) {
&& !simdjson::detail::has_annotation(dm, ^^simdjson::detail::skip_serializing_tag);
}
+template <class Target, class T, class... Visiting>
+consteval bool members_reach();
+
+// Whether a value of type T may hold a value of type Target, directly or
+// through optionals, smart pointers, containers and members. Visiting lists
+// the structures being searched, so that the search terminates.
+template <class Target, class T, class... Visiting>
+consteval bool reaches() {
+ if constexpr (std::is_same_v<T, Target>) {
+ return true;
+ } else if constexpr ((std::is_same_v<T, Visiting> || ...) || require_custom_serialization<T> ||
+ std::is_same_v<T, std::string> || std::is_same_v<T, std::string_view>) {
+ return false;
+ } else if constexpr (concepts::optional_type<T> || concepts::smart_pointer<T>) {
+ return reaches<Target, std::remove_cvref_t<decltype(*std::declval<const T &>())>, Visiting...>();
+ } else if constexpr (concepts::string_view_keyed_map<T>) {
+ return reaches<Target, std::remove_cvref_t<typename T::mapped_type>, Visiting...>();
+ } else if constexpr (concepts::container_but_not_string<T> || concepts::appendable_containers<T>) {
+ return reaches<Target, std::remove_cvref_t<std::ranges::range_value_t<T>>, Visiting...>();
+ } else if constexpr (std::is_class_v<T>) {
+ return members_reach<Target, T, Visiting...>();
+ } else {
+ return false;
+ }
+}
+
+template <class Target, class T, class... Visiting>
+consteval bool members_reach() {
+ bool found = false;
+ template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) {
+ if constexpr (is_serialized_member(dm)) {
+ found = found || reaches<Target, std::remove_cvref_t<decltype(std::declval<const T &>().[:dm:])>, T, Visiting...>();
+ }
+ };
+ return found;
+}
+
+// Whether the structure T may contain itself, as in
+// struct Rule { std::unique_ptr<Rule> subrule; }. Serializing such a type
+// recurses at runtime, which simdjson_really_inline functions cannot do.
+template <class T>
+consteval bool is_recursive() {
+ return members_reach<T, T>();
+}
+
// False when the member has a skip_serializing_if<pred> annotation and
// pred(value) is true.
template <auto dm, typename V>
@@ -53532,6 +51989,8 @@ simdjson_really_inline constexpr void atom(W &w, const T &m) {
template<class W, typename number_type,
typename = typename std::enable_if<std::is_arithmetic<number_type>::value && !std::is_same_v<number_type, char>>::type>
simdjson_really_inline constexpr void atom(W &w, const number_type t) {
+ static_assert(sizeof(number_type) <= sizeof(uint64_t),
+ "Integers wider than 64 bits are not supported");
// Booleans / floats: defer to string_builder (rare path; keeps writer hot
// path free of float-formatter machinery). For integers, write directly
// via jeaiii using local pos.
@@ -53570,6 +52029,31 @@ simdjson_really_inline constexpr void atom(W &w, const number_type t) {
}
}
+namespace annotation_detail {
+
+template <class W, class T>
+simdjson_really_inline void atom_object(W &w, const T &t) {
+ if constexpr (simdjson::detail::has_annotation(^^T, ^^simdjson::detail::transparent_tag)) {
+ // A transparent structure is serialized as its single member.
+ constexpr auto dm = simdjson::detail::transparent_member(^^T);
+ atom_member<dm>(w, t.[:dm:]);
+ } else {
+ bool first = true;
+ if (!w.ensure(1)) { return; }
+ w.ptr[w.pos++] = '{';
+ atom_fields(w, t, first);
+ if (!w.ensure(1)) { return; }
+ w.ptr[w.pos++] = '}';
+ }
+}
+
+template <class W, class T>
+void atom_object_outlined(W &w, const T &t) {
+ atom_object(w, t);
+}
+
+} // namespace annotation_detail
+
template <class W, class T>
requires(std::is_class_v<T> && !concepts::container_but_not_string<T> &&
!concepts::string_view_keyed_map<T> &&
@@ -53581,17 +52065,11 @@ template <class W, class T>
!std::is_same_v<T, const char*> &&
!std::is_same_v<T, char> && !require_custom_serialization<T>)
simdjson_really_inline constexpr void atom(W &w, const T &t) {
- if constexpr (simdjson::detail::has_annotation(^^T, ^^simdjson::detail::transparent_tag)) {
- // A transparent structure is serialized as its single member.
- constexpr auto dm = simdjson::detail::transparent_member(^^T);
- annotation_detail::atom_member<dm>(w, t.[:dm:]);
+ if constexpr (annotation_detail::is_recursive<T>()) {
+ // Break the cycle of simdjson_really_inline calls.
+ annotation_detail::atom_object_outlined(w, t);
} else {
- bool first = true;
- if (!w.ensure(1)) { return; }
- w.ptr[w.pos++] = '{';
- annotation_detail::atom_fields(w, t, first);
- if (!w.ensure(1)) { return; }
- w.ptr[w.pos++] = '}';
+ annotation_detail::atom_object(w, t);
}
}
@@ -53691,25 +52169,30 @@ namespace bound_detail {
// Whether size_bound covers everything that atom() writes for T: not when a
// member is serialized by a with<Adapter> serializer, which writes an unknown
// amount through the string_builder.
-template <class T>
+// Visiting lists the structures being checked, so that a recursive type
+// (struct Node { std::unique_ptr<Node> next; }) does not recurse forever: a
+// type already being visited adds nothing, its members get checked once.
+template <class T, class... Visiting>
consteval bool is_bounded() {
- if constexpr (require_custom_serialization<T>) {
+ if constexpr ((std::is_same_v<T, Visiting> || ...)) {
+ return true;
+ } else if constexpr (require_custom_serialization<T>) {
return false;
} else if constexpr (std::is_same_v<T, std::string> || std::is_same_v<T, std::string_view> ||
std::is_same_v<T, const char *> || std::is_arithmetic_v<T> || std::is_enum_v<T>) {
return true;
} else if constexpr (concepts::optional_type<T> || concepts::smart_pointer<T>) {
- return is_bounded<std::remove_cvref_t<decltype(*std::declval<const T &>())>>();
+ return is_bounded<std::remove_cvref_t<decltype(*std::declval<const T &>())>, Visiting...>();
} else if constexpr (concepts::string_view_keyed_map<T>) {
- return is_bounded<std::remove_cvref_t<typename T::mapped_type>>();
+ return is_bounded<std::remove_cvref_t<typename T::mapped_type>, Visiting...>();
} else if constexpr (concepts::container_but_not_string<T> || concepts::appendable_containers<T>) {
- return is_bounded<std::remove_cvref_t<std::ranges::range_value_t<T>>>();
+ return is_bounded<std::remove_cvref_t<std::ranges::range_value_t<T>>, Visiting...>();
} else {
bool bounded = true;
template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) {
if constexpr (annotation_detail::is_serialized_member(dm)) {
bounded = bounded && simdjson::detail::annotation_of_template(dm, ^^simdjson::detail::with_t) == std::meta::info{} &&
- is_bounded<std::remove_cvref_t<decltype(std::declval<const T &>().[:dm:])>>();
+ is_bounded<std::remove_cvref_t<decltype(std::declval<const T &>().[:dm:])>, T, Visiting...>();
}
};
return bounded;
@@ -53747,6 +52230,11 @@ simdjson_really_inline size_t fields_bound(const T &t) noexcept {
return bound;
}
+template <class T>
+size_t fields_bound_outlined(const T &t) noexcept {
+ return fields_bound(t);
+}
+
template <class T>
simdjson_really_inline size_t size_bound([[maybe_unused]] const T &t) noexcept {
if constexpr (std::is_same_v<T, char>) {
@@ -53791,6 +52279,9 @@ simdjson_really_inline size_t size_bound([[maybe_unused]] const T &t) noexcept {
} else if constexpr (simdjson::detail::has_annotation(^^T, ^^simdjson::detail::transparent_tag)) {
constexpr auto dm = simdjson::detail::transparent_member(^^T);
return size_bound(t.[:dm:]);
+ } else if constexpr (annotation_detail::is_recursive<T>()) {
+ // Break the cycle of simdjson_really_inline calls.
+ return 2 + fields_bound_outlined(t);
} else {
return 2 + fields_bound(t);
}
@@ -53925,10 +52416,7 @@ simdjson_warn_unused error_code to_json(const Z &z, std::string &s, size_t initi
} else {
string_builder b(initial_capacity);
append(b, z);
- std::string_view view;
- if(auto e = b.view().get(view); e) { return e; }
- s.assign(view);
- return SUCCESS;
+ return b.move_to(s);
}
}
@@ -53991,9 +52479,9 @@ template<constevalutil::fixed_string... FieldNames, typename T>
simdjson_warn_unused simdjson_result<std::string> extract_from(const T &obj, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) {
string_builder b(initial_capacity);
extract_from<FieldNames...>(b, obj);
- std::string_view s;
- if(auto e = b.view().get(s); e) { return e; }
- return std::string(s);
+ std::string s;
+ if(auto e = b.move_to(s); e) { return e; }
+ return s;
}
SIMDJSON_POP_DISABLE_WARNINGS
@@ -54015,16 +52503,16 @@ template<constevalutil::fixed_string... FieldNames, typename T>
simdjson_warn_unused simdjson_result<std::string> extract_from(const T &obj, size_t initial_capacity = haswell::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
haswell::builder::string_builder b(initial_capacity);
haswell::builder::extract_from<FieldNames...>(b, obj);
- std::string_view s;
- if(auto e = b.view().get(s); e) { return e; }
- return std::string(s);
+ std::string s;
+ if(auto e = b.move_to(s); e) { return e; }
+ return s;
}
} // namespace simdjson
#endif // SIMDJSON_STATIC_REFLECTION
-#endif
+#endif // SIMDJSON_GENERIC_BUILDER_H
/* end file simdjson/generic/builder/json_builder.h for haswell */
/* including simdjson/generic/builder/fractured_json_builder.h for haswell: #include "simdjson/generic/builder/fractured_json_builder.h" */
/* begin file simdjson/generic/builder/fractured_json_builder.h for haswell */
@@ -54163,6 +52651,7 @@ simdjson_warn_unused simdjson_result<std::string> extract_fractured_json(
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_STRING_BUILDER_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/builder/json_string_builder.h" */
+/* amalgamation skipped (editor-only): #include "simdjson/internal/jeaiii.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
/*
@@ -54275,6 +52764,12 @@ inline bool has_json_escapable_byte(uint64_t x) {
**/
+#if SIMDJSON_EXPERIMENTAL_HAS_SSE2 || SIMDJSON_EXPERIMENTAL_HAS_NEON
+#define SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE 1
+#endif
+
+// With block escaping (SSE2/NEON), write_string_escaped scans the input itself.
+#if !SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE
// Scalar fallback for finding next quotable character
SIMDJSON_CONSTEXPR_LAMBDA simdjson_inline size_t
find_next_json_quotable_character_scalar(const std::string_view view,
@@ -54290,82 +52785,7 @@ find_next_json_quotable_character_scalar(const std::string_view view,
// SIMD-accelerated position finding that directly locates the first quotable
// character, combining detection and position extraction in a single pass to
// minimize redundant work.
-#if SIMDJSON_EXPERIMENTAL_HAS_NEON
-simdjson_inline size_t
-find_next_json_quotable_character(const std::string_view view,
- size_t location) noexcept {
- const size_t len = view.size();
- const uint8_t *ptr =
- reinterpret_cast<const uint8_t *>(view.data()) + location;
- size_t remaining = len - location;
-
- // SIMD constants for characters requiring escape
- uint8x16_t v34 = vdupq_n_u8(34); // '"'
- uint8x16_t v92 = vdupq_n_u8(92); // '\\'
- uint8x16_t v32 = vdupq_n_u8(32); // control char threshold
-
- while (remaining >= 16) {
- uint8x16_t word = vld1q_u8(ptr);
-
- // Check for quotable characters: '"', '\\', or control chars (< 32)
- uint8x16_t needs_escape = vceqq_u8(word, v34);
- needs_escape = vorrq_u8(needs_escape, vceqq_u8(word, v92));
- needs_escape = vorrq_u8(needs_escape, vcltq_u8(word, v32));
-
- const uint8x8_t res = vshrn_n_u16(vreinterpretq_u16_u8(needs_escape), 4);
- const uint64_t mask = vget_lane_u64(vreinterpret_u64_u8(res), 0);
- if(mask != 0) {
- size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
- auto trailing_zero = trailing_zeroes(mask);
- return offset + (trailing_zero >> 2);
- }
- ptr += 16;
- remaining -= 16;
- }
-
- // Scalar fallback for remaining bytes
- size_t current = len - remaining;
- return find_next_json_quotable_character_scalar(view, current);
-}
-#elif SIMDJSON_EXPERIMENTAL_HAS_SSE2
-simdjson_inline size_t
-find_next_json_quotable_character(const std::string_view view,
- size_t location) noexcept {
- const size_t len = view.size();
- const uint8_t *ptr =
- reinterpret_cast<const uint8_t *>(view.data()) + location;
- size_t remaining = len - location;
-
- // SIMD constants
- __m128i v34 = _mm_set1_epi8(34); // '"'
- __m128i v92 = _mm_set1_epi8(92); // '\\'
- __m128i v31 = _mm_set1_epi8(31); // for control char detection
-
- while (remaining >= 16) {
- __m128i word = _mm_loadu_si128(reinterpret_cast<const __m128i *>(ptr));
-
- // Check for quotable characters
- __m128i needs_escape = _mm_cmpeq_epi8(word, v34);
- needs_escape = _mm_or_si128(needs_escape, _mm_cmpeq_epi8(word, v92));
- needs_escape = _mm_or_si128(
- needs_escape,
- _mm_cmpeq_epi8(_mm_subs_epu8(word, v31), _mm_setzero_si128()));
-
- int mask = _mm_movemask_epi8(needs_escape);
- if (mask != 0) {
- // Found quotable character - use trailing zero count to find position
- size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
- return offset + trailing_zeroes(mask);
- }
- ptr += 16;
- remaining -= 16;
- }
-
- // Scalar fallback for remaining bytes
- size_t current = len - remaining;
- return find_next_json_quotable_character_scalar(view, current);
-}
-#elif SIMDJSON_EXPERIMENTAL_HAS_LASX
+#if SIMDJSON_EXPERIMENTAL_HAS_LASX
simdjson_inline size_t
find_next_json_quotable_character(const std::string_view view,
size_t location) noexcept {
@@ -54538,6 +52958,7 @@ find_next_json_quotable_character(const std::string_view view,
return find_next_json_quotable_character_scalar(view, location);
}
#endif
+#endif // !SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE
SIMDJSON_CONSTEXPR_LAMBDA static std::string_view control_chars[] = {
"\\u0000", "\\u0001", "\\u0002", "\\u0003", "\\u0004", "\\u0005", "\\u0006",
@@ -54565,10 +52986,6 @@ SIMDJSON_CONSTEXPR_LAMBDA simdjson_inline void escape_json_char(char c, char *&o
}
}
-#if SIMDJSON_EXPERIMENTAL_HAS_SSE2 || SIMDJSON_EXPERIMENTAL_HAS_NEON
-#define SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE 1
-#endif
-
#if SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE
#if SIMDJSON_EXPERIMENTAL_HAS_SSE2
@@ -54849,10 +53266,21 @@ inline size_t write_string_escaped(const std::string_view input, char *out) {
#undef SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE
-simdjson_inline string_builder::string_builder(size_t initial_capacity)
- : buffer(new(std::nothrow) char[initial_capacity]), position(0),
- capacity(buffer.get() != nullptr ? initial_capacity : 0),
- is_valid(buffer.get() != nullptr) {}
+namespace internal {
+// Sets the size of s to n without initializing the new bytes when the
+// standard library allows it (C++23 resize_and_overwrite).
+simdjson_inline void resize_uninitialized(std::string &s, size_t n) {
+#if defined(__cpp_lib_string_resize_and_overwrite) && __cpp_lib_string_resize_and_overwrite >= 202110L
+ s.resize_and_overwrite(n, [](char *, size_t len) noexcept { return len; });
+#else
+ s.resize(n);
+#endif
+}
+} // namespace internal
+
+simdjson_inline string_builder::string_builder(size_t initial_capacity) {
+ grow_buffer(initial_capacity);
+}
simdjson_inline bool string_builder::capacity_check(size_t upcoming_bytes) {
// We use the convention that when is_valid is false, then the capacity and
@@ -54875,22 +53303,63 @@ inline void string_builder::grow_buffer(size_t desired_capacity) {
if (!is_valid) {
return;
}
- std::unique_ptr<char[]> new_buffer(new (std::nothrow) char[desired_capacity]);
- if (new_buffer.get() == nullptr) {
+ std::string new_buffer;
+ if (desired_capacity > new_buffer.max_size()) {
+ set_valid(false);
+ return;
+ }
+#if defined(__cpp_exceptions) || defined(_CPPUNWIND)
+ try {
+ internal::resize_uninitialized(new_buffer, desired_capacity);
+ } catch (...) { // std::bad_alloc
set_valid(false);
return;
}
- std::memcpy(new_buffer.get(), buffer.get(), position);
+#else
+ // Without exceptions, a failed allocation terminates the program.
+ internal::resize_uninitialized(new_buffer, desired_capacity);
+#endif
+ std::memcpy(&new_buffer[0], buffer_data(), position);
buffer.swap(new_buffer);
capacity = desired_capacity;
}
+// Not simdjson_inline: MSVC cannot force-inline a function with a try block (C4714).
+inline error_code string_builder::move_to(std::string &out) noexcept {
+ if (!is_valid) {
+ return simdjson::OUT_OF_CAPACITY;
+ }
+ if (position <= buffer.capacity() / 2) {
+ // Most of the buffer is unused: copy the bytes out, so that a small
+ // result does not keep the whole buffer allocated. The builder keeps its
+ // buffer for what it writes next.
+#if defined(__cpp_exceptions) || defined(_CPPUNWIND)
+ try {
+ out.assign(buffer.data(), position);
+ } catch (...) { // std::bad_alloc
+ return simdjson::MEMALLOC;
+ }
+#else
+ // Without exceptions, a failed allocation terminates the program.
+ out.assign(buffer.data(), position);
+#endif
+ position = 0;
+ return simdjson::SUCCESS;
+ }
+ buffer.resize(position); // shrinking never allocates
+ out = std::move(buffer);
+ std::string().swap(buffer);
+ position = 0;
+ capacity = 0;
+ return simdjson::SUCCESS;
+}
+
simdjson_inline void string_builder::set_valid(bool valid) noexcept {
if (!valid) {
is_valid = false;
capacity = 0;
position = 0;
- buffer.reset();
+ std::string().swap(buffer);
} else {
is_valid = true;
}
@@ -54902,7 +53371,7 @@ simdjson_inline size_t string_builder::size() const noexcept {
simdjson_inline void string_builder::append(char c) noexcept {
if (capacity_check(1)) {
- buffer.get()[position++] = c;
+ buffer_data()[position++] = c;
}
}
@@ -54910,7 +53379,7 @@ simdjson_inline void string_builder::append_null() noexcept {
constexpr char null_literal[] = "null";
constexpr size_t null_len = sizeof(null_literal) - 1;
if (capacity_check(null_len)) {
- std::memcpy(buffer.get() + position, null_literal, null_len);
+ std::memcpy(buffer_data() + position, null_literal, null_len);
position += null_len;
}
}
@@ -54920,124 +53389,14 @@ simdjson_inline void string_builder::clear() noexcept {
// if it was invalid, we should try to repair it
if (!is_valid) {
capacity = 0;
- buffer.reset();
+ std::string().swap(buffer);
is_valid = true;
}
}
namespace internal {
-// Integer to decimal: James Edward Anhalt III's algorithm
-static const char jeaiii_dd[201] =
- "00010203040506070809101112131415161718192021222324252627282930313233343536373839"
- "40414243444546474849505152535455565758596061626364656667686970717273747576777879"
- "8081828384858687888990919293949596979899";
-static const char jeaiii_fd[201] =
- "0\0" "1\0" "2\0" "3\0" "4\0" "5\0" "6\0" "7\0" "8\0" "9\0"
- "10111213141516171819202122232425262728293031323334353637383940414243444546474849"
- "50515253545556575859606162636465666768697071727374757677787980818283848586878889"
- "90919293949596979899";
-
-simdjson_really_inline void jeaiii_write_dd(char *p, uint64_t k) noexcept {
- std::memcpy(p, &jeaiii_dd[2 * k], 2);
-}
-simdjson_really_inline void jeaiii_write_fd(char *p, uint64_t k) noexcept {
- std::memcpy(p, &jeaiii_fd[2 * k], 2);
-}
-
-// Caller guarantees n < 10^8. Writes 1 to 8 digits.
-simdjson_really_inline char *jeaiii_lt1e8(char *b, uint32_t n) noexcept {
- constexpr uint64_t mask24 = (uint64_t(1) << 24) - 1;
- constexpr uint64_t mask32 = (uint64_t(1) << 32) - 1;
- if (n < 100) {
- jeaiii_write_fd(b, n);
- return n < 10 ? b + 1 : b + 2;
- }
- if (n < 1000000) {
- if (n < 10000) {
- const uint32_t f0 = uint32_t(10 * (1 << 24) / 1e3 + 1) * n;
- jeaiii_write_fd(b, f0 >> 24);
- b -= n < 1000;
- const uint32_t f2 = uint32_t(f0 & mask24) * 100;
- jeaiii_write_dd(b + 2, f2 >> 24);
- return b + 4;
- }
- const uint64_t f0 = uint64_t(10 * (1ull << 32) / 1e5 + 1) * n;
- jeaiii_write_fd(b, f0 >> 32);
- b -= n < 100000;
- const uint64_t f2 = (f0 & mask32) * 100;
- jeaiii_write_dd(b + 2, f2 >> 32);
- const uint64_t f4 = (f2 & mask32) * 100;
- jeaiii_write_dd(b + 4, f4 >> 32);
- return b + 6;
- }
- const uint64_t f0 = uint64_t(10 * (1ull << 48) / 1e7 + 1) * n >> 16;
- jeaiii_write_fd(b, f0 >> 32);
- b -= n < 10000000;
- const uint64_t f2 = (f0 & mask32) * 100;
- jeaiii_write_dd(b + 2, f2 >> 32);
- const uint64_t f4 = (f2 & mask32) * 100;
- jeaiii_write_dd(b + 4, f4 >> 32);
- const uint64_t f6 = (f4 & mask32) * 100;
- jeaiii_write_dd(b + 6, f6 >> 32);
- return b + 8;
-}
-
-// Caller guarantees z < 10^8. Always writes exactly 8 digits.
-simdjson_really_inline char *jeaiii_8_digits(char *b, uint32_t z) noexcept {
- constexpr uint64_t mask32 = (uint64_t(1) << 32) - 1;
- const uint64_t f0 = (uint64_t((1ull << 48) / 1e6 + 1) * z >> 16) + 1;
- jeaiii_write_dd(b, f0 >> 32);
- const uint64_t f2 = (f0 & mask32) * 100;
- jeaiii_write_dd(b + 2, f2 >> 32);
- const uint64_t f4 = (f2 & mask32) * 100;
- jeaiii_write_dd(b + 4, f4 >> 32);
- const uint64_t f6 = (f4 & mask32) * 100;
- jeaiii_write_dd(b + 6, f6 >> 32);
- return b + 8;
-}
-
-// Caller guarantees 10^8 <= n < 2^32. Writes 9 or 10 digits.
-simdjson_really_inline char *jeaiii_9_or_10(char *b, uint64_t n) noexcept {
- constexpr uint64_t mask57 = (uint64_t(1) << 57) - 1;
- const uint64_t f0 = uint64_t(10 * (1ull << 57) / 1e9 + 1) * n;
- jeaiii_write_fd(b, f0 >> 57);
- b -= n < 1000000000;
- const uint64_t f2 = (f0 & mask57) * 100;
- jeaiii_write_dd(b + 2, f2 >> 57);
- const uint64_t f4 = (f2 & mask57) * 100;
- jeaiii_write_dd(b + 4, f4 >> 57);
- const uint64_t f6 = (f4 & mask57) * 100;
- jeaiii_write_dd(b + 6, f6 >> 57);
- const uint64_t f8 = (f6 & mask57) * 100;
- jeaiii_write_dd(b + 8, f8 >> 57);
- return b + 10;
-}
-
-simdjson_really_inline char *write_uint_jeaiii(char *b, uint64_t n) noexcept {
- if (n < 100000000) {
- return jeaiii_lt1e8(b, uint32_t(n));
- }
- if (n < (uint64_t(1) << 32)) {
- return jeaiii_9_or_10(b, n);
- }
- // At least 10 digits: the low 8 digits, and 2 to 12 digits above them.
- const uint32_t z = uint32_t(n % 100000000);
- uint64_t u = n / 100000000;
- if (u < 100000000) {
- // u has 2 to 8 digits (if u < 10, n would be below 2^32).
- b = jeaiii_lt1e8(b, uint32_t(u));
- } else if (u < (uint64_t(1) << 32)) {
- b = jeaiii_9_or_10(b, u);
- } else {
- // u has 11 or 12 digits: split off 8 more.
- const uint32_t y = uint32_t(u % 100000000);
- u /= 100000000;
- b = jeaiii_lt1e8(b, uint32_t(u)); // 3 or 4 digits
- b = jeaiii_8_digits(b, y);
- }
- return jeaiii_8_digits(b, z);
-}
+using simdjson::internal::write_uint_jeaiii;
// Writes v at p, which must have to_chars_buffer_size bytes available, and
// returns the end of what was written.
@@ -55065,20 +53424,22 @@ simdjson_inline void string_builder::append(number_type v) noexcept {
std::is_integral<number_type>::value ||
std::is_floating_point<number_type>::value,
"Unsupported number type");
+ static_assert(sizeof(number_type) <= sizeof(uint64_t),
+ "Integers wider than 64 bits are not supported");
// If C++17 is available, we can 'if constexpr' here.
SIMDJSON_IF_CONSTEXPR(std::is_same<number_type, bool>::value) {
if (v) {
constexpr char true_literal[] = "true";
constexpr size_t true_len = sizeof(true_literal) - 1;
if (capacity_check(true_len)) {
- std::memcpy(buffer.get() + position, true_literal, true_len);
+ std::memcpy(buffer_data() + position, true_literal, true_len);
position += true_len;
}
} else {
constexpr char false_literal[] = "false";
constexpr size_t false_len = sizeof(false_literal) - 1;
if (capacity_check(false_len)) {
- std::memcpy(buffer.get() + position, false_literal, false_len);
+ std::memcpy(buffer_data() + position, false_literal, false_len);
position += false_len;
}
}
@@ -55088,9 +53449,9 @@ simdjson_inline void string_builder::append(number_type v) noexcept {
if (capacity_check(max_number_size)) {
using unsigned_type = typename std::make_unsigned<number_type>::type;
char* end = internal::write_uint_jeaiii(
- buffer.get() + position,
+ buffer_data() + position,
static_cast<uint64_t>(static_cast<unsigned_type>(v)));
- position = end - buffer.get();
+ position = end - buffer_data();
}
}
else SIMDJSON_IF_CONSTEXPR(std::is_integral<number_type>::value) {
@@ -55104,11 +53465,11 @@ simdjson_inline void string_builder::append(number_type v) noexcept {
? unsigned_type(0) - static_cast<unsigned_type>(v)
: static_cast<unsigned_type>(v);
// Branchless: always write '-', advance only if negative.
- buffer.get()[position] = '-';
+ buffer_data()[position] = '-';
position += negative;
char* end = internal::write_uint_jeaiii(
- buffer.get() + position, static_cast<uint64_t>(pv));
- position = end - buffer.get();
+ buffer_data() + position, static_cast<uint64_t>(pv));
+ position = end - buffer_data();
}
}
else SIMDJSON_IF_CONSTEXPR(std::is_floating_point<number_type>::value) {
@@ -55124,16 +53485,16 @@ simdjson_inline void string_builder::append(number_type v) noexcept {
constexpr char nan_literal[] = "NaN";
constexpr size_t nan_len = sizeof(nan_literal) - 1;
- std::memcpy(buffer.get() + position, nan_literal, nan_len);
+ std::memcpy(buffer_data() + position, nan_literal, nan_len);
position += nan_len;
} else {
constexpr char inf_literal[] = "Infinity";
constexpr size_t inf_len = sizeof(inf_literal) - 1;
if (v < 0) {
- buffer.get()[position] = '-';
+ buffer_data()[position] = '-';
++position;
}
- std::memcpy(buffer.get() + position, inf_literal, inf_len);
+ std::memcpy(buffer_data() + position, inf_literal, inf_len);
position += inf_len;
}
return;
@@ -55141,9 +53502,9 @@ simdjson_inline void string_builder::append(number_type v) noexcept {
#endif
// We could specialize for float.
- char *end = simdjson::internal::to_chars(buffer.get() + position, nullptr,
+ char *end = simdjson::internal::to_chars(buffer_data() + position, nullptr,
double(v));
- position = end - buffer.get();
+ position = end - buffer_data();
}
}
}
@@ -55157,7 +53518,7 @@ string_builder::escape_and_append(std::string_view input) noexcept {
return;
}
if (capacity_check(6 * input.size())) {
- position += write_string_escaped(input, buffer.get() + position);
+ position += write_string_escaped(input, buffer_data() + position);
}
}
@@ -55170,9 +53531,9 @@ string_builder::escape_and_append_with_quotes(std::string_view input) noexcept {
return;
}
if (capacity_check(2 + 6 * input.size())) {
- buffer.get()[position++] = '"';
- position += write_string_escaped(input, buffer.get() + position);
- buffer.get()[position++] = '"';
+ buffer_data()[position++] = '"';
+ position += write_string_escaped(input, buffer_data() + position);
+ buffer_data()[position++] = '"';
}
}
@@ -55180,10 +53541,10 @@ simdjson_inline void
string_builder::escape_and_append_with_quotes(char input) noexcept {
// escaping might turn a control character into \x00xx so 6 characters.
if (capacity_check(2 + 6 * 1)) {
- buffer.get()[position++] = '"';
+ buffer_data()[position++] = '"';
std::string_view cinput(&input, 1);
- position += write_string_escaped(cinput, buffer.get() + position);
- buffer.get()[position++] = '"';
+ position += write_string_escaped(cinput, buffer_data() + position);
+ buffer_data()[position++] = '"';
}
}
@@ -55213,7 +53574,7 @@ simdjson_inline void string_builder::append_raw(const char *c) noexcept {
simdjson_inline void
string_builder::append_raw(std::string_view input) noexcept {
if (capacity_check(input.size())) {
- std::memcpy(buffer.get() + position, input.data(), input.size());
+ std::memcpy(buffer_data() + position, input.data(), input.size());
position += input.size();
}
}
@@ -55221,7 +53582,7 @@ string_builder::append_raw(std::string_view input) noexcept {
simdjson_inline void string_builder::append_raw(const char *str,
size_t len) noexcept {
if (capacity_check(len)) {
- std::memcpy(buffer.get() + position, str, len);
+ std::memcpy(buffer_data() + position, str, len);
position += len;
}
}
@@ -55229,7 +53590,7 @@ simdjson_inline void string_builder::append_raw(const char *str,
template <size_t N>
simdjson_inline void string_builder::append_raw_n(const char *str) noexcept {
if (capacity_check(N)) {
- std::memcpy(buffer.get() + position, str, N);
+ std::memcpy(buffer_data() + position, str, N);
position += N;
}
}
@@ -55239,7 +53600,7 @@ template <typename Text>
simdjson_inline void string_builder::append_static_string() noexcept {
constexpr auto& padded = constevalutil::static_string<Text>;
if (capacity_check(padded.size())) {
- std::memcpy(buffer.get() + position, padded.data(), padded.size());
+ std::memcpy(buffer_data() + position, padded.data(), padded.size());
position += constevalutil::static_string_size<Text>();
}
}
@@ -55333,54 +53694,54 @@ string_builder::view() const noexcept {
if (!is_valid) {
return simdjson::OUT_OF_CAPACITY;
}
- return std::string_view(buffer.get(), position);
+ return std::string_view(buffer_data(), position);
}
simdjson_inline simdjson_result<const char *> string_builder::c_str() noexcept {
if (capacity_check(1)) {
- buffer.get()[position] = '\0';
- return buffer.get();
+ buffer_data()[position] = '\0';
+ return buffer_data();
}
return simdjson::OUT_OF_CAPACITY;
}
simdjson_inline bool string_builder::validate_unicode() const noexcept {
- return simdjson::validate_utf8(buffer.get(), position);
+ return simdjson::validate_utf8(buffer_data(), position);
}
simdjson_inline void string_builder::start_object() noexcept {
if (capacity_check(1)) {
- buffer.get()[position++] = '{';
+ buffer_data()[position++] = '{';
}
}
simdjson_inline void string_builder::end_object() noexcept {
if (capacity_check(1)) {
- buffer.get()[position++] = '}';
+ buffer_data()[position++] = '}';
}
}
simdjson_inline void string_builder::start_array() noexcept {
if (capacity_check(1)) {
- buffer.get()[position++] = '[';
+ buffer_data()[position++] = '[';
}
}
simdjson_inline void string_builder::end_array() noexcept {
if (capacity_check(1)) {
- buffer.get()[position++] = ']';
+ buffer_data()[position++] = ']';
}
}
simdjson_inline void string_builder::append_comma() noexcept {
if (capacity_check(1)) {
- buffer.get()[position++] = ',';
+ buffer_data()[position++] = ',';
}
}
simdjson_inline void string_builder::append_colon() noexcept {
if (capacity_check(1)) {
- buffer.get()[position++] = ':';
+ buffer_data()[position++] = ':';
}
}
@@ -56144,34 +54505,6 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin
}
-
-struct escaping {
- static constexpr uint32_t BYTES_PROCESSED = 64;
- simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
-
- simdjson_inline bool has_escape() { return escape_bits != 0; }
- simdjson_inline int escape_index() { return trailing_zeroes(uint64_t(escape_bits)); }
-
- __mmask64 escape_bits;
-}; // struct escaping
-
-
-
-simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
- static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes");
- simd8<uint8_t> v(src);
- v.store(dst);
- __mmask64 is_quote = _mm512_cmpeq_epi8_mask(v, _mm512_set1_epi8('"'));
- __mmask64 is_backslash = _mm512_cmpeq_epi8_mask(v, _mm512_set1_epi8('\\'));
- __mmask64 is_control = _mm512_cmplt_epi8_mask(v, _mm512_set1_epi8(32));
- return {
- (is_backslash | is_quote | is_control)
- };
-}
-
-
-
-
} // unnamed namespace
} // namespace icelake
} // namespace simdjson
@@ -56300,6 +54633,10 @@ namespace builder {
class string_builder {
public:
simdjson_inline string_builder(size_t initial_capacity = DEFAULT_INITIAL_CAPACITY);
+ string_builder(const string_builder &) = delete;
+ string_builder &operator=(const string_builder &) = delete;
+ string_builder(string_builder &&) noexcept = default;
+ string_builder &operator=(string_builder &&) noexcept = default;
static constexpr size_t DEFAULT_INITIAL_CAPACITY = 1024;
@@ -56313,6 +54650,18 @@ public:
typename = typename std::enable_if<std::is_arithmetic<number_type>::value>::type>
simdjson_inline void append(number_type v) noexcept;
+#ifdef __SIZEOF_INT128__
+ /**
+ * Integers wider than 64 bits are not supported. Without these overloads, a
+ * 128-bit integer would either be truncated to 64 bits by append(number_type)
+ * (when the standard library counts it as arithmetic, as libc++ does) or be
+ * converted to char by append(char) (when it does not, as libstdc++ does in
+ * strict ISO mode).
+ */
+ void append(__int128_t v) noexcept = delete;
+ void append(__uint128_t v) noexcept = delete;
+#endif
+
/**
* Append character c.
*/
@@ -56491,6 +54840,20 @@ requires (!std::is_convertible<R, std::string_view>::value && !concepts::optiona
*/
simdjson_inline simdjson_result<const char *> c_str() noexcept;
+ /**
+ * Moves the written JSON buffer into out, replacing its content, without
+ * copying it. When the content fills at most half of the buffer, it is
+ * copied instead, so that a small result does not keep the whole buffer
+ * allocated. The builder is left empty: it keeps its buffer when the
+ * content was copied, and gives up its memory when the buffer was moved.
+ * Returns an error, and leaves out and the builder unchanged, if memory
+ * allocation failed.
+ *
+ * The result may not be valid UTF-8 if some of your content was not valid UTF-8.
+ * Use validate_unicode() to check the content.
+ */
+ simdjson_warn_unused inline error_code move_to(std::string &out) noexcept;
+
/**
* Return true if the content is valid UTF-8.
*/
@@ -56510,7 +54873,7 @@ requires (!std::is_convertible<R, std::string_view>::value && !concepts::optiona
// forces those reloads when accessed via members of *this). User
// code should NOT call these directly.
// ============================================================
- simdjson_inline char *unsafe_data() noexcept { return buffer.get(); }
+ simdjson_inline char *unsafe_data() noexcept { return buffer_data(); }
simdjson_inline size_t unsafe_position() const noexcept { return position; }
simdjson_inline size_t unsafe_capacity() const noexcept { return capacity; }
simdjson_inline void unsafe_set_position(size_t p) noexcept { position = p; }
@@ -56551,7 +54914,13 @@ private:
*/
simdjson_inline void set_valid(bool valid) noexcept;
- std::unique_ptr<char[]> buffer{};
+ simdjson_inline char *buffer_data() noexcept { return &buffer[0]; }
+ simdjson_inline const char *buffer_data() const noexcept { return buffer.data(); }
+
+ // We own capacity bytes: buffer.size() == capacity. Only the first position
+ // bytes hold JSON. A std::string lets move_to() hand the result over
+ // without a copy.
+ std::string buffer{};
size_t position{0};
size_t capacity{0};
bool is_valid{true};
@@ -56569,20 +54938,16 @@ template <class Z>
simdjson_warn_unused simdjson_result<std::string> to_json(const Z &z, size_t initial_capacity = simdjson::icelake::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
simdjson::icelake::builder::string_builder b(initial_capacity);
b.append(z);
- std::string_view s;
- auto e = b.view().get(s);
+ std::string s;
+ auto e = b.move_to(s);
if(e) { return e; }
- return std::string(s);
+ return s;
}
template <class Z>
simdjson_warn_unused error_code to_json(const Z &z, std::string &s, size_t initial_capacity = simdjson::icelake::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
simdjson::icelake::builder::string_builder b(initial_capacity);
b.append(z);
- std::string_view sv;
- auto e = b.view().get(sv);
- if(e) { return e; }
- s.assign(sv.data(), sv.size());
- return simdjson::SUCCESS;
+ return b.move_to(s);
}
#endif
@@ -56598,7 +54963,7 @@ simdjson_warn_unused error_code to_json(const Z &z, std::string &s, size_t initi
#ifndef SIMDJSON_GENERIC_BUILDER_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
-/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_STRING_BUILDER_H */
+/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_BUILDER_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/builder/json_string_builder.h" */
/* amalgamation skipped (editor-only): #include "simdjson/concepts.h" */
/* amalgamation skipped (editor-only): #include "simdjson/annotations.h" */
@@ -56624,7 +54989,7 @@ namespace builder {
// included at the point this header is parsed; without these forwards,
// name lookup falls back to the wrong outer namespace).
namespace internal {
-simdjson_really_inline char *write_uint_jeaiii(char *p, uint64_t v) noexcept;
+using simdjson::internal::write_uint_jeaiii;
simdjson_inline char *write_double(char *p, double v) noexcept;
} // namespace internal
simdjson_really_inline size_t write_string_escaped(const std::string_view input, char *out);
@@ -56757,6 +55122,51 @@ consteval bool is_serialized_member(std::meta::info dm) {
&& !simdjson::detail::has_annotation(dm, ^^simdjson::detail::skip_serializing_tag);
}
+template <class Target, class T, class... Visiting>
+consteval bool members_reach();
+
+// Whether a value of type T may hold a value of type Target, directly or
+// through optionals, smart pointers, containers and members. Visiting lists
+// the structures being searched, so that the search terminates.
+template <class Target, class T, class... Visiting>
+consteval bool reaches() {
+ if constexpr (std::is_same_v<T, Target>) {
+ return true;
+ } else if constexpr ((std::is_same_v<T, Visiting> || ...) || require_custom_serialization<T> ||
+ std::is_same_v<T, std::string> || std::is_same_v<T, std::string_view>) {
+ return false;
+ } else if constexpr (concepts::optional_type<T> || concepts::smart_pointer<T>) {
+ return reaches<Target, std::remove_cvref_t<decltype(*std::declval<const T &>())>, Visiting...>();
+ } else if constexpr (concepts::string_view_keyed_map<T>) {
+ return reaches<Target, std::remove_cvref_t<typename T::mapped_type>, Visiting...>();
+ } else if constexpr (concepts::container_but_not_string<T> || concepts::appendable_containers<T>) {
+ return reaches<Target, std::remove_cvref_t<std::ranges::range_value_t<T>>, Visiting...>();
+ } else if constexpr (std::is_class_v<T>) {
+ return members_reach<Target, T, Visiting...>();
+ } else {
+ return false;
+ }
+}
+
+template <class Target, class T, class... Visiting>
+consteval bool members_reach() {
+ bool found = false;
+ template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) {
+ if constexpr (is_serialized_member(dm)) {
+ found = found || reaches<Target, std::remove_cvref_t<decltype(std::declval<const T &>().[:dm:])>, T, Visiting...>();
+ }
+ };
+ return found;
+}
+
+// Whether the structure T may contain itself, as in
+// struct Rule { std::unique_ptr<Rule> subrule; }. Serializing such a type
+// recurses at runtime, which simdjson_really_inline functions cannot do.
+template <class T>
+consteval bool is_recursive() {
+ return members_reach<T, T>();
+}
+
// False when the member has a skip_serializing_if<pred> annotation and
// pred(value) is true.
template <auto dm, typename V>
@@ -56940,6 +55350,8 @@ simdjson_really_inline constexpr void atom(W &w, const T &m) {
template<class W, typename number_type,
typename = typename std::enable_if<std::is_arithmetic<number_type>::value && !std::is_same_v<number_type, char>>::type>
simdjson_really_inline constexpr void atom(W &w, const number_type t) {
+ static_assert(sizeof(number_type) <= sizeof(uint64_t),
+ "Integers wider than 64 bits are not supported");
// Booleans / floats: defer to string_builder (rare path; keeps writer hot
// path free of float-formatter machinery). For integers, write directly
// via jeaiii using local pos.
@@ -56978,6 +55390,31 @@ simdjson_really_inline constexpr void atom(W &w, const number_type t) {
}
}
+namespace annotation_detail {
+
+template <class W, class T>
+simdjson_really_inline void atom_object(W &w, const T &t) {
+ if constexpr (simdjson::detail::has_annotation(^^T, ^^simdjson::detail::transparent_tag)) {
+ // A transparent structure is serialized as its single member.
+ constexpr auto dm = simdjson::detail::transparent_member(^^T);
+ atom_member<dm>(w, t.[:dm:]);
+ } else {
+ bool first = true;
+ if (!w.ensure(1)) { return; }
+ w.ptr[w.pos++] = '{';
+ atom_fields(w, t, first);
+ if (!w.ensure(1)) { return; }
+ w.ptr[w.pos++] = '}';
+ }
+}
+
+template <class W, class T>
+void atom_object_outlined(W &w, const T &t) {
+ atom_object(w, t);
+}
+
+} // namespace annotation_detail
+
template <class W, class T>
requires(std::is_class_v<T> && !concepts::container_but_not_string<T> &&
!concepts::string_view_keyed_map<T> &&
@@ -56989,17 +55426,11 @@ template <class W, class T>
!std::is_same_v<T, const char*> &&
!std::is_same_v<T, char> && !require_custom_serialization<T>)
simdjson_really_inline constexpr void atom(W &w, const T &t) {
- if constexpr (simdjson::detail::has_annotation(^^T, ^^simdjson::detail::transparent_tag)) {
- // A transparent structure is serialized as its single member.
- constexpr auto dm = simdjson::detail::transparent_member(^^T);
- annotation_detail::atom_member<dm>(w, t.[:dm:]);
+ if constexpr (annotation_detail::is_recursive<T>()) {
+ // Break the cycle of simdjson_really_inline calls.
+ annotation_detail::atom_object_outlined(w, t);
} else {
- bool first = true;
- if (!w.ensure(1)) { return; }
- w.ptr[w.pos++] = '{';
- annotation_detail::atom_fields(w, t, first);
- if (!w.ensure(1)) { return; }
- w.ptr[w.pos++] = '}';
+ annotation_detail::atom_object(w, t);
}
}
@@ -57099,25 +55530,30 @@ namespace bound_detail {
// Whether size_bound covers everything that atom() writes for T: not when a
// member is serialized by a with<Adapter> serializer, which writes an unknown
// amount through the string_builder.
-template <class T>
+// Visiting lists the structures being checked, so that a recursive type
+// (struct Node { std::unique_ptr<Node> next; }) does not recurse forever: a
+// type already being visited adds nothing, its members get checked once.
+template <class T, class... Visiting>
consteval bool is_bounded() {
- if constexpr (require_custom_serialization<T>) {
+ if constexpr ((std::is_same_v<T, Visiting> || ...)) {
+ return true;
+ } else if constexpr (require_custom_serialization<T>) {
return false;
} else if constexpr (std::is_same_v<T, std::string> || std::is_same_v<T, std::string_view> ||
std::is_same_v<T, const char *> || std::is_arithmetic_v<T> || std::is_enum_v<T>) {
return true;
} else if constexpr (concepts::optional_type<T> || concepts::smart_pointer<T>) {
- return is_bounded<std::remove_cvref_t<decltype(*std::declval<const T &>())>>();
+ return is_bounded<std::remove_cvref_t<decltype(*std::declval<const T &>())>, Visiting...>();
} else if constexpr (concepts::string_view_keyed_map<T>) {
- return is_bounded<std::remove_cvref_t<typename T::mapped_type>>();
+ return is_bounded<std::remove_cvref_t<typename T::mapped_type>, Visiting...>();
} else if constexpr (concepts::container_but_not_string<T> || concepts::appendable_containers<T>) {
- return is_bounded<std::remove_cvref_t<std::ranges::range_value_t<T>>>();
+ return is_bounded<std::remove_cvref_t<std::ranges::range_value_t<T>>, Visiting...>();
} else {
bool bounded = true;
template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) {
if constexpr (annotation_detail::is_serialized_member(dm)) {
bounded = bounded && simdjson::detail::annotation_of_template(dm, ^^simdjson::detail::with_t) == std::meta::info{} &&
- is_bounded<std::remove_cvref_t<decltype(std::declval<const T &>().[:dm:])>>();
+ is_bounded<std::remove_cvref_t<decltype(std::declval<const T &>().[:dm:])>, T, Visiting...>();
}
};
return bounded;
@@ -57155,6 +55591,11 @@ simdjson_really_inline size_t fields_bound(const T &t) noexcept {
return bound;
}
+template <class T>
+size_t fields_bound_outlined(const T &t) noexcept {
+ return fields_bound(t);
+}
+
template <class T>
simdjson_really_inline size_t size_bound([[maybe_unused]] const T &t) noexcept {
if constexpr (std::is_same_v<T, char>) {
@@ -57199,6 +55640,9 @@ simdjson_really_inline size_t size_bound([[maybe_unused]] const T &t) noexcept {
} else if constexpr (simdjson::detail::has_annotation(^^T, ^^simdjson::detail::transparent_tag)) {
constexpr auto dm = simdjson::detail::transparent_member(^^T);
return size_bound(t.[:dm:]);
+ } else if constexpr (annotation_detail::is_recursive<T>()) {
+ // Break the cycle of simdjson_really_inline calls.
+ return 2 + fields_bound_outlined(t);
} else {
return 2 + fields_bound(t);
}
@@ -57333,10 +55777,7 @@ simdjson_warn_unused error_code to_json(const Z &z, std::string &s, size_t initi
} else {
string_builder b(initial_capacity);
append(b, z);
- std::string_view view;
- if(auto e = b.view().get(view); e) { return e; }
- s.assign(view);
- return SUCCESS;
+ return b.move_to(s);
}
}
@@ -57399,9 +55840,9 @@ template<constevalutil::fixed_string... FieldNames, typename T>
simdjson_warn_unused simdjson_result<std::string> extract_from(const T &obj, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) {
string_builder b(initial_capacity);
extract_from<FieldNames...>(b, obj);
- std::string_view s;
- if(auto e = b.view().get(s); e) { return e; }
- return std::string(s);
+ std::string s;
+ if(auto e = b.move_to(s); e) { return e; }
+ return s;
}
SIMDJSON_POP_DISABLE_WARNINGS
@@ -57423,16 +55864,16 @@ template<constevalutil::fixed_string... FieldNames, typename T>
simdjson_warn_unused simdjson_result<std::string> extract_from(const T &obj, size_t initial_capacity = icelake::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
icelake::builder::string_builder b(initial_capacity);
icelake::builder::extract_from<FieldNames...>(b, obj);
- std::string_view s;
- if(auto e = b.view().get(s); e) { return e; }
- return std::string(s);
+ std::string s;
+ if(auto e = b.move_to(s); e) { return e; }
+ return s;
}
} // namespace simdjson
#endif // SIMDJSON_STATIC_REFLECTION
-#endif
+#endif // SIMDJSON_GENERIC_BUILDER_H
/* end file simdjson/generic/builder/json_builder.h for icelake */
/* including simdjson/generic/builder/fractured_json_builder.h for icelake: #include "simdjson/generic/builder/fractured_json_builder.h" */
/* begin file simdjson/generic/builder/fractured_json_builder.h for icelake */
@@ -57571,6 +56012,7 @@ simdjson_warn_unused simdjson_result<std::string> extract_fractured_json(
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_STRING_BUILDER_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/builder/json_string_builder.h" */
+/* amalgamation skipped (editor-only): #include "simdjson/internal/jeaiii.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
/*
@@ -57683,6 +56125,12 @@ inline bool has_json_escapable_byte(uint64_t x) {
**/
+#if SIMDJSON_EXPERIMENTAL_HAS_SSE2 || SIMDJSON_EXPERIMENTAL_HAS_NEON
+#define SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE 1
+#endif
+
+// With block escaping (SSE2/NEON), write_string_escaped scans the input itself.
+#if !SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE
// Scalar fallback for finding next quotable character
SIMDJSON_CONSTEXPR_LAMBDA simdjson_inline size_t
find_next_json_quotable_character_scalar(const std::string_view view,
@@ -57698,82 +56146,7 @@ find_next_json_quotable_character_scalar(const std::string_view view,
// SIMD-accelerated position finding that directly locates the first quotable
// character, combining detection and position extraction in a single pass to
// minimize redundant work.
-#if SIMDJSON_EXPERIMENTAL_HAS_NEON
-simdjson_inline size_t
-find_next_json_quotable_character(const std::string_view view,
- size_t location) noexcept {
- const size_t len = view.size();
- const uint8_t *ptr =
- reinterpret_cast<const uint8_t *>(view.data()) + location;
- size_t remaining = len - location;
-
- // SIMD constants for characters requiring escape
- uint8x16_t v34 = vdupq_n_u8(34); // '"'
- uint8x16_t v92 = vdupq_n_u8(92); // '\\'
- uint8x16_t v32 = vdupq_n_u8(32); // control char threshold
-
- while (remaining >= 16) {
- uint8x16_t word = vld1q_u8(ptr);
-
- // Check for quotable characters: '"', '\\', or control chars (< 32)
- uint8x16_t needs_escape = vceqq_u8(word, v34);
- needs_escape = vorrq_u8(needs_escape, vceqq_u8(word, v92));
- needs_escape = vorrq_u8(needs_escape, vcltq_u8(word, v32));
-
- const uint8x8_t res = vshrn_n_u16(vreinterpretq_u16_u8(needs_escape), 4);
- const uint64_t mask = vget_lane_u64(vreinterpret_u64_u8(res), 0);
- if(mask != 0) {
- size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
- auto trailing_zero = trailing_zeroes(mask);
- return offset + (trailing_zero >> 2);
- }
- ptr += 16;
- remaining -= 16;
- }
-
- // Scalar fallback for remaining bytes
- size_t current = len - remaining;
- return find_next_json_quotable_character_scalar(view, current);
-}
-#elif SIMDJSON_EXPERIMENTAL_HAS_SSE2
-simdjson_inline size_t
-find_next_json_quotable_character(const std::string_view view,
- size_t location) noexcept {
- const size_t len = view.size();
- const uint8_t *ptr =
- reinterpret_cast<const uint8_t *>(view.data()) + location;
- size_t remaining = len - location;
-
- // SIMD constants
- __m128i v34 = _mm_set1_epi8(34); // '"'
- __m128i v92 = _mm_set1_epi8(92); // '\\'
- __m128i v31 = _mm_set1_epi8(31); // for control char detection
-
- while (remaining >= 16) {
- __m128i word = _mm_loadu_si128(reinterpret_cast<const __m128i *>(ptr));
-
- // Check for quotable characters
- __m128i needs_escape = _mm_cmpeq_epi8(word, v34);
- needs_escape = _mm_or_si128(needs_escape, _mm_cmpeq_epi8(word, v92));
- needs_escape = _mm_or_si128(
- needs_escape,
- _mm_cmpeq_epi8(_mm_subs_epu8(word, v31), _mm_setzero_si128()));
-
- int mask = _mm_movemask_epi8(needs_escape);
- if (mask != 0) {
- // Found quotable character - use trailing zero count to find position
- size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
- return offset + trailing_zeroes(mask);
- }
- ptr += 16;
- remaining -= 16;
- }
-
- // Scalar fallback for remaining bytes
- size_t current = len - remaining;
- return find_next_json_quotable_character_scalar(view, current);
-}
-#elif SIMDJSON_EXPERIMENTAL_HAS_LASX
+#if SIMDJSON_EXPERIMENTAL_HAS_LASX
simdjson_inline size_t
find_next_json_quotable_character(const std::string_view view,
size_t location) noexcept {
@@ -57946,6 +56319,7 @@ find_next_json_quotable_character(const std::string_view view,
return find_next_json_quotable_character_scalar(view, location);
}
#endif
+#endif // !SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE
SIMDJSON_CONSTEXPR_LAMBDA static std::string_view control_chars[] = {
"\\u0000", "\\u0001", "\\u0002", "\\u0003", "\\u0004", "\\u0005", "\\u0006",
@@ -57973,10 +56347,6 @@ SIMDJSON_CONSTEXPR_LAMBDA simdjson_inline void escape_json_char(char c, char *&o
}
}
-#if SIMDJSON_EXPERIMENTAL_HAS_SSE2 || SIMDJSON_EXPERIMENTAL_HAS_NEON
-#define SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE 1
-#endif
-
#if SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE
#if SIMDJSON_EXPERIMENTAL_HAS_SSE2
@@ -58257,10 +56627,21 @@ inline size_t write_string_escaped(const std::string_view input, char *out) {
#undef SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE
-simdjson_inline string_builder::string_builder(size_t initial_capacity)
- : buffer(new(std::nothrow) char[initial_capacity]), position(0),
- capacity(buffer.get() != nullptr ? initial_capacity : 0),
- is_valid(buffer.get() != nullptr) {}
+namespace internal {
+// Sets the size of s to n without initializing the new bytes when the
+// standard library allows it (C++23 resize_and_overwrite).
+simdjson_inline void resize_uninitialized(std::string &s, size_t n) {
+#if defined(__cpp_lib_string_resize_and_overwrite) && __cpp_lib_string_resize_and_overwrite >= 202110L
+ s.resize_and_overwrite(n, [](char *, size_t len) noexcept { return len; });
+#else
+ s.resize(n);
+#endif
+}
+} // namespace internal
+
+simdjson_inline string_builder::string_builder(size_t initial_capacity) {
+ grow_buffer(initial_capacity);
+}
simdjson_inline bool string_builder::capacity_check(size_t upcoming_bytes) {
// We use the convention that when is_valid is false, then the capacity and
@@ -58283,22 +56664,63 @@ inline void string_builder::grow_buffer(size_t desired_capacity) {
if (!is_valid) {
return;
}
- std::unique_ptr<char[]> new_buffer(new (std::nothrow) char[desired_capacity]);
- if (new_buffer.get() == nullptr) {
+ std::string new_buffer;
+ if (desired_capacity > new_buffer.max_size()) {
set_valid(false);
return;
}
- std::memcpy(new_buffer.get(), buffer.get(), position);
+#if defined(__cpp_exceptions) || defined(_CPPUNWIND)
+ try {
+ internal::resize_uninitialized(new_buffer, desired_capacity);
+ } catch (...) { // std::bad_alloc
+ set_valid(false);
+ return;
+ }
+#else
+ // Without exceptions, a failed allocation terminates the program.
+ internal::resize_uninitialized(new_buffer, desired_capacity);
+#endif
+ std::memcpy(&new_buffer[0], buffer_data(), position);
buffer.swap(new_buffer);
capacity = desired_capacity;
}
+// Not simdjson_inline: MSVC cannot force-inline a function with a try block (C4714).
+inline error_code string_builder::move_to(std::string &out) noexcept {
+ if (!is_valid) {
+ return simdjson::OUT_OF_CAPACITY;
+ }
+ if (position <= buffer.capacity() / 2) {
+ // Most of the buffer is unused: copy the bytes out, so that a small
+ // result does not keep the whole buffer allocated. The builder keeps its
+ // buffer for what it writes next.
+#if defined(__cpp_exceptions) || defined(_CPPUNWIND)
+ try {
+ out.assign(buffer.data(), position);
+ } catch (...) { // std::bad_alloc
+ return simdjson::MEMALLOC;
+ }
+#else
+ // Without exceptions, a failed allocation terminates the program.
+ out.assign(buffer.data(), position);
+#endif
+ position = 0;
+ return simdjson::SUCCESS;
+ }
+ buffer.resize(position); // shrinking never allocates
+ out = std::move(buffer);
+ std::string().swap(buffer);
+ position = 0;
+ capacity = 0;
+ return simdjson::SUCCESS;
+}
+
simdjson_inline void string_builder::set_valid(bool valid) noexcept {
if (!valid) {
is_valid = false;
capacity = 0;
position = 0;
- buffer.reset();
+ std::string().swap(buffer);
} else {
is_valid = true;
}
@@ -58310,7 +56732,7 @@ simdjson_inline size_t string_builder::size() const noexcept {
simdjson_inline void string_builder::append(char c) noexcept {
if (capacity_check(1)) {
- buffer.get()[position++] = c;
+ buffer_data()[position++] = c;
}
}
@@ -58318,7 +56740,7 @@ simdjson_inline void string_builder::append_null() noexcept {
constexpr char null_literal[] = "null";
constexpr size_t null_len = sizeof(null_literal) - 1;
if (capacity_check(null_len)) {
- std::memcpy(buffer.get() + position, null_literal, null_len);
+ std::memcpy(buffer_data() + position, null_literal, null_len);
position += null_len;
}
}
@@ -58328,124 +56750,14 @@ simdjson_inline void string_builder::clear() noexcept {
// if it was invalid, we should try to repair it
if (!is_valid) {
capacity = 0;
- buffer.reset();
+ std::string().swap(buffer);
is_valid = true;
}
}
namespace internal {
-// Integer to decimal: James Edward Anhalt III's algorithm
-static const char jeaiii_dd[201] =
- "00010203040506070809101112131415161718192021222324252627282930313233343536373839"
- "40414243444546474849505152535455565758596061626364656667686970717273747576777879"
- "8081828384858687888990919293949596979899";
-static const char jeaiii_fd[201] =
- "0\0" "1\0" "2\0" "3\0" "4\0" "5\0" "6\0" "7\0" "8\0" "9\0"
- "10111213141516171819202122232425262728293031323334353637383940414243444546474849"
- "50515253545556575859606162636465666768697071727374757677787980818283848586878889"
- "90919293949596979899";
-
-simdjson_really_inline void jeaiii_write_dd(char *p, uint64_t k) noexcept {
- std::memcpy(p, &jeaiii_dd[2 * k], 2);
-}
-simdjson_really_inline void jeaiii_write_fd(char *p, uint64_t k) noexcept {
- std::memcpy(p, &jeaiii_fd[2 * k], 2);
-}
-
-// Caller guarantees n < 10^8. Writes 1 to 8 digits.
-simdjson_really_inline char *jeaiii_lt1e8(char *b, uint32_t n) noexcept {
- constexpr uint64_t mask24 = (uint64_t(1) << 24) - 1;
- constexpr uint64_t mask32 = (uint64_t(1) << 32) - 1;
- if (n < 100) {
- jeaiii_write_fd(b, n);
- return n < 10 ? b + 1 : b + 2;
- }
- if (n < 1000000) {
- if (n < 10000) {
- const uint32_t f0 = uint32_t(10 * (1 << 24) / 1e3 + 1) * n;
- jeaiii_write_fd(b, f0 >> 24);
- b -= n < 1000;
- const uint32_t f2 = uint32_t(f0 & mask24) * 100;
- jeaiii_write_dd(b + 2, f2 >> 24);
- return b + 4;
- }
- const uint64_t f0 = uint64_t(10 * (1ull << 32) / 1e5 + 1) * n;
- jeaiii_write_fd(b, f0 >> 32);
- b -= n < 100000;
- const uint64_t f2 = (f0 & mask32) * 100;
- jeaiii_write_dd(b + 2, f2 >> 32);
- const uint64_t f4 = (f2 & mask32) * 100;
- jeaiii_write_dd(b + 4, f4 >> 32);
- return b + 6;
- }
- const uint64_t f0 = uint64_t(10 * (1ull << 48) / 1e7 + 1) * n >> 16;
- jeaiii_write_fd(b, f0 >> 32);
- b -= n < 10000000;
- const uint64_t f2 = (f0 & mask32) * 100;
- jeaiii_write_dd(b + 2, f2 >> 32);
- const uint64_t f4 = (f2 & mask32) * 100;
- jeaiii_write_dd(b + 4, f4 >> 32);
- const uint64_t f6 = (f4 & mask32) * 100;
- jeaiii_write_dd(b + 6, f6 >> 32);
- return b + 8;
-}
-
-// Caller guarantees z < 10^8. Always writes exactly 8 digits.
-simdjson_really_inline char *jeaiii_8_digits(char *b, uint32_t z) noexcept {
- constexpr uint64_t mask32 = (uint64_t(1) << 32) - 1;
- const uint64_t f0 = (uint64_t((1ull << 48) / 1e6 + 1) * z >> 16) + 1;
- jeaiii_write_dd(b, f0 >> 32);
- const uint64_t f2 = (f0 & mask32) * 100;
- jeaiii_write_dd(b + 2, f2 >> 32);
- const uint64_t f4 = (f2 & mask32) * 100;
- jeaiii_write_dd(b + 4, f4 >> 32);
- const uint64_t f6 = (f4 & mask32) * 100;
- jeaiii_write_dd(b + 6, f6 >> 32);
- return b + 8;
-}
-
-// Caller guarantees 10^8 <= n < 2^32. Writes 9 or 10 digits.
-simdjson_really_inline char *jeaiii_9_or_10(char *b, uint64_t n) noexcept {
- constexpr uint64_t mask57 = (uint64_t(1) << 57) - 1;
- const uint64_t f0 = uint64_t(10 * (1ull << 57) / 1e9 + 1) * n;
- jeaiii_write_fd(b, f0 >> 57);
- b -= n < 1000000000;
- const uint64_t f2 = (f0 & mask57) * 100;
- jeaiii_write_dd(b + 2, f2 >> 57);
- const uint64_t f4 = (f2 & mask57) * 100;
- jeaiii_write_dd(b + 4, f4 >> 57);
- const uint64_t f6 = (f4 & mask57) * 100;
- jeaiii_write_dd(b + 6, f6 >> 57);
- const uint64_t f8 = (f6 & mask57) * 100;
- jeaiii_write_dd(b + 8, f8 >> 57);
- return b + 10;
-}
-
-simdjson_really_inline char *write_uint_jeaiii(char *b, uint64_t n) noexcept {
- if (n < 100000000) {
- return jeaiii_lt1e8(b, uint32_t(n));
- }
- if (n < (uint64_t(1) << 32)) {
- return jeaiii_9_or_10(b, n);
- }
- // At least 10 digits: the low 8 digits, and 2 to 12 digits above them.
- const uint32_t z = uint32_t(n % 100000000);
- uint64_t u = n / 100000000;
- if (u < 100000000) {
- // u has 2 to 8 digits (if u < 10, n would be below 2^32).
- b = jeaiii_lt1e8(b, uint32_t(u));
- } else if (u < (uint64_t(1) << 32)) {
- b = jeaiii_9_or_10(b, u);
- } else {
- // u has 11 or 12 digits: split off 8 more.
- const uint32_t y = uint32_t(u % 100000000);
- u /= 100000000;
- b = jeaiii_lt1e8(b, uint32_t(u)); // 3 or 4 digits
- b = jeaiii_8_digits(b, y);
- }
- return jeaiii_8_digits(b, z);
-}
+using simdjson::internal::write_uint_jeaiii;
// Writes v at p, which must have to_chars_buffer_size bytes available, and
// returns the end of what was written.
@@ -58473,20 +56785,22 @@ simdjson_inline void string_builder::append(number_type v) noexcept {
std::is_integral<number_type>::value ||
std::is_floating_point<number_type>::value,
"Unsupported number type");
+ static_assert(sizeof(number_type) <= sizeof(uint64_t),
+ "Integers wider than 64 bits are not supported");
// If C++17 is available, we can 'if constexpr' here.
SIMDJSON_IF_CONSTEXPR(std::is_same<number_type, bool>::value) {
if (v) {
constexpr char true_literal[] = "true";
constexpr size_t true_len = sizeof(true_literal) - 1;
if (capacity_check(true_len)) {
- std::memcpy(buffer.get() + position, true_literal, true_len);
+ std::memcpy(buffer_data() + position, true_literal, true_len);
position += true_len;
}
} else {
constexpr char false_literal[] = "false";
constexpr size_t false_len = sizeof(false_literal) - 1;
if (capacity_check(false_len)) {
- std::memcpy(buffer.get() + position, false_literal, false_len);
+ std::memcpy(buffer_data() + position, false_literal, false_len);
position += false_len;
}
}
@@ -58496,9 +56810,9 @@ simdjson_inline void string_builder::append(number_type v) noexcept {
if (capacity_check(max_number_size)) {
using unsigned_type = typename std::make_unsigned<number_type>::type;
char* end = internal::write_uint_jeaiii(
- buffer.get() + position,
+ buffer_data() + position,
static_cast<uint64_t>(static_cast<unsigned_type>(v)));
- position = end - buffer.get();
+ position = end - buffer_data();
}
}
else SIMDJSON_IF_CONSTEXPR(std::is_integral<number_type>::value) {
@@ -58512,11 +56826,11 @@ simdjson_inline void string_builder::append(number_type v) noexcept {
? unsigned_type(0) - static_cast<unsigned_type>(v)
: static_cast<unsigned_type>(v);
// Branchless: always write '-', advance only if negative.
- buffer.get()[position] = '-';
+ buffer_data()[position] = '-';
position += negative;
char* end = internal::write_uint_jeaiii(
- buffer.get() + position, static_cast<uint64_t>(pv));
- position = end - buffer.get();
+ buffer_data() + position, static_cast<uint64_t>(pv));
+ position = end - buffer_data();
}
}
else SIMDJSON_IF_CONSTEXPR(std::is_floating_point<number_type>::value) {
@@ -58532,16 +56846,16 @@ simdjson_inline void string_builder::append(number_type v) noexcept {
constexpr char nan_literal[] = "NaN";
constexpr size_t nan_len = sizeof(nan_literal) - 1;
- std::memcpy(buffer.get() + position, nan_literal, nan_len);
+ std::memcpy(buffer_data() + position, nan_literal, nan_len);
position += nan_len;
} else {
constexpr char inf_literal[] = "Infinity";
constexpr size_t inf_len = sizeof(inf_literal) - 1;
if (v < 0) {
- buffer.get()[position] = '-';
+ buffer_data()[position] = '-';
++position;
}
- std::memcpy(buffer.get() + position, inf_literal, inf_len);
+ std::memcpy(buffer_data() + position, inf_literal, inf_len);
position += inf_len;
}
return;
@@ -58549,9 +56863,9 @@ simdjson_inline void string_builder::append(number_type v) noexcept {
#endif
// We could specialize for float.
- char *end = simdjson::internal::to_chars(buffer.get() + position, nullptr,
+ char *end = simdjson::internal::to_chars(buffer_data() + position, nullptr,
double(v));
- position = end - buffer.get();
+ position = end - buffer_data();
}
}
}
@@ -58565,7 +56879,7 @@ string_builder::escape_and_append(std::string_view input) noexcept {
return;
}
if (capacity_check(6 * input.size())) {
- position += write_string_escaped(input, buffer.get() + position);
+ position += write_string_escaped(input, buffer_data() + position);
}
}
@@ -58578,9 +56892,9 @@ string_builder::escape_and_append_with_quotes(std::string_view input) noexcept {
return;
}
if (capacity_check(2 + 6 * input.size())) {
- buffer.get()[position++] = '"';
- position += write_string_escaped(input, buffer.get() + position);
- buffer.get()[position++] = '"';
+ buffer_data()[position++] = '"';
+ position += write_string_escaped(input, buffer_data() + position);
+ buffer_data()[position++] = '"';
}
}
@@ -58588,10 +56902,10 @@ simdjson_inline void
string_builder::escape_and_append_with_quotes(char input) noexcept {
// escaping might turn a control character into \x00xx so 6 characters.
if (capacity_check(2 + 6 * 1)) {
- buffer.get()[position++] = '"';
+ buffer_data()[position++] = '"';
std::string_view cinput(&input, 1);
- position += write_string_escaped(cinput, buffer.get() + position);
- buffer.get()[position++] = '"';
+ position += write_string_escaped(cinput, buffer_data() + position);
+ buffer_data()[position++] = '"';
}
}
@@ -58621,7 +56935,7 @@ simdjson_inline void string_builder::append_raw(const char *c) noexcept {
simdjson_inline void
string_builder::append_raw(std::string_view input) noexcept {
if (capacity_check(input.size())) {
- std::memcpy(buffer.get() + position, input.data(), input.size());
+ std::memcpy(buffer_data() + position, input.data(), input.size());
position += input.size();
}
}
@@ -58629,7 +56943,7 @@ string_builder::append_raw(std::string_view input) noexcept {
simdjson_inline void string_builder::append_raw(const char *str,
size_t len) noexcept {
if (capacity_check(len)) {
- std::memcpy(buffer.get() + position, str, len);
+ std::memcpy(buffer_data() + position, str, len);
position += len;
}
}
@@ -58637,7 +56951,7 @@ simdjson_inline void string_builder::append_raw(const char *str,
template <size_t N>
simdjson_inline void string_builder::append_raw_n(const char *str) noexcept {
if (capacity_check(N)) {
- std::memcpy(buffer.get() + position, str, N);
+ std::memcpy(buffer_data() + position, str, N);
position += N;
}
}
@@ -58647,7 +56961,7 @@ template <typename Text>
simdjson_inline void string_builder::append_static_string() noexcept {
constexpr auto& padded = constevalutil::static_string<Text>;
if (capacity_check(padded.size())) {
- std::memcpy(buffer.get() + position, padded.data(), padded.size());
+ std::memcpy(buffer_data() + position, padded.data(), padded.size());
position += constevalutil::static_string_size<Text>();
}
}
@@ -58741,54 +57055,54 @@ string_builder::view() const noexcept {
if (!is_valid) {
return simdjson::OUT_OF_CAPACITY;
}
- return std::string_view(buffer.get(), position);
+ return std::string_view(buffer_data(), position);
}
simdjson_inline simdjson_result<const char *> string_builder::c_str() noexcept {
if (capacity_check(1)) {
- buffer.get()[position] = '\0';
- return buffer.get();
+ buffer_data()[position] = '\0';
+ return buffer_data();
}
return simdjson::OUT_OF_CAPACITY;
}
simdjson_inline bool string_builder::validate_unicode() const noexcept {
- return simdjson::validate_utf8(buffer.get(), position);
+ return simdjson::validate_utf8(buffer_data(), position);
}
simdjson_inline void string_builder::start_object() noexcept {
if (capacity_check(1)) {
- buffer.get()[position++] = '{';
+ buffer_data()[position++] = '{';
}
}
simdjson_inline void string_builder::end_object() noexcept {
if (capacity_check(1)) {
- buffer.get()[position++] = '}';
+ buffer_data()[position++] = '}';
}
}
simdjson_inline void string_builder::start_array() noexcept {
if (capacity_check(1)) {
- buffer.get()[position++] = '[';
+ buffer_data()[position++] = '[';
}
}
simdjson_inline void string_builder::end_array() noexcept {
if (capacity_check(1)) {
- buffer.get()[position++] = ']';
+ buffer_data()[position++] = ']';
}
}
simdjson_inline void string_builder::append_comma() noexcept {
if (capacity_check(1)) {
- buffer.get()[position++] = ',';
+ buffer_data()[position++] = ',';
}
}
simdjson_inline void string_builder::append_colon() noexcept {
if (capacity_check(1)) {
- buffer.get()[position++] = ':';
+ buffer_data()[position++] = ':';
}
}
@@ -59728,31 +58042,6 @@ backslash_and_quote::copy_and_find(const uint8_t *src, uint8_t *dst) {
}
-struct escaping {
- static constexpr uint32_t BYTES_PROCESSED = 16;
- simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
-
- simdjson_inline bool has_escape() { return escape_bits != 0; }
- simdjson_inline int escape_index() { return trailing_zeroes(escape_bits); }
-
- uint64_t escape_bits;
-}; // struct escaping
-
-
-
-simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
- static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes");
- simd8<uint8_t> v(src);
- v.store(dst);
- simd8<bool> is_quote = (v == '"');
- simd8<bool> is_backslash = (v == '\\');
- simd8<bool> is_control = (v < 32);
- return {
- // We store it as a 64-bit bitmask even though we only need 16 bits.
- uint64_t((is_backslash | is_quote | is_control).to_bitmask())
- };
-}
-
} // unnamed namespace
} // namespace ppc64
} // namespace simdjson
@@ -59823,6 +58112,10 @@ namespace builder {
class string_builder {
public:
simdjson_inline string_builder(size_t initial_capacity = DEFAULT_INITIAL_CAPACITY);
+ string_builder(const string_builder &) = delete;
+ string_builder &operator=(const string_builder &) = delete;
+ string_builder(string_builder &&) noexcept = default;
+ string_builder &operator=(string_builder &&) noexcept = default;
static constexpr size_t DEFAULT_INITIAL_CAPACITY = 1024;
@@ -59836,6 +58129,18 @@ public:
typename = typename std::enable_if<std::is_arithmetic<number_type>::value>::type>
simdjson_inline void append(number_type v) noexcept;
+#ifdef __SIZEOF_INT128__
+ /**
+ * Integers wider than 64 bits are not supported. Without these overloads, a
+ * 128-bit integer would either be truncated to 64 bits by append(number_type)
+ * (when the standard library counts it as arithmetic, as libc++ does) or be
+ * converted to char by append(char) (when it does not, as libstdc++ does in
+ * strict ISO mode).
+ */
+ void append(__int128_t v) noexcept = delete;
+ void append(__uint128_t v) noexcept = delete;
+#endif
+
/**
* Append character c.
*/
@@ -60014,6 +58319,20 @@ requires (!std::is_convertible<R, std::string_view>::value && !concepts::optiona
*/
simdjson_inline simdjson_result<const char *> c_str() noexcept;
+ /**
+ * Moves the written JSON buffer into out, replacing its content, without
+ * copying it. When the content fills at most half of the buffer, it is
+ * copied instead, so that a small result does not keep the whole buffer
+ * allocated. The builder is left empty: it keeps its buffer when the
+ * content was copied, and gives up its memory when the buffer was moved.
+ * Returns an error, and leaves out and the builder unchanged, if memory
+ * allocation failed.
+ *
+ * The result may not be valid UTF-8 if some of your content was not valid UTF-8.
+ * Use validate_unicode() to check the content.
+ */
+ simdjson_warn_unused inline error_code move_to(std::string &out) noexcept;
+
/**
* Return true if the content is valid UTF-8.
*/
@@ -60033,7 +58352,7 @@ requires (!std::is_convertible<R, std::string_view>::value && !concepts::optiona
// forces those reloads when accessed via members of *this). User
// code should NOT call these directly.
// ============================================================
- simdjson_inline char *unsafe_data() noexcept { return buffer.get(); }
+ simdjson_inline char *unsafe_data() noexcept { return buffer_data(); }
simdjson_inline size_t unsafe_position() const noexcept { return position; }
simdjson_inline size_t unsafe_capacity() const noexcept { return capacity; }
simdjson_inline void unsafe_set_position(size_t p) noexcept { position = p; }
@@ -60074,7 +58393,13 @@ private:
*/
simdjson_inline void set_valid(bool valid) noexcept;
- std::unique_ptr<char[]> buffer{};
+ simdjson_inline char *buffer_data() noexcept { return &buffer[0]; }
+ simdjson_inline const char *buffer_data() const noexcept { return buffer.data(); }
+
+ // We own capacity bytes: buffer.size() == capacity. Only the first position
+ // bytes hold JSON. A std::string lets move_to() hand the result over
+ // without a copy.
+ std::string buffer{};
size_t position{0};
size_t capacity{0};
bool is_valid{true};
@@ -60092,20 +58417,16 @@ template <class Z>
simdjson_warn_unused simdjson_result<std::string> to_json(const Z &z, size_t initial_capacity = simdjson::ppc64::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
simdjson::ppc64::builder::string_builder b(initial_capacity);
b.append(z);
- std::string_view s;
- auto e = b.view().get(s);
+ std::string s;
+ auto e = b.move_to(s);
if(e) { return e; }
- return std::string(s);
+ return s;
}
template <class Z>
simdjson_warn_unused error_code to_json(const Z &z, std::string &s, size_t initial_capacity = simdjson::ppc64::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
simdjson::ppc64::builder::string_builder b(initial_capacity);
b.append(z);
- std::string_view sv;
- auto e = b.view().get(sv);
- if(e) { return e; }
- s.assign(sv.data(), sv.size());
- return simdjson::SUCCESS;
+ return b.move_to(s);
}
#endif
@@ -60121,7 +58442,7 @@ simdjson_warn_unused error_code to_json(const Z &z, std::string &s, size_t initi
#ifndef SIMDJSON_GENERIC_BUILDER_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
-/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_STRING_BUILDER_H */
+/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_BUILDER_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/builder/json_string_builder.h" */
/* amalgamation skipped (editor-only): #include "simdjson/concepts.h" */
/* amalgamation skipped (editor-only): #include "simdjson/annotations.h" */
@@ -60147,7 +58468,7 @@ namespace builder {
// included at the point this header is parsed; without these forwards,
// name lookup falls back to the wrong outer namespace).
namespace internal {
-simdjson_really_inline char *write_uint_jeaiii(char *p, uint64_t v) noexcept;
+using simdjson::internal::write_uint_jeaiii;
simdjson_inline char *write_double(char *p, double v) noexcept;
} // namespace internal
simdjson_really_inline size_t write_string_escaped(const std::string_view input, char *out);
@@ -60280,6 +58601,51 @@ consteval bool is_serialized_member(std::meta::info dm) {
&& !simdjson::detail::has_annotation(dm, ^^simdjson::detail::skip_serializing_tag);
}
+template <class Target, class T, class... Visiting>
+consteval bool members_reach();
+
+// Whether a value of type T may hold a value of type Target, directly or
+// through optionals, smart pointers, containers and members. Visiting lists
+// the structures being searched, so that the search terminates.
+template <class Target, class T, class... Visiting>
+consteval bool reaches() {
+ if constexpr (std::is_same_v<T, Target>) {
+ return true;
+ } else if constexpr ((std::is_same_v<T, Visiting> || ...) || require_custom_serialization<T> ||
+ std::is_same_v<T, std::string> || std::is_same_v<T, std::string_view>) {
+ return false;
+ } else if constexpr (concepts::optional_type<T> || concepts::smart_pointer<T>) {
+ return reaches<Target, std::remove_cvref_t<decltype(*std::declval<const T &>())>, Visiting...>();
+ } else if constexpr (concepts::string_view_keyed_map<T>) {
+ return reaches<Target, std::remove_cvref_t<typename T::mapped_type>, Visiting...>();
+ } else if constexpr (concepts::container_but_not_string<T> || concepts::appendable_containers<T>) {
+ return reaches<Target, std::remove_cvref_t<std::ranges::range_value_t<T>>, Visiting...>();
+ } else if constexpr (std::is_class_v<T>) {
+ return members_reach<Target, T, Visiting...>();
+ } else {
+ return false;
+ }
+}
+
+template <class Target, class T, class... Visiting>
+consteval bool members_reach() {
+ bool found = false;
+ template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) {
+ if constexpr (is_serialized_member(dm)) {
+ found = found || reaches<Target, std::remove_cvref_t<decltype(std::declval<const T &>().[:dm:])>, T, Visiting...>();
+ }
+ };
+ return found;
+}
+
+// Whether the structure T may contain itself, as in
+// struct Rule { std::unique_ptr<Rule> subrule; }. Serializing such a type
+// recurses at runtime, which simdjson_really_inline functions cannot do.
+template <class T>
+consteval bool is_recursive() {
+ return members_reach<T, T>();
+}
+
// False when the member has a skip_serializing_if<pred> annotation and
// pred(value) is true.
template <auto dm, typename V>
@@ -60463,6 +58829,8 @@ simdjson_really_inline constexpr void atom(W &w, const T &m) {
template<class W, typename number_type,
typename = typename std::enable_if<std::is_arithmetic<number_type>::value && !std::is_same_v<number_type, char>>::type>
simdjson_really_inline constexpr void atom(W &w, const number_type t) {
+ static_assert(sizeof(number_type) <= sizeof(uint64_t),
+ "Integers wider than 64 bits are not supported");
// Booleans / floats: defer to string_builder (rare path; keeps writer hot
// path free of float-formatter machinery). For integers, write directly
// via jeaiii using local pos.
@@ -60501,6 +58869,31 @@ simdjson_really_inline constexpr void atom(W &w, const number_type t) {
}
}
+namespace annotation_detail {
+
+template <class W, class T>
+simdjson_really_inline void atom_object(W &w, const T &t) {
+ if constexpr (simdjson::detail::has_annotation(^^T, ^^simdjson::detail::transparent_tag)) {
+ // A transparent structure is serialized as its single member.
+ constexpr auto dm = simdjson::detail::transparent_member(^^T);
+ atom_member<dm>(w, t.[:dm:]);
+ } else {
+ bool first = true;
+ if (!w.ensure(1)) { return; }
+ w.ptr[w.pos++] = '{';
+ atom_fields(w, t, first);
+ if (!w.ensure(1)) { return; }
+ w.ptr[w.pos++] = '}';
+ }
+}
+
+template <class W, class T>
+void atom_object_outlined(W &w, const T &t) {
+ atom_object(w, t);
+}
+
+} // namespace annotation_detail
+
template <class W, class T>
requires(std::is_class_v<T> && !concepts::container_but_not_string<T> &&
!concepts::string_view_keyed_map<T> &&
@@ -60512,17 +58905,11 @@ template <class W, class T>
!std::is_same_v<T, const char*> &&
!std::is_same_v<T, char> && !require_custom_serialization<T>)
simdjson_really_inline constexpr void atom(W &w, const T &t) {
- if constexpr (simdjson::detail::has_annotation(^^T, ^^simdjson::detail::transparent_tag)) {
- // A transparent structure is serialized as its single member.
- constexpr auto dm = simdjson::detail::transparent_member(^^T);
- annotation_detail::atom_member<dm>(w, t.[:dm:]);
+ if constexpr (annotation_detail::is_recursive<T>()) {
+ // Break the cycle of simdjson_really_inline calls.
+ annotation_detail::atom_object_outlined(w, t);
} else {
- bool first = true;
- if (!w.ensure(1)) { return; }
- w.ptr[w.pos++] = '{';
- annotation_detail::atom_fields(w, t, first);
- if (!w.ensure(1)) { return; }
- w.ptr[w.pos++] = '}';
+ annotation_detail::atom_object(w, t);
}
}
@@ -60622,25 +59009,30 @@ namespace bound_detail {
// Whether size_bound covers everything that atom() writes for T: not when a
// member is serialized by a with<Adapter> serializer, which writes an unknown
// amount through the string_builder.
-template <class T>
+// Visiting lists the structures being checked, so that a recursive type
+// (struct Node { std::unique_ptr<Node> next; }) does not recurse forever: a
+// type already being visited adds nothing, its members get checked once.
+template <class T, class... Visiting>
consteval bool is_bounded() {
- if constexpr (require_custom_serialization<T>) {
+ if constexpr ((std::is_same_v<T, Visiting> || ...)) {
+ return true;
+ } else if constexpr (require_custom_serialization<T>) {
return false;
} else if constexpr (std::is_same_v<T, std::string> || std::is_same_v<T, std::string_view> ||
std::is_same_v<T, const char *> || std::is_arithmetic_v<T> || std::is_enum_v<T>) {
return true;
} else if constexpr (concepts::optional_type<T> || concepts::smart_pointer<T>) {
- return is_bounded<std::remove_cvref_t<decltype(*std::declval<const T &>())>>();
+ return is_bounded<std::remove_cvref_t<decltype(*std::declval<const T &>())>, Visiting...>();
} else if constexpr (concepts::string_view_keyed_map<T>) {
- return is_bounded<std::remove_cvref_t<typename T::mapped_type>>();
+ return is_bounded<std::remove_cvref_t<typename T::mapped_type>, Visiting...>();
} else if constexpr (concepts::container_but_not_string<T> || concepts::appendable_containers<T>) {
- return is_bounded<std::remove_cvref_t<std::ranges::range_value_t<T>>>();
+ return is_bounded<std::remove_cvref_t<std::ranges::range_value_t<T>>, Visiting...>();
} else {
bool bounded = true;
template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) {
if constexpr (annotation_detail::is_serialized_member(dm)) {
bounded = bounded && simdjson::detail::annotation_of_template(dm, ^^simdjson::detail::with_t) == std::meta::info{} &&
- is_bounded<std::remove_cvref_t<decltype(std::declval<const T &>().[:dm:])>>();
+ is_bounded<std::remove_cvref_t<decltype(std::declval<const T &>().[:dm:])>, T, Visiting...>();
}
};
return bounded;
@@ -60678,6 +59070,11 @@ simdjson_really_inline size_t fields_bound(const T &t) noexcept {
return bound;
}
+template <class T>
+size_t fields_bound_outlined(const T &t) noexcept {
+ return fields_bound(t);
+}
+
template <class T>
simdjson_really_inline size_t size_bound([[maybe_unused]] const T &t) noexcept {
if constexpr (std::is_same_v<T, char>) {
@@ -60722,6 +59119,9 @@ simdjson_really_inline size_t size_bound([[maybe_unused]] const T &t) noexcept {
} else if constexpr (simdjson::detail::has_annotation(^^T, ^^simdjson::detail::transparent_tag)) {
constexpr auto dm = simdjson::detail::transparent_member(^^T);
return size_bound(t.[:dm:]);
+ } else if constexpr (annotation_detail::is_recursive<T>()) {
+ // Break the cycle of simdjson_really_inline calls.
+ return 2 + fields_bound_outlined(t);
} else {
return 2 + fields_bound(t);
}
@@ -60856,10 +59256,7 @@ simdjson_warn_unused error_code to_json(const Z &z, std::string &s, size_t initi
} else {
string_builder b(initial_capacity);
append(b, z);
- std::string_view view;
- if(auto e = b.view().get(view); e) { return e; }
- s.assign(view);
- return SUCCESS;
+ return b.move_to(s);
}
}
@@ -60922,9 +59319,9 @@ template<constevalutil::fixed_string... FieldNames, typename T>
simdjson_warn_unused simdjson_result<std::string> extract_from(const T &obj, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) {
string_builder b(initial_capacity);
extract_from<FieldNames...>(b, obj);
- std::string_view s;
- if(auto e = b.view().get(s); e) { return e; }
- return std::string(s);
+ std::string s;
+ if(auto e = b.move_to(s); e) { return e; }
+ return s;
}
SIMDJSON_POP_DISABLE_WARNINGS
@@ -60946,16 +59343,16 @@ template<constevalutil::fixed_string... FieldNames, typename T>
simdjson_warn_unused simdjson_result<std::string> extract_from(const T &obj, size_t initial_capacity = ppc64::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
ppc64::builder::string_builder b(initial_capacity);
ppc64::builder::extract_from<FieldNames...>(b, obj);
- std::string_view s;
- if(auto e = b.view().get(s); e) { return e; }
- return std::string(s);
+ std::string s;
+ if(auto e = b.move_to(s); e) { return e; }
+ return s;
}
} // namespace simdjson
#endif // SIMDJSON_STATIC_REFLECTION
-#endif
+#endif // SIMDJSON_GENERIC_BUILDER_H
/* end file simdjson/generic/builder/json_builder.h for ppc64 */
/* including simdjson/generic/builder/fractured_json_builder.h for ppc64: #include "simdjson/generic/builder/fractured_json_builder.h" */
/* begin file simdjson/generic/builder/fractured_json_builder.h for ppc64 */
@@ -61094,6 +59491,7 @@ simdjson_warn_unused simdjson_result<std::string> extract_fractured_json(
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_STRING_BUILDER_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/builder/json_string_builder.h" */
+/* amalgamation skipped (editor-only): #include "simdjson/internal/jeaiii.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
/*
@@ -61206,6 +59604,12 @@ inline bool has_json_escapable_byte(uint64_t x) {
**/
+#if SIMDJSON_EXPERIMENTAL_HAS_SSE2 || SIMDJSON_EXPERIMENTAL_HAS_NEON
+#define SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE 1
+#endif
+
+// With block escaping (SSE2/NEON), write_string_escaped scans the input itself.
+#if !SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE
// Scalar fallback for finding next quotable character
SIMDJSON_CONSTEXPR_LAMBDA simdjson_inline size_t
find_next_json_quotable_character_scalar(const std::string_view view,
@@ -61221,82 +59625,7 @@ find_next_json_quotable_character_scalar(const std::string_view view,
// SIMD-accelerated position finding that directly locates the first quotable
// character, combining detection and position extraction in a single pass to
// minimize redundant work.
-#if SIMDJSON_EXPERIMENTAL_HAS_NEON
-simdjson_inline size_t
-find_next_json_quotable_character(const std::string_view view,
- size_t location) noexcept {
- const size_t len = view.size();
- const uint8_t *ptr =
- reinterpret_cast<const uint8_t *>(view.data()) + location;
- size_t remaining = len - location;
-
- // SIMD constants for characters requiring escape
- uint8x16_t v34 = vdupq_n_u8(34); // '"'
- uint8x16_t v92 = vdupq_n_u8(92); // '\\'
- uint8x16_t v32 = vdupq_n_u8(32); // control char threshold
-
- while (remaining >= 16) {
- uint8x16_t word = vld1q_u8(ptr);
-
- // Check for quotable characters: '"', '\\', or control chars (< 32)
- uint8x16_t needs_escape = vceqq_u8(word, v34);
- needs_escape = vorrq_u8(needs_escape, vceqq_u8(word, v92));
- needs_escape = vorrq_u8(needs_escape, vcltq_u8(word, v32));
-
- const uint8x8_t res = vshrn_n_u16(vreinterpretq_u16_u8(needs_escape), 4);
- const uint64_t mask = vget_lane_u64(vreinterpret_u64_u8(res), 0);
- if(mask != 0) {
- size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
- auto trailing_zero = trailing_zeroes(mask);
- return offset + (trailing_zero >> 2);
- }
- ptr += 16;
- remaining -= 16;
- }
-
- // Scalar fallback for remaining bytes
- size_t current = len - remaining;
- return find_next_json_quotable_character_scalar(view, current);
-}
-#elif SIMDJSON_EXPERIMENTAL_HAS_SSE2
-simdjson_inline size_t
-find_next_json_quotable_character(const std::string_view view,
- size_t location) noexcept {
- const size_t len = view.size();
- const uint8_t *ptr =
- reinterpret_cast<const uint8_t *>(view.data()) + location;
- size_t remaining = len - location;
-
- // SIMD constants
- __m128i v34 = _mm_set1_epi8(34); // '"'
- __m128i v92 = _mm_set1_epi8(92); // '\\'
- __m128i v31 = _mm_set1_epi8(31); // for control char detection
-
- while (remaining >= 16) {
- __m128i word = _mm_loadu_si128(reinterpret_cast<const __m128i *>(ptr));
-
- // Check for quotable characters
- __m128i needs_escape = _mm_cmpeq_epi8(word, v34);
- needs_escape = _mm_or_si128(needs_escape, _mm_cmpeq_epi8(word, v92));
- needs_escape = _mm_or_si128(
- needs_escape,
- _mm_cmpeq_epi8(_mm_subs_epu8(word, v31), _mm_setzero_si128()));
-
- int mask = _mm_movemask_epi8(needs_escape);
- if (mask != 0) {
- // Found quotable character - use trailing zero count to find position
- size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
- return offset + trailing_zeroes(mask);
- }
- ptr += 16;
- remaining -= 16;
- }
-
- // Scalar fallback for remaining bytes
- size_t current = len - remaining;
- return find_next_json_quotable_character_scalar(view, current);
-}
-#elif SIMDJSON_EXPERIMENTAL_HAS_LASX
+#if SIMDJSON_EXPERIMENTAL_HAS_LASX
simdjson_inline size_t
find_next_json_quotable_character(const std::string_view view,
size_t location) noexcept {
@@ -61469,6 +59798,7 @@ find_next_json_quotable_character(const std::string_view view,
return find_next_json_quotable_character_scalar(view, location);
}
#endif
+#endif // !SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE
SIMDJSON_CONSTEXPR_LAMBDA static std::string_view control_chars[] = {
"\\u0000", "\\u0001", "\\u0002", "\\u0003", "\\u0004", "\\u0005", "\\u0006",
@@ -61496,10 +59826,6 @@ SIMDJSON_CONSTEXPR_LAMBDA simdjson_inline void escape_json_char(char c, char *&o
}
}
-#if SIMDJSON_EXPERIMENTAL_HAS_SSE2 || SIMDJSON_EXPERIMENTAL_HAS_NEON
-#define SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE 1
-#endif
-
#if SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE
#if SIMDJSON_EXPERIMENTAL_HAS_SSE2
@@ -61780,10 +60106,21 @@ inline size_t write_string_escaped(const std::string_view input, char *out) {
#undef SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE
-simdjson_inline string_builder::string_builder(size_t initial_capacity)
- : buffer(new(std::nothrow) char[initial_capacity]), position(0),
- capacity(buffer.get() != nullptr ? initial_capacity : 0),
- is_valid(buffer.get() != nullptr) {}
+namespace internal {
+// Sets the size of s to n without initializing the new bytes when the
+// standard library allows it (C++23 resize_and_overwrite).
+simdjson_inline void resize_uninitialized(std::string &s, size_t n) {
+#if defined(__cpp_lib_string_resize_and_overwrite) && __cpp_lib_string_resize_and_overwrite >= 202110L
+ s.resize_and_overwrite(n, [](char *, size_t len) noexcept { return len; });
+#else
+ s.resize(n);
+#endif
+}
+} // namespace internal
+
+simdjson_inline string_builder::string_builder(size_t initial_capacity) {
+ grow_buffer(initial_capacity);
+}
simdjson_inline bool string_builder::capacity_check(size_t upcoming_bytes) {
// We use the convention that when is_valid is false, then the capacity and
@@ -61806,22 +60143,63 @@ inline void string_builder::grow_buffer(size_t desired_capacity) {
if (!is_valid) {
return;
}
- std::unique_ptr<char[]> new_buffer(new (std::nothrow) char[desired_capacity]);
- if (new_buffer.get() == nullptr) {
+ std::string new_buffer;
+ if (desired_capacity > new_buffer.max_size()) {
+ set_valid(false);
+ return;
+ }
+#if defined(__cpp_exceptions) || defined(_CPPUNWIND)
+ try {
+ internal::resize_uninitialized(new_buffer, desired_capacity);
+ } catch (...) { // std::bad_alloc
set_valid(false);
return;
}
- std::memcpy(new_buffer.get(), buffer.get(), position);
+#else
+ // Without exceptions, a failed allocation terminates the program.
+ internal::resize_uninitialized(new_buffer, desired_capacity);
+#endif
+ std::memcpy(&new_buffer[0], buffer_data(), position);
buffer.swap(new_buffer);
capacity = desired_capacity;
}
+// Not simdjson_inline: MSVC cannot force-inline a function with a try block (C4714).
+inline error_code string_builder::move_to(std::string &out) noexcept {
+ if (!is_valid) {
+ return simdjson::OUT_OF_CAPACITY;
+ }
+ if (position <= buffer.capacity() / 2) {
+ // Most of the buffer is unused: copy the bytes out, so that a small
+ // result does not keep the whole buffer allocated. The builder keeps its
+ // buffer for what it writes next.
+#if defined(__cpp_exceptions) || defined(_CPPUNWIND)
+ try {
+ out.assign(buffer.data(), position);
+ } catch (...) { // std::bad_alloc
+ return simdjson::MEMALLOC;
+ }
+#else
+ // Without exceptions, a failed allocation terminates the program.
+ out.assign(buffer.data(), position);
+#endif
+ position = 0;
+ return simdjson::SUCCESS;
+ }
+ buffer.resize(position); // shrinking never allocates
+ out = std::move(buffer);
+ std::string().swap(buffer);
+ position = 0;
+ capacity = 0;
+ return simdjson::SUCCESS;
+}
+
simdjson_inline void string_builder::set_valid(bool valid) noexcept {
if (!valid) {
is_valid = false;
capacity = 0;
position = 0;
- buffer.reset();
+ std::string().swap(buffer);
} else {
is_valid = true;
}
@@ -61833,7 +60211,7 @@ simdjson_inline size_t string_builder::size() const noexcept {
simdjson_inline void string_builder::append(char c) noexcept {
if (capacity_check(1)) {
- buffer.get()[position++] = c;
+ buffer_data()[position++] = c;
}
}
@@ -61841,7 +60219,7 @@ simdjson_inline void string_builder::append_null() noexcept {
constexpr char null_literal[] = "null";
constexpr size_t null_len = sizeof(null_literal) - 1;
if (capacity_check(null_len)) {
- std::memcpy(buffer.get() + position, null_literal, null_len);
+ std::memcpy(buffer_data() + position, null_literal, null_len);
position += null_len;
}
}
@@ -61851,124 +60229,14 @@ simdjson_inline void string_builder::clear() noexcept {
// if it was invalid, we should try to repair it
if (!is_valid) {
capacity = 0;
- buffer.reset();
+ std::string().swap(buffer);
is_valid = true;
}
}
namespace internal {
-// Integer to decimal: James Edward Anhalt III's algorithm
-static const char jeaiii_dd[201] =
- "00010203040506070809101112131415161718192021222324252627282930313233343536373839"
- "40414243444546474849505152535455565758596061626364656667686970717273747576777879"
- "8081828384858687888990919293949596979899";
-static const char jeaiii_fd[201] =
- "0\0" "1\0" "2\0" "3\0" "4\0" "5\0" "6\0" "7\0" "8\0" "9\0"
- "10111213141516171819202122232425262728293031323334353637383940414243444546474849"
- "50515253545556575859606162636465666768697071727374757677787980818283848586878889"
- "90919293949596979899";
-
-simdjson_really_inline void jeaiii_write_dd(char *p, uint64_t k) noexcept {
- std::memcpy(p, &jeaiii_dd[2 * k], 2);
-}
-simdjson_really_inline void jeaiii_write_fd(char *p, uint64_t k) noexcept {
- std::memcpy(p, &jeaiii_fd[2 * k], 2);
-}
-
-// Caller guarantees n < 10^8. Writes 1 to 8 digits.
-simdjson_really_inline char *jeaiii_lt1e8(char *b, uint32_t n) noexcept {
- constexpr uint64_t mask24 = (uint64_t(1) << 24) - 1;
- constexpr uint64_t mask32 = (uint64_t(1) << 32) - 1;
- if (n < 100) {
- jeaiii_write_fd(b, n);
- return n < 10 ? b + 1 : b + 2;
- }
- if (n < 1000000) {
- if (n < 10000) {
- const uint32_t f0 = uint32_t(10 * (1 << 24) / 1e3 + 1) * n;
- jeaiii_write_fd(b, f0 >> 24);
- b -= n < 1000;
- const uint32_t f2 = uint32_t(f0 & mask24) * 100;
- jeaiii_write_dd(b + 2, f2 >> 24);
- return b + 4;
- }
- const uint64_t f0 = uint64_t(10 * (1ull << 32) / 1e5 + 1) * n;
- jeaiii_write_fd(b, f0 >> 32);
- b -= n < 100000;
- const uint64_t f2 = (f0 & mask32) * 100;
- jeaiii_write_dd(b + 2, f2 >> 32);
- const uint64_t f4 = (f2 & mask32) * 100;
- jeaiii_write_dd(b + 4, f4 >> 32);
- return b + 6;
- }
- const uint64_t f0 = uint64_t(10 * (1ull << 48) / 1e7 + 1) * n >> 16;
- jeaiii_write_fd(b, f0 >> 32);
- b -= n < 10000000;
- const uint64_t f2 = (f0 & mask32) * 100;
- jeaiii_write_dd(b + 2, f2 >> 32);
- const uint64_t f4 = (f2 & mask32) * 100;
- jeaiii_write_dd(b + 4, f4 >> 32);
- const uint64_t f6 = (f4 & mask32) * 100;
- jeaiii_write_dd(b + 6, f6 >> 32);
- return b + 8;
-}
-
-// Caller guarantees z < 10^8. Always writes exactly 8 digits.
-simdjson_really_inline char *jeaiii_8_digits(char *b, uint32_t z) noexcept {
- constexpr uint64_t mask32 = (uint64_t(1) << 32) - 1;
- const uint64_t f0 = (uint64_t((1ull << 48) / 1e6 + 1) * z >> 16) + 1;
- jeaiii_write_dd(b, f0 >> 32);
- const uint64_t f2 = (f0 & mask32) * 100;
- jeaiii_write_dd(b + 2, f2 >> 32);
- const uint64_t f4 = (f2 & mask32) * 100;
- jeaiii_write_dd(b + 4, f4 >> 32);
- const uint64_t f6 = (f4 & mask32) * 100;
- jeaiii_write_dd(b + 6, f6 >> 32);
- return b + 8;
-}
-
-// Caller guarantees 10^8 <= n < 2^32. Writes 9 or 10 digits.
-simdjson_really_inline char *jeaiii_9_or_10(char *b, uint64_t n) noexcept {
- constexpr uint64_t mask57 = (uint64_t(1) << 57) - 1;
- const uint64_t f0 = uint64_t(10 * (1ull << 57) / 1e9 + 1) * n;
- jeaiii_write_fd(b, f0 >> 57);
- b -= n < 1000000000;
- const uint64_t f2 = (f0 & mask57) * 100;
- jeaiii_write_dd(b + 2, f2 >> 57);
- const uint64_t f4 = (f2 & mask57) * 100;
- jeaiii_write_dd(b + 4, f4 >> 57);
- const uint64_t f6 = (f4 & mask57) * 100;
- jeaiii_write_dd(b + 6, f6 >> 57);
- const uint64_t f8 = (f6 & mask57) * 100;
- jeaiii_write_dd(b + 8, f8 >> 57);
- return b + 10;
-}
-
-simdjson_really_inline char *write_uint_jeaiii(char *b, uint64_t n) noexcept {
- if (n < 100000000) {
- return jeaiii_lt1e8(b, uint32_t(n));
- }
- if (n < (uint64_t(1) << 32)) {
- return jeaiii_9_or_10(b, n);
- }
- // At least 10 digits: the low 8 digits, and 2 to 12 digits above them.
- const uint32_t z = uint32_t(n % 100000000);
- uint64_t u = n / 100000000;
- if (u < 100000000) {
- // u has 2 to 8 digits (if u < 10, n would be below 2^32).
- b = jeaiii_lt1e8(b, uint32_t(u));
- } else if (u < (uint64_t(1) << 32)) {
- b = jeaiii_9_or_10(b, u);
- } else {
- // u has 11 or 12 digits: split off 8 more.
- const uint32_t y = uint32_t(u % 100000000);
- u /= 100000000;
- b = jeaiii_lt1e8(b, uint32_t(u)); // 3 or 4 digits
- b = jeaiii_8_digits(b, y);
- }
- return jeaiii_8_digits(b, z);
-}
+using simdjson::internal::write_uint_jeaiii;
// Writes v at p, which must have to_chars_buffer_size bytes available, and
// returns the end of what was written.
@@ -61996,20 +60264,22 @@ simdjson_inline void string_builder::append(number_type v) noexcept {
std::is_integral<number_type>::value ||
std::is_floating_point<number_type>::value,
"Unsupported number type");
+ static_assert(sizeof(number_type) <= sizeof(uint64_t),
+ "Integers wider than 64 bits are not supported");
// If C++17 is available, we can 'if constexpr' here.
SIMDJSON_IF_CONSTEXPR(std::is_same<number_type, bool>::value) {
if (v) {
constexpr char true_literal[] = "true";
constexpr size_t true_len = sizeof(true_literal) - 1;
if (capacity_check(true_len)) {
- std::memcpy(buffer.get() + position, true_literal, true_len);
+ std::memcpy(buffer_data() + position, true_literal, true_len);
position += true_len;
}
} else {
constexpr char false_literal[] = "false";
constexpr size_t false_len = sizeof(false_literal) - 1;
if (capacity_check(false_len)) {
- std::memcpy(buffer.get() + position, false_literal, false_len);
+ std::memcpy(buffer_data() + position, false_literal, false_len);
position += false_len;
}
}
@@ -62019,9 +60289,9 @@ simdjson_inline void string_builder::append(number_type v) noexcept {
if (capacity_check(max_number_size)) {
using unsigned_type = typename std::make_unsigned<number_type>::type;
char* end = internal::write_uint_jeaiii(
- buffer.get() + position,
+ buffer_data() + position,
static_cast<uint64_t>(static_cast<unsigned_type>(v)));
- position = end - buffer.get();
+ position = end - buffer_data();
}
}
else SIMDJSON_IF_CONSTEXPR(std::is_integral<number_type>::value) {
@@ -62035,11 +60305,11 @@ simdjson_inline void string_builder::append(number_type v) noexcept {
? unsigned_type(0) - static_cast<unsigned_type>(v)
: static_cast<unsigned_type>(v);
// Branchless: always write '-', advance only if negative.
- buffer.get()[position] = '-';
+ buffer_data()[position] = '-';
position += negative;
char* end = internal::write_uint_jeaiii(
- buffer.get() + position, static_cast<uint64_t>(pv));
- position = end - buffer.get();
+ buffer_data() + position, static_cast<uint64_t>(pv));
+ position = end - buffer_data();
}
}
else SIMDJSON_IF_CONSTEXPR(std::is_floating_point<number_type>::value) {
@@ -62055,16 +60325,16 @@ simdjson_inline void string_builder::append(number_type v) noexcept {
constexpr char nan_literal[] = "NaN";
constexpr size_t nan_len = sizeof(nan_literal) - 1;
- std::memcpy(buffer.get() + position, nan_literal, nan_len);
+ std::memcpy(buffer_data() + position, nan_literal, nan_len);
position += nan_len;
} else {
constexpr char inf_literal[] = "Infinity";
constexpr size_t inf_len = sizeof(inf_literal) - 1;
if (v < 0) {
- buffer.get()[position] = '-';
+ buffer_data()[position] = '-';
++position;
}
- std::memcpy(buffer.get() + position, inf_literal, inf_len);
+ std::memcpy(buffer_data() + position, inf_literal, inf_len);
position += inf_len;
}
return;
@@ -62072,9 +60342,9 @@ simdjson_inline void string_builder::append(number_type v) noexcept {
#endif
// We could specialize for float.
- char *end = simdjson::internal::to_chars(buffer.get() + position, nullptr,
+ char *end = simdjson::internal::to_chars(buffer_data() + position, nullptr,
double(v));
- position = end - buffer.get();
+ position = end - buffer_data();
}
}
}
@@ -62088,7 +60358,7 @@ string_builder::escape_and_append(std::string_view input) noexcept {
return;
}
if (capacity_check(6 * input.size())) {
- position += write_string_escaped(input, buffer.get() + position);
+ position += write_string_escaped(input, buffer_data() + position);
}
}
@@ -62101,9 +60371,9 @@ string_builder::escape_and_append_with_quotes(std::string_view input) noexcept {
return;
}
if (capacity_check(2 + 6 * input.size())) {
- buffer.get()[position++] = '"';
- position += write_string_escaped(input, buffer.get() + position);
- buffer.get()[position++] = '"';
+ buffer_data()[position++] = '"';
+ position += write_string_escaped(input, buffer_data() + position);
+ buffer_data()[position++] = '"';
}
}
@@ -62111,10 +60381,10 @@ simdjson_inline void
string_builder::escape_and_append_with_quotes(char input) noexcept {
// escaping might turn a control character into \x00xx so 6 characters.
if (capacity_check(2 + 6 * 1)) {
- buffer.get()[position++] = '"';
+ buffer_data()[position++] = '"';
std::string_view cinput(&input, 1);
- position += write_string_escaped(cinput, buffer.get() + position);
- buffer.get()[position++] = '"';
+ position += write_string_escaped(cinput, buffer_data() + position);
+ buffer_data()[position++] = '"';
}
}
@@ -62144,7 +60414,7 @@ simdjson_inline void string_builder::append_raw(const char *c) noexcept {
simdjson_inline void
string_builder::append_raw(std::string_view input) noexcept {
if (capacity_check(input.size())) {
- std::memcpy(buffer.get() + position, input.data(), input.size());
+ std::memcpy(buffer_data() + position, input.data(), input.size());
position += input.size();
}
}
@@ -62152,7 +60422,7 @@ string_builder::append_raw(std::string_view input) noexcept {
simdjson_inline void string_builder::append_raw(const char *str,
size_t len) noexcept {
if (capacity_check(len)) {
- std::memcpy(buffer.get() + position, str, len);
+ std::memcpy(buffer_data() + position, str, len);
position += len;
}
}
@@ -62160,7 +60430,7 @@ simdjson_inline void string_builder::append_raw(const char *str,
template <size_t N>
simdjson_inline void string_builder::append_raw_n(const char *str) noexcept {
if (capacity_check(N)) {
- std::memcpy(buffer.get() + position, str, N);
+ std::memcpy(buffer_data() + position, str, N);
position += N;
}
}
@@ -62170,7 +60440,7 @@ template <typename Text>
simdjson_inline void string_builder::append_static_string() noexcept {
constexpr auto& padded = constevalutil::static_string<Text>;
if (capacity_check(padded.size())) {
- std::memcpy(buffer.get() + position, padded.data(), padded.size());
+ std::memcpy(buffer_data() + position, padded.data(), padded.size());
position += constevalutil::static_string_size<Text>();
}
}
@@ -62264,54 +60534,54 @@ string_builder::view() const noexcept {
if (!is_valid) {
return simdjson::OUT_OF_CAPACITY;
}
- return std::string_view(buffer.get(), position);
+ return std::string_view(buffer_data(), position);
}
simdjson_inline simdjson_result<const char *> string_builder::c_str() noexcept {
if (capacity_check(1)) {
- buffer.get()[position] = '\0';
- return buffer.get();
+ buffer_data()[position] = '\0';
+ return buffer_data();
}
return simdjson::OUT_OF_CAPACITY;
}
simdjson_inline bool string_builder::validate_unicode() const noexcept {
- return simdjson::validate_utf8(buffer.get(), position);
+ return simdjson::validate_utf8(buffer_data(), position);
}
simdjson_inline void string_builder::start_object() noexcept {
if (capacity_check(1)) {
- buffer.get()[position++] = '{';
+ buffer_data()[position++] = '{';
}
}
simdjson_inline void string_builder::end_object() noexcept {
if (capacity_check(1)) {
- buffer.get()[position++] = '}';
+ buffer_data()[position++] = '}';
}
}
simdjson_inline void string_builder::start_array() noexcept {
if (capacity_check(1)) {
- buffer.get()[position++] = '[';
+ buffer_data()[position++] = '[';
}
}
simdjson_inline void string_builder::end_array() noexcept {
if (capacity_check(1)) {
- buffer.get()[position++] = ']';
+ buffer_data()[position++] = ']';
}
}
simdjson_inline void string_builder::append_comma() noexcept {
if (capacity_check(1)) {
- buffer.get()[position++] = ',';
+ buffer_data()[position++] = ',';
}
}
simdjson_inline void string_builder::append_colon() noexcept {
if (capacity_check(1)) {
- buffer.get()[position++] = ':';
+ buffer_data()[position++] = ':';
}
}
@@ -63561,30 +61831,6 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin
}
-struct escaping {
- static constexpr uint32_t BYTES_PROCESSED = 16;
- simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
-
- simdjson_inline bool has_escape() { return escape_bits != 0; }
- simdjson_inline int escape_index() { return trailing_zeroes(escape_bits); }
-
- uint64_t escape_bits;
-}; // struct escaping
-
-
-
-simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
- static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes");
- simd8<uint8_t> v(src);
- v.store(dst);
- simd8<bool> is_quote = (v == '"');
- simd8<bool> is_backslash = (v == '\\');
- simd8<bool> is_control = (v < 32);
- return {
- uint64_t((is_backslash | is_quote | is_control).to_bitmask())
- };
-}
-
} // unnamed namespace
} // namespace westmere
} // namespace simdjson
@@ -63653,6 +61899,10 @@ namespace builder {
class string_builder {
public:
simdjson_inline string_builder(size_t initial_capacity = DEFAULT_INITIAL_CAPACITY);
+ string_builder(const string_builder &) = delete;
+ string_builder &operator=(const string_builder &) = delete;
+ string_builder(string_builder &&) noexcept = default;
+ string_builder &operator=(string_builder &&) noexcept = default;
static constexpr size_t DEFAULT_INITIAL_CAPACITY = 1024;
@@ -63666,6 +61916,18 @@ public:
typename = typename std::enable_if<std::is_arithmetic<number_type>::value>::type>
simdjson_inline void append(number_type v) noexcept;
+#ifdef __SIZEOF_INT128__
+ /**
+ * Integers wider than 64 bits are not supported. Without these overloads, a
+ * 128-bit integer would either be truncated to 64 bits by append(number_type)
+ * (when the standard library counts it as arithmetic, as libc++ does) or be
+ * converted to char by append(char) (when it does not, as libstdc++ does in
+ * strict ISO mode).
+ */
+ void append(__int128_t v) noexcept = delete;
+ void append(__uint128_t v) noexcept = delete;
+#endif
+
/**
* Append character c.
*/
@@ -63844,6 +62106,20 @@ requires (!std::is_convertible<R, std::string_view>::value && !concepts::optiona
*/
simdjson_inline simdjson_result<const char *> c_str() noexcept;
+ /**
+ * Moves the written JSON buffer into out, replacing its content, without
+ * copying it. When the content fills at most half of the buffer, it is
+ * copied instead, so that a small result does not keep the whole buffer
+ * allocated. The builder is left empty: it keeps its buffer when the
+ * content was copied, and gives up its memory when the buffer was moved.
+ * Returns an error, and leaves out and the builder unchanged, if memory
+ * allocation failed.
+ *
+ * The result may not be valid UTF-8 if some of your content was not valid UTF-8.
+ * Use validate_unicode() to check the content.
+ */
+ simdjson_warn_unused inline error_code move_to(std::string &out) noexcept;
+
/**
* Return true if the content is valid UTF-8.
*/
@@ -63863,7 +62139,7 @@ requires (!std::is_convertible<R, std::string_view>::value && !concepts::optiona
// forces those reloads when accessed via members of *this). User
// code should NOT call these directly.
// ============================================================
- simdjson_inline char *unsafe_data() noexcept { return buffer.get(); }
+ simdjson_inline char *unsafe_data() noexcept { return buffer_data(); }
simdjson_inline size_t unsafe_position() const noexcept { return position; }
simdjson_inline size_t unsafe_capacity() const noexcept { return capacity; }
simdjson_inline void unsafe_set_position(size_t p) noexcept { position = p; }
@@ -63904,7 +62180,13 @@ private:
*/
simdjson_inline void set_valid(bool valid) noexcept;
- std::unique_ptr<char[]> buffer{};
+ simdjson_inline char *buffer_data() noexcept { return &buffer[0]; }
+ simdjson_inline const char *buffer_data() const noexcept { return buffer.data(); }
+
+ // We own capacity bytes: buffer.size() == capacity. Only the first position
+ // bytes hold JSON. A std::string lets move_to() hand the result over
+ // without a copy.
+ std::string buffer{};
size_t position{0};
size_t capacity{0};
bool is_valid{true};
@@ -63922,20 +62204,16 @@ template <class Z>
simdjson_warn_unused simdjson_result<std::string> to_json(const Z &z, size_t initial_capacity = simdjson::westmere::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
simdjson::westmere::builder::string_builder b(initial_capacity);
b.append(z);
- std::string_view s;
- auto e = b.view().get(s);
+ std::string s;
+ auto e = b.move_to(s);
if(e) { return e; }
- return std::string(s);
+ return s;
}
template <class Z>
simdjson_warn_unused error_code to_json(const Z &z, std::string &s, size_t initial_capacity = simdjson::westmere::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
simdjson::westmere::builder::string_builder b(initial_capacity);
b.append(z);
- std::string_view sv;
- auto e = b.view().get(sv);
- if(e) { return e; }
- s.assign(sv.data(), sv.size());
- return simdjson::SUCCESS;
+ return b.move_to(s);
}
#endif
@@ -63951,7 +62229,7 @@ simdjson_warn_unused error_code to_json(const Z &z, std::string &s, size_t initi
#ifndef SIMDJSON_GENERIC_BUILDER_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
-/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_STRING_BUILDER_H */
+/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_BUILDER_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/builder/json_string_builder.h" */
/* amalgamation skipped (editor-only): #include "simdjson/concepts.h" */
/* amalgamation skipped (editor-only): #include "simdjson/annotations.h" */
@@ -63977,7 +62255,7 @@ namespace builder {
// included at the point this header is parsed; without these forwards,
// name lookup falls back to the wrong outer namespace).
namespace internal {
-simdjson_really_inline char *write_uint_jeaiii(char *p, uint64_t v) noexcept;
+using simdjson::internal::write_uint_jeaiii;
simdjson_inline char *write_double(char *p, double v) noexcept;
} // namespace internal
simdjson_really_inline size_t write_string_escaped(const std::string_view input, char *out);
@@ -64110,6 +62388,51 @@ consteval bool is_serialized_member(std::meta::info dm) {
&& !simdjson::detail::has_annotation(dm, ^^simdjson::detail::skip_serializing_tag);
}
+template <class Target, class T, class... Visiting>
+consteval bool members_reach();
+
+// Whether a value of type T may hold a value of type Target, directly or
+// through optionals, smart pointers, containers and members. Visiting lists
+// the structures being searched, so that the search terminates.
+template <class Target, class T, class... Visiting>
+consteval bool reaches() {
+ if constexpr (std::is_same_v<T, Target>) {
+ return true;
+ } else if constexpr ((std::is_same_v<T, Visiting> || ...) || require_custom_serialization<T> ||
+ std::is_same_v<T, std::string> || std::is_same_v<T, std::string_view>) {
+ return false;
+ } else if constexpr (concepts::optional_type<T> || concepts::smart_pointer<T>) {
+ return reaches<Target, std::remove_cvref_t<decltype(*std::declval<const T &>())>, Visiting...>();
+ } else if constexpr (concepts::string_view_keyed_map<T>) {
+ return reaches<Target, std::remove_cvref_t<typename T::mapped_type>, Visiting...>();
+ } else if constexpr (concepts::container_but_not_string<T> || concepts::appendable_containers<T>) {
+ return reaches<Target, std::remove_cvref_t<std::ranges::range_value_t<T>>, Visiting...>();
+ } else if constexpr (std::is_class_v<T>) {
+ return members_reach<Target, T, Visiting...>();
+ } else {
+ return false;
+ }
+}
+
+template <class Target, class T, class... Visiting>
+consteval bool members_reach() {
+ bool found = false;
+ template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) {
+ if constexpr (is_serialized_member(dm)) {
+ found = found || reaches<Target, std::remove_cvref_t<decltype(std::declval<const T &>().[:dm:])>, T, Visiting...>();
+ }
+ };
+ return found;
+}
+
+// Whether the structure T may contain itself, as in
+// struct Rule { std::unique_ptr<Rule> subrule; }. Serializing such a type
+// recurses at runtime, which simdjson_really_inline functions cannot do.
+template <class T>
+consteval bool is_recursive() {
+ return members_reach<T, T>();
+}
+
// False when the member has a skip_serializing_if<pred> annotation and
// pred(value) is true.
template <auto dm, typename V>
@@ -64293,6 +62616,8 @@ simdjson_really_inline constexpr void atom(W &w, const T &m) {
template<class W, typename number_type,
typename = typename std::enable_if<std::is_arithmetic<number_type>::value && !std::is_same_v<number_type, char>>::type>
simdjson_really_inline constexpr void atom(W &w, const number_type t) {
+ static_assert(sizeof(number_type) <= sizeof(uint64_t),
+ "Integers wider than 64 bits are not supported");
// Booleans / floats: defer to string_builder (rare path; keeps writer hot
// path free of float-formatter machinery). For integers, write directly
// via jeaiii using local pos.
@@ -64331,6 +62656,31 @@ simdjson_really_inline constexpr void atom(W &w, const number_type t) {
}
}
+namespace annotation_detail {
+
+template <class W, class T>
+simdjson_really_inline void atom_object(W &w, const T &t) {
+ if constexpr (simdjson::detail::has_annotation(^^T, ^^simdjson::detail::transparent_tag)) {
+ // A transparent structure is serialized as its single member.
+ constexpr auto dm = simdjson::detail::transparent_member(^^T);
+ atom_member<dm>(w, t.[:dm:]);
+ } else {
+ bool first = true;
+ if (!w.ensure(1)) { return; }
+ w.ptr[w.pos++] = '{';
+ atom_fields(w, t, first);
+ if (!w.ensure(1)) { return; }
+ w.ptr[w.pos++] = '}';
+ }
+}
+
+template <class W, class T>
+void atom_object_outlined(W &w, const T &t) {
+ atom_object(w, t);
+}
+
+} // namespace annotation_detail
+
template <class W, class T>
requires(std::is_class_v<T> && !concepts::container_but_not_string<T> &&
!concepts::string_view_keyed_map<T> &&
@@ -64342,17 +62692,11 @@ template <class W, class T>
!std::is_same_v<T, const char*> &&
!std::is_same_v<T, char> && !require_custom_serialization<T>)
simdjson_really_inline constexpr void atom(W &w, const T &t) {
- if constexpr (simdjson::detail::has_annotation(^^T, ^^simdjson::detail::transparent_tag)) {
- // A transparent structure is serialized as its single member.
- constexpr auto dm = simdjson::detail::transparent_member(^^T);
- annotation_detail::atom_member<dm>(w, t.[:dm:]);
+ if constexpr (annotation_detail::is_recursive<T>()) {
+ // Break the cycle of simdjson_really_inline calls.
+ annotation_detail::atom_object_outlined(w, t);
} else {
- bool first = true;
- if (!w.ensure(1)) { return; }
- w.ptr[w.pos++] = '{';
- annotation_detail::atom_fields(w, t, first);
- if (!w.ensure(1)) { return; }
- w.ptr[w.pos++] = '}';
+ annotation_detail::atom_object(w, t);
}
}
@@ -64452,25 +62796,30 @@ namespace bound_detail {
// Whether size_bound covers everything that atom() writes for T: not when a
// member is serialized by a with<Adapter> serializer, which writes an unknown
// amount through the string_builder.
-template <class T>
+// Visiting lists the structures being checked, so that a recursive type
+// (struct Node { std::unique_ptr<Node> next; }) does not recurse forever: a
+// type already being visited adds nothing, its members get checked once.
+template <class T, class... Visiting>
consteval bool is_bounded() {
- if constexpr (require_custom_serialization<T>) {
+ if constexpr ((std::is_same_v<T, Visiting> || ...)) {
+ return true;
+ } else if constexpr (require_custom_serialization<T>) {
return false;
} else if constexpr (std::is_same_v<T, std::string> || std::is_same_v<T, std::string_view> ||
std::is_same_v<T, const char *> || std::is_arithmetic_v<T> || std::is_enum_v<T>) {
return true;
} else if constexpr (concepts::optional_type<T> || concepts::smart_pointer<T>) {
- return is_bounded<std::remove_cvref_t<decltype(*std::declval<const T &>())>>();
+ return is_bounded<std::remove_cvref_t<decltype(*std::declval<const T &>())>, Visiting...>();
} else if constexpr (concepts::string_view_keyed_map<T>) {
- return is_bounded<std::remove_cvref_t<typename T::mapped_type>>();
+ return is_bounded<std::remove_cvref_t<typename T::mapped_type>, Visiting...>();
} else if constexpr (concepts::container_but_not_string<T> || concepts::appendable_containers<T>) {
- return is_bounded<std::remove_cvref_t<std::ranges::range_value_t<T>>>();
+ return is_bounded<std::remove_cvref_t<std::ranges::range_value_t<T>>, Visiting...>();
} else {
bool bounded = true;
template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) {
if constexpr (annotation_detail::is_serialized_member(dm)) {
bounded = bounded && simdjson::detail::annotation_of_template(dm, ^^simdjson::detail::with_t) == std::meta::info{} &&
- is_bounded<std::remove_cvref_t<decltype(std::declval<const T &>().[:dm:])>>();
+ is_bounded<std::remove_cvref_t<decltype(std::declval<const T &>().[:dm:])>, T, Visiting...>();
}
};
return bounded;
@@ -64508,6 +62857,11 @@ simdjson_really_inline size_t fields_bound(const T &t) noexcept {
return bound;
}
+template <class T>
+size_t fields_bound_outlined(const T &t) noexcept {
+ return fields_bound(t);
+}
+
template <class T>
simdjson_really_inline size_t size_bound([[maybe_unused]] const T &t) noexcept {
if constexpr (std::is_same_v<T, char>) {
@@ -64552,6 +62906,9 @@ simdjson_really_inline size_t size_bound([[maybe_unused]] const T &t) noexcept {
} else if constexpr (simdjson::detail::has_annotation(^^T, ^^simdjson::detail::transparent_tag)) {
constexpr auto dm = simdjson::detail::transparent_member(^^T);
return size_bound(t.[:dm:]);
+ } else if constexpr (annotation_detail::is_recursive<T>()) {
+ // Break the cycle of simdjson_really_inline calls.
+ return 2 + fields_bound_outlined(t);
} else {
return 2 + fields_bound(t);
}
@@ -64686,10 +63043,7 @@ simdjson_warn_unused error_code to_json(const Z &z, std::string &s, size_t initi
} else {
string_builder b(initial_capacity);
append(b, z);
- std::string_view view;
- if(auto e = b.view().get(view); e) { return e; }
- s.assign(view);
- return SUCCESS;
+ return b.move_to(s);
}
}
@@ -64752,9 +63106,9 @@ template<constevalutil::fixed_string... FieldNames, typename T>
simdjson_warn_unused simdjson_result<std::string> extract_from(const T &obj, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) {
string_builder b(initial_capacity);
extract_from<FieldNames...>(b, obj);
- std::string_view s;
- if(auto e = b.view().get(s); e) { return e; }
- return std::string(s);
+ std::string s;
+ if(auto e = b.move_to(s); e) { return e; }
+ return s;
}
SIMDJSON_POP_DISABLE_WARNINGS
@@ -64776,16 +63130,16 @@ template<constevalutil::fixed_string... FieldNames, typename T>
simdjson_warn_unused simdjson_result<std::string> extract_from(const T &obj, size_t initial_capacity = westmere::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
westmere::builder::string_builder b(initial_capacity);
westmere::builder::extract_from<FieldNames...>(b, obj);
- std::string_view s;
- if(auto e = b.view().get(s); e) { return e; }
- return std::string(s);
+ std::string s;
+ if(auto e = b.move_to(s); e) { return e; }
+ return s;
}
} // namespace simdjson
#endif // SIMDJSON_STATIC_REFLECTION
-#endif
+#endif // SIMDJSON_GENERIC_BUILDER_H
/* end file simdjson/generic/builder/json_builder.h for westmere */
/* including simdjson/generic/builder/fractured_json_builder.h for westmere: #include "simdjson/generic/builder/fractured_json_builder.h" */
/* begin file simdjson/generic/builder/fractured_json_builder.h for westmere */
@@ -64924,6 +63278,7 @@ simdjson_warn_unused simdjson_result<std::string> extract_fractured_json(
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_STRING_BUILDER_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/builder/json_string_builder.h" */
+/* amalgamation skipped (editor-only): #include "simdjson/internal/jeaiii.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
/*
@@ -65036,6 +63391,12 @@ inline bool has_json_escapable_byte(uint64_t x) {
**/
+#if SIMDJSON_EXPERIMENTAL_HAS_SSE2 || SIMDJSON_EXPERIMENTAL_HAS_NEON
+#define SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE 1
+#endif
+
+// With block escaping (SSE2/NEON), write_string_escaped scans the input itself.
+#if !SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE
// Scalar fallback for finding next quotable character
SIMDJSON_CONSTEXPR_LAMBDA simdjson_inline size_t
find_next_json_quotable_character_scalar(const std::string_view view,
@@ -65051,82 +63412,7 @@ find_next_json_quotable_character_scalar(const std::string_view view,
// SIMD-accelerated position finding that directly locates the first quotable
// character, combining detection and position extraction in a single pass to
// minimize redundant work.
-#if SIMDJSON_EXPERIMENTAL_HAS_NEON
-simdjson_inline size_t
-find_next_json_quotable_character(const std::string_view view,
- size_t location) noexcept {
- const size_t len = view.size();
- const uint8_t *ptr =
- reinterpret_cast<const uint8_t *>(view.data()) + location;
- size_t remaining = len - location;
-
- // SIMD constants for characters requiring escape
- uint8x16_t v34 = vdupq_n_u8(34); // '"'
- uint8x16_t v92 = vdupq_n_u8(92); // '\\'
- uint8x16_t v32 = vdupq_n_u8(32); // control char threshold
-
- while (remaining >= 16) {
- uint8x16_t word = vld1q_u8(ptr);
-
- // Check for quotable characters: '"', '\\', or control chars (< 32)
- uint8x16_t needs_escape = vceqq_u8(word, v34);
- needs_escape = vorrq_u8(needs_escape, vceqq_u8(word, v92));
- needs_escape = vorrq_u8(needs_escape, vcltq_u8(word, v32));
-
- const uint8x8_t res = vshrn_n_u16(vreinterpretq_u16_u8(needs_escape), 4);
- const uint64_t mask = vget_lane_u64(vreinterpret_u64_u8(res), 0);
- if(mask != 0) {
- size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
- auto trailing_zero = trailing_zeroes(mask);
- return offset + (trailing_zero >> 2);
- }
- ptr += 16;
- remaining -= 16;
- }
-
- // Scalar fallback for remaining bytes
- size_t current = len - remaining;
- return find_next_json_quotable_character_scalar(view, current);
-}
-#elif SIMDJSON_EXPERIMENTAL_HAS_SSE2
-simdjson_inline size_t
-find_next_json_quotable_character(const std::string_view view,
- size_t location) noexcept {
- const size_t len = view.size();
- const uint8_t *ptr =
- reinterpret_cast<const uint8_t *>(view.data()) + location;
- size_t remaining = len - location;
-
- // SIMD constants
- __m128i v34 = _mm_set1_epi8(34); // '"'
- __m128i v92 = _mm_set1_epi8(92); // '\\'
- __m128i v31 = _mm_set1_epi8(31); // for control char detection
-
- while (remaining >= 16) {
- __m128i word = _mm_loadu_si128(reinterpret_cast<const __m128i *>(ptr));
-
- // Check for quotable characters
- __m128i needs_escape = _mm_cmpeq_epi8(word, v34);
- needs_escape = _mm_or_si128(needs_escape, _mm_cmpeq_epi8(word, v92));
- needs_escape = _mm_or_si128(
- needs_escape,
- _mm_cmpeq_epi8(_mm_subs_epu8(word, v31), _mm_setzero_si128()));
-
- int mask = _mm_movemask_epi8(needs_escape);
- if (mask != 0) {
- // Found quotable character - use trailing zero count to find position
- size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
- return offset + trailing_zeroes(mask);
- }
- ptr += 16;
- remaining -= 16;
- }
-
- // Scalar fallback for remaining bytes
- size_t current = len - remaining;
- return find_next_json_quotable_character_scalar(view, current);
-}
-#elif SIMDJSON_EXPERIMENTAL_HAS_LASX
+#if SIMDJSON_EXPERIMENTAL_HAS_LASX
simdjson_inline size_t
find_next_json_quotable_character(const std::string_view view,
size_t location) noexcept {
@@ -65299,6 +63585,7 @@ find_next_json_quotable_character(const std::string_view view,
return find_next_json_quotable_character_scalar(view, location);
}
#endif
+#endif // !SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE
SIMDJSON_CONSTEXPR_LAMBDA static std::string_view control_chars[] = {
"\\u0000", "\\u0001", "\\u0002", "\\u0003", "\\u0004", "\\u0005", "\\u0006",
@@ -65326,10 +63613,6 @@ SIMDJSON_CONSTEXPR_LAMBDA simdjson_inline void escape_json_char(char c, char *&o
}
}
-#if SIMDJSON_EXPERIMENTAL_HAS_SSE2 || SIMDJSON_EXPERIMENTAL_HAS_NEON
-#define SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE 1
-#endif
-
#if SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE
#if SIMDJSON_EXPERIMENTAL_HAS_SSE2
@@ -65610,10 +63893,21 @@ inline size_t write_string_escaped(const std::string_view input, char *out) {
#undef SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE
-simdjson_inline string_builder::string_builder(size_t initial_capacity)
- : buffer(new(std::nothrow) char[initial_capacity]), position(0),
- capacity(buffer.get() != nullptr ? initial_capacity : 0),
- is_valid(buffer.get() != nullptr) {}
+namespace internal {
+// Sets the size of s to n without initializing the new bytes when the
+// standard library allows it (C++23 resize_and_overwrite).
+simdjson_inline void resize_uninitialized(std::string &s, size_t n) {
+#if defined(__cpp_lib_string_resize_and_overwrite) && __cpp_lib_string_resize_and_overwrite >= 202110L
+ s.resize_and_overwrite(n, [](char *, size_t len) noexcept { return len; });
+#else
+ s.resize(n);
+#endif
+}
+} // namespace internal
+
+simdjson_inline string_builder::string_builder(size_t initial_capacity) {
+ grow_buffer(initial_capacity);
+}
simdjson_inline bool string_builder::capacity_check(size_t upcoming_bytes) {
// We use the convention that when is_valid is false, then the capacity and
@@ -65636,22 +63930,63 @@ inline void string_builder::grow_buffer(size_t desired_capacity) {
if (!is_valid) {
return;
}
- std::unique_ptr<char[]> new_buffer(new (std::nothrow) char[desired_capacity]);
- if (new_buffer.get() == nullptr) {
+ std::string new_buffer;
+ if (desired_capacity > new_buffer.max_size()) {
+ set_valid(false);
+ return;
+ }
+#if defined(__cpp_exceptions) || defined(_CPPUNWIND)
+ try {
+ internal::resize_uninitialized(new_buffer, desired_capacity);
+ } catch (...) { // std::bad_alloc
set_valid(false);
return;
}
- std::memcpy(new_buffer.get(), buffer.get(), position);
+#else
+ // Without exceptions, a failed allocation terminates the program.
+ internal::resize_uninitialized(new_buffer, desired_capacity);
+#endif
+ std::memcpy(&new_buffer[0], buffer_data(), position);
buffer.swap(new_buffer);
capacity = desired_capacity;
}
+// Not simdjson_inline: MSVC cannot force-inline a function with a try block (C4714).
+inline error_code string_builder::move_to(std::string &out) noexcept {
+ if (!is_valid) {
+ return simdjson::OUT_OF_CAPACITY;
+ }
+ if (position <= buffer.capacity() / 2) {
+ // Most of the buffer is unused: copy the bytes out, so that a small
+ // result does not keep the whole buffer allocated. The builder keeps its
+ // buffer for what it writes next.
+#if defined(__cpp_exceptions) || defined(_CPPUNWIND)
+ try {
+ out.assign(buffer.data(), position);
+ } catch (...) { // std::bad_alloc
+ return simdjson::MEMALLOC;
+ }
+#else
+ // Without exceptions, a failed allocation terminates the program.
+ out.assign(buffer.data(), position);
+#endif
+ position = 0;
+ return simdjson::SUCCESS;
+ }
+ buffer.resize(position); // shrinking never allocates
+ out = std::move(buffer);
+ std::string().swap(buffer);
+ position = 0;
+ capacity = 0;
+ return simdjson::SUCCESS;
+}
+
simdjson_inline void string_builder::set_valid(bool valid) noexcept {
if (!valid) {
is_valid = false;
capacity = 0;
position = 0;
- buffer.reset();
+ std::string().swap(buffer);
} else {
is_valid = true;
}
@@ -65663,7 +63998,7 @@ simdjson_inline size_t string_builder::size() const noexcept {
simdjson_inline void string_builder::append(char c) noexcept {
if (capacity_check(1)) {
- buffer.get()[position++] = c;
+ buffer_data()[position++] = c;
}
}
@@ -65671,7 +64006,7 @@ simdjson_inline void string_builder::append_null() noexcept {
constexpr char null_literal[] = "null";
constexpr size_t null_len = sizeof(null_literal) - 1;
if (capacity_check(null_len)) {
- std::memcpy(buffer.get() + position, null_literal, null_len);
+ std::memcpy(buffer_data() + position, null_literal, null_len);
position += null_len;
}
}
@@ -65681,124 +64016,14 @@ simdjson_inline void string_builder::clear() noexcept {
// if it was invalid, we should try to repair it
if (!is_valid) {
capacity = 0;
- buffer.reset();
+ std::string().swap(buffer);
is_valid = true;
}
}
namespace internal {
-// Integer to decimal: James Edward Anhalt III's algorithm
-static const char jeaiii_dd[201] =
- "00010203040506070809101112131415161718192021222324252627282930313233343536373839"
- "40414243444546474849505152535455565758596061626364656667686970717273747576777879"
- "8081828384858687888990919293949596979899";
-static const char jeaiii_fd[201] =
- "0\0" "1\0" "2\0" "3\0" "4\0" "5\0" "6\0" "7\0" "8\0" "9\0"
- "10111213141516171819202122232425262728293031323334353637383940414243444546474849"
- "50515253545556575859606162636465666768697071727374757677787980818283848586878889"
- "90919293949596979899";
-
-simdjson_really_inline void jeaiii_write_dd(char *p, uint64_t k) noexcept {
- std::memcpy(p, &jeaiii_dd[2 * k], 2);
-}
-simdjson_really_inline void jeaiii_write_fd(char *p, uint64_t k) noexcept {
- std::memcpy(p, &jeaiii_fd[2 * k], 2);
-}
-
-// Caller guarantees n < 10^8. Writes 1 to 8 digits.
-simdjson_really_inline char *jeaiii_lt1e8(char *b, uint32_t n) noexcept {
- constexpr uint64_t mask24 = (uint64_t(1) << 24) - 1;
- constexpr uint64_t mask32 = (uint64_t(1) << 32) - 1;
- if (n < 100) {
- jeaiii_write_fd(b, n);
- return n < 10 ? b + 1 : b + 2;
- }
- if (n < 1000000) {
- if (n < 10000) {
- const uint32_t f0 = uint32_t(10 * (1 << 24) / 1e3 + 1) * n;
- jeaiii_write_fd(b, f0 >> 24);
- b -= n < 1000;
- const uint32_t f2 = uint32_t(f0 & mask24) * 100;
- jeaiii_write_dd(b + 2, f2 >> 24);
- return b + 4;
- }
- const uint64_t f0 = uint64_t(10 * (1ull << 32) / 1e5 + 1) * n;
- jeaiii_write_fd(b, f0 >> 32);
- b -= n < 100000;
- const uint64_t f2 = (f0 & mask32) * 100;
- jeaiii_write_dd(b + 2, f2 >> 32);
- const uint64_t f4 = (f2 & mask32) * 100;
- jeaiii_write_dd(b + 4, f4 >> 32);
- return b + 6;
- }
- const uint64_t f0 = uint64_t(10 * (1ull << 48) / 1e7 + 1) * n >> 16;
- jeaiii_write_fd(b, f0 >> 32);
- b -= n < 10000000;
- const uint64_t f2 = (f0 & mask32) * 100;
- jeaiii_write_dd(b + 2, f2 >> 32);
- const uint64_t f4 = (f2 & mask32) * 100;
- jeaiii_write_dd(b + 4, f4 >> 32);
- const uint64_t f6 = (f4 & mask32) * 100;
- jeaiii_write_dd(b + 6, f6 >> 32);
- return b + 8;
-}
-
-// Caller guarantees z < 10^8. Always writes exactly 8 digits.
-simdjson_really_inline char *jeaiii_8_digits(char *b, uint32_t z) noexcept {
- constexpr uint64_t mask32 = (uint64_t(1) << 32) - 1;
- const uint64_t f0 = (uint64_t((1ull << 48) / 1e6 + 1) * z >> 16) + 1;
- jeaiii_write_dd(b, f0 >> 32);
- const uint64_t f2 = (f0 & mask32) * 100;
- jeaiii_write_dd(b + 2, f2 >> 32);
- const uint64_t f4 = (f2 & mask32) * 100;
- jeaiii_write_dd(b + 4, f4 >> 32);
- const uint64_t f6 = (f4 & mask32) * 100;
- jeaiii_write_dd(b + 6, f6 >> 32);
- return b + 8;
-}
-
-// Caller guarantees 10^8 <= n < 2^32. Writes 9 or 10 digits.
-simdjson_really_inline char *jeaiii_9_or_10(char *b, uint64_t n) noexcept {
- constexpr uint64_t mask57 = (uint64_t(1) << 57) - 1;
- const uint64_t f0 = uint64_t(10 * (1ull << 57) / 1e9 + 1) * n;
- jeaiii_write_fd(b, f0 >> 57);
- b -= n < 1000000000;
- const uint64_t f2 = (f0 & mask57) * 100;
- jeaiii_write_dd(b + 2, f2 >> 57);
- const uint64_t f4 = (f2 & mask57) * 100;
- jeaiii_write_dd(b + 4, f4 >> 57);
- const uint64_t f6 = (f4 & mask57) * 100;
- jeaiii_write_dd(b + 6, f6 >> 57);
- const uint64_t f8 = (f6 & mask57) * 100;
- jeaiii_write_dd(b + 8, f8 >> 57);
- return b + 10;
-}
-
-simdjson_really_inline char *write_uint_jeaiii(char *b, uint64_t n) noexcept {
- if (n < 100000000) {
- return jeaiii_lt1e8(b, uint32_t(n));
- }
- if (n < (uint64_t(1) << 32)) {
- return jeaiii_9_or_10(b, n);
- }
- // At least 10 digits: the low 8 digits, and 2 to 12 digits above them.
- const uint32_t z = uint32_t(n % 100000000);
- uint64_t u = n / 100000000;
- if (u < 100000000) {
- // u has 2 to 8 digits (if u < 10, n would be below 2^32).
- b = jeaiii_lt1e8(b, uint32_t(u));
- } else if (u < (uint64_t(1) << 32)) {
- b = jeaiii_9_or_10(b, u);
- } else {
- // u has 11 or 12 digits: split off 8 more.
- const uint32_t y = uint32_t(u % 100000000);
- u /= 100000000;
- b = jeaiii_lt1e8(b, uint32_t(u)); // 3 or 4 digits
- b = jeaiii_8_digits(b, y);
- }
- return jeaiii_8_digits(b, z);
-}
+using simdjson::internal::write_uint_jeaiii;
// Writes v at p, which must have to_chars_buffer_size bytes available, and
// returns the end of what was written.
@@ -65826,20 +64051,22 @@ simdjson_inline void string_builder::append(number_type v) noexcept {
std::is_integral<number_type>::value ||
std::is_floating_point<number_type>::value,
"Unsupported number type");
+ static_assert(sizeof(number_type) <= sizeof(uint64_t),
+ "Integers wider than 64 bits are not supported");
// If C++17 is available, we can 'if constexpr' here.
SIMDJSON_IF_CONSTEXPR(std::is_same<number_type, bool>::value) {
if (v) {
constexpr char true_literal[] = "true";
constexpr size_t true_len = sizeof(true_literal) - 1;
if (capacity_check(true_len)) {
- std::memcpy(buffer.get() + position, true_literal, true_len);
+ std::memcpy(buffer_data() + position, true_literal, true_len);
position += true_len;
}
} else {
constexpr char false_literal[] = "false";
constexpr size_t false_len = sizeof(false_literal) - 1;
if (capacity_check(false_len)) {
- std::memcpy(buffer.get() + position, false_literal, false_len);
+ std::memcpy(buffer_data() + position, false_literal, false_len);
position += false_len;
}
}
@@ -65849,9 +64076,9 @@ simdjson_inline void string_builder::append(number_type v) noexcept {
if (capacity_check(max_number_size)) {
using unsigned_type = typename std::make_unsigned<number_type>::type;
char* end = internal::write_uint_jeaiii(
- buffer.get() + position,
+ buffer_data() + position,
static_cast<uint64_t>(static_cast<unsigned_type>(v)));
- position = end - buffer.get();
+ position = end - buffer_data();
}
}
else SIMDJSON_IF_CONSTEXPR(std::is_integral<number_type>::value) {
@@ -65865,11 +64092,11 @@ simdjson_inline void string_builder::append(number_type v) noexcept {
? unsigned_type(0) - static_cast<unsigned_type>(v)
: static_cast<unsigned_type>(v);
// Branchless: always write '-', advance only if negative.
- buffer.get()[position] = '-';
+ buffer_data()[position] = '-';
position += negative;
char* end = internal::write_uint_jeaiii(
- buffer.get() + position, static_cast<uint64_t>(pv));
- position = end - buffer.get();
+ buffer_data() + position, static_cast<uint64_t>(pv));
+ position = end - buffer_data();
}
}
else SIMDJSON_IF_CONSTEXPR(std::is_floating_point<number_type>::value) {
@@ -65885,16 +64112,16 @@ simdjson_inline void string_builder::append(number_type v) noexcept {
constexpr char nan_literal[] = "NaN";
constexpr size_t nan_len = sizeof(nan_literal) - 1;
- std::memcpy(buffer.get() + position, nan_literal, nan_len);
+ std::memcpy(buffer_data() + position, nan_literal, nan_len);
position += nan_len;
} else {
constexpr char inf_literal[] = "Infinity";
constexpr size_t inf_len = sizeof(inf_literal) - 1;
if (v < 0) {
- buffer.get()[position] = '-';
+ buffer_data()[position] = '-';
++position;
}
- std::memcpy(buffer.get() + position, inf_literal, inf_len);
+ std::memcpy(buffer_data() + position, inf_literal, inf_len);
position += inf_len;
}
return;
@@ -65902,9 +64129,9 @@ simdjson_inline void string_builder::append(number_type v) noexcept {
#endif
// We could specialize for float.
- char *end = simdjson::internal::to_chars(buffer.get() + position, nullptr,
+ char *end = simdjson::internal::to_chars(buffer_data() + position, nullptr,
double(v));
- position = end - buffer.get();
+ position = end - buffer_data();
}
}
}
@@ -65918,7 +64145,7 @@ string_builder::escape_and_append(std::string_view input) noexcept {
return;
}
if (capacity_check(6 * input.size())) {
- position += write_string_escaped(input, buffer.get() + position);
+ position += write_string_escaped(input, buffer_data() + position);
}
}
@@ -65931,9 +64158,9 @@ string_builder::escape_and_append_with_quotes(std::string_view input) noexcept {
return;
}
if (capacity_check(2 + 6 * input.size())) {
- buffer.get()[position++] = '"';
- position += write_string_escaped(input, buffer.get() + position);
- buffer.get()[position++] = '"';
+ buffer_data()[position++] = '"';
+ position += write_string_escaped(input, buffer_data() + position);
+ buffer_data()[position++] = '"';
}
}
@@ -65941,10 +64168,10 @@ simdjson_inline void
string_builder::escape_and_append_with_quotes(char input) noexcept {
// escaping might turn a control character into \x00xx so 6 characters.
if (capacity_check(2 + 6 * 1)) {
- buffer.get()[position++] = '"';
+ buffer_data()[position++] = '"';
std::string_view cinput(&input, 1);
- position += write_string_escaped(cinput, buffer.get() + position);
- buffer.get()[position++] = '"';
+ position += write_string_escaped(cinput, buffer_data() + position);
+ buffer_data()[position++] = '"';
}
}
@@ -65974,7 +64201,7 @@ simdjson_inline void string_builder::append_raw(const char *c) noexcept {
simdjson_inline void
string_builder::append_raw(std::string_view input) noexcept {
if (capacity_check(input.size())) {
- std::memcpy(buffer.get() + position, input.data(), input.size());
+ std::memcpy(buffer_data() + position, input.data(), input.size());
position += input.size();
}
}
@@ -65982,7 +64209,7 @@ string_builder::append_raw(std::string_view input) noexcept {
simdjson_inline void string_builder::append_raw(const char *str,
size_t len) noexcept {
if (capacity_check(len)) {
- std::memcpy(buffer.get() + position, str, len);
+ std::memcpy(buffer_data() + position, str, len);
position += len;
}
}
@@ -65990,7 +64217,7 @@ simdjson_inline void string_builder::append_raw(const char *str,
template <size_t N>
simdjson_inline void string_builder::append_raw_n(const char *str) noexcept {
if (capacity_check(N)) {
- std::memcpy(buffer.get() + position, str, N);
+ std::memcpy(buffer_data() + position, str, N);
position += N;
}
}
@@ -66000,7 +64227,7 @@ template <typename Text>
simdjson_inline void string_builder::append_static_string() noexcept {
constexpr auto& padded = constevalutil::static_string<Text>;
if (capacity_check(padded.size())) {
- std::memcpy(buffer.get() + position, padded.data(), padded.size());
+ std::memcpy(buffer_data() + position, padded.data(), padded.size());
position += constevalutil::static_string_size<Text>();
}
}
@@ -66094,54 +64321,54 @@ string_builder::view() const noexcept {
if (!is_valid) {
return simdjson::OUT_OF_CAPACITY;
}
- return std::string_view(buffer.get(), position);
+ return std::string_view(buffer_data(), position);
}
simdjson_inline simdjson_result<const char *> string_builder::c_str() noexcept {
if (capacity_check(1)) {
- buffer.get()[position] = '\0';
- return buffer.get();
+ buffer_data()[position] = '\0';
+ return buffer_data();
}
return simdjson::OUT_OF_CAPACITY;
}
simdjson_inline bool string_builder::validate_unicode() const noexcept {
- return simdjson::validate_utf8(buffer.get(), position);
+ return simdjson::validate_utf8(buffer_data(), position);
}
simdjson_inline void string_builder::start_object() noexcept {
if (capacity_check(1)) {
- buffer.get()[position++] = '{';
+ buffer_data()[position++] = '{';
}
}
simdjson_inline void string_builder::end_object() noexcept {
if (capacity_check(1)) {
- buffer.get()[position++] = '}';
+ buffer_data()[position++] = '}';
}
}
simdjson_inline void string_builder::start_array() noexcept {
if (capacity_check(1)) {
- buffer.get()[position++] = '[';
+ buffer_data()[position++] = '[';
}
}
simdjson_inline void string_builder::end_array() noexcept {
if (capacity_check(1)) {
- buffer.get()[position++] = ']';
+ buffer_data()[position++] = ']';
}
}
simdjson_inline void string_builder::append_comma() noexcept {
if (capacity_check(1)) {
- buffer.get()[position++] = ',';
+ buffer_data()[position++] = ',';
}
}
simdjson_inline void string_builder::append_colon() noexcept {
if (capacity_check(1)) {
- buffer.get()[position++] = ':';
+ buffer_data()[position++] = ':';
}
}
@@ -66879,30 +65106,6 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin
}
-struct escaping {
- static constexpr uint32_t BYTES_PROCESSED = 16;
- simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
-
- simdjson_inline bool has_escape() { return escape_bits != 0; }
- simdjson_inline int escape_index() { return trailing_zeroes(escape_bits); }
-
- uint64_t escape_bits;
-}; // struct escaping
-
-
-
-simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
- static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes");
- simd8<uint8_t> v(src);
- v.store(dst);
- simd8<bool> is_quote = (v == '"');
- simd8<bool> is_backslash = (v == '\\');
- simd8<bool> is_control = (v < 32);
- return {
- static_cast<uint64_t>((is_backslash | is_quote | is_control).to_bitmask())
- };
-}
-
} // unnamed namespace
} // namespace lsx
} // namespace simdjson
@@ -66973,6 +65176,10 @@ namespace builder {
class string_builder {
public:
simdjson_inline string_builder(size_t initial_capacity = DEFAULT_INITIAL_CAPACITY);
+ string_builder(const string_builder &) = delete;
+ string_builder &operator=(const string_builder &) = delete;
+ string_builder(string_builder &&) noexcept = default;
+ string_builder &operator=(string_builder &&) noexcept = default;
static constexpr size_t DEFAULT_INITIAL_CAPACITY = 1024;
@@ -66986,6 +65193,18 @@ public:
typename = typename std::enable_if<std::is_arithmetic<number_type>::value>::type>
simdjson_inline void append(number_type v) noexcept;
+#ifdef __SIZEOF_INT128__
+ /**
+ * Integers wider than 64 bits are not supported. Without these overloads, a
+ * 128-bit integer would either be truncated to 64 bits by append(number_type)
+ * (when the standard library counts it as arithmetic, as libc++ does) or be
+ * converted to char by append(char) (when it does not, as libstdc++ does in
+ * strict ISO mode).
+ */
+ void append(__int128_t v) noexcept = delete;
+ void append(__uint128_t v) noexcept = delete;
+#endif
+
/**
* Append character c.
*/
@@ -67164,6 +65383,20 @@ requires (!std::is_convertible<R, std::string_view>::value && !concepts::optiona
*/
simdjson_inline simdjson_result<const char *> c_str() noexcept;
+ /**
+ * Moves the written JSON buffer into out, replacing its content, without
+ * copying it. When the content fills at most half of the buffer, it is
+ * copied instead, so that a small result does not keep the whole buffer
+ * allocated. The builder is left empty: it keeps its buffer when the
+ * content was copied, and gives up its memory when the buffer was moved.
+ * Returns an error, and leaves out and the builder unchanged, if memory
+ * allocation failed.
+ *
+ * The result may not be valid UTF-8 if some of your content was not valid UTF-8.
+ * Use validate_unicode() to check the content.
+ */
+ simdjson_warn_unused inline error_code move_to(std::string &out) noexcept;
+
/**
* Return true if the content is valid UTF-8.
*/
@@ -67183,7 +65416,7 @@ requires (!std::is_convertible<R, std::string_view>::value && !concepts::optiona
// forces those reloads when accessed via members of *this). User
// code should NOT call these directly.
// ============================================================
- simdjson_inline char *unsafe_data() noexcept { return buffer.get(); }
+ simdjson_inline char *unsafe_data() noexcept { return buffer_data(); }
simdjson_inline size_t unsafe_position() const noexcept { return position; }
simdjson_inline size_t unsafe_capacity() const noexcept { return capacity; }
simdjson_inline void unsafe_set_position(size_t p) noexcept { position = p; }
@@ -67224,7 +65457,13 @@ private:
*/
simdjson_inline void set_valid(bool valid) noexcept;
- std::unique_ptr<char[]> buffer{};
+ simdjson_inline char *buffer_data() noexcept { return &buffer[0]; }
+ simdjson_inline const char *buffer_data() const noexcept { return buffer.data(); }
+
+ // We own capacity bytes: buffer.size() == capacity. Only the first position
+ // bytes hold JSON. A std::string lets move_to() hand the result over
+ // without a copy.
+ std::string buffer{};
size_t position{0};
size_t capacity{0};
bool is_valid{true};
@@ -67242,20 +65481,16 @@ template <class Z>
simdjson_warn_unused simdjson_result<std::string> to_json(const Z &z, size_t initial_capacity = simdjson::lsx::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
simdjson::lsx::builder::string_builder b(initial_capacity);
b.append(z);
- std::string_view s;
- auto e = b.view().get(s);
+ std::string s;
+ auto e = b.move_to(s);
if(e) { return e; }
- return std::string(s);
+ return s;
}
template <class Z>
simdjson_warn_unused error_code to_json(const Z &z, std::string &s, size_t initial_capacity = simdjson::lsx::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
simdjson::lsx::builder::string_builder b(initial_capacity);
b.append(z);
- std::string_view sv;
- auto e = b.view().get(sv);
- if(e) { return e; }
- s.assign(sv.data(), sv.size());
- return simdjson::SUCCESS;
+ return b.move_to(s);
}
#endif
@@ -67271,7 +65506,7 @@ simdjson_warn_unused error_code to_json(const Z &z, std::string &s, size_t initi
#ifndef SIMDJSON_GENERIC_BUILDER_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
-/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_STRING_BUILDER_H */
+/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_BUILDER_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/builder/json_string_builder.h" */
/* amalgamation skipped (editor-only): #include "simdjson/concepts.h" */
/* amalgamation skipped (editor-only): #include "simdjson/annotations.h" */
@@ -67297,7 +65532,7 @@ namespace builder {
// included at the point this header is parsed; without these forwards,
// name lookup falls back to the wrong outer namespace).
namespace internal {
-simdjson_really_inline char *write_uint_jeaiii(char *p, uint64_t v) noexcept;
+using simdjson::internal::write_uint_jeaiii;
simdjson_inline char *write_double(char *p, double v) noexcept;
} // namespace internal
simdjson_really_inline size_t write_string_escaped(const std::string_view input, char *out);
@@ -67430,6 +65665,51 @@ consteval bool is_serialized_member(std::meta::info dm) {
&& !simdjson::detail::has_annotation(dm, ^^simdjson::detail::skip_serializing_tag);
}
+template <class Target, class T, class... Visiting>
+consteval bool members_reach();
+
+// Whether a value of type T may hold a value of type Target, directly or
+// through optionals, smart pointers, containers and members. Visiting lists
+// the structures being searched, so that the search terminates.
+template <class Target, class T, class... Visiting>
+consteval bool reaches() {
+ if constexpr (std::is_same_v<T, Target>) {
+ return true;
+ } else if constexpr ((std::is_same_v<T, Visiting> || ...) || require_custom_serialization<T> ||
+ std::is_same_v<T, std::string> || std::is_same_v<T, std::string_view>) {
+ return false;
+ } else if constexpr (concepts::optional_type<T> || concepts::smart_pointer<T>) {
+ return reaches<Target, std::remove_cvref_t<decltype(*std::declval<const T &>())>, Visiting...>();
+ } else if constexpr (concepts::string_view_keyed_map<T>) {
+ return reaches<Target, std::remove_cvref_t<typename T::mapped_type>, Visiting...>();
+ } else if constexpr (concepts::container_but_not_string<T> || concepts::appendable_containers<T>) {
+ return reaches<Target, std::remove_cvref_t<std::ranges::range_value_t<T>>, Visiting...>();
+ } else if constexpr (std::is_class_v<T>) {
+ return members_reach<Target, T, Visiting...>();
+ } else {
+ return false;
+ }
+}
+
+template <class Target, class T, class... Visiting>
+consteval bool members_reach() {
+ bool found = false;
+ template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) {
+ if constexpr (is_serialized_member(dm)) {
+ found = found || reaches<Target, std::remove_cvref_t<decltype(std::declval<const T &>().[:dm:])>, T, Visiting...>();
+ }
+ };
+ return found;
+}
+
+// Whether the structure T may contain itself, as in
+// struct Rule { std::unique_ptr<Rule> subrule; }. Serializing such a type
+// recurses at runtime, which simdjson_really_inline functions cannot do.
+template <class T>
+consteval bool is_recursive() {
+ return members_reach<T, T>();
+}
+
// False when the member has a skip_serializing_if<pred> annotation and
// pred(value) is true.
template <auto dm, typename V>
@@ -67613,6 +65893,8 @@ simdjson_really_inline constexpr void atom(W &w, const T &m) {
template<class W, typename number_type,
typename = typename std::enable_if<std::is_arithmetic<number_type>::value && !std::is_same_v<number_type, char>>::type>
simdjson_really_inline constexpr void atom(W &w, const number_type t) {
+ static_assert(sizeof(number_type) <= sizeof(uint64_t),
+ "Integers wider than 64 bits are not supported");
// Booleans / floats: defer to string_builder (rare path; keeps writer hot
// path free of float-formatter machinery). For integers, write directly
// via jeaiii using local pos.
@@ -67651,6 +65933,31 @@ simdjson_really_inline constexpr void atom(W &w, const number_type t) {
}
}
+namespace annotation_detail {
+
+template <class W, class T>
+simdjson_really_inline void atom_object(W &w, const T &t) {
+ if constexpr (simdjson::detail::has_annotation(^^T, ^^simdjson::detail::transparent_tag)) {
+ // A transparent structure is serialized as its single member.
+ constexpr auto dm = simdjson::detail::transparent_member(^^T);
+ atom_member<dm>(w, t.[:dm:]);
+ } else {
+ bool first = true;
+ if (!w.ensure(1)) { return; }
+ w.ptr[w.pos++] = '{';
+ atom_fields(w, t, first);
+ if (!w.ensure(1)) { return; }
+ w.ptr[w.pos++] = '}';
+ }
+}
+
+template <class W, class T>
+void atom_object_outlined(W &w, const T &t) {
+ atom_object(w, t);
+}
+
+} // namespace annotation_detail
+
template <class W, class T>
requires(std::is_class_v<T> && !concepts::container_but_not_string<T> &&
!concepts::string_view_keyed_map<T> &&
@@ -67662,17 +65969,11 @@ template <class W, class T>
!std::is_same_v<T, const char*> &&
!std::is_same_v<T, char> && !require_custom_serialization<T>)
simdjson_really_inline constexpr void atom(W &w, const T &t) {
- if constexpr (simdjson::detail::has_annotation(^^T, ^^simdjson::detail::transparent_tag)) {
- // A transparent structure is serialized as its single member.
- constexpr auto dm = simdjson::detail::transparent_member(^^T);
- annotation_detail::atom_member<dm>(w, t.[:dm:]);
+ if constexpr (annotation_detail::is_recursive<T>()) {
+ // Break the cycle of simdjson_really_inline calls.
+ annotation_detail::atom_object_outlined(w, t);
} else {
- bool first = true;
- if (!w.ensure(1)) { return; }
- w.ptr[w.pos++] = '{';
- annotation_detail::atom_fields(w, t, first);
- if (!w.ensure(1)) { return; }
- w.ptr[w.pos++] = '}';
+ annotation_detail::atom_object(w, t);
}
}
@@ -67772,25 +66073,30 @@ namespace bound_detail {
// Whether size_bound covers everything that atom() writes for T: not when a
// member is serialized by a with<Adapter> serializer, which writes an unknown
// amount through the string_builder.
-template <class T>
+// Visiting lists the structures being checked, so that a recursive type
+// (struct Node { std::unique_ptr<Node> next; }) does not recurse forever: a
+// type already being visited adds nothing, its members get checked once.
+template <class T, class... Visiting>
consteval bool is_bounded() {
- if constexpr (require_custom_serialization<T>) {
+ if constexpr ((std::is_same_v<T, Visiting> || ...)) {
+ return true;
+ } else if constexpr (require_custom_serialization<T>) {
return false;
} else if constexpr (std::is_same_v<T, std::string> || std::is_same_v<T, std::string_view> ||
std::is_same_v<T, const char *> || std::is_arithmetic_v<T> || std::is_enum_v<T>) {
return true;
} else if constexpr (concepts::optional_type<T> || concepts::smart_pointer<T>) {
- return is_bounded<std::remove_cvref_t<decltype(*std::declval<const T &>())>>();
+ return is_bounded<std::remove_cvref_t<decltype(*std::declval<const T &>())>, Visiting...>();
} else if constexpr (concepts::string_view_keyed_map<T>) {
- return is_bounded<std::remove_cvref_t<typename T::mapped_type>>();
+ return is_bounded<std::remove_cvref_t<typename T::mapped_type>, Visiting...>();
} else if constexpr (concepts::container_but_not_string<T> || concepts::appendable_containers<T>) {
- return is_bounded<std::remove_cvref_t<std::ranges::range_value_t<T>>>();
+ return is_bounded<std::remove_cvref_t<std::ranges::range_value_t<T>>, Visiting...>();
} else {
bool bounded = true;
template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) {
if constexpr (annotation_detail::is_serialized_member(dm)) {
bounded = bounded && simdjson::detail::annotation_of_template(dm, ^^simdjson::detail::with_t) == std::meta::info{} &&
- is_bounded<std::remove_cvref_t<decltype(std::declval<const T &>().[:dm:])>>();
+ is_bounded<std::remove_cvref_t<decltype(std::declval<const T &>().[:dm:])>, T, Visiting...>();
}
};
return bounded;
@@ -67828,6 +66134,11 @@ simdjson_really_inline size_t fields_bound(const T &t) noexcept {
return bound;
}
+template <class T>
+size_t fields_bound_outlined(const T &t) noexcept {
+ return fields_bound(t);
+}
+
template <class T>
simdjson_really_inline size_t size_bound([[maybe_unused]] const T &t) noexcept {
if constexpr (std::is_same_v<T, char>) {
@@ -67872,6 +66183,9 @@ simdjson_really_inline size_t size_bound([[maybe_unused]] const T &t) noexcept {
} else if constexpr (simdjson::detail::has_annotation(^^T, ^^simdjson::detail::transparent_tag)) {
constexpr auto dm = simdjson::detail::transparent_member(^^T);
return size_bound(t.[:dm:]);
+ } else if constexpr (annotation_detail::is_recursive<T>()) {
+ // Break the cycle of simdjson_really_inline calls.
+ return 2 + fields_bound_outlined(t);
} else {
return 2 + fields_bound(t);
}
@@ -68006,10 +66320,7 @@ simdjson_warn_unused error_code to_json(const Z &z, std::string &s, size_t initi
} else {
string_builder b(initial_capacity);
append(b, z);
- std::string_view view;
- if(auto e = b.view().get(view); e) { return e; }
- s.assign(view);
- return SUCCESS;
+ return b.move_to(s);
}
}
@@ -68072,9 +66383,9 @@ template<constevalutil::fixed_string... FieldNames, typename T>
simdjson_warn_unused simdjson_result<std::string> extract_from(const T &obj, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) {
string_builder b(initial_capacity);
extract_from<FieldNames...>(b, obj);
- std::string_view s;
- if(auto e = b.view().get(s); e) { return e; }
- return std::string(s);
+ std::string s;
+ if(auto e = b.move_to(s); e) { return e; }
+ return s;
}
SIMDJSON_POP_DISABLE_WARNINGS
@@ -68096,16 +66407,16 @@ template<constevalutil::fixed_string... FieldNames, typename T>
simdjson_warn_unused simdjson_result<std::string> extract_from(const T &obj, size_t initial_capacity = lsx::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
lsx::builder::string_builder b(initial_capacity);
lsx::builder::extract_from<FieldNames...>(b, obj);
- std::string_view s;
- if(auto e = b.view().get(s); e) { return e; }
- return std::string(s);
+ std::string s;
+ if(auto e = b.move_to(s); e) { return e; }
+ return s;
}
} // namespace simdjson
#endif // SIMDJSON_STATIC_REFLECTION
-#endif
+#endif // SIMDJSON_GENERIC_BUILDER_H
/* end file simdjson/generic/builder/json_builder.h for lsx */
/* including simdjson/generic/builder/fractured_json_builder.h for lsx: #include "simdjson/generic/builder/fractured_json_builder.h" */
/* begin file simdjson/generic/builder/fractured_json_builder.h for lsx */
@@ -68244,6 +66555,7 @@ simdjson_warn_unused simdjson_result<std::string> extract_fractured_json(
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_STRING_BUILDER_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/builder/json_string_builder.h" */
+/* amalgamation skipped (editor-only): #include "simdjson/internal/jeaiii.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
/*
@@ -68356,6 +66668,12 @@ inline bool has_json_escapable_byte(uint64_t x) {
**/
+#if SIMDJSON_EXPERIMENTAL_HAS_SSE2 || SIMDJSON_EXPERIMENTAL_HAS_NEON
+#define SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE 1
+#endif
+
+// With block escaping (SSE2/NEON), write_string_escaped scans the input itself.
+#if !SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE
// Scalar fallback for finding next quotable character
SIMDJSON_CONSTEXPR_LAMBDA simdjson_inline size_t
find_next_json_quotable_character_scalar(const std::string_view view,
@@ -68371,82 +66689,7 @@ find_next_json_quotable_character_scalar(const std::string_view view,
// SIMD-accelerated position finding that directly locates the first quotable
// character, combining detection and position extraction in a single pass to
// minimize redundant work.
-#if SIMDJSON_EXPERIMENTAL_HAS_NEON
-simdjson_inline size_t
-find_next_json_quotable_character(const std::string_view view,
- size_t location) noexcept {
- const size_t len = view.size();
- const uint8_t *ptr =
- reinterpret_cast<const uint8_t *>(view.data()) + location;
- size_t remaining = len - location;
-
- // SIMD constants for characters requiring escape
- uint8x16_t v34 = vdupq_n_u8(34); // '"'
- uint8x16_t v92 = vdupq_n_u8(92); // '\\'
- uint8x16_t v32 = vdupq_n_u8(32); // control char threshold
-
- while (remaining >= 16) {
- uint8x16_t word = vld1q_u8(ptr);
-
- // Check for quotable characters: '"', '\\', or control chars (< 32)
- uint8x16_t needs_escape = vceqq_u8(word, v34);
- needs_escape = vorrq_u8(needs_escape, vceqq_u8(word, v92));
- needs_escape = vorrq_u8(needs_escape, vcltq_u8(word, v32));
-
- const uint8x8_t res = vshrn_n_u16(vreinterpretq_u16_u8(needs_escape), 4);
- const uint64_t mask = vget_lane_u64(vreinterpret_u64_u8(res), 0);
- if(mask != 0) {
- size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
- auto trailing_zero = trailing_zeroes(mask);
- return offset + (trailing_zero >> 2);
- }
- ptr += 16;
- remaining -= 16;
- }
-
- // Scalar fallback for remaining bytes
- size_t current = len - remaining;
- return find_next_json_quotable_character_scalar(view, current);
-}
-#elif SIMDJSON_EXPERIMENTAL_HAS_SSE2
-simdjson_inline size_t
-find_next_json_quotable_character(const std::string_view view,
- size_t location) noexcept {
- const size_t len = view.size();
- const uint8_t *ptr =
- reinterpret_cast<const uint8_t *>(view.data()) + location;
- size_t remaining = len - location;
-
- // SIMD constants
- __m128i v34 = _mm_set1_epi8(34); // '"'
- __m128i v92 = _mm_set1_epi8(92); // '\\'
- __m128i v31 = _mm_set1_epi8(31); // for control char detection
-
- while (remaining >= 16) {
- __m128i word = _mm_loadu_si128(reinterpret_cast<const __m128i *>(ptr));
-
- // Check for quotable characters
- __m128i needs_escape = _mm_cmpeq_epi8(word, v34);
- needs_escape = _mm_or_si128(needs_escape, _mm_cmpeq_epi8(word, v92));
- needs_escape = _mm_or_si128(
- needs_escape,
- _mm_cmpeq_epi8(_mm_subs_epu8(word, v31), _mm_setzero_si128()));
-
- int mask = _mm_movemask_epi8(needs_escape);
- if (mask != 0) {
- // Found quotable character - use trailing zero count to find position
- size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
- return offset + trailing_zeroes(mask);
- }
- ptr += 16;
- remaining -= 16;
- }
-
- // Scalar fallback for remaining bytes
- size_t current = len - remaining;
- return find_next_json_quotable_character_scalar(view, current);
-}
-#elif SIMDJSON_EXPERIMENTAL_HAS_LASX
+#if SIMDJSON_EXPERIMENTAL_HAS_LASX
simdjson_inline size_t
find_next_json_quotable_character(const std::string_view view,
size_t location) noexcept {
@@ -68619,6 +66862,7 @@ find_next_json_quotable_character(const std::string_view view,
return find_next_json_quotable_character_scalar(view, location);
}
#endif
+#endif // !SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE
SIMDJSON_CONSTEXPR_LAMBDA static std::string_view control_chars[] = {
"\\u0000", "\\u0001", "\\u0002", "\\u0003", "\\u0004", "\\u0005", "\\u0006",
@@ -68646,10 +66890,6 @@ SIMDJSON_CONSTEXPR_LAMBDA simdjson_inline void escape_json_char(char c, char *&o
}
}
-#if SIMDJSON_EXPERIMENTAL_HAS_SSE2 || SIMDJSON_EXPERIMENTAL_HAS_NEON
-#define SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE 1
-#endif
-
#if SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE
#if SIMDJSON_EXPERIMENTAL_HAS_SSE2
@@ -68930,10 +67170,21 @@ inline size_t write_string_escaped(const std::string_view input, char *out) {
#undef SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE
-simdjson_inline string_builder::string_builder(size_t initial_capacity)
- : buffer(new(std::nothrow) char[initial_capacity]), position(0),
- capacity(buffer.get() != nullptr ? initial_capacity : 0),
- is_valid(buffer.get() != nullptr) {}
+namespace internal {
+// Sets the size of s to n without initializing the new bytes when the
+// standard library allows it (C++23 resize_and_overwrite).
+simdjson_inline void resize_uninitialized(std::string &s, size_t n) {
+#if defined(__cpp_lib_string_resize_and_overwrite) && __cpp_lib_string_resize_and_overwrite >= 202110L
+ s.resize_and_overwrite(n, [](char *, size_t len) noexcept { return len; });
+#else
+ s.resize(n);
+#endif
+}
+} // namespace internal
+
+simdjson_inline string_builder::string_builder(size_t initial_capacity) {
+ grow_buffer(initial_capacity);
+}
simdjson_inline bool string_builder::capacity_check(size_t upcoming_bytes) {
// We use the convention that when is_valid is false, then the capacity and
@@ -68956,22 +67207,63 @@ inline void string_builder::grow_buffer(size_t desired_capacity) {
if (!is_valid) {
return;
}
- std::unique_ptr<char[]> new_buffer(new (std::nothrow) char[desired_capacity]);
- if (new_buffer.get() == nullptr) {
+ std::string new_buffer;
+ if (desired_capacity > new_buffer.max_size()) {
+ set_valid(false);
+ return;
+ }
+#if defined(__cpp_exceptions) || defined(_CPPUNWIND)
+ try {
+ internal::resize_uninitialized(new_buffer, desired_capacity);
+ } catch (...) { // std::bad_alloc
set_valid(false);
return;
}
- std::memcpy(new_buffer.get(), buffer.get(), position);
+#else
+ // Without exceptions, a failed allocation terminates the program.
+ internal::resize_uninitialized(new_buffer, desired_capacity);
+#endif
+ std::memcpy(&new_buffer[0], buffer_data(), position);
buffer.swap(new_buffer);
capacity = desired_capacity;
}
+// Not simdjson_inline: MSVC cannot force-inline a function with a try block (C4714).
+inline error_code string_builder::move_to(std::string &out) noexcept {
+ if (!is_valid) {
+ return simdjson::OUT_OF_CAPACITY;
+ }
+ if (position <= buffer.capacity() / 2) {
+ // Most of the buffer is unused: copy the bytes out, so that a small
+ // result does not keep the whole buffer allocated. The builder keeps its
+ // buffer for what it writes next.
+#if defined(__cpp_exceptions) || defined(_CPPUNWIND)
+ try {
+ out.assign(buffer.data(), position);
+ } catch (...) { // std::bad_alloc
+ return simdjson::MEMALLOC;
+ }
+#else
+ // Without exceptions, a failed allocation terminates the program.
+ out.assign(buffer.data(), position);
+#endif
+ position = 0;
+ return simdjson::SUCCESS;
+ }
+ buffer.resize(position); // shrinking never allocates
+ out = std::move(buffer);
+ std::string().swap(buffer);
+ position = 0;
+ capacity = 0;
+ return simdjson::SUCCESS;
+}
+
simdjson_inline void string_builder::set_valid(bool valid) noexcept {
if (!valid) {
is_valid = false;
capacity = 0;
position = 0;
- buffer.reset();
+ std::string().swap(buffer);
} else {
is_valid = true;
}
@@ -68983,7 +67275,7 @@ simdjson_inline size_t string_builder::size() const noexcept {
simdjson_inline void string_builder::append(char c) noexcept {
if (capacity_check(1)) {
- buffer.get()[position++] = c;
+ buffer_data()[position++] = c;
}
}
@@ -68991,7 +67283,7 @@ simdjson_inline void string_builder::append_null() noexcept {
constexpr char null_literal[] = "null";
constexpr size_t null_len = sizeof(null_literal) - 1;
if (capacity_check(null_len)) {
- std::memcpy(buffer.get() + position, null_literal, null_len);
+ std::memcpy(buffer_data() + position, null_literal, null_len);
position += null_len;
}
}
@@ -69001,124 +67293,14 @@ simdjson_inline void string_builder::clear() noexcept {
// if it was invalid, we should try to repair it
if (!is_valid) {
capacity = 0;
- buffer.reset();
+ std::string().swap(buffer);
is_valid = true;
}
}
namespace internal {
-// Integer to decimal: James Edward Anhalt III's algorithm
-static const char jeaiii_dd[201] =
- "00010203040506070809101112131415161718192021222324252627282930313233343536373839"
- "40414243444546474849505152535455565758596061626364656667686970717273747576777879"
- "8081828384858687888990919293949596979899";
-static const char jeaiii_fd[201] =
- "0\0" "1\0" "2\0" "3\0" "4\0" "5\0" "6\0" "7\0" "8\0" "9\0"
- "10111213141516171819202122232425262728293031323334353637383940414243444546474849"
- "50515253545556575859606162636465666768697071727374757677787980818283848586878889"
- "90919293949596979899";
-
-simdjson_really_inline void jeaiii_write_dd(char *p, uint64_t k) noexcept {
- std::memcpy(p, &jeaiii_dd[2 * k], 2);
-}
-simdjson_really_inline void jeaiii_write_fd(char *p, uint64_t k) noexcept {
- std::memcpy(p, &jeaiii_fd[2 * k], 2);
-}
-
-// Caller guarantees n < 10^8. Writes 1 to 8 digits.
-simdjson_really_inline char *jeaiii_lt1e8(char *b, uint32_t n) noexcept {
- constexpr uint64_t mask24 = (uint64_t(1) << 24) - 1;
- constexpr uint64_t mask32 = (uint64_t(1) << 32) - 1;
- if (n < 100) {
- jeaiii_write_fd(b, n);
- return n < 10 ? b + 1 : b + 2;
- }
- if (n < 1000000) {
- if (n < 10000) {
- const uint32_t f0 = uint32_t(10 * (1 << 24) / 1e3 + 1) * n;
- jeaiii_write_fd(b, f0 >> 24);
- b -= n < 1000;
- const uint32_t f2 = uint32_t(f0 & mask24) * 100;
- jeaiii_write_dd(b + 2, f2 >> 24);
- return b + 4;
- }
- const uint64_t f0 = uint64_t(10 * (1ull << 32) / 1e5 + 1) * n;
- jeaiii_write_fd(b, f0 >> 32);
- b -= n < 100000;
- const uint64_t f2 = (f0 & mask32) * 100;
- jeaiii_write_dd(b + 2, f2 >> 32);
- const uint64_t f4 = (f2 & mask32) * 100;
- jeaiii_write_dd(b + 4, f4 >> 32);
- return b + 6;
- }
- const uint64_t f0 = uint64_t(10 * (1ull << 48) / 1e7 + 1) * n >> 16;
- jeaiii_write_fd(b, f0 >> 32);
- b -= n < 10000000;
- const uint64_t f2 = (f0 & mask32) * 100;
- jeaiii_write_dd(b + 2, f2 >> 32);
- const uint64_t f4 = (f2 & mask32) * 100;
- jeaiii_write_dd(b + 4, f4 >> 32);
- const uint64_t f6 = (f4 & mask32) * 100;
- jeaiii_write_dd(b + 6, f6 >> 32);
- return b + 8;
-}
-
-// Caller guarantees z < 10^8. Always writes exactly 8 digits.
-simdjson_really_inline char *jeaiii_8_digits(char *b, uint32_t z) noexcept {
- constexpr uint64_t mask32 = (uint64_t(1) << 32) - 1;
- const uint64_t f0 = (uint64_t((1ull << 48) / 1e6 + 1) * z >> 16) + 1;
- jeaiii_write_dd(b, f0 >> 32);
- const uint64_t f2 = (f0 & mask32) * 100;
- jeaiii_write_dd(b + 2, f2 >> 32);
- const uint64_t f4 = (f2 & mask32) * 100;
- jeaiii_write_dd(b + 4, f4 >> 32);
- const uint64_t f6 = (f4 & mask32) * 100;
- jeaiii_write_dd(b + 6, f6 >> 32);
- return b + 8;
-}
-
-// Caller guarantees 10^8 <= n < 2^32. Writes 9 or 10 digits.
-simdjson_really_inline char *jeaiii_9_or_10(char *b, uint64_t n) noexcept {
- constexpr uint64_t mask57 = (uint64_t(1) << 57) - 1;
- const uint64_t f0 = uint64_t(10 * (1ull << 57) / 1e9 + 1) * n;
- jeaiii_write_fd(b, f0 >> 57);
- b -= n < 1000000000;
- const uint64_t f2 = (f0 & mask57) * 100;
- jeaiii_write_dd(b + 2, f2 >> 57);
- const uint64_t f4 = (f2 & mask57) * 100;
- jeaiii_write_dd(b + 4, f4 >> 57);
- const uint64_t f6 = (f4 & mask57) * 100;
- jeaiii_write_dd(b + 6, f6 >> 57);
- const uint64_t f8 = (f6 & mask57) * 100;
- jeaiii_write_dd(b + 8, f8 >> 57);
- return b + 10;
-}
-
-simdjson_really_inline char *write_uint_jeaiii(char *b, uint64_t n) noexcept {
- if (n < 100000000) {
- return jeaiii_lt1e8(b, uint32_t(n));
- }
- if (n < (uint64_t(1) << 32)) {
- return jeaiii_9_or_10(b, n);
- }
- // At least 10 digits: the low 8 digits, and 2 to 12 digits above them.
- const uint32_t z = uint32_t(n % 100000000);
- uint64_t u = n / 100000000;
- if (u < 100000000) {
- // u has 2 to 8 digits (if u < 10, n would be below 2^32).
- b = jeaiii_lt1e8(b, uint32_t(u));
- } else if (u < (uint64_t(1) << 32)) {
- b = jeaiii_9_or_10(b, u);
- } else {
- // u has 11 or 12 digits: split off 8 more.
- const uint32_t y = uint32_t(u % 100000000);
- u /= 100000000;
- b = jeaiii_lt1e8(b, uint32_t(u)); // 3 or 4 digits
- b = jeaiii_8_digits(b, y);
- }
- return jeaiii_8_digits(b, z);
-}
+using simdjson::internal::write_uint_jeaiii;
// Writes v at p, which must have to_chars_buffer_size bytes available, and
// returns the end of what was written.
@@ -69146,20 +67328,22 @@ simdjson_inline void string_builder::append(number_type v) noexcept {
std::is_integral<number_type>::value ||
std::is_floating_point<number_type>::value,
"Unsupported number type");
+ static_assert(sizeof(number_type) <= sizeof(uint64_t),
+ "Integers wider than 64 bits are not supported");
// If C++17 is available, we can 'if constexpr' here.
SIMDJSON_IF_CONSTEXPR(std::is_same<number_type, bool>::value) {
if (v) {
constexpr char true_literal[] = "true";
constexpr size_t true_len = sizeof(true_literal) - 1;
if (capacity_check(true_len)) {
- std::memcpy(buffer.get() + position, true_literal, true_len);
+ std::memcpy(buffer_data() + position, true_literal, true_len);
position += true_len;
}
} else {
constexpr char false_literal[] = "false";
constexpr size_t false_len = sizeof(false_literal) - 1;
if (capacity_check(false_len)) {
- std::memcpy(buffer.get() + position, false_literal, false_len);
+ std::memcpy(buffer_data() + position, false_literal, false_len);
position += false_len;
}
}
@@ -69169,9 +67353,9 @@ simdjson_inline void string_builder::append(number_type v) noexcept {
if (capacity_check(max_number_size)) {
using unsigned_type = typename std::make_unsigned<number_type>::type;
char* end = internal::write_uint_jeaiii(
- buffer.get() + position,
+ buffer_data() + position,
static_cast<uint64_t>(static_cast<unsigned_type>(v)));
- position = end - buffer.get();
+ position = end - buffer_data();
}
}
else SIMDJSON_IF_CONSTEXPR(std::is_integral<number_type>::value) {
@@ -69185,11 +67369,11 @@ simdjson_inline void string_builder::append(number_type v) noexcept {
? unsigned_type(0) - static_cast<unsigned_type>(v)
: static_cast<unsigned_type>(v);
// Branchless: always write '-', advance only if negative.
- buffer.get()[position] = '-';
+ buffer_data()[position] = '-';
position += negative;
char* end = internal::write_uint_jeaiii(
- buffer.get() + position, static_cast<uint64_t>(pv));
- position = end - buffer.get();
+ buffer_data() + position, static_cast<uint64_t>(pv));
+ position = end - buffer_data();
}
}
else SIMDJSON_IF_CONSTEXPR(std::is_floating_point<number_type>::value) {
@@ -69205,16 +67389,16 @@ simdjson_inline void string_builder::append(number_type v) noexcept {
constexpr char nan_literal[] = "NaN";
constexpr size_t nan_len = sizeof(nan_literal) - 1;
- std::memcpy(buffer.get() + position, nan_literal, nan_len);
+ std::memcpy(buffer_data() + position, nan_literal, nan_len);
position += nan_len;
} else {
constexpr char inf_literal[] = "Infinity";
constexpr size_t inf_len = sizeof(inf_literal) - 1;
if (v < 0) {
- buffer.get()[position] = '-';
+ buffer_data()[position] = '-';
++position;
}
- std::memcpy(buffer.get() + position, inf_literal, inf_len);
+ std::memcpy(buffer_data() + position, inf_literal, inf_len);
position += inf_len;
}
return;
@@ -69222,9 +67406,9 @@ simdjson_inline void string_builder::append(number_type v) noexcept {
#endif
// We could specialize for float.
- char *end = simdjson::internal::to_chars(buffer.get() + position, nullptr,
+ char *end = simdjson::internal::to_chars(buffer_data() + position, nullptr,
double(v));
- position = end - buffer.get();
+ position = end - buffer_data();
}
}
}
@@ -69238,7 +67422,7 @@ string_builder::escape_and_append(std::string_view input) noexcept {
return;
}
if (capacity_check(6 * input.size())) {
- position += write_string_escaped(input, buffer.get() + position);
+ position += write_string_escaped(input, buffer_data() + position);
}
}
@@ -69251,9 +67435,9 @@ string_builder::escape_and_append_with_quotes(std::string_view input) noexcept {
return;
}
if (capacity_check(2 + 6 * input.size())) {
- buffer.get()[position++] = '"';
- position += write_string_escaped(input, buffer.get() + position);
- buffer.get()[position++] = '"';
+ buffer_data()[position++] = '"';
+ position += write_string_escaped(input, buffer_data() + position);
+ buffer_data()[position++] = '"';
}
}
@@ -69261,10 +67445,10 @@ simdjson_inline void
string_builder::escape_and_append_with_quotes(char input) noexcept {
// escaping might turn a control character into \x00xx so 6 characters.
if (capacity_check(2 + 6 * 1)) {
- buffer.get()[position++] = '"';
+ buffer_data()[position++] = '"';
std::string_view cinput(&input, 1);
- position += write_string_escaped(cinput, buffer.get() + position);
- buffer.get()[position++] = '"';
+ position += write_string_escaped(cinput, buffer_data() + position);
+ buffer_data()[position++] = '"';
}
}
@@ -69294,7 +67478,7 @@ simdjson_inline void string_builder::append_raw(const char *c) noexcept {
simdjson_inline void
string_builder::append_raw(std::string_view input) noexcept {
if (capacity_check(input.size())) {
- std::memcpy(buffer.get() + position, input.data(), input.size());
+ std::memcpy(buffer_data() + position, input.data(), input.size());
position += input.size();
}
}
@@ -69302,7 +67486,7 @@ string_builder::append_raw(std::string_view input) noexcept {
simdjson_inline void string_builder::append_raw(const char *str,
size_t len) noexcept {
if (capacity_check(len)) {
- std::memcpy(buffer.get() + position, str, len);
+ std::memcpy(buffer_data() + position, str, len);
position += len;
}
}
@@ -69310,7 +67494,7 @@ simdjson_inline void string_builder::append_raw(const char *str,
template <size_t N>
simdjson_inline void string_builder::append_raw_n(const char *str) noexcept {
if (capacity_check(N)) {
- std::memcpy(buffer.get() + position, str, N);
+ std::memcpy(buffer_data() + position, str, N);
position += N;
}
}
@@ -69320,7 +67504,7 @@ template <typename Text>
simdjson_inline void string_builder::append_static_string() noexcept {
constexpr auto& padded = constevalutil::static_string<Text>;
if (capacity_check(padded.size())) {
- std::memcpy(buffer.get() + position, padded.data(), padded.size());
+ std::memcpy(buffer_data() + position, padded.data(), padded.size());
position += constevalutil::static_string_size<Text>();
}
}
@@ -69414,54 +67598,54 @@ string_builder::view() const noexcept {
if (!is_valid) {
return simdjson::OUT_OF_CAPACITY;
}
- return std::string_view(buffer.get(), position);
+ return std::string_view(buffer_data(), position);
}
simdjson_inline simdjson_result<const char *> string_builder::c_str() noexcept {
if (capacity_check(1)) {
- buffer.get()[position] = '\0';
- return buffer.get();
+ buffer_data()[position] = '\0';
+ return buffer_data();
}
return simdjson::OUT_OF_CAPACITY;
}
simdjson_inline bool string_builder::validate_unicode() const noexcept {
- return simdjson::validate_utf8(buffer.get(), position);
+ return simdjson::validate_utf8(buffer_data(), position);
}
simdjson_inline void string_builder::start_object() noexcept {
if (capacity_check(1)) {
- buffer.get()[position++] = '{';
+ buffer_data()[position++] = '{';
}
}
simdjson_inline void string_builder::end_object() noexcept {
if (capacity_check(1)) {
- buffer.get()[position++] = '}';
+ buffer_data()[position++] = '}';
}
}
simdjson_inline void string_builder::start_array() noexcept {
if (capacity_check(1)) {
- buffer.get()[position++] = '[';
+ buffer_data()[position++] = '[';
}
}
simdjson_inline void string_builder::end_array() noexcept {
if (capacity_check(1)) {
- buffer.get()[position++] = ']';
+ buffer_data()[position++] = ']';
}
}
simdjson_inline void string_builder::append_comma() noexcept {
if (capacity_check(1)) {
- buffer.get()[position++] = ',';
+ buffer_data()[position++] = ',';
}
}
simdjson_inline void string_builder::append_colon() noexcept {
if (capacity_check(1)) {
- buffer.get()[position++] = ':';
+ buffer_data()[position++] = ':';
}
}
@@ -70220,30 +68404,6 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin
}
-struct escaping {
- static constexpr uint32_t BYTES_PROCESSED = 16;
- simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
-
- simdjson_inline bool has_escape() { return escape_bits != 0; }
- simdjson_inline int escape_index() { return trailing_zeroes(escape_bits); }
-
- uint64_t escape_bits;
-}; // struct escaping
-
-
-
-simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
- static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes");
- simd8<uint8_t> v(src);
- v.store(dst);
- simd8<bool> is_quote = (v == '"');
- simd8<bool> is_backslash = (v == '\\');
- simd8<bool> is_control = (v < 32);
- return {
- static_cast<uint64_t>((is_backslash | is_quote | is_control).to_bitmask())
- };
-}
-
} // unnamed namespace
} // namespace lasx
} // namespace simdjson
@@ -70316,6 +68476,10 @@ namespace builder {
class string_builder {
public:
simdjson_inline string_builder(size_t initial_capacity = DEFAULT_INITIAL_CAPACITY);
+ string_builder(const string_builder &) = delete;
+ string_builder &operator=(const string_builder &) = delete;
+ string_builder(string_builder &&) noexcept = default;
+ string_builder &operator=(string_builder &&) noexcept = default;
static constexpr size_t DEFAULT_INITIAL_CAPACITY = 1024;
@@ -70329,6 +68493,18 @@ public:
typename = typename std::enable_if<std::is_arithmetic<number_type>::value>::type>
simdjson_inline void append(number_type v) noexcept;
+#ifdef __SIZEOF_INT128__
+ /**
+ * Integers wider than 64 bits are not supported. Without these overloads, a
+ * 128-bit integer would either be truncated to 64 bits by append(number_type)
+ * (when the standard library counts it as arithmetic, as libc++ does) or be
+ * converted to char by append(char) (when it does not, as libstdc++ does in
+ * strict ISO mode).
+ */
+ void append(__int128_t v) noexcept = delete;
+ void append(__uint128_t v) noexcept = delete;
+#endif
+
/**
* Append character c.
*/
@@ -70507,6 +68683,20 @@ requires (!std::is_convertible<R, std::string_view>::value && !concepts::optiona
*/
simdjson_inline simdjson_result<const char *> c_str() noexcept;
+ /**
+ * Moves the written JSON buffer into out, replacing its content, without
+ * copying it. When the content fills at most half of the buffer, it is
+ * copied instead, so that a small result does not keep the whole buffer
+ * allocated. The builder is left empty: it keeps its buffer when the
+ * content was copied, and gives up its memory when the buffer was moved.
+ * Returns an error, and leaves out and the builder unchanged, if memory
+ * allocation failed.
+ *
+ * The result may not be valid UTF-8 if some of your content was not valid UTF-8.
+ * Use validate_unicode() to check the content.
+ */
+ simdjson_warn_unused inline error_code move_to(std::string &out) noexcept;
+
/**
* Return true if the content is valid UTF-8.
*/
@@ -70526,7 +68716,7 @@ requires (!std::is_convertible<R, std::string_view>::value && !concepts::optiona
// forces those reloads when accessed via members of *this). User
// code should NOT call these directly.
// ============================================================
- simdjson_inline char *unsafe_data() noexcept { return buffer.get(); }
+ simdjson_inline char *unsafe_data() noexcept { return buffer_data(); }
simdjson_inline size_t unsafe_position() const noexcept { return position; }
simdjson_inline size_t unsafe_capacity() const noexcept { return capacity; }
simdjson_inline void unsafe_set_position(size_t p) noexcept { position = p; }
@@ -70567,7 +68757,13 @@ private:
*/
simdjson_inline void set_valid(bool valid) noexcept;
- std::unique_ptr<char[]> buffer{};
+ simdjson_inline char *buffer_data() noexcept { return &buffer[0]; }
+ simdjson_inline const char *buffer_data() const noexcept { return buffer.data(); }
+
+ // We own capacity bytes: buffer.size() == capacity. Only the first position
+ // bytes hold JSON. A std::string lets move_to() hand the result over
+ // without a copy.
+ std::string buffer{};
size_t position{0};
size_t capacity{0};
bool is_valid{true};
@@ -70585,20 +68781,16 @@ template <class Z>
simdjson_warn_unused simdjson_result<std::string> to_json(const Z &z, size_t initial_capacity = simdjson::lasx::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
simdjson::lasx::builder::string_builder b(initial_capacity);
b.append(z);
- std::string_view s;
- auto e = b.view().get(s);
+ std::string s;
+ auto e = b.move_to(s);
if(e) { return e; }
- return std::string(s);
+ return s;
}
template <class Z>
simdjson_warn_unused error_code to_json(const Z &z, std::string &s, size_t initial_capacity = simdjson::lasx::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
simdjson::lasx::builder::string_builder b(initial_capacity);
b.append(z);
- std::string_view sv;
- auto e = b.view().get(sv);
- if(e) { return e; }
- s.assign(sv.data(), sv.size());
- return simdjson::SUCCESS;
+ return b.move_to(s);
}
#endif
@@ -70614,7 +68806,7 @@ simdjson_warn_unused error_code to_json(const Z &z, std::string &s, size_t initi
#ifndef SIMDJSON_GENERIC_BUILDER_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
-/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_STRING_BUILDER_H */
+/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_BUILDER_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/builder/json_string_builder.h" */
/* amalgamation skipped (editor-only): #include "simdjson/concepts.h" */
/* amalgamation skipped (editor-only): #include "simdjson/annotations.h" */
@@ -70640,7 +68832,7 @@ namespace builder {
// included at the point this header is parsed; without these forwards,
// name lookup falls back to the wrong outer namespace).
namespace internal {
-simdjson_really_inline char *write_uint_jeaiii(char *p, uint64_t v) noexcept;
+using simdjson::internal::write_uint_jeaiii;
simdjson_inline char *write_double(char *p, double v) noexcept;
} // namespace internal
simdjson_really_inline size_t write_string_escaped(const std::string_view input, char *out);
@@ -70773,6 +68965,51 @@ consteval bool is_serialized_member(std::meta::info dm) {
&& !simdjson::detail::has_annotation(dm, ^^simdjson::detail::skip_serializing_tag);
}
+template <class Target, class T, class... Visiting>
+consteval bool members_reach();
+
+// Whether a value of type T may hold a value of type Target, directly or
+// through optionals, smart pointers, containers and members. Visiting lists
+// the structures being searched, so that the search terminates.
+template <class Target, class T, class... Visiting>
+consteval bool reaches() {
+ if constexpr (std::is_same_v<T, Target>) {
+ return true;
+ } else if constexpr ((std::is_same_v<T, Visiting> || ...) || require_custom_serialization<T> ||
+ std::is_same_v<T, std::string> || std::is_same_v<T, std::string_view>) {
+ return false;
+ } else if constexpr (concepts::optional_type<T> || concepts::smart_pointer<T>) {
+ return reaches<Target, std::remove_cvref_t<decltype(*std::declval<const T &>())>, Visiting...>();
+ } else if constexpr (concepts::string_view_keyed_map<T>) {
+ return reaches<Target, std::remove_cvref_t<typename T::mapped_type>, Visiting...>();
+ } else if constexpr (concepts::container_but_not_string<T> || concepts::appendable_containers<T>) {
+ return reaches<Target, std::remove_cvref_t<std::ranges::range_value_t<T>>, Visiting...>();
+ } else if constexpr (std::is_class_v<T>) {
+ return members_reach<Target, T, Visiting...>();
+ } else {
+ return false;
+ }
+}
+
+template <class Target, class T, class... Visiting>
+consteval bool members_reach() {
+ bool found = false;
+ template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) {
+ if constexpr (is_serialized_member(dm)) {
+ found = found || reaches<Target, std::remove_cvref_t<decltype(std::declval<const T &>().[:dm:])>, T, Visiting...>();
+ }
+ };
+ return found;
+}
+
+// Whether the structure T may contain itself, as in
+// struct Rule { std::unique_ptr<Rule> subrule; }. Serializing such a type
+// recurses at runtime, which simdjson_really_inline functions cannot do.
+template <class T>
+consteval bool is_recursive() {
+ return members_reach<T, T>();
+}
+
// False when the member has a skip_serializing_if<pred> annotation and
// pred(value) is true.
template <auto dm, typename V>
@@ -70956,6 +69193,8 @@ simdjson_really_inline constexpr void atom(W &w, const T &m) {
template<class W, typename number_type,
typename = typename std::enable_if<std::is_arithmetic<number_type>::value && !std::is_same_v<number_type, char>>::type>
simdjson_really_inline constexpr void atom(W &w, const number_type t) {
+ static_assert(sizeof(number_type) <= sizeof(uint64_t),
+ "Integers wider than 64 bits are not supported");
// Booleans / floats: defer to string_builder (rare path; keeps writer hot
// path free of float-formatter machinery). For integers, write directly
// via jeaiii using local pos.
@@ -70994,6 +69233,31 @@ simdjson_really_inline constexpr void atom(W &w, const number_type t) {
}
}
+namespace annotation_detail {
+
+template <class W, class T>
+simdjson_really_inline void atom_object(W &w, const T &t) {
+ if constexpr (simdjson::detail::has_annotation(^^T, ^^simdjson::detail::transparent_tag)) {
+ // A transparent structure is serialized as its single member.
+ constexpr auto dm = simdjson::detail::transparent_member(^^T);
+ atom_member<dm>(w, t.[:dm:]);
+ } else {
+ bool first = true;
+ if (!w.ensure(1)) { return; }
+ w.ptr[w.pos++] = '{';
+ atom_fields(w, t, first);
+ if (!w.ensure(1)) { return; }
+ w.ptr[w.pos++] = '}';
+ }
+}
+
+template <class W, class T>
+void atom_object_outlined(W &w, const T &t) {
+ atom_object(w, t);
+}
+
+} // namespace annotation_detail
+
template <class W, class T>
requires(std::is_class_v<T> && !concepts::container_but_not_string<T> &&
!concepts::string_view_keyed_map<T> &&
@@ -71005,17 +69269,11 @@ template <class W, class T>
!std::is_same_v<T, const char*> &&
!std::is_same_v<T, char> && !require_custom_serialization<T>)
simdjson_really_inline constexpr void atom(W &w, const T &t) {
- if constexpr (simdjson::detail::has_annotation(^^T, ^^simdjson::detail::transparent_tag)) {
- // A transparent structure is serialized as its single member.
- constexpr auto dm = simdjson::detail::transparent_member(^^T);
- annotation_detail::atom_member<dm>(w, t.[:dm:]);
+ if constexpr (annotation_detail::is_recursive<T>()) {
+ // Break the cycle of simdjson_really_inline calls.
+ annotation_detail::atom_object_outlined(w, t);
} else {
- bool first = true;
- if (!w.ensure(1)) { return; }
- w.ptr[w.pos++] = '{';
- annotation_detail::atom_fields(w, t, first);
- if (!w.ensure(1)) { return; }
- w.ptr[w.pos++] = '}';
+ annotation_detail::atom_object(w, t);
}
}
@@ -71115,25 +69373,30 @@ namespace bound_detail {
// Whether size_bound covers everything that atom() writes for T: not when a
// member is serialized by a with<Adapter> serializer, which writes an unknown
// amount through the string_builder.
-template <class T>
+// Visiting lists the structures being checked, so that a recursive type
+// (struct Node { std::unique_ptr<Node> next; }) does not recurse forever: a
+// type already being visited adds nothing, its members get checked once.
+template <class T, class... Visiting>
consteval bool is_bounded() {
- if constexpr (require_custom_serialization<T>) {
+ if constexpr ((std::is_same_v<T, Visiting> || ...)) {
+ return true;
+ } else if constexpr (require_custom_serialization<T>) {
return false;
} else if constexpr (std::is_same_v<T, std::string> || std::is_same_v<T, std::string_view> ||
std::is_same_v<T, const char *> || std::is_arithmetic_v<T> || std::is_enum_v<T>) {
return true;
} else if constexpr (concepts::optional_type<T> || concepts::smart_pointer<T>) {
- return is_bounded<std::remove_cvref_t<decltype(*std::declval<const T &>())>>();
+ return is_bounded<std::remove_cvref_t<decltype(*std::declval<const T &>())>, Visiting...>();
} else if constexpr (concepts::string_view_keyed_map<T>) {
- return is_bounded<std::remove_cvref_t<typename T::mapped_type>>();
+ return is_bounded<std::remove_cvref_t<typename T::mapped_type>, Visiting...>();
} else if constexpr (concepts::container_but_not_string<T> || concepts::appendable_containers<T>) {
- return is_bounded<std::remove_cvref_t<std::ranges::range_value_t<T>>>();
+ return is_bounded<std::remove_cvref_t<std::ranges::range_value_t<T>>, Visiting...>();
} else {
bool bounded = true;
template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) {
if constexpr (annotation_detail::is_serialized_member(dm)) {
bounded = bounded && simdjson::detail::annotation_of_template(dm, ^^simdjson::detail::with_t) == std::meta::info{} &&
- is_bounded<std::remove_cvref_t<decltype(std::declval<const T &>().[:dm:])>>();
+ is_bounded<std::remove_cvref_t<decltype(std::declval<const T &>().[:dm:])>, T, Visiting...>();
}
};
return bounded;
@@ -71171,6 +69434,11 @@ simdjson_really_inline size_t fields_bound(const T &t) noexcept {
return bound;
}
+template <class T>
+size_t fields_bound_outlined(const T &t) noexcept {
+ return fields_bound(t);
+}
+
template <class T>
simdjson_really_inline size_t size_bound([[maybe_unused]] const T &t) noexcept {
if constexpr (std::is_same_v<T, char>) {
@@ -71215,6 +69483,9 @@ simdjson_really_inline size_t size_bound([[maybe_unused]] const T &t) noexcept {
} else if constexpr (simdjson::detail::has_annotation(^^T, ^^simdjson::detail::transparent_tag)) {
constexpr auto dm = simdjson::detail::transparent_member(^^T);
return size_bound(t.[:dm:]);
+ } else if constexpr (annotation_detail::is_recursive<T>()) {
+ // Break the cycle of simdjson_really_inline calls.
+ return 2 + fields_bound_outlined(t);
} else {
return 2 + fields_bound(t);
}
@@ -71349,10 +69620,7 @@ simdjson_warn_unused error_code to_json(const Z &z, std::string &s, size_t initi
} else {
string_builder b(initial_capacity);
append(b, z);
- std::string_view view;
- if(auto e = b.view().get(view); e) { return e; }
- s.assign(view);
- return SUCCESS;
+ return b.move_to(s);
}
}
@@ -71415,9 +69683,9 @@ template<constevalutil::fixed_string... FieldNames, typename T>
simdjson_warn_unused simdjson_result<std::string> extract_from(const T &obj, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) {
string_builder b(initial_capacity);
extract_from<FieldNames...>(b, obj);
- std::string_view s;
- if(auto e = b.view().get(s); e) { return e; }
- return std::string(s);
+ std::string s;
+ if(auto e = b.move_to(s); e) { return e; }
+ return s;
}
SIMDJSON_POP_DISABLE_WARNINGS
@@ -71439,16 +69707,16 @@ template<constevalutil::fixed_string... FieldNames, typename T>
simdjson_warn_unused simdjson_result<std::string> extract_from(const T &obj, size_t initial_capacity = lasx::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
lasx::builder::string_builder b(initial_capacity);
lasx::builder::extract_from<FieldNames...>(b, obj);
- std::string_view s;
- if(auto e = b.view().get(s); e) { return e; }
- return std::string(s);
+ std::string s;
+ if(auto e = b.move_to(s); e) { return e; }
+ return s;
}
} // namespace simdjson
#endif // SIMDJSON_STATIC_REFLECTION
-#endif
+#endif // SIMDJSON_GENERIC_BUILDER_H
/* end file simdjson/generic/builder/json_builder.h for lasx */
/* including simdjson/generic/builder/fractured_json_builder.h for lasx: #include "simdjson/generic/builder/fractured_json_builder.h" */
/* begin file simdjson/generic/builder/fractured_json_builder.h for lasx */
@@ -71587,6 +69855,7 @@ simdjson_warn_unused simdjson_result<std::string> extract_fractured_json(
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_STRING_BUILDER_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/builder/json_string_builder.h" */
+/* amalgamation skipped (editor-only): #include "simdjson/internal/jeaiii.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
/*
@@ -71699,6 +69968,12 @@ inline bool has_json_escapable_byte(uint64_t x) {
**/
+#if SIMDJSON_EXPERIMENTAL_HAS_SSE2 || SIMDJSON_EXPERIMENTAL_HAS_NEON
+#define SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE 1
+#endif
+
+// With block escaping (SSE2/NEON), write_string_escaped scans the input itself.
+#if !SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE
// Scalar fallback for finding next quotable character
SIMDJSON_CONSTEXPR_LAMBDA simdjson_inline size_t
find_next_json_quotable_character_scalar(const std::string_view view,
@@ -71714,82 +69989,7 @@ find_next_json_quotable_character_scalar(const std::string_view view,
// SIMD-accelerated position finding that directly locates the first quotable
// character, combining detection and position extraction in a single pass to
// minimize redundant work.
-#if SIMDJSON_EXPERIMENTAL_HAS_NEON
-simdjson_inline size_t
-find_next_json_quotable_character(const std::string_view view,
- size_t location) noexcept {
- const size_t len = view.size();
- const uint8_t *ptr =
- reinterpret_cast<const uint8_t *>(view.data()) + location;
- size_t remaining = len - location;
-
- // SIMD constants for characters requiring escape
- uint8x16_t v34 = vdupq_n_u8(34); // '"'
- uint8x16_t v92 = vdupq_n_u8(92); // '\\'
- uint8x16_t v32 = vdupq_n_u8(32); // control char threshold
-
- while (remaining >= 16) {
- uint8x16_t word = vld1q_u8(ptr);
-
- // Check for quotable characters: '"', '\\', or control chars (< 32)
- uint8x16_t needs_escape = vceqq_u8(word, v34);
- needs_escape = vorrq_u8(needs_escape, vceqq_u8(word, v92));
- needs_escape = vorrq_u8(needs_escape, vcltq_u8(word, v32));
-
- const uint8x8_t res = vshrn_n_u16(vreinterpretq_u16_u8(needs_escape), 4);
- const uint64_t mask = vget_lane_u64(vreinterpret_u64_u8(res), 0);
- if(mask != 0) {
- size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
- auto trailing_zero = trailing_zeroes(mask);
- return offset + (trailing_zero >> 2);
- }
- ptr += 16;
- remaining -= 16;
- }
-
- // Scalar fallback for remaining bytes
- size_t current = len - remaining;
- return find_next_json_quotable_character_scalar(view, current);
-}
-#elif SIMDJSON_EXPERIMENTAL_HAS_SSE2
-simdjson_inline size_t
-find_next_json_quotable_character(const std::string_view view,
- size_t location) noexcept {
- const size_t len = view.size();
- const uint8_t *ptr =
- reinterpret_cast<const uint8_t *>(view.data()) + location;
- size_t remaining = len - location;
-
- // SIMD constants
- __m128i v34 = _mm_set1_epi8(34); // '"'
- __m128i v92 = _mm_set1_epi8(92); // '\\'
- __m128i v31 = _mm_set1_epi8(31); // for control char detection
-
- while (remaining >= 16) {
- __m128i word = _mm_loadu_si128(reinterpret_cast<const __m128i *>(ptr));
-
- // Check for quotable characters
- __m128i needs_escape = _mm_cmpeq_epi8(word, v34);
- needs_escape = _mm_or_si128(needs_escape, _mm_cmpeq_epi8(word, v92));
- needs_escape = _mm_or_si128(
- needs_escape,
- _mm_cmpeq_epi8(_mm_subs_epu8(word, v31), _mm_setzero_si128()));
-
- int mask = _mm_movemask_epi8(needs_escape);
- if (mask != 0) {
- // Found quotable character - use trailing zero count to find position
- size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
- return offset + trailing_zeroes(mask);
- }
- ptr += 16;
- remaining -= 16;
- }
-
- // Scalar fallback for remaining bytes
- size_t current = len - remaining;
- return find_next_json_quotable_character_scalar(view, current);
-}
-#elif SIMDJSON_EXPERIMENTAL_HAS_LASX
+#if SIMDJSON_EXPERIMENTAL_HAS_LASX
simdjson_inline size_t
find_next_json_quotable_character(const std::string_view view,
size_t location) noexcept {
@@ -71962,6 +70162,7 @@ find_next_json_quotable_character(const std::string_view view,
return find_next_json_quotable_character_scalar(view, location);
}
#endif
+#endif // !SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE
SIMDJSON_CONSTEXPR_LAMBDA static std::string_view control_chars[] = {
"\\u0000", "\\u0001", "\\u0002", "\\u0003", "\\u0004", "\\u0005", "\\u0006",
@@ -71989,10 +70190,6 @@ SIMDJSON_CONSTEXPR_LAMBDA simdjson_inline void escape_json_char(char c, char *&o
}
}
-#if SIMDJSON_EXPERIMENTAL_HAS_SSE2 || SIMDJSON_EXPERIMENTAL_HAS_NEON
-#define SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE 1
-#endif
-
#if SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE
#if SIMDJSON_EXPERIMENTAL_HAS_SSE2
@@ -72273,10 +70470,21 @@ inline size_t write_string_escaped(const std::string_view input, char *out) {
#undef SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE
-simdjson_inline string_builder::string_builder(size_t initial_capacity)
- : buffer(new(std::nothrow) char[initial_capacity]), position(0),
- capacity(buffer.get() != nullptr ? initial_capacity : 0),
- is_valid(buffer.get() != nullptr) {}
+namespace internal {
+// Sets the size of s to n without initializing the new bytes when the
+// standard library allows it (C++23 resize_and_overwrite).
+simdjson_inline void resize_uninitialized(std::string &s, size_t n) {
+#if defined(__cpp_lib_string_resize_and_overwrite) && __cpp_lib_string_resize_and_overwrite >= 202110L
+ s.resize_and_overwrite(n, [](char *, size_t len) noexcept { return len; });
+#else
+ s.resize(n);
+#endif
+}
+} // namespace internal
+
+simdjson_inline string_builder::string_builder(size_t initial_capacity) {
+ grow_buffer(initial_capacity);
+}
simdjson_inline bool string_builder::capacity_check(size_t upcoming_bytes) {
// We use the convention that when is_valid is false, then the capacity and
@@ -72299,22 +70507,63 @@ inline void string_builder::grow_buffer(size_t desired_capacity) {
if (!is_valid) {
return;
}
- std::unique_ptr<char[]> new_buffer(new (std::nothrow) char[desired_capacity]);
- if (new_buffer.get() == nullptr) {
+ std::string new_buffer;
+ if (desired_capacity > new_buffer.max_size()) {
+ set_valid(false);
+ return;
+ }
+#if defined(__cpp_exceptions) || defined(_CPPUNWIND)
+ try {
+ internal::resize_uninitialized(new_buffer, desired_capacity);
+ } catch (...) { // std::bad_alloc
set_valid(false);
return;
}
- std::memcpy(new_buffer.get(), buffer.get(), position);
+#else
+ // Without exceptions, a failed allocation terminates the program.
+ internal::resize_uninitialized(new_buffer, desired_capacity);
+#endif
+ std::memcpy(&new_buffer[0], buffer_data(), position);
buffer.swap(new_buffer);
capacity = desired_capacity;
}
+// Not simdjson_inline: MSVC cannot force-inline a function with a try block (C4714).
+inline error_code string_builder::move_to(std::string &out) noexcept {
+ if (!is_valid) {
+ return simdjson::OUT_OF_CAPACITY;
+ }
+ if (position <= buffer.capacity() / 2) {
+ // Most of the buffer is unused: copy the bytes out, so that a small
+ // result does not keep the whole buffer allocated. The builder keeps its
+ // buffer for what it writes next.
+#if defined(__cpp_exceptions) || defined(_CPPUNWIND)
+ try {
+ out.assign(buffer.data(), position);
+ } catch (...) { // std::bad_alloc
+ return simdjson::MEMALLOC;
+ }
+#else
+ // Without exceptions, a failed allocation terminates the program.
+ out.assign(buffer.data(), position);
+#endif
+ position = 0;
+ return simdjson::SUCCESS;
+ }
+ buffer.resize(position); // shrinking never allocates
+ out = std::move(buffer);
+ std::string().swap(buffer);
+ position = 0;
+ capacity = 0;
+ return simdjson::SUCCESS;
+}
+
simdjson_inline void string_builder::set_valid(bool valid) noexcept {
if (!valid) {
is_valid = false;
capacity = 0;
position = 0;
- buffer.reset();
+ std::string().swap(buffer);
} else {
is_valid = true;
}
@@ -72326,7 +70575,7 @@ simdjson_inline size_t string_builder::size() const noexcept {
simdjson_inline void string_builder::append(char c) noexcept {
if (capacity_check(1)) {
- buffer.get()[position++] = c;
+ buffer_data()[position++] = c;
}
}
@@ -72334,7 +70583,7 @@ simdjson_inline void string_builder::append_null() noexcept {
constexpr char null_literal[] = "null";
constexpr size_t null_len = sizeof(null_literal) - 1;
if (capacity_check(null_len)) {
- std::memcpy(buffer.get() + position, null_literal, null_len);
+ std::memcpy(buffer_data() + position, null_literal, null_len);
position += null_len;
}
}
@@ -72344,124 +70593,14 @@ simdjson_inline void string_builder::clear() noexcept {
// if it was invalid, we should try to repair it
if (!is_valid) {
capacity = 0;
- buffer.reset();
+ std::string().swap(buffer);
is_valid = true;
}
}
namespace internal {
-// Integer to decimal: James Edward Anhalt III's algorithm
-static const char jeaiii_dd[201] =
- "00010203040506070809101112131415161718192021222324252627282930313233343536373839"
- "40414243444546474849505152535455565758596061626364656667686970717273747576777879"
- "8081828384858687888990919293949596979899";
-static const char jeaiii_fd[201] =
- "0\0" "1\0" "2\0" "3\0" "4\0" "5\0" "6\0" "7\0" "8\0" "9\0"
- "10111213141516171819202122232425262728293031323334353637383940414243444546474849"
- "50515253545556575859606162636465666768697071727374757677787980818283848586878889"
- "90919293949596979899";
-
-simdjson_really_inline void jeaiii_write_dd(char *p, uint64_t k) noexcept {
- std::memcpy(p, &jeaiii_dd[2 * k], 2);
-}
-simdjson_really_inline void jeaiii_write_fd(char *p, uint64_t k) noexcept {
- std::memcpy(p, &jeaiii_fd[2 * k], 2);
-}
-
-// Caller guarantees n < 10^8. Writes 1 to 8 digits.
-simdjson_really_inline char *jeaiii_lt1e8(char *b, uint32_t n) noexcept {
- constexpr uint64_t mask24 = (uint64_t(1) << 24) - 1;
- constexpr uint64_t mask32 = (uint64_t(1) << 32) - 1;
- if (n < 100) {
- jeaiii_write_fd(b, n);
- return n < 10 ? b + 1 : b + 2;
- }
- if (n < 1000000) {
- if (n < 10000) {
- const uint32_t f0 = uint32_t(10 * (1 << 24) / 1e3 + 1) * n;
- jeaiii_write_fd(b, f0 >> 24);
- b -= n < 1000;
- const uint32_t f2 = uint32_t(f0 & mask24) * 100;
- jeaiii_write_dd(b + 2, f2 >> 24);
- return b + 4;
- }
- const uint64_t f0 = uint64_t(10 * (1ull << 32) / 1e5 + 1) * n;
- jeaiii_write_fd(b, f0 >> 32);
- b -= n < 100000;
- const uint64_t f2 = (f0 & mask32) * 100;
- jeaiii_write_dd(b + 2, f2 >> 32);
- const uint64_t f4 = (f2 & mask32) * 100;
- jeaiii_write_dd(b + 4, f4 >> 32);
- return b + 6;
- }
- const uint64_t f0 = uint64_t(10 * (1ull << 48) / 1e7 + 1) * n >> 16;
- jeaiii_write_fd(b, f0 >> 32);
- b -= n < 10000000;
- const uint64_t f2 = (f0 & mask32) * 100;
- jeaiii_write_dd(b + 2, f2 >> 32);
- const uint64_t f4 = (f2 & mask32) * 100;
- jeaiii_write_dd(b + 4, f4 >> 32);
- const uint64_t f6 = (f4 & mask32) * 100;
- jeaiii_write_dd(b + 6, f6 >> 32);
- return b + 8;
-}
-
-// Caller guarantees z < 10^8. Always writes exactly 8 digits.
-simdjson_really_inline char *jeaiii_8_digits(char *b, uint32_t z) noexcept {
- constexpr uint64_t mask32 = (uint64_t(1) << 32) - 1;
- const uint64_t f0 = (uint64_t((1ull << 48) / 1e6 + 1) * z >> 16) + 1;
- jeaiii_write_dd(b, f0 >> 32);
- const uint64_t f2 = (f0 & mask32) * 100;
- jeaiii_write_dd(b + 2, f2 >> 32);
- const uint64_t f4 = (f2 & mask32) * 100;
- jeaiii_write_dd(b + 4, f4 >> 32);
- const uint64_t f6 = (f4 & mask32) * 100;
- jeaiii_write_dd(b + 6, f6 >> 32);
- return b + 8;
-}
-
-// Caller guarantees 10^8 <= n < 2^32. Writes 9 or 10 digits.
-simdjson_really_inline char *jeaiii_9_or_10(char *b, uint64_t n) noexcept {
- constexpr uint64_t mask57 = (uint64_t(1) << 57) - 1;
- const uint64_t f0 = uint64_t(10 * (1ull << 57) / 1e9 + 1) * n;
- jeaiii_write_fd(b, f0 >> 57);
- b -= n < 1000000000;
- const uint64_t f2 = (f0 & mask57) * 100;
- jeaiii_write_dd(b + 2, f2 >> 57);
- const uint64_t f4 = (f2 & mask57) * 100;
- jeaiii_write_dd(b + 4, f4 >> 57);
- const uint64_t f6 = (f4 & mask57) * 100;
- jeaiii_write_dd(b + 6, f6 >> 57);
- const uint64_t f8 = (f6 & mask57) * 100;
- jeaiii_write_dd(b + 8, f8 >> 57);
- return b + 10;
-}
-
-simdjson_really_inline char *write_uint_jeaiii(char *b, uint64_t n) noexcept {
- if (n < 100000000) {
- return jeaiii_lt1e8(b, uint32_t(n));
- }
- if (n < (uint64_t(1) << 32)) {
- return jeaiii_9_or_10(b, n);
- }
- // At least 10 digits: the low 8 digits, and 2 to 12 digits above them.
- const uint32_t z = uint32_t(n % 100000000);
- uint64_t u = n / 100000000;
- if (u < 100000000) {
- // u has 2 to 8 digits (if u < 10, n would be below 2^32).
- b = jeaiii_lt1e8(b, uint32_t(u));
- } else if (u < (uint64_t(1) << 32)) {
- b = jeaiii_9_or_10(b, u);
- } else {
- // u has 11 or 12 digits: split off 8 more.
- const uint32_t y = uint32_t(u % 100000000);
- u /= 100000000;
- b = jeaiii_lt1e8(b, uint32_t(u)); // 3 or 4 digits
- b = jeaiii_8_digits(b, y);
- }
- return jeaiii_8_digits(b, z);
-}
+using simdjson::internal::write_uint_jeaiii;
// Writes v at p, which must have to_chars_buffer_size bytes available, and
// returns the end of what was written.
@@ -72489,20 +70628,22 @@ simdjson_inline void string_builder::append(number_type v) noexcept {
std::is_integral<number_type>::value ||
std::is_floating_point<number_type>::value,
"Unsupported number type");
+ static_assert(sizeof(number_type) <= sizeof(uint64_t),
+ "Integers wider than 64 bits are not supported");
// If C++17 is available, we can 'if constexpr' here.
SIMDJSON_IF_CONSTEXPR(std::is_same<number_type, bool>::value) {
if (v) {
constexpr char true_literal[] = "true";
constexpr size_t true_len = sizeof(true_literal) - 1;
if (capacity_check(true_len)) {
- std::memcpy(buffer.get() + position, true_literal, true_len);
+ std::memcpy(buffer_data() + position, true_literal, true_len);
position += true_len;
}
} else {
constexpr char false_literal[] = "false";
constexpr size_t false_len = sizeof(false_literal) - 1;
if (capacity_check(false_len)) {
- std::memcpy(buffer.get() + position, false_literal, false_len);
+ std::memcpy(buffer_data() + position, false_literal, false_len);
position += false_len;
}
}
@@ -72512,9 +70653,9 @@ simdjson_inline void string_builder::append(number_type v) noexcept {
if (capacity_check(max_number_size)) {
using unsigned_type = typename std::make_unsigned<number_type>::type;
char* end = internal::write_uint_jeaiii(
- buffer.get() + position,
+ buffer_data() + position,
static_cast<uint64_t>(static_cast<unsigned_type>(v)));
- position = end - buffer.get();
+ position = end - buffer_data();
}
}
else SIMDJSON_IF_CONSTEXPR(std::is_integral<number_type>::value) {
@@ -72528,11 +70669,11 @@ simdjson_inline void string_builder::append(number_type v) noexcept {
? unsigned_type(0) - static_cast<unsigned_type>(v)
: static_cast<unsigned_type>(v);
// Branchless: always write '-', advance only if negative.
- buffer.get()[position] = '-';
+ buffer_data()[position] = '-';
position += negative;
char* end = internal::write_uint_jeaiii(
- buffer.get() + position, static_cast<uint64_t>(pv));
- position = end - buffer.get();
+ buffer_data() + position, static_cast<uint64_t>(pv));
+ position = end - buffer_data();
}
}
else SIMDJSON_IF_CONSTEXPR(std::is_floating_point<number_type>::value) {
@@ -72548,16 +70689,16 @@ simdjson_inline void string_builder::append(number_type v) noexcept {
constexpr char nan_literal[] = "NaN";
constexpr size_t nan_len = sizeof(nan_literal) - 1;
- std::memcpy(buffer.get() + position, nan_literal, nan_len);
+ std::memcpy(buffer_data() + position, nan_literal, nan_len);
position += nan_len;
} else {
constexpr char inf_literal[] = "Infinity";
constexpr size_t inf_len = sizeof(inf_literal) - 1;
if (v < 0) {
- buffer.get()[position] = '-';
+ buffer_data()[position] = '-';
++position;
}
- std::memcpy(buffer.get() + position, inf_literal, inf_len);
+ std::memcpy(buffer_data() + position, inf_literal, inf_len);
position += inf_len;
}
return;
@@ -72565,9 +70706,9 @@ simdjson_inline void string_builder::append(number_type v) noexcept {
#endif
// We could specialize for float.
- char *end = simdjson::internal::to_chars(buffer.get() + position, nullptr,
+ char *end = simdjson::internal::to_chars(buffer_data() + position, nullptr,
double(v));
- position = end - buffer.get();
+ position = end - buffer_data();
}
}
}
@@ -72581,7 +70722,7 @@ string_builder::escape_and_append(std::string_view input) noexcept {
return;
}
if (capacity_check(6 * input.size())) {
- position += write_string_escaped(input, buffer.get() + position);
+ position += write_string_escaped(input, buffer_data() + position);
}
}
@@ -72594,9 +70735,9 @@ string_builder::escape_and_append_with_quotes(std::string_view input) noexcept {
return;
}
if (capacity_check(2 + 6 * input.size())) {
- buffer.get()[position++] = '"';
- position += write_string_escaped(input, buffer.get() + position);
- buffer.get()[position++] = '"';
+ buffer_data()[position++] = '"';
+ position += write_string_escaped(input, buffer_data() + position);
+ buffer_data()[position++] = '"';
}
}
@@ -72604,10 +70745,10 @@ simdjson_inline void
string_builder::escape_and_append_with_quotes(char input) noexcept {
// escaping might turn a control character into \x00xx so 6 characters.
if (capacity_check(2 + 6 * 1)) {
- buffer.get()[position++] = '"';
+ buffer_data()[position++] = '"';
std::string_view cinput(&input, 1);
- position += write_string_escaped(cinput, buffer.get() + position);
- buffer.get()[position++] = '"';
+ position += write_string_escaped(cinput, buffer_data() + position);
+ buffer_data()[position++] = '"';
}
}
@@ -72637,7 +70778,7 @@ simdjson_inline void string_builder::append_raw(const char *c) noexcept {
simdjson_inline void
string_builder::append_raw(std::string_view input) noexcept {
if (capacity_check(input.size())) {
- std::memcpy(buffer.get() + position, input.data(), input.size());
+ std::memcpy(buffer_data() + position, input.data(), input.size());
position += input.size();
}
}
@@ -72645,7 +70786,7 @@ string_builder::append_raw(std::string_view input) noexcept {
simdjson_inline void string_builder::append_raw(const char *str,
size_t len) noexcept {
if (capacity_check(len)) {
- std::memcpy(buffer.get() + position, str, len);
+ std::memcpy(buffer_data() + position, str, len);
position += len;
}
}
@@ -72653,7 +70794,7 @@ simdjson_inline void string_builder::append_raw(const char *str,
template <size_t N>
simdjson_inline void string_builder::append_raw_n(const char *str) noexcept {
if (capacity_check(N)) {
- std::memcpy(buffer.get() + position, str, N);
+ std::memcpy(buffer_data() + position, str, N);
position += N;
}
}
@@ -72663,7 +70804,7 @@ template <typename Text>
simdjson_inline void string_builder::append_static_string() noexcept {
constexpr auto& padded = constevalutil::static_string<Text>;
if (capacity_check(padded.size())) {
- std::memcpy(buffer.get() + position, padded.data(), padded.size());
+ std::memcpy(buffer_data() + position, padded.data(), padded.size());
position += constevalutil::static_string_size<Text>();
}
}
@@ -72757,54 +70898,54 @@ string_builder::view() const noexcept {
if (!is_valid) {
return simdjson::OUT_OF_CAPACITY;
}
- return std::string_view(buffer.get(), position);
+ return std::string_view(buffer_data(), position);
}
simdjson_inline simdjson_result<const char *> string_builder::c_str() noexcept {
if (capacity_check(1)) {
- buffer.get()[position] = '\0';
- return buffer.get();
+ buffer_data()[position] = '\0';
+ return buffer_data();
}
return simdjson::OUT_OF_CAPACITY;
}
simdjson_inline bool string_builder::validate_unicode() const noexcept {
- return simdjson::validate_utf8(buffer.get(), position);
+ return simdjson::validate_utf8(buffer_data(), position);
}
simdjson_inline void string_builder::start_object() noexcept {
if (capacity_check(1)) {
- buffer.get()[position++] = '{';
+ buffer_data()[position++] = '{';
}
}
simdjson_inline void string_builder::end_object() noexcept {
if (capacity_check(1)) {
- buffer.get()[position++] = '}';
+ buffer_data()[position++] = '}';
}
}
simdjson_inline void string_builder::start_array() noexcept {
if (capacity_check(1)) {
- buffer.get()[position++] = '[';
+ buffer_data()[position++] = '[';
}
}
simdjson_inline void string_builder::end_array() noexcept {
if (capacity_check(1)) {
- buffer.get()[position++] = ']';
+ buffer_data()[position++] = ']';
}
}
simdjson_inline void string_builder::append_comma() noexcept {
if (capacity_check(1)) {
- buffer.get()[position++] = ',';
+ buffer_data()[position++] = ',';
}
}
simdjson_inline void string_builder::append_colon() noexcept {
if (capacity_check(1)) {
- buffer.get()[position++] = ':';
+ buffer_data()[position++] = ':';
}
}
@@ -73515,23 +71656,6 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin
return { (v == '\\').to_bitmask(), (v == '"').to_bitmask() };
}
-struct escaping {
- static constexpr uint64_t BYTES_PROCESSED = sizeof(simd8<uint8_t>);
- simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
-
- simdjson_inline bool has_escape() { return escape_bits != 0; }
- simdjson_inline int escape_index() { return trailing_zeroes(escape_bits) / 4; }
-
- uint64_t escape_bits;
-}; // struct escaping
-
-simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
- static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes");
- simd8<uint8_t> v(src);
- v.store(dst);
- return { ((v == '"') | (v == '\\') | (v == 32)).to_bitmask() };
-}
-
} // unnamed namespace
} // namespace rvv_vls
@@ -73662,6 +71786,10 @@ namespace builder {
class string_builder {
public:
simdjson_inline string_builder(size_t initial_capacity = DEFAULT_INITIAL_CAPACITY);
+ string_builder(const string_builder &) = delete;
+ string_builder &operator=(const string_builder &) = delete;
+ string_builder(string_builder &&) noexcept = default;
+ string_builder &operator=(string_builder &&) noexcept = default;
static constexpr size_t DEFAULT_INITIAL_CAPACITY = 1024;
@@ -73675,6 +71803,18 @@ public:
typename = typename std::enable_if<std::is_arithmetic<number_type>::value>::type>
simdjson_inline void append(number_type v) noexcept;
+#ifdef __SIZEOF_INT128__
+ /**
+ * Integers wider than 64 bits are not supported. Without these overloads, a
+ * 128-bit integer would either be truncated to 64 bits by append(number_type)
+ * (when the standard library counts it as arithmetic, as libc++ does) or be
+ * converted to char by append(char) (when it does not, as libstdc++ does in
+ * strict ISO mode).
+ */
+ void append(__int128_t v) noexcept = delete;
+ void append(__uint128_t v) noexcept = delete;
+#endif
+
/**
* Append character c.
*/
@@ -73853,6 +71993,20 @@ requires (!std::is_convertible<R, std::string_view>::value && !concepts::optiona
*/
simdjson_inline simdjson_result<const char *> c_str() noexcept;
+ /**
+ * Moves the written JSON buffer into out, replacing its content, without
+ * copying it. When the content fills at most half of the buffer, it is
+ * copied instead, so that a small result does not keep the whole buffer
+ * allocated. The builder is left empty: it keeps its buffer when the
+ * content was copied, and gives up its memory when the buffer was moved.
+ * Returns an error, and leaves out and the builder unchanged, if memory
+ * allocation failed.
+ *
+ * The result may not be valid UTF-8 if some of your content was not valid UTF-8.
+ * Use validate_unicode() to check the content.
+ */
+ simdjson_warn_unused inline error_code move_to(std::string &out) noexcept;
+
/**
* Return true if the content is valid UTF-8.
*/
@@ -73872,7 +72026,7 @@ requires (!std::is_convertible<R, std::string_view>::value && !concepts::optiona
// forces those reloads when accessed via members of *this). User
// code should NOT call these directly.
// ============================================================
- simdjson_inline char *unsafe_data() noexcept { return buffer.get(); }
+ simdjson_inline char *unsafe_data() noexcept { return buffer_data(); }
simdjson_inline size_t unsafe_position() const noexcept { return position; }
simdjson_inline size_t unsafe_capacity() const noexcept { return capacity; }
simdjson_inline void unsafe_set_position(size_t p) noexcept { position = p; }
@@ -73913,7 +72067,13 @@ private:
*/
simdjson_inline void set_valid(bool valid) noexcept;
- std::unique_ptr<char[]> buffer{};
+ simdjson_inline char *buffer_data() noexcept { return &buffer[0]; }
+ simdjson_inline const char *buffer_data() const noexcept { return buffer.data(); }
+
+ // We own capacity bytes: buffer.size() == capacity. Only the first position
+ // bytes hold JSON. A std::string lets move_to() hand the result over
+ // without a copy.
+ std::string buffer{};
size_t position{0};
size_t capacity{0};
bool is_valid{true};
@@ -73931,20 +72091,16 @@ template <class Z>
simdjson_warn_unused simdjson_result<std::string> to_json(const Z &z, size_t initial_capacity = simdjson::rvv_vls::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
simdjson::rvv_vls::builder::string_builder b(initial_capacity);
b.append(z);
- std::string_view s;
- auto e = b.view().get(s);
+ std::string s;
+ auto e = b.move_to(s);
if(e) { return e; }
- return std::string(s);
+ return s;
}
template <class Z>
simdjson_warn_unused error_code to_json(const Z &z, std::string &s, size_t initial_capacity = simdjson::rvv_vls::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
simdjson::rvv_vls::builder::string_builder b(initial_capacity);
b.append(z);
- std::string_view sv;
- auto e = b.view().get(sv);
- if(e) { return e; }
- s.assign(sv.data(), sv.size());
- return simdjson::SUCCESS;
+ return b.move_to(s);
}
#endif
@@ -73960,7 +72116,7 @@ simdjson_warn_unused error_code to_json(const Z &z, std::string &s, size_t initi
#ifndef SIMDJSON_GENERIC_BUILDER_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
-/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_STRING_BUILDER_H */
+/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_BUILDER_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/builder/json_string_builder.h" */
/* amalgamation skipped (editor-only): #include "simdjson/concepts.h" */
/* amalgamation skipped (editor-only): #include "simdjson/annotations.h" */
@@ -73986,7 +72142,7 @@ namespace builder {
// included at the point this header is parsed; without these forwards,
// name lookup falls back to the wrong outer namespace).
namespace internal {
-simdjson_really_inline char *write_uint_jeaiii(char *p, uint64_t v) noexcept;
+using simdjson::internal::write_uint_jeaiii;
simdjson_inline char *write_double(char *p, double v) noexcept;
} // namespace internal
simdjson_really_inline size_t write_string_escaped(const std::string_view input, char *out);
@@ -74119,6 +72275,51 @@ consteval bool is_serialized_member(std::meta::info dm) {
&& !simdjson::detail::has_annotation(dm, ^^simdjson::detail::skip_serializing_tag);
}
+template <class Target, class T, class... Visiting>
+consteval bool members_reach();
+
+// Whether a value of type T may hold a value of type Target, directly or
+// through optionals, smart pointers, containers and members. Visiting lists
+// the structures being searched, so that the search terminates.
+template <class Target, class T, class... Visiting>
+consteval bool reaches() {
+ if constexpr (std::is_same_v<T, Target>) {
+ return true;
+ } else if constexpr ((std::is_same_v<T, Visiting> || ...) || require_custom_serialization<T> ||
+ std::is_same_v<T, std::string> || std::is_same_v<T, std::string_view>) {
+ return false;
+ } else if constexpr (concepts::optional_type<T> || concepts::smart_pointer<T>) {
+ return reaches<Target, std::remove_cvref_t<decltype(*std::declval<const T &>())>, Visiting...>();
+ } else if constexpr (concepts::string_view_keyed_map<T>) {
+ return reaches<Target, std::remove_cvref_t<typename T::mapped_type>, Visiting...>();
+ } else if constexpr (concepts::container_but_not_string<T> || concepts::appendable_containers<T>) {
+ return reaches<Target, std::remove_cvref_t<std::ranges::range_value_t<T>>, Visiting...>();
+ } else if constexpr (std::is_class_v<T>) {
+ return members_reach<Target, T, Visiting...>();
+ } else {
+ return false;
+ }
+}
+
+template <class Target, class T, class... Visiting>
+consteval bool members_reach() {
+ bool found = false;
+ template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) {
+ if constexpr (is_serialized_member(dm)) {
+ found = found || reaches<Target, std::remove_cvref_t<decltype(std::declval<const T &>().[:dm:])>, T, Visiting...>();
+ }
+ };
+ return found;
+}
+
+// Whether the structure T may contain itself, as in
+// struct Rule { std::unique_ptr<Rule> subrule; }. Serializing such a type
+// recurses at runtime, which simdjson_really_inline functions cannot do.
+template <class T>
+consteval bool is_recursive() {
+ return members_reach<T, T>();
+}
+
// False when the member has a skip_serializing_if<pred> annotation and
// pred(value) is true.
template <auto dm, typename V>
@@ -74302,6 +72503,8 @@ simdjson_really_inline constexpr void atom(W &w, const T &m) {
template<class W, typename number_type,
typename = typename std::enable_if<std::is_arithmetic<number_type>::value && !std::is_same_v<number_type, char>>::type>
simdjson_really_inline constexpr void atom(W &w, const number_type t) {
+ static_assert(sizeof(number_type) <= sizeof(uint64_t),
+ "Integers wider than 64 bits are not supported");
// Booleans / floats: defer to string_builder (rare path; keeps writer hot
// path free of float-formatter machinery). For integers, write directly
// via jeaiii using local pos.
@@ -74340,6 +72543,31 @@ simdjson_really_inline constexpr void atom(W &w, const number_type t) {
}
}
+namespace annotation_detail {
+
+template <class W, class T>
+simdjson_really_inline void atom_object(W &w, const T &t) {
+ if constexpr (simdjson::detail::has_annotation(^^T, ^^simdjson::detail::transparent_tag)) {
+ // A transparent structure is serialized as its single member.
+ constexpr auto dm = simdjson::detail::transparent_member(^^T);
+ atom_member<dm>(w, t.[:dm:]);
+ } else {
+ bool first = true;
+ if (!w.ensure(1)) { return; }
+ w.ptr[w.pos++] = '{';
+ atom_fields(w, t, first);
+ if (!w.ensure(1)) { return; }
+ w.ptr[w.pos++] = '}';
+ }
+}
+
+template <class W, class T>
+void atom_object_outlined(W &w, const T &t) {
+ atom_object(w, t);
+}
+
+} // namespace annotation_detail
+
template <class W, class T>
requires(std::is_class_v<T> && !concepts::container_but_not_string<T> &&
!concepts::string_view_keyed_map<T> &&
@@ -74351,17 +72579,11 @@ template <class W, class T>
!std::is_same_v<T, const char*> &&
!std::is_same_v<T, char> && !require_custom_serialization<T>)
simdjson_really_inline constexpr void atom(W &w, const T &t) {
- if constexpr (simdjson::detail::has_annotation(^^T, ^^simdjson::detail::transparent_tag)) {
- // A transparent structure is serialized as its single member.
- constexpr auto dm = simdjson::detail::transparent_member(^^T);
- annotation_detail::atom_member<dm>(w, t.[:dm:]);
+ if constexpr (annotation_detail::is_recursive<T>()) {
+ // Break the cycle of simdjson_really_inline calls.
+ annotation_detail::atom_object_outlined(w, t);
} else {
- bool first = true;
- if (!w.ensure(1)) { return; }
- w.ptr[w.pos++] = '{';
- annotation_detail::atom_fields(w, t, first);
- if (!w.ensure(1)) { return; }
- w.ptr[w.pos++] = '}';
+ annotation_detail::atom_object(w, t);
}
}
@@ -74461,25 +72683,30 @@ namespace bound_detail {
// Whether size_bound covers everything that atom() writes for T: not when a
// member is serialized by a with<Adapter> serializer, which writes an unknown
// amount through the string_builder.
-template <class T>
+// Visiting lists the structures being checked, so that a recursive type
+// (struct Node { std::unique_ptr<Node> next; }) does not recurse forever: a
+// type already being visited adds nothing, its members get checked once.
+template <class T, class... Visiting>
consteval bool is_bounded() {
- if constexpr (require_custom_serialization<T>) {
+ if constexpr ((std::is_same_v<T, Visiting> || ...)) {
+ return true;
+ } else if constexpr (require_custom_serialization<T>) {
return false;
} else if constexpr (std::is_same_v<T, std::string> || std::is_same_v<T, std::string_view> ||
std::is_same_v<T, const char *> || std::is_arithmetic_v<T> || std::is_enum_v<T>) {
return true;
} else if constexpr (concepts::optional_type<T> || concepts::smart_pointer<T>) {
- return is_bounded<std::remove_cvref_t<decltype(*std::declval<const T &>())>>();
+ return is_bounded<std::remove_cvref_t<decltype(*std::declval<const T &>())>, Visiting...>();
} else if constexpr (concepts::string_view_keyed_map<T>) {
- return is_bounded<std::remove_cvref_t<typename T::mapped_type>>();
+ return is_bounded<std::remove_cvref_t<typename T::mapped_type>, Visiting...>();
} else if constexpr (concepts::container_but_not_string<T> || concepts::appendable_containers<T>) {
- return is_bounded<std::remove_cvref_t<std::ranges::range_value_t<T>>>();
+ return is_bounded<std::remove_cvref_t<std::ranges::range_value_t<T>>, Visiting...>();
} else {
bool bounded = true;
template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) {
if constexpr (annotation_detail::is_serialized_member(dm)) {
bounded = bounded && simdjson::detail::annotation_of_template(dm, ^^simdjson::detail::with_t) == std::meta::info{} &&
- is_bounded<std::remove_cvref_t<decltype(std::declval<const T &>().[:dm:])>>();
+ is_bounded<std::remove_cvref_t<decltype(std::declval<const T &>().[:dm:])>, T, Visiting...>();
}
};
return bounded;
@@ -74517,6 +72744,11 @@ simdjson_really_inline size_t fields_bound(const T &t) noexcept {
return bound;
}
+template <class T>
+size_t fields_bound_outlined(const T &t) noexcept {
+ return fields_bound(t);
+}
+
template <class T>
simdjson_really_inline size_t size_bound([[maybe_unused]] const T &t) noexcept {
if constexpr (std::is_same_v<T, char>) {
@@ -74561,6 +72793,9 @@ simdjson_really_inline size_t size_bound([[maybe_unused]] const T &t) noexcept {
} else if constexpr (simdjson::detail::has_annotation(^^T, ^^simdjson::detail::transparent_tag)) {
constexpr auto dm = simdjson::detail::transparent_member(^^T);
return size_bound(t.[:dm:]);
+ } else if constexpr (annotation_detail::is_recursive<T>()) {
+ // Break the cycle of simdjson_really_inline calls.
+ return 2 + fields_bound_outlined(t);
} else {
return 2 + fields_bound(t);
}
@@ -74695,10 +72930,7 @@ simdjson_warn_unused error_code to_json(const Z &z, std::string &s, size_t initi
} else {
string_builder b(initial_capacity);
append(b, z);
- std::string_view view;
- if(auto e = b.view().get(view); e) { return e; }
- s.assign(view);
- return SUCCESS;
+ return b.move_to(s);
}
}
@@ -74761,9 +72993,9 @@ template<constevalutil::fixed_string... FieldNames, typename T>
simdjson_warn_unused simdjson_result<std::string> extract_from(const T &obj, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) {
string_builder b(initial_capacity);
extract_from<FieldNames...>(b, obj);
- std::string_view s;
- if(auto e = b.view().get(s); e) { return e; }
- return std::string(s);
+ std::string s;
+ if(auto e = b.move_to(s); e) { return e; }
+ return s;
}
SIMDJSON_POP_DISABLE_WARNINGS
@@ -74785,16 +73017,16 @@ template<constevalutil::fixed_string... FieldNames, typename T>
simdjson_warn_unused simdjson_result<std::string> extract_from(const T &obj, size_t initial_capacity = rvv_vls::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
rvv_vls::builder::string_builder b(initial_capacity);
rvv_vls::builder::extract_from<FieldNames...>(b, obj);
- std::string_view s;
- if(auto e = b.view().get(s); e) { return e; }
- return std::string(s);
+ std::string s;
+ if(auto e = b.move_to(s); e) { return e; }
+ return s;
}
} // namespace simdjson
#endif // SIMDJSON_STATIC_REFLECTION
-#endif
+#endif // SIMDJSON_GENERIC_BUILDER_H
/* end file simdjson/generic/builder/json_builder.h for rvv_vls */
/* including simdjson/generic/builder/fractured_json_builder.h for rvv_vls: #include "simdjson/generic/builder/fractured_json_builder.h" */
/* begin file simdjson/generic/builder/fractured_json_builder.h for rvv_vls */
@@ -74933,6 +73165,7 @@ simdjson_warn_unused simdjson_result<std::string> extract_fractured_json(
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_STRING_BUILDER_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/builder/json_string_builder.h" */
+/* amalgamation skipped (editor-only): #include "simdjson/internal/jeaiii.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
/*
@@ -75045,6 +73278,12 @@ inline bool has_json_escapable_byte(uint64_t x) {
**/
+#if SIMDJSON_EXPERIMENTAL_HAS_SSE2 || SIMDJSON_EXPERIMENTAL_HAS_NEON
+#define SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE 1
+#endif
+
+// With block escaping (SSE2/NEON), write_string_escaped scans the input itself.
+#if !SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE
// Scalar fallback for finding next quotable character
SIMDJSON_CONSTEXPR_LAMBDA simdjson_inline size_t
find_next_json_quotable_character_scalar(const std::string_view view,
@@ -75060,82 +73299,7 @@ find_next_json_quotable_character_scalar(const std::string_view view,
// SIMD-accelerated position finding that directly locates the first quotable
// character, combining detection and position extraction in a single pass to
// minimize redundant work.
-#if SIMDJSON_EXPERIMENTAL_HAS_NEON
-simdjson_inline size_t
-find_next_json_quotable_character(const std::string_view view,
- size_t location) noexcept {
- const size_t len = view.size();
- const uint8_t *ptr =
- reinterpret_cast<const uint8_t *>(view.data()) + location;
- size_t remaining = len - location;
-
- // SIMD constants for characters requiring escape
- uint8x16_t v34 = vdupq_n_u8(34); // '"'
- uint8x16_t v92 = vdupq_n_u8(92); // '\\'
- uint8x16_t v32 = vdupq_n_u8(32); // control char threshold
-
- while (remaining >= 16) {
- uint8x16_t word = vld1q_u8(ptr);
-
- // Check for quotable characters: '"', '\\', or control chars (< 32)
- uint8x16_t needs_escape = vceqq_u8(word, v34);
- needs_escape = vorrq_u8(needs_escape, vceqq_u8(word, v92));
- needs_escape = vorrq_u8(needs_escape, vcltq_u8(word, v32));
-
- const uint8x8_t res = vshrn_n_u16(vreinterpretq_u16_u8(needs_escape), 4);
- const uint64_t mask = vget_lane_u64(vreinterpret_u64_u8(res), 0);
- if(mask != 0) {
- size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
- auto trailing_zero = trailing_zeroes(mask);
- return offset + (trailing_zero >> 2);
- }
- ptr += 16;
- remaining -= 16;
- }
-
- // Scalar fallback for remaining bytes
- size_t current = len - remaining;
- return find_next_json_quotable_character_scalar(view, current);
-}
-#elif SIMDJSON_EXPERIMENTAL_HAS_SSE2
-simdjson_inline size_t
-find_next_json_quotable_character(const std::string_view view,
- size_t location) noexcept {
- const size_t len = view.size();
- const uint8_t *ptr =
- reinterpret_cast<const uint8_t *>(view.data()) + location;
- size_t remaining = len - location;
-
- // SIMD constants
- __m128i v34 = _mm_set1_epi8(34); // '"'
- __m128i v92 = _mm_set1_epi8(92); // '\\'
- __m128i v31 = _mm_set1_epi8(31); // for control char detection
-
- while (remaining >= 16) {
- __m128i word = _mm_loadu_si128(reinterpret_cast<const __m128i *>(ptr));
-
- // Check for quotable characters
- __m128i needs_escape = _mm_cmpeq_epi8(word, v34);
- needs_escape = _mm_or_si128(needs_escape, _mm_cmpeq_epi8(word, v92));
- needs_escape = _mm_or_si128(
- needs_escape,
- _mm_cmpeq_epi8(_mm_subs_epu8(word, v31), _mm_setzero_si128()));
-
- int mask = _mm_movemask_epi8(needs_escape);
- if (mask != 0) {
- // Found quotable character - use trailing zero count to find position
- size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
- return offset + trailing_zeroes(mask);
- }
- ptr += 16;
- remaining -= 16;
- }
-
- // Scalar fallback for remaining bytes
- size_t current = len - remaining;
- return find_next_json_quotable_character_scalar(view, current);
-}
-#elif SIMDJSON_EXPERIMENTAL_HAS_LASX
+#if SIMDJSON_EXPERIMENTAL_HAS_LASX
simdjson_inline size_t
find_next_json_quotable_character(const std::string_view view,
size_t location) noexcept {
@@ -75308,6 +73472,7 @@ find_next_json_quotable_character(const std::string_view view,
return find_next_json_quotable_character_scalar(view, location);
}
#endif
+#endif // !SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE
SIMDJSON_CONSTEXPR_LAMBDA static std::string_view control_chars[] = {
"\\u0000", "\\u0001", "\\u0002", "\\u0003", "\\u0004", "\\u0005", "\\u0006",
@@ -75335,10 +73500,6 @@ SIMDJSON_CONSTEXPR_LAMBDA simdjson_inline void escape_json_char(char c, char *&o
}
}
-#if SIMDJSON_EXPERIMENTAL_HAS_SSE2 || SIMDJSON_EXPERIMENTAL_HAS_NEON
-#define SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE 1
-#endif
-
#if SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE
#if SIMDJSON_EXPERIMENTAL_HAS_SSE2
@@ -75619,10 +73780,21 @@ inline size_t write_string_escaped(const std::string_view input, char *out) {
#undef SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE
-simdjson_inline string_builder::string_builder(size_t initial_capacity)
- : buffer(new(std::nothrow) char[initial_capacity]), position(0),
- capacity(buffer.get() != nullptr ? initial_capacity : 0),
- is_valid(buffer.get() != nullptr) {}
+namespace internal {
+// Sets the size of s to n without initializing the new bytes when the
+// standard library allows it (C++23 resize_and_overwrite).
+simdjson_inline void resize_uninitialized(std::string &s, size_t n) {
+#if defined(__cpp_lib_string_resize_and_overwrite) && __cpp_lib_string_resize_and_overwrite >= 202110L
+ s.resize_and_overwrite(n, [](char *, size_t len) noexcept { return len; });
+#else
+ s.resize(n);
+#endif
+}
+} // namespace internal
+
+simdjson_inline string_builder::string_builder(size_t initial_capacity) {
+ grow_buffer(initial_capacity);
+}
simdjson_inline bool string_builder::capacity_check(size_t upcoming_bytes) {
// We use the convention that when is_valid is false, then the capacity and
@@ -75645,22 +73817,63 @@ inline void string_builder::grow_buffer(size_t desired_capacity) {
if (!is_valid) {
return;
}
- std::unique_ptr<char[]> new_buffer(new (std::nothrow) char[desired_capacity]);
- if (new_buffer.get() == nullptr) {
+ std::string new_buffer;
+ if (desired_capacity > new_buffer.max_size()) {
+ set_valid(false);
+ return;
+ }
+#if defined(__cpp_exceptions) || defined(_CPPUNWIND)
+ try {
+ internal::resize_uninitialized(new_buffer, desired_capacity);
+ } catch (...) { // std::bad_alloc
set_valid(false);
return;
}
- std::memcpy(new_buffer.get(), buffer.get(), position);
+#else
+ // Without exceptions, a failed allocation terminates the program.
+ internal::resize_uninitialized(new_buffer, desired_capacity);
+#endif
+ std::memcpy(&new_buffer[0], buffer_data(), position);
buffer.swap(new_buffer);
capacity = desired_capacity;
}
+// Not simdjson_inline: MSVC cannot force-inline a function with a try block (C4714).
+inline error_code string_builder::move_to(std::string &out) noexcept {
+ if (!is_valid) {
+ return simdjson::OUT_OF_CAPACITY;
+ }
+ if (position <= buffer.capacity() / 2) {
+ // Most of the buffer is unused: copy the bytes out, so that a small
+ // result does not keep the whole buffer allocated. The builder keeps its
+ // buffer for what it writes next.
+#if defined(__cpp_exceptions) || defined(_CPPUNWIND)
+ try {
+ out.assign(buffer.data(), position);
+ } catch (...) { // std::bad_alloc
+ return simdjson::MEMALLOC;
+ }
+#else
+ // Without exceptions, a failed allocation terminates the program.
+ out.assign(buffer.data(), position);
+#endif
+ position = 0;
+ return simdjson::SUCCESS;
+ }
+ buffer.resize(position); // shrinking never allocates
+ out = std::move(buffer);
+ std::string().swap(buffer);
+ position = 0;
+ capacity = 0;
+ return simdjson::SUCCESS;
+}
+
simdjson_inline void string_builder::set_valid(bool valid) noexcept {
if (!valid) {
is_valid = false;
capacity = 0;
position = 0;
- buffer.reset();
+ std::string().swap(buffer);
} else {
is_valid = true;
}
@@ -75672,7 +73885,7 @@ simdjson_inline size_t string_builder::size() const noexcept {
simdjson_inline void string_builder::append(char c) noexcept {
if (capacity_check(1)) {
- buffer.get()[position++] = c;
+ buffer_data()[position++] = c;
}
}
@@ -75680,7 +73893,7 @@ simdjson_inline void string_builder::append_null() noexcept {
constexpr char null_literal[] = "null";
constexpr size_t null_len = sizeof(null_literal) - 1;
if (capacity_check(null_len)) {
- std::memcpy(buffer.get() + position, null_literal, null_len);
+ std::memcpy(buffer_data() + position, null_literal, null_len);
position += null_len;
}
}
@@ -75690,124 +73903,14 @@ simdjson_inline void string_builder::clear() noexcept {
// if it was invalid, we should try to repair it
if (!is_valid) {
capacity = 0;
- buffer.reset();
+ std::string().swap(buffer);
is_valid = true;
}
}
namespace internal {
-// Integer to decimal: James Edward Anhalt III's algorithm
-static const char jeaiii_dd[201] =
- "00010203040506070809101112131415161718192021222324252627282930313233343536373839"
- "40414243444546474849505152535455565758596061626364656667686970717273747576777879"
- "8081828384858687888990919293949596979899";
-static const char jeaiii_fd[201] =
- "0\0" "1\0" "2\0" "3\0" "4\0" "5\0" "6\0" "7\0" "8\0" "9\0"
- "10111213141516171819202122232425262728293031323334353637383940414243444546474849"
- "50515253545556575859606162636465666768697071727374757677787980818283848586878889"
- "90919293949596979899";
-
-simdjson_really_inline void jeaiii_write_dd(char *p, uint64_t k) noexcept {
- std::memcpy(p, &jeaiii_dd[2 * k], 2);
-}
-simdjson_really_inline void jeaiii_write_fd(char *p, uint64_t k) noexcept {
- std::memcpy(p, &jeaiii_fd[2 * k], 2);
-}
-
-// Caller guarantees n < 10^8. Writes 1 to 8 digits.
-simdjson_really_inline char *jeaiii_lt1e8(char *b, uint32_t n) noexcept {
- constexpr uint64_t mask24 = (uint64_t(1) << 24) - 1;
- constexpr uint64_t mask32 = (uint64_t(1) << 32) - 1;
- if (n < 100) {
- jeaiii_write_fd(b, n);
- return n < 10 ? b + 1 : b + 2;
- }
- if (n < 1000000) {
- if (n < 10000) {
- const uint32_t f0 = uint32_t(10 * (1 << 24) / 1e3 + 1) * n;
- jeaiii_write_fd(b, f0 >> 24);
- b -= n < 1000;
- const uint32_t f2 = uint32_t(f0 & mask24) * 100;
- jeaiii_write_dd(b + 2, f2 >> 24);
- return b + 4;
- }
- const uint64_t f0 = uint64_t(10 * (1ull << 32) / 1e5 + 1) * n;
- jeaiii_write_fd(b, f0 >> 32);
- b -= n < 100000;
- const uint64_t f2 = (f0 & mask32) * 100;
- jeaiii_write_dd(b + 2, f2 >> 32);
- const uint64_t f4 = (f2 & mask32) * 100;
- jeaiii_write_dd(b + 4, f4 >> 32);
- return b + 6;
- }
- const uint64_t f0 = uint64_t(10 * (1ull << 48) / 1e7 + 1) * n >> 16;
- jeaiii_write_fd(b, f0 >> 32);
- b -= n < 10000000;
- const uint64_t f2 = (f0 & mask32) * 100;
- jeaiii_write_dd(b + 2, f2 >> 32);
- const uint64_t f4 = (f2 & mask32) * 100;
- jeaiii_write_dd(b + 4, f4 >> 32);
- const uint64_t f6 = (f4 & mask32) * 100;
- jeaiii_write_dd(b + 6, f6 >> 32);
- return b + 8;
-}
-
-// Caller guarantees z < 10^8. Always writes exactly 8 digits.
-simdjson_really_inline char *jeaiii_8_digits(char *b, uint32_t z) noexcept {
- constexpr uint64_t mask32 = (uint64_t(1) << 32) - 1;
- const uint64_t f0 = (uint64_t((1ull << 48) / 1e6 + 1) * z >> 16) + 1;
- jeaiii_write_dd(b, f0 >> 32);
- const uint64_t f2 = (f0 & mask32) * 100;
- jeaiii_write_dd(b + 2, f2 >> 32);
- const uint64_t f4 = (f2 & mask32) * 100;
- jeaiii_write_dd(b + 4, f4 >> 32);
- const uint64_t f6 = (f4 & mask32) * 100;
- jeaiii_write_dd(b + 6, f6 >> 32);
- return b + 8;
-}
-
-// Caller guarantees 10^8 <= n < 2^32. Writes 9 or 10 digits.
-simdjson_really_inline char *jeaiii_9_or_10(char *b, uint64_t n) noexcept {
- constexpr uint64_t mask57 = (uint64_t(1) << 57) - 1;
- const uint64_t f0 = uint64_t(10 * (1ull << 57) / 1e9 + 1) * n;
- jeaiii_write_fd(b, f0 >> 57);
- b -= n < 1000000000;
- const uint64_t f2 = (f0 & mask57) * 100;
- jeaiii_write_dd(b + 2, f2 >> 57);
- const uint64_t f4 = (f2 & mask57) * 100;
- jeaiii_write_dd(b + 4, f4 >> 57);
- const uint64_t f6 = (f4 & mask57) * 100;
- jeaiii_write_dd(b + 6, f6 >> 57);
- const uint64_t f8 = (f6 & mask57) * 100;
- jeaiii_write_dd(b + 8, f8 >> 57);
- return b + 10;
-}
-
-simdjson_really_inline char *write_uint_jeaiii(char *b, uint64_t n) noexcept {
- if (n < 100000000) {
- return jeaiii_lt1e8(b, uint32_t(n));
- }
- if (n < (uint64_t(1) << 32)) {
- return jeaiii_9_or_10(b, n);
- }
- // At least 10 digits: the low 8 digits, and 2 to 12 digits above them.
- const uint32_t z = uint32_t(n % 100000000);
- uint64_t u = n / 100000000;
- if (u < 100000000) {
- // u has 2 to 8 digits (if u < 10, n would be below 2^32).
- b = jeaiii_lt1e8(b, uint32_t(u));
- } else if (u < (uint64_t(1) << 32)) {
- b = jeaiii_9_or_10(b, u);
- } else {
- // u has 11 or 12 digits: split off 8 more.
- const uint32_t y = uint32_t(u % 100000000);
- u /= 100000000;
- b = jeaiii_lt1e8(b, uint32_t(u)); // 3 or 4 digits
- b = jeaiii_8_digits(b, y);
- }
- return jeaiii_8_digits(b, z);
-}
+using simdjson::internal::write_uint_jeaiii;
// Writes v at p, which must have to_chars_buffer_size bytes available, and
// returns the end of what was written.
@@ -75835,20 +73938,22 @@ simdjson_inline void string_builder::append(number_type v) noexcept {
std::is_integral<number_type>::value ||
std::is_floating_point<number_type>::value,
"Unsupported number type");
+ static_assert(sizeof(number_type) <= sizeof(uint64_t),
+ "Integers wider than 64 bits are not supported");
// If C++17 is available, we can 'if constexpr' here.
SIMDJSON_IF_CONSTEXPR(std::is_same<number_type, bool>::value) {
if (v) {
constexpr char true_literal[] = "true";
constexpr size_t true_len = sizeof(true_literal) - 1;
if (capacity_check(true_len)) {
- std::memcpy(buffer.get() + position, true_literal, true_len);
+ std::memcpy(buffer_data() + position, true_literal, true_len);
position += true_len;
}
} else {
constexpr char false_literal[] = "false";
constexpr size_t false_len = sizeof(false_literal) - 1;
if (capacity_check(false_len)) {
- std::memcpy(buffer.get() + position, false_literal, false_len);
+ std::memcpy(buffer_data() + position, false_literal, false_len);
position += false_len;
}
}
@@ -75858,9 +73963,9 @@ simdjson_inline void string_builder::append(number_type v) noexcept {
if (capacity_check(max_number_size)) {
using unsigned_type = typename std::make_unsigned<number_type>::type;
char* end = internal::write_uint_jeaiii(
- buffer.get() + position,
+ buffer_data() + position,
static_cast<uint64_t>(static_cast<unsigned_type>(v)));
- position = end - buffer.get();
+ position = end - buffer_data();
}
}
else SIMDJSON_IF_CONSTEXPR(std::is_integral<number_type>::value) {
@@ -75874,11 +73979,11 @@ simdjson_inline void string_builder::append(number_type v) noexcept {
? unsigned_type(0) - static_cast<unsigned_type>(v)
: static_cast<unsigned_type>(v);
// Branchless: always write '-', advance only if negative.
- buffer.get()[position] = '-';
+ buffer_data()[position] = '-';
position += negative;
char* end = internal::write_uint_jeaiii(
- buffer.get() + position, static_cast<uint64_t>(pv));
- position = end - buffer.get();
+ buffer_data() + position, static_cast<uint64_t>(pv));
+ position = end - buffer_data();
}
}
else SIMDJSON_IF_CONSTEXPR(std::is_floating_point<number_type>::value) {
@@ -75894,16 +73999,16 @@ simdjson_inline void string_builder::append(number_type v) noexcept {
constexpr char nan_literal[] = "NaN";
constexpr size_t nan_len = sizeof(nan_literal) - 1;
- std::memcpy(buffer.get() + position, nan_literal, nan_len);
+ std::memcpy(buffer_data() + position, nan_literal, nan_len);
position += nan_len;
} else {
constexpr char inf_literal[] = "Infinity";
constexpr size_t inf_len = sizeof(inf_literal) - 1;
if (v < 0) {
- buffer.get()[position] = '-';
+ buffer_data()[position] = '-';
++position;
}
- std::memcpy(buffer.get() + position, inf_literal, inf_len);
+ std::memcpy(buffer_data() + position, inf_literal, inf_len);
position += inf_len;
}
return;
@@ -75911,9 +74016,9 @@ simdjson_inline void string_builder::append(number_type v) noexcept {
#endif
// We could specialize for float.
- char *end = simdjson::internal::to_chars(buffer.get() + position, nullptr,
+ char *end = simdjson::internal::to_chars(buffer_data() + position, nullptr,
double(v));
- position = end - buffer.get();
+ position = end - buffer_data();
}
}
}
@@ -75927,7 +74032,7 @@ string_builder::escape_and_append(std::string_view input) noexcept {
return;
}
if (capacity_check(6 * input.size())) {
- position += write_string_escaped(input, buffer.get() + position);
+ position += write_string_escaped(input, buffer_data() + position);
}
}
@@ -75940,9 +74045,9 @@ string_builder::escape_and_append_with_quotes(std::string_view input) noexcept {
return;
}
if (capacity_check(2 + 6 * input.size())) {
- buffer.get()[position++] = '"';
- position += write_string_escaped(input, buffer.get() + position);
- buffer.get()[position++] = '"';
+ buffer_data()[position++] = '"';
+ position += write_string_escaped(input, buffer_data() + position);
+ buffer_data()[position++] = '"';
}
}
@@ -75950,10 +74055,10 @@ simdjson_inline void
string_builder::escape_and_append_with_quotes(char input) noexcept {
// escaping might turn a control character into \x00xx so 6 characters.
if (capacity_check(2 + 6 * 1)) {
- buffer.get()[position++] = '"';
+ buffer_data()[position++] = '"';
std::string_view cinput(&input, 1);
- position += write_string_escaped(cinput, buffer.get() + position);
- buffer.get()[position++] = '"';
+ position += write_string_escaped(cinput, buffer_data() + position);
+ buffer_data()[position++] = '"';
}
}
@@ -75983,7 +74088,7 @@ simdjson_inline void string_builder::append_raw(const char *c) noexcept {
simdjson_inline void
string_builder::append_raw(std::string_view input) noexcept {
if (capacity_check(input.size())) {
- std::memcpy(buffer.get() + position, input.data(), input.size());
+ std::memcpy(buffer_data() + position, input.data(), input.size());
position += input.size();
}
}
@@ -75991,7 +74096,7 @@ string_builder::append_raw(std::string_view input) noexcept {
simdjson_inline void string_builder::append_raw(const char *str,
size_t len) noexcept {
if (capacity_check(len)) {
- std::memcpy(buffer.get() + position, str, len);
+ std::memcpy(buffer_data() + position, str, len);
position += len;
}
}
@@ -75999,7 +74104,7 @@ simdjson_inline void string_builder::append_raw(const char *str,
template <size_t N>
simdjson_inline void string_builder::append_raw_n(const char *str) noexcept {
if (capacity_check(N)) {
- std::memcpy(buffer.get() + position, str, N);
+ std::memcpy(buffer_data() + position, str, N);
position += N;
}
}
@@ -76009,7 +74114,7 @@ template <typename Text>
simdjson_inline void string_builder::append_static_string() noexcept {
constexpr auto& padded = constevalutil::static_string<Text>;
if (capacity_check(padded.size())) {
- std::memcpy(buffer.get() + position, padded.data(), padded.size());
+ std::memcpy(buffer_data() + position, padded.data(), padded.size());
position += constevalutil::static_string_size<Text>();
}
}
@@ -76103,54 +74208,54 @@ string_builder::view() const noexcept {
if (!is_valid) {
return simdjson::OUT_OF_CAPACITY;
}
- return std::string_view(buffer.get(), position);
+ return std::string_view(buffer_data(), position);
}
simdjson_inline simdjson_result<const char *> string_builder::c_str() noexcept {
if (capacity_check(1)) {
- buffer.get()[position] = '\0';
- return buffer.get();
+ buffer_data()[position] = '\0';
+ return buffer_data();
}
return simdjson::OUT_OF_CAPACITY;
}
simdjson_inline bool string_builder::validate_unicode() const noexcept {
- return simdjson::validate_utf8(buffer.get(), position);
+ return simdjson::validate_utf8(buffer_data(), position);
}
simdjson_inline void string_builder::start_object() noexcept {
if (capacity_check(1)) {
- buffer.get()[position++] = '{';
+ buffer_data()[position++] = '{';
}
}
simdjson_inline void string_builder::end_object() noexcept {
if (capacity_check(1)) {
- buffer.get()[position++] = '}';
+ buffer_data()[position++] = '}';
}
}
simdjson_inline void string_builder::start_array() noexcept {
if (capacity_check(1)) {
- buffer.get()[position++] = '[';
+ buffer_data()[position++] = '[';
}
}
simdjson_inline void string_builder::end_array() noexcept {
if (capacity_check(1)) {
- buffer.get()[position++] = ']';
+ buffer_data()[position++] = ']';
}
}
simdjson_inline void string_builder::append_comma() noexcept {
if (capacity_check(1)) {
- buffer.get()[position++] = ',';
+ buffer_data()[position++] = ',';
}
}
simdjson_inline void string_builder::append_colon() noexcept {
if (capacity_check(1)) {
- buffer.get()[position++] = ':';
+ buffer_data()[position++] = ':';
}
}
@@ -77201,31 +75306,6 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin
};
}
-struct escaping {
- static constexpr uint32_t BYTES_PROCESSED = 16;
- simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
-
- simdjson_inline bool has_escape() { return escape_bits != 0; }
- simdjson_inline int escape_index() { return trailing_zeroes(escape_bits) / 4; }
-
- uint64_t escape_bits;
-}; // struct escaping
-
-
-
-simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
- static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes");
- simd8<uint8_t> v(src);
- v.store(dst);
- simd8<bool> is_quote = (v == '"');
- simd8<bool> is_backslash = (v == '\\');
- simd8<bool> is_control = (v < 32);
- return {
- (is_backslash | is_quote | is_control).to_bitmask64()
- };
-}
-
-
} // unnamed namespace
} // namespace arm64
@@ -77438,11 +75518,6 @@ protected:
public:
simdjson_inline value_iterator() noexcept = default;
- /**
- * Denote that we're starting a document.
- */
- simdjson_inline void start_document() noexcept;
-
/**
* Skips a non-iterated or partially-iterated JSON value, whether it is a scalar, array or object.
*
@@ -77843,16 +75918,7 @@ protected:
simdjson_warn_unused simdjson_inline error_code start_container(uint8_t start_char, const char *incorrect_type_message, const char *type) noexcept;
simdjson_warn_unused simdjson_inline error_code end_container() noexcept;
- /**
- * Advance to a place expecting a value (increasing depth).
- *
- * @return The current token (the one left behind).
- * @error TAPE_ERROR If the document ended early.
- */
- simdjson_inline simdjson_result<const uint8_t *> advance_to_value() noexcept;
-
simdjson_warn_unused simdjson_inline error_code incorrect_type_error(const char *message) const noexcept;
- simdjson_warn_unused simdjson_inline error_code error_unless_more_tokens(uint32_t tokens=1) const noexcept;
simdjson_inline bool is_at_start() const noexcept;
/**
@@ -77935,6 +76001,7 @@ public:
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
+/* amalgamation skipped (editor-only): #include "simdjson/jsonpathutil.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/deserialize.h" */
/* amalgamation skipped (editor-only): #include <vector> */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
@@ -78788,11 +76855,19 @@ protected:
*/
simdjson_inline simdjson_result<object> start_or_resume_object() noexcept;
+ /**
+ * The recursive step of for_each_at_path_with_wildcard: matches
+ * path[index] at this level and what follows below it.
+ */
+ template <typename Func>
+ inline error_code for_each_at_path_segments(const std::vector<simdjson::internal::json_path_segment> &path, size_t index, Func &callback) noexcept;
+
// simdjson_inline void log_value(const char *type) const noexcept;
// simdjson_inline void log_error(const char *message) const noexcept;
value_iterator iter{};
+ friend class array;
friend class document;
friend class array_iterator;
friend class field;
@@ -79481,6 +77556,7 @@ public:
/**
* Take an input in json containing max_len characters and attempt to copy it over to tmpbuf, a buffer with
* N bytes of capacity. It will return false if N is too small (smaller than max_len) of if it is zero.
+ * Trailing whitespace, commas, and record separators (0x1E) do not count toward max_len.
* The buffer (tmpbuf) is padded with space characters.
*/
simdjson_warn_unused simdjson_inline bool copy_to_buffer(const uint8_t *json, uint32_t max_len, uint8_t *tmpbuf, size_t N) noexcept;
@@ -79513,8 +77589,11 @@ public:
/**
* Updates this json iterator so that it is back at the beginning of the document,
* as if it had just been created.
+ *
+ * An iterator that is no longer alive (e.g., abandoned after a fatal error) has no
+ * document to go back to: it is left as is and its error is returned.
*/
- inline void rewind() noexcept;
+ simdjson_inline error_code rewind() noexcept;
/**
* This checks whether the {,},[,] are balanced so that the document
* ends with proper zero depth. This requires scanning the whole document
@@ -79869,6 +77948,28 @@ public:
private:
+ /**
+ * Returns true if target ends with an odd number of backslashes. The last one
+ * then escapes the quote that follows the matching bytes in the raw string, so
+ * that quote does not end the string. Not inlined: callers check the last byte
+ * first, and keys ending in a backslash are rare.
+ */
+ static simdjson_never_inline bool ends_with_unpaired_backslash(std::string_view target) noexcept {
+ size_t backslash_count{0};
+ while(backslash_count < target.size() && target[target.size() - 1 - backslash_count] == '\\') {
+ backslash_count++;
+ }
+ return backslash_count % 2 == 1;
+ }
+
+ /**
+ * True when a byte-for-byte match must be rejected because target escapes the
+ * closing quote. The last-byte test keeps the counting loop off the common path.
+ */
+ static simdjson_inline bool escapes_trailing_quote(std::string_view target) noexcept {
+ return simdjson_unlikely(!target.empty() && target[target.size() - 1] == '\\')
+ && ends_with_unpaired_backslash(target);
+ }
/**
* This will set the inner pointer to zero, effectively making
@@ -80442,6 +78543,7 @@ public:
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
+/* amalgamation skipped (editor-only): #include "simdjson/jsonpathutil.h" */
/* amalgamation skipped (editor-only): #include <vector> */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
@@ -80634,6 +78736,13 @@ protected:
*/
simdjson_warn_unused simdjson_inline error_code consume() noexcept;
+ /**
+ * The recursive step of for_each_at_path_with_wildcard: matches
+ * path[index] at this level and what follows below it.
+ */
+ template <typename Func>
+ inline error_code for_each_at_path_segments(const std::vector<simdjson::internal::json_path_segment> &path, size_t index, Func &callback) noexcept;
+
/**
* Begin array iteration.
*
@@ -81643,6 +79752,9 @@ public:
* Reset the iterator inside the document instance so we are pointing back at the
* beginning of the document, as if it had just been created. It invalidates all
* values, objects and arrays that you have created so far (including unescaped strings).
+ *
+ * A document that is no longer alive (see is_alive()) cannot be rewound: it is
+ * left as is.
*/
inline void rewind() noexcept;
/**
@@ -81716,6 +79828,8 @@ public:
* - INCORRECT_TYPE if a non-integer is used to access an array
* - INVALID_JSON_POINTER if the JSON pointer is invalid and cannot be parsed
* - SCALAR_DOCUMENT_AS_VALUE if the json_pointer is empty and the document is not a scalar (see is_scalar() function).
+ * - the fatal error (e.g., TAPE_ERROR) that ended an earlier call, as the document can
+ * no longer be rewound (see is_alive()).
*/
simdjson_inline simdjson_result<value> at_pointer(std::string_view json_pointer) noexcept;
@@ -81739,6 +79853,8 @@ public:
* - NO_SUCH_FIELD if a field does not exist in an object
* - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length
* - INCORRECT_TYPE if a non-integer is used to access an array
+ * - the fatal error (e.g., TAPE_ERROR) that ended an earlier call, as the document can
+ * no longer be rewound (see is_alive()).
*/
simdjson_inline simdjson_result<value> at_path(std::string_view json_path) noexcept;
@@ -81765,7 +79881,9 @@ public:
/**
* Consumes the document and returns a string_view instance corresponding to the
* document as represented in JSON. It points inside the original byte array containing
- * the JSON document.
+ * the JSON document. It starts at the first byte of the document and ends at its last
+ * byte: it does not include the white space that follows the document, nor, in a
+ * document_stream, the separators or the documents that follow it.
*/
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
@@ -82092,8 +80210,6 @@ public:
using arm64::implementation_simdjson_result_base<arm64::ondemand::document>::operator*;
using arm64::implementation_simdjson_result_base<arm64::ondemand::document>::operator->;
- template <class T, typename std::enable_if<std::is_same<T, arm64::ondemand::document>::value == false>::type>
- explicit simdjson_inline operator T() noexcept(false);
simdjson_inline operator arm64::ondemand::array() & noexcept(false);
simdjson_inline operator arm64::ondemand::object() & noexcept(false);
explicit simdjson_inline operator uint64_t() noexcept(false);
@@ -82128,6 +80244,8 @@ public:
simdjson_inline simdjson_result<arm64::ondemand::number> get_number() noexcept;
/** @copydoc simdjson_inline std::string_view document::raw_json_token() const noexcept */
simdjson_inline simdjson_result<std::string_view> raw_json_token() noexcept;
+ /** @copydoc simdjson_inline simdjson_result<std::string_view> document::raw_json() noexcept */
+ simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
simdjson_inline simdjson_result<arm64::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
simdjson_inline simdjson_result<arm64::ondemand::value> at_path(std::string_view json_path) noexcept;
@@ -82254,6 +80372,8 @@ public:
simdjson_inline simdjson_result<arm64::ondemand::number> get_number() noexcept;
/** @copydoc simdjson_inline std::string_view document_reference::raw_json_token() const noexcept */
simdjson_inline simdjson_result<std::string_view> raw_json_token() noexcept;
+ /** @copydoc simdjson_inline simdjson_result<std::string_view> document::raw_json() noexcept */
+ simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
simdjson_inline simdjson_result<arm64::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
simdjson_inline simdjson_result<arm64::ondemand::value> at_path(std::string_view json_path) noexcept;
@@ -84215,6 +82335,7 @@ concept key_selector_type = key_selector_detail::is_key_selector<T>::value;
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
+/* amalgamation skipped (editor-only): #include "simdjson/jsonpathutil.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/key_selector.h" */
/* amalgamation skipped (editor-only): #include <vector> */
/* amalgamation skipped (editor-only): #if SIMDJSON_STATIC_REFLECTION && SIMDJSON_SUPPORTS_CONCEPTS */
@@ -84759,6 +82880,13 @@ protected:
simdjson_warn_unused simdjson_inline error_code find_field_raw(const std::string_view key) noexcept;
+ /**
+ * The recursive step of for_each_at_path_with_wildcard: matches
+ * path[index] at this level and what follows below it.
+ */
+ template <typename Func>
+ inline error_code for_each_at_path_segments(const std::vector<simdjson::internal::json_path_segment> &path, size_t index, Func &callback) noexcept;
+
value_iterator iter{};
#if SIMDJSON_DEVELOPMENT_CHECKS
bool locked{false};
@@ -85623,6 +83751,14 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) {
std::is_default_constructible_v<element_type>,
"The specified type inside the unique_ptr must default constructible.");
+ // A null pointer is serialized as null.
+ bool is_null_value;
+ SIMDJSON_TRY( val.is_null().get(is_null_value) );
+ if (is_null_value) {
+ out.reset();
+ return SUCCESS;
+ }
+
// Own the allocation before get(): a user tag_invoke may throw.
std::unique_ptr<element_type> ptr(new (std::nothrow) element_type());
if (!ptr) {
@@ -85671,6 +83807,8 @@ constexpr bool user_defined_type = (std::is_class_v<T>
#endif // SIMDJSON_SUPPORTS_CHAR8_T
&& !concepts::optional_type<T> &&
!concepts::appendable_containers<T>
+// Smart pointers are handled by the generic smart-pointer tag_invoke.
+&& !concepts::smart_pointer<T>
// simdjson's own types (array, object, value, raw_json_string, number,
// document, document_reference) have dedicated get<T>() specializations and
// must never go through reflection.
@@ -86263,251 +84401,8 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept {
#endif
}
-template <typename simdjson_value, typename T>
- requires(user_defined_type<std::remove_cvref_t<T>>)
-error_code tag_invoke(deserialize_tag, simdjson_value &val, std::unique_ptr<T> &out) noexcept(false) {
- std::unique_ptr<T> ptr(new (std::nothrow) T());
- if (!ptr) {
- return MEMALLOC;
- }
- SIMDJSON_TRY(val.get(*ptr));
- out = std::move(ptr);
- return SUCCESS;
-}
-
-template <typename simdjson_value, typename T>
- requires(user_defined_type<std::remove_cvref_t<T>>)
-error_code tag_invoke(deserialize_tag, simdjson_value &val, std::shared_ptr<T> &out) noexcept(false) {
- std::shared_ptr<T> ptr(new (std::nothrow) T());
- if (!ptr) {
- return MEMALLOC;
- }
- SIMDJSON_TRY(val.get(*ptr));
- out = std::move(ptr);
- return SUCCESS;
-}
-
#endif // SIMDJSON_STATIC_REFLECTION
-////////////////////////////////////////
-// Unique pointers
-////////////////////////////////////////
-error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<bool> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_unique<bool>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_bool().get(*out));
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<int64_t> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_unique<int64_t>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_int64().get(*out));
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<uint64_t> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_unique<uint64_t>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_uint64().get(*out));
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<double> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_unique<double>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_double().get(*out));
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<std::string_view> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_unique<std::string_view>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_string().get(*out));
- return SUCCESS;
-}
-
-
-////////////////////////////////////////
-// Shared pointers
-////////////////////////////////////////
-error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<bool> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_shared<bool>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_bool().get(*out));
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<int64_t> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_shared<int64_t>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_int64().get(*out));
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<uint64_t> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_shared<uint64_t>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_uint64().get(*out));
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<double> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_shared<double>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_double().get(*out));
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<std::string_view> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_shared<std::string_view>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_string().get(*out));
- return SUCCESS;
-}
-
-
-////////////////////////////////////////
-// Explicit optional specializations
-////////////////////////////////////////
-
-////////////////////////////////////////
-// Explicit smart pointer specializations for string and int types
-////////////////////////////////////////
-error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<std::string> &out) noexcept {
- // Check if the value is null
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset(); // Set to nullptr
- return SUCCESS;
- }
-
- if (!out) {
- out = std::make_unique<std::string>();
- }
- std::string_view str;
- SIMDJSON_TRY(val.get_string().get(str));
- *out = std::string{str};
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<std::string> &out) noexcept {
- // Check if the value is null
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset(); // Set to nullptr
- return SUCCESS;
- }
-
- if (!out) {
- out = std::make_shared<std::string>();
- }
- std::string_view str;
- SIMDJSON_TRY(val.get_string().get(str));
- *out = std::string{str};
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<int> &out) noexcept {
- // Check if the value is null
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset(); // Set to nullptr
- return SUCCESS;
- }
-
- if (!out) {
- out = std::make_unique<int>();
- }
- int64_t temp;
- SIMDJSON_TRY(val.get_int64().get(temp));
- *out = static_cast<int>(temp);
- return SUCCESS;
-}
-
} // namespace simdjson
#endif // SIMDJSON_ONDEMAND_DESERIALIZE_H
@@ -86622,10 +84517,9 @@ simdjson_inline simdjson_result<std::string_view> array::raw_json() noexcept {
const uint8_t * starting_point{iter.peek_start()};
auto error = consume();
if(error) { return error; }
- // After 'consume()', we could be left pointing just beyond the document, but that
- // is ok because we are not going to dereference the final pointer position, we just
- // use it to compute the length in bytes.
- const uint8_t * final_point{iter._json_iter->unsafe_pointer()};
+ // After 'consume()', the last token consumed is the closing bracket: we end on it
+ // rather than on the next token, which may follow white space.
+ const uint8_t * final_point{iter._json_iter->token.peek(-1) + 1};
return std::string_view(reinterpret_cast<const char*>(starting_point), size_t(final_point - starting_point));
}
@@ -86709,12 +84603,23 @@ template <typename Func>
template <typename Func>
#endif
inline error_code array::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
- auto result_pair = get_next_key_and_json_path(json_path);
- std::string_view key = result_pair.first;
- std::string_view remaining_path = result_pair.second;
+ // The path is split once: every element reached by a wildcard sees the same
+ // remaining path, so re-parsing it at each one would be quadratic.
+ const std::vector<internal::json_path_segment> path = internal::split_json_path(json_path);
+ // A path that cannot be parsed matches nothing, whatever the document: say
+ // so rather than letting the walk skip it as a mismatch below a wildcard.
+ if (path.back().key.empty()) {
+ return INVALID_JSON_POINTER;
+ }
+ return for_each_at_path_segments(path, 0, callback);
+}
+
+template <typename Func>
+inline error_code array::for_each_at_path_segments(const std::vector<internal::json_path_segment> &path, size_t index, Func &callback) noexcept {
+ std::string_view key = path[index].key;
+ const bool last = index + 1 == path.size();
// Empty key means the path segment could not be parsed (including malformed
- // bracket-quoted keys). Without this check, an empty key is treated as index
- // 0 and remaining_path may be unchanged, causing infinite recursion.
+ // bracket-quoted keys).
if (key.empty()) {
return INVALID_JSON_POINTER;
}
@@ -86723,11 +84628,11 @@ inline error_code array::for_each_at_path_with_wildcard(std::string_view json_pa
for(auto element: *this) {
value val;
SIMDJSON_TRY(element.get(val));
- if (remaining_path.empty()) {
+ if (last) {
callback(val);
} else {
// An element that does not match the rest of the path is skipped.
- error_code err = val.for_each_at_path_with_wildcard(remaining_path, callback);
+ error_code err = val.for_each_at_path_segments(path, index + 1, callback);
if (err && !internal::is_matching_failure_error(err)) { return err; }
}
}
@@ -86746,16 +84651,16 @@ inline error_code array::for_each_at_path_with_wildcard(std::string_view json_pa
value val;
if (!error) { error = at(idx).get(val); }
if (error) {
- if (error == INDEX_OUT_OF_BOUNDS && internal::json_path_has_wildcard(remaining_path)) {
+ if (error == INDEX_OUT_OF_BOUNDS && path[index].wildcard_follows) {
return SUCCESS;
}
return error;
}
- if (remaining_path.empty()){
+ if (last){
callback(val);
return SUCCESS;
} else {
- return val.for_each_at_path_with_wildcard(remaining_path, callback);
+ return val.for_each_at_path_segments(path, index + 1, callback);
}
}
}
@@ -87413,6 +85318,19 @@ template <typename Func>
template <typename Func>
#endif
inline error_code value::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
+ // The path is split once: every value reached by a wildcard sees the same
+ // remaining path, so re-parsing it at each one would be quadratic.
+ const std::vector<internal::json_path_segment> path = internal::split_json_path(json_path);
+ // A path that cannot be parsed matches nothing, whatever the document: say
+ // so rather than letting the walk skip it as a mismatch below a wildcard.
+ if (path.back().key.empty()) {
+ return INVALID_JSON_POINTER;
+ }
+ return for_each_at_path_segments(path, 0, callback);
+}
+
+template <typename Func>
+inline error_code value::for_each_at_path_segments(const std::vector<internal::json_path_segment> &path, size_t index, Func &callback) noexcept {
// Every recursive step of for_each_at_path_with_wildcard goes through this
// function, and each one descends one level into the document. A path with
// many segments applied to a deeply nested document would otherwise recurse
@@ -87421,10 +85339,16 @@ inline error_code value::for_each_at_path_with_wildcard(std::string_view json_pa
json_type t;
SIMDJSON_TRY(type().get(t));
switch (t) {
- case json_type::array:
- return (*this).get_array().for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
- case json_type::object:
- return (*this).get_object().for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
+ case json_type::array: {
+ array a;
+ SIMDJSON_TRY(get_array().get(a));
+ return a.for_each_at_path_segments(path, index, callback);
+ }
+ case json_type::object: {
+ object o;
+ SIMDJSON_TRY(get_object().get(o));
+ return o.for_each_at_path_segments(path, index, callback);
+ }
default:
return INVALID_JSON_POINTER;
}
@@ -87797,7 +85721,8 @@ simdjson_inline document document::start(json_iterator &&iter) noexcept {
}
inline void document::rewind() noexcept {
- iter.rewind();
+ // A document that is no longer alive is left as it is (see is_alive()).
+ static_cast<void>(iter.rewind());
}
inline std::string document::to_debug_string() noexcept {
@@ -88097,7 +86022,13 @@ simdjson_warn_unused simdjson_inline error_code document::consume() noexcept {
auto error = is_scalar().get(scalar);
if(error) { return error; }
if(scalar) {
- iter.return_current_and_advance();
+ // Step over the scalar and leave depth 0, as skip_child() does: otherwise a
+ // document_stream takes the next document as the rest of this one. At depth
+ // 0 the scalar has already been consumed (e.g., by get_int64()).
+ if(iter.depth() > 0) {
+ iter.return_current_and_advance();
+ iter.ascend_to(0);
+ }
return SUCCESS;
}
error = iter.skip_child(0);
@@ -88110,10 +86041,36 @@ simdjson_inline simdjson_result<std::string_view> document::raw_json() noexcept
const uint8_t * starting_point{_iter.peek_start()};
auto error = consume();
if(error) { return error; }
- // After 'consume()', we could be left pointing just beyond the document, but that
- // is ok because we are not going to dereference the final pointer position, we just
- // use it to compute the length in bytes.
- const uint8_t * final_point{iter.unsafe_pointer()};
+ // After 'consume()', the iterator points at the token after the document. In a
+ // document_stream, that is the next document, past white space and separators, or
+ // the end of the input when a truncated document follows. We end on the last byte
+ // of this document instead.
+ const uint8_t * final_point;
+ if(*starting_point == '{' || *starting_point == '[') {
+ // The last token consumed is the closing brace or bracket.
+ final_point = iter.token.peek(-1) + 1;
+ } else {
+ // A scalar is a single token: a string ends at its closing quote, anything else
+ // at the first white space, structural character, quote, RS (0x1E) or NUL.
+ const uint8_t * next_token{iter.unsafe_pointer()};
+ final_point = starting_point + 1;
+ if(*starting_point == '"') {
+ while(final_point < next_token) {
+ uint8_t c = *final_point++;
+ if(c == '\\') {
+ final_point++;
+ } else if(c == '"') {
+ break;
+ }
+ }
+ if(final_point > next_token) { final_point = next_token; }
+ } else {
+ while(final_point < next_token && !jsoncharutils::is_structural_or_whitespace(*final_point)
+ && *final_point != '"' && *final_point != 0x1E && *final_point != 0) {
+ final_point++;
+ }
+ }
+ }
return std::string_view(reinterpret_cast<const char*>(starting_point), size_t(final_point - starting_point));
}
@@ -88160,7 +86117,9 @@ simdjson_inline simdjson_result<std::string_view> document::raw_json_token() noe
}
simdjson_inline simdjson_result<value> document::at_pointer(std::string_view json_pointer) noexcept {
- rewind(); // Rewind the document each time at_pointer is called
+ // Rewind the document each time at_pointer is called. After a fatal error,
+ // the document cannot be walked again: we report the error instead.
+ SIMDJSON_TRY(iter.rewind());
if (json_pointer.empty()) {
return this->get_value();
}
@@ -88178,7 +86137,9 @@ simdjson_inline simdjson_result<value> document::at_pointer(std::string_view jso
}
simdjson_inline simdjson_result<value> document::at_path(std::string_view json_path) noexcept {
- rewind(); // Rewind the document each time at_pointer is called
+ // Rewind the document each time at_path is called. After a fatal error,
+ // the document cannot be walked again: we report the error instead.
+ SIMDJSON_TRY(iter.rewind());
if (json_path.empty()) {
return this->get_value();
}
@@ -88201,7 +86162,9 @@ template <typename Func>
template <typename Func>
#endif
simdjson_inline error_code document::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
- rewind(); // Rewind the document each time for_each_at_path_with_wildcard is called
+ // Rewind the document each time for_each_at_path_with_wildcard is called. After
+ // a fatal error, the document cannot be walked again: we report the error instead.
+ SIMDJSON_TRY(iter.rewind());
if (json_path.empty()) {
return INVALID_JSON_POINTER;
}
@@ -88539,11 +86502,6 @@ simdjson_inline simdjson_result<arm64::ondemand::number> simdjson_result<arm64::
#if SIMDJSON_EXCEPTIONS
-template <class T, typename std::enable_if<std::is_same<T, arm64::ondemand::document>::value == false>::type>
-simdjson_inline simdjson_result<arm64::ondemand::document>::operator T() noexcept(false) {
- if (error()) { throw simdjson_error(error()); }
- return first;
-}
simdjson_inline simdjson_result<arm64::ondemand::document>::operator arm64::ondemand::array() & noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
@@ -88604,6 +86562,11 @@ simdjson_inline simdjson_result<std::string_view> simdjson_result<arm64::ondeman
return first.raw_json_token();
}
+simdjson_inline simdjson_result<std::string_view> simdjson_result<arm64::ondemand::document>::raw_json() noexcept {
+ if (error()) { return error(); }
+ return first.raw_json();
+}
+
simdjson_inline simdjson_result<arm64::ondemand::value> simdjson_result<arm64::ondemand::document>::at_pointer(std::string_view json_pointer) noexcept {
if (error()) { return error(); }
return first.at_pointer(json_pointer);
@@ -89090,6 +87053,11 @@ simdjson_inline simdjson_result<std::string_view> simdjson_result<arm64::ondeman
return first.raw_json_token();
}
+simdjson_inline simdjson_result<std::string_view> simdjson_result<arm64::ondemand::document_reference>::raw_json() noexcept {
+ if (error()) { return error(); }
+ return first.raw_json();
+}
+
simdjson_inline simdjson_result<arm64::ondemand::value> simdjson_result<arm64::ondemand::document_reference>::at_pointer(std::string_view json_pointer) noexcept {
if (error()) { return error(); }
return first.at_pointer(json_pointer);
@@ -89481,6 +87449,13 @@ simdjson_inline bool document_stream::skip_to_delimiter(uint8_t delimiter) noexc
}
inline void document_stream::next_document() noexcept {
+ // Iteration stops at the first document that met a fatal error. The error
+ // may have abandoned the document's iterator, which then no longer has a
+ // parser to walk the rest of the document with.
+ if (!doc.iter.is_alive() || doc.iter.error) {
+ error = doc.iter.error ? doc.iter.error : TAPE_ERROR;
+ return;
+ }
// A delimiter that cannot occur inside a document tells us where the current
// one ends, so we can jump there instead of walking every structural. Only
// valid while the iterator is still inside the document: a consumed document
@@ -89630,7 +87605,10 @@ simdjson_inline std::string_view document_stream::iterator::source() const noexc
cur_struct_index++;
}
- while (cur_struct_index <= static_cast<int64_t>(stream->parser->implementation->n_structural_indexes)) {
+ // The structural index at n_structural_indexes is the end of the batch, not
+ // a token: it must not be read as a closing bracket.
+ const int64_t n_structural_indexes = static_cast<int64_t>(stream->parser->implementation->n_structural_indexes);
+ while (cur_struct_index < n_structural_indexes) {
switch (stream->buf[stream->batch_start + stream->parser->implementation->structural_indexes[cur_struct_index]]) {
case '{': case '[':
depth++;
@@ -89642,6 +87620,11 @@ simdjson_inline std::string_view document_stream::iterator::source() const noexc
if (depth == 0) { break; }
cur_struct_index++;
}
+ // An invalid last document such as "[1 2" is never closed: it runs to the
+ // end of the batch, and not one byte past it.
+ if (cur_struct_index >= n_structural_indexes) {
+ return std::string_view(reinterpret_cast<const char*>(stream->buf) + current_index(), stream->batch_start + stream->parser->implementation->structural_indexes[cur_struct_index] - current_index());
+ }
return std::string_view(reinterpret_cast<const char*>(stream->buf) + current_index(), stream->parser->implementation->structural_indexes[cur_struct_index] - current_index() + stream->batch_start + 1);;
}
@@ -89658,7 +87641,12 @@ inline void document_stream::load_from_stage1_thread() noexcept {
// enough memory to swap to, as well.
std::swap(stage1_thread_parser,*parser);
error = stage1_thread_error;
- if (error) { return; }
+ // An EMPTY batch (no complete document in it, e.g. a run of white space at
+ // least batch_size long) is not the end of the stream: next() moves on to
+ // the batch after it, so queue that batch now as we would after a success.
+ // Returning here would leave no job for the next call, which then swaps
+ // the stale previous batch back in and ends the stream early.
+ if (error && error != EMPTY) { return; }
// If there's anything left, start the stage 1 thread!
if (next_batch_start() < len) {
@@ -89770,12 +87758,14 @@ simdjson_inline raw_json_string field::key() const noexcept {
simdjson_inline std::string_view field::key_raw_json_token() const noexcept {
SIMDJSON_ASSUME(first.buf != nullptr); // We would like to call .alive() by Visual Studio won't let us.
- return std::string_view(reinterpret_cast<const char*>(first.buf-1), second.iter._json_iter->token.peek(-1) - first.buf + 1);
+ // The ':' is the token just before the value. Use the value's start position
+ // rather than the current position, which moves once the value is consumed.
+ return std::string_view(reinterpret_cast<const char*>(first.buf-1), second.iter._json_iter->token.peek(second.iter.start_position() - 1) - first.buf + 1);
}
simdjson_inline std::string_view field::escaped_key() const noexcept {
SIMDJSON_ASSUME(first.buf != nullptr); // We would like to call .alive() by Visual Studio won't let us.
- auto end_quote = second.iter._json_iter->token.peek(-1);
+ auto end_quote = second.iter._json_iter->token.peek(second.iter.start_position() - 1);
while(*end_quote != '"') end_quote--;
return std::string_view(reinterpret_cast<const char*>(first.buf), end_quote - first.buf);
}
@@ -89946,11 +87936,14 @@ simdjson_inline json_iterator::json_iterator(const uint8_t *buf, ondemand::parse
}
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
-inline void json_iterator::rewind() noexcept {
+simdjson_inline error_code json_iterator::rewind() noexcept {
+ // abandon() let go of the parser: the iterator cannot be walked again.
+ if (!is_alive()) { return error ? error : UNINITIALIZED; }
token.set_position( root_position() );
logger::log_headers(); // We start again
_string_buf_loc = parser->string_buf.get();
_depth = 1;
+ return SUCCESS;
}
inline bool json_iterator::balanced() const noexcept {
@@ -89974,6 +87967,18 @@ inline bool json_iterator::balanced() const noexcept {
}
+// Depth change of each structural character when skipping: +1 for [ and {, -1 for ] and }.
+static constexpr int8_t skip_depth_delta[256] = {
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,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,
+};
+
// GCC 7 warns when the first line of this function is inlined away into oblivion due to the caller
// relating depth and parent_depth, which is a desired effect. The warning does not show up if the
// skip_child() function is not marked inline).
@@ -90029,7 +88034,30 @@ simdjson_warn_unused simdjson_inline error_code json_iterator::skip_child(depth_
break;
}
- // Now that we've considered the first value, we only increment/decrement for arrays/objects
+ // Now that we've considered the first value, we only increment/decrement for arrays/objects.
+ //
+ // Without logging, look up the depth change of each token in a table instead of branching
+ // on it: the next byte is hard to predict, so a switch mispredicts often. The loop also keeps
+ // the position and depth in locals, because the byte loads are char loads that may alias
+ // token._position and _depth, which would force a store of both on every token.
+ if (!logger::LOG_ENABLED) {
+ token_position pos = token.position();
+ const token_position end = end_position();
+ const uint8_t *const json = token.buf;
+ depth_t depth = _depth;
+ while (pos < end) {
+ depth += skip_depth_delta[json[*pos++]];
+ if (depth <= parent_depth) {
+ token.set_position(pos);
+ _depth = depth;
+ return SUCCESS;
+ }
+ }
+ token.set_position(pos);
+ _depth = depth;
+ return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "not enough close braces");
+ }
+
while (position() < end_position()) {
switch (*return_current_and_advance()) {
case '[': case '{':
@@ -90314,6 +88342,17 @@ simdjson_warn_unused simdjson_inline error_code json_iterator::optional_error(er
simdjson_warn_unused simdjson_inline bool json_iterator::copy_to_buffer(const uint8_t *json, uint32_t max_len, uint8_t *tmpbuf, size_t N) noexcept {
// This function is not expected to be called in performance-sensitive settings.
+ // peek_root_length() stops at the next structural index. Stage 1 omits the
+ // comma or RS (0x1E) that separates stream documents, so that byte and the
+ // whitespace around it remain in the span. RS is not a valid number finisher.
+ while (max_len > 0) {
+ const uint8_t last = json[max_len - 1];
+ if (last != ' ' && last != '\t' && last != '\n' && last != '\r' &&
+ last != ',' && last != 0x1E) {
+ break;
+ }
+ max_len--;
+ }
// Let us guard against silly cases:
if((N < max_len) || (N == 0)) { return false; }
// Copy to the buffer.
@@ -90955,7 +88994,9 @@ simdjson_inline simdjson_result<std::string_view> object::raw_json() noexcept {
const uint8_t * starting_point{iter.peek_start()};
auto error = consume();
if(error) { return error; }
- const uint8_t * final_point{iter._json_iter->peek()};
+ // After 'consume()', the last token consumed is the closing brace: we end on it
+ // rather than on the next token, which may follow white space.
+ const uint8_t * final_point{iter._json_iter->token.peek(-1) + 1};
return std::string_view(reinterpret_cast<const char*>(starting_point), size_t(final_point - starting_point));
}
@@ -91048,9 +89089,21 @@ template <typename Func>
template <typename Func>
#endif
inline error_code object::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
- auto result_pair = get_next_key_and_json_path(json_path);
- std::string_view key = result_pair.first;
- std::string_view remaining_path = result_pair.second;
+ // The path is split once: every field reached by a wildcard sees the same
+ // remaining path, so re-parsing it at each one would be quadratic.
+ const std::vector<internal::json_path_segment> path = internal::split_json_path(json_path);
+ // A path that cannot be parsed matches nothing, whatever the document: say
+ // so rather than letting the walk skip it as a mismatch below a wildcard.
+ if (path.back().key.empty()) {
+ return INVALID_JSON_POINTER;
+ }
+ return for_each_at_path_segments(path, 0, callback);
+}
+
+template <typename Func>
+inline error_code object::for_each_at_path_segments(const std::vector<internal::json_path_segment> &path, size_t index, Func &callback) noexcept {
+ std::string_view key = path[index].key;
+ const bool last = index + 1 == path.size();
// Empty key means the path segment could not be parsed (including malformed
// bracket-quoted keys). Match DOM at_path_with_wildcard and avoid treating
// "no progress" as a recursive lookup of "".
@@ -91063,11 +89116,11 @@ inline error_code object::for_each_at_path_with_wildcard(std::string_view json_p
for (auto field : *this) {
value val;
SIMDJSON_TRY(field.value().get(val));
- if (remaining_path.empty()) {
+ if (last) {
callback(val);
} else {
// A field that does not match the rest of the path is skipped.
- error_code err = val.for_each_at_path_with_wildcard(remaining_path, callback);
+ error_code err = val.for_each_at_path_segments(path, index + 1, callback);
if (err && !internal::is_matching_failure_error(err)) { return err; }
}
}
@@ -91076,17 +89129,17 @@ inline error_code object::for_each_at_path_with_wildcard(std::string_view json_p
value val;
error_code error = find_field(key).get(val);
if (error) {
- if (error == NO_SUCH_FIELD && internal::json_path_has_wildcard(remaining_path)) {
+ if (error == NO_SUCH_FIELD && path[index].wildcard_follows) {
return SUCCESS;
}
return error;
}
- if (remaining_path.empty()) {
+ if (last) {
callback(val);
return SUCCESS;
} else {
- return val.for_each_at_path_with_wildcard(remaining_path, callback);
+ return val.for_each_at_path_segments(path, index + 1, callback);
}
}
}
@@ -91739,6 +89792,7 @@ simdjson_inline simdjson_warn_unused bool parser::string_buffer_overflow(const u
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(padded_string_view json) & noexcept {
if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
+ json.use_trailing_whitespace_as_padding();
json.remove_utf8_bom();
_document_len = json.length();
@@ -91756,6 +89810,7 @@ simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(p
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate_allow_incomplete_json(padded_string_view json) & noexcept {
if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
+ json.use_trailing_whitespace_as_padding();
json.remove_utf8_bom();
_document_len = json.length();
@@ -91811,6 +89866,7 @@ simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(c
simdjson_warn_unused simdjson_inline simdjson_result<json_iterator> parser::iterate_raw(padded_string_view json) & noexcept {
if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
+ json.use_trailing_whitespace_as_padding();
json.remove_utf8_bom();
@@ -91837,6 +89893,7 @@ inline simdjson_result<document_stream> parser::iterate_many(const char *buf, si
}
inline simdjson_result<document_stream> parser::iterate_many(padded_string_view s, size_t batch_size) noexcept {
if (!s.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
+ s.use_trailing_whitespace_as_padding();
return iterate_many(s.data(), s.length(), batch_size);
}
inline simdjson_result<document_stream> parser::iterate_many(const padded_string &s, size_t batch_size) noexcept {
@@ -91872,6 +89929,7 @@ inline simdjson_result<document_stream> parser::iterate_many(const char *buf, si
}
inline simdjson_result<document_stream> parser::iterate_many(padded_string_view s, size_t batch_size, bool allow_comma_separated) noexcept {
if (!s.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
+ s.use_trailing_whitespace_as_padding();
return iterate_many(s.data(), s.length(), batch_size, allow_comma_separated);
}
inline simdjson_result<document_stream> parser::iterate_many(const padded_string &s, size_t batch_size, bool allow_comma_separated) noexcept {
@@ -91917,6 +89975,7 @@ inline simdjson_result<document_stream> parser::iterate_many(const char *buf, si
}
inline simdjson_result<document_stream> parser::iterate_many(padded_string_view s, size_t batch_size, stream_format format) noexcept {
if (!s.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
+ s.use_trailing_whitespace_as_padding();
return iterate_many(s.data(), s.length(), batch_size, format);
}
inline simdjson_result<document_stream> parser::iterate_many(const std::string &s, size_t batch_size, stream_format format) noexcept {
@@ -92071,17 +90130,18 @@ simdjson_inline bool raw_json_string::is_free_from_unescaped_quote(const char* t
return true;
}
-
simdjson_inline bool raw_json_string::unsafe_is_equal(size_t length, std::string_view target) const noexcept {
// If we are going to call memcmp, then we must know something about the length of the raw_json_string.
- return (length >= target.size()) && (raw()[target.size()] == '"') && !memcmp(raw(), target.data(), target.size());
+ return (length >= target.size()) && (raw()[target.size()] == '"') && !memcmp(raw(), target.data(), target.size())
+ && !escapes_trailing_quote(target);
}
simdjson_inline bool raw_json_string::unsafe_is_equal(std::string_view target) const noexcept {
// Assumptions: does not contain unescaped quote characters("), and
// the raw content is quote terminated within a valid JSON string.
if(target.size() <= SIMDJSON_PADDING) {
- return (raw()[target.size()] == '"') && !memcmp(raw(), target.data(), target.size());
+ return (raw()[target.size()] == '"') && !memcmp(raw(), target.data(), target.size())
+ && !escapes_trailing_quote(target);
}
// Past SIMDJSON_PADDING we can no longer rely on the padding to keep the
// comparison in bounds, so we must stop at the quote terminating the key.
@@ -92107,7 +90167,7 @@ simdjson_inline bool raw_json_string::is_equal(std::string_view target) const no
escaping = false;
}
}
- if(r[pos] != '"') { return false; }
+ if((r[pos] != '"') || escaping) { return false; }
return true;
}
@@ -92120,7 +90180,7 @@ simdjson_inline bool raw_json_string::unsafe_is_equal(const char * target) const
for(;target[pos];pos++) {
if(r[pos] != target[pos]) { return false; }
}
- if(r[pos] != '"') { return false; }
+ if((r[pos] != '"') || escapes_trailing_quote(std::string_view(target, pos))) { return false; }
return true;
}
@@ -92145,7 +90205,7 @@ simdjson_inline bool raw_json_string::is_equal(const char* target) const noexcep
escaping = false;
}
}
- if(r[pos] != '"') { return false; }
+ if((r[pos] != '"') || escaping) { return false; }
return true;
}
@@ -92257,7 +90317,11 @@ simdjson_inline uint32_t token_iterator::peek_index(token_position position) con
return *position;
}
simdjson_inline uint32_t token_iterator::peek_length(token_position position) const noexcept {
- return *(position+1) - *position;
+ // In the last batch of a document_stream, the index past the end of the
+ // structural indexes holds the start of the truncated bytes (see
+ // document_stream::truncated_bytes()), which can come before the end of the
+ // input: a token at the end is then empty.
+ return *(position+1) > *position ? *(position+1) - *position : 0;
}
simdjson_inline uint32_t token_iterator::peek_root_length(token_position position) const noexcept {
@@ -92272,7 +90336,7 @@ simdjson_inline uint32_t token_iterator::peek_index(int32_t delta) const noexcep
return *(_position+delta);
}
simdjson_inline uint32_t token_iterator::peek_length(int32_t delta) const noexcept {
- return *(_position+delta+1) - *(_position+delta);
+ return peek_length(_position+delta);
}
simdjson_inline token_position token_iterator::position() const noexcept {
@@ -92331,6 +90395,8 @@ simdjson_inline simdjson_result<arm64::ondemand::token_iterator>::simdjson_resul
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
+#include <cstring> // std::memset (zeroing the root-number scratch padding)
+
namespace simdjson {
namespace arm64 {
namespace ondemand {
@@ -92377,6 +90443,10 @@ simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::start
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::started_object() noexcept {
assert_at_container_start();
#if SIMDJSON_DEVELOPMENT_CHECKS
+ // The development checks record the start of every open container in a
+ // buffer of max_depth() entries: a document nested deeper than that is
+ // rejected, as the DOM parser does.
+ if (size_t(_depth) >= _json_iter->parser->max_depth()) { return report_error(DEPTH_ERROR, "Exceeded max depth!"); }
_json_iter->set_start_position(_depth, start_position());
#endif
if (*_json_iter->peek() == '}') {
@@ -92786,6 +90856,10 @@ inline std::string value_iterator::to_string() const noexcept {
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::started_array() noexcept {
assert_at_container_start();
+#if SIMDJSON_DEVELOPMENT_CHECKS
+ // See started_object().
+ if (size_t(_depth) >= _json_iter->parser->max_depth()) { return report_error(DEPTH_ERROR, "Exceeded max depth!"); }
+#endif
if (*_json_iter->peek() == ']') {
logger::log_value(*_json_iter, "empty array");
_json_iter->return_current_and_advance();
@@ -92991,8 +91065,10 @@ simdjson_inline simdjson_result<arm64::number_type> value_iterator::get_root_num
// Per https://www.exploringbinary.com/maximum-number-of-decimal-digits-in-binary-floating-point-numbers/,
// 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest
// number: -0.<fraction>e-308.
- uint8_t tmpbuf[1074+8+1+1];
- tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
+ // +SIMDJSON_PADDING: the digit scan reads up to 7 bytes past the terminator.
+ uint8_t tmpbuf[1074+8+1+1+SIMDJSON_PADDING];
+ // Null terminate, and zero the padding as allocate_padded_buffer does.
+ std::memset(tmpbuf+1074+8+1, 0, 1+SIMDJSON_PADDING);
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
if(numberparsing::check_if_integer(json, max_len)) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
@@ -93013,8 +91089,10 @@ simdjson_inline simdjson_result<number> value_iterator::get_root_number(bool che
// 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest
// number: -0.<fraction>e-308.
// NOTE: the current approach doesn't work for very big integer numbers containing more than 1074 digits.
- uint8_t tmpbuf[1074+8+1+1];
- tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
+ // +SIMDJSON_PADDING: the digit scan reads up to 7 bytes past the terminator.
+ uint8_t tmpbuf[1074+8+1+1+SIMDJSON_PADDING];
+ // Null terminate, and zero the padding as allocate_padded_buffer does.
+ std::memset(tmpbuf+1074+8+1, 0, 1+SIMDJSON_PADDING);
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
if(numberparsing::check_if_integer(json, max_len)) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
@@ -93084,10 +91162,10 @@ simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> value_iterator::g
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> value_iterator::get_root_uint64_in_string(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("uint64");
- uint8_t tmpbuf[20+1+1]{}; // <20 digits> is the longest possible unsigned integer
- tmpbuf[20+1] = '\0'; // make sure that buffer is always null terminated.
- if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 20+1)) {
- logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 20 characters");
+ uint8_t tmpbuf[20+2+1]{}; // "<20 digits>" is the longest possible unsigned integer
+ tmpbuf[20+2] = '\0'; // make sure that buffer is always null terminated.
+ if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 20+2)) {
+ logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 22 characters");
return NUMBER_ERROR;
}
auto result = numberparsing::parse_unsigned_in_string(tmpbuf);
@@ -93117,10 +91195,10 @@ simdjson_warn_unused simdjson_inline simdjson_result<int64_t> value_iterator::ge
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> value_iterator::get_root_int64_in_string(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("int64");
- uint8_t tmpbuf[20+1+1]; // -<19 digits> is the longest possible integer
- tmpbuf[20+1] = '\0'; // make sure that buffer is always null terminated.
- if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 20+1)) {
- logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 20 characters");
+ uint8_t tmpbuf[20+2+1]; // "-<19 digits>" is the longest possible integer
+ tmpbuf[20+2] = '\0'; // make sure that buffer is always null terminated.
+ if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 20+2)) {
+ logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 22 characters");
return NUMBER_ERROR;
}
@@ -93137,8 +91215,10 @@ simdjson_warn_unused simdjson_inline simdjson_result<double> value_iterator::get
// Per https://www.exploringbinary.com/maximum-number-of-decimal-digits-in-binary-floating-point-numbers/,
// 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest
// number: -0.<fraction>e-308.
- uint8_t tmpbuf[1074+8+1+1]; // +1 for null termination.
- tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
+ // +SIMDJSON_PADDING: the digit scan reads up to 7 bytes past the terminator.
+ uint8_t tmpbuf[1074+8+1+1+SIMDJSON_PADDING]; // +1 for null termination.
+ // Null terminate, and zero the padding as allocate_padded_buffer does.
+ std::memset(tmpbuf+1074+8+1, 0, 1+SIMDJSON_PADDING);
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters");
return NUMBER_ERROR;
@@ -93157,8 +91237,10 @@ simdjson_warn_unused simdjson_inline simdjson_result<double> value_iterator::get
// Per https://www.exploringbinary.com/maximum-number-of-decimal-digits-in-binary-floating-point-numbers/,
// 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest
// number: -0.<fraction>e-308.
- uint8_t tmpbuf[1074+8+1+1]; // +1 for null termination.
- tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
+ // +SIMDJSON_PADDING: the digit scan reads up to 7 bytes past the terminator.
+ uint8_t tmpbuf[1074+8+1+1+SIMDJSON_PADDING]; // +1 for null termination.
+ // Null terminate, and zero the padding as allocate_padded_buffer does.
+ std::memset(tmpbuf+1074+8+1, 0, 1+SIMDJSON_PADDING);
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters");
return NUMBER_ERROR;
@@ -93177,8 +91259,10 @@ simdjson_warn_unused simdjson_inline simdjson_result<float> value_iterator::get_
// We use the same buffer size as get_root_double: the number of significant
// digits that matter is smaller for binary32, but the JSON document may still
// spell out a long number that we must parse (and round) faithfully.
- uint8_t tmpbuf[1074+8+1+1]; // +1 for null termination.
- tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
+ // +SIMDJSON_PADDING: the digit scan reads up to 7 bytes past the terminator.
+ uint8_t tmpbuf[1074+8+1+1+SIMDJSON_PADDING]; // +1 for null termination.
+ // Null terminate, and zero the padding as allocate_padded_buffer does.
+ std::memset(tmpbuf+1074+8+1, 0, 1+SIMDJSON_PADDING);
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters");
return NUMBER_ERROR;
@@ -93194,8 +91278,10 @@ simdjson_warn_unused simdjson_inline simdjson_result<float> value_iterator::get_
simdjson_warn_unused simdjson_inline simdjson_result<float> value_iterator::get_root_float_in_string(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("float");
- uint8_t tmpbuf[1074+8+1+1]; // +1 for null termination.
- tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
+ // +SIMDJSON_PADDING: the digit scan reads up to 7 bytes past the terminator.
+ uint8_t tmpbuf[1074+8+1+1+SIMDJSON_PADDING]; // +1 for null termination.
+ // Null terminate, and zero the padding as allocate_padded_buffer does.
+ std::memset(tmpbuf+1074+8+1, 0, 1+SIMDJSON_PADDING);
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters");
return NUMBER_ERROR;
@@ -93216,7 +91302,7 @@ simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::get_r
// "true" and "false" are 4 and 5 characters long, respectively.
bool value_true = (max_len >= 4 && !atomparsing::str4ncmp(json, "true") &&
(max_len == 4 || jsoncharutils::is_structural_or_whitespace(json[4])));
- bool value_false = (max_len >= 5 && !atomparsing::str4ncmp(json, "false") &&
+ bool value_false = (max_len >= 5 && !atomparsing::str4ncmp(json, "fals") && json[4] == 'e' &&
(max_len == 5 || jsoncharutils::is_structural_or_whitespace(json[5])));
if(value_true == false && value_false == false) { return incorrect_type_error("Not a boolean"); }
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
@@ -93856,49 +91942,6 @@ namespace json_path {
using ::simdjson::arm64::ondemand::value;
-// Path step types
-enum class step_type {
- field, // .field_name or ["field_name"]
- array_index // [index]
-};
-
-// Represents a single step in a JSON path
-template<std::size_t N>
-struct path_step {
- step_type type;
- char key[N]; // Field name (empty for array indices)
- std::size_t index; // Array index (0 for field access)
-
- constexpr path_step(step_type t, const char (&k)[N], std::size_t idx = 0)
- : type(t), index(idx) {
- for (std::size_t i = 0; i < N; ++i) {
- key[i] = k[i];
- }
- }
-
- constexpr std::string_view key_view() const {
- return {key, N - 1};
- }
-};
-
-// Helper to create field step
-template<std::size_t N>
-consteval auto make_field_step(const char (&name)[N]) {
- return path_step<N>(step_type::field, name, 0);
-}
-
-// Helper to create array index step
-consteval auto make_index_step(std::size_t idx) {
- return path_step<1>(step_type::array_index, "", idx);
-}
-
-// Parse state for compile-time JSON path parsing
-struct parse_result {
- bool success;
- std::size_t pos;
- std::string_view error_msg;
-};
-
// Compile-time JSON path parser
// Supports subset: .field, ["field"], [index], nested combinations
template<constevalutil::fixed_string Path>
@@ -93912,77 +91955,6 @@ struct json_path_parser {
}
return 0;
}
-
- // Count the number of steps in the path at compile time
- static consteval std::size_t count_steps() {
- std::size_t count = 0;
- std::size_t i = skip_root();
-
- while (i < path_str.size()) {
- if (path_str[i] == '.') {
- // Field access: .field
- ++i;
- if (i >= path_str.size()) break;
-
- // Skip field name
- while (i < path_str.size() && path_str[i] != '.' && path_str[i] != '[') {
- ++i;
- }
- ++count;
- } else if (path_str[i] == '[') {
- // Array or bracket notation
- ++i;
- if (i >= path_str.size()) break;
-
- if (path_str[i] == '"' || path_str[i] == '\'') {
- // Field access: ["field"] or ['field']
- char quote = path_str[i];
- ++i;
- while (i < path_str.size() && path_str[i] != quote) {
- ++i;
- }
- if (i < path_str.size()) ++i; // skip closing quote
- if (i < path_str.size() && path_str[i] == ']') ++i;
- } else {
- // Array index: [0], [123]
- while (i < path_str.size() && path_str[i] != ']') {
- ++i;
- }
- if (i < path_str.size()) ++i; // skip ]
- }
- ++count;
- } else {
- ++i;
- }
- }
-
- return count;
- }
-
- // Parse a field name at compile time
- static consteval std::size_t parse_field_name(std::size_t start, char* out, std::size_t max_len) {
- std::size_t len = 0;
- std::size_t i = start;
-
- while (i < path_str.size() && path_str[i] != '.' && path_str[i] != '[' && len < max_len - 1) {
- out[len++] = path_str[i++];
- }
- out[len] = '\0';
- return i;
- }
-
- // Parse an array index at compile time
- static consteval std::pair<std::size_t, std::size_t> parse_array_index(std::size_t start) {
- std::size_t index = 0;
- std::size_t i = start;
-
- while (i < path_str.size() && path_str[i] >= '0' && path_str[i] <= '9') {
- index = index * 10 + (path_str[i] - '0');
- ++i;
- }
-
- return {i, index};
- }
};
// Compile-time path accessor generator
@@ -93991,7 +91963,6 @@ struct path_accessor {
using value = ::simdjson::arm64::ondemand::value;
static constexpr auto parser = json_path_parser<Path>();
- static constexpr std::size_t num_steps = parser.count_steps();
static constexpr std::string_view path_view = Path.view();
// Compile-time accessor generation
@@ -94106,93 +92077,6 @@ private:
return current_type;
}
-private:
- // Walk path and extract directly into final field using compile-time reflection
- template<std::meta::info CurrentType, std::size_t PathPos, typename TargetType>
- static inline error_code extract_with_reflection(simdjson_result<value> current, TargetType& target_ref) noexcept {
- if (current.error()) return current.error();
-
- // Base case: end of path - extract into target
- if constexpr (PathPos >= path_view.size()) {
- return current.get(target_ref);
- }
- // Field access: .field_name
- else if constexpr (path_view[PathPos] == '.') {
- constexpr auto field_info = parse_next_field(PathPos);
- constexpr std::string_view field_name = std::get<0>(field_info);
- constexpr std::size_t next_pos = std::get<1>(field_info);
-
- constexpr auto member_info = find_member_by_name(CurrentType, field_name);
- static_assert(member_info != ^^void, "Field not found in struct");
-
- constexpr auto member_type = std::meta::type_of(member_info);
-
- auto obj_result = current.get_object();
- if (obj_result.error()) return obj_result.error();
- auto obj = obj_result.value_unsafe();
- auto field_value = obj.find_field_unordered(field_name);
-
- if constexpr (next_pos >= path_view.size()) {
- return field_value.get(target_ref);
- } else {
- return extract_with_reflection<member_type, next_pos>(field_value, target_ref);
- }
- }
- // Bracket notation: [index] or ["field"]
- else if constexpr (path_view[PathPos] == '[') {
- constexpr auto bracket_info = parse_bracket(PathPos);
- constexpr bool is_field = std::get<0>(bracket_info);
- constexpr std::size_t next_pos = std::get<2>(bracket_info);
-
- if constexpr (is_field) {
- constexpr std::string_view field_name = std::get<1>(bracket_info);
- constexpr auto member_info = find_member_by_name(CurrentType, field_name);
- static_assert(member_info != ^^void, "Field not found in struct");
- constexpr auto member_type = std::meta::type_of(member_info);
-
- auto obj_result = current.get_object();
- if (obj_result.error()) return obj_result.error();
- auto obj = obj_result.value_unsafe();
- auto field_value = obj.find_field_unordered(field_name);
-
- if constexpr (next_pos >= path_view.size()) {
- return field_value.get(target_ref);
- } else {
- return extract_with_reflection<member_type, next_pos>(field_value, target_ref);
- }
- } else {
- constexpr std::size_t index = std::get<3>(bracket_info);
- constexpr auto elem_type = get_element_type_reflected(CurrentType);
- static_assert(elem_type != ^^void, "Could not determine array element type");
-
- auto arr_result = current.get_array();
- if (arr_result.error()) return arr_result.error();
- auto arr = arr_result.value_unsafe();
- auto elem_value = arr.at(index);
-
- if constexpr (next_pos >= path_view.size()) {
- return elem_value.get(target_ref);
- } else {
- return extract_with_reflection<elem_type, next_pos>(elem_value, target_ref);
- }
- }
- }
- // Skip unexpected characters and continue
- else {
- return extract_with_reflection<CurrentType, PathPos + 1>(current, target_ref);
- }
- }
-
- // Find member by name in reflected type
- static consteval std::meta::info find_member_by_name(std::meta::info type_refl, std::string_view name) {
- auto members = std::meta::nonstatic_data_members_of(type_refl, std::meta::access_context::unchecked());
- for (auto mem : members) {
- if (std::meta::identifier_of(mem) == name) {
- return mem;
- }
- }
- }
-
// Generate compile-time accessor code by walking the path
template<std::size_t PathPos>
static inline simdjson_result<value> access_impl(simdjson_result<value> current) noexcept {
@@ -94322,55 +92206,6 @@ public:
}
private:
- // Check if type has member with given name
- template<typename Type>
- static consteval bool has_member(std::string_view member_name) {
- constexpr auto members = std::meta::nonstatic_data_members_of(^^Type, std::meta::access_context::unchecked());
- for (auto mem : members) {
- if (std::meta::identifier_of(mem) == member_name) {
- return true;
- }
- }
- return false;
- }
-
- // Get type of member by name
- template<typename Type>
- static consteval auto get_member_type(std::string_view member_name) {
- constexpr auto members = std::meta::nonstatic_data_members_of(^^Type, std::meta::access_context::unchecked());
- for (auto mem : members) {
- if (std::meta::identifier_of(mem) == member_name) {
- return std::meta::type_of(mem);
- }
- }
- return ^^void;
- }
-
- // Check if non-reflected type is array-like
- template<typename Type>
- static consteval bool is_container_type() {
- using BaseType = std::remove_cvref_t<Type>;
- if constexpr (requires { typename BaseType::value_type; }) {
- return true;
- }
- if constexpr (std::is_array_v<BaseType>) {
- return true;
- }
- return false;
- }
-
- // Extract element type from non-reflected container
- template<typename Type>
- using extract_element_type = std::conditional_t<
- requires { typename std::remove_cvref_t<Type>::value_type; },
- typename std::remove_cvref_t<Type>::value_type,
- std::conditional_t<
- std::is_array_v<std::remove_cvref_t<Type>>,
- std::remove_extent_t<std::remove_cvref_t<Type>>,
- void
- >
- >;
-
// Validate path matches struct definition
static consteval bool validate_path() {
if constexpr (!std::is_class_v<T>) {
@@ -94870,23 +92705,6 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin
}
-struct escaping {
- static constexpr uint32_t BYTES_PROCESSED = 1;
- simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
-
- simdjson_inline bool has_escape() { return escape_bits; }
- simdjson_inline int escape_index() { return 0; }
-
- bool escape_bits;
-}; // struct escaping
-
-
-
-simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
- dst[0] = src[0];
- return { (src[0] == '\\') || (src[0] == '"') || (src[0] < 32) };
-}
-
} // unnamed namespace
} // namespace fallback
} // namespace simdjson
@@ -95185,11 +93003,6 @@ protected:
public:
simdjson_inline value_iterator() noexcept = default;
- /**
- * Denote that we're starting a document.
- */
- simdjson_inline void start_document() noexcept;
-
/**
* Skips a non-iterated or partially-iterated JSON value, whether it is a scalar, array or object.
*
@@ -95590,16 +93403,7 @@ protected:
simdjson_warn_unused simdjson_inline error_code start_container(uint8_t start_char, const char *incorrect_type_message, const char *type) noexcept;
simdjson_warn_unused simdjson_inline error_code end_container() noexcept;
- /**
- * Advance to a place expecting a value (increasing depth).
- *
- * @return The current token (the one left behind).
- * @error TAPE_ERROR If the document ended early.
- */
- simdjson_inline simdjson_result<const uint8_t *> advance_to_value() noexcept;
-
simdjson_warn_unused simdjson_inline error_code incorrect_type_error(const char *message) const noexcept;
- simdjson_warn_unused simdjson_inline error_code error_unless_more_tokens(uint32_t tokens=1) const noexcept;
simdjson_inline bool is_at_start() const noexcept;
/**
@@ -95682,6 +93486,7 @@ public:
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
+/* amalgamation skipped (editor-only): #include "simdjson/jsonpathutil.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/deserialize.h" */
/* amalgamation skipped (editor-only): #include <vector> */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
@@ -96535,11 +94340,19 @@ protected:
*/
simdjson_inline simdjson_result<object> start_or_resume_object() noexcept;
+ /**
+ * The recursive step of for_each_at_path_with_wildcard: matches
+ * path[index] at this level and what follows below it.
+ */
+ template <typename Func>
+ inline error_code for_each_at_path_segments(const std::vector<simdjson::internal::json_path_segment> &path, size_t index, Func &callback) noexcept;
+
// simdjson_inline void log_value(const char *type) const noexcept;
// simdjson_inline void log_error(const char *message) const noexcept;
value_iterator iter{};
+ friend class array;
friend class document;
friend class array_iterator;
friend class field;
@@ -97228,6 +95041,7 @@ public:
/**
* Take an input in json containing max_len characters and attempt to copy it over to tmpbuf, a buffer with
* N bytes of capacity. It will return false if N is too small (smaller than max_len) of if it is zero.
+ * Trailing whitespace, commas, and record separators (0x1E) do not count toward max_len.
* The buffer (tmpbuf) is padded with space characters.
*/
simdjson_warn_unused simdjson_inline bool copy_to_buffer(const uint8_t *json, uint32_t max_len, uint8_t *tmpbuf, size_t N) noexcept;
@@ -97260,8 +95074,11 @@ public:
/**
* Updates this json iterator so that it is back at the beginning of the document,
* as if it had just been created.
+ *
+ * An iterator that is no longer alive (e.g., abandoned after a fatal error) has no
+ * document to go back to: it is left as is and its error is returned.
*/
- inline void rewind() noexcept;
+ simdjson_inline error_code rewind() noexcept;
/**
* This checks whether the {,},[,] are balanced so that the document
* ends with proper zero depth. This requires scanning the whole document
@@ -97616,6 +95433,28 @@ public:
private:
+ /**
+ * Returns true if target ends with an odd number of backslashes. The last one
+ * then escapes the quote that follows the matching bytes in the raw string, so
+ * that quote does not end the string. Not inlined: callers check the last byte
+ * first, and keys ending in a backslash are rare.
+ */
+ static simdjson_never_inline bool ends_with_unpaired_backslash(std::string_view target) noexcept {
+ size_t backslash_count{0};
+ while(backslash_count < target.size() && target[target.size() - 1 - backslash_count] == '\\') {
+ backslash_count++;
+ }
+ return backslash_count % 2 == 1;
+ }
+
+ /**
+ * True when a byte-for-byte match must be rejected because target escapes the
+ * closing quote. The last-byte test keeps the counting loop off the common path.
+ */
+ static simdjson_inline bool escapes_trailing_quote(std::string_view target) noexcept {
+ return simdjson_unlikely(!target.empty() && target[target.size() - 1] == '\\')
+ && ends_with_unpaired_backslash(target);
+ }
/**
* This will set the inner pointer to zero, effectively making
@@ -98189,6 +96028,7 @@ public:
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
+/* amalgamation skipped (editor-only): #include "simdjson/jsonpathutil.h" */
/* amalgamation skipped (editor-only): #include <vector> */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
@@ -98381,6 +96221,13 @@ protected:
*/
simdjson_warn_unused simdjson_inline error_code consume() noexcept;
+ /**
+ * The recursive step of for_each_at_path_with_wildcard: matches
+ * path[index] at this level and what follows below it.
+ */
+ template <typename Func>
+ inline error_code for_each_at_path_segments(const std::vector<simdjson::internal::json_path_segment> &path, size_t index, Func &callback) noexcept;
+
/**
* Begin array iteration.
*
@@ -99390,6 +97237,9 @@ public:
* Reset the iterator inside the document instance so we are pointing back at the
* beginning of the document, as if it had just been created. It invalidates all
* values, objects and arrays that you have created so far (including unescaped strings).
+ *
+ * A document that is no longer alive (see is_alive()) cannot be rewound: it is
+ * left as is.
*/
inline void rewind() noexcept;
/**
@@ -99463,6 +97313,8 @@ public:
* - INCORRECT_TYPE if a non-integer is used to access an array
* - INVALID_JSON_POINTER if the JSON pointer is invalid and cannot be parsed
* - SCALAR_DOCUMENT_AS_VALUE if the json_pointer is empty and the document is not a scalar (see is_scalar() function).
+ * - the fatal error (e.g., TAPE_ERROR) that ended an earlier call, as the document can
+ * no longer be rewound (see is_alive()).
*/
simdjson_inline simdjson_result<value> at_pointer(std::string_view json_pointer) noexcept;
@@ -99486,6 +97338,8 @@ public:
* - NO_SUCH_FIELD if a field does not exist in an object
* - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length
* - INCORRECT_TYPE if a non-integer is used to access an array
+ * - the fatal error (e.g., TAPE_ERROR) that ended an earlier call, as the document can
+ * no longer be rewound (see is_alive()).
*/
simdjson_inline simdjson_result<value> at_path(std::string_view json_path) noexcept;
@@ -99512,7 +97366,9 @@ public:
/**
* Consumes the document and returns a string_view instance corresponding to the
* document as represented in JSON. It points inside the original byte array containing
- * the JSON document.
+ * the JSON document. It starts at the first byte of the document and ends at its last
+ * byte: it does not include the white space that follows the document, nor, in a
+ * document_stream, the separators or the documents that follow it.
*/
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
@@ -99839,134 +97695,134 @@ public:
using fallback::implementation_simdjson_result_base<fallback::ondemand::document>::operator*;
using fallback::implementation_simdjson_result_base<fallback::ondemand::document>::operator->;
- template <class T, typename std::enable_if<std::is_same<T, fallback::ondemand::document>::value == false>::type>
- explicit simdjson_inline operator T() noexcept(false);
- simdjson_inline operator fallback::ondemand::array() & noexcept(false);
- simdjson_inline operator fallback::ondemand::object() & noexcept(false);
- explicit simdjson_inline operator uint64_t() noexcept(false);
- explicit simdjson_inline operator int64_t() noexcept(false);
- explicit simdjson_inline operator double() noexcept(false);
- explicit simdjson_inline operator std::string_view() noexcept(false);
- explicit simdjson_inline operator fallback::ondemand::raw_json_string() noexcept(false);
- explicit simdjson_inline operator bool() noexcept(false);
- simdjson_inline operator fallback::ondemand::value() noexcept(false);
-#endif
- simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
- simdjson_inline simdjson_result<size_t> count_fields() & noexcept;
- simdjson_inline simdjson_result<fallback::ondemand::value> at(size_t index) & noexcept;
- simdjson_inline simdjson_result<fallback::ondemand::array_iterator> begin() & noexcept;
- simdjson_inline simdjson_result<fallback::ondemand::array_iterator> end() & noexcept;
- simdjson_inline simdjson_result<fallback::ondemand::value> find_field(std::string_view key) & noexcept;
- simdjson_inline simdjson_result<fallback::ondemand::value> find_field(const char *key) & noexcept;
- simdjson_inline simdjson_result<fallback::ondemand::value> operator[](std::string_view key) & noexcept;
- simdjson_inline simdjson_result<fallback::ondemand::value> operator[](const char *key) & noexcept;
- simdjson_result<fallback::ondemand::value> operator[](int) & noexcept = delete;
- simdjson_inline simdjson_result<fallback::ondemand::value> find_field_unordered(std::string_view key) & noexcept;
- simdjson_inline simdjson_result<fallback::ondemand::value> find_field_unordered(const char *key) & noexcept;
- simdjson_inline simdjson_result<fallback::ondemand::json_type> type() noexcept;
- simdjson_inline simdjson_result<bool> is_scalar() noexcept;
- simdjson_inline simdjson_result<bool> is_string() noexcept;
- simdjson_inline simdjson_result<const char *> current_location() noexcept;
- simdjson_inline int32_t current_depth() const noexcept;
- simdjson_inline bool at_end() const noexcept;
- simdjson_inline bool is_negative() noexcept;
- simdjson_inline simdjson_result<bool> is_integer() noexcept;
- simdjson_inline simdjson_result<fallback::number_type> get_number_type() noexcept;
- simdjson_inline simdjson_result<fallback::ondemand::number> get_number() noexcept;
- /** @copydoc simdjson_inline std::string_view document::raw_json_token() const noexcept */
- simdjson_inline simdjson_result<std::string_view> raw_json_token() noexcept;
-
- simdjson_inline simdjson_result<fallback::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
- simdjson_inline simdjson_result<fallback::ondemand::value> at_path(std::string_view json_path) noexcept;
-#if SIMDJSON_SUPPORTS_CONCEPTS
- template <typename Func>
- requires std::invocable<Func, fallback::ondemand::value>
-#else
- template <typename Func>
-#endif
- simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
-#if SIMDJSON_STATIC_REFLECTION
- template<constevalutil::fixed_string... FieldNames, typename T>
- requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
- simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept;
-#endif // SIMDJSON_STATIC_REFLECTION
-};
-
-
-} // namespace simdjson
-
-
-
-namespace simdjson {
-
-template<>
-struct simdjson_result<fallback::ondemand::document_reference> : public fallback::implementation_simdjson_result_base<fallback::ondemand::document_reference> {
-public:
- simdjson_inline simdjson_result(fallback::ondemand::document_reference value, error_code error) noexcept;
- simdjson_inline simdjson_result() noexcept = default;
- simdjson_inline error_code rewind() noexcept;
-
- simdjson_inline simdjson_result<fallback::ondemand::array> get_array() & noexcept;
- simdjson_inline simdjson_result<fallback::ondemand::object> get_object() & noexcept;
- simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
- simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
- simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
- simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
- simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
- simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
- simdjson_inline simdjson_result<uint16_t> get_uint16() noexcept;
- simdjson_inline simdjson_result<int16_t> get_int16() noexcept;
- simdjson_inline simdjson_result<uint8_t> get_uint8() noexcept;
- simdjson_inline simdjson_result<int8_t> get_int8() noexcept;
- simdjson_inline simdjson_result<double> get_double() noexcept;
- simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
- simdjson_inline simdjson_result<float> get_float() noexcept;
- simdjson_inline simdjson_result<float> get_float_in_string() noexcept;
-#if SIMDJSON_SUPPORTS_FLOAT32_T
- simdjson_inline simdjson_result<std::float32_t> get_float32() noexcept;
-#endif // SIMDJSON_SUPPORTS_FLOAT32_T
-#if SIMDJSON_SUPPORTS_FLOAT64_T
- simdjson_inline simdjson_result<std::float64_t> get_float64() noexcept;
-#endif // SIMDJSON_SUPPORTS_FLOAT64_T
- simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
-#if SIMDJSON_SUPPORTS_CHAR8_T
- simdjson_inline simdjson_result<std::u8string_view> get_u8string(bool allow_replacement = false) noexcept;
-#endif // SIMDJSON_SUPPORTS_CHAR8_T
- template <typename string_type>
- simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
- simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
- simdjson_inline simdjson_result<fallback::ondemand::raw_json_string> get_raw_json_string() noexcept;
- simdjson_inline simdjson_result<bool> get_bool() noexcept;
- simdjson_inline simdjson_result<fallback::ondemand::value> get_value() noexcept;
- simdjson_inline simdjson_result<bool> is_null() noexcept;
-
- template<typename T> simdjson_inline simdjson_result<T> get() &
-#if SIMDJSON_SUPPORTS_CONCEPTS
- noexcept(nothrow_gettable<T, fallback::ondemand::document_reference>);
-#else
- noexcept;
-#endif
- template<typename T> simdjson_inline simdjson_result<T> get() &&
-#if SIMDJSON_SUPPORTS_CONCEPTS
- noexcept(nothrow_gettable<T, fallback::ondemand::document_reference>);
-#else
- noexcept;
-#endif
-
- template<typename T> simdjson_inline error_code get(T &out) &
-#if SIMDJSON_SUPPORTS_CONCEPTS
- noexcept(nothrow_gettable<T, fallback::ondemand::document_reference>);
-#else
- noexcept;
-#endif
- template<typename T> simdjson_inline error_code get(T &out) &&
-#if SIMDJSON_SUPPORTS_CONCEPTS
- noexcept(nothrow_gettable<T, fallback::ondemand::document_reference>);
-#else
- noexcept;
-#endif
-#if SIMDJSON_EXCEPTIONS
- template <class T>
+ simdjson_inline operator fallback::ondemand::array() & noexcept(false);
+ simdjson_inline operator fallback::ondemand::object() & noexcept(false);
+ explicit simdjson_inline operator uint64_t() noexcept(false);
+ explicit simdjson_inline operator int64_t() noexcept(false);
+ explicit simdjson_inline operator double() noexcept(false);
+ explicit simdjson_inline operator std::string_view() noexcept(false);
+ explicit simdjson_inline operator fallback::ondemand::raw_json_string() noexcept(false);
+ explicit simdjson_inline operator bool() noexcept(false);
+ simdjson_inline operator fallback::ondemand::value() noexcept(false);
+#endif
+ simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
+ simdjson_inline simdjson_result<size_t> count_fields() & noexcept;
+ simdjson_inline simdjson_result<fallback::ondemand::value> at(size_t index) & noexcept;
+ simdjson_inline simdjson_result<fallback::ondemand::array_iterator> begin() & noexcept;
+ simdjson_inline simdjson_result<fallback::ondemand::array_iterator> end() & noexcept;
+ simdjson_inline simdjson_result<fallback::ondemand::value> find_field(std::string_view key) & noexcept;
+ simdjson_inline simdjson_result<fallback::ondemand::value> find_field(const char *key) & noexcept;
+ simdjson_inline simdjson_result<fallback::ondemand::value> operator[](std::string_view key) & noexcept;
+ simdjson_inline simdjson_result<fallback::ondemand::value> operator[](const char *key) & noexcept;
+ simdjson_result<fallback::ondemand::value> operator[](int) & noexcept = delete;
+ simdjson_inline simdjson_result<fallback::ondemand::value> find_field_unordered(std::string_view key) & noexcept;
+ simdjson_inline simdjson_result<fallback::ondemand::value> find_field_unordered(const char *key) & noexcept;
+ simdjson_inline simdjson_result<fallback::ondemand::json_type> type() noexcept;
+ simdjson_inline simdjson_result<bool> is_scalar() noexcept;
+ simdjson_inline simdjson_result<bool> is_string() noexcept;
+ simdjson_inline simdjson_result<const char *> current_location() noexcept;
+ simdjson_inline int32_t current_depth() const noexcept;
+ simdjson_inline bool at_end() const noexcept;
+ simdjson_inline bool is_negative() noexcept;
+ simdjson_inline simdjson_result<bool> is_integer() noexcept;
+ simdjson_inline simdjson_result<fallback::number_type> get_number_type() noexcept;
+ simdjson_inline simdjson_result<fallback::ondemand::number> get_number() noexcept;
+ /** @copydoc simdjson_inline std::string_view document::raw_json_token() const noexcept */
+ simdjson_inline simdjson_result<std::string_view> raw_json_token() noexcept;
+ /** @copydoc simdjson_inline simdjson_result<std::string_view> document::raw_json() noexcept */
+ simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
+
+ simdjson_inline simdjson_result<fallback::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
+ simdjson_inline simdjson_result<fallback::ondemand::value> at_path(std::string_view json_path) noexcept;
+#if SIMDJSON_SUPPORTS_CONCEPTS
+ template <typename Func>
+ requires std::invocable<Func, fallback::ondemand::value>
+#else
+ template <typename Func>
+#endif
+ simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
+#if SIMDJSON_STATIC_REFLECTION
+ template<constevalutil::fixed_string... FieldNames, typename T>
+ requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
+ simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept;
+#endif // SIMDJSON_STATIC_REFLECTION
+};
+
+
+} // namespace simdjson
+
+
+
+namespace simdjson {
+
+template<>
+struct simdjson_result<fallback::ondemand::document_reference> : public fallback::implementation_simdjson_result_base<fallback::ondemand::document_reference> {
+public:
+ simdjson_inline simdjson_result(fallback::ondemand::document_reference value, error_code error) noexcept;
+ simdjson_inline simdjson_result() noexcept = default;
+ simdjson_inline error_code rewind() noexcept;
+
+ simdjson_inline simdjson_result<fallback::ondemand::array> get_array() & noexcept;
+ simdjson_inline simdjson_result<fallback::ondemand::object> get_object() & noexcept;
+ simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
+ simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
+ simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
+ simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
+ simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
+ simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
+ simdjson_inline simdjson_result<uint16_t> get_uint16() noexcept;
+ simdjson_inline simdjson_result<int16_t> get_int16() noexcept;
+ simdjson_inline simdjson_result<uint8_t> get_uint8() noexcept;
+ simdjson_inline simdjson_result<int8_t> get_int8() noexcept;
+ simdjson_inline simdjson_result<double> get_double() noexcept;
+ simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
+ simdjson_inline simdjson_result<float> get_float() noexcept;
+ simdjson_inline simdjson_result<float> get_float_in_string() noexcept;
+#if SIMDJSON_SUPPORTS_FLOAT32_T
+ simdjson_inline simdjson_result<std::float32_t> get_float32() noexcept;
+#endif // SIMDJSON_SUPPORTS_FLOAT32_T
+#if SIMDJSON_SUPPORTS_FLOAT64_T
+ simdjson_inline simdjson_result<std::float64_t> get_float64() noexcept;
+#endif // SIMDJSON_SUPPORTS_FLOAT64_T
+ simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
+#if SIMDJSON_SUPPORTS_CHAR8_T
+ simdjson_inline simdjson_result<std::u8string_view> get_u8string(bool allow_replacement = false) noexcept;
+#endif // SIMDJSON_SUPPORTS_CHAR8_T
+ template <typename string_type>
+ simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
+ simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
+ simdjson_inline simdjson_result<fallback::ondemand::raw_json_string> get_raw_json_string() noexcept;
+ simdjson_inline simdjson_result<bool> get_bool() noexcept;
+ simdjson_inline simdjson_result<fallback::ondemand::value> get_value() noexcept;
+ simdjson_inline simdjson_result<bool> is_null() noexcept;
+
+ template<typename T> simdjson_inline simdjson_result<T> get() &
+#if SIMDJSON_SUPPORTS_CONCEPTS
+ noexcept(nothrow_gettable<T, fallback::ondemand::document_reference>);
+#else
+ noexcept;
+#endif
+ template<typename T> simdjson_inline simdjson_result<T> get() &&
+#if SIMDJSON_SUPPORTS_CONCEPTS
+ noexcept(nothrow_gettable<T, fallback::ondemand::document_reference>);
+#else
+ noexcept;
+#endif
+
+ template<typename T> simdjson_inline error_code get(T &out) &
+#if SIMDJSON_SUPPORTS_CONCEPTS
+ noexcept(nothrow_gettable<T, fallback::ondemand::document_reference>);
+#else
+ noexcept;
+#endif
+ template<typename T> simdjson_inline error_code get(T &out) &&
+#if SIMDJSON_SUPPORTS_CONCEPTS
+ noexcept(nothrow_gettable<T, fallback::ondemand::document_reference>);
+#else
+ noexcept;
+#endif
+#if SIMDJSON_EXCEPTIONS
+ template <class T>
explicit simdjson_inline operator T() noexcept(false);
simdjson_inline operator fallback::ondemand::array() & noexcept(false);
simdjson_inline operator fallback::ondemand::object() & noexcept(false);
@@ -100001,6 +97857,8 @@ public:
simdjson_inline simdjson_result<fallback::ondemand::number> get_number() noexcept;
/** @copydoc simdjson_inline std::string_view document_reference::raw_json_token() const noexcept */
simdjson_inline simdjson_result<std::string_view> raw_json_token() noexcept;
+ /** @copydoc simdjson_inline simdjson_result<std::string_view> document::raw_json() noexcept */
+ simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
simdjson_inline simdjson_result<fallback::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
simdjson_inline simdjson_result<fallback::ondemand::value> at_path(std::string_view json_path) noexcept;
@@ -101962,6 +99820,7 @@ concept key_selector_type = key_selector_detail::is_key_selector<T>::value;
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
+/* amalgamation skipped (editor-only): #include "simdjson/jsonpathutil.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/key_selector.h" */
/* amalgamation skipped (editor-only): #include <vector> */
/* amalgamation skipped (editor-only): #if SIMDJSON_STATIC_REFLECTION && SIMDJSON_SUPPORTS_CONCEPTS */
@@ -102506,6 +100365,13 @@ protected:
simdjson_warn_unused simdjson_inline error_code find_field_raw(const std::string_view key) noexcept;
+ /**
+ * The recursive step of for_each_at_path_with_wildcard: matches
+ * path[index] at this level and what follows below it.
+ */
+ template <typename Func>
+ inline error_code for_each_at_path_segments(const std::vector<simdjson::internal::json_path_segment> &path, size_t index, Func &callback) noexcept;
+
value_iterator iter{};
#if SIMDJSON_DEVELOPMENT_CHECKS
bool locked{false};
@@ -103370,6 +101236,14 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) {
std::is_default_constructible_v<element_type>,
"The specified type inside the unique_ptr must default constructible.");
+ // A null pointer is serialized as null.
+ bool is_null_value;
+ SIMDJSON_TRY( val.is_null().get(is_null_value) );
+ if (is_null_value) {
+ out.reset();
+ return SUCCESS;
+ }
+
// Own the allocation before get(): a user tag_invoke may throw.
std::unique_ptr<element_type> ptr(new (std::nothrow) element_type());
if (!ptr) {
@@ -103418,6 +101292,8 @@ constexpr bool user_defined_type = (std::is_class_v<T>
#endif // SIMDJSON_SUPPORTS_CHAR8_T
&& !concepts::optional_type<T> &&
!concepts::appendable_containers<T>
+// Smart pointers are handled by the generic smart-pointer tag_invoke.
+&& !concepts::smart_pointer<T>
// simdjson's own types (array, object, value, raw_json_string, number,
// document, document_reference) have dedicated get<T>() specializations and
// must never go through reflection.
@@ -104010,251 +101886,8 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept {
#endif
}
-template <typename simdjson_value, typename T>
- requires(user_defined_type<std::remove_cvref_t<T>>)
-error_code tag_invoke(deserialize_tag, simdjson_value &val, std::unique_ptr<T> &out) noexcept(false) {
- std::unique_ptr<T> ptr(new (std::nothrow) T());
- if (!ptr) {
- return MEMALLOC;
- }
- SIMDJSON_TRY(val.get(*ptr));
- out = std::move(ptr);
- return SUCCESS;
-}
-
-template <typename simdjson_value, typename T>
- requires(user_defined_type<std::remove_cvref_t<T>>)
-error_code tag_invoke(deserialize_tag, simdjson_value &val, std::shared_ptr<T> &out) noexcept(false) {
- std::shared_ptr<T> ptr(new (std::nothrow) T());
- if (!ptr) {
- return MEMALLOC;
- }
- SIMDJSON_TRY(val.get(*ptr));
- out = std::move(ptr);
- return SUCCESS;
-}
-
#endif // SIMDJSON_STATIC_REFLECTION
-////////////////////////////////////////
-// Unique pointers
-////////////////////////////////////////
-error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<bool> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_unique<bool>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_bool().get(*out));
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<int64_t> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_unique<int64_t>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_int64().get(*out));
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<uint64_t> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_unique<uint64_t>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_uint64().get(*out));
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<double> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_unique<double>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_double().get(*out));
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<std::string_view> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_unique<std::string_view>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_string().get(*out));
- return SUCCESS;
-}
-
-
-////////////////////////////////////////
-// Shared pointers
-////////////////////////////////////////
-error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<bool> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_shared<bool>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_bool().get(*out));
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<int64_t> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_shared<int64_t>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_int64().get(*out));
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<uint64_t> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_shared<uint64_t>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_uint64().get(*out));
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<double> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_shared<double>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_double().get(*out));
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<std::string_view> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_shared<std::string_view>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_string().get(*out));
- return SUCCESS;
-}
-
-
-////////////////////////////////////////
-// Explicit optional specializations
-////////////////////////////////////////
-
-////////////////////////////////////////
-// Explicit smart pointer specializations for string and int types
-////////////////////////////////////////
-error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<std::string> &out) noexcept {
- // Check if the value is null
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset(); // Set to nullptr
- return SUCCESS;
- }
-
- if (!out) {
- out = std::make_unique<std::string>();
- }
- std::string_view str;
- SIMDJSON_TRY(val.get_string().get(str));
- *out = std::string{str};
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<std::string> &out) noexcept {
- // Check if the value is null
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset(); // Set to nullptr
- return SUCCESS;
- }
-
- if (!out) {
- out = std::make_shared<std::string>();
- }
- std::string_view str;
- SIMDJSON_TRY(val.get_string().get(str));
- *out = std::string{str};
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<int> &out) noexcept {
- // Check if the value is null
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset(); // Set to nullptr
- return SUCCESS;
- }
-
- if (!out) {
- out = std::make_unique<int>();
- }
- int64_t temp;
- SIMDJSON_TRY(val.get_int64().get(temp));
- *out = static_cast<int>(temp);
- return SUCCESS;
-}
-
} // namespace simdjson
#endif // SIMDJSON_ONDEMAND_DESERIALIZE_H
@@ -104369,10 +102002,9 @@ simdjson_inline simdjson_result<std::string_view> array::raw_json() noexcept {
const uint8_t * starting_point{iter.peek_start()};
auto error = consume();
if(error) { return error; }
- // After 'consume()', we could be left pointing just beyond the document, but that
- // is ok because we are not going to dereference the final pointer position, we just
- // use it to compute the length in bytes.
- const uint8_t * final_point{iter._json_iter->unsafe_pointer()};
+ // After 'consume()', the last token consumed is the closing bracket: we end on it
+ // rather than on the next token, which may follow white space.
+ const uint8_t * final_point{iter._json_iter->token.peek(-1) + 1};
return std::string_view(reinterpret_cast<const char*>(starting_point), size_t(final_point - starting_point));
}
@@ -104456,12 +102088,23 @@ template <typename Func>
template <typename Func>
#endif
inline error_code array::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
- auto result_pair = get_next_key_and_json_path(json_path);
- std::string_view key = result_pair.first;
- std::string_view remaining_path = result_pair.second;
+ // The path is split once: every element reached by a wildcard sees the same
+ // remaining path, so re-parsing it at each one would be quadratic.
+ const std::vector<internal::json_path_segment> path = internal::split_json_path(json_path);
+ // A path that cannot be parsed matches nothing, whatever the document: say
+ // so rather than letting the walk skip it as a mismatch below a wildcard.
+ if (path.back().key.empty()) {
+ return INVALID_JSON_POINTER;
+ }
+ return for_each_at_path_segments(path, 0, callback);
+}
+
+template <typename Func>
+inline error_code array::for_each_at_path_segments(const std::vector<internal::json_path_segment> &path, size_t index, Func &callback) noexcept {
+ std::string_view key = path[index].key;
+ const bool last = index + 1 == path.size();
// Empty key means the path segment could not be parsed (including malformed
- // bracket-quoted keys). Without this check, an empty key is treated as index
- // 0 and remaining_path may be unchanged, causing infinite recursion.
+ // bracket-quoted keys).
if (key.empty()) {
return INVALID_JSON_POINTER;
}
@@ -104470,11 +102113,11 @@ inline error_code array::for_each_at_path_with_wildcard(std::string_view json_pa
for(auto element: *this) {
value val;
SIMDJSON_TRY(element.get(val));
- if (remaining_path.empty()) {
+ if (last) {
callback(val);
} else {
// An element that does not match the rest of the path is skipped.
- error_code err = val.for_each_at_path_with_wildcard(remaining_path, callback);
+ error_code err = val.for_each_at_path_segments(path, index + 1, callback);
if (err && !internal::is_matching_failure_error(err)) { return err; }
}
}
@@ -104493,16 +102136,16 @@ inline error_code array::for_each_at_path_with_wildcard(std::string_view json_pa
value val;
if (!error) { error = at(idx).get(val); }
if (error) {
- if (error == INDEX_OUT_OF_BOUNDS && internal::json_path_has_wildcard(remaining_path)) {
+ if (error == INDEX_OUT_OF_BOUNDS && path[index].wildcard_follows) {
return SUCCESS;
}
return error;
}
- if (remaining_path.empty()){
+ if (last){
callback(val);
return SUCCESS;
} else {
- return val.for_each_at_path_with_wildcard(remaining_path, callback);
+ return val.for_each_at_path_segments(path, index + 1, callback);
}
}
}
@@ -105160,6 +102803,19 @@ template <typename Func>
template <typename Func>
#endif
inline error_code value::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
+ // The path is split once: every value reached by a wildcard sees the same
+ // remaining path, so re-parsing it at each one would be quadratic.
+ const std::vector<internal::json_path_segment> path = internal::split_json_path(json_path);
+ // A path that cannot be parsed matches nothing, whatever the document: say
+ // so rather than letting the walk skip it as a mismatch below a wildcard.
+ if (path.back().key.empty()) {
+ return INVALID_JSON_POINTER;
+ }
+ return for_each_at_path_segments(path, 0, callback);
+}
+
+template <typename Func>
+inline error_code value::for_each_at_path_segments(const std::vector<internal::json_path_segment> &path, size_t index, Func &callback) noexcept {
// Every recursive step of for_each_at_path_with_wildcard goes through this
// function, and each one descends one level into the document. A path with
// many segments applied to a deeply nested document would otherwise recurse
@@ -105168,10 +102824,16 @@ inline error_code value::for_each_at_path_with_wildcard(std::string_view json_pa
json_type t;
SIMDJSON_TRY(type().get(t));
switch (t) {
- case json_type::array:
- return (*this).get_array().for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
- case json_type::object:
- return (*this).get_object().for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
+ case json_type::array: {
+ array a;
+ SIMDJSON_TRY(get_array().get(a));
+ return a.for_each_at_path_segments(path, index, callback);
+ }
+ case json_type::object: {
+ object o;
+ SIMDJSON_TRY(get_object().get(o));
+ return o.for_each_at_path_segments(path, index, callback);
+ }
default:
return INVALID_JSON_POINTER;
}
@@ -105544,7 +103206,8 @@ simdjson_inline document document::start(json_iterator &&iter) noexcept {
}
inline void document::rewind() noexcept {
- iter.rewind();
+ // A document that is no longer alive is left as it is (see is_alive()).
+ static_cast<void>(iter.rewind());
}
inline std::string document::to_debug_string() noexcept {
@@ -105844,7 +103507,13 @@ simdjson_warn_unused simdjson_inline error_code document::consume() noexcept {
auto error = is_scalar().get(scalar);
if(error) { return error; }
if(scalar) {
- iter.return_current_and_advance();
+ // Step over the scalar and leave depth 0, as skip_child() does: otherwise a
+ // document_stream takes the next document as the rest of this one. At depth
+ // 0 the scalar has already been consumed (e.g., by get_int64()).
+ if(iter.depth() > 0) {
+ iter.return_current_and_advance();
+ iter.ascend_to(0);
+ }
return SUCCESS;
}
error = iter.skip_child(0);
@@ -105857,10 +103526,36 @@ simdjson_inline simdjson_result<std::string_view> document::raw_json() noexcept
const uint8_t * starting_point{_iter.peek_start()};
auto error = consume();
if(error) { return error; }
- // After 'consume()', we could be left pointing just beyond the document, but that
- // is ok because we are not going to dereference the final pointer position, we just
- // use it to compute the length in bytes.
- const uint8_t * final_point{iter.unsafe_pointer()};
+ // After 'consume()', the iterator points at the token after the document. In a
+ // document_stream, that is the next document, past white space and separators, or
+ // the end of the input when a truncated document follows. We end on the last byte
+ // of this document instead.
+ const uint8_t * final_point;
+ if(*starting_point == '{' || *starting_point == '[') {
+ // The last token consumed is the closing brace or bracket.
+ final_point = iter.token.peek(-1) + 1;
+ } else {
+ // A scalar is a single token: a string ends at its closing quote, anything else
+ // at the first white space, structural character, quote, RS (0x1E) or NUL.
+ const uint8_t * next_token{iter.unsafe_pointer()};
+ final_point = starting_point + 1;
+ if(*starting_point == '"') {
+ while(final_point < next_token) {
+ uint8_t c = *final_point++;
+ if(c == '\\') {
+ final_point++;
+ } else if(c == '"') {
+ break;
+ }
+ }
+ if(final_point > next_token) { final_point = next_token; }
+ } else {
+ while(final_point < next_token && !jsoncharutils::is_structural_or_whitespace(*final_point)
+ && *final_point != '"' && *final_point != 0x1E && *final_point != 0) {
+ final_point++;
+ }
+ }
+ }
return std::string_view(reinterpret_cast<const char*>(starting_point), size_t(final_point - starting_point));
}
@@ -105907,7 +103602,9 @@ simdjson_inline simdjson_result<std::string_view> document::raw_json_token() noe
}
simdjson_inline simdjson_result<value> document::at_pointer(std::string_view json_pointer) noexcept {
- rewind(); // Rewind the document each time at_pointer is called
+ // Rewind the document each time at_pointer is called. After a fatal error,
+ // the document cannot be walked again: we report the error instead.
+ SIMDJSON_TRY(iter.rewind());
if (json_pointer.empty()) {
return this->get_value();
}
@@ -105925,7 +103622,9 @@ simdjson_inline simdjson_result<value> document::at_pointer(std::string_view jso
}
simdjson_inline simdjson_result<value> document::at_path(std::string_view json_path) noexcept {
- rewind(); // Rewind the document each time at_pointer is called
+ // Rewind the document each time at_path is called. After a fatal error,
+ // the document cannot be walked again: we report the error instead.
+ SIMDJSON_TRY(iter.rewind());
if (json_path.empty()) {
return this->get_value();
}
@@ -105948,7 +103647,9 @@ template <typename Func>
template <typename Func>
#endif
simdjson_inline error_code document::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
- rewind(); // Rewind the document each time for_each_at_path_with_wildcard is called
+ // Rewind the document each time for_each_at_path_with_wildcard is called. After
+ // a fatal error, the document cannot be walked again: we report the error instead.
+ SIMDJSON_TRY(iter.rewind());
if (json_path.empty()) {
return INVALID_JSON_POINTER;
}
@@ -106286,11 +103987,6 @@ simdjson_inline simdjson_result<fallback::ondemand::number> simdjson_result<fall
#if SIMDJSON_EXCEPTIONS
-template <class T, typename std::enable_if<std::is_same<T, fallback::ondemand::document>::value == false>::type>
-simdjson_inline simdjson_result<fallback::ondemand::document>::operator T() noexcept(false) {
- if (error()) { throw simdjson_error(error()); }
- return first;
-}
simdjson_inline simdjson_result<fallback::ondemand::document>::operator fallback::ondemand::array() & noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
@@ -106351,6 +104047,11 @@ simdjson_inline simdjson_result<std::string_view> simdjson_result<fallback::onde
return first.raw_json_token();
}
+simdjson_inline simdjson_result<std::string_view> simdjson_result<fallback::ondemand::document>::raw_json() noexcept {
+ if (error()) { return error(); }
+ return first.raw_json();
+}
+
simdjson_inline simdjson_result<fallback::ondemand::value> simdjson_result<fallback::ondemand::document>::at_pointer(std::string_view json_pointer) noexcept {
if (error()) { return error(); }
return first.at_pointer(json_pointer);
@@ -106837,6 +104538,11 @@ simdjson_inline simdjson_result<std::string_view> simdjson_result<fallback::onde
return first.raw_json_token();
}
+simdjson_inline simdjson_result<std::string_view> simdjson_result<fallback::ondemand::document_reference>::raw_json() noexcept {
+ if (error()) { return error(); }
+ return first.raw_json();
+}
+
simdjson_inline simdjson_result<fallback::ondemand::value> simdjson_result<fallback::ondemand::document_reference>::at_pointer(std::string_view json_pointer) noexcept {
if (error()) { return error(); }
return first.at_pointer(json_pointer);
@@ -107228,6 +104934,13 @@ simdjson_inline bool document_stream::skip_to_delimiter(uint8_t delimiter) noexc
}
inline void document_stream::next_document() noexcept {
+ // Iteration stops at the first document that met a fatal error. The error
+ // may have abandoned the document's iterator, which then no longer has a
+ // parser to walk the rest of the document with.
+ if (!doc.iter.is_alive() || doc.iter.error) {
+ error = doc.iter.error ? doc.iter.error : TAPE_ERROR;
+ return;
+ }
// A delimiter that cannot occur inside a document tells us where the current
// one ends, so we can jump there instead of walking every structural. Only
// valid while the iterator is still inside the document: a consumed document
@@ -107377,7 +105090,10 @@ simdjson_inline std::string_view document_stream::iterator::source() const noexc
cur_struct_index++;
}
- while (cur_struct_index <= static_cast<int64_t>(stream->parser->implementation->n_structural_indexes)) {
+ // The structural index at n_structural_indexes is the end of the batch, not
+ // a token: it must not be read as a closing bracket.
+ const int64_t n_structural_indexes = static_cast<int64_t>(stream->parser->implementation->n_structural_indexes);
+ while (cur_struct_index < n_structural_indexes) {
switch (stream->buf[stream->batch_start + stream->parser->implementation->structural_indexes[cur_struct_index]]) {
case '{': case '[':
depth++;
@@ -107389,6 +105105,11 @@ simdjson_inline std::string_view document_stream::iterator::source() const noexc
if (depth == 0) { break; }
cur_struct_index++;
}
+ // An invalid last document such as "[1 2" is never closed: it runs to the
+ // end of the batch, and not one byte past it.
+ if (cur_struct_index >= n_structural_indexes) {
+ return std::string_view(reinterpret_cast<const char*>(stream->buf) + current_index(), stream->batch_start + stream->parser->implementation->structural_indexes[cur_struct_index] - current_index());
+ }
return std::string_view(reinterpret_cast<const char*>(stream->buf) + current_index(), stream->parser->implementation->structural_indexes[cur_struct_index] - current_index() + stream->batch_start + 1);;
}
@@ -107405,7 +105126,12 @@ inline void document_stream::load_from_stage1_thread() noexcept {
// enough memory to swap to, as well.
std::swap(stage1_thread_parser,*parser);
error = stage1_thread_error;
- if (error) { return; }
+ // An EMPTY batch (no complete document in it, e.g. a run of white space at
+ // least batch_size long) is not the end of the stream: next() moves on to
+ // the batch after it, so queue that batch now as we would after a success.
+ // Returning here would leave no job for the next call, which then swaps
+ // the stale previous batch back in and ends the stream early.
+ if (error && error != EMPTY) { return; }
// If there's anything left, start the stage 1 thread!
if (next_batch_start() < len) {
@@ -107517,12 +105243,14 @@ simdjson_inline raw_json_string field::key() const noexcept {
simdjson_inline std::string_view field::key_raw_json_token() const noexcept {
SIMDJSON_ASSUME(first.buf != nullptr); // We would like to call .alive() by Visual Studio won't let us.
- return std::string_view(reinterpret_cast<const char*>(first.buf-1), second.iter._json_iter->token.peek(-1) - first.buf + 1);
+ // The ':' is the token just before the value. Use the value's start position
+ // rather than the current position, which moves once the value is consumed.
+ return std::string_view(reinterpret_cast<const char*>(first.buf-1), second.iter._json_iter->token.peek(second.iter.start_position() - 1) - first.buf + 1);
}
simdjson_inline std::string_view field::escaped_key() const noexcept {
SIMDJSON_ASSUME(first.buf != nullptr); // We would like to call .alive() by Visual Studio won't let us.
- auto end_quote = second.iter._json_iter->token.peek(-1);
+ auto end_quote = second.iter._json_iter->token.peek(second.iter.start_position() - 1);
while(*end_quote != '"') end_quote--;
return std::string_view(reinterpret_cast<const char*>(first.buf), end_quote - first.buf);
}
@@ -107693,11 +105421,14 @@ simdjson_inline json_iterator::json_iterator(const uint8_t *buf, ondemand::parse
}
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
-inline void json_iterator::rewind() noexcept {
+simdjson_inline error_code json_iterator::rewind() noexcept {
+ // abandon() let go of the parser: the iterator cannot be walked again.
+ if (!is_alive()) { return error ? error : UNINITIALIZED; }
token.set_position( root_position() );
logger::log_headers(); // We start again
_string_buf_loc = parser->string_buf.get();
_depth = 1;
+ return SUCCESS;
}
inline bool json_iterator::balanced() const noexcept {
@@ -107721,6 +105452,18 @@ inline bool json_iterator::balanced() const noexcept {
}
+// Depth change of each structural character when skipping: +1 for [ and {, -1 for ] and }.
+static constexpr int8_t skip_depth_delta[256] = {
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,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,
+};
+
// GCC 7 warns when the first line of this function is inlined away into oblivion due to the caller
// relating depth and parent_depth, which is a desired effect. The warning does not show up if the
// skip_child() function is not marked inline).
@@ -107776,7 +105519,30 @@ simdjson_warn_unused simdjson_inline error_code json_iterator::skip_child(depth_
break;
}
- // Now that we've considered the first value, we only increment/decrement for arrays/objects
+ // Now that we've considered the first value, we only increment/decrement for arrays/objects.
+ //
+ // Without logging, look up the depth change of each token in a table instead of branching
+ // on it: the next byte is hard to predict, so a switch mispredicts often. The loop also keeps
+ // the position and depth in locals, because the byte loads are char loads that may alias
+ // token._position and _depth, which would force a store of both on every token.
+ if (!logger::LOG_ENABLED) {
+ token_position pos = token.position();
+ const token_position end = end_position();
+ const uint8_t *const json = token.buf;
+ depth_t depth = _depth;
+ while (pos < end) {
+ depth += skip_depth_delta[json[*pos++]];
+ if (depth <= parent_depth) {
+ token.set_position(pos);
+ _depth = depth;
+ return SUCCESS;
+ }
+ }
+ token.set_position(pos);
+ _depth = depth;
+ return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "not enough close braces");
+ }
+
while (position() < end_position()) {
switch (*return_current_and_advance()) {
case '[': case '{':
@@ -108061,6 +105827,17 @@ simdjson_warn_unused simdjson_inline error_code json_iterator::optional_error(er
simdjson_warn_unused simdjson_inline bool json_iterator::copy_to_buffer(const uint8_t *json, uint32_t max_len, uint8_t *tmpbuf, size_t N) noexcept {
// This function is not expected to be called in performance-sensitive settings.
+ // peek_root_length() stops at the next structural index. Stage 1 omits the
+ // comma or RS (0x1E) that separates stream documents, so that byte and the
+ // whitespace around it remain in the span. RS is not a valid number finisher.
+ while (max_len > 0) {
+ const uint8_t last = json[max_len - 1];
+ if (last != ' ' && last != '\t' && last != '\n' && last != '\r' &&
+ last != ',' && last != 0x1E) {
+ break;
+ }
+ max_len--;
+ }
// Let us guard against silly cases:
if((N < max_len) || (N == 0)) { return false; }
// Copy to the buffer.
@@ -108702,7 +106479,9 @@ simdjson_inline simdjson_result<std::string_view> object::raw_json() noexcept {
const uint8_t * starting_point{iter.peek_start()};
auto error = consume();
if(error) { return error; }
- const uint8_t * final_point{iter._json_iter->peek()};
+ // After 'consume()', the last token consumed is the closing brace: we end on it
+ // rather than on the next token, which may follow white space.
+ const uint8_t * final_point{iter._json_iter->token.peek(-1) + 1};
return std::string_view(reinterpret_cast<const char*>(starting_point), size_t(final_point - starting_point));
}
@@ -108795,9 +106574,21 @@ template <typename Func>
template <typename Func>
#endif
inline error_code object::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
- auto result_pair = get_next_key_and_json_path(json_path);
- std::string_view key = result_pair.first;
- std::string_view remaining_path = result_pair.second;
+ // The path is split once: every field reached by a wildcard sees the same
+ // remaining path, so re-parsing it at each one would be quadratic.
+ const std::vector<internal::json_path_segment> path = internal::split_json_path(json_path);
+ // A path that cannot be parsed matches nothing, whatever the document: say
+ // so rather than letting the walk skip it as a mismatch below a wildcard.
+ if (path.back().key.empty()) {
+ return INVALID_JSON_POINTER;
+ }
+ return for_each_at_path_segments(path, 0, callback);
+}
+
+template <typename Func>
+inline error_code object::for_each_at_path_segments(const std::vector<internal::json_path_segment> &path, size_t index, Func &callback) noexcept {
+ std::string_view key = path[index].key;
+ const bool last = index + 1 == path.size();
// Empty key means the path segment could not be parsed (including malformed
// bracket-quoted keys). Match DOM at_path_with_wildcard and avoid treating
// "no progress" as a recursive lookup of "".
@@ -108810,11 +106601,11 @@ inline error_code object::for_each_at_path_with_wildcard(std::string_view json_p
for (auto field : *this) {
value val;
SIMDJSON_TRY(field.value().get(val));
- if (remaining_path.empty()) {
+ if (last) {
callback(val);
} else {
// A field that does not match the rest of the path is skipped.
- error_code err = val.for_each_at_path_with_wildcard(remaining_path, callback);
+ error_code err = val.for_each_at_path_segments(path, index + 1, callback);
if (err && !internal::is_matching_failure_error(err)) { return err; }
}
}
@@ -108823,17 +106614,17 @@ inline error_code object::for_each_at_path_with_wildcard(std::string_view json_p
value val;
error_code error = find_field(key).get(val);
if (error) {
- if (error == NO_SUCH_FIELD && internal::json_path_has_wildcard(remaining_path)) {
+ if (error == NO_SUCH_FIELD && path[index].wildcard_follows) {
return SUCCESS;
}
return error;
}
- if (remaining_path.empty()) {
+ if (last) {
callback(val);
return SUCCESS;
} else {
- return val.for_each_at_path_with_wildcard(remaining_path, callback);
+ return val.for_each_at_path_segments(path, index + 1, callback);
}
}
}
@@ -109486,6 +107277,7 @@ simdjson_inline simdjson_warn_unused bool parser::string_buffer_overflow(const u
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(padded_string_view json) & noexcept {
if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
+ json.use_trailing_whitespace_as_padding();
json.remove_utf8_bom();
_document_len = json.length();
@@ -109503,6 +107295,7 @@ simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(p
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate_allow_incomplete_json(padded_string_view json) & noexcept {
if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
+ json.use_trailing_whitespace_as_padding();
json.remove_utf8_bom();
_document_len = json.length();
@@ -109558,6 +107351,7 @@ simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(c
simdjson_warn_unused simdjson_inline simdjson_result<json_iterator> parser::iterate_raw(padded_string_view json) & noexcept {
if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
+ json.use_trailing_whitespace_as_padding();
json.remove_utf8_bom();
@@ -109584,6 +107378,7 @@ inline simdjson_result<document_stream> parser::iterate_many(const char *buf, si
}
inline simdjson_result<document_stream> parser::iterate_many(padded_string_view s, size_t batch_size) noexcept {
if (!s.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
+ s.use_trailing_whitespace_as_padding();
return iterate_many(s.data(), s.length(), batch_size);
}
inline simdjson_result<document_stream> parser::iterate_many(const padded_string &s, size_t batch_size) noexcept {
@@ -109619,6 +107414,7 @@ inline simdjson_result<document_stream> parser::iterate_many(const char *buf, si
}
inline simdjson_result<document_stream> parser::iterate_many(padded_string_view s, size_t batch_size, bool allow_comma_separated) noexcept {
if (!s.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
+ s.use_trailing_whitespace_as_padding();
return iterate_many(s.data(), s.length(), batch_size, allow_comma_separated);
}
inline simdjson_result<document_stream> parser::iterate_many(const padded_string &s, size_t batch_size, bool allow_comma_separated) noexcept {
@@ -109664,6 +107460,7 @@ inline simdjson_result<document_stream> parser::iterate_many(const char *buf, si
}
inline simdjson_result<document_stream> parser::iterate_many(padded_string_view s, size_t batch_size, stream_format format) noexcept {
if (!s.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
+ s.use_trailing_whitespace_as_padding();
return iterate_many(s.data(), s.length(), batch_size, format);
}
inline simdjson_result<document_stream> parser::iterate_many(const std::string &s, size_t batch_size, stream_format format) noexcept {
@@ -109818,17 +107615,18 @@ simdjson_inline bool raw_json_string::is_free_from_unescaped_quote(const char* t
return true;
}
-
simdjson_inline bool raw_json_string::unsafe_is_equal(size_t length, std::string_view target) const noexcept {
// If we are going to call memcmp, then we must know something about the length of the raw_json_string.
- return (length >= target.size()) && (raw()[target.size()] == '"') && !memcmp(raw(), target.data(), target.size());
+ return (length >= target.size()) && (raw()[target.size()] == '"') && !memcmp(raw(), target.data(), target.size())
+ && !escapes_trailing_quote(target);
}
simdjson_inline bool raw_json_string::unsafe_is_equal(std::string_view target) const noexcept {
// Assumptions: does not contain unescaped quote characters("), and
// the raw content is quote terminated within a valid JSON string.
if(target.size() <= SIMDJSON_PADDING) {
- return (raw()[target.size()] == '"') && !memcmp(raw(), target.data(), target.size());
+ return (raw()[target.size()] == '"') && !memcmp(raw(), target.data(), target.size())
+ && !escapes_trailing_quote(target);
}
// Past SIMDJSON_PADDING we can no longer rely on the padding to keep the
// comparison in bounds, so we must stop at the quote terminating the key.
@@ -109854,7 +107652,7 @@ simdjson_inline bool raw_json_string::is_equal(std::string_view target) const no
escaping = false;
}
}
- if(r[pos] != '"') { return false; }
+ if((r[pos] != '"') || escaping) { return false; }
return true;
}
@@ -109867,7 +107665,7 @@ simdjson_inline bool raw_json_string::unsafe_is_equal(const char * target) const
for(;target[pos];pos++) {
if(r[pos] != target[pos]) { return false; }
}
- if(r[pos] != '"') { return false; }
+ if((r[pos] != '"') || escapes_trailing_quote(std::string_view(target, pos))) { return false; }
return true;
}
@@ -109892,7 +107690,7 @@ simdjson_inline bool raw_json_string::is_equal(const char* target) const noexcep
escaping = false;
}
}
- if(r[pos] != '"') { return false; }
+ if((r[pos] != '"') || escaping) { return false; }
return true;
}
@@ -110004,7 +107802,11 @@ simdjson_inline uint32_t token_iterator::peek_index(token_position position) con
return *position;
}
simdjson_inline uint32_t token_iterator::peek_length(token_position position) const noexcept {
- return *(position+1) - *position;
+ // In the last batch of a document_stream, the index past the end of the
+ // structural indexes holds the start of the truncated bytes (see
+ // document_stream::truncated_bytes()), which can come before the end of the
+ // input: a token at the end is then empty.
+ return *(position+1) > *position ? *(position+1) - *position : 0;
}
simdjson_inline uint32_t token_iterator::peek_root_length(token_position position) const noexcept {
@@ -110019,7 +107821,7 @@ simdjson_inline uint32_t token_iterator::peek_index(int32_t delta) const noexcep
return *(_position+delta);
}
simdjson_inline uint32_t token_iterator::peek_length(int32_t delta) const noexcept {
- return *(_position+delta+1) - *(_position+delta);
+ return peek_length(_position+delta);
}
simdjson_inline token_position token_iterator::position() const noexcept {
@@ -110078,6 +107880,8 @@ simdjson_inline simdjson_result<fallback::ondemand::token_iterator>::simdjson_re
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
+#include <cstring> // std::memset (zeroing the root-number scratch padding)
+
namespace simdjson {
namespace fallback {
namespace ondemand {
@@ -110124,6 +107928,10 @@ simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::start
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::started_object() noexcept {
assert_at_container_start();
#if SIMDJSON_DEVELOPMENT_CHECKS
+ // The development checks record the start of every open container in a
+ // buffer of max_depth() entries: a document nested deeper than that is
+ // rejected, as the DOM parser does.
+ if (size_t(_depth) >= _json_iter->parser->max_depth()) { return report_error(DEPTH_ERROR, "Exceeded max depth!"); }
_json_iter->set_start_position(_depth, start_position());
#endif
if (*_json_iter->peek() == '}') {
@@ -110533,6 +108341,10 @@ inline std::string value_iterator::to_string() const noexcept {
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::started_array() noexcept {
assert_at_container_start();
+#if SIMDJSON_DEVELOPMENT_CHECKS
+ // See started_object().
+ if (size_t(_depth) >= _json_iter->parser->max_depth()) { return report_error(DEPTH_ERROR, "Exceeded max depth!"); }
+#endif
if (*_json_iter->peek() == ']') {
logger::log_value(*_json_iter, "empty array");
_json_iter->return_current_and_advance();
@@ -110738,8 +108550,10 @@ simdjson_inline simdjson_result<fallback::number_type> value_iterator::get_root_
// Per https://www.exploringbinary.com/maximum-number-of-decimal-digits-in-binary-floating-point-numbers/,
// 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest
// number: -0.<fraction>e-308.
- uint8_t tmpbuf[1074+8+1+1];
- tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
+ // +SIMDJSON_PADDING: the digit scan reads up to 7 bytes past the terminator.
+ uint8_t tmpbuf[1074+8+1+1+SIMDJSON_PADDING];
+ // Null terminate, and zero the padding as allocate_padded_buffer does.
+ std::memset(tmpbuf+1074+8+1, 0, 1+SIMDJSON_PADDING);
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
if(numberparsing::check_if_integer(json, max_len)) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
@@ -110760,8 +108574,10 @@ simdjson_inline simdjson_result<number> value_iterator::get_root_number(bool che
// 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest
// number: -0.<fraction>e-308.
// NOTE: the current approach doesn't work for very big integer numbers containing more than 1074 digits.
- uint8_t tmpbuf[1074+8+1+1];
- tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
+ // +SIMDJSON_PADDING: the digit scan reads up to 7 bytes past the terminator.
+ uint8_t tmpbuf[1074+8+1+1+SIMDJSON_PADDING];
+ // Null terminate, and zero the padding as allocate_padded_buffer does.
+ std::memset(tmpbuf+1074+8+1, 0, 1+SIMDJSON_PADDING);
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
if(numberparsing::check_if_integer(json, max_len)) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
@@ -110831,10 +108647,10 @@ simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> value_iterator::g
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> value_iterator::get_root_uint64_in_string(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("uint64");
- uint8_t tmpbuf[20+1+1]{}; // <20 digits> is the longest possible unsigned integer
- tmpbuf[20+1] = '\0'; // make sure that buffer is always null terminated.
- if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 20+1)) {
- logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 20 characters");
+ uint8_t tmpbuf[20+2+1]{}; // "<20 digits>" is the longest possible unsigned integer
+ tmpbuf[20+2] = '\0'; // make sure that buffer is always null terminated.
+ if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 20+2)) {
+ logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 22 characters");
return NUMBER_ERROR;
}
auto result = numberparsing::parse_unsigned_in_string(tmpbuf);
@@ -110864,10 +108680,10 @@ simdjson_warn_unused simdjson_inline simdjson_result<int64_t> value_iterator::ge
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> value_iterator::get_root_int64_in_string(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("int64");
- uint8_t tmpbuf[20+1+1]; // -<19 digits> is the longest possible integer
- tmpbuf[20+1] = '\0'; // make sure that buffer is always null terminated.
- if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 20+1)) {
- logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 20 characters");
+ uint8_t tmpbuf[20+2+1]; // "-<19 digits>" is the longest possible integer
+ tmpbuf[20+2] = '\0'; // make sure that buffer is always null terminated.
+ if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 20+2)) {
+ logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 22 characters");
return NUMBER_ERROR;
}
@@ -110884,8 +108700,10 @@ simdjson_warn_unused simdjson_inline simdjson_result<double> value_iterator::get
// Per https://www.exploringbinary.com/maximum-number-of-decimal-digits-in-binary-floating-point-numbers/,
// 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest
// number: -0.<fraction>e-308.
- uint8_t tmpbuf[1074+8+1+1]; // +1 for null termination.
- tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
+ // +SIMDJSON_PADDING: the digit scan reads up to 7 bytes past the terminator.
+ uint8_t tmpbuf[1074+8+1+1+SIMDJSON_PADDING]; // +1 for null termination.
+ // Null terminate, and zero the padding as allocate_padded_buffer does.
+ std::memset(tmpbuf+1074+8+1, 0, 1+SIMDJSON_PADDING);
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters");
return NUMBER_ERROR;
@@ -110904,8 +108722,10 @@ simdjson_warn_unused simdjson_inline simdjson_result<double> value_iterator::get
// Per https://www.exploringbinary.com/maximum-number-of-decimal-digits-in-binary-floating-point-numbers/,
// 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest
// number: -0.<fraction>e-308.
- uint8_t tmpbuf[1074+8+1+1]; // +1 for null termination.
- tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
+ // +SIMDJSON_PADDING: the digit scan reads up to 7 bytes past the terminator.
+ uint8_t tmpbuf[1074+8+1+1+SIMDJSON_PADDING]; // +1 for null termination.
+ // Null terminate, and zero the padding as allocate_padded_buffer does.
+ std::memset(tmpbuf+1074+8+1, 0, 1+SIMDJSON_PADDING);
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters");
return NUMBER_ERROR;
@@ -110924,8 +108744,10 @@ simdjson_warn_unused simdjson_inline simdjson_result<float> value_iterator::get_
// We use the same buffer size as get_root_double: the number of significant
// digits that matter is smaller for binary32, but the JSON document may still
// spell out a long number that we must parse (and round) faithfully.
- uint8_t tmpbuf[1074+8+1+1]; // +1 for null termination.
- tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
+ // +SIMDJSON_PADDING: the digit scan reads up to 7 bytes past the terminator.
+ uint8_t tmpbuf[1074+8+1+1+SIMDJSON_PADDING]; // +1 for null termination.
+ // Null terminate, and zero the padding as allocate_padded_buffer does.
+ std::memset(tmpbuf+1074+8+1, 0, 1+SIMDJSON_PADDING);
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters");
return NUMBER_ERROR;
@@ -110941,8 +108763,10 @@ simdjson_warn_unused simdjson_inline simdjson_result<float> value_iterator::get_
simdjson_warn_unused simdjson_inline simdjson_result<float> value_iterator::get_root_float_in_string(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("float");
- uint8_t tmpbuf[1074+8+1+1]; // +1 for null termination.
- tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
+ // +SIMDJSON_PADDING: the digit scan reads up to 7 bytes past the terminator.
+ uint8_t tmpbuf[1074+8+1+1+SIMDJSON_PADDING]; // +1 for null termination.
+ // Null terminate, and zero the padding as allocate_padded_buffer does.
+ std::memset(tmpbuf+1074+8+1, 0, 1+SIMDJSON_PADDING);
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters");
return NUMBER_ERROR;
@@ -110963,7 +108787,7 @@ simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::get_r
// "true" and "false" are 4 and 5 characters long, respectively.
bool value_true = (max_len >= 4 && !atomparsing::str4ncmp(json, "true") &&
(max_len == 4 || jsoncharutils::is_structural_or_whitespace(json[4])));
- bool value_false = (max_len >= 5 && !atomparsing::str4ncmp(json, "false") &&
+ bool value_false = (max_len >= 5 && !atomparsing::str4ncmp(json, "fals") && json[4] == 'e' &&
(max_len == 5 || jsoncharutils::is_structural_or_whitespace(json[5])));
if(value_true == false && value_false == false) { return incorrect_type_error("Not a boolean"); }
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
@@ -111603,49 +109427,6 @@ namespace json_path {
using ::simdjson::fallback::ondemand::value;
-// Path step types
-enum class step_type {
- field, // .field_name or ["field_name"]
- array_index // [index]
-};
-
-// Represents a single step in a JSON path
-template<std::size_t N>
-struct path_step {
- step_type type;
- char key[N]; // Field name (empty for array indices)
- std::size_t index; // Array index (0 for field access)
-
- constexpr path_step(step_type t, const char (&k)[N], std::size_t idx = 0)
- : type(t), index(idx) {
- for (std::size_t i = 0; i < N; ++i) {
- key[i] = k[i];
- }
- }
-
- constexpr std::string_view key_view() const {
- return {key, N - 1};
- }
-};
-
-// Helper to create field step
-template<std::size_t N>
-consteval auto make_field_step(const char (&name)[N]) {
- return path_step<N>(step_type::field, name, 0);
-}
-
-// Helper to create array index step
-consteval auto make_index_step(std::size_t idx) {
- return path_step<1>(step_type::array_index, "", idx);
-}
-
-// Parse state for compile-time JSON path parsing
-struct parse_result {
- bool success;
- std::size_t pos;
- std::string_view error_msg;
-};
-
// Compile-time JSON path parser
// Supports subset: .field, ["field"], [index], nested combinations
template<constevalutil::fixed_string Path>
@@ -111659,77 +109440,6 @@ struct json_path_parser {
}
return 0;
}
-
- // Count the number of steps in the path at compile time
- static consteval std::size_t count_steps() {
- std::size_t count = 0;
- std::size_t i = skip_root();
-
- while (i < path_str.size()) {
- if (path_str[i] == '.') {
- // Field access: .field
- ++i;
- if (i >= path_str.size()) break;
-
- // Skip field name
- while (i < path_str.size() && path_str[i] != '.' && path_str[i] != '[') {
- ++i;
- }
- ++count;
- } else if (path_str[i] == '[') {
- // Array or bracket notation
- ++i;
- if (i >= path_str.size()) break;
-
- if (path_str[i] == '"' || path_str[i] == '\'') {
- // Field access: ["field"] or ['field']
- char quote = path_str[i];
- ++i;
- while (i < path_str.size() && path_str[i] != quote) {
- ++i;
- }
- if (i < path_str.size()) ++i; // skip closing quote
- if (i < path_str.size() && path_str[i] == ']') ++i;
- } else {
- // Array index: [0], [123]
- while (i < path_str.size() && path_str[i] != ']') {
- ++i;
- }
- if (i < path_str.size()) ++i; // skip ]
- }
- ++count;
- } else {
- ++i;
- }
- }
-
- return count;
- }
-
- // Parse a field name at compile time
- static consteval std::size_t parse_field_name(std::size_t start, char* out, std::size_t max_len) {
- std::size_t len = 0;
- std::size_t i = start;
-
- while (i < path_str.size() && path_str[i] != '.' && path_str[i] != '[' && len < max_len - 1) {
- out[len++] = path_str[i++];
- }
- out[len] = '\0';
- return i;
- }
-
- // Parse an array index at compile time
- static consteval std::pair<std::size_t, std::size_t> parse_array_index(std::size_t start) {
- std::size_t index = 0;
- std::size_t i = start;
-
- while (i < path_str.size() && path_str[i] >= '0' && path_str[i] <= '9') {
- index = index * 10 + (path_str[i] - '0');
- ++i;
- }
-
- return {i, index};
- }
};
// Compile-time path accessor generator
@@ -111738,7 +109448,6 @@ struct path_accessor {
using value = ::simdjson::fallback::ondemand::value;
static constexpr auto parser = json_path_parser<Path>();
- static constexpr std::size_t num_steps = parser.count_steps();
static constexpr std::string_view path_view = Path.view();
// Compile-time accessor generation
@@ -111853,93 +109562,6 @@ private:
return current_type;
}
-private:
- // Walk path and extract directly into final field using compile-time reflection
- template<std::meta::info CurrentType, std::size_t PathPos, typename TargetType>
- static inline error_code extract_with_reflection(simdjson_result<value> current, TargetType& target_ref) noexcept {
- if (current.error()) return current.error();
-
- // Base case: end of path - extract into target
- if constexpr (PathPos >= path_view.size()) {
- return current.get(target_ref);
- }
- // Field access: .field_name
- else if constexpr (path_view[PathPos] == '.') {
- constexpr auto field_info = parse_next_field(PathPos);
- constexpr std::string_view field_name = std::get<0>(field_info);
- constexpr std::size_t next_pos = std::get<1>(field_info);
-
- constexpr auto member_info = find_member_by_name(CurrentType, field_name);
- static_assert(member_info != ^^void, "Field not found in struct");
-
- constexpr auto member_type = std::meta::type_of(member_info);
-
- auto obj_result = current.get_object();
- if (obj_result.error()) return obj_result.error();
- auto obj = obj_result.value_unsafe();
- auto field_value = obj.find_field_unordered(field_name);
-
- if constexpr (next_pos >= path_view.size()) {
- return field_value.get(target_ref);
- } else {
- return extract_with_reflection<member_type, next_pos>(field_value, target_ref);
- }
- }
- // Bracket notation: [index] or ["field"]
- else if constexpr (path_view[PathPos] == '[') {
- constexpr auto bracket_info = parse_bracket(PathPos);
- constexpr bool is_field = std::get<0>(bracket_info);
- constexpr std::size_t next_pos = std::get<2>(bracket_info);
-
- if constexpr (is_field) {
- constexpr std::string_view field_name = std::get<1>(bracket_info);
- constexpr auto member_info = find_member_by_name(CurrentType, field_name);
- static_assert(member_info != ^^void, "Field not found in struct");
- constexpr auto member_type = std::meta::type_of(member_info);
-
- auto obj_result = current.get_object();
- if (obj_result.error()) return obj_result.error();
- auto obj = obj_result.value_unsafe();
- auto field_value = obj.find_field_unordered(field_name);
-
- if constexpr (next_pos >= path_view.size()) {
- return field_value.get(target_ref);
- } else {
- return extract_with_reflection<member_type, next_pos>(field_value, target_ref);
- }
- } else {
- constexpr std::size_t index = std::get<3>(bracket_info);
- constexpr auto elem_type = get_element_type_reflected(CurrentType);
- static_assert(elem_type != ^^void, "Could not determine array element type");
-
- auto arr_result = current.get_array();
- if (arr_result.error()) return arr_result.error();
- auto arr = arr_result.value_unsafe();
- auto elem_value = arr.at(index);
-
- if constexpr (next_pos >= path_view.size()) {
- return elem_value.get(target_ref);
- } else {
- return extract_with_reflection<elem_type, next_pos>(elem_value, target_ref);
- }
- }
- }
- // Skip unexpected characters and continue
- else {
- return extract_with_reflection<CurrentType, PathPos + 1>(current, target_ref);
- }
- }
-
- // Find member by name in reflected type
- static consteval std::meta::info find_member_by_name(std::meta::info type_refl, std::string_view name) {
- auto members = std::meta::nonstatic_data_members_of(type_refl, std::meta::access_context::unchecked());
- for (auto mem : members) {
- if (std::meta::identifier_of(mem) == name) {
- return mem;
- }
- }
- }
-
// Generate compile-time accessor code by walking the path
template<std::size_t PathPos>
static inline simdjson_result<value> access_impl(simdjson_result<value> current) noexcept {
@@ -112069,55 +109691,6 @@ public:
}
private:
- // Check if type has member with given name
- template<typename Type>
- static consteval bool has_member(std::string_view member_name) {
- constexpr auto members = std::meta::nonstatic_data_members_of(^^Type, std::meta::access_context::unchecked());
- for (auto mem : members) {
- if (std::meta::identifier_of(mem) == member_name) {
- return true;
- }
- }
- return false;
- }
-
- // Get type of member by name
- template<typename Type>
- static consteval auto get_member_type(std::string_view member_name) {
- constexpr auto members = std::meta::nonstatic_data_members_of(^^Type, std::meta::access_context::unchecked());
- for (auto mem : members) {
- if (std::meta::identifier_of(mem) == member_name) {
- return std::meta::type_of(mem);
- }
- }
- return ^^void;
- }
-
- // Check if non-reflected type is array-like
- template<typename Type>
- static consteval bool is_container_type() {
- using BaseType = std::remove_cvref_t<Type>;
- if constexpr (requires { typename BaseType::value_type; }) {
- return true;
- }
- if constexpr (std::is_array_v<BaseType>) {
- return true;
- }
- return false;
- }
-
- // Extract element type from non-reflected container
- template<typename Type>
- using extract_element_type = std::conditional_t<
- requires { typename std::remove_cvref_t<Type>::value_type; },
- typename std::remove_cvref_t<Type>::value_type,
- std::conditional_t<
- std::is_array_v<std::remove_cvref_t<Type>>,
- std::remove_extent_t<std::remove_cvref_t<Type>>,
- void
- >
- >;
-
// Validate path matches struct definition
static consteval bool validate_path() {
if constexpr (!std::is_class_v<T>) {
@@ -113176,30 +110749,6 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin
}
-struct escaping {
- static constexpr uint32_t BYTES_PROCESSED = 32;
- simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
-
- simdjson_inline bool has_escape() { return escape_bits != 0; }
- simdjson_inline int escape_index() { return trailing_zeroes(escape_bits); }
-
- uint64_t escape_bits;
-}; // struct escaping
-
-
-
-simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
- static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes");
- simd8<uint8_t> v(src);
- v.store(dst);
- simd8<bool> is_quote = (v == '"');
- simd8<bool> is_backslash = (v == '\\');
- simd8<bool> is_control = (v < 32);
- return {
- uint64_t((is_backslash | is_quote | is_control).to_bitmask())
- };
-}
-
} // unnamed namespace
} // namespace haswell
} // namespace simdjson
@@ -113409,11 +110958,6 @@ protected:
public:
simdjson_inline value_iterator() noexcept = default;
- /**
- * Denote that we're starting a document.
- */
- simdjson_inline void start_document() noexcept;
-
/**
* Skips a non-iterated or partially-iterated JSON value, whether it is a scalar, array or object.
*
@@ -113814,16 +111358,7 @@ protected:
simdjson_warn_unused simdjson_inline error_code start_container(uint8_t start_char, const char *incorrect_type_message, const char *type) noexcept;
simdjson_warn_unused simdjson_inline error_code end_container() noexcept;
- /**
- * Advance to a place expecting a value (increasing depth).
- *
- * @return The current token (the one left behind).
- * @error TAPE_ERROR If the document ended early.
- */
- simdjson_inline simdjson_result<const uint8_t *> advance_to_value() noexcept;
-
simdjson_warn_unused simdjson_inline error_code incorrect_type_error(const char *message) const noexcept;
- simdjson_warn_unused simdjson_inline error_code error_unless_more_tokens(uint32_t tokens=1) const noexcept;
simdjson_inline bool is_at_start() const noexcept;
/**
@@ -113906,6 +111441,7 @@ public:
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
+/* amalgamation skipped (editor-only): #include "simdjson/jsonpathutil.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/deserialize.h" */
/* amalgamation skipped (editor-only): #include <vector> */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
@@ -114759,11 +112295,19 @@ protected:
*/
simdjson_inline simdjson_result<object> start_or_resume_object() noexcept;
+ /**
+ * The recursive step of for_each_at_path_with_wildcard: matches
+ * path[index] at this level and what follows below it.
+ */
+ template <typename Func>
+ inline error_code for_each_at_path_segments(const std::vector<simdjson::internal::json_path_segment> &path, size_t index, Func &callback) noexcept;
+
// simdjson_inline void log_value(const char *type) const noexcept;
// simdjson_inline void log_error(const char *message) const noexcept;
value_iterator iter{};
+ friend class array;
friend class document;
friend class array_iterator;
friend class field;
@@ -115452,6 +112996,7 @@ public:
/**
* Take an input in json containing max_len characters and attempt to copy it over to tmpbuf, a buffer with
* N bytes of capacity. It will return false if N is too small (smaller than max_len) of if it is zero.
+ * Trailing whitespace, commas, and record separators (0x1E) do not count toward max_len.
* The buffer (tmpbuf) is padded with space characters.
*/
simdjson_warn_unused simdjson_inline bool copy_to_buffer(const uint8_t *json, uint32_t max_len, uint8_t *tmpbuf, size_t N) noexcept;
@@ -115484,8 +113029,11 @@ public:
/**
* Updates this json iterator so that it is back at the beginning of the document,
* as if it had just been created.
+ *
+ * An iterator that is no longer alive (e.g., abandoned after a fatal error) has no
+ * document to go back to: it is left as is and its error is returned.
*/
- inline void rewind() noexcept;
+ simdjson_inline error_code rewind() noexcept;
/**
* This checks whether the {,},[,] are balanced so that the document
* ends with proper zero depth. This requires scanning the whole document
@@ -115840,6 +113388,28 @@ public:
private:
+ /**
+ * Returns true if target ends with an odd number of backslashes. The last one
+ * then escapes the quote that follows the matching bytes in the raw string, so
+ * that quote does not end the string. Not inlined: callers check the last byte
+ * first, and keys ending in a backslash are rare.
+ */
+ static simdjson_never_inline bool ends_with_unpaired_backslash(std::string_view target) noexcept {
+ size_t backslash_count{0};
+ while(backslash_count < target.size() && target[target.size() - 1 - backslash_count] == '\\') {
+ backslash_count++;
+ }
+ return backslash_count % 2 == 1;
+ }
+
+ /**
+ * True when a byte-for-byte match must be rejected because target escapes the
+ * closing quote. The last-byte test keeps the counting loop off the common path.
+ */
+ static simdjson_inline bool escapes_trailing_quote(std::string_view target) noexcept {
+ return simdjson_unlikely(!target.empty() && target[target.size() - 1] == '\\')
+ && ends_with_unpaired_backslash(target);
+ }
/**
* This will set the inner pointer to zero, effectively making
@@ -116413,6 +113983,7 @@ public:
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
+/* amalgamation skipped (editor-only): #include "simdjson/jsonpathutil.h" */
/* amalgamation skipped (editor-only): #include <vector> */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
@@ -116605,6 +114176,13 @@ protected:
*/
simdjson_warn_unused simdjson_inline error_code consume() noexcept;
+ /**
+ * The recursive step of for_each_at_path_with_wildcard: matches
+ * path[index] at this level and what follows below it.
+ */
+ template <typename Func>
+ inline error_code for_each_at_path_segments(const std::vector<simdjson::internal::json_path_segment> &path, size_t index, Func &callback) noexcept;
+
/**
* Begin array iteration.
*
@@ -117614,6 +115192,9 @@ public:
* Reset the iterator inside the document instance so we are pointing back at the
* beginning of the document, as if it had just been created. It invalidates all
* values, objects and arrays that you have created so far (including unescaped strings).
+ *
+ * A document that is no longer alive (see is_alive()) cannot be rewound: it is
+ * left as is.
*/
inline void rewind() noexcept;
/**
@@ -117687,6 +115268,8 @@ public:
* - INCORRECT_TYPE if a non-integer is used to access an array
* - INVALID_JSON_POINTER if the JSON pointer is invalid and cannot be parsed
* - SCALAR_DOCUMENT_AS_VALUE if the json_pointer is empty and the document is not a scalar (see is_scalar() function).
+ * - the fatal error (e.g., TAPE_ERROR) that ended an earlier call, as the document can
+ * no longer be rewound (see is_alive()).
*/
simdjson_inline simdjson_result<value> at_pointer(std::string_view json_pointer) noexcept;
@@ -117710,6 +115293,8 @@ public:
* - NO_SUCH_FIELD if a field does not exist in an object
* - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length
* - INCORRECT_TYPE if a non-integer is used to access an array
+ * - the fatal error (e.g., TAPE_ERROR) that ended an earlier call, as the document can
+ * no longer be rewound (see is_alive()).
*/
simdjson_inline simdjson_result<value> at_path(std::string_view json_path) noexcept;
@@ -117736,7 +115321,9 @@ public:
/**
* Consumes the document and returns a string_view instance corresponding to the
* document as represented in JSON. It points inside the original byte array containing
- * the JSON document.
+ * the JSON document. It starts at the first byte of the document and ends at its last
+ * byte: it does not include the white space that follows the document, nor, in a
+ * document_stream, the separators or the documents that follow it.
*/
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
@@ -118063,134 +115650,134 @@ public:
using haswell::implementation_simdjson_result_base<haswell::ondemand::document>::operator*;
using haswell::implementation_simdjson_result_base<haswell::ondemand::document>::operator->;
- template <class T, typename std::enable_if<std::is_same<T, haswell::ondemand::document>::value == false>::type>
- explicit simdjson_inline operator T() noexcept(false);
- simdjson_inline operator haswell::ondemand::array() & noexcept(false);
- simdjson_inline operator haswell::ondemand::object() & noexcept(false);
- explicit simdjson_inline operator uint64_t() noexcept(false);
- explicit simdjson_inline operator int64_t() noexcept(false);
- explicit simdjson_inline operator double() noexcept(false);
- explicit simdjson_inline operator std::string_view() noexcept(false);
- explicit simdjson_inline operator haswell::ondemand::raw_json_string() noexcept(false);
- explicit simdjson_inline operator bool() noexcept(false);
- simdjson_inline operator haswell::ondemand::value() noexcept(false);
-#endif
- simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
- simdjson_inline simdjson_result<size_t> count_fields() & noexcept;
- simdjson_inline simdjson_result<haswell::ondemand::value> at(size_t index) & noexcept;
- simdjson_inline simdjson_result<haswell::ondemand::array_iterator> begin() & noexcept;
- simdjson_inline simdjson_result<haswell::ondemand::array_iterator> end() & noexcept;
- simdjson_inline simdjson_result<haswell::ondemand::value> find_field(std::string_view key) & noexcept;
- simdjson_inline simdjson_result<haswell::ondemand::value> find_field(const char *key) & noexcept;
- simdjson_inline simdjson_result<haswell::ondemand::value> operator[](std::string_view key) & noexcept;
- simdjson_inline simdjson_result<haswell::ondemand::value> operator[](const char *key) & noexcept;
- simdjson_result<haswell::ondemand::value> operator[](int) & noexcept = delete;
- simdjson_inline simdjson_result<haswell::ondemand::value> find_field_unordered(std::string_view key) & noexcept;
- simdjson_inline simdjson_result<haswell::ondemand::value> find_field_unordered(const char *key) & noexcept;
- simdjson_inline simdjson_result<haswell::ondemand::json_type> type() noexcept;
- simdjson_inline simdjson_result<bool> is_scalar() noexcept;
- simdjson_inline simdjson_result<bool> is_string() noexcept;
- simdjson_inline simdjson_result<const char *> current_location() noexcept;
- simdjson_inline int32_t current_depth() const noexcept;
- simdjson_inline bool at_end() const noexcept;
- simdjson_inline bool is_negative() noexcept;
- simdjson_inline simdjson_result<bool> is_integer() noexcept;
- simdjson_inline simdjson_result<haswell::number_type> get_number_type() noexcept;
- simdjson_inline simdjson_result<haswell::ondemand::number> get_number() noexcept;
- /** @copydoc simdjson_inline std::string_view document::raw_json_token() const noexcept */
- simdjson_inline simdjson_result<std::string_view> raw_json_token() noexcept;
-
- simdjson_inline simdjson_result<haswell::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
- simdjson_inline simdjson_result<haswell::ondemand::value> at_path(std::string_view json_path) noexcept;
-#if SIMDJSON_SUPPORTS_CONCEPTS
- template <typename Func>
- requires std::invocable<Func, haswell::ondemand::value>
-#else
- template <typename Func>
-#endif
- simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
-#if SIMDJSON_STATIC_REFLECTION
- template<constevalutil::fixed_string... FieldNames, typename T>
- requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
- simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept;
-#endif // SIMDJSON_STATIC_REFLECTION
-};
-
-
-} // namespace simdjson
-
-
-
-namespace simdjson {
-
-template<>
-struct simdjson_result<haswell::ondemand::document_reference> : public haswell::implementation_simdjson_result_base<haswell::ondemand::document_reference> {
-public:
- simdjson_inline simdjson_result(haswell::ondemand::document_reference value, error_code error) noexcept;
- simdjson_inline simdjson_result() noexcept = default;
- simdjson_inline error_code rewind() noexcept;
-
- simdjson_inline simdjson_result<haswell::ondemand::array> get_array() & noexcept;
- simdjson_inline simdjson_result<haswell::ondemand::object> get_object() & noexcept;
- simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
- simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
- simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
- simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
- simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
- simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
- simdjson_inline simdjson_result<uint16_t> get_uint16() noexcept;
- simdjson_inline simdjson_result<int16_t> get_int16() noexcept;
- simdjson_inline simdjson_result<uint8_t> get_uint8() noexcept;
- simdjson_inline simdjson_result<int8_t> get_int8() noexcept;
- simdjson_inline simdjson_result<double> get_double() noexcept;
- simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
- simdjson_inline simdjson_result<float> get_float() noexcept;
- simdjson_inline simdjson_result<float> get_float_in_string() noexcept;
-#if SIMDJSON_SUPPORTS_FLOAT32_T
- simdjson_inline simdjson_result<std::float32_t> get_float32() noexcept;
-#endif // SIMDJSON_SUPPORTS_FLOAT32_T
-#if SIMDJSON_SUPPORTS_FLOAT64_T
- simdjson_inline simdjson_result<std::float64_t> get_float64() noexcept;
-#endif // SIMDJSON_SUPPORTS_FLOAT64_T
- simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
-#if SIMDJSON_SUPPORTS_CHAR8_T
- simdjson_inline simdjson_result<std::u8string_view> get_u8string(bool allow_replacement = false) noexcept;
-#endif // SIMDJSON_SUPPORTS_CHAR8_T
- template <typename string_type>
- simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
- simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
- simdjson_inline simdjson_result<haswell::ondemand::raw_json_string> get_raw_json_string() noexcept;
- simdjson_inline simdjson_result<bool> get_bool() noexcept;
- simdjson_inline simdjson_result<haswell::ondemand::value> get_value() noexcept;
- simdjson_inline simdjson_result<bool> is_null() noexcept;
-
- template<typename T> simdjson_inline simdjson_result<T> get() &
-#if SIMDJSON_SUPPORTS_CONCEPTS
- noexcept(nothrow_gettable<T, haswell::ondemand::document_reference>);
-#else
- noexcept;
-#endif
- template<typename T> simdjson_inline simdjson_result<T> get() &&
-#if SIMDJSON_SUPPORTS_CONCEPTS
- noexcept(nothrow_gettable<T, haswell::ondemand::document_reference>);
-#else
- noexcept;
-#endif
-
- template<typename T> simdjson_inline error_code get(T &out) &
-#if SIMDJSON_SUPPORTS_CONCEPTS
- noexcept(nothrow_gettable<T, haswell::ondemand::document_reference>);
-#else
- noexcept;
-#endif
- template<typename T> simdjson_inline error_code get(T &out) &&
-#if SIMDJSON_SUPPORTS_CONCEPTS
- noexcept(nothrow_gettable<T, haswell::ondemand::document_reference>);
-#else
- noexcept;
-#endif
-#if SIMDJSON_EXCEPTIONS
- template <class T>
+ simdjson_inline operator haswell::ondemand::array() & noexcept(false);
+ simdjson_inline operator haswell::ondemand::object() & noexcept(false);
+ explicit simdjson_inline operator uint64_t() noexcept(false);
+ explicit simdjson_inline operator int64_t() noexcept(false);
+ explicit simdjson_inline operator double() noexcept(false);
+ explicit simdjson_inline operator std::string_view() noexcept(false);
+ explicit simdjson_inline operator haswell::ondemand::raw_json_string() noexcept(false);
+ explicit simdjson_inline operator bool() noexcept(false);
+ simdjson_inline operator haswell::ondemand::value() noexcept(false);
+#endif
+ simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
+ simdjson_inline simdjson_result<size_t> count_fields() & noexcept;
+ simdjson_inline simdjson_result<haswell::ondemand::value> at(size_t index) & noexcept;
+ simdjson_inline simdjson_result<haswell::ondemand::array_iterator> begin() & noexcept;
+ simdjson_inline simdjson_result<haswell::ondemand::array_iterator> end() & noexcept;
+ simdjson_inline simdjson_result<haswell::ondemand::value> find_field(std::string_view key) & noexcept;
+ simdjson_inline simdjson_result<haswell::ondemand::value> find_field(const char *key) & noexcept;
+ simdjson_inline simdjson_result<haswell::ondemand::value> operator[](std::string_view key) & noexcept;
+ simdjson_inline simdjson_result<haswell::ondemand::value> operator[](const char *key) & noexcept;
+ simdjson_result<haswell::ondemand::value> operator[](int) & noexcept = delete;
+ simdjson_inline simdjson_result<haswell::ondemand::value> find_field_unordered(std::string_view key) & noexcept;
+ simdjson_inline simdjson_result<haswell::ondemand::value> find_field_unordered(const char *key) & noexcept;
+ simdjson_inline simdjson_result<haswell::ondemand::json_type> type() noexcept;
+ simdjson_inline simdjson_result<bool> is_scalar() noexcept;
+ simdjson_inline simdjson_result<bool> is_string() noexcept;
+ simdjson_inline simdjson_result<const char *> current_location() noexcept;
+ simdjson_inline int32_t current_depth() const noexcept;
+ simdjson_inline bool at_end() const noexcept;
+ simdjson_inline bool is_negative() noexcept;
+ simdjson_inline simdjson_result<bool> is_integer() noexcept;
+ simdjson_inline simdjson_result<haswell::number_type> get_number_type() noexcept;
+ simdjson_inline simdjson_result<haswell::ondemand::number> get_number() noexcept;
+ /** @copydoc simdjson_inline std::string_view document::raw_json_token() const noexcept */
+ simdjson_inline simdjson_result<std::string_view> raw_json_token() noexcept;
+ /** @copydoc simdjson_inline simdjson_result<std::string_view> document::raw_json() noexcept */
+ simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
+
+ simdjson_inline simdjson_result<haswell::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
+ simdjson_inline simdjson_result<haswell::ondemand::value> at_path(std::string_view json_path) noexcept;
+#if SIMDJSON_SUPPORTS_CONCEPTS
+ template <typename Func>
+ requires std::invocable<Func, haswell::ondemand::value>
+#else
+ template <typename Func>
+#endif
+ simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
+#if SIMDJSON_STATIC_REFLECTION
+ template<constevalutil::fixed_string... FieldNames, typename T>
+ requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
+ simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept;
+#endif // SIMDJSON_STATIC_REFLECTION
+};
+
+
+} // namespace simdjson
+
+
+
+namespace simdjson {
+
+template<>
+struct simdjson_result<haswell::ondemand::document_reference> : public haswell::implementation_simdjson_result_base<haswell::ondemand::document_reference> {
+public:
+ simdjson_inline simdjson_result(haswell::ondemand::document_reference value, error_code error) noexcept;
+ simdjson_inline simdjson_result() noexcept = default;
+ simdjson_inline error_code rewind() noexcept;
+
+ simdjson_inline simdjson_result<haswell::ondemand::array> get_array() & noexcept;
+ simdjson_inline simdjson_result<haswell::ondemand::object> get_object() & noexcept;
+ simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
+ simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
+ simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
+ simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
+ simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
+ simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
+ simdjson_inline simdjson_result<uint16_t> get_uint16() noexcept;
+ simdjson_inline simdjson_result<int16_t> get_int16() noexcept;
+ simdjson_inline simdjson_result<uint8_t> get_uint8() noexcept;
+ simdjson_inline simdjson_result<int8_t> get_int8() noexcept;
+ simdjson_inline simdjson_result<double> get_double() noexcept;
+ simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
+ simdjson_inline simdjson_result<float> get_float() noexcept;
+ simdjson_inline simdjson_result<float> get_float_in_string() noexcept;
+#if SIMDJSON_SUPPORTS_FLOAT32_T
+ simdjson_inline simdjson_result<std::float32_t> get_float32() noexcept;
+#endif // SIMDJSON_SUPPORTS_FLOAT32_T
+#if SIMDJSON_SUPPORTS_FLOAT64_T
+ simdjson_inline simdjson_result<std::float64_t> get_float64() noexcept;
+#endif // SIMDJSON_SUPPORTS_FLOAT64_T
+ simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
+#if SIMDJSON_SUPPORTS_CHAR8_T
+ simdjson_inline simdjson_result<std::u8string_view> get_u8string(bool allow_replacement = false) noexcept;
+#endif // SIMDJSON_SUPPORTS_CHAR8_T
+ template <typename string_type>
+ simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
+ simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
+ simdjson_inline simdjson_result<haswell::ondemand::raw_json_string> get_raw_json_string() noexcept;
+ simdjson_inline simdjson_result<bool> get_bool() noexcept;
+ simdjson_inline simdjson_result<haswell::ondemand::value> get_value() noexcept;
+ simdjson_inline simdjson_result<bool> is_null() noexcept;
+
+ template<typename T> simdjson_inline simdjson_result<T> get() &
+#if SIMDJSON_SUPPORTS_CONCEPTS
+ noexcept(nothrow_gettable<T, haswell::ondemand::document_reference>);
+#else
+ noexcept;
+#endif
+ template<typename T> simdjson_inline simdjson_result<T> get() &&
+#if SIMDJSON_SUPPORTS_CONCEPTS
+ noexcept(nothrow_gettable<T, haswell::ondemand::document_reference>);
+#else
+ noexcept;
+#endif
+
+ template<typename T> simdjson_inline error_code get(T &out) &
+#if SIMDJSON_SUPPORTS_CONCEPTS
+ noexcept(nothrow_gettable<T, haswell::ondemand::document_reference>);
+#else
+ noexcept;
+#endif
+ template<typename T> simdjson_inline error_code get(T &out) &&
+#if SIMDJSON_SUPPORTS_CONCEPTS
+ noexcept(nothrow_gettable<T, haswell::ondemand::document_reference>);
+#else
+ noexcept;
+#endif
+#if SIMDJSON_EXCEPTIONS
+ template <class T>
explicit simdjson_inline operator T() noexcept(false);
simdjson_inline operator haswell::ondemand::array() & noexcept(false);
simdjson_inline operator haswell::ondemand::object() & noexcept(false);
@@ -118225,6 +115812,8 @@ public:
simdjson_inline simdjson_result<haswell::ondemand::number> get_number() noexcept;
/** @copydoc simdjson_inline std::string_view document_reference::raw_json_token() const noexcept */
simdjson_inline simdjson_result<std::string_view> raw_json_token() noexcept;
+ /** @copydoc simdjson_inline simdjson_result<std::string_view> document::raw_json() noexcept */
+ simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
simdjson_inline simdjson_result<haswell::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
simdjson_inline simdjson_result<haswell::ondemand::value> at_path(std::string_view json_path) noexcept;
@@ -120186,6 +117775,7 @@ concept key_selector_type = key_selector_detail::is_key_selector<T>::value;
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
+/* amalgamation skipped (editor-only): #include "simdjson/jsonpathutil.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/key_selector.h" */
/* amalgamation skipped (editor-only): #include <vector> */
/* amalgamation skipped (editor-only): #if SIMDJSON_STATIC_REFLECTION && SIMDJSON_SUPPORTS_CONCEPTS */
@@ -120730,6 +118320,13 @@ protected:
simdjson_warn_unused simdjson_inline error_code find_field_raw(const std::string_view key) noexcept;
+ /**
+ * The recursive step of for_each_at_path_with_wildcard: matches
+ * path[index] at this level and what follows below it.
+ */
+ template <typename Func>
+ inline error_code for_each_at_path_segments(const std::vector<simdjson::internal::json_path_segment> &path, size_t index, Func &callback) noexcept;
+
value_iterator iter{};
#if SIMDJSON_DEVELOPMENT_CHECKS
bool locked{false};
@@ -121594,6 +119191,14 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) {
std::is_default_constructible_v<element_type>,
"The specified type inside the unique_ptr must default constructible.");
+ // A null pointer is serialized as null.
+ bool is_null_value;
+ SIMDJSON_TRY( val.is_null().get(is_null_value) );
+ if (is_null_value) {
+ out.reset();
+ return SUCCESS;
+ }
+
// Own the allocation before get(): a user tag_invoke may throw.
std::unique_ptr<element_type> ptr(new (std::nothrow) element_type());
if (!ptr) {
@@ -121642,6 +119247,8 @@ constexpr bool user_defined_type = (std::is_class_v<T>
#endif // SIMDJSON_SUPPORTS_CHAR8_T
&& !concepts::optional_type<T> &&
!concepts::appendable_containers<T>
+// Smart pointers are handled by the generic smart-pointer tag_invoke.
+&& !concepts::smart_pointer<T>
// simdjson's own types (array, object, value, raw_json_string, number,
// document, document_reference) have dedicated get<T>() specializations and
// must never go through reflection.
@@ -122234,251 +119841,8 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept {
#endif
}
-template <typename simdjson_value, typename T>
- requires(user_defined_type<std::remove_cvref_t<T>>)
-error_code tag_invoke(deserialize_tag, simdjson_value &val, std::unique_ptr<T> &out) noexcept(false) {
- std::unique_ptr<T> ptr(new (std::nothrow) T());
- if (!ptr) {
- return MEMALLOC;
- }
- SIMDJSON_TRY(val.get(*ptr));
- out = std::move(ptr);
- return SUCCESS;
-}
-
-template <typename simdjson_value, typename T>
- requires(user_defined_type<std::remove_cvref_t<T>>)
-error_code tag_invoke(deserialize_tag, simdjson_value &val, std::shared_ptr<T> &out) noexcept(false) {
- std::shared_ptr<T> ptr(new (std::nothrow) T());
- if (!ptr) {
- return MEMALLOC;
- }
- SIMDJSON_TRY(val.get(*ptr));
- out = std::move(ptr);
- return SUCCESS;
-}
-
#endif // SIMDJSON_STATIC_REFLECTION
-////////////////////////////////////////
-// Unique pointers
-////////////////////////////////////////
-error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<bool> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_unique<bool>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_bool().get(*out));
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<int64_t> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_unique<int64_t>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_int64().get(*out));
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<uint64_t> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_unique<uint64_t>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_uint64().get(*out));
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<double> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_unique<double>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_double().get(*out));
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<std::string_view> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_unique<std::string_view>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_string().get(*out));
- return SUCCESS;
-}
-
-
-////////////////////////////////////////
-// Shared pointers
-////////////////////////////////////////
-error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<bool> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_shared<bool>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_bool().get(*out));
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<int64_t> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_shared<int64_t>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_int64().get(*out));
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<uint64_t> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_shared<uint64_t>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_uint64().get(*out));
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<double> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_shared<double>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_double().get(*out));
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<std::string_view> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_shared<std::string_view>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_string().get(*out));
- return SUCCESS;
-}
-
-
-////////////////////////////////////////
-// Explicit optional specializations
-////////////////////////////////////////
-
-////////////////////////////////////////
-// Explicit smart pointer specializations for string and int types
-////////////////////////////////////////
-error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<std::string> &out) noexcept {
- // Check if the value is null
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset(); // Set to nullptr
- return SUCCESS;
- }
-
- if (!out) {
- out = std::make_unique<std::string>();
- }
- std::string_view str;
- SIMDJSON_TRY(val.get_string().get(str));
- *out = std::string{str};
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<std::string> &out) noexcept {
- // Check if the value is null
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset(); // Set to nullptr
- return SUCCESS;
- }
-
- if (!out) {
- out = std::make_shared<std::string>();
- }
- std::string_view str;
- SIMDJSON_TRY(val.get_string().get(str));
- *out = std::string{str};
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<int> &out) noexcept {
- // Check if the value is null
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset(); // Set to nullptr
- return SUCCESS;
- }
-
- if (!out) {
- out = std::make_unique<int>();
- }
- int64_t temp;
- SIMDJSON_TRY(val.get_int64().get(temp));
- *out = static_cast<int>(temp);
- return SUCCESS;
-}
-
} // namespace simdjson
#endif // SIMDJSON_ONDEMAND_DESERIALIZE_H
@@ -122593,10 +119957,9 @@ simdjson_inline simdjson_result<std::string_view> array::raw_json() noexcept {
const uint8_t * starting_point{iter.peek_start()};
auto error = consume();
if(error) { return error; }
- // After 'consume()', we could be left pointing just beyond the document, but that
- // is ok because we are not going to dereference the final pointer position, we just
- // use it to compute the length in bytes.
- const uint8_t * final_point{iter._json_iter->unsafe_pointer()};
+ // After 'consume()', the last token consumed is the closing bracket: we end on it
+ // rather than on the next token, which may follow white space.
+ const uint8_t * final_point{iter._json_iter->token.peek(-1) + 1};
return std::string_view(reinterpret_cast<const char*>(starting_point), size_t(final_point - starting_point));
}
@@ -122680,12 +120043,23 @@ template <typename Func>
template <typename Func>
#endif
inline error_code array::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
- auto result_pair = get_next_key_and_json_path(json_path);
- std::string_view key = result_pair.first;
- std::string_view remaining_path = result_pair.second;
+ // The path is split once: every element reached by a wildcard sees the same
+ // remaining path, so re-parsing it at each one would be quadratic.
+ const std::vector<internal::json_path_segment> path = internal::split_json_path(json_path);
+ // A path that cannot be parsed matches nothing, whatever the document: say
+ // so rather than letting the walk skip it as a mismatch below a wildcard.
+ if (path.back().key.empty()) {
+ return INVALID_JSON_POINTER;
+ }
+ return for_each_at_path_segments(path, 0, callback);
+}
+
+template <typename Func>
+inline error_code array::for_each_at_path_segments(const std::vector<internal::json_path_segment> &path, size_t index, Func &callback) noexcept {
+ std::string_view key = path[index].key;
+ const bool last = index + 1 == path.size();
// Empty key means the path segment could not be parsed (including malformed
- // bracket-quoted keys). Without this check, an empty key is treated as index
- // 0 and remaining_path may be unchanged, causing infinite recursion.
+ // bracket-quoted keys).
if (key.empty()) {
return INVALID_JSON_POINTER;
}
@@ -122694,11 +120068,11 @@ inline error_code array::for_each_at_path_with_wildcard(std::string_view json_pa
for(auto element: *this) {
value val;
SIMDJSON_TRY(element.get(val));
- if (remaining_path.empty()) {
+ if (last) {
callback(val);
} else {
// An element that does not match the rest of the path is skipped.
- error_code err = val.for_each_at_path_with_wildcard(remaining_path, callback);
+ error_code err = val.for_each_at_path_segments(path, index + 1, callback);
if (err && !internal::is_matching_failure_error(err)) { return err; }
}
}
@@ -122717,16 +120091,16 @@ inline error_code array::for_each_at_path_with_wildcard(std::string_view json_pa
value val;
if (!error) { error = at(idx).get(val); }
if (error) {
- if (error == INDEX_OUT_OF_BOUNDS && internal::json_path_has_wildcard(remaining_path)) {
+ if (error == INDEX_OUT_OF_BOUNDS && path[index].wildcard_follows) {
return SUCCESS;
}
return error;
}
- if (remaining_path.empty()){
+ if (last){
callback(val);
return SUCCESS;
} else {
- return val.for_each_at_path_with_wildcard(remaining_path, callback);
+ return val.for_each_at_path_segments(path, index + 1, callback);
}
}
}
@@ -123384,6 +120758,19 @@ template <typename Func>
template <typename Func>
#endif
inline error_code value::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
+ // The path is split once: every value reached by a wildcard sees the same
+ // remaining path, so re-parsing it at each one would be quadratic.
+ const std::vector<internal::json_path_segment> path = internal::split_json_path(json_path);
+ // A path that cannot be parsed matches nothing, whatever the document: say
+ // so rather than letting the walk skip it as a mismatch below a wildcard.
+ if (path.back().key.empty()) {
+ return INVALID_JSON_POINTER;
+ }
+ return for_each_at_path_segments(path, 0, callback);
+}
+
+template <typename Func>
+inline error_code value::for_each_at_path_segments(const std::vector<internal::json_path_segment> &path, size_t index, Func &callback) noexcept {
// Every recursive step of for_each_at_path_with_wildcard goes through this
// function, and each one descends one level into the document. A path with
// many segments applied to a deeply nested document would otherwise recurse
@@ -123392,10 +120779,16 @@ inline error_code value::for_each_at_path_with_wildcard(std::string_view json_pa
json_type t;
SIMDJSON_TRY(type().get(t));
switch (t) {
- case json_type::array:
- return (*this).get_array().for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
- case json_type::object:
- return (*this).get_object().for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
+ case json_type::array: {
+ array a;
+ SIMDJSON_TRY(get_array().get(a));
+ return a.for_each_at_path_segments(path, index, callback);
+ }
+ case json_type::object: {
+ object o;
+ SIMDJSON_TRY(get_object().get(o));
+ return o.for_each_at_path_segments(path, index, callback);
+ }
default:
return INVALID_JSON_POINTER;
}
@@ -123768,7 +121161,8 @@ simdjson_inline document document::start(json_iterator &&iter) noexcept {
}
inline void document::rewind() noexcept {
- iter.rewind();
+ // A document that is no longer alive is left as it is (see is_alive()).
+ static_cast<void>(iter.rewind());
}
inline std::string document::to_debug_string() noexcept {
@@ -124068,7 +121462,13 @@ simdjson_warn_unused simdjson_inline error_code document::consume() noexcept {
auto error = is_scalar().get(scalar);
if(error) { return error; }
if(scalar) {
- iter.return_current_and_advance();
+ // Step over the scalar and leave depth 0, as skip_child() does: otherwise a
+ // document_stream takes the next document as the rest of this one. At depth
+ // 0 the scalar has already been consumed (e.g., by get_int64()).
+ if(iter.depth() > 0) {
+ iter.return_current_and_advance();
+ iter.ascend_to(0);
+ }
return SUCCESS;
}
error = iter.skip_child(0);
@@ -124081,10 +121481,36 @@ simdjson_inline simdjson_result<std::string_view> document::raw_json() noexcept
const uint8_t * starting_point{_iter.peek_start()};
auto error = consume();
if(error) { return error; }
- // After 'consume()', we could be left pointing just beyond the document, but that
- // is ok because we are not going to dereference the final pointer position, we just
- // use it to compute the length in bytes.
- const uint8_t * final_point{iter.unsafe_pointer()};
+ // After 'consume()', the iterator points at the token after the document. In a
+ // document_stream, that is the next document, past white space and separators, or
+ // the end of the input when a truncated document follows. We end on the last byte
+ // of this document instead.
+ const uint8_t * final_point;
+ if(*starting_point == '{' || *starting_point == '[') {
+ // The last token consumed is the closing brace or bracket.
+ final_point = iter.token.peek(-1) + 1;
+ } else {
+ // A scalar is a single token: a string ends at its closing quote, anything else
+ // at the first white space, structural character, quote, RS (0x1E) or NUL.
+ const uint8_t * next_token{iter.unsafe_pointer()};
+ final_point = starting_point + 1;
+ if(*starting_point == '"') {
+ while(final_point < next_token) {
+ uint8_t c = *final_point++;
+ if(c == '\\') {
+ final_point++;
+ } else if(c == '"') {
+ break;
+ }
+ }
+ if(final_point > next_token) { final_point = next_token; }
+ } else {
+ while(final_point < next_token && !jsoncharutils::is_structural_or_whitespace(*final_point)
+ && *final_point != '"' && *final_point != 0x1E && *final_point != 0) {
+ final_point++;
+ }
+ }
+ }
return std::string_view(reinterpret_cast<const char*>(starting_point), size_t(final_point - starting_point));
}
@@ -124131,7 +121557,9 @@ simdjson_inline simdjson_result<std::string_view> document::raw_json_token() noe
}
simdjson_inline simdjson_result<value> document::at_pointer(std::string_view json_pointer) noexcept {
- rewind(); // Rewind the document each time at_pointer is called
+ // Rewind the document each time at_pointer is called. After a fatal error,
+ // the document cannot be walked again: we report the error instead.
+ SIMDJSON_TRY(iter.rewind());
if (json_pointer.empty()) {
return this->get_value();
}
@@ -124149,7 +121577,9 @@ simdjson_inline simdjson_result<value> document::at_pointer(std::string_view jso
}
simdjson_inline simdjson_result<value> document::at_path(std::string_view json_path) noexcept {
- rewind(); // Rewind the document each time at_pointer is called
+ // Rewind the document each time at_path is called. After a fatal error,
+ // the document cannot be walked again: we report the error instead.
+ SIMDJSON_TRY(iter.rewind());
if (json_path.empty()) {
return this->get_value();
}
@@ -124172,7 +121602,9 @@ template <typename Func>
template <typename Func>
#endif
simdjson_inline error_code document::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
- rewind(); // Rewind the document each time for_each_at_path_with_wildcard is called
+ // Rewind the document each time for_each_at_path_with_wildcard is called. After
+ // a fatal error, the document cannot be walked again: we report the error instead.
+ SIMDJSON_TRY(iter.rewind());
if (json_path.empty()) {
return INVALID_JSON_POINTER;
}
@@ -124510,11 +121942,6 @@ simdjson_inline simdjson_result<haswell::ondemand::number> simdjson_result<haswe
#if SIMDJSON_EXCEPTIONS
-template <class T, typename std::enable_if<std::is_same<T, haswell::ondemand::document>::value == false>::type>
-simdjson_inline simdjson_result<haswell::ondemand::document>::operator T() noexcept(false) {
- if (error()) { throw simdjson_error(error()); }
- return first;
-}
simdjson_inline simdjson_result<haswell::ondemand::document>::operator haswell::ondemand::array() & noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
@@ -124575,6 +122002,11 @@ simdjson_inline simdjson_result<std::string_view> simdjson_result<haswell::ondem
return first.raw_json_token();
}
+simdjson_inline simdjson_result<std::string_view> simdjson_result<haswell::ondemand::document>::raw_json() noexcept {
+ if (error()) { return error(); }
+ return first.raw_json();
+}
+
simdjson_inline simdjson_result<haswell::ondemand::value> simdjson_result<haswell::ondemand::document>::at_pointer(std::string_view json_pointer) noexcept {
if (error()) { return error(); }
return first.at_pointer(json_pointer);
@@ -125061,6 +122493,11 @@ simdjson_inline simdjson_result<std::string_view> simdjson_result<haswell::ondem
return first.raw_json_token();
}
+simdjson_inline simdjson_result<std::string_view> simdjson_result<haswell::ondemand::document_reference>::raw_json() noexcept {
+ if (error()) { return error(); }
+ return first.raw_json();
+}
+
simdjson_inline simdjson_result<haswell::ondemand::value> simdjson_result<haswell::ondemand::document_reference>::at_pointer(std::string_view json_pointer) noexcept {
if (error()) { return error(); }
return first.at_pointer(json_pointer);
@@ -125452,6 +122889,13 @@ simdjson_inline bool document_stream::skip_to_delimiter(uint8_t delimiter) noexc
}
inline void document_stream::next_document() noexcept {
+ // Iteration stops at the first document that met a fatal error. The error
+ // may have abandoned the document's iterator, which then no longer has a
+ // parser to walk the rest of the document with.
+ if (!doc.iter.is_alive() || doc.iter.error) {
+ error = doc.iter.error ? doc.iter.error : TAPE_ERROR;
+ return;
+ }
// A delimiter that cannot occur inside a document tells us where the current
// one ends, so we can jump there instead of walking every structural. Only
// valid while the iterator is still inside the document: a consumed document
@@ -125601,7 +123045,10 @@ simdjson_inline std::string_view document_stream::iterator::source() const noexc
cur_struct_index++;
}
- while (cur_struct_index <= static_cast<int64_t>(stream->parser->implementation->n_structural_indexes)) {
+ // The structural index at n_structural_indexes is the end of the batch, not
+ // a token: it must not be read as a closing bracket.
+ const int64_t n_structural_indexes = static_cast<int64_t>(stream->parser->implementation->n_structural_indexes);
+ while (cur_struct_index < n_structural_indexes) {
switch (stream->buf[stream->batch_start + stream->parser->implementation->structural_indexes[cur_struct_index]]) {
case '{': case '[':
depth++;
@@ -125613,6 +123060,11 @@ simdjson_inline std::string_view document_stream::iterator::source() const noexc
if (depth == 0) { break; }
cur_struct_index++;
}
+ // An invalid last document such as "[1 2" is never closed: it runs to the
+ // end of the batch, and not one byte past it.
+ if (cur_struct_index >= n_structural_indexes) {
+ return std::string_view(reinterpret_cast<const char*>(stream->buf) + current_index(), stream->batch_start + stream->parser->implementation->structural_indexes[cur_struct_index] - current_index());
+ }
return std::string_view(reinterpret_cast<const char*>(stream->buf) + current_index(), stream->parser->implementation->structural_indexes[cur_struct_index] - current_index() + stream->batch_start + 1);;
}
@@ -125629,7 +123081,12 @@ inline void document_stream::load_from_stage1_thread() noexcept {
// enough memory to swap to, as well.
std::swap(stage1_thread_parser,*parser);
error = stage1_thread_error;
- if (error) { return; }
+ // An EMPTY batch (no complete document in it, e.g. a run of white space at
+ // least batch_size long) is not the end of the stream: next() moves on to
+ // the batch after it, so queue that batch now as we would after a success.
+ // Returning here would leave no job for the next call, which then swaps
+ // the stale previous batch back in and ends the stream early.
+ if (error && error != EMPTY) { return; }
// If there's anything left, start the stage 1 thread!
if (next_batch_start() < len) {
@@ -125741,12 +123198,14 @@ simdjson_inline raw_json_string field::key() const noexcept {
simdjson_inline std::string_view field::key_raw_json_token() const noexcept {
SIMDJSON_ASSUME(first.buf != nullptr); // We would like to call .alive() by Visual Studio won't let us.
- return std::string_view(reinterpret_cast<const char*>(first.buf-1), second.iter._json_iter->token.peek(-1) - first.buf + 1);
+ // The ':' is the token just before the value. Use the value's start position
+ // rather than the current position, which moves once the value is consumed.
+ return std::string_view(reinterpret_cast<const char*>(first.buf-1), second.iter._json_iter->token.peek(second.iter.start_position() - 1) - first.buf + 1);
}
simdjson_inline std::string_view field::escaped_key() const noexcept {
SIMDJSON_ASSUME(first.buf != nullptr); // We would like to call .alive() by Visual Studio won't let us.
- auto end_quote = second.iter._json_iter->token.peek(-1);
+ auto end_quote = second.iter._json_iter->token.peek(second.iter.start_position() - 1);
while(*end_quote != '"') end_quote--;
return std::string_view(reinterpret_cast<const char*>(first.buf), end_quote - first.buf);
}
@@ -125917,11 +123376,14 @@ simdjson_inline json_iterator::json_iterator(const uint8_t *buf, ondemand::parse
}
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
-inline void json_iterator::rewind() noexcept {
+simdjson_inline error_code json_iterator::rewind() noexcept {
+ // abandon() let go of the parser: the iterator cannot be walked again.
+ if (!is_alive()) { return error ? error : UNINITIALIZED; }
token.set_position( root_position() );
logger::log_headers(); // We start again
_string_buf_loc = parser->string_buf.get();
_depth = 1;
+ return SUCCESS;
}
inline bool json_iterator::balanced() const noexcept {
@@ -125945,6 +123407,18 @@ inline bool json_iterator::balanced() const noexcept {
}
+// Depth change of each structural character when skipping: +1 for [ and {, -1 for ] and }.
+static constexpr int8_t skip_depth_delta[256] = {
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,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,
+};
+
// GCC 7 warns when the first line of this function is inlined away into oblivion due to the caller
// relating depth and parent_depth, which is a desired effect. The warning does not show up if the
// skip_child() function is not marked inline).
@@ -126000,7 +123474,30 @@ simdjson_warn_unused simdjson_inline error_code json_iterator::skip_child(depth_
break;
}
- // Now that we've considered the first value, we only increment/decrement for arrays/objects
+ // Now that we've considered the first value, we only increment/decrement for arrays/objects.
+ //
+ // Without logging, look up the depth change of each token in a table instead of branching
+ // on it: the next byte is hard to predict, so a switch mispredicts often. The loop also keeps
+ // the position and depth in locals, because the byte loads are char loads that may alias
+ // token._position and _depth, which would force a store of both on every token.
+ if (!logger::LOG_ENABLED) {
+ token_position pos = token.position();
+ const token_position end = end_position();
+ const uint8_t *const json = token.buf;
+ depth_t depth = _depth;
+ while (pos < end) {
+ depth += skip_depth_delta[json[*pos++]];
+ if (depth <= parent_depth) {
+ token.set_position(pos);
+ _depth = depth;
+ return SUCCESS;
+ }
+ }
+ token.set_position(pos);
+ _depth = depth;
+ return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "not enough close braces");
+ }
+
while (position() < end_position()) {
switch (*return_current_and_advance()) {
case '[': case '{':
@@ -126285,6 +123782,17 @@ simdjson_warn_unused simdjson_inline error_code json_iterator::optional_error(er
simdjson_warn_unused simdjson_inline bool json_iterator::copy_to_buffer(const uint8_t *json, uint32_t max_len, uint8_t *tmpbuf, size_t N) noexcept {
// This function is not expected to be called in performance-sensitive settings.
+ // peek_root_length() stops at the next structural index. Stage 1 omits the
+ // comma or RS (0x1E) that separates stream documents, so that byte and the
+ // whitespace around it remain in the span. RS is not a valid number finisher.
+ while (max_len > 0) {
+ const uint8_t last = json[max_len - 1];
+ if (last != ' ' && last != '\t' && last != '\n' && last != '\r' &&
+ last != ',' && last != 0x1E) {
+ break;
+ }
+ max_len--;
+ }
// Let us guard against silly cases:
if((N < max_len) || (N == 0)) { return false; }
// Copy to the buffer.
@@ -126926,7 +124434,9 @@ simdjson_inline simdjson_result<std::string_view> object::raw_json() noexcept {
const uint8_t * starting_point{iter.peek_start()};
auto error = consume();
if(error) { return error; }
- const uint8_t * final_point{iter._json_iter->peek()};
+ // After 'consume()', the last token consumed is the closing brace: we end on it
+ // rather than on the next token, which may follow white space.
+ const uint8_t * final_point{iter._json_iter->token.peek(-1) + 1};
return std::string_view(reinterpret_cast<const char*>(starting_point), size_t(final_point - starting_point));
}
@@ -127019,9 +124529,21 @@ template <typename Func>
template <typename Func>
#endif
inline error_code object::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
- auto result_pair = get_next_key_and_json_path(json_path);
- std::string_view key = result_pair.first;
- std::string_view remaining_path = result_pair.second;
+ // The path is split once: every field reached by a wildcard sees the same
+ // remaining path, so re-parsing it at each one would be quadratic.
+ const std::vector<internal::json_path_segment> path = internal::split_json_path(json_path);
+ // A path that cannot be parsed matches nothing, whatever the document: say
+ // so rather than letting the walk skip it as a mismatch below a wildcard.
+ if (path.back().key.empty()) {
+ return INVALID_JSON_POINTER;
+ }
+ return for_each_at_path_segments(path, 0, callback);
+}
+
+template <typename Func>
+inline error_code object::for_each_at_path_segments(const std::vector<internal::json_path_segment> &path, size_t index, Func &callback) noexcept {
+ std::string_view key = path[index].key;
+ const bool last = index + 1 == path.size();
// Empty key means the path segment could not be parsed (including malformed
// bracket-quoted keys). Match DOM at_path_with_wildcard and avoid treating
// "no progress" as a recursive lookup of "".
@@ -127034,11 +124556,11 @@ inline error_code object::for_each_at_path_with_wildcard(std::string_view json_p
for (auto field : *this) {
value val;
SIMDJSON_TRY(field.value().get(val));
- if (remaining_path.empty()) {
+ if (last) {
callback(val);
} else {
// A field that does not match the rest of the path is skipped.
- error_code err = val.for_each_at_path_with_wildcard(remaining_path, callback);
+ error_code err = val.for_each_at_path_segments(path, index + 1, callback);
if (err && !internal::is_matching_failure_error(err)) { return err; }
}
}
@@ -127047,17 +124569,17 @@ inline error_code object::for_each_at_path_with_wildcard(std::string_view json_p
value val;
error_code error = find_field(key).get(val);
if (error) {
- if (error == NO_SUCH_FIELD && internal::json_path_has_wildcard(remaining_path)) {
+ if (error == NO_SUCH_FIELD && path[index].wildcard_follows) {
return SUCCESS;
}
return error;
}
- if (remaining_path.empty()) {
+ if (last) {
callback(val);
return SUCCESS;
} else {
- return val.for_each_at_path_with_wildcard(remaining_path, callback);
+ return val.for_each_at_path_segments(path, index + 1, callback);
}
}
}
@@ -127710,6 +125232,7 @@ simdjson_inline simdjson_warn_unused bool parser::string_buffer_overflow(const u
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(padded_string_view json) & noexcept {
if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
+ json.use_trailing_whitespace_as_padding();
json.remove_utf8_bom();
_document_len = json.length();
@@ -127727,6 +125250,7 @@ simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(p
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate_allow_incomplete_json(padded_string_view json) & noexcept {
if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
+ json.use_trailing_whitespace_as_padding();
json.remove_utf8_bom();
_document_len = json.length();
@@ -127782,6 +125306,7 @@ simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(c
simdjson_warn_unused simdjson_inline simdjson_result<json_iterator> parser::iterate_raw(padded_string_view json) & noexcept {
if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
+ json.use_trailing_whitespace_as_padding();
json.remove_utf8_bom();
@@ -127808,6 +125333,7 @@ inline simdjson_result<document_stream> parser::iterate_many(const char *buf, si
}
inline simdjson_result<document_stream> parser::iterate_many(padded_string_view s, size_t batch_size) noexcept {
if (!s.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
+ s.use_trailing_whitespace_as_padding();
return iterate_many(s.data(), s.length(), batch_size);
}
inline simdjson_result<document_stream> parser::iterate_many(const padded_string &s, size_t batch_size) noexcept {
@@ -127843,6 +125369,7 @@ inline simdjson_result<document_stream> parser::iterate_many(const char *buf, si
}
inline simdjson_result<document_stream> parser::iterate_many(padded_string_view s, size_t batch_size, bool allow_comma_separated) noexcept {
if (!s.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
+ s.use_trailing_whitespace_as_padding();
return iterate_many(s.data(), s.length(), batch_size, allow_comma_separated);
}
inline simdjson_result<document_stream> parser::iterate_many(const padded_string &s, size_t batch_size, bool allow_comma_separated) noexcept {
@@ -127888,6 +125415,7 @@ inline simdjson_result<document_stream> parser::iterate_many(const char *buf, si
}
inline simdjson_result<document_stream> parser::iterate_many(padded_string_view s, size_t batch_size, stream_format format) noexcept {
if (!s.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
+ s.use_trailing_whitespace_as_padding();
return iterate_many(s.data(), s.length(), batch_size, format);
}
inline simdjson_result<document_stream> parser::iterate_many(const std::string &s, size_t batch_size, stream_format format) noexcept {
@@ -128042,17 +125570,18 @@ simdjson_inline bool raw_json_string::is_free_from_unescaped_quote(const char* t
return true;
}
-
simdjson_inline bool raw_json_string::unsafe_is_equal(size_t length, std::string_view target) const noexcept {
// If we are going to call memcmp, then we must know something about the length of the raw_json_string.
- return (length >= target.size()) && (raw()[target.size()] == '"') && !memcmp(raw(), target.data(), target.size());
+ return (length >= target.size()) && (raw()[target.size()] == '"') && !memcmp(raw(), target.data(), target.size())
+ && !escapes_trailing_quote(target);
}
simdjson_inline bool raw_json_string::unsafe_is_equal(std::string_view target) const noexcept {
// Assumptions: does not contain unescaped quote characters("), and
// the raw content is quote terminated within a valid JSON string.
if(target.size() <= SIMDJSON_PADDING) {
- return (raw()[target.size()] == '"') && !memcmp(raw(), target.data(), target.size());
+ return (raw()[target.size()] == '"') && !memcmp(raw(), target.data(), target.size())
+ && !escapes_trailing_quote(target);
}
// Past SIMDJSON_PADDING we can no longer rely on the padding to keep the
// comparison in bounds, so we must stop at the quote terminating the key.
@@ -128078,7 +125607,7 @@ simdjson_inline bool raw_json_string::is_equal(std::string_view target) const no
escaping = false;
}
}
- if(r[pos] != '"') { return false; }
+ if((r[pos] != '"') || escaping) { return false; }
return true;
}
@@ -128091,7 +125620,7 @@ simdjson_inline bool raw_json_string::unsafe_is_equal(const char * target) const
for(;target[pos];pos++) {
if(r[pos] != target[pos]) { return false; }
}
- if(r[pos] != '"') { return false; }
+ if((r[pos] != '"') || escapes_trailing_quote(std::string_view(target, pos))) { return false; }
return true;
}
@@ -128116,7 +125645,7 @@ simdjson_inline bool raw_json_string::is_equal(const char* target) const noexcep
escaping = false;
}
}
- if(r[pos] != '"') { return false; }
+ if((r[pos] != '"') || escaping) { return false; }
return true;
}
@@ -128228,7 +125757,11 @@ simdjson_inline uint32_t token_iterator::peek_index(token_position position) con
return *position;
}
simdjson_inline uint32_t token_iterator::peek_length(token_position position) const noexcept {
- return *(position+1) - *position;
+ // In the last batch of a document_stream, the index past the end of the
+ // structural indexes holds the start of the truncated bytes (see
+ // document_stream::truncated_bytes()), which can come before the end of the
+ // input: a token at the end is then empty.
+ return *(position+1) > *position ? *(position+1) - *position : 0;
}
simdjson_inline uint32_t token_iterator::peek_root_length(token_position position) const noexcept {
@@ -128243,7 +125776,7 @@ simdjson_inline uint32_t token_iterator::peek_index(int32_t delta) const noexcep
return *(_position+delta);
}
simdjson_inline uint32_t token_iterator::peek_length(int32_t delta) const noexcept {
- return *(_position+delta+1) - *(_position+delta);
+ return peek_length(_position+delta);
}
simdjson_inline token_position token_iterator::position() const noexcept {
@@ -128302,6 +125835,8 @@ simdjson_inline simdjson_result<haswell::ondemand::token_iterator>::simdjson_res
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
+#include <cstring> // std::memset (zeroing the root-number scratch padding)
+
namespace simdjson {
namespace haswell {
namespace ondemand {
@@ -128348,6 +125883,10 @@ simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::start
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::started_object() noexcept {
assert_at_container_start();
#if SIMDJSON_DEVELOPMENT_CHECKS
+ // The development checks record the start of every open container in a
+ // buffer of max_depth() entries: a document nested deeper than that is
+ // rejected, as the DOM parser does.
+ if (size_t(_depth) >= _json_iter->parser->max_depth()) { return report_error(DEPTH_ERROR, "Exceeded max depth!"); }
_json_iter->set_start_position(_depth, start_position());
#endif
if (*_json_iter->peek() == '}') {
@@ -128757,6 +126296,10 @@ inline std::string value_iterator::to_string() const noexcept {
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::started_array() noexcept {
assert_at_container_start();
+#if SIMDJSON_DEVELOPMENT_CHECKS
+ // See started_object().
+ if (size_t(_depth) >= _json_iter->parser->max_depth()) { return report_error(DEPTH_ERROR, "Exceeded max depth!"); }
+#endif
if (*_json_iter->peek() == ']') {
logger::log_value(*_json_iter, "empty array");
_json_iter->return_current_and_advance();
@@ -128962,8 +126505,10 @@ simdjson_inline simdjson_result<haswell::number_type> value_iterator::get_root_n
// Per https://www.exploringbinary.com/maximum-number-of-decimal-digits-in-binary-floating-point-numbers/,
// 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest
// number: -0.<fraction>e-308.
- uint8_t tmpbuf[1074+8+1+1];
- tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
+ // +SIMDJSON_PADDING: the digit scan reads up to 7 bytes past the terminator.
+ uint8_t tmpbuf[1074+8+1+1+SIMDJSON_PADDING];
+ // Null terminate, and zero the padding as allocate_padded_buffer does.
+ std::memset(tmpbuf+1074+8+1, 0, 1+SIMDJSON_PADDING);
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
if(numberparsing::check_if_integer(json, max_len)) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
@@ -128984,8 +126529,10 @@ simdjson_inline simdjson_result<number> value_iterator::get_root_number(bool che
// 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest
// number: -0.<fraction>e-308.
// NOTE: the current approach doesn't work for very big integer numbers containing more than 1074 digits.
- uint8_t tmpbuf[1074+8+1+1];
- tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
+ // +SIMDJSON_PADDING: the digit scan reads up to 7 bytes past the terminator.
+ uint8_t tmpbuf[1074+8+1+1+SIMDJSON_PADDING];
+ // Null terminate, and zero the padding as allocate_padded_buffer does.
+ std::memset(tmpbuf+1074+8+1, 0, 1+SIMDJSON_PADDING);
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
if(numberparsing::check_if_integer(json, max_len)) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
@@ -129055,10 +126602,10 @@ simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> value_iterator::g
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> value_iterator::get_root_uint64_in_string(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("uint64");
- uint8_t tmpbuf[20+1+1]{}; // <20 digits> is the longest possible unsigned integer
- tmpbuf[20+1] = '\0'; // make sure that buffer is always null terminated.
- if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 20+1)) {
- logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 20 characters");
+ uint8_t tmpbuf[20+2+1]{}; // "<20 digits>" is the longest possible unsigned integer
+ tmpbuf[20+2] = '\0'; // make sure that buffer is always null terminated.
+ if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 20+2)) {
+ logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 22 characters");
return NUMBER_ERROR;
}
auto result = numberparsing::parse_unsigned_in_string(tmpbuf);
@@ -129088,10 +126635,10 @@ simdjson_warn_unused simdjson_inline simdjson_result<int64_t> value_iterator::ge
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> value_iterator::get_root_int64_in_string(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("int64");
- uint8_t tmpbuf[20+1+1]; // -<19 digits> is the longest possible integer
- tmpbuf[20+1] = '\0'; // make sure that buffer is always null terminated.
- if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 20+1)) {
- logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 20 characters");
+ uint8_t tmpbuf[20+2+1]; // "-<19 digits>" is the longest possible integer
+ tmpbuf[20+2] = '\0'; // make sure that buffer is always null terminated.
+ if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 20+2)) {
+ logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 22 characters");
return NUMBER_ERROR;
}
@@ -129108,8 +126655,10 @@ simdjson_warn_unused simdjson_inline simdjson_result<double> value_iterator::get
// Per https://www.exploringbinary.com/maximum-number-of-decimal-digits-in-binary-floating-point-numbers/,
// 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest
// number: -0.<fraction>e-308.
- uint8_t tmpbuf[1074+8+1+1]; // +1 for null termination.
- tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
+ // +SIMDJSON_PADDING: the digit scan reads up to 7 bytes past the terminator.
+ uint8_t tmpbuf[1074+8+1+1+SIMDJSON_PADDING]; // +1 for null termination.
+ // Null terminate, and zero the padding as allocate_padded_buffer does.
+ std::memset(tmpbuf+1074+8+1, 0, 1+SIMDJSON_PADDING);
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters");
return NUMBER_ERROR;
@@ -129128,8 +126677,10 @@ simdjson_warn_unused simdjson_inline simdjson_result<double> value_iterator::get
// Per https://www.exploringbinary.com/maximum-number-of-decimal-digits-in-binary-floating-point-numbers/,
// 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest
// number: -0.<fraction>e-308.
- uint8_t tmpbuf[1074+8+1+1]; // +1 for null termination.
- tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
+ // +SIMDJSON_PADDING: the digit scan reads up to 7 bytes past the terminator.
+ uint8_t tmpbuf[1074+8+1+1+SIMDJSON_PADDING]; // +1 for null termination.
+ // Null terminate, and zero the padding as allocate_padded_buffer does.
+ std::memset(tmpbuf+1074+8+1, 0, 1+SIMDJSON_PADDING);
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters");
return NUMBER_ERROR;
@@ -129148,8 +126699,10 @@ simdjson_warn_unused simdjson_inline simdjson_result<float> value_iterator::get_
// We use the same buffer size as get_root_double: the number of significant
// digits that matter is smaller for binary32, but the JSON document may still
// spell out a long number that we must parse (and round) faithfully.
- uint8_t tmpbuf[1074+8+1+1]; // +1 for null termination.
- tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
+ // +SIMDJSON_PADDING: the digit scan reads up to 7 bytes past the terminator.
+ uint8_t tmpbuf[1074+8+1+1+SIMDJSON_PADDING]; // +1 for null termination.
+ // Null terminate, and zero the padding as allocate_padded_buffer does.
+ std::memset(tmpbuf+1074+8+1, 0, 1+SIMDJSON_PADDING);
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters");
return NUMBER_ERROR;
@@ -129165,8 +126718,10 @@ simdjson_warn_unused simdjson_inline simdjson_result<float> value_iterator::get_
simdjson_warn_unused simdjson_inline simdjson_result<float> value_iterator::get_root_float_in_string(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("float");
- uint8_t tmpbuf[1074+8+1+1]; // +1 for null termination.
- tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
+ // +SIMDJSON_PADDING: the digit scan reads up to 7 bytes past the terminator.
+ uint8_t tmpbuf[1074+8+1+1+SIMDJSON_PADDING]; // +1 for null termination.
+ // Null terminate, and zero the padding as allocate_padded_buffer does.
+ std::memset(tmpbuf+1074+8+1, 0, 1+SIMDJSON_PADDING);
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters");
return NUMBER_ERROR;
@@ -129187,7 +126742,7 @@ simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::get_r
// "true" and "false" are 4 and 5 characters long, respectively.
bool value_true = (max_len >= 4 && !atomparsing::str4ncmp(json, "true") &&
(max_len == 4 || jsoncharutils::is_structural_or_whitespace(json[4])));
- bool value_false = (max_len >= 5 && !atomparsing::str4ncmp(json, "false") &&
+ bool value_false = (max_len >= 5 && !atomparsing::str4ncmp(json, "fals") && json[4] == 'e' &&
(max_len == 5 || jsoncharutils::is_structural_or_whitespace(json[5])));
if(value_true == false && value_false == false) { return incorrect_type_error("Not a boolean"); }
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
@@ -129827,49 +127382,6 @@ namespace json_path {
using ::simdjson::haswell::ondemand::value;
-// Path step types
-enum class step_type {
- field, // .field_name or ["field_name"]
- array_index // [index]
-};
-
-// Represents a single step in a JSON path
-template<std::size_t N>
-struct path_step {
- step_type type;
- char key[N]; // Field name (empty for array indices)
- std::size_t index; // Array index (0 for field access)
-
- constexpr path_step(step_type t, const char (&k)[N], std::size_t idx = 0)
- : type(t), index(idx) {
- for (std::size_t i = 0; i < N; ++i) {
- key[i] = k[i];
- }
- }
-
- constexpr std::string_view key_view() const {
- return {key, N - 1};
- }
-};
-
-// Helper to create field step
-template<std::size_t N>
-consteval auto make_field_step(const char (&name)[N]) {
- return path_step<N>(step_type::field, name, 0);
-}
-
-// Helper to create array index step
-consteval auto make_index_step(std::size_t idx) {
- return path_step<1>(step_type::array_index, "", idx);
-}
-
-// Parse state for compile-time JSON path parsing
-struct parse_result {
- bool success;
- std::size_t pos;
- std::string_view error_msg;
-};
-
// Compile-time JSON path parser
// Supports subset: .field, ["field"], [index], nested combinations
template<constevalutil::fixed_string Path>
@@ -129883,77 +127395,6 @@ struct json_path_parser {
}
return 0;
}
-
- // Count the number of steps in the path at compile time
- static consteval std::size_t count_steps() {
- std::size_t count = 0;
- std::size_t i = skip_root();
-
- while (i < path_str.size()) {
- if (path_str[i] == '.') {
- // Field access: .field
- ++i;
- if (i >= path_str.size()) break;
-
- // Skip field name
- while (i < path_str.size() && path_str[i] != '.' && path_str[i] != '[') {
- ++i;
- }
- ++count;
- } else if (path_str[i] == '[') {
- // Array or bracket notation
- ++i;
- if (i >= path_str.size()) break;
-
- if (path_str[i] == '"' || path_str[i] == '\'') {
- // Field access: ["field"] or ['field']
- char quote = path_str[i];
- ++i;
- while (i < path_str.size() && path_str[i] != quote) {
- ++i;
- }
- if (i < path_str.size()) ++i; // skip closing quote
- if (i < path_str.size() && path_str[i] == ']') ++i;
- } else {
- // Array index: [0], [123]
- while (i < path_str.size() && path_str[i] != ']') {
- ++i;
- }
- if (i < path_str.size()) ++i; // skip ]
- }
- ++count;
- } else {
- ++i;
- }
- }
-
- return count;
- }
-
- // Parse a field name at compile time
- static consteval std::size_t parse_field_name(std::size_t start, char* out, std::size_t max_len) {
- std::size_t len = 0;
- std::size_t i = start;
-
- while (i < path_str.size() && path_str[i] != '.' && path_str[i] != '[' && len < max_len - 1) {
- out[len++] = path_str[i++];
- }
- out[len] = '\0';
- return i;
- }
-
- // Parse an array index at compile time
- static consteval std::pair<std::size_t, std::size_t> parse_array_index(std::size_t start) {
- std::size_t index = 0;
- std::size_t i = start;
-
- while (i < path_str.size() && path_str[i] >= '0' && path_str[i] <= '9') {
- index = index * 10 + (path_str[i] - '0');
- ++i;
- }
-
- return {i, index};
- }
};
// Compile-time path accessor generator
@@ -129962,7 +127403,6 @@ struct path_accessor {
using value = ::simdjson::haswell::ondemand::value;
static constexpr auto parser = json_path_parser<Path>();
- static constexpr std::size_t num_steps = parser.count_steps();
static constexpr std::string_view path_view = Path.view();
// Compile-time accessor generation
@@ -130077,93 +127517,6 @@ private:
return current_type;
}
-private:
- // Walk path and extract directly into final field using compile-time reflection
- template<std::meta::info CurrentType, std::size_t PathPos, typename TargetType>
- static inline error_code extract_with_reflection(simdjson_result<value> current, TargetType& target_ref) noexcept {
- if (current.error()) return current.error();
-
- // Base case: end of path - extract into target
- if constexpr (PathPos >= path_view.size()) {
- return current.get(target_ref);
- }
- // Field access: .field_name
- else if constexpr (path_view[PathPos] == '.') {
- constexpr auto field_info = parse_next_field(PathPos);
- constexpr std::string_view field_name = std::get<0>(field_info);
- constexpr std::size_t next_pos = std::get<1>(field_info);
-
- constexpr auto member_info = find_member_by_name(CurrentType, field_name);
- static_assert(member_info != ^^void, "Field not found in struct");
-
- constexpr auto member_type = std::meta::type_of(member_info);
-
- auto obj_result = current.get_object();
- if (obj_result.error()) return obj_result.error();
- auto obj = obj_result.value_unsafe();
- auto field_value = obj.find_field_unordered(field_name);
-
- if constexpr (next_pos >= path_view.size()) {
- return field_value.get(target_ref);
- } else {
- return extract_with_reflection<member_type, next_pos>(field_value, target_ref);
- }
- }
- // Bracket notation: [index] or ["field"]
- else if constexpr (path_view[PathPos] == '[') {
- constexpr auto bracket_info = parse_bracket(PathPos);
- constexpr bool is_field = std::get<0>(bracket_info);
- constexpr std::size_t next_pos = std::get<2>(bracket_info);
-
- if constexpr (is_field) {
- constexpr std::string_view field_name = std::get<1>(bracket_info);
- constexpr auto member_info = find_member_by_name(CurrentType, field_name);
- static_assert(member_info != ^^void, "Field not found in struct");
- constexpr auto member_type = std::meta::type_of(member_info);
-
- auto obj_result = current.get_object();
- if (obj_result.error()) return obj_result.error();
- auto obj = obj_result.value_unsafe();
- auto field_value = obj.find_field_unordered(field_name);
-
- if constexpr (next_pos >= path_view.size()) {
- return field_value.get(target_ref);
- } else {
- return extract_with_reflection<member_type, next_pos>(field_value, target_ref);
- }
- } else {
- constexpr std::size_t index = std::get<3>(bracket_info);
- constexpr auto elem_type = get_element_type_reflected(CurrentType);
- static_assert(elem_type != ^^void, "Could not determine array element type");
-
- auto arr_result = current.get_array();
- if (arr_result.error()) return arr_result.error();
- auto arr = arr_result.value_unsafe();
- auto elem_value = arr.at(index);
-
- if constexpr (next_pos >= path_view.size()) {
- return elem_value.get(target_ref);
- } else {
- return extract_with_reflection<elem_type, next_pos>(elem_value, target_ref);
- }
- }
- }
- // Skip unexpected characters and continue
- else {
- return extract_with_reflection<CurrentType, PathPos + 1>(current, target_ref);
- }
- }
-
- // Find member by name in reflected type
- static consteval std::meta::info find_member_by_name(std::meta::info type_refl, std::string_view name) {
- auto members = std::meta::nonstatic_data_members_of(type_refl, std::meta::access_context::unchecked());
- for (auto mem : members) {
- if (std::meta::identifier_of(mem) == name) {
- return mem;
- }
- }
- }
-
// Generate compile-time accessor code by walking the path
template<std::size_t PathPos>
static inline simdjson_result<value> access_impl(simdjson_result<value> current) noexcept {
@@ -130293,55 +127646,6 @@ public:
}
private:
- // Check if type has member with given name
- template<typename Type>
- static consteval bool has_member(std::string_view member_name) {
- constexpr auto members = std::meta::nonstatic_data_members_of(^^Type, std::meta::access_context::unchecked());
- for (auto mem : members) {
- if (std::meta::identifier_of(mem) == member_name) {
- return true;
- }
- }
- return false;
- }
-
- // Get type of member by name
- template<typename Type>
- static consteval auto get_member_type(std::string_view member_name) {
- constexpr auto members = std::meta::nonstatic_data_members_of(^^Type, std::meta::access_context::unchecked());
- for (auto mem : members) {
- if (std::meta::identifier_of(mem) == member_name) {
- return std::meta::type_of(mem);
- }
- }
- return ^^void;
- }
-
- // Check if non-reflected type is array-like
- template<typename Type>
- static consteval bool is_container_type() {
- using BaseType = std::remove_cvref_t<Type>;
- if constexpr (requires { typename BaseType::value_type; }) {
- return true;
- }
- if constexpr (std::is_array_v<BaseType>) {
- return true;
- }
- return false;
- }
-
- // Extract element type from non-reflected container
- template<typename Type>
- using extract_element_type = std::conditional_t<
- requires { typename std::remove_cvref_t<Type>::value_type; },
- typename std::remove_cvref_t<Type>::value_type,
- std::conditional_t<
- std::is_array_v<std::remove_cvref_t<Type>>,
- std::remove_extent_t<std::remove_cvref_t<Type>>,
- void
- >
- >;
-
// Validate path matches struct definition
static consteval bool validate_path() {
if constexpr (!std::is_class_v<T>) {
@@ -131336,34 +128640,6 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin
}
-
-struct escaping {
- static constexpr uint32_t BYTES_PROCESSED = 64;
- simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
-
- simdjson_inline bool has_escape() { return escape_bits != 0; }
- simdjson_inline int escape_index() { return trailing_zeroes(uint64_t(escape_bits)); }
-
- __mmask64 escape_bits;
-}; // struct escaping
-
-
-
-simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
- static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes");
- simd8<uint8_t> v(src);
- v.store(dst);
- __mmask64 is_quote = _mm512_cmpeq_epi8_mask(v, _mm512_set1_epi8('"'));
- __mmask64 is_backslash = _mm512_cmpeq_epi8_mask(v, _mm512_set1_epi8('\\'));
- __mmask64 is_control = _mm512_cmplt_epi8_mask(v, _mm512_set1_epi8(32));
- return {
- (is_backslash | is_quote | is_control)
- };
-}
-
-
-
-
} // unnamed namespace
} // namespace icelake
} // namespace simdjson
@@ -131633,11 +128909,6 @@ protected:
public:
simdjson_inline value_iterator() noexcept = default;
- /**
- * Denote that we're starting a document.
- */
- simdjson_inline void start_document() noexcept;
-
/**
* Skips a non-iterated or partially-iterated JSON value, whether it is a scalar, array or object.
*
@@ -132038,16 +129309,7 @@ protected:
simdjson_warn_unused simdjson_inline error_code start_container(uint8_t start_char, const char *incorrect_type_message, const char *type) noexcept;
simdjson_warn_unused simdjson_inline error_code end_container() noexcept;
- /**
- * Advance to a place expecting a value (increasing depth).
- *
- * @return The current token (the one left behind).
- * @error TAPE_ERROR If the document ended early.
- */
- simdjson_inline simdjson_result<const uint8_t *> advance_to_value() noexcept;
-
simdjson_warn_unused simdjson_inline error_code incorrect_type_error(const char *message) const noexcept;
- simdjson_warn_unused simdjson_inline error_code error_unless_more_tokens(uint32_t tokens=1) const noexcept;
simdjson_inline bool is_at_start() const noexcept;
/**
@@ -132130,6 +129392,7 @@ public:
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
+/* amalgamation skipped (editor-only): #include "simdjson/jsonpathutil.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/deserialize.h" */
/* amalgamation skipped (editor-only): #include <vector> */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
@@ -132983,11 +130246,19 @@ protected:
*/
simdjson_inline simdjson_result<object> start_or_resume_object() noexcept;
+ /**
+ * The recursive step of for_each_at_path_with_wildcard: matches
+ * path[index] at this level and what follows below it.
+ */
+ template <typename Func>
+ inline error_code for_each_at_path_segments(const std::vector<simdjson::internal::json_path_segment> &path, size_t index, Func &callback) noexcept;
+
// simdjson_inline void log_value(const char *type) const noexcept;
// simdjson_inline void log_error(const char *message) const noexcept;
value_iterator iter{};
+ friend class array;
friend class document;
friend class array_iterator;
friend class field;
@@ -133676,6 +130947,7 @@ public:
/**
* Take an input in json containing max_len characters and attempt to copy it over to tmpbuf, a buffer with
* N bytes of capacity. It will return false if N is too small (smaller than max_len) of if it is zero.
+ * Trailing whitespace, commas, and record separators (0x1E) do not count toward max_len.
* The buffer (tmpbuf) is padded with space characters.
*/
simdjson_warn_unused simdjson_inline bool copy_to_buffer(const uint8_t *json, uint32_t max_len, uint8_t *tmpbuf, size_t N) noexcept;
@@ -133708,8 +130980,11 @@ public:
/**
* Updates this json iterator so that it is back at the beginning of the document,
* as if it had just been created.
+ *
+ * An iterator that is no longer alive (e.g., abandoned after a fatal error) has no
+ * document to go back to: it is left as is and its error is returned.
*/
- inline void rewind() noexcept;
+ simdjson_inline error_code rewind() noexcept;
/**
* This checks whether the {,},[,] are balanced so that the document
* ends with proper zero depth. This requires scanning the whole document
@@ -134064,6 +131339,28 @@ public:
private:
+ /**
+ * Returns true if target ends with an odd number of backslashes. The last one
+ * then escapes the quote that follows the matching bytes in the raw string, so
+ * that quote does not end the string. Not inlined: callers check the last byte
+ * first, and keys ending in a backslash are rare.
+ */
+ static simdjson_never_inline bool ends_with_unpaired_backslash(std::string_view target) noexcept {
+ size_t backslash_count{0};
+ while(backslash_count < target.size() && target[target.size() - 1 - backslash_count] == '\\') {
+ backslash_count++;
+ }
+ return backslash_count % 2 == 1;
+ }
+
+ /**
+ * True when a byte-for-byte match must be rejected because target escapes the
+ * closing quote. The last-byte test keeps the counting loop off the common path.
+ */
+ static simdjson_inline bool escapes_trailing_quote(std::string_view target) noexcept {
+ return simdjson_unlikely(!target.empty() && target[target.size() - 1] == '\\')
+ && ends_with_unpaired_backslash(target);
+ }
/**
* This will set the inner pointer to zero, effectively making
@@ -134637,6 +131934,7 @@ public:
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
+/* amalgamation skipped (editor-only): #include "simdjson/jsonpathutil.h" */
/* amalgamation skipped (editor-only): #include <vector> */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
@@ -134829,6 +132127,13 @@ protected:
*/
simdjson_warn_unused simdjson_inline error_code consume() noexcept;
+ /**
+ * The recursive step of for_each_at_path_with_wildcard: matches
+ * path[index] at this level and what follows below it.
+ */
+ template <typename Func>
+ inline error_code for_each_at_path_segments(const std::vector<simdjson::internal::json_path_segment> &path, size_t index, Func &callback) noexcept;
+
/**
* Begin array iteration.
*
@@ -135838,6 +133143,9 @@ public:
* Reset the iterator inside the document instance so we are pointing back at the
* beginning of the document, as if it had just been created. It invalidates all
* values, objects and arrays that you have created so far (including unescaped strings).
+ *
+ * A document that is no longer alive (see is_alive()) cannot be rewound: it is
+ * left as is.
*/
inline void rewind() noexcept;
/**
@@ -135911,6 +133219,8 @@ public:
* - INCORRECT_TYPE if a non-integer is used to access an array
* - INVALID_JSON_POINTER if the JSON pointer is invalid and cannot be parsed
* - SCALAR_DOCUMENT_AS_VALUE if the json_pointer is empty and the document is not a scalar (see is_scalar() function).
+ * - the fatal error (e.g., TAPE_ERROR) that ended an earlier call, as the document can
+ * no longer be rewound (see is_alive()).
*/
simdjson_inline simdjson_result<value> at_pointer(std::string_view json_pointer) noexcept;
@@ -135934,6 +133244,8 @@ public:
* - NO_SUCH_FIELD if a field does not exist in an object
* - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length
* - INCORRECT_TYPE if a non-integer is used to access an array
+ * - the fatal error (e.g., TAPE_ERROR) that ended an earlier call, as the document can
+ * no longer be rewound (see is_alive()).
*/
simdjson_inline simdjson_result<value> at_path(std::string_view json_path) noexcept;
@@ -135960,7 +133272,9 @@ public:
/**
* Consumes the document and returns a string_view instance corresponding to the
* document as represented in JSON. It points inside the original byte array containing
- * the JSON document.
+ * the JSON document. It starts at the first byte of the document and ends at its last
+ * byte: it does not include the white space that follows the document, nor, in a
+ * document_stream, the separators or the documents that follow it.
*/
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
@@ -136287,134 +133601,134 @@ public:
using icelake::implementation_simdjson_result_base<icelake::ondemand::document>::operator*;
using icelake::implementation_simdjson_result_base<icelake::ondemand::document>::operator->;
- template <class T, typename std::enable_if<std::is_same<T, icelake::ondemand::document>::value == false>::type>
- explicit simdjson_inline operator T() noexcept(false);
- simdjson_inline operator icelake::ondemand::array() & noexcept(false);
- simdjson_inline operator icelake::ondemand::object() & noexcept(false);
- explicit simdjson_inline operator uint64_t() noexcept(false);
- explicit simdjson_inline operator int64_t() noexcept(false);
- explicit simdjson_inline operator double() noexcept(false);
- explicit simdjson_inline operator std::string_view() noexcept(false);
- explicit simdjson_inline operator icelake::ondemand::raw_json_string() noexcept(false);
- explicit simdjson_inline operator bool() noexcept(false);
- simdjson_inline operator icelake::ondemand::value() noexcept(false);
-#endif
- simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
- simdjson_inline simdjson_result<size_t> count_fields() & noexcept;
- simdjson_inline simdjson_result<icelake::ondemand::value> at(size_t index) & noexcept;
- simdjson_inline simdjson_result<icelake::ondemand::array_iterator> begin() & noexcept;
- simdjson_inline simdjson_result<icelake::ondemand::array_iterator> end() & noexcept;
- simdjson_inline simdjson_result<icelake::ondemand::value> find_field(std::string_view key) & noexcept;
- simdjson_inline simdjson_result<icelake::ondemand::value> find_field(const char *key) & noexcept;
- simdjson_inline simdjson_result<icelake::ondemand::value> operator[](std::string_view key) & noexcept;
- simdjson_inline simdjson_result<icelake::ondemand::value> operator[](const char *key) & noexcept;
- simdjson_result<icelake::ondemand::value> operator[](int) & noexcept = delete;
- simdjson_inline simdjson_result<icelake::ondemand::value> find_field_unordered(std::string_view key) & noexcept;
- simdjson_inline simdjson_result<icelake::ondemand::value> find_field_unordered(const char *key) & noexcept;
- simdjson_inline simdjson_result<icelake::ondemand::json_type> type() noexcept;
- simdjson_inline simdjson_result<bool> is_scalar() noexcept;
- simdjson_inline simdjson_result<bool> is_string() noexcept;
- simdjson_inline simdjson_result<const char *> current_location() noexcept;
- simdjson_inline int32_t current_depth() const noexcept;
- simdjson_inline bool at_end() const noexcept;
- simdjson_inline bool is_negative() noexcept;
- simdjson_inline simdjson_result<bool> is_integer() noexcept;
- simdjson_inline simdjson_result<icelake::number_type> get_number_type() noexcept;
- simdjson_inline simdjson_result<icelake::ondemand::number> get_number() noexcept;
- /** @copydoc simdjson_inline std::string_view document::raw_json_token() const noexcept */
- simdjson_inline simdjson_result<std::string_view> raw_json_token() noexcept;
-
- simdjson_inline simdjson_result<icelake::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
- simdjson_inline simdjson_result<icelake::ondemand::value> at_path(std::string_view json_path) noexcept;
-#if SIMDJSON_SUPPORTS_CONCEPTS
- template <typename Func>
- requires std::invocable<Func, icelake::ondemand::value>
-#else
- template <typename Func>
-#endif
- simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
-#if SIMDJSON_STATIC_REFLECTION
- template<constevalutil::fixed_string... FieldNames, typename T>
- requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
- simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept;
-#endif // SIMDJSON_STATIC_REFLECTION
-};
-
-
-} // namespace simdjson
-
-
-
-namespace simdjson {
-
-template<>
-struct simdjson_result<icelake::ondemand::document_reference> : public icelake::implementation_simdjson_result_base<icelake::ondemand::document_reference> {
-public:
- simdjson_inline simdjson_result(icelake::ondemand::document_reference value, error_code error) noexcept;
- simdjson_inline simdjson_result() noexcept = default;
- simdjson_inline error_code rewind() noexcept;
-
- simdjson_inline simdjson_result<icelake::ondemand::array> get_array() & noexcept;
- simdjson_inline simdjson_result<icelake::ondemand::object> get_object() & noexcept;
- simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
- simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
- simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
- simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
- simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
- simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
- simdjson_inline simdjson_result<uint16_t> get_uint16() noexcept;
- simdjson_inline simdjson_result<int16_t> get_int16() noexcept;
- simdjson_inline simdjson_result<uint8_t> get_uint8() noexcept;
- simdjson_inline simdjson_result<int8_t> get_int8() noexcept;
- simdjson_inline simdjson_result<double> get_double() noexcept;
- simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
- simdjson_inline simdjson_result<float> get_float() noexcept;
- simdjson_inline simdjson_result<float> get_float_in_string() noexcept;
-#if SIMDJSON_SUPPORTS_FLOAT32_T
- simdjson_inline simdjson_result<std::float32_t> get_float32() noexcept;
-#endif // SIMDJSON_SUPPORTS_FLOAT32_T
-#if SIMDJSON_SUPPORTS_FLOAT64_T
- simdjson_inline simdjson_result<std::float64_t> get_float64() noexcept;
-#endif // SIMDJSON_SUPPORTS_FLOAT64_T
- simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
-#if SIMDJSON_SUPPORTS_CHAR8_T
- simdjson_inline simdjson_result<std::u8string_view> get_u8string(bool allow_replacement = false) noexcept;
-#endif // SIMDJSON_SUPPORTS_CHAR8_T
- template <typename string_type>
- simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
- simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
- simdjson_inline simdjson_result<icelake::ondemand::raw_json_string> get_raw_json_string() noexcept;
- simdjson_inline simdjson_result<bool> get_bool() noexcept;
- simdjson_inline simdjson_result<icelake::ondemand::value> get_value() noexcept;
- simdjson_inline simdjson_result<bool> is_null() noexcept;
-
- template<typename T> simdjson_inline simdjson_result<T> get() &
-#if SIMDJSON_SUPPORTS_CONCEPTS
- noexcept(nothrow_gettable<T, icelake::ondemand::document_reference>);
-#else
- noexcept;
-#endif
- template<typename T> simdjson_inline simdjson_result<T> get() &&
-#if SIMDJSON_SUPPORTS_CONCEPTS
- noexcept(nothrow_gettable<T, icelake::ondemand::document_reference>);
-#else
- noexcept;
-#endif
-
- template<typename T> simdjson_inline error_code get(T &out) &
-#if SIMDJSON_SUPPORTS_CONCEPTS
- noexcept(nothrow_gettable<T, icelake::ondemand::document_reference>);
-#else
- noexcept;
-#endif
- template<typename T> simdjson_inline error_code get(T &out) &&
-#if SIMDJSON_SUPPORTS_CONCEPTS
- noexcept(nothrow_gettable<T, icelake::ondemand::document_reference>);
-#else
- noexcept;
-#endif
-#if SIMDJSON_EXCEPTIONS
- template <class T>
+ simdjson_inline operator icelake::ondemand::array() & noexcept(false);
+ simdjson_inline operator icelake::ondemand::object() & noexcept(false);
+ explicit simdjson_inline operator uint64_t() noexcept(false);
+ explicit simdjson_inline operator int64_t() noexcept(false);
+ explicit simdjson_inline operator double() noexcept(false);
+ explicit simdjson_inline operator std::string_view() noexcept(false);
+ explicit simdjson_inline operator icelake::ondemand::raw_json_string() noexcept(false);
+ explicit simdjson_inline operator bool() noexcept(false);
+ simdjson_inline operator icelake::ondemand::value() noexcept(false);
+#endif
+ simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
+ simdjson_inline simdjson_result<size_t> count_fields() & noexcept;
+ simdjson_inline simdjson_result<icelake::ondemand::value> at(size_t index) & noexcept;
+ simdjson_inline simdjson_result<icelake::ondemand::array_iterator> begin() & noexcept;
+ simdjson_inline simdjson_result<icelake::ondemand::array_iterator> end() & noexcept;
+ simdjson_inline simdjson_result<icelake::ondemand::value> find_field(std::string_view key) & noexcept;
+ simdjson_inline simdjson_result<icelake::ondemand::value> find_field(const char *key) & noexcept;
+ simdjson_inline simdjson_result<icelake::ondemand::value> operator[](std::string_view key) & noexcept;
+ simdjson_inline simdjson_result<icelake::ondemand::value> operator[](const char *key) & noexcept;
+ simdjson_result<icelake::ondemand::value> operator[](int) & noexcept = delete;
+ simdjson_inline simdjson_result<icelake::ondemand::value> find_field_unordered(std::string_view key) & noexcept;
+ simdjson_inline simdjson_result<icelake::ondemand::value> find_field_unordered(const char *key) & noexcept;
+ simdjson_inline simdjson_result<icelake::ondemand::json_type> type() noexcept;
+ simdjson_inline simdjson_result<bool> is_scalar() noexcept;
+ simdjson_inline simdjson_result<bool> is_string() noexcept;
+ simdjson_inline simdjson_result<const char *> current_location() noexcept;
+ simdjson_inline int32_t current_depth() const noexcept;
+ simdjson_inline bool at_end() const noexcept;
+ simdjson_inline bool is_negative() noexcept;
+ simdjson_inline simdjson_result<bool> is_integer() noexcept;
+ simdjson_inline simdjson_result<icelake::number_type> get_number_type() noexcept;
+ simdjson_inline simdjson_result<icelake::ondemand::number> get_number() noexcept;
+ /** @copydoc simdjson_inline std::string_view document::raw_json_token() const noexcept */
+ simdjson_inline simdjson_result<std::string_view> raw_json_token() noexcept;
+ /** @copydoc simdjson_inline simdjson_result<std::string_view> document::raw_json() noexcept */
+ simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
+
+ simdjson_inline simdjson_result<icelake::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
+ simdjson_inline simdjson_result<icelake::ondemand::value> at_path(std::string_view json_path) noexcept;
+#if SIMDJSON_SUPPORTS_CONCEPTS
+ template <typename Func>
+ requires std::invocable<Func, icelake::ondemand::value>
+#else
+ template <typename Func>
+#endif
+ simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
+#if SIMDJSON_STATIC_REFLECTION
+ template<constevalutil::fixed_string... FieldNames, typename T>
+ requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
+ simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept;
+#endif // SIMDJSON_STATIC_REFLECTION
+};
+
+
+} // namespace simdjson
+
+
+
+namespace simdjson {
+
+template<>
+struct simdjson_result<icelake::ondemand::document_reference> : public icelake::implementation_simdjson_result_base<icelake::ondemand::document_reference> {
+public:
+ simdjson_inline simdjson_result(icelake::ondemand::document_reference value, error_code error) noexcept;
+ simdjson_inline simdjson_result() noexcept = default;
+ simdjson_inline error_code rewind() noexcept;
+
+ simdjson_inline simdjson_result<icelake::ondemand::array> get_array() & noexcept;
+ simdjson_inline simdjson_result<icelake::ondemand::object> get_object() & noexcept;
+ simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
+ simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
+ simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
+ simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
+ simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
+ simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
+ simdjson_inline simdjson_result<uint16_t> get_uint16() noexcept;
+ simdjson_inline simdjson_result<int16_t> get_int16() noexcept;
+ simdjson_inline simdjson_result<uint8_t> get_uint8() noexcept;
+ simdjson_inline simdjson_result<int8_t> get_int8() noexcept;
+ simdjson_inline simdjson_result<double> get_double() noexcept;
+ simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
+ simdjson_inline simdjson_result<float> get_float() noexcept;
+ simdjson_inline simdjson_result<float> get_float_in_string() noexcept;
+#if SIMDJSON_SUPPORTS_FLOAT32_T
+ simdjson_inline simdjson_result<std::float32_t> get_float32() noexcept;
+#endif // SIMDJSON_SUPPORTS_FLOAT32_T
+#if SIMDJSON_SUPPORTS_FLOAT64_T
+ simdjson_inline simdjson_result<std::float64_t> get_float64() noexcept;
+#endif // SIMDJSON_SUPPORTS_FLOAT64_T
+ simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
+#if SIMDJSON_SUPPORTS_CHAR8_T
+ simdjson_inline simdjson_result<std::u8string_view> get_u8string(bool allow_replacement = false) noexcept;
+#endif // SIMDJSON_SUPPORTS_CHAR8_T
+ template <typename string_type>
+ simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
+ simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
+ simdjson_inline simdjson_result<icelake::ondemand::raw_json_string> get_raw_json_string() noexcept;
+ simdjson_inline simdjson_result<bool> get_bool() noexcept;
+ simdjson_inline simdjson_result<icelake::ondemand::value> get_value() noexcept;
+ simdjson_inline simdjson_result<bool> is_null() noexcept;
+
+ template<typename T> simdjson_inline simdjson_result<T> get() &
+#if SIMDJSON_SUPPORTS_CONCEPTS
+ noexcept(nothrow_gettable<T, icelake::ondemand::document_reference>);
+#else
+ noexcept;
+#endif
+ template<typename T> simdjson_inline simdjson_result<T> get() &&
+#if SIMDJSON_SUPPORTS_CONCEPTS
+ noexcept(nothrow_gettable<T, icelake::ondemand::document_reference>);
+#else
+ noexcept;
+#endif
+
+ template<typename T> simdjson_inline error_code get(T &out) &
+#if SIMDJSON_SUPPORTS_CONCEPTS
+ noexcept(nothrow_gettable<T, icelake::ondemand::document_reference>);
+#else
+ noexcept;
+#endif
+ template<typename T> simdjson_inline error_code get(T &out) &&
+#if SIMDJSON_SUPPORTS_CONCEPTS
+ noexcept(nothrow_gettable<T, icelake::ondemand::document_reference>);
+#else
+ noexcept;
+#endif
+#if SIMDJSON_EXCEPTIONS
+ template <class T>
explicit simdjson_inline operator T() noexcept(false);
simdjson_inline operator icelake::ondemand::array() & noexcept(false);
simdjson_inline operator icelake::ondemand::object() & noexcept(false);
@@ -136449,6 +133763,8 @@ public:
simdjson_inline simdjson_result<icelake::ondemand::number> get_number() noexcept;
/** @copydoc simdjson_inline std::string_view document_reference::raw_json_token() const noexcept */
simdjson_inline simdjson_result<std::string_view> raw_json_token() noexcept;
+ /** @copydoc simdjson_inline simdjson_result<std::string_view> document::raw_json() noexcept */
+ simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
simdjson_inline simdjson_result<icelake::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
simdjson_inline simdjson_result<icelake::ondemand::value> at_path(std::string_view json_path) noexcept;
@@ -138410,6 +135726,7 @@ concept key_selector_type = key_selector_detail::is_key_selector<T>::value;
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
+/* amalgamation skipped (editor-only): #include "simdjson/jsonpathutil.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/key_selector.h" */
/* amalgamation skipped (editor-only): #include <vector> */
/* amalgamation skipped (editor-only): #if SIMDJSON_STATIC_REFLECTION && SIMDJSON_SUPPORTS_CONCEPTS */
@@ -138954,6 +136271,13 @@ protected:
simdjson_warn_unused simdjson_inline error_code find_field_raw(const std::string_view key) noexcept;
+ /**
+ * The recursive step of for_each_at_path_with_wildcard: matches
+ * path[index] at this level and what follows below it.
+ */
+ template <typename Func>
+ inline error_code for_each_at_path_segments(const std::vector<simdjson::internal::json_path_segment> &path, size_t index, Func &callback) noexcept;
+
value_iterator iter{};
#if SIMDJSON_DEVELOPMENT_CHECKS
bool locked{false};
@@ -139818,6 +137142,14 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) {
std::is_default_constructible_v<element_type>,
"The specified type inside the unique_ptr must default constructible.");
+ // A null pointer is serialized as null.
+ bool is_null_value;
+ SIMDJSON_TRY( val.is_null().get(is_null_value) );
+ if (is_null_value) {
+ out.reset();
+ return SUCCESS;
+ }
+
// Own the allocation before get(): a user tag_invoke may throw.
std::unique_ptr<element_type> ptr(new (std::nothrow) element_type());
if (!ptr) {
@@ -139866,6 +137198,8 @@ constexpr bool user_defined_type = (std::is_class_v<T>
#endif // SIMDJSON_SUPPORTS_CHAR8_T
&& !concepts::optional_type<T> &&
!concepts::appendable_containers<T>
+// Smart pointers are handled by the generic smart-pointer tag_invoke.
+&& !concepts::smart_pointer<T>
// simdjson's own types (array, object, value, raw_json_string, number,
// document, document_reference) have dedicated get<T>() specializations and
// must never go through reflection.
@@ -140458,251 +137792,8 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept {
#endif
}
-template <typename simdjson_value, typename T>
- requires(user_defined_type<std::remove_cvref_t<T>>)
-error_code tag_invoke(deserialize_tag, simdjson_value &val, std::unique_ptr<T> &out) noexcept(false) {
- std::unique_ptr<T> ptr(new (std::nothrow) T());
- if (!ptr) {
- return MEMALLOC;
- }
- SIMDJSON_TRY(val.get(*ptr));
- out = std::move(ptr);
- return SUCCESS;
-}
-
-template <typename simdjson_value, typename T>
- requires(user_defined_type<std::remove_cvref_t<T>>)
-error_code tag_invoke(deserialize_tag, simdjson_value &val, std::shared_ptr<T> &out) noexcept(false) {
- std::shared_ptr<T> ptr(new (std::nothrow) T());
- if (!ptr) {
- return MEMALLOC;
- }
- SIMDJSON_TRY(val.get(*ptr));
- out = std::move(ptr);
- return SUCCESS;
-}
-
#endif // SIMDJSON_STATIC_REFLECTION
-////////////////////////////////////////
-// Unique pointers
-////////////////////////////////////////
-error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<bool> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_unique<bool>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_bool().get(*out));
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<int64_t> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_unique<int64_t>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_int64().get(*out));
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<uint64_t> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_unique<uint64_t>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_uint64().get(*out));
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<double> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_unique<double>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_double().get(*out));
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<std::string_view> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_unique<std::string_view>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_string().get(*out));
- return SUCCESS;
-}
-
-
-////////////////////////////////////////
-// Shared pointers
-////////////////////////////////////////
-error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<bool> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_shared<bool>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_bool().get(*out));
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<int64_t> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_shared<int64_t>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_int64().get(*out));
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<uint64_t> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_shared<uint64_t>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_uint64().get(*out));
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<double> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_shared<double>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_double().get(*out));
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<std::string_view> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_shared<std::string_view>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_string().get(*out));
- return SUCCESS;
-}
-
-
-////////////////////////////////////////
-// Explicit optional specializations
-////////////////////////////////////////
-
-////////////////////////////////////////
-// Explicit smart pointer specializations for string and int types
-////////////////////////////////////////
-error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<std::string> &out) noexcept {
- // Check if the value is null
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset(); // Set to nullptr
- return SUCCESS;
- }
-
- if (!out) {
- out = std::make_unique<std::string>();
- }
- std::string_view str;
- SIMDJSON_TRY(val.get_string().get(str));
- *out = std::string{str};
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<std::string> &out) noexcept {
- // Check if the value is null
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset(); // Set to nullptr
- return SUCCESS;
- }
-
- if (!out) {
- out = std::make_shared<std::string>();
- }
- std::string_view str;
- SIMDJSON_TRY(val.get_string().get(str));
- *out = std::string{str};
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<int> &out) noexcept {
- // Check if the value is null
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset(); // Set to nullptr
- return SUCCESS;
- }
-
- if (!out) {
- out = std::make_unique<int>();
- }
- int64_t temp;
- SIMDJSON_TRY(val.get_int64().get(temp));
- *out = static_cast<int>(temp);
- return SUCCESS;
-}
-
} // namespace simdjson
#endif // SIMDJSON_ONDEMAND_DESERIALIZE_H
@@ -140817,10 +137908,9 @@ simdjson_inline simdjson_result<std::string_view> array::raw_json() noexcept {
const uint8_t * starting_point{iter.peek_start()};
auto error = consume();
if(error) { return error; }
- // After 'consume()', we could be left pointing just beyond the document, but that
- // is ok because we are not going to dereference the final pointer position, we just
- // use it to compute the length in bytes.
- const uint8_t * final_point{iter._json_iter->unsafe_pointer()};
+ // After 'consume()', the last token consumed is the closing bracket: we end on it
+ // rather than on the next token, which may follow white space.
+ const uint8_t * final_point{iter._json_iter->token.peek(-1) + 1};
return std::string_view(reinterpret_cast<const char*>(starting_point), size_t(final_point - starting_point));
}
@@ -140904,12 +137994,23 @@ template <typename Func>
template <typename Func>
#endif
inline error_code array::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
- auto result_pair = get_next_key_and_json_path(json_path);
- std::string_view key = result_pair.first;
- std::string_view remaining_path = result_pair.second;
+ // The path is split once: every element reached by a wildcard sees the same
+ // remaining path, so re-parsing it at each one would be quadratic.
+ const std::vector<internal::json_path_segment> path = internal::split_json_path(json_path);
+ // A path that cannot be parsed matches nothing, whatever the document: say
+ // so rather than letting the walk skip it as a mismatch below a wildcard.
+ if (path.back().key.empty()) {
+ return INVALID_JSON_POINTER;
+ }
+ return for_each_at_path_segments(path, 0, callback);
+}
+
+template <typename Func>
+inline error_code array::for_each_at_path_segments(const std::vector<internal::json_path_segment> &path, size_t index, Func &callback) noexcept {
+ std::string_view key = path[index].key;
+ const bool last = index + 1 == path.size();
// Empty key means the path segment could not be parsed (including malformed
- // bracket-quoted keys). Without this check, an empty key is treated as index
- // 0 and remaining_path may be unchanged, causing infinite recursion.
+ // bracket-quoted keys).
if (key.empty()) {
return INVALID_JSON_POINTER;
}
@@ -140918,11 +138019,11 @@ inline error_code array::for_each_at_path_with_wildcard(std::string_view json_pa
for(auto element: *this) {
value val;
SIMDJSON_TRY(element.get(val));
- if (remaining_path.empty()) {
+ if (last) {
callback(val);
} else {
// An element that does not match the rest of the path is skipped.
- error_code err = val.for_each_at_path_with_wildcard(remaining_path, callback);
+ error_code err = val.for_each_at_path_segments(path, index + 1, callback);
if (err && !internal::is_matching_failure_error(err)) { return err; }
}
}
@@ -140941,16 +138042,16 @@ inline error_code array::for_each_at_path_with_wildcard(std::string_view json_pa
value val;
if (!error) { error = at(idx).get(val); }
if (error) {
- if (error == INDEX_OUT_OF_BOUNDS && internal::json_path_has_wildcard(remaining_path)) {
+ if (error == INDEX_OUT_OF_BOUNDS && path[index].wildcard_follows) {
return SUCCESS;
}
return error;
}
- if (remaining_path.empty()){
+ if (last){
callback(val);
return SUCCESS;
} else {
- return val.for_each_at_path_with_wildcard(remaining_path, callback);
+ return val.for_each_at_path_segments(path, index + 1, callback);
}
}
}
@@ -141608,6 +138709,19 @@ template <typename Func>
template <typename Func>
#endif
inline error_code value::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
+ // The path is split once: every value reached by a wildcard sees the same
+ // remaining path, so re-parsing it at each one would be quadratic.
+ const std::vector<internal::json_path_segment> path = internal::split_json_path(json_path);
+ // A path that cannot be parsed matches nothing, whatever the document: say
+ // so rather than letting the walk skip it as a mismatch below a wildcard.
+ if (path.back().key.empty()) {
+ return INVALID_JSON_POINTER;
+ }
+ return for_each_at_path_segments(path, 0, callback);
+}
+
+template <typename Func>
+inline error_code value::for_each_at_path_segments(const std::vector<internal::json_path_segment> &path, size_t index, Func &callback) noexcept {
// Every recursive step of for_each_at_path_with_wildcard goes through this
// function, and each one descends one level into the document. A path with
// many segments applied to a deeply nested document would otherwise recurse
@@ -141616,10 +138730,16 @@ inline error_code value::for_each_at_path_with_wildcard(std::string_view json_pa
json_type t;
SIMDJSON_TRY(type().get(t));
switch (t) {
- case json_type::array:
- return (*this).get_array().for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
- case json_type::object:
- return (*this).get_object().for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
+ case json_type::array: {
+ array a;
+ SIMDJSON_TRY(get_array().get(a));
+ return a.for_each_at_path_segments(path, index, callback);
+ }
+ case json_type::object: {
+ object o;
+ SIMDJSON_TRY(get_object().get(o));
+ return o.for_each_at_path_segments(path, index, callback);
+ }
default:
return INVALID_JSON_POINTER;
}
@@ -141992,7 +139112,8 @@ simdjson_inline document document::start(json_iterator &&iter) noexcept {
}
inline void document::rewind() noexcept {
- iter.rewind();
+ // A document that is no longer alive is left as it is (see is_alive()).
+ static_cast<void>(iter.rewind());
}
inline std::string document::to_debug_string() noexcept {
@@ -142292,7 +139413,13 @@ simdjson_warn_unused simdjson_inline error_code document::consume() noexcept {
auto error = is_scalar().get(scalar);
if(error) { return error; }
if(scalar) {
- iter.return_current_and_advance();
+ // Step over the scalar and leave depth 0, as skip_child() does: otherwise a
+ // document_stream takes the next document as the rest of this one. At depth
+ // 0 the scalar has already been consumed (e.g., by get_int64()).
+ if(iter.depth() > 0) {
+ iter.return_current_and_advance();
+ iter.ascend_to(0);
+ }
return SUCCESS;
}
error = iter.skip_child(0);
@@ -142305,10 +139432,36 @@ simdjson_inline simdjson_result<std::string_view> document::raw_json() noexcept
const uint8_t * starting_point{_iter.peek_start()};
auto error = consume();
if(error) { return error; }
- // After 'consume()', we could be left pointing just beyond the document, but that
- // is ok because we are not going to dereference the final pointer position, we just
- // use it to compute the length in bytes.
- const uint8_t * final_point{iter.unsafe_pointer()};
+ // After 'consume()', the iterator points at the token after the document. In a
+ // document_stream, that is the next document, past white space and separators, or
+ // the end of the input when a truncated document follows. We end on the last byte
+ // of this document instead.
+ const uint8_t * final_point;
+ if(*starting_point == '{' || *starting_point == '[') {
+ // The last token consumed is the closing brace or bracket.
+ final_point = iter.token.peek(-1) + 1;
+ } else {
+ // A scalar is a single token: a string ends at its closing quote, anything else
+ // at the first white space, structural character, quote, RS (0x1E) or NUL.
+ const uint8_t * next_token{iter.unsafe_pointer()};
+ final_point = starting_point + 1;
+ if(*starting_point == '"') {
+ while(final_point < next_token) {
+ uint8_t c = *final_point++;
+ if(c == '\\') {
+ final_point++;
+ } else if(c == '"') {
+ break;
+ }
+ }
+ if(final_point > next_token) { final_point = next_token; }
+ } else {
+ while(final_point < next_token && !jsoncharutils::is_structural_or_whitespace(*final_point)
+ && *final_point != '"' && *final_point != 0x1E && *final_point != 0) {
+ final_point++;
+ }
+ }
+ }
return std::string_view(reinterpret_cast<const char*>(starting_point), size_t(final_point - starting_point));
}
@@ -142355,7 +139508,9 @@ simdjson_inline simdjson_result<std::string_view> document::raw_json_token() noe
}
simdjson_inline simdjson_result<value> document::at_pointer(std::string_view json_pointer) noexcept {
- rewind(); // Rewind the document each time at_pointer is called
+ // Rewind the document each time at_pointer is called. After a fatal error,
+ // the document cannot be walked again: we report the error instead.
+ SIMDJSON_TRY(iter.rewind());
if (json_pointer.empty()) {
return this->get_value();
}
@@ -142373,7 +139528,9 @@ simdjson_inline simdjson_result<value> document::at_pointer(std::string_view jso
}
simdjson_inline simdjson_result<value> document::at_path(std::string_view json_path) noexcept {
- rewind(); // Rewind the document each time at_pointer is called
+ // Rewind the document each time at_path is called. After a fatal error,
+ // the document cannot be walked again: we report the error instead.
+ SIMDJSON_TRY(iter.rewind());
if (json_path.empty()) {
return this->get_value();
}
@@ -142396,7 +139553,9 @@ template <typename Func>
template <typename Func>
#endif
simdjson_inline error_code document::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
- rewind(); // Rewind the document each time for_each_at_path_with_wildcard is called
+ // Rewind the document each time for_each_at_path_with_wildcard is called. After
+ // a fatal error, the document cannot be walked again: we report the error instead.
+ SIMDJSON_TRY(iter.rewind());
if (json_path.empty()) {
return INVALID_JSON_POINTER;
}
@@ -142734,11 +139893,6 @@ simdjson_inline simdjson_result<icelake::ondemand::number> simdjson_result<icela
#if SIMDJSON_EXCEPTIONS
-template <class T, typename std::enable_if<std::is_same<T, icelake::ondemand::document>::value == false>::type>
-simdjson_inline simdjson_result<icelake::ondemand::document>::operator T() noexcept(false) {
- if (error()) { throw simdjson_error(error()); }
- return first;
-}
simdjson_inline simdjson_result<icelake::ondemand::document>::operator icelake::ondemand::array() & noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
@@ -142799,6 +139953,11 @@ simdjson_inline simdjson_result<std::string_view> simdjson_result<icelake::ondem
return first.raw_json_token();
}
+simdjson_inline simdjson_result<std::string_view> simdjson_result<icelake::ondemand::document>::raw_json() noexcept {
+ if (error()) { return error(); }
+ return first.raw_json();
+}
+
simdjson_inline simdjson_result<icelake::ondemand::value> simdjson_result<icelake::ondemand::document>::at_pointer(std::string_view json_pointer) noexcept {
if (error()) { return error(); }
return first.at_pointer(json_pointer);
@@ -143285,6 +140444,11 @@ simdjson_inline simdjson_result<std::string_view> simdjson_result<icelake::ondem
return first.raw_json_token();
}
+simdjson_inline simdjson_result<std::string_view> simdjson_result<icelake::ondemand::document_reference>::raw_json() noexcept {
+ if (error()) { return error(); }
+ return first.raw_json();
+}
+
simdjson_inline simdjson_result<icelake::ondemand::value> simdjson_result<icelake::ondemand::document_reference>::at_pointer(std::string_view json_pointer) noexcept {
if (error()) { return error(); }
return first.at_pointer(json_pointer);
@@ -143676,6 +140840,13 @@ simdjson_inline bool document_stream::skip_to_delimiter(uint8_t delimiter) noexc
}
inline void document_stream::next_document() noexcept {
+ // Iteration stops at the first document that met a fatal error. The error
+ // may have abandoned the document's iterator, which then no longer has a
+ // parser to walk the rest of the document with.
+ if (!doc.iter.is_alive() || doc.iter.error) {
+ error = doc.iter.error ? doc.iter.error : TAPE_ERROR;
+ return;
+ }
// A delimiter that cannot occur inside a document tells us where the current
// one ends, so we can jump there instead of walking every structural. Only
// valid while the iterator is still inside the document: a consumed document
@@ -143825,7 +140996,10 @@ simdjson_inline std::string_view document_stream::iterator::source() const noexc
cur_struct_index++;
}
- while (cur_struct_index <= static_cast<int64_t>(stream->parser->implementation->n_structural_indexes)) {
+ // The structural index at n_structural_indexes is the end of the batch, not
+ // a token: it must not be read as a closing bracket.
+ const int64_t n_structural_indexes = static_cast<int64_t>(stream->parser->implementation->n_structural_indexes);
+ while (cur_struct_index < n_structural_indexes) {
switch (stream->buf[stream->batch_start + stream->parser->implementation->structural_indexes[cur_struct_index]]) {
case '{': case '[':
depth++;
@@ -143837,6 +141011,11 @@ simdjson_inline std::string_view document_stream::iterator::source() const noexc
if (depth == 0) { break; }
cur_struct_index++;
}
+ // An invalid last document such as "[1 2" is never closed: it runs to the
+ // end of the batch, and not one byte past it.
+ if (cur_struct_index >= n_structural_indexes) {
+ return std::string_view(reinterpret_cast<const char*>(stream->buf) + current_index(), stream->batch_start + stream->parser->implementation->structural_indexes[cur_struct_index] - current_index());
+ }
return std::string_view(reinterpret_cast<const char*>(stream->buf) + current_index(), stream->parser->implementation->structural_indexes[cur_struct_index] - current_index() + stream->batch_start + 1);;
}
@@ -143853,7 +141032,12 @@ inline void document_stream::load_from_stage1_thread() noexcept {
// enough memory to swap to, as well.
std::swap(stage1_thread_parser,*parser);
error = stage1_thread_error;
- if (error) { return; }
+ // An EMPTY batch (no complete document in it, e.g. a run of white space at
+ // least batch_size long) is not the end of the stream: next() moves on to
+ // the batch after it, so queue that batch now as we would after a success.
+ // Returning here would leave no job for the next call, which then swaps
+ // the stale previous batch back in and ends the stream early.
+ if (error && error != EMPTY) { return; }
// If there's anything left, start the stage 1 thread!
if (next_batch_start() < len) {
@@ -143965,12 +141149,14 @@ simdjson_inline raw_json_string field::key() const noexcept {
simdjson_inline std::string_view field::key_raw_json_token() const noexcept {
SIMDJSON_ASSUME(first.buf != nullptr); // We would like to call .alive() by Visual Studio won't let us.
- return std::string_view(reinterpret_cast<const char*>(first.buf-1), second.iter._json_iter->token.peek(-1) - first.buf + 1);
+ // The ':' is the token just before the value. Use the value's start position
+ // rather than the current position, which moves once the value is consumed.
+ return std::string_view(reinterpret_cast<const char*>(first.buf-1), second.iter._json_iter->token.peek(second.iter.start_position() - 1) - first.buf + 1);
}
simdjson_inline std::string_view field::escaped_key() const noexcept {
SIMDJSON_ASSUME(first.buf != nullptr); // We would like to call .alive() by Visual Studio won't let us.
- auto end_quote = second.iter._json_iter->token.peek(-1);
+ auto end_quote = second.iter._json_iter->token.peek(second.iter.start_position() - 1);
while(*end_quote != '"') end_quote--;
return std::string_view(reinterpret_cast<const char*>(first.buf), end_quote - first.buf);
}
@@ -144141,11 +141327,14 @@ simdjson_inline json_iterator::json_iterator(const uint8_t *buf, ondemand::parse
}
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
-inline void json_iterator::rewind() noexcept {
+simdjson_inline error_code json_iterator::rewind() noexcept {
+ // abandon() let go of the parser: the iterator cannot be walked again.
+ if (!is_alive()) { return error ? error : UNINITIALIZED; }
token.set_position( root_position() );
logger::log_headers(); // We start again
_string_buf_loc = parser->string_buf.get();
_depth = 1;
+ return SUCCESS;
}
inline bool json_iterator::balanced() const noexcept {
@@ -144169,6 +141358,18 @@ inline bool json_iterator::balanced() const noexcept {
}
+// Depth change of each structural character when skipping: +1 for [ and {, -1 for ] and }.
+static constexpr int8_t skip_depth_delta[256] = {
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,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,
+};
+
// GCC 7 warns when the first line of this function is inlined away into oblivion due to the caller
// relating depth and parent_depth, which is a desired effect. The warning does not show up if the
// skip_child() function is not marked inline).
@@ -144224,7 +141425,30 @@ simdjson_warn_unused simdjson_inline error_code json_iterator::skip_child(depth_
break;
}
- // Now that we've considered the first value, we only increment/decrement for arrays/objects
+ // Now that we've considered the first value, we only increment/decrement for arrays/objects.
+ //
+ // Without logging, look up the depth change of each token in a table instead of branching
+ // on it: the next byte is hard to predict, so a switch mispredicts often. The loop also keeps
+ // the position and depth in locals, because the byte loads are char loads that may alias
+ // token._position and _depth, which would force a store of both on every token.
+ if (!logger::LOG_ENABLED) {
+ token_position pos = token.position();
+ const token_position end = end_position();
+ const uint8_t *const json = token.buf;
+ depth_t depth = _depth;
+ while (pos < end) {
+ depth += skip_depth_delta[json[*pos++]];
+ if (depth <= parent_depth) {
+ token.set_position(pos);
+ _depth = depth;
+ return SUCCESS;
+ }
+ }
+ token.set_position(pos);
+ _depth = depth;
+ return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "not enough close braces");
+ }
+
while (position() < end_position()) {
switch (*return_current_and_advance()) {
case '[': case '{':
@@ -144509,6 +141733,17 @@ simdjson_warn_unused simdjson_inline error_code json_iterator::optional_error(er
simdjson_warn_unused simdjson_inline bool json_iterator::copy_to_buffer(const uint8_t *json, uint32_t max_len, uint8_t *tmpbuf, size_t N) noexcept {
// This function is not expected to be called in performance-sensitive settings.
+ // peek_root_length() stops at the next structural index. Stage 1 omits the
+ // comma or RS (0x1E) that separates stream documents, so that byte and the
+ // whitespace around it remain in the span. RS is not a valid number finisher.
+ while (max_len > 0) {
+ const uint8_t last = json[max_len - 1];
+ if (last != ' ' && last != '\t' && last != '\n' && last != '\r' &&
+ last != ',' && last != 0x1E) {
+ break;
+ }
+ max_len--;
+ }
// Let us guard against silly cases:
if((N < max_len) || (N == 0)) { return false; }
// Copy to the buffer.
@@ -145150,7 +142385,9 @@ simdjson_inline simdjson_result<std::string_view> object::raw_json() noexcept {
const uint8_t * starting_point{iter.peek_start()};
auto error = consume();
if(error) { return error; }
- const uint8_t * final_point{iter._json_iter->peek()};
+ // After 'consume()', the last token consumed is the closing brace: we end on it
+ // rather than on the next token, which may follow white space.
+ const uint8_t * final_point{iter._json_iter->token.peek(-1) + 1};
return std::string_view(reinterpret_cast<const char*>(starting_point), size_t(final_point - starting_point));
}
@@ -145243,9 +142480,21 @@ template <typename Func>
template <typename Func>
#endif
inline error_code object::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
- auto result_pair = get_next_key_and_json_path(json_path);
- std::string_view key = result_pair.first;
- std::string_view remaining_path = result_pair.second;
+ // The path is split once: every field reached by a wildcard sees the same
+ // remaining path, so re-parsing it at each one would be quadratic.
+ const std::vector<internal::json_path_segment> path = internal::split_json_path(json_path);
+ // A path that cannot be parsed matches nothing, whatever the document: say
+ // so rather than letting the walk skip it as a mismatch below a wildcard.
+ if (path.back().key.empty()) {
+ return INVALID_JSON_POINTER;
+ }
+ return for_each_at_path_segments(path, 0, callback);
+}
+
+template <typename Func>
+inline error_code object::for_each_at_path_segments(const std::vector<internal::json_path_segment> &path, size_t index, Func &callback) noexcept {
+ std::string_view key = path[index].key;
+ const bool last = index + 1 == path.size();
// Empty key means the path segment could not be parsed (including malformed
// bracket-quoted keys). Match DOM at_path_with_wildcard and avoid treating
// "no progress" as a recursive lookup of "".
@@ -145258,11 +142507,11 @@ inline error_code object::for_each_at_path_with_wildcard(std::string_view json_p
for (auto field : *this) {
value val;
SIMDJSON_TRY(field.value().get(val));
- if (remaining_path.empty()) {
+ if (last) {
callback(val);
} else {
// A field that does not match the rest of the path is skipped.
- error_code err = val.for_each_at_path_with_wildcard(remaining_path, callback);
+ error_code err = val.for_each_at_path_segments(path, index + 1, callback);
if (err && !internal::is_matching_failure_error(err)) { return err; }
}
}
@@ -145271,17 +142520,17 @@ inline error_code object::for_each_at_path_with_wildcard(std::string_view json_p
value val;
error_code error = find_field(key).get(val);
if (error) {
- if (error == NO_SUCH_FIELD && internal::json_path_has_wildcard(remaining_path)) {
+ if (error == NO_SUCH_FIELD && path[index].wildcard_follows) {
return SUCCESS;
}
return error;
}
- if (remaining_path.empty()) {
+ if (last) {
callback(val);
return SUCCESS;
} else {
- return val.for_each_at_path_with_wildcard(remaining_path, callback);
+ return val.for_each_at_path_segments(path, index + 1, callback);
}
}
}
@@ -145934,6 +143183,7 @@ simdjson_inline simdjson_warn_unused bool parser::string_buffer_overflow(const u
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(padded_string_view json) & noexcept {
if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
+ json.use_trailing_whitespace_as_padding();
json.remove_utf8_bom();
_document_len = json.length();
@@ -145951,6 +143201,7 @@ simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(p
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate_allow_incomplete_json(padded_string_view json) & noexcept {
if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
+ json.use_trailing_whitespace_as_padding();
json.remove_utf8_bom();
_document_len = json.length();
@@ -146006,6 +143257,7 @@ simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(c
simdjson_warn_unused simdjson_inline simdjson_result<json_iterator> parser::iterate_raw(padded_string_view json) & noexcept {
if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
+ json.use_trailing_whitespace_as_padding();
json.remove_utf8_bom();
@@ -146032,6 +143284,7 @@ inline simdjson_result<document_stream> parser::iterate_many(const char *buf, si
}
inline simdjson_result<document_stream> parser::iterate_many(padded_string_view s, size_t batch_size) noexcept {
if (!s.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
+ s.use_trailing_whitespace_as_padding();
return iterate_many(s.data(), s.length(), batch_size);
}
inline simdjson_result<document_stream> parser::iterate_many(const padded_string &s, size_t batch_size) noexcept {
@@ -146067,6 +143320,7 @@ inline simdjson_result<document_stream> parser::iterate_many(const char *buf, si
}
inline simdjson_result<document_stream> parser::iterate_many(padded_string_view s, size_t batch_size, bool allow_comma_separated) noexcept {
if (!s.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
+ s.use_trailing_whitespace_as_padding();
return iterate_many(s.data(), s.length(), batch_size, allow_comma_separated);
}
inline simdjson_result<document_stream> parser::iterate_many(const padded_string &s, size_t batch_size, bool allow_comma_separated) noexcept {
@@ -146112,6 +143366,7 @@ inline simdjson_result<document_stream> parser::iterate_many(const char *buf, si
}
inline simdjson_result<document_stream> parser::iterate_many(padded_string_view s, size_t batch_size, stream_format format) noexcept {
if (!s.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
+ s.use_trailing_whitespace_as_padding();
return iterate_many(s.data(), s.length(), batch_size, format);
}
inline simdjson_result<document_stream> parser::iterate_many(const std::string &s, size_t batch_size, stream_format format) noexcept {
@@ -146266,17 +143521,18 @@ simdjson_inline bool raw_json_string::is_free_from_unescaped_quote(const char* t
return true;
}
-
simdjson_inline bool raw_json_string::unsafe_is_equal(size_t length, std::string_view target) const noexcept {
// If we are going to call memcmp, then we must know something about the length of the raw_json_string.
- return (length >= target.size()) && (raw()[target.size()] == '"') && !memcmp(raw(), target.data(), target.size());
+ return (length >= target.size()) && (raw()[target.size()] == '"') && !memcmp(raw(), target.data(), target.size())
+ && !escapes_trailing_quote(target);
}
simdjson_inline bool raw_json_string::unsafe_is_equal(std::string_view target) const noexcept {
// Assumptions: does not contain unescaped quote characters("), and
// the raw content is quote terminated within a valid JSON string.
if(target.size() <= SIMDJSON_PADDING) {
- return (raw()[target.size()] == '"') && !memcmp(raw(), target.data(), target.size());
+ return (raw()[target.size()] == '"') && !memcmp(raw(), target.data(), target.size())
+ && !escapes_trailing_quote(target);
}
// Past SIMDJSON_PADDING we can no longer rely on the padding to keep the
// comparison in bounds, so we must stop at the quote terminating the key.
@@ -146302,7 +143558,7 @@ simdjson_inline bool raw_json_string::is_equal(std::string_view target) const no
escaping = false;
}
}
- if(r[pos] != '"') { return false; }
+ if((r[pos] != '"') || escaping) { return false; }
return true;
}
@@ -146315,7 +143571,7 @@ simdjson_inline bool raw_json_string::unsafe_is_equal(const char * target) const
for(;target[pos];pos++) {
if(r[pos] != target[pos]) { return false; }
}
- if(r[pos] != '"') { return false; }
+ if((r[pos] != '"') || escapes_trailing_quote(std::string_view(target, pos))) { return false; }
return true;
}
@@ -146340,7 +143596,7 @@ simdjson_inline bool raw_json_string::is_equal(const char* target) const noexcep
escaping = false;
}
}
- if(r[pos] != '"') { return false; }
+ if((r[pos] != '"') || escaping) { return false; }
return true;
}
@@ -146452,7 +143708,11 @@ simdjson_inline uint32_t token_iterator::peek_index(token_position position) con
return *position;
}
simdjson_inline uint32_t token_iterator::peek_length(token_position position) const noexcept {
- return *(position+1) - *position;
+ // In the last batch of a document_stream, the index past the end of the
+ // structural indexes holds the start of the truncated bytes (see
+ // document_stream::truncated_bytes()), which can come before the end of the
+ // input: a token at the end is then empty.
+ return *(position+1) > *position ? *(position+1) - *position : 0;
}
simdjson_inline uint32_t token_iterator::peek_root_length(token_position position) const noexcept {
@@ -146467,7 +143727,7 @@ simdjson_inline uint32_t token_iterator::peek_index(int32_t delta) const noexcep
return *(_position+delta);
}
simdjson_inline uint32_t token_iterator::peek_length(int32_t delta) const noexcept {
- return *(_position+delta+1) - *(_position+delta);
+ return peek_length(_position+delta);
}
simdjson_inline token_position token_iterator::position() const noexcept {
@@ -146526,6 +143786,8 @@ simdjson_inline simdjson_result<icelake::ondemand::token_iterator>::simdjson_res
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
+#include <cstring> // std::memset (zeroing the root-number scratch padding)
+
namespace simdjson {
namespace icelake {
namespace ondemand {
@@ -146572,6 +143834,10 @@ simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::start
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::started_object() noexcept {
assert_at_container_start();
#if SIMDJSON_DEVELOPMENT_CHECKS
+ // The development checks record the start of every open container in a
+ // buffer of max_depth() entries: a document nested deeper than that is
+ // rejected, as the DOM parser does.
+ if (size_t(_depth) >= _json_iter->parser->max_depth()) { return report_error(DEPTH_ERROR, "Exceeded max depth!"); }
_json_iter->set_start_position(_depth, start_position());
#endif
if (*_json_iter->peek() == '}') {
@@ -146981,6 +144247,10 @@ inline std::string value_iterator::to_string() const noexcept {
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::started_array() noexcept {
assert_at_container_start();
+#if SIMDJSON_DEVELOPMENT_CHECKS
+ // See started_object().
+ if (size_t(_depth) >= _json_iter->parser->max_depth()) { return report_error(DEPTH_ERROR, "Exceeded max depth!"); }
+#endif
if (*_json_iter->peek() == ']') {
logger::log_value(*_json_iter, "empty array");
_json_iter->return_current_and_advance();
@@ -147186,8 +144456,10 @@ simdjson_inline simdjson_result<icelake::number_type> value_iterator::get_root_n
// Per https://www.exploringbinary.com/maximum-number-of-decimal-digits-in-binary-floating-point-numbers/,
// 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest
// number: -0.<fraction>e-308.
- uint8_t tmpbuf[1074+8+1+1];
- tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
+ // +SIMDJSON_PADDING: the digit scan reads up to 7 bytes past the terminator.
+ uint8_t tmpbuf[1074+8+1+1+SIMDJSON_PADDING];
+ // Null terminate, and zero the padding as allocate_padded_buffer does.
+ std::memset(tmpbuf+1074+8+1, 0, 1+SIMDJSON_PADDING);
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
if(numberparsing::check_if_integer(json, max_len)) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
@@ -147208,8 +144480,10 @@ simdjson_inline simdjson_result<number> value_iterator::get_root_number(bool che
// 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest
// number: -0.<fraction>e-308.
// NOTE: the current approach doesn't work for very big integer numbers containing more than 1074 digits.
- uint8_t tmpbuf[1074+8+1+1];
- tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
+ // +SIMDJSON_PADDING: the digit scan reads up to 7 bytes past the terminator.
+ uint8_t tmpbuf[1074+8+1+1+SIMDJSON_PADDING];
+ // Null terminate, and zero the padding as allocate_padded_buffer does.
+ std::memset(tmpbuf+1074+8+1, 0, 1+SIMDJSON_PADDING);
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
if(numberparsing::check_if_integer(json, max_len)) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
@@ -147279,10 +144553,10 @@ simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> value_iterator::g
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> value_iterator::get_root_uint64_in_string(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("uint64");
- uint8_t tmpbuf[20+1+1]{}; // <20 digits> is the longest possible unsigned integer
- tmpbuf[20+1] = '\0'; // make sure that buffer is always null terminated.
- if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 20+1)) {
- logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 20 characters");
+ uint8_t tmpbuf[20+2+1]{}; // "<20 digits>" is the longest possible unsigned integer
+ tmpbuf[20+2] = '\0'; // make sure that buffer is always null terminated.
+ if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 20+2)) {
+ logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 22 characters");
return NUMBER_ERROR;
}
auto result = numberparsing::parse_unsigned_in_string(tmpbuf);
@@ -147312,10 +144586,10 @@ simdjson_warn_unused simdjson_inline simdjson_result<int64_t> value_iterator::ge
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> value_iterator::get_root_int64_in_string(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("int64");
- uint8_t tmpbuf[20+1+1]; // -<19 digits> is the longest possible integer
- tmpbuf[20+1] = '\0'; // make sure that buffer is always null terminated.
- if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 20+1)) {
- logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 20 characters");
+ uint8_t tmpbuf[20+2+1]; // "-<19 digits>" is the longest possible integer
+ tmpbuf[20+2] = '\0'; // make sure that buffer is always null terminated.
+ if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 20+2)) {
+ logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 22 characters");
return NUMBER_ERROR;
}
@@ -147332,8 +144606,10 @@ simdjson_warn_unused simdjson_inline simdjson_result<double> value_iterator::get
// Per https://www.exploringbinary.com/maximum-number-of-decimal-digits-in-binary-floating-point-numbers/,
// 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest
// number: -0.<fraction>e-308.
- uint8_t tmpbuf[1074+8+1+1]; // +1 for null termination.
- tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
+ // +SIMDJSON_PADDING: the digit scan reads up to 7 bytes past the terminator.
+ uint8_t tmpbuf[1074+8+1+1+SIMDJSON_PADDING]; // +1 for null termination.
+ // Null terminate, and zero the padding as allocate_padded_buffer does.
+ std::memset(tmpbuf+1074+8+1, 0, 1+SIMDJSON_PADDING);
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters");
return NUMBER_ERROR;
@@ -147352,8 +144628,10 @@ simdjson_warn_unused simdjson_inline simdjson_result<double> value_iterator::get
// Per https://www.exploringbinary.com/maximum-number-of-decimal-digits-in-binary-floating-point-numbers/,
// 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest
// number: -0.<fraction>e-308.
- uint8_t tmpbuf[1074+8+1+1]; // +1 for null termination.
- tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
+ // +SIMDJSON_PADDING: the digit scan reads up to 7 bytes past the terminator.
+ uint8_t tmpbuf[1074+8+1+1+SIMDJSON_PADDING]; // +1 for null termination.
+ // Null terminate, and zero the padding as allocate_padded_buffer does.
+ std::memset(tmpbuf+1074+8+1, 0, 1+SIMDJSON_PADDING);
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters");
return NUMBER_ERROR;
@@ -147372,8 +144650,10 @@ simdjson_warn_unused simdjson_inline simdjson_result<float> value_iterator::get_
// We use the same buffer size as get_root_double: the number of significant
// digits that matter is smaller for binary32, but the JSON document may still
// spell out a long number that we must parse (and round) faithfully.
- uint8_t tmpbuf[1074+8+1+1]; // +1 for null termination.
- tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
+ // +SIMDJSON_PADDING: the digit scan reads up to 7 bytes past the terminator.
+ uint8_t tmpbuf[1074+8+1+1+SIMDJSON_PADDING]; // +1 for null termination.
+ // Null terminate, and zero the padding as allocate_padded_buffer does.
+ std::memset(tmpbuf+1074+8+1, 0, 1+SIMDJSON_PADDING);
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters");
return NUMBER_ERROR;
@@ -147389,8 +144669,10 @@ simdjson_warn_unused simdjson_inline simdjson_result<float> value_iterator::get_
simdjson_warn_unused simdjson_inline simdjson_result<float> value_iterator::get_root_float_in_string(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("float");
- uint8_t tmpbuf[1074+8+1+1]; // +1 for null termination.
- tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
+ // +SIMDJSON_PADDING: the digit scan reads up to 7 bytes past the terminator.
+ uint8_t tmpbuf[1074+8+1+1+SIMDJSON_PADDING]; // +1 for null termination.
+ // Null terminate, and zero the padding as allocate_padded_buffer does.
+ std::memset(tmpbuf+1074+8+1, 0, 1+SIMDJSON_PADDING);
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters");
return NUMBER_ERROR;
@@ -147411,7 +144693,7 @@ simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::get_r
// "true" and "false" are 4 and 5 characters long, respectively.
bool value_true = (max_len >= 4 && !atomparsing::str4ncmp(json, "true") &&
(max_len == 4 || jsoncharutils::is_structural_or_whitespace(json[4])));
- bool value_false = (max_len >= 5 && !atomparsing::str4ncmp(json, "false") &&
+ bool value_false = (max_len >= 5 && !atomparsing::str4ncmp(json, "fals") && json[4] == 'e' &&
(max_len == 5 || jsoncharutils::is_structural_or_whitespace(json[5])));
if(value_true == false && value_false == false) { return incorrect_type_error("Not a boolean"); }
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
@@ -148051,49 +145333,6 @@ namespace json_path {
using ::simdjson::icelake::ondemand::value;
-// Path step types
-enum class step_type {
- field, // .field_name or ["field_name"]
- array_index // [index]
-};
-
-// Represents a single step in a JSON path
-template<std::size_t N>
-struct path_step {
- step_type type;
- char key[N]; // Field name (empty for array indices)
- std::size_t index; // Array index (0 for field access)
-
- constexpr path_step(step_type t, const char (&k)[N], std::size_t idx = 0)
- : type(t), index(idx) {
- for (std::size_t i = 0; i < N; ++i) {
- key[i] = k[i];
- }
- }
-
- constexpr std::string_view key_view() const {
- return {key, N - 1};
- }
-};
-
-// Helper to create field step
-template<std::size_t N>
-consteval auto make_field_step(const char (&name)[N]) {
- return path_step<N>(step_type::field, name, 0);
-}
-
-// Helper to create array index step
-consteval auto make_index_step(std::size_t idx) {
- return path_step<1>(step_type::array_index, "", idx);
-}
-
-// Parse state for compile-time JSON path parsing
-struct parse_result {
- bool success;
- std::size_t pos;
- std::string_view error_msg;
-};
-
// Compile-time JSON path parser
// Supports subset: .field, ["field"], [index], nested combinations
template<constevalutil::fixed_string Path>
@@ -148107,77 +145346,6 @@ struct json_path_parser {
}
return 0;
}
-
- // Count the number of steps in the path at compile time
- static consteval std::size_t count_steps() {
- std::size_t count = 0;
- std::size_t i = skip_root();
-
- while (i < path_str.size()) {
- if (path_str[i] == '.') {
- // Field access: .field
- ++i;
- if (i >= path_str.size()) break;
-
- // Skip field name
- while (i < path_str.size() && path_str[i] != '.' && path_str[i] != '[') {
- ++i;
- }
- ++count;
- } else if (path_str[i] == '[') {
- // Array or bracket notation
- ++i;
- if (i >= path_str.size()) break;
-
- if (path_str[i] == '"' || path_str[i] == '\'') {
- // Field access: ["field"] or ['field']
- char quote = path_str[i];
- ++i;
- while (i < path_str.size() && path_str[i] != quote) {
- ++i;
- }
- if (i < path_str.size()) ++i; // skip closing quote
- if (i < path_str.size() && path_str[i] == ']') ++i;
- } else {
- // Array index: [0], [123]
- while (i < path_str.size() && path_str[i] != ']') {
- ++i;
- }
- if (i < path_str.size()) ++i; // skip ]
- }
- ++count;
- } else {
- ++i;
- }
- }
-
- return count;
- }
-
- // Parse a field name at compile time
- static consteval std::size_t parse_field_name(std::size_t start, char* out, std::size_t max_len) {
- std::size_t len = 0;
- std::size_t i = start;
-
- while (i < path_str.size() && path_str[i] != '.' && path_str[i] != '[' && len < max_len - 1) {
- out[len++] = path_str[i++];
- }
- out[len] = '\0';
- return i;
- }
-
- // Parse an array index at compile time
- static consteval std::pair<std::size_t, std::size_t> parse_array_index(std::size_t start) {
- std::size_t index = 0;
- std::size_t i = start;
-
- while (i < path_str.size() && path_str[i] >= '0' && path_str[i] <= '9') {
- index = index * 10 + (path_str[i] - '0');
- ++i;
- }
-
- return {i, index};
- }
};
// Compile-time path accessor generator
@@ -148186,7 +145354,6 @@ struct path_accessor {
using value = ::simdjson::icelake::ondemand::value;
static constexpr auto parser = json_path_parser<Path>();
- static constexpr std::size_t num_steps = parser.count_steps();
static constexpr std::string_view path_view = Path.view();
// Compile-time accessor generation
@@ -148301,93 +145468,6 @@ private:
return current_type;
}
-private:
- // Walk path and extract directly into final field using compile-time reflection
- template<std::meta::info CurrentType, std::size_t PathPos, typename TargetType>
- static inline error_code extract_with_reflection(simdjson_result<value> current, TargetType& target_ref) noexcept {
- if (current.error()) return current.error();
-
- // Base case: end of path - extract into target
- if constexpr (PathPos >= path_view.size()) {
- return current.get(target_ref);
- }
- // Field access: .field_name
- else if constexpr (path_view[PathPos] == '.') {
- constexpr auto field_info = parse_next_field(PathPos);
- constexpr std::string_view field_name = std::get<0>(field_info);
- constexpr std::size_t next_pos = std::get<1>(field_info);
-
- constexpr auto member_info = find_member_by_name(CurrentType, field_name);
- static_assert(member_info != ^^void, "Field not found in struct");
-
- constexpr auto member_type = std::meta::type_of(member_info);
-
- auto obj_result = current.get_object();
- if (obj_result.error()) return obj_result.error();
- auto obj = obj_result.value_unsafe();
- auto field_value = obj.find_field_unordered(field_name);
-
- if constexpr (next_pos >= path_view.size()) {
- return field_value.get(target_ref);
- } else {
- return extract_with_reflection<member_type, next_pos>(field_value, target_ref);
- }
- }
- // Bracket notation: [index] or ["field"]
- else if constexpr (path_view[PathPos] == '[') {
- constexpr auto bracket_info = parse_bracket(PathPos);
- constexpr bool is_field = std::get<0>(bracket_info);
- constexpr std::size_t next_pos = std::get<2>(bracket_info);
-
- if constexpr (is_field) {
- constexpr std::string_view field_name = std::get<1>(bracket_info);
- constexpr auto member_info = find_member_by_name(CurrentType, field_name);
- static_assert(member_info != ^^void, "Field not found in struct");
- constexpr auto member_type = std::meta::type_of(member_info);
-
- auto obj_result = current.get_object();
- if (obj_result.error()) return obj_result.error();
- auto obj = obj_result.value_unsafe();
- auto field_value = obj.find_field_unordered(field_name);
-
- if constexpr (next_pos >= path_view.size()) {
- return field_value.get(target_ref);
- } else {
- return extract_with_reflection<member_type, next_pos>(field_value, target_ref);
- }
- } else {
- constexpr std::size_t index = std::get<3>(bracket_info);
- constexpr auto elem_type = get_element_type_reflected(CurrentType);
- static_assert(elem_type != ^^void, "Could not determine array element type");
-
- auto arr_result = current.get_array();
- if (arr_result.error()) return arr_result.error();
- auto arr = arr_result.value_unsafe();
- auto elem_value = arr.at(index);
-
- if constexpr (next_pos >= path_view.size()) {
- return elem_value.get(target_ref);
- } else {
- return extract_with_reflection<elem_type, next_pos>(elem_value, target_ref);
- }
- }
- }
- // Skip unexpected characters and continue
- else {
- return extract_with_reflection<CurrentType, PathPos + 1>(current, target_ref);
- }
- }
-
- // Find member by name in reflected type
- static consteval std::meta::info find_member_by_name(std::meta::info type_refl, std::string_view name) {
- auto members = std::meta::nonstatic_data_members_of(type_refl, std::meta::access_context::unchecked());
- for (auto mem : members) {
- if (std::meta::identifier_of(mem) == name) {
- return mem;
- }
- }
- }
-
// Generate compile-time accessor code by walking the path
template<std::size_t PathPos>
static inline simdjson_result<value> access_impl(simdjson_result<value> current) noexcept {
@@ -148517,55 +145597,6 @@ public:
}
private:
- // Check if type has member with given name
- template<typename Type>
- static consteval bool has_member(std::string_view member_name) {
- constexpr auto members = std::meta::nonstatic_data_members_of(^^Type, std::meta::access_context::unchecked());
- for (auto mem : members) {
- if (std::meta::identifier_of(mem) == member_name) {
- return true;
- }
- }
- return false;
- }
-
- // Get type of member by name
- template<typename Type>
- static consteval auto get_member_type(std::string_view member_name) {
- constexpr auto members = std::meta::nonstatic_data_members_of(^^Type, std::meta::access_context::unchecked());
- for (auto mem : members) {
- if (std::meta::identifier_of(mem) == member_name) {
- return std::meta::type_of(mem);
- }
- }
- return ^^void;
- }
-
- // Check if non-reflected type is array-like
- template<typename Type>
- static consteval bool is_container_type() {
- using BaseType = std::remove_cvref_t<Type>;
- if constexpr (requires { typename BaseType::value_type; }) {
- return true;
- }
- if constexpr (std::is_array_v<BaseType>) {
- return true;
- }
- return false;
- }
-
- // Extract element type from non-reflected container
- template<typename Type>
- using extract_element_type = std::conditional_t<
- requires { typename std::remove_cvref_t<Type>::value_type; },
- typename std::remove_cvref_t<Type>::value_type,
- std::conditional_t<
- std::is_array_v<std::remove_cvref_t<Type>>,
- std::remove_extent_t<std::remove_cvref_t<Type>>,
- void
- >
- >;
-
// Validate path matches struct definition
static consteval bool validate_path() {
if constexpr (!std::is_class_v<T>) {
@@ -149736,31 +146767,6 @@ backslash_and_quote::copy_and_find(const uint8_t *src, uint8_t *dst) {
}
-struct escaping {
- static constexpr uint32_t BYTES_PROCESSED = 16;
- simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
-
- simdjson_inline bool has_escape() { return escape_bits != 0; }
- simdjson_inline int escape_index() { return trailing_zeroes(escape_bits); }
-
- uint64_t escape_bits;
-}; // struct escaping
-
-
-
-simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
- static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes");
- simd8<uint8_t> v(src);
- v.store(dst);
- simd8<bool> is_quote = (v == '"');
- simd8<bool> is_backslash = (v == '\\');
- simd8<bool> is_control = (v < 32);
- return {
- // We store it as a 64-bit bitmask even though we only need 16 bits.
- uint64_t((is_backslash | is_quote | is_control).to_bitmask())
- };
-}
-
} // unnamed namespace
} // namespace ppc64
} // namespace simdjson
@@ -149972,11 +146978,6 @@ protected:
public:
simdjson_inline value_iterator() noexcept = default;
- /**
- * Denote that we're starting a document.
- */
- simdjson_inline void start_document() noexcept;
-
/**
* Skips a non-iterated or partially-iterated JSON value, whether it is a scalar, array or object.
*
@@ -150377,16 +147378,7 @@ protected:
simdjson_warn_unused simdjson_inline error_code start_container(uint8_t start_char, const char *incorrect_type_message, const char *type) noexcept;
simdjson_warn_unused simdjson_inline error_code end_container() noexcept;
- /**
- * Advance to a place expecting a value (increasing depth).
- *
- * @return The current token (the one left behind).
- * @error TAPE_ERROR If the document ended early.
- */
- simdjson_inline simdjson_result<const uint8_t *> advance_to_value() noexcept;
-
simdjson_warn_unused simdjson_inline error_code incorrect_type_error(const char *message) const noexcept;
- simdjson_warn_unused simdjson_inline error_code error_unless_more_tokens(uint32_t tokens=1) const noexcept;
simdjson_inline bool is_at_start() const noexcept;
/**
@@ -150469,6 +147461,7 @@ public:
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
+/* amalgamation skipped (editor-only): #include "simdjson/jsonpathutil.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/deserialize.h" */
/* amalgamation skipped (editor-only): #include <vector> */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
@@ -151322,11 +148315,19 @@ protected:
*/
simdjson_inline simdjson_result<object> start_or_resume_object() noexcept;
+ /**
+ * The recursive step of for_each_at_path_with_wildcard: matches
+ * path[index] at this level and what follows below it.
+ */
+ template <typename Func>
+ inline error_code for_each_at_path_segments(const std::vector<simdjson::internal::json_path_segment> &path, size_t index, Func &callback) noexcept;
+
// simdjson_inline void log_value(const char *type) const noexcept;
// simdjson_inline void log_error(const char *message) const noexcept;
value_iterator iter{};
+ friend class array;
friend class document;
friend class array_iterator;
friend class field;
@@ -152015,6 +149016,7 @@ public:
/**
* Take an input in json containing max_len characters and attempt to copy it over to tmpbuf, a buffer with
* N bytes of capacity. It will return false if N is too small (smaller than max_len) of if it is zero.
+ * Trailing whitespace, commas, and record separators (0x1E) do not count toward max_len.
* The buffer (tmpbuf) is padded with space characters.
*/
simdjson_warn_unused simdjson_inline bool copy_to_buffer(const uint8_t *json, uint32_t max_len, uint8_t *tmpbuf, size_t N) noexcept;
@@ -152047,8 +149049,11 @@ public:
/**
* Updates this json iterator so that it is back at the beginning of the document,
* as if it had just been created.
+ *
+ * An iterator that is no longer alive (e.g., abandoned after a fatal error) has no
+ * document to go back to: it is left as is and its error is returned.
*/
- inline void rewind() noexcept;
+ simdjson_inline error_code rewind() noexcept;
/**
* This checks whether the {,},[,] are balanced so that the document
* ends with proper zero depth. This requires scanning the whole document
@@ -152403,6 +149408,28 @@ public:
private:
+ /**
+ * Returns true if target ends with an odd number of backslashes. The last one
+ * then escapes the quote that follows the matching bytes in the raw string, so
+ * that quote does not end the string. Not inlined: callers check the last byte
+ * first, and keys ending in a backslash are rare.
+ */
+ static simdjson_never_inline bool ends_with_unpaired_backslash(std::string_view target) noexcept {
+ size_t backslash_count{0};
+ while(backslash_count < target.size() && target[target.size() - 1 - backslash_count] == '\\') {
+ backslash_count++;
+ }
+ return backslash_count % 2 == 1;
+ }
+
+ /**
+ * True when a byte-for-byte match must be rejected because target escapes the
+ * closing quote. The last-byte test keeps the counting loop off the common path.
+ */
+ static simdjson_inline bool escapes_trailing_quote(std::string_view target) noexcept {
+ return simdjson_unlikely(!target.empty() && target[target.size() - 1] == '\\')
+ && ends_with_unpaired_backslash(target);
+ }
/**
* This will set the inner pointer to zero, effectively making
@@ -152976,6 +150003,7 @@ public:
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
+/* amalgamation skipped (editor-only): #include "simdjson/jsonpathutil.h" */
/* amalgamation skipped (editor-only): #include <vector> */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
@@ -153168,6 +150196,13 @@ protected:
*/
simdjson_warn_unused simdjson_inline error_code consume() noexcept;
+ /**
+ * The recursive step of for_each_at_path_with_wildcard: matches
+ * path[index] at this level and what follows below it.
+ */
+ template <typename Func>
+ inline error_code for_each_at_path_segments(const std::vector<simdjson::internal::json_path_segment> &path, size_t index, Func &callback) noexcept;
+
/**
* Begin array iteration.
*
@@ -154177,6 +151212,9 @@ public:
* Reset the iterator inside the document instance so we are pointing back at the
* beginning of the document, as if it had just been created. It invalidates all
* values, objects and arrays that you have created so far (including unescaped strings).
+ *
+ * A document that is no longer alive (see is_alive()) cannot be rewound: it is
+ * left as is.
*/
inline void rewind() noexcept;
/**
@@ -154250,6 +151288,8 @@ public:
* - INCORRECT_TYPE if a non-integer is used to access an array
* - INVALID_JSON_POINTER if the JSON pointer is invalid and cannot be parsed
* - SCALAR_DOCUMENT_AS_VALUE if the json_pointer is empty and the document is not a scalar (see is_scalar() function).
+ * - the fatal error (e.g., TAPE_ERROR) that ended an earlier call, as the document can
+ * no longer be rewound (see is_alive()).
*/
simdjson_inline simdjson_result<value> at_pointer(std::string_view json_pointer) noexcept;
@@ -154273,6 +151313,8 @@ public:
* - NO_SUCH_FIELD if a field does not exist in an object
* - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length
* - INCORRECT_TYPE if a non-integer is used to access an array
+ * - the fatal error (e.g., TAPE_ERROR) that ended an earlier call, as the document can
+ * no longer be rewound (see is_alive()).
*/
simdjson_inline simdjson_result<value> at_path(std::string_view json_path) noexcept;
@@ -154299,7 +151341,9 @@ public:
/**
* Consumes the document and returns a string_view instance corresponding to the
* document as represented in JSON. It points inside the original byte array containing
- * the JSON document.
+ * the JSON document. It starts at the first byte of the document and ends at its last
+ * byte: it does not include the white space that follows the document, nor, in a
+ * document_stream, the separators or the documents that follow it.
*/
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
@@ -154626,134 +151670,134 @@ public:
using ppc64::implementation_simdjson_result_base<ppc64::ondemand::document>::operator*;
using ppc64::implementation_simdjson_result_base<ppc64::ondemand::document>::operator->;
- template <class T, typename std::enable_if<std::is_same<T, ppc64::ondemand::document>::value == false>::type>
- explicit simdjson_inline operator T() noexcept(false);
- simdjson_inline operator ppc64::ondemand::array() & noexcept(false);
- simdjson_inline operator ppc64::ondemand::object() & noexcept(false);
- explicit simdjson_inline operator uint64_t() noexcept(false);
- explicit simdjson_inline operator int64_t() noexcept(false);
- explicit simdjson_inline operator double() noexcept(false);
- explicit simdjson_inline operator std::string_view() noexcept(false);
- explicit simdjson_inline operator ppc64::ondemand::raw_json_string() noexcept(false);
- explicit simdjson_inline operator bool() noexcept(false);
- simdjson_inline operator ppc64::ondemand::value() noexcept(false);
-#endif
- simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
- simdjson_inline simdjson_result<size_t> count_fields() & noexcept;
- simdjson_inline simdjson_result<ppc64::ondemand::value> at(size_t index) & noexcept;
- simdjson_inline simdjson_result<ppc64::ondemand::array_iterator> begin() & noexcept;
- simdjson_inline simdjson_result<ppc64::ondemand::array_iterator> end() & noexcept;
- simdjson_inline simdjson_result<ppc64::ondemand::value> find_field(std::string_view key) & noexcept;
- simdjson_inline simdjson_result<ppc64::ondemand::value> find_field(const char *key) & noexcept;
- simdjson_inline simdjson_result<ppc64::ondemand::value> operator[](std::string_view key) & noexcept;
- simdjson_inline simdjson_result<ppc64::ondemand::value> operator[](const char *key) & noexcept;
- simdjson_result<ppc64::ondemand::value> operator[](int) & noexcept = delete;
- simdjson_inline simdjson_result<ppc64::ondemand::value> find_field_unordered(std::string_view key) & noexcept;
- simdjson_inline simdjson_result<ppc64::ondemand::value> find_field_unordered(const char *key) & noexcept;
- simdjson_inline simdjson_result<ppc64::ondemand::json_type> type() noexcept;
- simdjson_inline simdjson_result<bool> is_scalar() noexcept;
- simdjson_inline simdjson_result<bool> is_string() noexcept;
- simdjson_inline simdjson_result<const char *> current_location() noexcept;
- simdjson_inline int32_t current_depth() const noexcept;
- simdjson_inline bool at_end() const noexcept;
- simdjson_inline bool is_negative() noexcept;
- simdjson_inline simdjson_result<bool> is_integer() noexcept;
- simdjson_inline simdjson_result<ppc64::number_type> get_number_type() noexcept;
- simdjson_inline simdjson_result<ppc64::ondemand::number> get_number() noexcept;
- /** @copydoc simdjson_inline std::string_view document::raw_json_token() const noexcept */
- simdjson_inline simdjson_result<std::string_view> raw_json_token() noexcept;
-
- simdjson_inline simdjson_result<ppc64::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
- simdjson_inline simdjson_result<ppc64::ondemand::value> at_path(std::string_view json_path) noexcept;
-#if SIMDJSON_SUPPORTS_CONCEPTS
- template <typename Func>
- requires std::invocable<Func, ppc64::ondemand::value>
-#else
- template <typename Func>
-#endif
- simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
-#if SIMDJSON_STATIC_REFLECTION
- template<constevalutil::fixed_string... FieldNames, typename T>
- requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
- simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept;
-#endif // SIMDJSON_STATIC_REFLECTION
-};
-
-
-} // namespace simdjson
-
-
-
-namespace simdjson {
-
-template<>
-struct simdjson_result<ppc64::ondemand::document_reference> : public ppc64::implementation_simdjson_result_base<ppc64::ondemand::document_reference> {
-public:
- simdjson_inline simdjson_result(ppc64::ondemand::document_reference value, error_code error) noexcept;
- simdjson_inline simdjson_result() noexcept = default;
- simdjson_inline error_code rewind() noexcept;
-
- simdjson_inline simdjson_result<ppc64::ondemand::array> get_array() & noexcept;
- simdjson_inline simdjson_result<ppc64::ondemand::object> get_object() & noexcept;
- simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
- simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
- simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
- simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
- simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
- simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
- simdjson_inline simdjson_result<uint16_t> get_uint16() noexcept;
- simdjson_inline simdjson_result<int16_t> get_int16() noexcept;
- simdjson_inline simdjson_result<uint8_t> get_uint8() noexcept;
- simdjson_inline simdjson_result<int8_t> get_int8() noexcept;
- simdjson_inline simdjson_result<double> get_double() noexcept;
- simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
- simdjson_inline simdjson_result<float> get_float() noexcept;
- simdjson_inline simdjson_result<float> get_float_in_string() noexcept;
-#if SIMDJSON_SUPPORTS_FLOAT32_T
- simdjson_inline simdjson_result<std::float32_t> get_float32() noexcept;
-#endif // SIMDJSON_SUPPORTS_FLOAT32_T
-#if SIMDJSON_SUPPORTS_FLOAT64_T
- simdjson_inline simdjson_result<std::float64_t> get_float64() noexcept;
-#endif // SIMDJSON_SUPPORTS_FLOAT64_T
- simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
-#if SIMDJSON_SUPPORTS_CHAR8_T
- simdjson_inline simdjson_result<std::u8string_view> get_u8string(bool allow_replacement = false) noexcept;
-#endif // SIMDJSON_SUPPORTS_CHAR8_T
- template <typename string_type>
- simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
- simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
- simdjson_inline simdjson_result<ppc64::ondemand::raw_json_string> get_raw_json_string() noexcept;
- simdjson_inline simdjson_result<bool> get_bool() noexcept;
- simdjson_inline simdjson_result<ppc64::ondemand::value> get_value() noexcept;
- simdjson_inline simdjson_result<bool> is_null() noexcept;
-
- template<typename T> simdjson_inline simdjson_result<T> get() &
-#if SIMDJSON_SUPPORTS_CONCEPTS
- noexcept(nothrow_gettable<T, ppc64::ondemand::document_reference>);
-#else
- noexcept;
-#endif
- template<typename T> simdjson_inline simdjson_result<T> get() &&
-#if SIMDJSON_SUPPORTS_CONCEPTS
- noexcept(nothrow_gettable<T, ppc64::ondemand::document_reference>);
-#else
- noexcept;
-#endif
-
- template<typename T> simdjson_inline error_code get(T &out) &
-#if SIMDJSON_SUPPORTS_CONCEPTS
- noexcept(nothrow_gettable<T, ppc64::ondemand::document_reference>);
-#else
- noexcept;
-#endif
- template<typename T> simdjson_inline error_code get(T &out) &&
-#if SIMDJSON_SUPPORTS_CONCEPTS
- noexcept(nothrow_gettable<T, ppc64::ondemand::document_reference>);
-#else
- noexcept;
-#endif
-#if SIMDJSON_EXCEPTIONS
- template <class T>
+ simdjson_inline operator ppc64::ondemand::array() & noexcept(false);
+ simdjson_inline operator ppc64::ondemand::object() & noexcept(false);
+ explicit simdjson_inline operator uint64_t() noexcept(false);
+ explicit simdjson_inline operator int64_t() noexcept(false);
+ explicit simdjson_inline operator double() noexcept(false);
+ explicit simdjson_inline operator std::string_view() noexcept(false);
+ explicit simdjson_inline operator ppc64::ondemand::raw_json_string() noexcept(false);
+ explicit simdjson_inline operator bool() noexcept(false);
+ simdjson_inline operator ppc64::ondemand::value() noexcept(false);
+#endif
+ simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
+ simdjson_inline simdjson_result<size_t> count_fields() & noexcept;
+ simdjson_inline simdjson_result<ppc64::ondemand::value> at(size_t index) & noexcept;
+ simdjson_inline simdjson_result<ppc64::ondemand::array_iterator> begin() & noexcept;
+ simdjson_inline simdjson_result<ppc64::ondemand::array_iterator> end() & noexcept;
+ simdjson_inline simdjson_result<ppc64::ondemand::value> find_field(std::string_view key) & noexcept;
+ simdjson_inline simdjson_result<ppc64::ondemand::value> find_field(const char *key) & noexcept;
+ simdjson_inline simdjson_result<ppc64::ondemand::value> operator[](std::string_view key) & noexcept;
+ simdjson_inline simdjson_result<ppc64::ondemand::value> operator[](const char *key) & noexcept;
+ simdjson_result<ppc64::ondemand::value> operator[](int) & noexcept = delete;
+ simdjson_inline simdjson_result<ppc64::ondemand::value> find_field_unordered(std::string_view key) & noexcept;
+ simdjson_inline simdjson_result<ppc64::ondemand::value> find_field_unordered(const char *key) & noexcept;
+ simdjson_inline simdjson_result<ppc64::ondemand::json_type> type() noexcept;
+ simdjson_inline simdjson_result<bool> is_scalar() noexcept;
+ simdjson_inline simdjson_result<bool> is_string() noexcept;
+ simdjson_inline simdjson_result<const char *> current_location() noexcept;
+ simdjson_inline int32_t current_depth() const noexcept;
+ simdjson_inline bool at_end() const noexcept;
+ simdjson_inline bool is_negative() noexcept;
+ simdjson_inline simdjson_result<bool> is_integer() noexcept;
+ simdjson_inline simdjson_result<ppc64::number_type> get_number_type() noexcept;
+ simdjson_inline simdjson_result<ppc64::ondemand::number> get_number() noexcept;
+ /** @copydoc simdjson_inline std::string_view document::raw_json_token() const noexcept */
+ simdjson_inline simdjson_result<std::string_view> raw_json_token() noexcept;
+ /** @copydoc simdjson_inline simdjson_result<std::string_view> document::raw_json() noexcept */
+ simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
+
+ simdjson_inline simdjson_result<ppc64::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
+ simdjson_inline simdjson_result<ppc64::ondemand::value> at_path(std::string_view json_path) noexcept;
+#if SIMDJSON_SUPPORTS_CONCEPTS
+ template <typename Func>
+ requires std::invocable<Func, ppc64::ondemand::value>
+#else
+ template <typename Func>
+#endif
+ simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
+#if SIMDJSON_STATIC_REFLECTION
+ template<constevalutil::fixed_string... FieldNames, typename T>
+ requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
+ simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept;
+#endif // SIMDJSON_STATIC_REFLECTION
+};
+
+
+} // namespace simdjson
+
+
+
+namespace simdjson {
+
+template<>
+struct simdjson_result<ppc64::ondemand::document_reference> : public ppc64::implementation_simdjson_result_base<ppc64::ondemand::document_reference> {
+public:
+ simdjson_inline simdjson_result(ppc64::ondemand::document_reference value, error_code error) noexcept;
+ simdjson_inline simdjson_result() noexcept = default;
+ simdjson_inline error_code rewind() noexcept;
+
+ simdjson_inline simdjson_result<ppc64::ondemand::array> get_array() & noexcept;
+ simdjson_inline simdjson_result<ppc64::ondemand::object> get_object() & noexcept;
+ simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
+ simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
+ simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
+ simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
+ simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
+ simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
+ simdjson_inline simdjson_result<uint16_t> get_uint16() noexcept;
+ simdjson_inline simdjson_result<int16_t> get_int16() noexcept;
+ simdjson_inline simdjson_result<uint8_t> get_uint8() noexcept;
+ simdjson_inline simdjson_result<int8_t> get_int8() noexcept;
+ simdjson_inline simdjson_result<double> get_double() noexcept;
+ simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
+ simdjson_inline simdjson_result<float> get_float() noexcept;
+ simdjson_inline simdjson_result<float> get_float_in_string() noexcept;
+#if SIMDJSON_SUPPORTS_FLOAT32_T
+ simdjson_inline simdjson_result<std::float32_t> get_float32() noexcept;
+#endif // SIMDJSON_SUPPORTS_FLOAT32_T
+#if SIMDJSON_SUPPORTS_FLOAT64_T
+ simdjson_inline simdjson_result<std::float64_t> get_float64() noexcept;
+#endif // SIMDJSON_SUPPORTS_FLOAT64_T
+ simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
+#if SIMDJSON_SUPPORTS_CHAR8_T
+ simdjson_inline simdjson_result<std::u8string_view> get_u8string(bool allow_replacement = false) noexcept;
+#endif // SIMDJSON_SUPPORTS_CHAR8_T
+ template <typename string_type>
+ simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
+ simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
+ simdjson_inline simdjson_result<ppc64::ondemand::raw_json_string> get_raw_json_string() noexcept;
+ simdjson_inline simdjson_result<bool> get_bool() noexcept;
+ simdjson_inline simdjson_result<ppc64::ondemand::value> get_value() noexcept;
+ simdjson_inline simdjson_result<bool> is_null() noexcept;
+
+ template<typename T> simdjson_inline simdjson_result<T> get() &
+#if SIMDJSON_SUPPORTS_CONCEPTS
+ noexcept(nothrow_gettable<T, ppc64::ondemand::document_reference>);
+#else
+ noexcept;
+#endif
+ template<typename T> simdjson_inline simdjson_result<T> get() &&
+#if SIMDJSON_SUPPORTS_CONCEPTS
+ noexcept(nothrow_gettable<T, ppc64::ondemand::document_reference>);
+#else
+ noexcept;
+#endif
+
+ template<typename T> simdjson_inline error_code get(T &out) &
+#if SIMDJSON_SUPPORTS_CONCEPTS
+ noexcept(nothrow_gettable<T, ppc64::ondemand::document_reference>);
+#else
+ noexcept;
+#endif
+ template<typename T> simdjson_inline error_code get(T &out) &&
+#if SIMDJSON_SUPPORTS_CONCEPTS
+ noexcept(nothrow_gettable<T, ppc64::ondemand::document_reference>);
+#else
+ noexcept;
+#endif
+#if SIMDJSON_EXCEPTIONS
+ template <class T>
explicit simdjson_inline operator T() noexcept(false);
simdjson_inline operator ppc64::ondemand::array() & noexcept(false);
simdjson_inline operator ppc64::ondemand::object() & noexcept(false);
@@ -154788,6 +151832,8 @@ public:
simdjson_inline simdjson_result<ppc64::ondemand::number> get_number() noexcept;
/** @copydoc simdjson_inline std::string_view document_reference::raw_json_token() const noexcept */
simdjson_inline simdjson_result<std::string_view> raw_json_token() noexcept;
+ /** @copydoc simdjson_inline simdjson_result<std::string_view> document::raw_json() noexcept */
+ simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
simdjson_inline simdjson_result<ppc64::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
simdjson_inline simdjson_result<ppc64::ondemand::value> at_path(std::string_view json_path) noexcept;
@@ -156749,6 +153795,7 @@ concept key_selector_type = key_selector_detail::is_key_selector<T>::value;
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
+/* amalgamation skipped (editor-only): #include "simdjson/jsonpathutil.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/key_selector.h" */
/* amalgamation skipped (editor-only): #include <vector> */
/* amalgamation skipped (editor-only): #if SIMDJSON_STATIC_REFLECTION && SIMDJSON_SUPPORTS_CONCEPTS */
@@ -157293,6 +154340,13 @@ protected:
simdjson_warn_unused simdjson_inline error_code find_field_raw(const std::string_view key) noexcept;
+ /**
+ * The recursive step of for_each_at_path_with_wildcard: matches
+ * path[index] at this level and what follows below it.
+ */
+ template <typename Func>
+ inline error_code for_each_at_path_segments(const std::vector<simdjson::internal::json_path_segment> &path, size_t index, Func &callback) noexcept;
+
value_iterator iter{};
#if SIMDJSON_DEVELOPMENT_CHECKS
bool locked{false};
@@ -158157,6 +155211,14 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) {
std::is_default_constructible_v<element_type>,
"The specified type inside the unique_ptr must default constructible.");
+ // A null pointer is serialized as null.
+ bool is_null_value;
+ SIMDJSON_TRY( val.is_null().get(is_null_value) );
+ if (is_null_value) {
+ out.reset();
+ return SUCCESS;
+ }
+
// Own the allocation before get(): a user tag_invoke may throw.
std::unique_ptr<element_type> ptr(new (std::nothrow) element_type());
if (!ptr) {
@@ -158205,6 +155267,8 @@ constexpr bool user_defined_type = (std::is_class_v<T>
#endif // SIMDJSON_SUPPORTS_CHAR8_T
&& !concepts::optional_type<T> &&
!concepts::appendable_containers<T>
+// Smart pointers are handled by the generic smart-pointer tag_invoke.
+&& !concepts::smart_pointer<T>
// simdjson's own types (array, object, value, raw_json_string, number,
// document, document_reference) have dedicated get<T>() specializations and
// must never go through reflection.
@@ -158797,251 +155861,8 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept {
#endif
}
-template <typename simdjson_value, typename T>
- requires(user_defined_type<std::remove_cvref_t<T>>)
-error_code tag_invoke(deserialize_tag, simdjson_value &val, std::unique_ptr<T> &out) noexcept(false) {
- std::unique_ptr<T> ptr(new (std::nothrow) T());
- if (!ptr) {
- return MEMALLOC;
- }
- SIMDJSON_TRY(val.get(*ptr));
- out = std::move(ptr);
- return SUCCESS;
-}
-
-template <typename simdjson_value, typename T>
- requires(user_defined_type<std::remove_cvref_t<T>>)
-error_code tag_invoke(deserialize_tag, simdjson_value &val, std::shared_ptr<T> &out) noexcept(false) {
- std::shared_ptr<T> ptr(new (std::nothrow) T());
- if (!ptr) {
- return MEMALLOC;
- }
- SIMDJSON_TRY(val.get(*ptr));
- out = std::move(ptr);
- return SUCCESS;
-}
-
#endif // SIMDJSON_STATIC_REFLECTION
-////////////////////////////////////////
-// Unique pointers
-////////////////////////////////////////
-error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<bool> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_unique<bool>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_bool().get(*out));
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<int64_t> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_unique<int64_t>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_int64().get(*out));
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<uint64_t> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_unique<uint64_t>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_uint64().get(*out));
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<double> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_unique<double>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_double().get(*out));
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<std::string_view> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_unique<std::string_view>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_string().get(*out));
- return SUCCESS;
-}
-
-
-////////////////////////////////////////
-// Shared pointers
-////////////////////////////////////////
-error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<bool> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_shared<bool>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_bool().get(*out));
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<int64_t> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_shared<int64_t>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_int64().get(*out));
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<uint64_t> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_shared<uint64_t>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_uint64().get(*out));
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<double> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_shared<double>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_double().get(*out));
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<std::string_view> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_shared<std::string_view>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_string().get(*out));
- return SUCCESS;
-}
-
-
-////////////////////////////////////////
-// Explicit optional specializations
-////////////////////////////////////////
-
-////////////////////////////////////////
-// Explicit smart pointer specializations for string and int types
-////////////////////////////////////////
-error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<std::string> &out) noexcept {
- // Check if the value is null
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset(); // Set to nullptr
- return SUCCESS;
- }
-
- if (!out) {
- out = std::make_unique<std::string>();
- }
- std::string_view str;
- SIMDJSON_TRY(val.get_string().get(str));
- *out = std::string{str};
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<std::string> &out) noexcept {
- // Check if the value is null
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset(); // Set to nullptr
- return SUCCESS;
- }
-
- if (!out) {
- out = std::make_shared<std::string>();
- }
- std::string_view str;
- SIMDJSON_TRY(val.get_string().get(str));
- *out = std::string{str};
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<int> &out) noexcept {
- // Check if the value is null
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset(); // Set to nullptr
- return SUCCESS;
- }
-
- if (!out) {
- out = std::make_unique<int>();
- }
- int64_t temp;
- SIMDJSON_TRY(val.get_int64().get(temp));
- *out = static_cast<int>(temp);
- return SUCCESS;
-}
-
} // namespace simdjson
#endif // SIMDJSON_ONDEMAND_DESERIALIZE_H
@@ -159156,10 +155977,9 @@ simdjson_inline simdjson_result<std::string_view> array::raw_json() noexcept {
const uint8_t * starting_point{iter.peek_start()};
auto error = consume();
if(error) { return error; }
- // After 'consume()', we could be left pointing just beyond the document, but that
- // is ok because we are not going to dereference the final pointer position, we just
- // use it to compute the length in bytes.
- const uint8_t * final_point{iter._json_iter->unsafe_pointer()};
+ // After 'consume()', the last token consumed is the closing bracket: we end on it
+ // rather than on the next token, which may follow white space.
+ const uint8_t * final_point{iter._json_iter->token.peek(-1) + 1};
return std::string_view(reinterpret_cast<const char*>(starting_point), size_t(final_point - starting_point));
}
@@ -159243,12 +156063,23 @@ template <typename Func>
template <typename Func>
#endif
inline error_code array::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
- auto result_pair = get_next_key_and_json_path(json_path);
- std::string_view key = result_pair.first;
- std::string_view remaining_path = result_pair.second;
+ // The path is split once: every element reached by a wildcard sees the same
+ // remaining path, so re-parsing it at each one would be quadratic.
+ const std::vector<internal::json_path_segment> path = internal::split_json_path(json_path);
+ // A path that cannot be parsed matches nothing, whatever the document: say
+ // so rather than letting the walk skip it as a mismatch below a wildcard.
+ if (path.back().key.empty()) {
+ return INVALID_JSON_POINTER;
+ }
+ return for_each_at_path_segments(path, 0, callback);
+}
+
+template <typename Func>
+inline error_code array::for_each_at_path_segments(const std::vector<internal::json_path_segment> &path, size_t index, Func &callback) noexcept {
+ std::string_view key = path[index].key;
+ const bool last = index + 1 == path.size();
// Empty key means the path segment could not be parsed (including malformed
- // bracket-quoted keys). Without this check, an empty key is treated as index
- // 0 and remaining_path may be unchanged, causing infinite recursion.
+ // bracket-quoted keys).
if (key.empty()) {
return INVALID_JSON_POINTER;
}
@@ -159257,11 +156088,11 @@ inline error_code array::for_each_at_path_with_wildcard(std::string_view json_pa
for(auto element: *this) {
value val;
SIMDJSON_TRY(element.get(val));
- if (remaining_path.empty()) {
+ if (last) {
callback(val);
} else {
// An element that does not match the rest of the path is skipped.
- error_code err = val.for_each_at_path_with_wildcard(remaining_path, callback);
+ error_code err = val.for_each_at_path_segments(path, index + 1, callback);
if (err && !internal::is_matching_failure_error(err)) { return err; }
}
}
@@ -159280,16 +156111,16 @@ inline error_code array::for_each_at_path_with_wildcard(std::string_view json_pa
value val;
if (!error) { error = at(idx).get(val); }
if (error) {
- if (error == INDEX_OUT_OF_BOUNDS && internal::json_path_has_wildcard(remaining_path)) {
+ if (error == INDEX_OUT_OF_BOUNDS && path[index].wildcard_follows) {
return SUCCESS;
}
return error;
}
- if (remaining_path.empty()){
+ if (last){
callback(val);
return SUCCESS;
} else {
- return val.for_each_at_path_with_wildcard(remaining_path, callback);
+ return val.for_each_at_path_segments(path, index + 1, callback);
}
}
}
@@ -159947,6 +156778,19 @@ template <typename Func>
template <typename Func>
#endif
inline error_code value::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
+ // The path is split once: every value reached by a wildcard sees the same
+ // remaining path, so re-parsing it at each one would be quadratic.
+ const std::vector<internal::json_path_segment> path = internal::split_json_path(json_path);
+ // A path that cannot be parsed matches nothing, whatever the document: say
+ // so rather than letting the walk skip it as a mismatch below a wildcard.
+ if (path.back().key.empty()) {
+ return INVALID_JSON_POINTER;
+ }
+ return for_each_at_path_segments(path, 0, callback);
+}
+
+template <typename Func>
+inline error_code value::for_each_at_path_segments(const std::vector<internal::json_path_segment> &path, size_t index, Func &callback) noexcept {
// Every recursive step of for_each_at_path_with_wildcard goes through this
// function, and each one descends one level into the document. A path with
// many segments applied to a deeply nested document would otherwise recurse
@@ -159955,10 +156799,16 @@ inline error_code value::for_each_at_path_with_wildcard(std::string_view json_pa
json_type t;
SIMDJSON_TRY(type().get(t));
switch (t) {
- case json_type::array:
- return (*this).get_array().for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
- case json_type::object:
- return (*this).get_object().for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
+ case json_type::array: {
+ array a;
+ SIMDJSON_TRY(get_array().get(a));
+ return a.for_each_at_path_segments(path, index, callback);
+ }
+ case json_type::object: {
+ object o;
+ SIMDJSON_TRY(get_object().get(o));
+ return o.for_each_at_path_segments(path, index, callback);
+ }
default:
return INVALID_JSON_POINTER;
}
@@ -160331,7 +157181,8 @@ simdjson_inline document document::start(json_iterator &&iter) noexcept {
}
inline void document::rewind() noexcept {
- iter.rewind();
+ // A document that is no longer alive is left as it is (see is_alive()).
+ static_cast<void>(iter.rewind());
}
inline std::string document::to_debug_string() noexcept {
@@ -160631,7 +157482,13 @@ simdjson_warn_unused simdjson_inline error_code document::consume() noexcept {
auto error = is_scalar().get(scalar);
if(error) { return error; }
if(scalar) {
- iter.return_current_and_advance();
+ // Step over the scalar and leave depth 0, as skip_child() does: otherwise a
+ // document_stream takes the next document as the rest of this one. At depth
+ // 0 the scalar has already been consumed (e.g., by get_int64()).
+ if(iter.depth() > 0) {
+ iter.return_current_and_advance();
+ iter.ascend_to(0);
+ }
return SUCCESS;
}
error = iter.skip_child(0);
@@ -160644,10 +157501,36 @@ simdjson_inline simdjson_result<std::string_view> document::raw_json() noexcept
const uint8_t * starting_point{_iter.peek_start()};
auto error = consume();
if(error) { return error; }
- // After 'consume()', we could be left pointing just beyond the document, but that
- // is ok because we are not going to dereference the final pointer position, we just
- // use it to compute the length in bytes.
- const uint8_t * final_point{iter.unsafe_pointer()};
+ // After 'consume()', the iterator points at the token after the document. In a
+ // document_stream, that is the next document, past white space and separators, or
+ // the end of the input when a truncated document follows. We end on the last byte
+ // of this document instead.
+ const uint8_t * final_point;
+ if(*starting_point == '{' || *starting_point == '[') {
+ // The last token consumed is the closing brace or bracket.
+ final_point = iter.token.peek(-1) + 1;
+ } else {
+ // A scalar is a single token: a string ends at its closing quote, anything else
+ // at the first white space, structural character, quote, RS (0x1E) or NUL.
+ const uint8_t * next_token{iter.unsafe_pointer()};
+ final_point = starting_point + 1;
+ if(*starting_point == '"') {
+ while(final_point < next_token) {
+ uint8_t c = *final_point++;
+ if(c == '\\') {
+ final_point++;
+ } else if(c == '"') {
+ break;
+ }
+ }
+ if(final_point > next_token) { final_point = next_token; }
+ } else {
+ while(final_point < next_token && !jsoncharutils::is_structural_or_whitespace(*final_point)
+ && *final_point != '"' && *final_point != 0x1E && *final_point != 0) {
+ final_point++;
+ }
+ }
+ }
return std::string_view(reinterpret_cast<const char*>(starting_point), size_t(final_point - starting_point));
}
@@ -160694,7 +157577,9 @@ simdjson_inline simdjson_result<std::string_view> document::raw_json_token() noe
}
simdjson_inline simdjson_result<value> document::at_pointer(std::string_view json_pointer) noexcept {
- rewind(); // Rewind the document each time at_pointer is called
+ // Rewind the document each time at_pointer is called. After a fatal error,
+ // the document cannot be walked again: we report the error instead.
+ SIMDJSON_TRY(iter.rewind());
if (json_pointer.empty()) {
return this->get_value();
}
@@ -160712,7 +157597,9 @@ simdjson_inline simdjson_result<value> document::at_pointer(std::string_view jso
}
simdjson_inline simdjson_result<value> document::at_path(std::string_view json_path) noexcept {
- rewind(); // Rewind the document each time at_pointer is called
+ // Rewind the document each time at_path is called. After a fatal error,
+ // the document cannot be walked again: we report the error instead.
+ SIMDJSON_TRY(iter.rewind());
if (json_path.empty()) {
return this->get_value();
}
@@ -160735,7 +157622,9 @@ template <typename Func>
template <typename Func>
#endif
simdjson_inline error_code document::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
- rewind(); // Rewind the document each time for_each_at_path_with_wildcard is called
+ // Rewind the document each time for_each_at_path_with_wildcard is called. After
+ // a fatal error, the document cannot be walked again: we report the error instead.
+ SIMDJSON_TRY(iter.rewind());
if (json_path.empty()) {
return INVALID_JSON_POINTER;
}
@@ -161073,11 +157962,6 @@ simdjson_inline simdjson_result<ppc64::ondemand::number> simdjson_result<ppc64::
#if SIMDJSON_EXCEPTIONS
-template <class T, typename std::enable_if<std::is_same<T, ppc64::ondemand::document>::value == false>::type>
-simdjson_inline simdjson_result<ppc64::ondemand::document>::operator T() noexcept(false) {
- if (error()) { throw simdjson_error(error()); }
- return first;
-}
simdjson_inline simdjson_result<ppc64::ondemand::document>::operator ppc64::ondemand::array() & noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
@@ -161138,6 +158022,11 @@ simdjson_inline simdjson_result<std::string_view> simdjson_result<ppc64::ondeman
return first.raw_json_token();
}
+simdjson_inline simdjson_result<std::string_view> simdjson_result<ppc64::ondemand::document>::raw_json() noexcept {
+ if (error()) { return error(); }
+ return first.raw_json();
+}
+
simdjson_inline simdjson_result<ppc64::ondemand::value> simdjson_result<ppc64::ondemand::document>::at_pointer(std::string_view json_pointer) noexcept {
if (error()) { return error(); }
return first.at_pointer(json_pointer);
@@ -161624,6 +158513,11 @@ simdjson_inline simdjson_result<std::string_view> simdjson_result<ppc64::ondeman
return first.raw_json_token();
}
+simdjson_inline simdjson_result<std::string_view> simdjson_result<ppc64::ondemand::document_reference>::raw_json() noexcept {
+ if (error()) { return error(); }
+ return first.raw_json();
+}
+
simdjson_inline simdjson_result<ppc64::ondemand::value> simdjson_result<ppc64::ondemand::document_reference>::at_pointer(std::string_view json_pointer) noexcept {
if (error()) { return error(); }
return first.at_pointer(json_pointer);
@@ -162015,6 +158909,13 @@ simdjson_inline bool document_stream::skip_to_delimiter(uint8_t delimiter) noexc
}
inline void document_stream::next_document() noexcept {
+ // Iteration stops at the first document that met a fatal error. The error
+ // may have abandoned the document's iterator, which then no longer has a
+ // parser to walk the rest of the document with.
+ if (!doc.iter.is_alive() || doc.iter.error) {
+ error = doc.iter.error ? doc.iter.error : TAPE_ERROR;
+ return;
+ }
// A delimiter that cannot occur inside a document tells us where the current
// one ends, so we can jump there instead of walking every structural. Only
// valid while the iterator is still inside the document: a consumed document
@@ -162164,7 +159065,10 @@ simdjson_inline std::string_view document_stream::iterator::source() const noexc
cur_struct_index++;
}
- while (cur_struct_index <= static_cast<int64_t>(stream->parser->implementation->n_structural_indexes)) {
+ // The structural index at n_structural_indexes is the end of the batch, not
+ // a token: it must not be read as a closing bracket.
+ const int64_t n_structural_indexes = static_cast<int64_t>(stream->parser->implementation->n_structural_indexes);
+ while (cur_struct_index < n_structural_indexes) {
switch (stream->buf[stream->batch_start + stream->parser->implementation->structural_indexes[cur_struct_index]]) {
case '{': case '[':
depth++;
@@ -162176,6 +159080,11 @@ simdjson_inline std::string_view document_stream::iterator::source() const noexc
if (depth == 0) { break; }
cur_struct_index++;
}
+ // An invalid last document such as "[1 2" is never closed: it runs to the
+ // end of the batch, and not one byte past it.
+ if (cur_struct_index >= n_structural_indexes) {
+ return std::string_view(reinterpret_cast<const char*>(stream->buf) + current_index(), stream->batch_start + stream->parser->implementation->structural_indexes[cur_struct_index] - current_index());
+ }
return std::string_view(reinterpret_cast<const char*>(stream->buf) + current_index(), stream->parser->implementation->structural_indexes[cur_struct_index] - current_index() + stream->batch_start + 1);;
}
@@ -162192,7 +159101,12 @@ inline void document_stream::load_from_stage1_thread() noexcept {
// enough memory to swap to, as well.
std::swap(stage1_thread_parser,*parser);
error = stage1_thread_error;
- if (error) { return; }
+ // An EMPTY batch (no complete document in it, e.g. a run of white space at
+ // least batch_size long) is not the end of the stream: next() moves on to
+ // the batch after it, so queue that batch now as we would after a success.
+ // Returning here would leave no job for the next call, which then swaps
+ // the stale previous batch back in and ends the stream early.
+ if (error && error != EMPTY) { return; }
// If there's anything left, start the stage 1 thread!
if (next_batch_start() < len) {
@@ -162304,12 +159218,14 @@ simdjson_inline raw_json_string field::key() const noexcept {
simdjson_inline std::string_view field::key_raw_json_token() const noexcept {
SIMDJSON_ASSUME(first.buf != nullptr); // We would like to call .alive() by Visual Studio won't let us.
- return std::string_view(reinterpret_cast<const char*>(first.buf-1), second.iter._json_iter->token.peek(-1) - first.buf + 1);
+ // The ':' is the token just before the value. Use the value's start position
+ // rather than the current position, which moves once the value is consumed.
+ return std::string_view(reinterpret_cast<const char*>(first.buf-1), second.iter._json_iter->token.peek(second.iter.start_position() - 1) - first.buf + 1);
}
simdjson_inline std::string_view field::escaped_key() const noexcept {
SIMDJSON_ASSUME(first.buf != nullptr); // We would like to call .alive() by Visual Studio won't let us.
- auto end_quote = second.iter._json_iter->token.peek(-1);
+ auto end_quote = second.iter._json_iter->token.peek(second.iter.start_position() - 1);
while(*end_quote != '"') end_quote--;
return std::string_view(reinterpret_cast<const char*>(first.buf), end_quote - first.buf);
}
@@ -162480,11 +159396,14 @@ simdjson_inline json_iterator::json_iterator(const uint8_t *buf, ondemand::parse
}
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
-inline void json_iterator::rewind() noexcept {
+simdjson_inline error_code json_iterator::rewind() noexcept {
+ // abandon() let go of the parser: the iterator cannot be walked again.
+ if (!is_alive()) { return error ? error : UNINITIALIZED; }
token.set_position( root_position() );
logger::log_headers(); // We start again
_string_buf_loc = parser->string_buf.get();
_depth = 1;
+ return SUCCESS;
}
inline bool json_iterator::balanced() const noexcept {
@@ -162508,6 +159427,18 @@ inline bool json_iterator::balanced() const noexcept {
}
+// Depth change of each structural character when skipping: +1 for [ and {, -1 for ] and }.
+static constexpr int8_t skip_depth_delta[256] = {
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,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,
+};
+
// GCC 7 warns when the first line of this function is inlined away into oblivion due to the caller
// relating depth and parent_depth, which is a desired effect. The warning does not show up if the
// skip_child() function is not marked inline).
@@ -162563,7 +159494,30 @@ simdjson_warn_unused simdjson_inline error_code json_iterator::skip_child(depth_
break;
}
- // Now that we've considered the first value, we only increment/decrement for arrays/objects
+ // Now that we've considered the first value, we only increment/decrement for arrays/objects.
+ //
+ // Without logging, look up the depth change of each token in a table instead of branching
+ // on it: the next byte is hard to predict, so a switch mispredicts often. The loop also keeps
+ // the position and depth in locals, because the byte loads are char loads that may alias
+ // token._position and _depth, which would force a store of both on every token.
+ if (!logger::LOG_ENABLED) {
+ token_position pos = token.position();
+ const token_position end = end_position();
+ const uint8_t *const json = token.buf;
+ depth_t depth = _depth;
+ while (pos < end) {
+ depth += skip_depth_delta[json[*pos++]];
+ if (depth <= parent_depth) {
+ token.set_position(pos);
+ _depth = depth;
+ return SUCCESS;
+ }
+ }
+ token.set_position(pos);
+ _depth = depth;
+ return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "not enough close braces");
+ }
+
while (position() < end_position()) {
switch (*return_current_and_advance()) {
case '[': case '{':
@@ -162848,6 +159802,17 @@ simdjson_warn_unused simdjson_inline error_code json_iterator::optional_error(er
simdjson_warn_unused simdjson_inline bool json_iterator::copy_to_buffer(const uint8_t *json, uint32_t max_len, uint8_t *tmpbuf, size_t N) noexcept {
// This function is not expected to be called in performance-sensitive settings.
+ // peek_root_length() stops at the next structural index. Stage 1 omits the
+ // comma or RS (0x1E) that separates stream documents, so that byte and the
+ // whitespace around it remain in the span. RS is not a valid number finisher.
+ while (max_len > 0) {
+ const uint8_t last = json[max_len - 1];
+ if (last != ' ' && last != '\t' && last != '\n' && last != '\r' &&
+ last != ',' && last != 0x1E) {
+ break;
+ }
+ max_len--;
+ }
// Let us guard against silly cases:
if((N < max_len) || (N == 0)) { return false; }
// Copy to the buffer.
@@ -163489,7 +160454,9 @@ simdjson_inline simdjson_result<std::string_view> object::raw_json() noexcept {
const uint8_t * starting_point{iter.peek_start()};
auto error = consume();
if(error) { return error; }
- const uint8_t * final_point{iter._json_iter->peek()};
+ // After 'consume()', the last token consumed is the closing brace: we end on it
+ // rather than on the next token, which may follow white space.
+ const uint8_t * final_point{iter._json_iter->token.peek(-1) + 1};
return std::string_view(reinterpret_cast<const char*>(starting_point), size_t(final_point - starting_point));
}
@@ -163582,9 +160549,21 @@ template <typename Func>
template <typename Func>
#endif
inline error_code object::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
- auto result_pair = get_next_key_and_json_path(json_path);
- std::string_view key = result_pair.first;
- std::string_view remaining_path = result_pair.second;
+ // The path is split once: every field reached by a wildcard sees the same
+ // remaining path, so re-parsing it at each one would be quadratic.
+ const std::vector<internal::json_path_segment> path = internal::split_json_path(json_path);
+ // A path that cannot be parsed matches nothing, whatever the document: say
+ // so rather than letting the walk skip it as a mismatch below a wildcard.
+ if (path.back().key.empty()) {
+ return INVALID_JSON_POINTER;
+ }
+ return for_each_at_path_segments(path, 0, callback);
+}
+
+template <typename Func>
+inline error_code object::for_each_at_path_segments(const std::vector<internal::json_path_segment> &path, size_t index, Func &callback) noexcept {
+ std::string_view key = path[index].key;
+ const bool last = index + 1 == path.size();
// Empty key means the path segment could not be parsed (including malformed
// bracket-quoted keys). Match DOM at_path_with_wildcard and avoid treating
// "no progress" as a recursive lookup of "".
@@ -163597,11 +160576,11 @@ inline error_code object::for_each_at_path_with_wildcard(std::string_view json_p
for (auto field : *this) {
value val;
SIMDJSON_TRY(field.value().get(val));
- if (remaining_path.empty()) {
+ if (last) {
callback(val);
} else {
// A field that does not match the rest of the path is skipped.
- error_code err = val.for_each_at_path_with_wildcard(remaining_path, callback);
+ error_code err = val.for_each_at_path_segments(path, index + 1, callback);
if (err && !internal::is_matching_failure_error(err)) { return err; }
}
}
@@ -163610,17 +160589,17 @@ inline error_code object::for_each_at_path_with_wildcard(std::string_view json_p
value val;
error_code error = find_field(key).get(val);
if (error) {
- if (error == NO_SUCH_FIELD && internal::json_path_has_wildcard(remaining_path)) {
+ if (error == NO_SUCH_FIELD && path[index].wildcard_follows) {
return SUCCESS;
}
return error;
}
- if (remaining_path.empty()) {
+ if (last) {
callback(val);
return SUCCESS;
} else {
- return val.for_each_at_path_with_wildcard(remaining_path, callback);
+ return val.for_each_at_path_segments(path, index + 1, callback);
}
}
}
@@ -164273,6 +161252,7 @@ simdjson_inline simdjson_warn_unused bool parser::string_buffer_overflow(const u
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(padded_string_view json) & noexcept {
if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
+ json.use_trailing_whitespace_as_padding();
json.remove_utf8_bom();
_document_len = json.length();
@@ -164290,6 +161270,7 @@ simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(p
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate_allow_incomplete_json(padded_string_view json) & noexcept {
if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
+ json.use_trailing_whitespace_as_padding();
json.remove_utf8_bom();
_document_len = json.length();
@@ -164345,6 +161326,7 @@ simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(c
simdjson_warn_unused simdjson_inline simdjson_result<json_iterator> parser::iterate_raw(padded_string_view json) & noexcept {
if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
+ json.use_trailing_whitespace_as_padding();
json.remove_utf8_bom();
@@ -164371,6 +161353,7 @@ inline simdjson_result<document_stream> parser::iterate_many(const char *buf, si
}
inline simdjson_result<document_stream> parser::iterate_many(padded_string_view s, size_t batch_size) noexcept {
if (!s.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
+ s.use_trailing_whitespace_as_padding();
return iterate_many(s.data(), s.length(), batch_size);
}
inline simdjson_result<document_stream> parser::iterate_many(const padded_string &s, size_t batch_size) noexcept {
@@ -164406,6 +161389,7 @@ inline simdjson_result<document_stream> parser::iterate_many(const char *buf, si
}
inline simdjson_result<document_stream> parser::iterate_many(padded_string_view s, size_t batch_size, bool allow_comma_separated) noexcept {
if (!s.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
+ s.use_trailing_whitespace_as_padding();
return iterate_many(s.data(), s.length(), batch_size, allow_comma_separated);
}
inline simdjson_result<document_stream> parser::iterate_many(const padded_string &s, size_t batch_size, bool allow_comma_separated) noexcept {
@@ -164451,6 +161435,7 @@ inline simdjson_result<document_stream> parser::iterate_many(const char *buf, si
}
inline simdjson_result<document_stream> parser::iterate_many(padded_string_view s, size_t batch_size, stream_format format) noexcept {
if (!s.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
+ s.use_trailing_whitespace_as_padding();
return iterate_many(s.data(), s.length(), batch_size, format);
}
inline simdjson_result<document_stream> parser::iterate_many(const std::string &s, size_t batch_size, stream_format format) noexcept {
@@ -164605,17 +161590,18 @@ simdjson_inline bool raw_json_string::is_free_from_unescaped_quote(const char* t
return true;
}
-
simdjson_inline bool raw_json_string::unsafe_is_equal(size_t length, std::string_view target) const noexcept {
// If we are going to call memcmp, then we must know something about the length of the raw_json_string.
- return (length >= target.size()) && (raw()[target.size()] == '"') && !memcmp(raw(), target.data(), target.size());
+ return (length >= target.size()) && (raw()[target.size()] == '"') && !memcmp(raw(), target.data(), target.size())
+ && !escapes_trailing_quote(target);
}
simdjson_inline bool raw_json_string::unsafe_is_equal(std::string_view target) const noexcept {
// Assumptions: does not contain unescaped quote characters("), and
// the raw content is quote terminated within a valid JSON string.
if(target.size() <= SIMDJSON_PADDING) {
- return (raw()[target.size()] == '"') && !memcmp(raw(), target.data(), target.size());
+ return (raw()[target.size()] == '"') && !memcmp(raw(), target.data(), target.size())
+ && !escapes_trailing_quote(target);
}
// Past SIMDJSON_PADDING we can no longer rely on the padding to keep the
// comparison in bounds, so we must stop at the quote terminating the key.
@@ -164641,7 +161627,7 @@ simdjson_inline bool raw_json_string::is_equal(std::string_view target) const no
escaping = false;
}
}
- if(r[pos] != '"') { return false; }
+ if((r[pos] != '"') || escaping) { return false; }
return true;
}
@@ -164654,7 +161640,7 @@ simdjson_inline bool raw_json_string::unsafe_is_equal(const char * target) const
for(;target[pos];pos++) {
if(r[pos] != target[pos]) { return false; }
}
- if(r[pos] != '"') { return false; }
+ if((r[pos] != '"') || escapes_trailing_quote(std::string_view(target, pos))) { return false; }
return true;
}
@@ -164679,7 +161665,7 @@ simdjson_inline bool raw_json_string::is_equal(const char* target) const noexcep
escaping = false;
}
}
- if(r[pos] != '"') { return false; }
+ if((r[pos] != '"') || escaping) { return false; }
return true;
}
@@ -164791,7 +161777,11 @@ simdjson_inline uint32_t token_iterator::peek_index(token_position position) con
return *position;
}
simdjson_inline uint32_t token_iterator::peek_length(token_position position) const noexcept {
- return *(position+1) - *position;
+ // In the last batch of a document_stream, the index past the end of the
+ // structural indexes holds the start of the truncated bytes (see
+ // document_stream::truncated_bytes()), which can come before the end of the
+ // input: a token at the end is then empty.
+ return *(position+1) > *position ? *(position+1) - *position : 0;
}
simdjson_inline uint32_t token_iterator::peek_root_length(token_position position) const noexcept {
@@ -164806,7 +161796,7 @@ simdjson_inline uint32_t token_iterator::peek_index(int32_t delta) const noexcep
return *(_position+delta);
}
simdjson_inline uint32_t token_iterator::peek_length(int32_t delta) const noexcept {
- return *(_position+delta+1) - *(_position+delta);
+ return peek_length(_position+delta);
}
simdjson_inline token_position token_iterator::position() const noexcept {
@@ -164865,6 +161855,8 @@ simdjson_inline simdjson_result<ppc64::ondemand::token_iterator>::simdjson_resul
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
+#include <cstring> // std::memset (zeroing the root-number scratch padding)
+
namespace simdjson {
namespace ppc64 {
namespace ondemand {
@@ -164911,6 +161903,10 @@ simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::start
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::started_object() noexcept {
assert_at_container_start();
#if SIMDJSON_DEVELOPMENT_CHECKS
+ // The development checks record the start of every open container in a
+ // buffer of max_depth() entries: a document nested deeper than that is
+ // rejected, as the DOM parser does.
+ if (size_t(_depth) >= _json_iter->parser->max_depth()) { return report_error(DEPTH_ERROR, "Exceeded max depth!"); }
_json_iter->set_start_position(_depth, start_position());
#endif
if (*_json_iter->peek() == '}') {
@@ -165320,6 +162316,10 @@ inline std::string value_iterator::to_string() const noexcept {
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::started_array() noexcept {
assert_at_container_start();
+#if SIMDJSON_DEVELOPMENT_CHECKS
+ // See started_object().
+ if (size_t(_depth) >= _json_iter->parser->max_depth()) { return report_error(DEPTH_ERROR, "Exceeded max depth!"); }
+#endif
if (*_json_iter->peek() == ']') {
logger::log_value(*_json_iter, "empty array");
_json_iter->return_current_and_advance();
@@ -165525,8 +162525,10 @@ simdjson_inline simdjson_result<ppc64::number_type> value_iterator::get_root_num
// Per https://www.exploringbinary.com/maximum-number-of-decimal-digits-in-binary-floating-point-numbers/,
// 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest
// number: -0.<fraction>e-308.
- uint8_t tmpbuf[1074+8+1+1];
- tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
+ // +SIMDJSON_PADDING: the digit scan reads up to 7 bytes past the terminator.
+ uint8_t tmpbuf[1074+8+1+1+SIMDJSON_PADDING];
+ // Null terminate, and zero the padding as allocate_padded_buffer does.
+ std::memset(tmpbuf+1074+8+1, 0, 1+SIMDJSON_PADDING);
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
if(numberparsing::check_if_integer(json, max_len)) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
@@ -165547,8 +162549,10 @@ simdjson_inline simdjson_result<number> value_iterator::get_root_number(bool che
// 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest
// number: -0.<fraction>e-308.
// NOTE: the current approach doesn't work for very big integer numbers containing more than 1074 digits.
- uint8_t tmpbuf[1074+8+1+1];
- tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
+ // +SIMDJSON_PADDING: the digit scan reads up to 7 bytes past the terminator.
+ uint8_t tmpbuf[1074+8+1+1+SIMDJSON_PADDING];
+ // Null terminate, and zero the padding as allocate_padded_buffer does.
+ std::memset(tmpbuf+1074+8+1, 0, 1+SIMDJSON_PADDING);
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
if(numberparsing::check_if_integer(json, max_len)) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
@@ -165618,10 +162622,10 @@ simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> value_iterator::g
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> value_iterator::get_root_uint64_in_string(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("uint64");
- uint8_t tmpbuf[20+1+1]{}; // <20 digits> is the longest possible unsigned integer
- tmpbuf[20+1] = '\0'; // make sure that buffer is always null terminated.
- if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 20+1)) {
- logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 20 characters");
+ uint8_t tmpbuf[20+2+1]{}; // "<20 digits>" is the longest possible unsigned integer
+ tmpbuf[20+2] = '\0'; // make sure that buffer is always null terminated.
+ if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 20+2)) {
+ logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 22 characters");
return NUMBER_ERROR;
}
auto result = numberparsing::parse_unsigned_in_string(tmpbuf);
@@ -165651,10 +162655,10 @@ simdjson_warn_unused simdjson_inline simdjson_result<int64_t> value_iterator::ge
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> value_iterator::get_root_int64_in_string(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("int64");
- uint8_t tmpbuf[20+1+1]; // -<19 digits> is the longest possible integer
- tmpbuf[20+1] = '\0'; // make sure that buffer is always null terminated.
- if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 20+1)) {
- logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 20 characters");
+ uint8_t tmpbuf[20+2+1]; // "-<19 digits>" is the longest possible integer
+ tmpbuf[20+2] = '\0'; // make sure that buffer is always null terminated.
+ if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 20+2)) {
+ logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 22 characters");
return NUMBER_ERROR;
}
@@ -165671,8 +162675,10 @@ simdjson_warn_unused simdjson_inline simdjson_result<double> value_iterator::get
// Per https://www.exploringbinary.com/maximum-number-of-decimal-digits-in-binary-floating-point-numbers/,
// 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest
// number: -0.<fraction>e-308.
- uint8_t tmpbuf[1074+8+1+1]; // +1 for null termination.
- tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
+ // +SIMDJSON_PADDING: the digit scan reads up to 7 bytes past the terminator.
+ uint8_t tmpbuf[1074+8+1+1+SIMDJSON_PADDING]; // +1 for null termination.
+ // Null terminate, and zero the padding as allocate_padded_buffer does.
+ std::memset(tmpbuf+1074+8+1, 0, 1+SIMDJSON_PADDING);
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters");
return NUMBER_ERROR;
@@ -165691,8 +162697,10 @@ simdjson_warn_unused simdjson_inline simdjson_result<double> value_iterator::get
// Per https://www.exploringbinary.com/maximum-number-of-decimal-digits-in-binary-floating-point-numbers/,
// 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest
// number: -0.<fraction>e-308.
- uint8_t tmpbuf[1074+8+1+1]; // +1 for null termination.
- tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
+ // +SIMDJSON_PADDING: the digit scan reads up to 7 bytes past the terminator.
+ uint8_t tmpbuf[1074+8+1+1+SIMDJSON_PADDING]; // +1 for null termination.
+ // Null terminate, and zero the padding as allocate_padded_buffer does.
+ std::memset(tmpbuf+1074+8+1, 0, 1+SIMDJSON_PADDING);
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters");
return NUMBER_ERROR;
@@ -165711,8 +162719,10 @@ simdjson_warn_unused simdjson_inline simdjson_result<float> value_iterator::get_
// We use the same buffer size as get_root_double: the number of significant
// digits that matter is smaller for binary32, but the JSON document may still
// spell out a long number that we must parse (and round) faithfully.
- uint8_t tmpbuf[1074+8+1+1]; // +1 for null termination.
- tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
+ // +SIMDJSON_PADDING: the digit scan reads up to 7 bytes past the terminator.
+ uint8_t tmpbuf[1074+8+1+1+SIMDJSON_PADDING]; // +1 for null termination.
+ // Null terminate, and zero the padding as allocate_padded_buffer does.
+ std::memset(tmpbuf+1074+8+1, 0, 1+SIMDJSON_PADDING);
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters");
return NUMBER_ERROR;
@@ -165728,8 +162738,10 @@ simdjson_warn_unused simdjson_inline simdjson_result<float> value_iterator::get_
simdjson_warn_unused simdjson_inline simdjson_result<float> value_iterator::get_root_float_in_string(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("float");
- uint8_t tmpbuf[1074+8+1+1]; // +1 for null termination.
- tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
+ // +SIMDJSON_PADDING: the digit scan reads up to 7 bytes past the terminator.
+ uint8_t tmpbuf[1074+8+1+1+SIMDJSON_PADDING]; // +1 for null termination.
+ // Null terminate, and zero the padding as allocate_padded_buffer does.
+ std::memset(tmpbuf+1074+8+1, 0, 1+SIMDJSON_PADDING);
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters");
return NUMBER_ERROR;
@@ -165750,7 +162762,7 @@ simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::get_r
// "true" and "false" are 4 and 5 characters long, respectively.
bool value_true = (max_len >= 4 && !atomparsing::str4ncmp(json, "true") &&
(max_len == 4 || jsoncharutils::is_structural_or_whitespace(json[4])));
- bool value_false = (max_len >= 5 && !atomparsing::str4ncmp(json, "false") &&
+ bool value_false = (max_len >= 5 && !atomparsing::str4ncmp(json, "fals") && json[4] == 'e' &&
(max_len == 5 || jsoncharutils::is_structural_or_whitespace(json[5])));
if(value_true == false && value_false == false) { return incorrect_type_error("Not a boolean"); }
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
@@ -166390,49 +163402,6 @@ namespace json_path {
using ::simdjson::ppc64::ondemand::value;
-// Path step types
-enum class step_type {
- field, // .field_name or ["field_name"]
- array_index // [index]
-};
-
-// Represents a single step in a JSON path
-template<std::size_t N>
-struct path_step {
- step_type type;
- char key[N]; // Field name (empty for array indices)
- std::size_t index; // Array index (0 for field access)
-
- constexpr path_step(step_type t, const char (&k)[N], std::size_t idx = 0)
- : type(t), index(idx) {
- for (std::size_t i = 0; i < N; ++i) {
- key[i] = k[i];
- }
- }
-
- constexpr std::string_view key_view() const {
- return {key, N - 1};
- }
-};
-
-// Helper to create field step
-template<std::size_t N>
-consteval auto make_field_step(const char (&name)[N]) {
- return path_step<N>(step_type::field, name, 0);
-}
-
-// Helper to create array index step
-consteval auto make_index_step(std::size_t idx) {
- return path_step<1>(step_type::array_index, "", idx);
-}
-
-// Parse state for compile-time JSON path parsing
-struct parse_result {
- bool success;
- std::size_t pos;
- std::string_view error_msg;
-};
-
// Compile-time JSON path parser
// Supports subset: .field, ["field"], [index], nested combinations
template<constevalutil::fixed_string Path>
@@ -166446,77 +163415,6 @@ struct json_path_parser {
}
return 0;
}
-
- // Count the number of steps in the path at compile time
- static consteval std::size_t count_steps() {
- std::size_t count = 0;
- std::size_t i = skip_root();
-
- while (i < path_str.size()) {
- if (path_str[i] == '.') {
- // Field access: .field
- ++i;
- if (i >= path_str.size()) break;
-
- // Skip field name
- while (i < path_str.size() && path_str[i] != '.' && path_str[i] != '[') {
- ++i;
- }
- ++count;
- } else if (path_str[i] == '[') {
- // Array or bracket notation
- ++i;
- if (i >= path_str.size()) break;
-
- if (path_str[i] == '"' || path_str[i] == '\'') {
- // Field access: ["field"] or ['field']
- char quote = path_str[i];
- ++i;
- while (i < path_str.size() && path_str[i] != quote) {
- ++i;
- }
- if (i < path_str.size()) ++i; // skip closing quote
- if (i < path_str.size() && path_str[i] == ']') ++i;
- } else {
- // Array index: [0], [123]
- while (i < path_str.size() && path_str[i] != ']') {
- ++i;
- }
- if (i < path_str.size()) ++i; // skip ]
- }
- ++count;
- } else {
- ++i;
- }
- }
-
- return count;
- }
-
- // Parse a field name at compile time
- static consteval std::size_t parse_field_name(std::size_t start, char* out, std::size_t max_len) {
- std::size_t len = 0;
- std::size_t i = start;
-
- while (i < path_str.size() && path_str[i] != '.' && path_str[i] != '[' && len < max_len - 1) {
- out[len++] = path_str[i++];
- }
- out[len] = '\0';
- return i;
- }
-
- // Parse an array index at compile time
- static consteval std::pair<std::size_t, std::size_t> parse_array_index(std::size_t start) {
- std::size_t index = 0;
- std::size_t i = start;
-
- while (i < path_str.size() && path_str[i] >= '0' && path_str[i] <= '9') {
- index = index * 10 + (path_str[i] - '0');
- ++i;
- }
-
- return {i, index};
- }
};
// Compile-time path accessor generator
@@ -166525,7 +163423,6 @@ struct path_accessor {
using value = ::simdjson::ppc64::ondemand::value;
static constexpr auto parser = json_path_parser<Path>();
- static constexpr std::size_t num_steps = parser.count_steps();
static constexpr std::string_view path_view = Path.view();
// Compile-time accessor generation
@@ -166640,93 +163537,6 @@ private:
return current_type;
}
-private:
- // Walk path and extract directly into final field using compile-time reflection
- template<std::meta::info CurrentType, std::size_t PathPos, typename TargetType>
- static inline error_code extract_with_reflection(simdjson_result<value> current, TargetType& target_ref) noexcept {
- if (current.error()) return current.error();
-
- // Base case: end of path - extract into target
- if constexpr (PathPos >= path_view.size()) {
- return current.get(target_ref);
- }
- // Field access: .field_name
- else if constexpr (path_view[PathPos] == '.') {
- constexpr auto field_info = parse_next_field(PathPos);
- constexpr std::string_view field_name = std::get<0>(field_info);
- constexpr std::size_t next_pos = std::get<1>(field_info);
-
- constexpr auto member_info = find_member_by_name(CurrentType, field_name);
- static_assert(member_info != ^^void, "Field not found in struct");
-
- constexpr auto member_type = std::meta::type_of(member_info);
-
- auto obj_result = current.get_object();
- if (obj_result.error()) return obj_result.error();
- auto obj = obj_result.value_unsafe();
- auto field_value = obj.find_field_unordered(field_name);
-
- if constexpr (next_pos >= path_view.size()) {
- return field_value.get(target_ref);
- } else {
- return extract_with_reflection<member_type, next_pos>(field_value, target_ref);
- }
- }
- // Bracket notation: [index] or ["field"]
- else if constexpr (path_view[PathPos] == '[') {
- constexpr auto bracket_info = parse_bracket(PathPos);
- constexpr bool is_field = std::get<0>(bracket_info);
- constexpr std::size_t next_pos = std::get<2>(bracket_info);
-
- if constexpr (is_field) {
- constexpr std::string_view field_name = std::get<1>(bracket_info);
- constexpr auto member_info = find_member_by_name(CurrentType, field_name);
- static_assert(member_info != ^^void, "Field not found in struct");
- constexpr auto member_type = std::meta::type_of(member_info);
-
- auto obj_result = current.get_object();
- if (obj_result.error()) return obj_result.error();
- auto obj = obj_result.value_unsafe();
- auto field_value = obj.find_field_unordered(field_name);
-
- if constexpr (next_pos >= path_view.size()) {
- return field_value.get(target_ref);
- } else {
- return extract_with_reflection<member_type, next_pos>(field_value, target_ref);
- }
- } else {
- constexpr std::size_t index = std::get<3>(bracket_info);
- constexpr auto elem_type = get_element_type_reflected(CurrentType);
- static_assert(elem_type != ^^void, "Could not determine array element type");
-
- auto arr_result = current.get_array();
- if (arr_result.error()) return arr_result.error();
- auto arr = arr_result.value_unsafe();
- auto elem_value = arr.at(index);
-
- if constexpr (next_pos >= path_view.size()) {
- return elem_value.get(target_ref);
- } else {
- return extract_with_reflection<elem_type, next_pos>(elem_value, target_ref);
- }
- }
- }
- // Skip unexpected characters and continue
- else {
- return extract_with_reflection<CurrentType, PathPos + 1>(current, target_ref);
- }
- }
-
- // Find member by name in reflected type
- static consteval std::meta::info find_member_by_name(std::meta::info type_refl, std::string_view name) {
- auto members = std::meta::nonstatic_data_members_of(type_refl, std::meta::access_context::unchecked());
- for (auto mem : members) {
- if (std::meta::identifier_of(mem) == name) {
- return mem;
- }
- }
- }
-
// Generate compile-time accessor code by walking the path
template<std::size_t PathPos>
static inline simdjson_result<value> access_impl(simdjson_result<value> current) noexcept {
@@ -166856,55 +163666,6 @@ public:
}
private:
- // Check if type has member with given name
- template<typename Type>
- static consteval bool has_member(std::string_view member_name) {
- constexpr auto members = std::meta::nonstatic_data_members_of(^^Type, std::meta::access_context::unchecked());
- for (auto mem : members) {
- if (std::meta::identifier_of(mem) == member_name) {
- return true;
- }
- }
- return false;
- }
-
- // Get type of member by name
- template<typename Type>
- static consteval auto get_member_type(std::string_view member_name) {
- constexpr auto members = std::meta::nonstatic_data_members_of(^^Type, std::meta::access_context::unchecked());
- for (auto mem : members) {
- if (std::meta::identifier_of(mem) == member_name) {
- return std::meta::type_of(mem);
- }
- }
- return ^^void;
- }
-
- // Check if non-reflected type is array-like
- template<typename Type>
- static consteval bool is_container_type() {
- using BaseType = std::remove_cvref_t<Type>;
- if constexpr (requires { typename BaseType::value_type; }) {
- return true;
- }
- if constexpr (std::is_array_v<BaseType>) {
- return true;
- }
- return false;
- }
-
- // Extract element type from non-reflected container
- template<typename Type>
- using extract_element_type = std::conditional_t<
- requires { typename std::remove_cvref_t<Type>::value_type; },
- typename std::remove_cvref_t<Type>::value_type,
- std::conditional_t<
- std::is_array_v<std::remove_cvref_t<Type>>,
- std::remove_extent_t<std::remove_cvref_t<Type>>,
- void
- >
- >;
-
// Validate path matches struct definition
static consteval bool validate_path() {
if constexpr (!std::is_class_v<T>) {
@@ -168385,30 +165146,6 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin
}
-struct escaping {
- static constexpr uint32_t BYTES_PROCESSED = 16;
- simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
-
- simdjson_inline bool has_escape() { return escape_bits != 0; }
- simdjson_inline int escape_index() { return trailing_zeroes(escape_bits); }
-
- uint64_t escape_bits;
-}; // struct escaping
-
-
-
-simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
- static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes");
- simd8<uint8_t> v(src);
- v.store(dst);
- simd8<bool> is_quote = (v == '"');
- simd8<bool> is_backslash = (v == '\\');
- simd8<bool> is_control = (v < 32);
- return {
- uint64_t((is_backslash | is_quote | is_control).to_bitmask())
- };
-}
-
} // unnamed namespace
} // namespace westmere
} // namespace simdjson
@@ -168618,11 +165355,6 @@ protected:
public:
simdjson_inline value_iterator() noexcept = default;
- /**
- * Denote that we're starting a document.
- */
- simdjson_inline void start_document() noexcept;
-
/**
* Skips a non-iterated or partially-iterated JSON value, whether it is a scalar, array or object.
*
@@ -169023,16 +165755,7 @@ protected:
simdjson_warn_unused simdjson_inline error_code start_container(uint8_t start_char, const char *incorrect_type_message, const char *type) noexcept;
simdjson_warn_unused simdjson_inline error_code end_container() noexcept;
- /**
- * Advance to a place expecting a value (increasing depth).
- *
- * @return The current token (the one left behind).
- * @error TAPE_ERROR If the document ended early.
- */
- simdjson_inline simdjson_result<const uint8_t *> advance_to_value() noexcept;
-
simdjson_warn_unused simdjson_inline error_code incorrect_type_error(const char *message) const noexcept;
- simdjson_warn_unused simdjson_inline error_code error_unless_more_tokens(uint32_t tokens=1) const noexcept;
simdjson_inline bool is_at_start() const noexcept;
/**
@@ -169115,6 +165838,7 @@ public:
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
+/* amalgamation skipped (editor-only): #include "simdjson/jsonpathutil.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/deserialize.h" */
/* amalgamation skipped (editor-only): #include <vector> */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
@@ -169968,11 +166692,19 @@ protected:
*/
simdjson_inline simdjson_result<object> start_or_resume_object() noexcept;
+ /**
+ * The recursive step of for_each_at_path_with_wildcard: matches
+ * path[index] at this level and what follows below it.
+ */
+ template <typename Func>
+ inline error_code for_each_at_path_segments(const std::vector<simdjson::internal::json_path_segment> &path, size_t index, Func &callback) noexcept;
+
// simdjson_inline void log_value(const char *type) const noexcept;
// simdjson_inline void log_error(const char *message) const noexcept;
value_iterator iter{};
+ friend class array;
friend class document;
friend class array_iterator;
friend class field;
@@ -170661,6 +167393,7 @@ public:
/**
* Take an input in json containing max_len characters and attempt to copy it over to tmpbuf, a buffer with
* N bytes of capacity. It will return false if N is too small (smaller than max_len) of if it is zero.
+ * Trailing whitespace, commas, and record separators (0x1E) do not count toward max_len.
* The buffer (tmpbuf) is padded with space characters.
*/
simdjson_warn_unused simdjson_inline bool copy_to_buffer(const uint8_t *json, uint32_t max_len, uint8_t *tmpbuf, size_t N) noexcept;
@@ -170693,8 +167426,11 @@ public:
/**
* Updates this json iterator so that it is back at the beginning of the document,
* as if it had just been created.
+ *
+ * An iterator that is no longer alive (e.g., abandoned after a fatal error) has no
+ * document to go back to: it is left as is and its error is returned.
*/
- inline void rewind() noexcept;
+ simdjson_inline error_code rewind() noexcept;
/**
* This checks whether the {,},[,] are balanced so that the document
* ends with proper zero depth. This requires scanning the whole document
@@ -171049,6 +167785,28 @@ public:
private:
+ /**
+ * Returns true if target ends with an odd number of backslashes. The last one
+ * then escapes the quote that follows the matching bytes in the raw string, so
+ * that quote does not end the string. Not inlined: callers check the last byte
+ * first, and keys ending in a backslash are rare.
+ */
+ static simdjson_never_inline bool ends_with_unpaired_backslash(std::string_view target) noexcept {
+ size_t backslash_count{0};
+ while(backslash_count < target.size() && target[target.size() - 1 - backslash_count] == '\\') {
+ backslash_count++;
+ }
+ return backslash_count % 2 == 1;
+ }
+
+ /**
+ * True when a byte-for-byte match must be rejected because target escapes the
+ * closing quote. The last-byte test keeps the counting loop off the common path.
+ */
+ static simdjson_inline bool escapes_trailing_quote(std::string_view target) noexcept {
+ return simdjson_unlikely(!target.empty() && target[target.size() - 1] == '\\')
+ && ends_with_unpaired_backslash(target);
+ }
/**
* This will set the inner pointer to zero, effectively making
@@ -171622,6 +168380,7 @@ public:
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
+/* amalgamation skipped (editor-only): #include "simdjson/jsonpathutil.h" */
/* amalgamation skipped (editor-only): #include <vector> */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
@@ -171814,6 +168573,13 @@ protected:
*/
simdjson_warn_unused simdjson_inline error_code consume() noexcept;
+ /**
+ * The recursive step of for_each_at_path_with_wildcard: matches
+ * path[index] at this level and what follows below it.
+ */
+ template <typename Func>
+ inline error_code for_each_at_path_segments(const std::vector<simdjson::internal::json_path_segment> &path, size_t index, Func &callback) noexcept;
+
/**
* Begin array iteration.
*
@@ -172823,6 +169589,9 @@ public:
* Reset the iterator inside the document instance so we are pointing back at the
* beginning of the document, as if it had just been created. It invalidates all
* values, objects and arrays that you have created so far (including unescaped strings).
+ *
+ * A document that is no longer alive (see is_alive()) cannot be rewound: it is
+ * left as is.
*/
inline void rewind() noexcept;
/**
@@ -172896,6 +169665,8 @@ public:
* - INCORRECT_TYPE if a non-integer is used to access an array
* - INVALID_JSON_POINTER if the JSON pointer is invalid and cannot be parsed
* - SCALAR_DOCUMENT_AS_VALUE if the json_pointer is empty and the document is not a scalar (see is_scalar() function).
+ * - the fatal error (e.g., TAPE_ERROR) that ended an earlier call, as the document can
+ * no longer be rewound (see is_alive()).
*/
simdjson_inline simdjson_result<value> at_pointer(std::string_view json_pointer) noexcept;
@@ -172919,6 +169690,8 @@ public:
* - NO_SUCH_FIELD if a field does not exist in an object
* - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length
* - INCORRECT_TYPE if a non-integer is used to access an array
+ * - the fatal error (e.g., TAPE_ERROR) that ended an earlier call, as the document can
+ * no longer be rewound (see is_alive()).
*/
simdjson_inline simdjson_result<value> at_path(std::string_view json_path) noexcept;
@@ -172945,7 +169718,9 @@ public:
/**
* Consumes the document and returns a string_view instance corresponding to the
* document as represented in JSON. It points inside the original byte array containing
- * the JSON document.
+ * the JSON document. It starts at the first byte of the document and ends at its last
+ * byte: it does not include the white space that follows the document, nor, in a
+ * document_stream, the separators or the documents that follow it.
*/
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
@@ -173272,134 +170047,134 @@ public:
using westmere::implementation_simdjson_result_base<westmere::ondemand::document>::operator*;
using westmere::implementation_simdjson_result_base<westmere::ondemand::document>::operator->;
- template <class T, typename std::enable_if<std::is_same<T, westmere::ondemand::document>::value == false>::type>
- explicit simdjson_inline operator T() noexcept(false);
- simdjson_inline operator westmere::ondemand::array() & noexcept(false);
- simdjson_inline operator westmere::ondemand::object() & noexcept(false);
- explicit simdjson_inline operator uint64_t() noexcept(false);
- explicit simdjson_inline operator int64_t() noexcept(false);
- explicit simdjson_inline operator double() noexcept(false);
- explicit simdjson_inline operator std::string_view() noexcept(false);
- explicit simdjson_inline operator westmere::ondemand::raw_json_string() noexcept(false);
- explicit simdjson_inline operator bool() noexcept(false);
- simdjson_inline operator westmere::ondemand::value() noexcept(false);
-#endif
- simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
- simdjson_inline simdjson_result<size_t> count_fields() & noexcept;
- simdjson_inline simdjson_result<westmere::ondemand::value> at(size_t index) & noexcept;
- simdjson_inline simdjson_result<westmere::ondemand::array_iterator> begin() & noexcept;
- simdjson_inline simdjson_result<westmere::ondemand::array_iterator> end() & noexcept;
- simdjson_inline simdjson_result<westmere::ondemand::value> find_field(std::string_view key) & noexcept;
- simdjson_inline simdjson_result<westmere::ondemand::value> find_field(const char *key) & noexcept;
- simdjson_inline simdjson_result<westmere::ondemand::value> operator[](std::string_view key) & noexcept;
- simdjson_inline simdjson_result<westmere::ondemand::value> operator[](const char *key) & noexcept;
- simdjson_result<westmere::ondemand::value> operator[](int) & noexcept = delete;
- simdjson_inline simdjson_result<westmere::ondemand::value> find_field_unordered(std::string_view key) & noexcept;
- simdjson_inline simdjson_result<westmere::ondemand::value> find_field_unordered(const char *key) & noexcept;
- simdjson_inline simdjson_result<westmere::ondemand::json_type> type() noexcept;
- simdjson_inline simdjson_result<bool> is_scalar() noexcept;
- simdjson_inline simdjson_result<bool> is_string() noexcept;
- simdjson_inline simdjson_result<const char *> current_location() noexcept;
- simdjson_inline int32_t current_depth() const noexcept;
- simdjson_inline bool at_end() const noexcept;
- simdjson_inline bool is_negative() noexcept;
- simdjson_inline simdjson_result<bool> is_integer() noexcept;
- simdjson_inline simdjson_result<westmere::number_type> get_number_type() noexcept;
- simdjson_inline simdjson_result<westmere::ondemand::number> get_number() noexcept;
- /** @copydoc simdjson_inline std::string_view document::raw_json_token() const noexcept */
- simdjson_inline simdjson_result<std::string_view> raw_json_token() noexcept;
-
- simdjson_inline simdjson_result<westmere::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
- simdjson_inline simdjson_result<westmere::ondemand::value> at_path(std::string_view json_path) noexcept;
-#if SIMDJSON_SUPPORTS_CONCEPTS
- template <typename Func>
- requires std::invocable<Func, westmere::ondemand::value>
-#else
- template <typename Func>
-#endif
- simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
-#if SIMDJSON_STATIC_REFLECTION
- template<constevalutil::fixed_string... FieldNames, typename T>
- requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
- simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept;
-#endif // SIMDJSON_STATIC_REFLECTION
-};
-
-
-} // namespace simdjson
-
-
-
-namespace simdjson {
-
-template<>
-struct simdjson_result<westmere::ondemand::document_reference> : public westmere::implementation_simdjson_result_base<westmere::ondemand::document_reference> {
-public:
- simdjson_inline simdjson_result(westmere::ondemand::document_reference value, error_code error) noexcept;
- simdjson_inline simdjson_result() noexcept = default;
- simdjson_inline error_code rewind() noexcept;
-
- simdjson_inline simdjson_result<westmere::ondemand::array> get_array() & noexcept;
- simdjson_inline simdjson_result<westmere::ondemand::object> get_object() & noexcept;
- simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
- simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
- simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
- simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
- simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
- simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
- simdjson_inline simdjson_result<uint16_t> get_uint16() noexcept;
- simdjson_inline simdjson_result<int16_t> get_int16() noexcept;
- simdjson_inline simdjson_result<uint8_t> get_uint8() noexcept;
- simdjson_inline simdjson_result<int8_t> get_int8() noexcept;
- simdjson_inline simdjson_result<double> get_double() noexcept;
- simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
- simdjson_inline simdjson_result<float> get_float() noexcept;
- simdjson_inline simdjson_result<float> get_float_in_string() noexcept;
-#if SIMDJSON_SUPPORTS_FLOAT32_T
- simdjson_inline simdjson_result<std::float32_t> get_float32() noexcept;
-#endif // SIMDJSON_SUPPORTS_FLOAT32_T
-#if SIMDJSON_SUPPORTS_FLOAT64_T
- simdjson_inline simdjson_result<std::float64_t> get_float64() noexcept;
-#endif // SIMDJSON_SUPPORTS_FLOAT64_T
- simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
-#if SIMDJSON_SUPPORTS_CHAR8_T
- simdjson_inline simdjson_result<std::u8string_view> get_u8string(bool allow_replacement = false) noexcept;
-#endif // SIMDJSON_SUPPORTS_CHAR8_T
- template <typename string_type>
- simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
- simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
- simdjson_inline simdjson_result<westmere::ondemand::raw_json_string> get_raw_json_string() noexcept;
- simdjson_inline simdjson_result<bool> get_bool() noexcept;
- simdjson_inline simdjson_result<westmere::ondemand::value> get_value() noexcept;
- simdjson_inline simdjson_result<bool> is_null() noexcept;
-
- template<typename T> simdjson_inline simdjson_result<T> get() &
-#if SIMDJSON_SUPPORTS_CONCEPTS
- noexcept(nothrow_gettable<T, westmere::ondemand::document_reference>);
-#else
- noexcept;
-#endif
- template<typename T> simdjson_inline simdjson_result<T> get() &&
-#if SIMDJSON_SUPPORTS_CONCEPTS
- noexcept(nothrow_gettable<T, westmere::ondemand::document_reference>);
-#else
- noexcept;
-#endif
-
- template<typename T> simdjson_inline error_code get(T &out) &
-#if SIMDJSON_SUPPORTS_CONCEPTS
- noexcept(nothrow_gettable<T, westmere::ondemand::document_reference>);
-#else
- noexcept;
-#endif
- template<typename T> simdjson_inline error_code get(T &out) &&
-#if SIMDJSON_SUPPORTS_CONCEPTS
- noexcept(nothrow_gettable<T, westmere::ondemand::document_reference>);
-#else
- noexcept;
-#endif
-#if SIMDJSON_EXCEPTIONS
- template <class T>
+ simdjson_inline operator westmere::ondemand::array() & noexcept(false);
+ simdjson_inline operator westmere::ondemand::object() & noexcept(false);
+ explicit simdjson_inline operator uint64_t() noexcept(false);
+ explicit simdjson_inline operator int64_t() noexcept(false);
+ explicit simdjson_inline operator double() noexcept(false);
+ explicit simdjson_inline operator std::string_view() noexcept(false);
+ explicit simdjson_inline operator westmere::ondemand::raw_json_string() noexcept(false);
+ explicit simdjson_inline operator bool() noexcept(false);
+ simdjson_inline operator westmere::ondemand::value() noexcept(false);
+#endif
+ simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
+ simdjson_inline simdjson_result<size_t> count_fields() & noexcept;
+ simdjson_inline simdjson_result<westmere::ondemand::value> at(size_t index) & noexcept;
+ simdjson_inline simdjson_result<westmere::ondemand::array_iterator> begin() & noexcept;
+ simdjson_inline simdjson_result<westmere::ondemand::array_iterator> end() & noexcept;
+ simdjson_inline simdjson_result<westmere::ondemand::value> find_field(std::string_view key) & noexcept;
+ simdjson_inline simdjson_result<westmere::ondemand::value> find_field(const char *key) & noexcept;
+ simdjson_inline simdjson_result<westmere::ondemand::value> operator[](std::string_view key) & noexcept;
+ simdjson_inline simdjson_result<westmere::ondemand::value> operator[](const char *key) & noexcept;
+ simdjson_result<westmere::ondemand::value> operator[](int) & noexcept = delete;
+ simdjson_inline simdjson_result<westmere::ondemand::value> find_field_unordered(std::string_view key) & noexcept;
+ simdjson_inline simdjson_result<westmere::ondemand::value> find_field_unordered(const char *key) & noexcept;
+ simdjson_inline simdjson_result<westmere::ondemand::json_type> type() noexcept;
+ simdjson_inline simdjson_result<bool> is_scalar() noexcept;
+ simdjson_inline simdjson_result<bool> is_string() noexcept;
+ simdjson_inline simdjson_result<const char *> current_location() noexcept;
+ simdjson_inline int32_t current_depth() const noexcept;
+ simdjson_inline bool at_end() const noexcept;
+ simdjson_inline bool is_negative() noexcept;
+ simdjson_inline simdjson_result<bool> is_integer() noexcept;
+ simdjson_inline simdjson_result<westmere::number_type> get_number_type() noexcept;
+ simdjson_inline simdjson_result<westmere::ondemand::number> get_number() noexcept;
+ /** @copydoc simdjson_inline std::string_view document::raw_json_token() const noexcept */
+ simdjson_inline simdjson_result<std::string_view> raw_json_token() noexcept;
+ /** @copydoc simdjson_inline simdjson_result<std::string_view> document::raw_json() noexcept */
+ simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
+
+ simdjson_inline simdjson_result<westmere::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
+ simdjson_inline simdjson_result<westmere::ondemand::value> at_path(std::string_view json_path) noexcept;
+#if SIMDJSON_SUPPORTS_CONCEPTS
+ template <typename Func>
+ requires std::invocable<Func, westmere::ondemand::value>
+#else
+ template <typename Func>
+#endif
+ simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
+#if SIMDJSON_STATIC_REFLECTION
+ template<constevalutil::fixed_string... FieldNames, typename T>
+ requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
+ simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept;
+#endif // SIMDJSON_STATIC_REFLECTION
+};
+
+
+} // namespace simdjson
+
+
+
+namespace simdjson {
+
+template<>
+struct simdjson_result<westmere::ondemand::document_reference> : public westmere::implementation_simdjson_result_base<westmere::ondemand::document_reference> {
+public:
+ simdjson_inline simdjson_result(westmere::ondemand::document_reference value, error_code error) noexcept;
+ simdjson_inline simdjson_result() noexcept = default;
+ simdjson_inline error_code rewind() noexcept;
+
+ simdjson_inline simdjson_result<westmere::ondemand::array> get_array() & noexcept;
+ simdjson_inline simdjson_result<westmere::ondemand::object> get_object() & noexcept;
+ simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
+ simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
+ simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
+ simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
+ simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
+ simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
+ simdjson_inline simdjson_result<uint16_t> get_uint16() noexcept;
+ simdjson_inline simdjson_result<int16_t> get_int16() noexcept;
+ simdjson_inline simdjson_result<uint8_t> get_uint8() noexcept;
+ simdjson_inline simdjson_result<int8_t> get_int8() noexcept;
+ simdjson_inline simdjson_result<double> get_double() noexcept;
+ simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
+ simdjson_inline simdjson_result<float> get_float() noexcept;
+ simdjson_inline simdjson_result<float> get_float_in_string() noexcept;
+#if SIMDJSON_SUPPORTS_FLOAT32_T
+ simdjson_inline simdjson_result<std::float32_t> get_float32() noexcept;
+#endif // SIMDJSON_SUPPORTS_FLOAT32_T
+#if SIMDJSON_SUPPORTS_FLOAT64_T
+ simdjson_inline simdjson_result<std::float64_t> get_float64() noexcept;
+#endif // SIMDJSON_SUPPORTS_FLOAT64_T
+ simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
+#if SIMDJSON_SUPPORTS_CHAR8_T
+ simdjson_inline simdjson_result<std::u8string_view> get_u8string(bool allow_replacement = false) noexcept;
+#endif // SIMDJSON_SUPPORTS_CHAR8_T
+ template <typename string_type>
+ simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
+ simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
+ simdjson_inline simdjson_result<westmere::ondemand::raw_json_string> get_raw_json_string() noexcept;
+ simdjson_inline simdjson_result<bool> get_bool() noexcept;
+ simdjson_inline simdjson_result<westmere::ondemand::value> get_value() noexcept;
+ simdjson_inline simdjson_result<bool> is_null() noexcept;
+
+ template<typename T> simdjson_inline simdjson_result<T> get() &
+#if SIMDJSON_SUPPORTS_CONCEPTS
+ noexcept(nothrow_gettable<T, westmere::ondemand::document_reference>);
+#else
+ noexcept;
+#endif
+ template<typename T> simdjson_inline simdjson_result<T> get() &&
+#if SIMDJSON_SUPPORTS_CONCEPTS
+ noexcept(nothrow_gettable<T, westmere::ondemand::document_reference>);
+#else
+ noexcept;
+#endif
+
+ template<typename T> simdjson_inline error_code get(T &out) &
+#if SIMDJSON_SUPPORTS_CONCEPTS
+ noexcept(nothrow_gettable<T, westmere::ondemand::document_reference>);
+#else
+ noexcept;
+#endif
+ template<typename T> simdjson_inline error_code get(T &out) &&
+#if SIMDJSON_SUPPORTS_CONCEPTS
+ noexcept(nothrow_gettable<T, westmere::ondemand::document_reference>);
+#else
+ noexcept;
+#endif
+#if SIMDJSON_EXCEPTIONS
+ template <class T>
explicit simdjson_inline operator T() noexcept(false);
simdjson_inline operator westmere::ondemand::array() & noexcept(false);
simdjson_inline operator westmere::ondemand::object() & noexcept(false);
@@ -173434,6 +170209,8 @@ public:
simdjson_inline simdjson_result<westmere::ondemand::number> get_number() noexcept;
/** @copydoc simdjson_inline std::string_view document_reference::raw_json_token() const noexcept */
simdjson_inline simdjson_result<std::string_view> raw_json_token() noexcept;
+ /** @copydoc simdjson_inline simdjson_result<std::string_view> document::raw_json() noexcept */
+ simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
simdjson_inline simdjson_result<westmere::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
simdjson_inline simdjson_result<westmere::ondemand::value> at_path(std::string_view json_path) noexcept;
@@ -175395,6 +172172,7 @@ concept key_selector_type = key_selector_detail::is_key_selector<T>::value;
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
+/* amalgamation skipped (editor-only): #include "simdjson/jsonpathutil.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/key_selector.h" */
/* amalgamation skipped (editor-only): #include <vector> */
/* amalgamation skipped (editor-only): #if SIMDJSON_STATIC_REFLECTION && SIMDJSON_SUPPORTS_CONCEPTS */
@@ -175939,6 +172717,13 @@ protected:
simdjson_warn_unused simdjson_inline error_code find_field_raw(const std::string_view key) noexcept;
+ /**
+ * The recursive step of for_each_at_path_with_wildcard: matches
+ * path[index] at this level and what follows below it.
+ */
+ template <typename Func>
+ inline error_code for_each_at_path_segments(const std::vector<simdjson::internal::json_path_segment> &path, size_t index, Func &callback) noexcept;
+
value_iterator iter{};
#if SIMDJSON_DEVELOPMENT_CHECKS
bool locked{false};
@@ -176803,6 +173588,14 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) {
std::is_default_constructible_v<element_type>,
"The specified type inside the unique_ptr must default constructible.");
+ // A null pointer is serialized as null.
+ bool is_null_value;
+ SIMDJSON_TRY( val.is_null().get(is_null_value) );
+ if (is_null_value) {
+ out.reset();
+ return SUCCESS;
+ }
+
// Own the allocation before get(): a user tag_invoke may throw.
std::unique_ptr<element_type> ptr(new (std::nothrow) element_type());
if (!ptr) {
@@ -176851,6 +173644,8 @@ constexpr bool user_defined_type = (std::is_class_v<T>
#endif // SIMDJSON_SUPPORTS_CHAR8_T
&& !concepts::optional_type<T> &&
!concepts::appendable_containers<T>
+// Smart pointers are handled by the generic smart-pointer tag_invoke.
+&& !concepts::smart_pointer<T>
// simdjson's own types (array, object, value, raw_json_string, number,
// document, document_reference) have dedicated get<T>() specializations and
// must never go through reflection.
@@ -177443,251 +174238,8 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept {
#endif
}
-template <typename simdjson_value, typename T>
- requires(user_defined_type<std::remove_cvref_t<T>>)
-error_code tag_invoke(deserialize_tag, simdjson_value &val, std::unique_ptr<T> &out) noexcept(false) {
- std::unique_ptr<T> ptr(new (std::nothrow) T());
- if (!ptr) {
- return MEMALLOC;
- }
- SIMDJSON_TRY(val.get(*ptr));
- out = std::move(ptr);
- return SUCCESS;
-}
-
-template <typename simdjson_value, typename T>
- requires(user_defined_type<std::remove_cvref_t<T>>)
-error_code tag_invoke(deserialize_tag, simdjson_value &val, std::shared_ptr<T> &out) noexcept(false) {
- std::shared_ptr<T> ptr(new (std::nothrow) T());
- if (!ptr) {
- return MEMALLOC;
- }
- SIMDJSON_TRY(val.get(*ptr));
- out = std::move(ptr);
- return SUCCESS;
-}
-
#endif // SIMDJSON_STATIC_REFLECTION
-////////////////////////////////////////
-// Unique pointers
-////////////////////////////////////////
-error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<bool> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_unique<bool>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_bool().get(*out));
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<int64_t> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_unique<int64_t>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_int64().get(*out));
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<uint64_t> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_unique<uint64_t>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_uint64().get(*out));
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<double> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_unique<double>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_double().get(*out));
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<std::string_view> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_unique<std::string_view>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_string().get(*out));
- return SUCCESS;
-}
-
-
-////////////////////////////////////////
-// Shared pointers
-////////////////////////////////////////
-error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<bool> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_shared<bool>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_bool().get(*out));
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<int64_t> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_shared<int64_t>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_int64().get(*out));
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<uint64_t> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_shared<uint64_t>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_uint64().get(*out));
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<double> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_shared<double>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_double().get(*out));
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<std::string_view> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_shared<std::string_view>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_string().get(*out));
- return SUCCESS;
-}
-
-
-////////////////////////////////////////
-// Explicit optional specializations
-////////////////////////////////////////
-
-////////////////////////////////////////
-// Explicit smart pointer specializations for string and int types
-////////////////////////////////////////
-error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<std::string> &out) noexcept {
- // Check if the value is null
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset(); // Set to nullptr
- return SUCCESS;
- }
-
- if (!out) {
- out = std::make_unique<std::string>();
- }
- std::string_view str;
- SIMDJSON_TRY(val.get_string().get(str));
- *out = std::string{str};
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<std::string> &out) noexcept {
- // Check if the value is null
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset(); // Set to nullptr
- return SUCCESS;
- }
-
- if (!out) {
- out = std::make_shared<std::string>();
- }
- std::string_view str;
- SIMDJSON_TRY(val.get_string().get(str));
- *out = std::string{str};
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<int> &out) noexcept {
- // Check if the value is null
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset(); // Set to nullptr
- return SUCCESS;
- }
-
- if (!out) {
- out = std::make_unique<int>();
- }
- int64_t temp;
- SIMDJSON_TRY(val.get_int64().get(temp));
- *out = static_cast<int>(temp);
- return SUCCESS;
-}
-
} // namespace simdjson
#endif // SIMDJSON_ONDEMAND_DESERIALIZE_H
@@ -177802,10 +174354,9 @@ simdjson_inline simdjson_result<std::string_view> array::raw_json() noexcept {
const uint8_t * starting_point{iter.peek_start()};
auto error = consume();
if(error) { return error; }
- // After 'consume()', we could be left pointing just beyond the document, but that
- // is ok because we are not going to dereference the final pointer position, we just
- // use it to compute the length in bytes.
- const uint8_t * final_point{iter._json_iter->unsafe_pointer()};
+ // After 'consume()', the last token consumed is the closing bracket: we end on it
+ // rather than on the next token, which may follow white space.
+ const uint8_t * final_point{iter._json_iter->token.peek(-1) + 1};
return std::string_view(reinterpret_cast<const char*>(starting_point), size_t(final_point - starting_point));
}
@@ -177889,12 +174440,23 @@ template <typename Func>
template <typename Func>
#endif
inline error_code array::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
- auto result_pair = get_next_key_and_json_path(json_path);
- std::string_view key = result_pair.first;
- std::string_view remaining_path = result_pair.second;
+ // The path is split once: every element reached by a wildcard sees the same
+ // remaining path, so re-parsing it at each one would be quadratic.
+ const std::vector<internal::json_path_segment> path = internal::split_json_path(json_path);
+ // A path that cannot be parsed matches nothing, whatever the document: say
+ // so rather than letting the walk skip it as a mismatch below a wildcard.
+ if (path.back().key.empty()) {
+ return INVALID_JSON_POINTER;
+ }
+ return for_each_at_path_segments(path, 0, callback);
+}
+
+template <typename Func>
+inline error_code array::for_each_at_path_segments(const std::vector<internal::json_path_segment> &path, size_t index, Func &callback) noexcept {
+ std::string_view key = path[index].key;
+ const bool last = index + 1 == path.size();
// Empty key means the path segment could not be parsed (including malformed
- // bracket-quoted keys). Without this check, an empty key is treated as index
- // 0 and remaining_path may be unchanged, causing infinite recursion.
+ // bracket-quoted keys).
if (key.empty()) {
return INVALID_JSON_POINTER;
}
@@ -177903,11 +174465,11 @@ inline error_code array::for_each_at_path_with_wildcard(std::string_view json_pa
for(auto element: *this) {
value val;
SIMDJSON_TRY(element.get(val));
- if (remaining_path.empty()) {
+ if (last) {
callback(val);
} else {
// An element that does not match the rest of the path is skipped.
- error_code err = val.for_each_at_path_with_wildcard(remaining_path, callback);
+ error_code err = val.for_each_at_path_segments(path, index + 1, callback);
if (err && !internal::is_matching_failure_error(err)) { return err; }
}
}
@@ -177926,16 +174488,16 @@ inline error_code array::for_each_at_path_with_wildcard(std::string_view json_pa
value val;
if (!error) { error = at(idx).get(val); }
if (error) {
- if (error == INDEX_OUT_OF_BOUNDS && internal::json_path_has_wildcard(remaining_path)) {
+ if (error == INDEX_OUT_OF_BOUNDS && path[index].wildcard_follows) {
return SUCCESS;
}
return error;
}
- if (remaining_path.empty()){
+ if (last){
callback(val);
return SUCCESS;
} else {
- return val.for_each_at_path_with_wildcard(remaining_path, callback);
+ return val.for_each_at_path_segments(path, index + 1, callback);
}
}
}
@@ -178593,6 +175155,19 @@ template <typename Func>
template <typename Func>
#endif
inline error_code value::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
+ // The path is split once: every value reached by a wildcard sees the same
+ // remaining path, so re-parsing it at each one would be quadratic.
+ const std::vector<internal::json_path_segment> path = internal::split_json_path(json_path);
+ // A path that cannot be parsed matches nothing, whatever the document: say
+ // so rather than letting the walk skip it as a mismatch below a wildcard.
+ if (path.back().key.empty()) {
+ return INVALID_JSON_POINTER;
+ }
+ return for_each_at_path_segments(path, 0, callback);
+}
+
+template <typename Func>
+inline error_code value::for_each_at_path_segments(const std::vector<internal::json_path_segment> &path, size_t index, Func &callback) noexcept {
// Every recursive step of for_each_at_path_with_wildcard goes through this
// function, and each one descends one level into the document. A path with
// many segments applied to a deeply nested document would otherwise recurse
@@ -178601,10 +175176,16 @@ inline error_code value::for_each_at_path_with_wildcard(std::string_view json_pa
json_type t;
SIMDJSON_TRY(type().get(t));
switch (t) {
- case json_type::array:
- return (*this).get_array().for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
- case json_type::object:
- return (*this).get_object().for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
+ case json_type::array: {
+ array a;
+ SIMDJSON_TRY(get_array().get(a));
+ return a.for_each_at_path_segments(path, index, callback);
+ }
+ case json_type::object: {
+ object o;
+ SIMDJSON_TRY(get_object().get(o));
+ return o.for_each_at_path_segments(path, index, callback);
+ }
default:
return INVALID_JSON_POINTER;
}
@@ -178977,7 +175558,8 @@ simdjson_inline document document::start(json_iterator &&iter) noexcept {
}
inline void document::rewind() noexcept {
- iter.rewind();
+ // A document that is no longer alive is left as it is (see is_alive()).
+ static_cast<void>(iter.rewind());
}
inline std::string document::to_debug_string() noexcept {
@@ -179277,7 +175859,13 @@ simdjson_warn_unused simdjson_inline error_code document::consume() noexcept {
auto error = is_scalar().get(scalar);
if(error) { return error; }
if(scalar) {
- iter.return_current_and_advance();
+ // Step over the scalar and leave depth 0, as skip_child() does: otherwise a
+ // document_stream takes the next document as the rest of this one. At depth
+ // 0 the scalar has already been consumed (e.g., by get_int64()).
+ if(iter.depth() > 0) {
+ iter.return_current_and_advance();
+ iter.ascend_to(0);
+ }
return SUCCESS;
}
error = iter.skip_child(0);
@@ -179290,10 +175878,36 @@ simdjson_inline simdjson_result<std::string_view> document::raw_json() noexcept
const uint8_t * starting_point{_iter.peek_start()};
auto error = consume();
if(error) { return error; }
- // After 'consume()', we could be left pointing just beyond the document, but that
- // is ok because we are not going to dereference the final pointer position, we just
- // use it to compute the length in bytes.
- const uint8_t * final_point{iter.unsafe_pointer()};
+ // After 'consume()', the iterator points at the token after the document. In a
+ // document_stream, that is the next document, past white space and separators, or
+ // the end of the input when a truncated document follows. We end on the last byte
+ // of this document instead.
+ const uint8_t * final_point;
+ if(*starting_point == '{' || *starting_point == '[') {
+ // The last token consumed is the closing brace or bracket.
+ final_point = iter.token.peek(-1) + 1;
+ } else {
+ // A scalar is a single token: a string ends at its closing quote, anything else
+ // at the first white space, structural character, quote, RS (0x1E) or NUL.
+ const uint8_t * next_token{iter.unsafe_pointer()};
+ final_point = starting_point + 1;
+ if(*starting_point == '"') {
+ while(final_point < next_token) {
+ uint8_t c = *final_point++;
+ if(c == '\\') {
+ final_point++;
+ } else if(c == '"') {
+ break;
+ }
+ }
+ if(final_point > next_token) { final_point = next_token; }
+ } else {
+ while(final_point < next_token && !jsoncharutils::is_structural_or_whitespace(*final_point)
+ && *final_point != '"' && *final_point != 0x1E && *final_point != 0) {
+ final_point++;
+ }
+ }
+ }
return std::string_view(reinterpret_cast<const char*>(starting_point), size_t(final_point - starting_point));
}
@@ -179340,7 +175954,9 @@ simdjson_inline simdjson_result<std::string_view> document::raw_json_token() noe
}
simdjson_inline simdjson_result<value> document::at_pointer(std::string_view json_pointer) noexcept {
- rewind(); // Rewind the document each time at_pointer is called
+ // Rewind the document each time at_pointer is called. After a fatal error,
+ // the document cannot be walked again: we report the error instead.
+ SIMDJSON_TRY(iter.rewind());
if (json_pointer.empty()) {
return this->get_value();
}
@@ -179358,7 +175974,9 @@ simdjson_inline simdjson_result<value> document::at_pointer(std::string_view jso
}
simdjson_inline simdjson_result<value> document::at_path(std::string_view json_path) noexcept {
- rewind(); // Rewind the document each time at_pointer is called
+ // Rewind the document each time at_path is called. After a fatal error,
+ // the document cannot be walked again: we report the error instead.
+ SIMDJSON_TRY(iter.rewind());
if (json_path.empty()) {
return this->get_value();
}
@@ -179381,7 +175999,9 @@ template <typename Func>
template <typename Func>
#endif
simdjson_inline error_code document::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
- rewind(); // Rewind the document each time for_each_at_path_with_wildcard is called
+ // Rewind the document each time for_each_at_path_with_wildcard is called. After
+ // a fatal error, the document cannot be walked again: we report the error instead.
+ SIMDJSON_TRY(iter.rewind());
if (json_path.empty()) {
return INVALID_JSON_POINTER;
}
@@ -179719,11 +176339,6 @@ simdjson_inline simdjson_result<westmere::ondemand::number> simdjson_result<west
#if SIMDJSON_EXCEPTIONS
-template <class T, typename std::enable_if<std::is_same<T, westmere::ondemand::document>::value == false>::type>
-simdjson_inline simdjson_result<westmere::ondemand::document>::operator T() noexcept(false) {
- if (error()) { throw simdjson_error(error()); }
- return first;
-}
simdjson_inline simdjson_result<westmere::ondemand::document>::operator westmere::ondemand::array() & noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
@@ -179784,6 +176399,11 @@ simdjson_inline simdjson_result<std::string_view> simdjson_result<westmere::onde
return first.raw_json_token();
}
+simdjson_inline simdjson_result<std::string_view> simdjson_result<westmere::ondemand::document>::raw_json() noexcept {
+ if (error()) { return error(); }
+ return first.raw_json();
+}
+
simdjson_inline simdjson_result<westmere::ondemand::value> simdjson_result<westmere::ondemand::document>::at_pointer(std::string_view json_pointer) noexcept {
if (error()) { return error(); }
return first.at_pointer(json_pointer);
@@ -180270,6 +176890,11 @@ simdjson_inline simdjson_result<std::string_view> simdjson_result<westmere::onde
return first.raw_json_token();
}
+simdjson_inline simdjson_result<std::string_view> simdjson_result<westmere::ondemand::document_reference>::raw_json() noexcept {
+ if (error()) { return error(); }
+ return first.raw_json();
+}
+
simdjson_inline simdjson_result<westmere::ondemand::value> simdjson_result<westmere::ondemand::document_reference>::at_pointer(std::string_view json_pointer) noexcept {
if (error()) { return error(); }
return first.at_pointer(json_pointer);
@@ -180661,6 +177286,13 @@ simdjson_inline bool document_stream::skip_to_delimiter(uint8_t delimiter) noexc
}
inline void document_stream::next_document() noexcept {
+ // Iteration stops at the first document that met a fatal error. The error
+ // may have abandoned the document's iterator, which then no longer has a
+ // parser to walk the rest of the document with.
+ if (!doc.iter.is_alive() || doc.iter.error) {
+ error = doc.iter.error ? doc.iter.error : TAPE_ERROR;
+ return;
+ }
// A delimiter that cannot occur inside a document tells us where the current
// one ends, so we can jump there instead of walking every structural. Only
// valid while the iterator is still inside the document: a consumed document
@@ -180810,7 +177442,10 @@ simdjson_inline std::string_view document_stream::iterator::source() const noexc
cur_struct_index++;
}
- while (cur_struct_index <= static_cast<int64_t>(stream->parser->implementation->n_structural_indexes)) {
+ // The structural index at n_structural_indexes is the end of the batch, not
+ // a token: it must not be read as a closing bracket.
+ const int64_t n_structural_indexes = static_cast<int64_t>(stream->parser->implementation->n_structural_indexes);
+ while (cur_struct_index < n_structural_indexes) {
switch (stream->buf[stream->batch_start + stream->parser->implementation->structural_indexes[cur_struct_index]]) {
case '{': case '[':
depth++;
@@ -180822,6 +177457,11 @@ simdjson_inline std::string_view document_stream::iterator::source() const noexc
if (depth == 0) { break; }
cur_struct_index++;
}
+ // An invalid last document such as "[1 2" is never closed: it runs to the
+ // end of the batch, and not one byte past it.
+ if (cur_struct_index >= n_structural_indexes) {
+ return std::string_view(reinterpret_cast<const char*>(stream->buf) + current_index(), stream->batch_start + stream->parser->implementation->structural_indexes[cur_struct_index] - current_index());
+ }
return std::string_view(reinterpret_cast<const char*>(stream->buf) + current_index(), stream->parser->implementation->structural_indexes[cur_struct_index] - current_index() + stream->batch_start + 1);;
}
@@ -180838,7 +177478,12 @@ inline void document_stream::load_from_stage1_thread() noexcept {
// enough memory to swap to, as well.
std::swap(stage1_thread_parser,*parser);
error = stage1_thread_error;
- if (error) { return; }
+ // An EMPTY batch (no complete document in it, e.g. a run of white space at
+ // least batch_size long) is not the end of the stream: next() moves on to
+ // the batch after it, so queue that batch now as we would after a success.
+ // Returning here would leave no job for the next call, which then swaps
+ // the stale previous batch back in and ends the stream early.
+ if (error && error != EMPTY) { return; }
// If there's anything left, start the stage 1 thread!
if (next_batch_start() < len) {
@@ -180950,12 +177595,14 @@ simdjson_inline raw_json_string field::key() const noexcept {
simdjson_inline std::string_view field::key_raw_json_token() const noexcept {
SIMDJSON_ASSUME(first.buf != nullptr); // We would like to call .alive() by Visual Studio won't let us.
- return std::string_view(reinterpret_cast<const char*>(first.buf-1), second.iter._json_iter->token.peek(-1) - first.buf + 1);
+ // The ':' is the token just before the value. Use the value's start position
+ // rather than the current position, which moves once the value is consumed.
+ return std::string_view(reinterpret_cast<const char*>(first.buf-1), second.iter._json_iter->token.peek(second.iter.start_position() - 1) - first.buf + 1);
}
simdjson_inline std::string_view field::escaped_key() const noexcept {
SIMDJSON_ASSUME(first.buf != nullptr); // We would like to call .alive() by Visual Studio won't let us.
- auto end_quote = second.iter._json_iter->token.peek(-1);
+ auto end_quote = second.iter._json_iter->token.peek(second.iter.start_position() - 1);
while(*end_quote != '"') end_quote--;
return std::string_view(reinterpret_cast<const char*>(first.buf), end_quote - first.buf);
}
@@ -181126,11 +177773,14 @@ simdjson_inline json_iterator::json_iterator(const uint8_t *buf, ondemand::parse
}
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
-inline void json_iterator::rewind() noexcept {
+simdjson_inline error_code json_iterator::rewind() noexcept {
+ // abandon() let go of the parser: the iterator cannot be walked again.
+ if (!is_alive()) { return error ? error : UNINITIALIZED; }
token.set_position( root_position() );
logger::log_headers(); // We start again
_string_buf_loc = parser->string_buf.get();
_depth = 1;
+ return SUCCESS;
}
inline bool json_iterator::balanced() const noexcept {
@@ -181154,6 +177804,18 @@ inline bool json_iterator::balanced() const noexcept {
}
+// Depth change of each structural character when skipping: +1 for [ and {, -1 for ] and }.
+static constexpr int8_t skip_depth_delta[256] = {
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,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,
+};
+
// GCC 7 warns when the first line of this function is inlined away into oblivion due to the caller
// relating depth and parent_depth, which is a desired effect. The warning does not show up if the
// skip_child() function is not marked inline).
@@ -181209,7 +177871,30 @@ simdjson_warn_unused simdjson_inline error_code json_iterator::skip_child(depth_
break;
}
- // Now that we've considered the first value, we only increment/decrement for arrays/objects
+ // Now that we've considered the first value, we only increment/decrement for arrays/objects.
+ //
+ // Without logging, look up the depth change of each token in a table instead of branching
+ // on it: the next byte is hard to predict, so a switch mispredicts often. The loop also keeps
+ // the position and depth in locals, because the byte loads are char loads that may alias
+ // token._position and _depth, which would force a store of both on every token.
+ if (!logger::LOG_ENABLED) {
+ token_position pos = token.position();
+ const token_position end = end_position();
+ const uint8_t *const json = token.buf;
+ depth_t depth = _depth;
+ while (pos < end) {
+ depth += skip_depth_delta[json[*pos++]];
+ if (depth <= parent_depth) {
+ token.set_position(pos);
+ _depth = depth;
+ return SUCCESS;
+ }
+ }
+ token.set_position(pos);
+ _depth = depth;
+ return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "not enough close braces");
+ }
+
while (position() < end_position()) {
switch (*return_current_and_advance()) {
case '[': case '{':
@@ -181494,6 +178179,17 @@ simdjson_warn_unused simdjson_inline error_code json_iterator::optional_error(er
simdjson_warn_unused simdjson_inline bool json_iterator::copy_to_buffer(const uint8_t *json, uint32_t max_len, uint8_t *tmpbuf, size_t N) noexcept {
// This function is not expected to be called in performance-sensitive settings.
+ // peek_root_length() stops at the next structural index. Stage 1 omits the
+ // comma or RS (0x1E) that separates stream documents, so that byte and the
+ // whitespace around it remain in the span. RS is not a valid number finisher.
+ while (max_len > 0) {
+ const uint8_t last = json[max_len - 1];
+ if (last != ' ' && last != '\t' && last != '\n' && last != '\r' &&
+ last != ',' && last != 0x1E) {
+ break;
+ }
+ max_len--;
+ }
// Let us guard against silly cases:
if((N < max_len) || (N == 0)) { return false; }
// Copy to the buffer.
@@ -182135,7 +178831,9 @@ simdjson_inline simdjson_result<std::string_view> object::raw_json() noexcept {
const uint8_t * starting_point{iter.peek_start()};
auto error = consume();
if(error) { return error; }
- const uint8_t * final_point{iter._json_iter->peek()};
+ // After 'consume()', the last token consumed is the closing brace: we end on it
+ // rather than on the next token, which may follow white space.
+ const uint8_t * final_point{iter._json_iter->token.peek(-1) + 1};
return std::string_view(reinterpret_cast<const char*>(starting_point), size_t(final_point - starting_point));
}
@@ -182228,9 +178926,21 @@ template <typename Func>
template <typename Func>
#endif
inline error_code object::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
- auto result_pair = get_next_key_and_json_path(json_path);
- std::string_view key = result_pair.first;
- std::string_view remaining_path = result_pair.second;
+ // The path is split once: every field reached by a wildcard sees the same
+ // remaining path, so re-parsing it at each one would be quadratic.
+ const std::vector<internal::json_path_segment> path = internal::split_json_path(json_path);
+ // A path that cannot be parsed matches nothing, whatever the document: say
+ // so rather than letting the walk skip it as a mismatch below a wildcard.
+ if (path.back().key.empty()) {
+ return INVALID_JSON_POINTER;
+ }
+ return for_each_at_path_segments(path, 0, callback);
+}
+
+template <typename Func>
+inline error_code object::for_each_at_path_segments(const std::vector<internal::json_path_segment> &path, size_t index, Func &callback) noexcept {
+ std::string_view key = path[index].key;
+ const bool last = index + 1 == path.size();
// Empty key means the path segment could not be parsed (including malformed
// bracket-quoted keys). Match DOM at_path_with_wildcard and avoid treating
// "no progress" as a recursive lookup of "".
@@ -182243,11 +178953,11 @@ inline error_code object::for_each_at_path_with_wildcard(std::string_view json_p
for (auto field : *this) {
value val;
SIMDJSON_TRY(field.value().get(val));
- if (remaining_path.empty()) {
+ if (last) {
callback(val);
} else {
// A field that does not match the rest of the path is skipped.
- error_code err = val.for_each_at_path_with_wildcard(remaining_path, callback);
+ error_code err = val.for_each_at_path_segments(path, index + 1, callback);
if (err && !internal::is_matching_failure_error(err)) { return err; }
}
}
@@ -182256,17 +178966,17 @@ inline error_code object::for_each_at_path_with_wildcard(std::string_view json_p
value val;
error_code error = find_field(key).get(val);
if (error) {
- if (error == NO_SUCH_FIELD && internal::json_path_has_wildcard(remaining_path)) {
+ if (error == NO_SUCH_FIELD && path[index].wildcard_follows) {
return SUCCESS;
}
return error;
}
- if (remaining_path.empty()) {
+ if (last) {
callback(val);
return SUCCESS;
} else {
- return val.for_each_at_path_with_wildcard(remaining_path, callback);
+ return val.for_each_at_path_segments(path, index + 1, callback);
}
}
}
@@ -182919,6 +179629,7 @@ simdjson_inline simdjson_warn_unused bool parser::string_buffer_overflow(const u
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(padded_string_view json) & noexcept {
if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
+ json.use_trailing_whitespace_as_padding();
json.remove_utf8_bom();
_document_len = json.length();
@@ -182936,6 +179647,7 @@ simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(p
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate_allow_incomplete_json(padded_string_view json) & noexcept {
if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
+ json.use_trailing_whitespace_as_padding();
json.remove_utf8_bom();
_document_len = json.length();
@@ -182991,6 +179703,7 @@ simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(c
simdjson_warn_unused simdjson_inline simdjson_result<json_iterator> parser::iterate_raw(padded_string_view json) & noexcept {
if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
+ json.use_trailing_whitespace_as_padding();
json.remove_utf8_bom();
@@ -183017,6 +179730,7 @@ inline simdjson_result<document_stream> parser::iterate_many(const char *buf, si
}
inline simdjson_result<document_stream> parser::iterate_many(padded_string_view s, size_t batch_size) noexcept {
if (!s.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
+ s.use_trailing_whitespace_as_padding();
return iterate_many(s.data(), s.length(), batch_size);
}
inline simdjson_result<document_stream> parser::iterate_many(const padded_string &s, size_t batch_size) noexcept {
@@ -183052,6 +179766,7 @@ inline simdjson_result<document_stream> parser::iterate_many(const char *buf, si
}
inline simdjson_result<document_stream> parser::iterate_many(padded_string_view s, size_t batch_size, bool allow_comma_separated) noexcept {
if (!s.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
+ s.use_trailing_whitespace_as_padding();
return iterate_many(s.data(), s.length(), batch_size, allow_comma_separated);
}
inline simdjson_result<document_stream> parser::iterate_many(const padded_string &s, size_t batch_size, bool allow_comma_separated) noexcept {
@@ -183097,6 +179812,7 @@ inline simdjson_result<document_stream> parser::iterate_many(const char *buf, si
}
inline simdjson_result<document_stream> parser::iterate_many(padded_string_view s, size_t batch_size, stream_format format) noexcept {
if (!s.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
+ s.use_trailing_whitespace_as_padding();
return iterate_many(s.data(), s.length(), batch_size, format);
}
inline simdjson_result<document_stream> parser::iterate_many(const std::string &s, size_t batch_size, stream_format format) noexcept {
@@ -183251,17 +179967,18 @@ simdjson_inline bool raw_json_string::is_free_from_unescaped_quote(const char* t
return true;
}
-
simdjson_inline bool raw_json_string::unsafe_is_equal(size_t length, std::string_view target) const noexcept {
// If we are going to call memcmp, then we must know something about the length of the raw_json_string.
- return (length >= target.size()) && (raw()[target.size()] == '"') && !memcmp(raw(), target.data(), target.size());
+ return (length >= target.size()) && (raw()[target.size()] == '"') && !memcmp(raw(), target.data(), target.size())
+ && !escapes_trailing_quote(target);
}
simdjson_inline bool raw_json_string::unsafe_is_equal(std::string_view target) const noexcept {
// Assumptions: does not contain unescaped quote characters("), and
// the raw content is quote terminated within a valid JSON string.
if(target.size() <= SIMDJSON_PADDING) {
- return (raw()[target.size()] == '"') && !memcmp(raw(), target.data(), target.size());
+ return (raw()[target.size()] == '"') && !memcmp(raw(), target.data(), target.size())
+ && !escapes_trailing_quote(target);
}
// Past SIMDJSON_PADDING we can no longer rely on the padding to keep the
// comparison in bounds, so we must stop at the quote terminating the key.
@@ -183287,7 +180004,7 @@ simdjson_inline bool raw_json_string::is_equal(std::string_view target) const no
escaping = false;
}
}
- if(r[pos] != '"') { return false; }
+ if((r[pos] != '"') || escaping) { return false; }
return true;
}
@@ -183300,7 +180017,7 @@ simdjson_inline bool raw_json_string::unsafe_is_equal(const char * target) const
for(;target[pos];pos++) {
if(r[pos] != target[pos]) { return false; }
}
- if(r[pos] != '"') { return false; }
+ if((r[pos] != '"') || escapes_trailing_quote(std::string_view(target, pos))) { return false; }
return true;
}
@@ -183325,7 +180042,7 @@ simdjson_inline bool raw_json_string::is_equal(const char* target) const noexcep
escaping = false;
}
}
- if(r[pos] != '"') { return false; }
+ if((r[pos] != '"') || escaping) { return false; }
return true;
}
@@ -183437,7 +180154,11 @@ simdjson_inline uint32_t token_iterator::peek_index(token_position position) con
return *position;
}
simdjson_inline uint32_t token_iterator::peek_length(token_position position) const noexcept {
- return *(position+1) - *position;
+ // In the last batch of a document_stream, the index past the end of the
+ // structural indexes holds the start of the truncated bytes (see
+ // document_stream::truncated_bytes()), which can come before the end of the
+ // input: a token at the end is then empty.
+ return *(position+1) > *position ? *(position+1) - *position : 0;
}
simdjson_inline uint32_t token_iterator::peek_root_length(token_position position) const noexcept {
@@ -183452,7 +180173,7 @@ simdjson_inline uint32_t token_iterator::peek_index(int32_t delta) const noexcep
return *(_position+delta);
}
simdjson_inline uint32_t token_iterator::peek_length(int32_t delta) const noexcept {
- return *(_position+delta+1) - *(_position+delta);
+ return peek_length(_position+delta);
}
simdjson_inline token_position token_iterator::position() const noexcept {
@@ -183511,6 +180232,8 @@ simdjson_inline simdjson_result<westmere::ondemand::token_iterator>::simdjson_re
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
+#include <cstring> // std::memset (zeroing the root-number scratch padding)
+
namespace simdjson {
namespace westmere {
namespace ondemand {
@@ -183557,6 +180280,10 @@ simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::start
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::started_object() noexcept {
assert_at_container_start();
#if SIMDJSON_DEVELOPMENT_CHECKS
+ // The development checks record the start of every open container in a
+ // buffer of max_depth() entries: a document nested deeper than that is
+ // rejected, as the DOM parser does.
+ if (size_t(_depth) >= _json_iter->parser->max_depth()) { return report_error(DEPTH_ERROR, "Exceeded max depth!"); }
_json_iter->set_start_position(_depth, start_position());
#endif
if (*_json_iter->peek() == '}') {
@@ -183966,6 +180693,10 @@ inline std::string value_iterator::to_string() const noexcept {
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::started_array() noexcept {
assert_at_container_start();
+#if SIMDJSON_DEVELOPMENT_CHECKS
+ // See started_object().
+ if (size_t(_depth) >= _json_iter->parser->max_depth()) { return report_error(DEPTH_ERROR, "Exceeded max depth!"); }
+#endif
if (*_json_iter->peek() == ']') {
logger::log_value(*_json_iter, "empty array");
_json_iter->return_current_and_advance();
@@ -184171,8 +180902,10 @@ simdjson_inline simdjson_result<westmere::number_type> value_iterator::get_root_
// Per https://www.exploringbinary.com/maximum-number-of-decimal-digits-in-binary-floating-point-numbers/,
// 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest
// number: -0.<fraction>e-308.
- uint8_t tmpbuf[1074+8+1+1];
- tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
+ // +SIMDJSON_PADDING: the digit scan reads up to 7 bytes past the terminator.
+ uint8_t tmpbuf[1074+8+1+1+SIMDJSON_PADDING];
+ // Null terminate, and zero the padding as allocate_padded_buffer does.
+ std::memset(tmpbuf+1074+8+1, 0, 1+SIMDJSON_PADDING);
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
if(numberparsing::check_if_integer(json, max_len)) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
@@ -184193,8 +180926,10 @@ simdjson_inline simdjson_result<number> value_iterator::get_root_number(bool che
// 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest
// number: -0.<fraction>e-308.
// NOTE: the current approach doesn't work for very big integer numbers containing more than 1074 digits.
- uint8_t tmpbuf[1074+8+1+1];
- tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
+ // +SIMDJSON_PADDING: the digit scan reads up to 7 bytes past the terminator.
+ uint8_t tmpbuf[1074+8+1+1+SIMDJSON_PADDING];
+ // Null terminate, and zero the padding as allocate_padded_buffer does.
+ std::memset(tmpbuf+1074+8+1, 0, 1+SIMDJSON_PADDING);
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
if(numberparsing::check_if_integer(json, max_len)) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
@@ -184264,10 +180999,10 @@ simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> value_iterator::g
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> value_iterator::get_root_uint64_in_string(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("uint64");
- uint8_t tmpbuf[20+1+1]{}; // <20 digits> is the longest possible unsigned integer
- tmpbuf[20+1] = '\0'; // make sure that buffer is always null terminated.
- if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 20+1)) {
- logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 20 characters");
+ uint8_t tmpbuf[20+2+1]{}; // "<20 digits>" is the longest possible unsigned integer
+ tmpbuf[20+2] = '\0'; // make sure that buffer is always null terminated.
+ if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 20+2)) {
+ logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 22 characters");
return NUMBER_ERROR;
}
auto result = numberparsing::parse_unsigned_in_string(tmpbuf);
@@ -184297,10 +181032,10 @@ simdjson_warn_unused simdjson_inline simdjson_result<int64_t> value_iterator::ge
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> value_iterator::get_root_int64_in_string(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("int64");
- uint8_t tmpbuf[20+1+1]; // -<19 digits> is the longest possible integer
- tmpbuf[20+1] = '\0'; // make sure that buffer is always null terminated.
- if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 20+1)) {
- logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 20 characters");
+ uint8_t tmpbuf[20+2+1]; // "-<19 digits>" is the longest possible integer
+ tmpbuf[20+2] = '\0'; // make sure that buffer is always null terminated.
+ if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 20+2)) {
+ logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 22 characters");
return NUMBER_ERROR;
}
@@ -184317,8 +181052,10 @@ simdjson_warn_unused simdjson_inline simdjson_result<double> value_iterator::get
// Per https://www.exploringbinary.com/maximum-number-of-decimal-digits-in-binary-floating-point-numbers/,
// 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest
// number: -0.<fraction>e-308.
- uint8_t tmpbuf[1074+8+1+1]; // +1 for null termination.
- tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
+ // +SIMDJSON_PADDING: the digit scan reads up to 7 bytes past the terminator.
+ uint8_t tmpbuf[1074+8+1+1+SIMDJSON_PADDING]; // +1 for null termination.
+ // Null terminate, and zero the padding as allocate_padded_buffer does.
+ std::memset(tmpbuf+1074+8+1, 0, 1+SIMDJSON_PADDING);
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters");
return NUMBER_ERROR;
@@ -184337,8 +181074,10 @@ simdjson_warn_unused simdjson_inline simdjson_result<double> value_iterator::get
// Per https://www.exploringbinary.com/maximum-number-of-decimal-digits-in-binary-floating-point-numbers/,
// 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest
// number: -0.<fraction>e-308.
- uint8_t tmpbuf[1074+8+1+1]; // +1 for null termination.
- tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
+ // +SIMDJSON_PADDING: the digit scan reads up to 7 bytes past the terminator.
+ uint8_t tmpbuf[1074+8+1+1+SIMDJSON_PADDING]; // +1 for null termination.
+ // Null terminate, and zero the padding as allocate_padded_buffer does.
+ std::memset(tmpbuf+1074+8+1, 0, 1+SIMDJSON_PADDING);
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters");
return NUMBER_ERROR;
@@ -184357,8 +181096,10 @@ simdjson_warn_unused simdjson_inline simdjson_result<float> value_iterator::get_
// We use the same buffer size as get_root_double: the number of significant
// digits that matter is smaller for binary32, but the JSON document may still
// spell out a long number that we must parse (and round) faithfully.
- uint8_t tmpbuf[1074+8+1+1]; // +1 for null termination.
- tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
+ // +SIMDJSON_PADDING: the digit scan reads up to 7 bytes past the terminator.
+ uint8_t tmpbuf[1074+8+1+1+SIMDJSON_PADDING]; // +1 for null termination.
+ // Null terminate, and zero the padding as allocate_padded_buffer does.
+ std::memset(tmpbuf+1074+8+1, 0, 1+SIMDJSON_PADDING);
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters");
return NUMBER_ERROR;
@@ -184374,8 +181115,10 @@ simdjson_warn_unused simdjson_inline simdjson_result<float> value_iterator::get_
simdjson_warn_unused simdjson_inline simdjson_result<float> value_iterator::get_root_float_in_string(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("float");
- uint8_t tmpbuf[1074+8+1+1]; // +1 for null termination.
- tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
+ // +SIMDJSON_PADDING: the digit scan reads up to 7 bytes past the terminator.
+ uint8_t tmpbuf[1074+8+1+1+SIMDJSON_PADDING]; // +1 for null termination.
+ // Null terminate, and zero the padding as allocate_padded_buffer does.
+ std::memset(tmpbuf+1074+8+1, 0, 1+SIMDJSON_PADDING);
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters");
return NUMBER_ERROR;
@@ -184396,7 +181139,7 @@ simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::get_r
// "true" and "false" are 4 and 5 characters long, respectively.
bool value_true = (max_len >= 4 && !atomparsing::str4ncmp(json, "true") &&
(max_len == 4 || jsoncharutils::is_structural_or_whitespace(json[4])));
- bool value_false = (max_len >= 5 && !atomparsing::str4ncmp(json, "false") &&
+ bool value_false = (max_len >= 5 && !atomparsing::str4ncmp(json, "fals") && json[4] == 'e' &&
(max_len == 5 || jsoncharutils::is_structural_or_whitespace(json[5])));
if(value_true == false && value_false == false) { return incorrect_type_error("Not a boolean"); }
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
@@ -185036,49 +181779,6 @@ namespace json_path {
using ::simdjson::westmere::ondemand::value;
-// Path step types
-enum class step_type {
- field, // .field_name or ["field_name"]
- array_index // [index]
-};
-
-// Represents a single step in a JSON path
-template<std::size_t N>
-struct path_step {
- step_type type;
- char key[N]; // Field name (empty for array indices)
- std::size_t index; // Array index (0 for field access)
-
- constexpr path_step(step_type t, const char (&k)[N], std::size_t idx = 0)
- : type(t), index(idx) {
- for (std::size_t i = 0; i < N; ++i) {
- key[i] = k[i];
- }
- }
-
- constexpr std::string_view key_view() const {
- return {key, N - 1};
- }
-};
-
-// Helper to create field step
-template<std::size_t N>
-consteval auto make_field_step(const char (&name)[N]) {
- return path_step<N>(step_type::field, name, 0);
-}
-
-// Helper to create array index step
-consteval auto make_index_step(std::size_t idx) {
- return path_step<1>(step_type::array_index, "", idx);
-}
-
-// Parse state for compile-time JSON path parsing
-struct parse_result {
- bool success;
- std::size_t pos;
- std::string_view error_msg;
-};
-
// Compile-time JSON path parser
// Supports subset: .field, ["field"], [index], nested combinations
template<constevalutil::fixed_string Path>
@@ -185092,77 +181792,6 @@ struct json_path_parser {
}
return 0;
}
-
- // Count the number of steps in the path at compile time
- static consteval std::size_t count_steps() {
- std::size_t count = 0;
- std::size_t i = skip_root();
-
- while (i < path_str.size()) {
- if (path_str[i] == '.') {
- // Field access: .field
- ++i;
- if (i >= path_str.size()) break;
-
- // Skip field name
- while (i < path_str.size() && path_str[i] != '.' && path_str[i] != '[') {
- ++i;
- }
- ++count;
- } else if (path_str[i] == '[') {
- // Array or bracket notation
- ++i;
- if (i >= path_str.size()) break;
-
- if (path_str[i] == '"' || path_str[i] == '\'') {
- // Field access: ["field"] or ['field']
- char quote = path_str[i];
- ++i;
- while (i < path_str.size() && path_str[i] != quote) {
- ++i;
- }
- if (i < path_str.size()) ++i; // skip closing quote
- if (i < path_str.size() && path_str[i] == ']') ++i;
- } else {
- // Array index: [0], [123]
- while (i < path_str.size() && path_str[i] != ']') {
- ++i;
- }
- if (i < path_str.size()) ++i; // skip ]
- }
- ++count;
- } else {
- ++i;
- }
- }
-
- return count;
- }
-
- // Parse a field name at compile time
- static consteval std::size_t parse_field_name(std::size_t start, char* out, std::size_t max_len) {
- std::size_t len = 0;
- std::size_t i = start;
-
- while (i < path_str.size() && path_str[i] != '.' && path_str[i] != '[' && len < max_len - 1) {
- out[len++] = path_str[i++];
- }
- out[len] = '\0';
- return i;
- }
-
- // Parse an array index at compile time
- static consteval std::pair<std::size_t, std::size_t> parse_array_index(std::size_t start) {
- std::size_t index = 0;
- std::size_t i = start;
-
- while (i < path_str.size() && path_str[i] >= '0' && path_str[i] <= '9') {
- index = index * 10 + (path_str[i] - '0');
- ++i;
- }
-
- return {i, index};
- }
};
// Compile-time path accessor generator
@@ -185171,7 +181800,6 @@ struct path_accessor {
using value = ::simdjson::westmere::ondemand::value;
static constexpr auto parser = json_path_parser<Path>();
- static constexpr std::size_t num_steps = parser.count_steps();
static constexpr std::string_view path_view = Path.view();
// Compile-time accessor generation
@@ -185286,93 +181914,6 @@ private:
return current_type;
}
-private:
- // Walk path and extract directly into final field using compile-time reflection
- template<std::meta::info CurrentType, std::size_t PathPos, typename TargetType>
- static inline error_code extract_with_reflection(simdjson_result<value> current, TargetType& target_ref) noexcept {
- if (current.error()) return current.error();
-
- // Base case: end of path - extract into target
- if constexpr (PathPos >= path_view.size()) {
- return current.get(target_ref);
- }
- // Field access: .field_name
- else if constexpr (path_view[PathPos] == '.') {
- constexpr auto field_info = parse_next_field(PathPos);
- constexpr std::string_view field_name = std::get<0>(field_info);
- constexpr std::size_t next_pos = std::get<1>(field_info);
-
- constexpr auto member_info = find_member_by_name(CurrentType, field_name);
- static_assert(member_info != ^^void, "Field not found in struct");
-
- constexpr auto member_type = std::meta::type_of(member_info);
-
- auto obj_result = current.get_object();
- if (obj_result.error()) return obj_result.error();
- auto obj = obj_result.value_unsafe();
- auto field_value = obj.find_field_unordered(field_name);
-
- if constexpr (next_pos >= path_view.size()) {
- return field_value.get(target_ref);
- } else {
- return extract_with_reflection<member_type, next_pos>(field_value, target_ref);
- }
- }
- // Bracket notation: [index] or ["field"]
- else if constexpr (path_view[PathPos] == '[') {
- constexpr auto bracket_info = parse_bracket(PathPos);
- constexpr bool is_field = std::get<0>(bracket_info);
- constexpr std::size_t next_pos = std::get<2>(bracket_info);
-
- if constexpr (is_field) {
- constexpr std::string_view field_name = std::get<1>(bracket_info);
- constexpr auto member_info = find_member_by_name(CurrentType, field_name);
- static_assert(member_info != ^^void, "Field not found in struct");
- constexpr auto member_type = std::meta::type_of(member_info);
-
- auto obj_result = current.get_object();
- if (obj_result.error()) return obj_result.error();
- auto obj = obj_result.value_unsafe();
- auto field_value = obj.find_field_unordered(field_name);
-
- if constexpr (next_pos >= path_view.size()) {
- return field_value.get(target_ref);
- } else {
- return extract_with_reflection<member_type, next_pos>(field_value, target_ref);
- }
- } else {
- constexpr std::size_t index = std::get<3>(bracket_info);
- constexpr auto elem_type = get_element_type_reflected(CurrentType);
- static_assert(elem_type != ^^void, "Could not determine array element type");
-
- auto arr_result = current.get_array();
- if (arr_result.error()) return arr_result.error();
- auto arr = arr_result.value_unsafe();
- auto elem_value = arr.at(index);
-
- if constexpr (next_pos >= path_view.size()) {
- return elem_value.get(target_ref);
- } else {
- return extract_with_reflection<elem_type, next_pos>(elem_value, target_ref);
- }
- }
- }
- // Skip unexpected characters and continue
- else {
- return extract_with_reflection<CurrentType, PathPos + 1>(current, target_ref);
- }
- }
-
- // Find member by name in reflected type
- static consteval std::meta::info find_member_by_name(std::meta::info type_refl, std::string_view name) {
- auto members = std::meta::nonstatic_data_members_of(type_refl, std::meta::access_context::unchecked());
- for (auto mem : members) {
- if (std::meta::identifier_of(mem) == name) {
- return mem;
- }
- }
- }
-
// Generate compile-time accessor code by walking the path
template<std::size_t PathPos>
static inline simdjson_result<value> access_impl(simdjson_result<value> current) noexcept {
@@ -185502,55 +182043,6 @@ public:
}
private:
- // Check if type has member with given name
- template<typename Type>
- static consteval bool has_member(std::string_view member_name) {
- constexpr auto members = std::meta::nonstatic_data_members_of(^^Type, std::meta::access_context::unchecked());
- for (auto mem : members) {
- if (std::meta::identifier_of(mem) == member_name) {
- return true;
- }
- }
- return false;
- }
-
- // Get type of member by name
- template<typename Type>
- static consteval auto get_member_type(std::string_view member_name) {
- constexpr auto members = std::meta::nonstatic_data_members_of(^^Type, std::meta::access_context::unchecked());
- for (auto mem : members) {
- if (std::meta::identifier_of(mem) == member_name) {
- return std::meta::type_of(mem);
- }
- }
- return ^^void;
- }
-
- // Check if non-reflected type is array-like
- template<typename Type>
- static consteval bool is_container_type() {
- using BaseType = std::remove_cvref_t<Type>;
- if constexpr (requires { typename BaseType::value_type; }) {
- return true;
- }
- if constexpr (std::is_array_v<BaseType>) {
- return true;
- }
- return false;
- }
-
- // Extract element type from non-reflected container
- template<typename Type>
- using extract_element_type = std::conditional_t<
- requires { typename std::remove_cvref_t<Type>::value_type; },
- typename std::remove_cvref_t<Type>::value_type,
- std::conditional_t<
- std::is_array_v<std::remove_cvref_t<Type>>,
- std::remove_extent_t<std::remove_cvref_t<Type>>,
- void
- >
- >;
-
// Validate path matches struct definition
static consteval bool validate_path() {
if constexpr (!std::is_class_v<T>) {
@@ -186519,30 +183011,6 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin
}
-struct escaping {
- static constexpr uint32_t BYTES_PROCESSED = 16;
- simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
-
- simdjson_inline bool has_escape() { return escape_bits != 0; }
- simdjson_inline int escape_index() { return trailing_zeroes(escape_bits); }
-
- uint64_t escape_bits;
-}; // struct escaping
-
-
-
-simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
- static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes");
- simd8<uint8_t> v(src);
- v.store(dst);
- simd8<bool> is_quote = (v == '"');
- simd8<bool> is_backslash = (v == '\\');
- simd8<bool> is_control = (v < 32);
- return {
- static_cast<uint64_t>((is_backslash | is_quote | is_control).to_bitmask())
- };
-}
-
} // unnamed namespace
} // namespace lsx
} // namespace simdjson
@@ -186754,11 +183222,6 @@ protected:
public:
simdjson_inline value_iterator() noexcept = default;
- /**
- * Denote that we're starting a document.
- */
- simdjson_inline void start_document() noexcept;
-
/**
* Skips a non-iterated or partially-iterated JSON value, whether it is a scalar, array or object.
*
@@ -187159,16 +183622,7 @@ protected:
simdjson_warn_unused simdjson_inline error_code start_container(uint8_t start_char, const char *incorrect_type_message, const char *type) noexcept;
simdjson_warn_unused simdjson_inline error_code end_container() noexcept;
- /**
- * Advance to a place expecting a value (increasing depth).
- *
- * @return The current token (the one left behind).
- * @error TAPE_ERROR If the document ended early.
- */
- simdjson_inline simdjson_result<const uint8_t *> advance_to_value() noexcept;
-
simdjson_warn_unused simdjson_inline error_code incorrect_type_error(const char *message) const noexcept;
- simdjson_warn_unused simdjson_inline error_code error_unless_more_tokens(uint32_t tokens=1) const noexcept;
simdjson_inline bool is_at_start() const noexcept;
/**
@@ -187251,6 +183705,7 @@ public:
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
+/* amalgamation skipped (editor-only): #include "simdjson/jsonpathutil.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/deserialize.h" */
/* amalgamation skipped (editor-only): #include <vector> */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
@@ -188104,11 +184559,19 @@ protected:
*/
simdjson_inline simdjson_result<object> start_or_resume_object() noexcept;
+ /**
+ * The recursive step of for_each_at_path_with_wildcard: matches
+ * path[index] at this level and what follows below it.
+ */
+ template <typename Func>
+ inline error_code for_each_at_path_segments(const std::vector<simdjson::internal::json_path_segment> &path, size_t index, Func &callback) noexcept;
+
// simdjson_inline void log_value(const char *type) const noexcept;
// simdjson_inline void log_error(const char *message) const noexcept;
value_iterator iter{};
+ friend class array;
friend class document;
friend class array_iterator;
friend class field;
@@ -188797,6 +185260,7 @@ public:
/**
* Take an input in json containing max_len characters and attempt to copy it over to tmpbuf, a buffer with
* N bytes of capacity. It will return false if N is too small (smaller than max_len) of if it is zero.
+ * Trailing whitespace, commas, and record separators (0x1E) do not count toward max_len.
* The buffer (tmpbuf) is padded with space characters.
*/
simdjson_warn_unused simdjson_inline bool copy_to_buffer(const uint8_t *json, uint32_t max_len, uint8_t *tmpbuf, size_t N) noexcept;
@@ -188829,8 +185293,11 @@ public:
/**
* Updates this json iterator so that it is back at the beginning of the document,
* as if it had just been created.
+ *
+ * An iterator that is no longer alive (e.g., abandoned after a fatal error) has no
+ * document to go back to: it is left as is and its error is returned.
*/
- inline void rewind() noexcept;
+ simdjson_inline error_code rewind() noexcept;
/**
* This checks whether the {,},[,] are balanced so that the document
* ends with proper zero depth. This requires scanning the whole document
@@ -189185,6 +185652,28 @@ public:
private:
+ /**
+ * Returns true if target ends with an odd number of backslashes. The last one
+ * then escapes the quote that follows the matching bytes in the raw string, so
+ * that quote does not end the string. Not inlined: callers check the last byte
+ * first, and keys ending in a backslash are rare.
+ */
+ static simdjson_never_inline bool ends_with_unpaired_backslash(std::string_view target) noexcept {
+ size_t backslash_count{0};
+ while(backslash_count < target.size() && target[target.size() - 1 - backslash_count] == '\\') {
+ backslash_count++;
+ }
+ return backslash_count % 2 == 1;
+ }
+
+ /**
+ * True when a byte-for-byte match must be rejected because target escapes the
+ * closing quote. The last-byte test keeps the counting loop off the common path.
+ */
+ static simdjson_inline bool escapes_trailing_quote(std::string_view target) noexcept {
+ return simdjson_unlikely(!target.empty() && target[target.size() - 1] == '\\')
+ && ends_with_unpaired_backslash(target);
+ }
/**
* This will set the inner pointer to zero, effectively making
@@ -189758,6 +186247,7 @@ public:
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
+/* amalgamation skipped (editor-only): #include "simdjson/jsonpathutil.h" */
/* amalgamation skipped (editor-only): #include <vector> */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
@@ -189950,6 +186440,13 @@ protected:
*/
simdjson_warn_unused simdjson_inline error_code consume() noexcept;
+ /**
+ * The recursive step of for_each_at_path_with_wildcard: matches
+ * path[index] at this level and what follows below it.
+ */
+ template <typename Func>
+ inline error_code for_each_at_path_segments(const std::vector<simdjson::internal::json_path_segment> &path, size_t index, Func &callback) noexcept;
+
/**
* Begin array iteration.
*
@@ -190959,6 +187456,9 @@ public:
* Reset the iterator inside the document instance so we are pointing back at the
* beginning of the document, as if it had just been created. It invalidates all
* values, objects and arrays that you have created so far (including unescaped strings).
+ *
+ * A document that is no longer alive (see is_alive()) cannot be rewound: it is
+ * left as is.
*/
inline void rewind() noexcept;
/**
@@ -191032,6 +187532,8 @@ public:
* - INCORRECT_TYPE if a non-integer is used to access an array
* - INVALID_JSON_POINTER if the JSON pointer is invalid and cannot be parsed
* - SCALAR_DOCUMENT_AS_VALUE if the json_pointer is empty and the document is not a scalar (see is_scalar() function).
+ * - the fatal error (e.g., TAPE_ERROR) that ended an earlier call, as the document can
+ * no longer be rewound (see is_alive()).
*/
simdjson_inline simdjson_result<value> at_pointer(std::string_view json_pointer) noexcept;
@@ -191055,6 +187557,8 @@ public:
* - NO_SUCH_FIELD if a field does not exist in an object
* - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length
* - INCORRECT_TYPE if a non-integer is used to access an array
+ * - the fatal error (e.g., TAPE_ERROR) that ended an earlier call, as the document can
+ * no longer be rewound (see is_alive()).
*/
simdjson_inline simdjson_result<value> at_path(std::string_view json_path) noexcept;
@@ -191081,7 +187585,9 @@ public:
/**
* Consumes the document and returns a string_view instance corresponding to the
* document as represented in JSON. It points inside the original byte array containing
- * the JSON document.
+ * the JSON document. It starts at the first byte of the document and ends at its last
+ * byte: it does not include the white space that follows the document, nor, in a
+ * document_stream, the separators or the documents that follow it.
*/
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
@@ -191408,134 +187914,134 @@ public:
using lsx::implementation_simdjson_result_base<lsx::ondemand::document>::operator*;
using lsx::implementation_simdjson_result_base<lsx::ondemand::document>::operator->;
- template <class T, typename std::enable_if<std::is_same<T, lsx::ondemand::document>::value == false>::type>
- explicit simdjson_inline operator T() noexcept(false);
- simdjson_inline operator lsx::ondemand::array() & noexcept(false);
- simdjson_inline operator lsx::ondemand::object() & noexcept(false);
- explicit simdjson_inline operator uint64_t() noexcept(false);
- explicit simdjson_inline operator int64_t() noexcept(false);
- explicit simdjson_inline operator double() noexcept(false);
- explicit simdjson_inline operator std::string_view() noexcept(false);
- explicit simdjson_inline operator lsx::ondemand::raw_json_string() noexcept(false);
- explicit simdjson_inline operator bool() noexcept(false);
- simdjson_inline operator lsx::ondemand::value() noexcept(false);
-#endif
- simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
- simdjson_inline simdjson_result<size_t> count_fields() & noexcept;
- simdjson_inline simdjson_result<lsx::ondemand::value> at(size_t index) & noexcept;
- simdjson_inline simdjson_result<lsx::ondemand::array_iterator> begin() & noexcept;
- simdjson_inline simdjson_result<lsx::ondemand::array_iterator> end() & noexcept;
- simdjson_inline simdjson_result<lsx::ondemand::value> find_field(std::string_view key) & noexcept;
- simdjson_inline simdjson_result<lsx::ondemand::value> find_field(const char *key) & noexcept;
- simdjson_inline simdjson_result<lsx::ondemand::value> operator[](std::string_view key) & noexcept;
- simdjson_inline simdjson_result<lsx::ondemand::value> operator[](const char *key) & noexcept;
- simdjson_result<lsx::ondemand::value> operator[](int) & noexcept = delete;
- simdjson_inline simdjson_result<lsx::ondemand::value> find_field_unordered(std::string_view key) & noexcept;
- simdjson_inline simdjson_result<lsx::ondemand::value> find_field_unordered(const char *key) & noexcept;
- simdjson_inline simdjson_result<lsx::ondemand::json_type> type() noexcept;
- simdjson_inline simdjson_result<bool> is_scalar() noexcept;
- simdjson_inline simdjson_result<bool> is_string() noexcept;
- simdjson_inline simdjson_result<const char *> current_location() noexcept;
- simdjson_inline int32_t current_depth() const noexcept;
- simdjson_inline bool at_end() const noexcept;
- simdjson_inline bool is_negative() noexcept;
- simdjson_inline simdjson_result<bool> is_integer() noexcept;
- simdjson_inline simdjson_result<lsx::number_type> get_number_type() noexcept;
- simdjson_inline simdjson_result<lsx::ondemand::number> get_number() noexcept;
- /** @copydoc simdjson_inline std::string_view document::raw_json_token() const noexcept */
- simdjson_inline simdjson_result<std::string_view> raw_json_token() noexcept;
-
- simdjson_inline simdjson_result<lsx::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
- simdjson_inline simdjson_result<lsx::ondemand::value> at_path(std::string_view json_path) noexcept;
-#if SIMDJSON_SUPPORTS_CONCEPTS
- template <typename Func>
- requires std::invocable<Func, lsx::ondemand::value>
-#else
- template <typename Func>
-#endif
- simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
-#if SIMDJSON_STATIC_REFLECTION
- template<constevalutil::fixed_string... FieldNames, typename T>
- requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
- simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept;
-#endif // SIMDJSON_STATIC_REFLECTION
-};
-
-
-} // namespace simdjson
-
-
-
-namespace simdjson {
-
-template<>
-struct simdjson_result<lsx::ondemand::document_reference> : public lsx::implementation_simdjson_result_base<lsx::ondemand::document_reference> {
-public:
- simdjson_inline simdjson_result(lsx::ondemand::document_reference value, error_code error) noexcept;
- simdjson_inline simdjson_result() noexcept = default;
- simdjson_inline error_code rewind() noexcept;
-
- simdjson_inline simdjson_result<lsx::ondemand::array> get_array() & noexcept;
- simdjson_inline simdjson_result<lsx::ondemand::object> get_object() & noexcept;
- simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
- simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
- simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
- simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
- simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
- simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
- simdjson_inline simdjson_result<uint16_t> get_uint16() noexcept;
- simdjson_inline simdjson_result<int16_t> get_int16() noexcept;
- simdjson_inline simdjson_result<uint8_t> get_uint8() noexcept;
- simdjson_inline simdjson_result<int8_t> get_int8() noexcept;
- simdjson_inline simdjson_result<double> get_double() noexcept;
- simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
- simdjson_inline simdjson_result<float> get_float() noexcept;
- simdjson_inline simdjson_result<float> get_float_in_string() noexcept;
-#if SIMDJSON_SUPPORTS_FLOAT32_T
- simdjson_inline simdjson_result<std::float32_t> get_float32() noexcept;
-#endif // SIMDJSON_SUPPORTS_FLOAT32_T
-#if SIMDJSON_SUPPORTS_FLOAT64_T
- simdjson_inline simdjson_result<std::float64_t> get_float64() noexcept;
-#endif // SIMDJSON_SUPPORTS_FLOAT64_T
- simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
-#if SIMDJSON_SUPPORTS_CHAR8_T
- simdjson_inline simdjson_result<std::u8string_view> get_u8string(bool allow_replacement = false) noexcept;
-#endif // SIMDJSON_SUPPORTS_CHAR8_T
- template <typename string_type>
- simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
- simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
- simdjson_inline simdjson_result<lsx::ondemand::raw_json_string> get_raw_json_string() noexcept;
- simdjson_inline simdjson_result<bool> get_bool() noexcept;
- simdjson_inline simdjson_result<lsx::ondemand::value> get_value() noexcept;
- simdjson_inline simdjson_result<bool> is_null() noexcept;
-
- template<typename T> simdjson_inline simdjson_result<T> get() &
-#if SIMDJSON_SUPPORTS_CONCEPTS
- noexcept(nothrow_gettable<T, lsx::ondemand::document_reference>);
-#else
- noexcept;
-#endif
- template<typename T> simdjson_inline simdjson_result<T> get() &&
-#if SIMDJSON_SUPPORTS_CONCEPTS
- noexcept(nothrow_gettable<T, lsx::ondemand::document_reference>);
-#else
- noexcept;
-#endif
-
- template<typename T> simdjson_inline error_code get(T &out) &
-#if SIMDJSON_SUPPORTS_CONCEPTS
- noexcept(nothrow_gettable<T, lsx::ondemand::document_reference>);
-#else
- noexcept;
-#endif
- template<typename T> simdjson_inline error_code get(T &out) &&
-#if SIMDJSON_SUPPORTS_CONCEPTS
- noexcept(nothrow_gettable<T, lsx::ondemand::document_reference>);
-#else
- noexcept;
-#endif
-#if SIMDJSON_EXCEPTIONS
- template <class T>
+ simdjson_inline operator lsx::ondemand::array() & noexcept(false);
+ simdjson_inline operator lsx::ondemand::object() & noexcept(false);
+ explicit simdjson_inline operator uint64_t() noexcept(false);
+ explicit simdjson_inline operator int64_t() noexcept(false);
+ explicit simdjson_inline operator double() noexcept(false);
+ explicit simdjson_inline operator std::string_view() noexcept(false);
+ explicit simdjson_inline operator lsx::ondemand::raw_json_string() noexcept(false);
+ explicit simdjson_inline operator bool() noexcept(false);
+ simdjson_inline operator lsx::ondemand::value() noexcept(false);
+#endif
+ simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
+ simdjson_inline simdjson_result<size_t> count_fields() & noexcept;
+ simdjson_inline simdjson_result<lsx::ondemand::value> at(size_t index) & noexcept;
+ simdjson_inline simdjson_result<lsx::ondemand::array_iterator> begin() & noexcept;
+ simdjson_inline simdjson_result<lsx::ondemand::array_iterator> end() & noexcept;
+ simdjson_inline simdjson_result<lsx::ondemand::value> find_field(std::string_view key) & noexcept;
+ simdjson_inline simdjson_result<lsx::ondemand::value> find_field(const char *key) & noexcept;
+ simdjson_inline simdjson_result<lsx::ondemand::value> operator[](std::string_view key) & noexcept;
+ simdjson_inline simdjson_result<lsx::ondemand::value> operator[](const char *key) & noexcept;
+ simdjson_result<lsx::ondemand::value> operator[](int) & noexcept = delete;
+ simdjson_inline simdjson_result<lsx::ondemand::value> find_field_unordered(std::string_view key) & noexcept;
+ simdjson_inline simdjson_result<lsx::ondemand::value> find_field_unordered(const char *key) & noexcept;
+ simdjson_inline simdjson_result<lsx::ondemand::json_type> type() noexcept;
+ simdjson_inline simdjson_result<bool> is_scalar() noexcept;
+ simdjson_inline simdjson_result<bool> is_string() noexcept;
+ simdjson_inline simdjson_result<const char *> current_location() noexcept;
+ simdjson_inline int32_t current_depth() const noexcept;
+ simdjson_inline bool at_end() const noexcept;
+ simdjson_inline bool is_negative() noexcept;
+ simdjson_inline simdjson_result<bool> is_integer() noexcept;
+ simdjson_inline simdjson_result<lsx::number_type> get_number_type() noexcept;
+ simdjson_inline simdjson_result<lsx::ondemand::number> get_number() noexcept;
+ /** @copydoc simdjson_inline std::string_view document::raw_json_token() const noexcept */
+ simdjson_inline simdjson_result<std::string_view> raw_json_token() noexcept;
+ /** @copydoc simdjson_inline simdjson_result<std::string_view> document::raw_json() noexcept */
+ simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
+
+ simdjson_inline simdjson_result<lsx::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
+ simdjson_inline simdjson_result<lsx::ondemand::value> at_path(std::string_view json_path) noexcept;
+#if SIMDJSON_SUPPORTS_CONCEPTS
+ template <typename Func>
+ requires std::invocable<Func, lsx::ondemand::value>
+#else
+ template <typename Func>
+#endif
+ simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
+#if SIMDJSON_STATIC_REFLECTION
+ template<constevalutil::fixed_string... FieldNames, typename T>
+ requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
+ simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept;
+#endif // SIMDJSON_STATIC_REFLECTION
+};
+
+
+} // namespace simdjson
+
+
+
+namespace simdjson {
+
+template<>
+struct simdjson_result<lsx::ondemand::document_reference> : public lsx::implementation_simdjson_result_base<lsx::ondemand::document_reference> {
+public:
+ simdjson_inline simdjson_result(lsx::ondemand::document_reference value, error_code error) noexcept;
+ simdjson_inline simdjson_result() noexcept = default;
+ simdjson_inline error_code rewind() noexcept;
+
+ simdjson_inline simdjson_result<lsx::ondemand::array> get_array() & noexcept;
+ simdjson_inline simdjson_result<lsx::ondemand::object> get_object() & noexcept;
+ simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
+ simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
+ simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
+ simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
+ simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
+ simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
+ simdjson_inline simdjson_result<uint16_t> get_uint16() noexcept;
+ simdjson_inline simdjson_result<int16_t> get_int16() noexcept;
+ simdjson_inline simdjson_result<uint8_t> get_uint8() noexcept;
+ simdjson_inline simdjson_result<int8_t> get_int8() noexcept;
+ simdjson_inline simdjson_result<double> get_double() noexcept;
+ simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
+ simdjson_inline simdjson_result<float> get_float() noexcept;
+ simdjson_inline simdjson_result<float> get_float_in_string() noexcept;
+#if SIMDJSON_SUPPORTS_FLOAT32_T
+ simdjson_inline simdjson_result<std::float32_t> get_float32() noexcept;
+#endif // SIMDJSON_SUPPORTS_FLOAT32_T
+#if SIMDJSON_SUPPORTS_FLOAT64_T
+ simdjson_inline simdjson_result<std::float64_t> get_float64() noexcept;
+#endif // SIMDJSON_SUPPORTS_FLOAT64_T
+ simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
+#if SIMDJSON_SUPPORTS_CHAR8_T
+ simdjson_inline simdjson_result<std::u8string_view> get_u8string(bool allow_replacement = false) noexcept;
+#endif // SIMDJSON_SUPPORTS_CHAR8_T
+ template <typename string_type>
+ simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
+ simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
+ simdjson_inline simdjson_result<lsx::ondemand::raw_json_string> get_raw_json_string() noexcept;
+ simdjson_inline simdjson_result<bool> get_bool() noexcept;
+ simdjson_inline simdjson_result<lsx::ondemand::value> get_value() noexcept;
+ simdjson_inline simdjson_result<bool> is_null() noexcept;
+
+ template<typename T> simdjson_inline simdjson_result<T> get() &
+#if SIMDJSON_SUPPORTS_CONCEPTS
+ noexcept(nothrow_gettable<T, lsx::ondemand::document_reference>);
+#else
+ noexcept;
+#endif
+ template<typename T> simdjson_inline simdjson_result<T> get() &&
+#if SIMDJSON_SUPPORTS_CONCEPTS
+ noexcept(nothrow_gettable<T, lsx::ondemand::document_reference>);
+#else
+ noexcept;
+#endif
+
+ template<typename T> simdjson_inline error_code get(T &out) &
+#if SIMDJSON_SUPPORTS_CONCEPTS
+ noexcept(nothrow_gettable<T, lsx::ondemand::document_reference>);
+#else
+ noexcept;
+#endif
+ template<typename T> simdjson_inline error_code get(T &out) &&
+#if SIMDJSON_SUPPORTS_CONCEPTS
+ noexcept(nothrow_gettable<T, lsx::ondemand::document_reference>);
+#else
+ noexcept;
+#endif
+#if SIMDJSON_EXCEPTIONS
+ template <class T>
explicit simdjson_inline operator T() noexcept(false);
simdjson_inline operator lsx::ondemand::array() & noexcept(false);
simdjson_inline operator lsx::ondemand::object() & noexcept(false);
@@ -191570,6 +188076,8 @@ public:
simdjson_inline simdjson_result<lsx::ondemand::number> get_number() noexcept;
/** @copydoc simdjson_inline std::string_view document_reference::raw_json_token() const noexcept */
simdjson_inline simdjson_result<std::string_view> raw_json_token() noexcept;
+ /** @copydoc simdjson_inline simdjson_result<std::string_view> document::raw_json() noexcept */
+ simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
simdjson_inline simdjson_result<lsx::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
simdjson_inline simdjson_result<lsx::ondemand::value> at_path(std::string_view json_path) noexcept;
@@ -193531,6 +190039,7 @@ concept key_selector_type = key_selector_detail::is_key_selector<T>::value;
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
+/* amalgamation skipped (editor-only): #include "simdjson/jsonpathutil.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/key_selector.h" */
/* amalgamation skipped (editor-only): #include <vector> */
/* amalgamation skipped (editor-only): #if SIMDJSON_STATIC_REFLECTION && SIMDJSON_SUPPORTS_CONCEPTS */
@@ -194075,6 +190584,13 @@ protected:
simdjson_warn_unused simdjson_inline error_code find_field_raw(const std::string_view key) noexcept;
+ /**
+ * The recursive step of for_each_at_path_with_wildcard: matches
+ * path[index] at this level and what follows below it.
+ */
+ template <typename Func>
+ inline error_code for_each_at_path_segments(const std::vector<simdjson::internal::json_path_segment> &path, size_t index, Func &callback) noexcept;
+
value_iterator iter{};
#if SIMDJSON_DEVELOPMENT_CHECKS
bool locked{false};
@@ -194939,6 +191455,14 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) {
std::is_default_constructible_v<element_type>,
"The specified type inside the unique_ptr must default constructible.");
+ // A null pointer is serialized as null.
+ bool is_null_value;
+ SIMDJSON_TRY( val.is_null().get(is_null_value) );
+ if (is_null_value) {
+ out.reset();
+ return SUCCESS;
+ }
+
// Own the allocation before get(): a user tag_invoke may throw.
std::unique_ptr<element_type> ptr(new (std::nothrow) element_type());
if (!ptr) {
@@ -194987,6 +191511,8 @@ constexpr bool user_defined_type = (std::is_class_v<T>
#endif // SIMDJSON_SUPPORTS_CHAR8_T
&& !concepts::optional_type<T> &&
!concepts::appendable_containers<T>
+// Smart pointers are handled by the generic smart-pointer tag_invoke.
+&& !concepts::smart_pointer<T>
// simdjson's own types (array, object, value, raw_json_string, number,
// document, document_reference) have dedicated get<T>() specializations and
// must never go through reflection.
@@ -195579,251 +192105,8 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept {
#endif
}
-template <typename simdjson_value, typename T>
- requires(user_defined_type<std::remove_cvref_t<T>>)
-error_code tag_invoke(deserialize_tag, simdjson_value &val, std::unique_ptr<T> &out) noexcept(false) {
- std::unique_ptr<T> ptr(new (std::nothrow) T());
- if (!ptr) {
- return MEMALLOC;
- }
- SIMDJSON_TRY(val.get(*ptr));
- out = std::move(ptr);
- return SUCCESS;
-}
-
-template <typename simdjson_value, typename T>
- requires(user_defined_type<std::remove_cvref_t<T>>)
-error_code tag_invoke(deserialize_tag, simdjson_value &val, std::shared_ptr<T> &out) noexcept(false) {
- std::shared_ptr<T> ptr(new (std::nothrow) T());
- if (!ptr) {
- return MEMALLOC;
- }
- SIMDJSON_TRY(val.get(*ptr));
- out = std::move(ptr);
- return SUCCESS;
-}
-
#endif // SIMDJSON_STATIC_REFLECTION
-////////////////////////////////////////
-// Unique pointers
-////////////////////////////////////////
-error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<bool> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_unique<bool>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_bool().get(*out));
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<int64_t> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_unique<int64_t>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_int64().get(*out));
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<uint64_t> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_unique<uint64_t>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_uint64().get(*out));
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<double> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_unique<double>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_double().get(*out));
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<std::string_view> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_unique<std::string_view>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_string().get(*out));
- return SUCCESS;
-}
-
-
-////////////////////////////////////////
-// Shared pointers
-////////////////////////////////////////
-error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<bool> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_shared<bool>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_bool().get(*out));
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<int64_t> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_shared<int64_t>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_int64().get(*out));
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<uint64_t> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_shared<uint64_t>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_uint64().get(*out));
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<double> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_shared<double>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_double().get(*out));
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<std::string_view> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_shared<std::string_view>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_string().get(*out));
- return SUCCESS;
-}
-
-
-////////////////////////////////////////
-// Explicit optional specializations
-////////////////////////////////////////
-
-////////////////////////////////////////
-// Explicit smart pointer specializations for string and int types
-////////////////////////////////////////
-error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<std::string> &out) noexcept {
- // Check if the value is null
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset(); // Set to nullptr
- return SUCCESS;
- }
-
- if (!out) {
- out = std::make_unique<std::string>();
- }
- std::string_view str;
- SIMDJSON_TRY(val.get_string().get(str));
- *out = std::string{str};
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<std::string> &out) noexcept {
- // Check if the value is null
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset(); // Set to nullptr
- return SUCCESS;
- }
-
- if (!out) {
- out = std::make_shared<std::string>();
- }
- std::string_view str;
- SIMDJSON_TRY(val.get_string().get(str));
- *out = std::string{str};
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<int> &out) noexcept {
- // Check if the value is null
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset(); // Set to nullptr
- return SUCCESS;
- }
-
- if (!out) {
- out = std::make_unique<int>();
- }
- int64_t temp;
- SIMDJSON_TRY(val.get_int64().get(temp));
- *out = static_cast<int>(temp);
- return SUCCESS;
-}
-
} // namespace simdjson
#endif // SIMDJSON_ONDEMAND_DESERIALIZE_H
@@ -195938,10 +192221,9 @@ simdjson_inline simdjson_result<std::string_view> array::raw_json() noexcept {
const uint8_t * starting_point{iter.peek_start()};
auto error = consume();
if(error) { return error; }
- // After 'consume()', we could be left pointing just beyond the document, but that
- // is ok because we are not going to dereference the final pointer position, we just
- // use it to compute the length in bytes.
- const uint8_t * final_point{iter._json_iter->unsafe_pointer()};
+ // After 'consume()', the last token consumed is the closing bracket: we end on it
+ // rather than on the next token, which may follow white space.
+ const uint8_t * final_point{iter._json_iter->token.peek(-1) + 1};
return std::string_view(reinterpret_cast<const char*>(starting_point), size_t(final_point - starting_point));
}
@@ -196025,12 +192307,23 @@ template <typename Func>
template <typename Func>
#endif
inline error_code array::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
- auto result_pair = get_next_key_and_json_path(json_path);
- std::string_view key = result_pair.first;
- std::string_view remaining_path = result_pair.second;
+ // The path is split once: every element reached by a wildcard sees the same
+ // remaining path, so re-parsing it at each one would be quadratic.
+ const std::vector<internal::json_path_segment> path = internal::split_json_path(json_path);
+ // A path that cannot be parsed matches nothing, whatever the document: say
+ // so rather than letting the walk skip it as a mismatch below a wildcard.
+ if (path.back().key.empty()) {
+ return INVALID_JSON_POINTER;
+ }
+ return for_each_at_path_segments(path, 0, callback);
+}
+
+template <typename Func>
+inline error_code array::for_each_at_path_segments(const std::vector<internal::json_path_segment> &path, size_t index, Func &callback) noexcept {
+ std::string_view key = path[index].key;
+ const bool last = index + 1 == path.size();
// Empty key means the path segment could not be parsed (including malformed
- // bracket-quoted keys). Without this check, an empty key is treated as index
- // 0 and remaining_path may be unchanged, causing infinite recursion.
+ // bracket-quoted keys).
if (key.empty()) {
return INVALID_JSON_POINTER;
}
@@ -196039,11 +192332,11 @@ inline error_code array::for_each_at_path_with_wildcard(std::string_view json_pa
for(auto element: *this) {
value val;
SIMDJSON_TRY(element.get(val));
- if (remaining_path.empty()) {
+ if (last) {
callback(val);
} else {
// An element that does not match the rest of the path is skipped.
- error_code err = val.for_each_at_path_with_wildcard(remaining_path, callback);
+ error_code err = val.for_each_at_path_segments(path, index + 1, callback);
if (err && !internal::is_matching_failure_error(err)) { return err; }
}
}
@@ -196062,16 +192355,16 @@ inline error_code array::for_each_at_path_with_wildcard(std::string_view json_pa
value val;
if (!error) { error = at(idx).get(val); }
if (error) {
- if (error == INDEX_OUT_OF_BOUNDS && internal::json_path_has_wildcard(remaining_path)) {
+ if (error == INDEX_OUT_OF_BOUNDS && path[index].wildcard_follows) {
return SUCCESS;
}
return error;
}
- if (remaining_path.empty()){
+ if (last){
callback(val);
return SUCCESS;
} else {
- return val.for_each_at_path_with_wildcard(remaining_path, callback);
+ return val.for_each_at_path_segments(path, index + 1, callback);
}
}
}
@@ -196729,6 +193022,19 @@ template <typename Func>
template <typename Func>
#endif
inline error_code value::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
+ // The path is split once: every value reached by a wildcard sees the same
+ // remaining path, so re-parsing it at each one would be quadratic.
+ const std::vector<internal::json_path_segment> path = internal::split_json_path(json_path);
+ // A path that cannot be parsed matches nothing, whatever the document: say
+ // so rather than letting the walk skip it as a mismatch below a wildcard.
+ if (path.back().key.empty()) {
+ return INVALID_JSON_POINTER;
+ }
+ return for_each_at_path_segments(path, 0, callback);
+}
+
+template <typename Func>
+inline error_code value::for_each_at_path_segments(const std::vector<internal::json_path_segment> &path, size_t index, Func &callback) noexcept {
// Every recursive step of for_each_at_path_with_wildcard goes through this
// function, and each one descends one level into the document. A path with
// many segments applied to a deeply nested document would otherwise recurse
@@ -196737,10 +193043,16 @@ inline error_code value::for_each_at_path_with_wildcard(std::string_view json_pa
json_type t;
SIMDJSON_TRY(type().get(t));
switch (t) {
- case json_type::array:
- return (*this).get_array().for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
- case json_type::object:
- return (*this).get_object().for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
+ case json_type::array: {
+ array a;
+ SIMDJSON_TRY(get_array().get(a));
+ return a.for_each_at_path_segments(path, index, callback);
+ }
+ case json_type::object: {
+ object o;
+ SIMDJSON_TRY(get_object().get(o));
+ return o.for_each_at_path_segments(path, index, callback);
+ }
default:
return INVALID_JSON_POINTER;
}
@@ -197113,7 +193425,8 @@ simdjson_inline document document::start(json_iterator &&iter) noexcept {
}
inline void document::rewind() noexcept {
- iter.rewind();
+ // A document that is no longer alive is left as it is (see is_alive()).
+ static_cast<void>(iter.rewind());
}
inline std::string document::to_debug_string() noexcept {
@@ -197413,7 +193726,13 @@ simdjson_warn_unused simdjson_inline error_code document::consume() noexcept {
auto error = is_scalar().get(scalar);
if(error) { return error; }
if(scalar) {
- iter.return_current_and_advance();
+ // Step over the scalar and leave depth 0, as skip_child() does: otherwise a
+ // document_stream takes the next document as the rest of this one. At depth
+ // 0 the scalar has already been consumed (e.g., by get_int64()).
+ if(iter.depth() > 0) {
+ iter.return_current_and_advance();
+ iter.ascend_to(0);
+ }
return SUCCESS;
}
error = iter.skip_child(0);
@@ -197426,10 +193745,36 @@ simdjson_inline simdjson_result<std::string_view> document::raw_json() noexcept
const uint8_t * starting_point{_iter.peek_start()};
auto error = consume();
if(error) { return error; }
- // After 'consume()', we could be left pointing just beyond the document, but that
- // is ok because we are not going to dereference the final pointer position, we just
- // use it to compute the length in bytes.
- const uint8_t * final_point{iter.unsafe_pointer()};
+ // After 'consume()', the iterator points at the token after the document. In a
+ // document_stream, that is the next document, past white space and separators, or
+ // the end of the input when a truncated document follows. We end on the last byte
+ // of this document instead.
+ const uint8_t * final_point;
+ if(*starting_point == '{' || *starting_point == '[') {
+ // The last token consumed is the closing brace or bracket.
+ final_point = iter.token.peek(-1) + 1;
+ } else {
+ // A scalar is a single token: a string ends at its closing quote, anything else
+ // at the first white space, structural character, quote, RS (0x1E) or NUL.
+ const uint8_t * next_token{iter.unsafe_pointer()};
+ final_point = starting_point + 1;
+ if(*starting_point == '"') {
+ while(final_point < next_token) {
+ uint8_t c = *final_point++;
+ if(c == '\\') {
+ final_point++;
+ } else if(c == '"') {
+ break;
+ }
+ }
+ if(final_point > next_token) { final_point = next_token; }
+ } else {
+ while(final_point < next_token && !jsoncharutils::is_structural_or_whitespace(*final_point)
+ && *final_point != '"' && *final_point != 0x1E && *final_point != 0) {
+ final_point++;
+ }
+ }
+ }
return std::string_view(reinterpret_cast<const char*>(starting_point), size_t(final_point - starting_point));
}
@@ -197476,7 +193821,9 @@ simdjson_inline simdjson_result<std::string_view> document::raw_json_token() noe
}
simdjson_inline simdjson_result<value> document::at_pointer(std::string_view json_pointer) noexcept {
- rewind(); // Rewind the document each time at_pointer is called
+ // Rewind the document each time at_pointer is called. After a fatal error,
+ // the document cannot be walked again: we report the error instead.
+ SIMDJSON_TRY(iter.rewind());
if (json_pointer.empty()) {
return this->get_value();
}
@@ -197494,7 +193841,9 @@ simdjson_inline simdjson_result<value> document::at_pointer(std::string_view jso
}
simdjson_inline simdjson_result<value> document::at_path(std::string_view json_path) noexcept {
- rewind(); // Rewind the document each time at_pointer is called
+ // Rewind the document each time at_path is called. After a fatal error,
+ // the document cannot be walked again: we report the error instead.
+ SIMDJSON_TRY(iter.rewind());
if (json_path.empty()) {
return this->get_value();
}
@@ -197517,7 +193866,9 @@ template <typename Func>
template <typename Func>
#endif
simdjson_inline error_code document::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
- rewind(); // Rewind the document each time for_each_at_path_with_wildcard is called
+ // Rewind the document each time for_each_at_path_with_wildcard is called. After
+ // a fatal error, the document cannot be walked again: we report the error instead.
+ SIMDJSON_TRY(iter.rewind());
if (json_path.empty()) {
return INVALID_JSON_POINTER;
}
@@ -197855,11 +194206,6 @@ simdjson_inline simdjson_result<lsx::ondemand::number> simdjson_result<lsx::onde
#if SIMDJSON_EXCEPTIONS
-template <class T, typename std::enable_if<std::is_same<T, lsx::ondemand::document>::value == false>::type>
-simdjson_inline simdjson_result<lsx::ondemand::document>::operator T() noexcept(false) {
- if (error()) { throw simdjson_error(error()); }
- return first;
-}
simdjson_inline simdjson_result<lsx::ondemand::document>::operator lsx::ondemand::array() & noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
@@ -197920,6 +194266,11 @@ simdjson_inline simdjson_result<std::string_view> simdjson_result<lsx::ondemand:
return first.raw_json_token();
}
+simdjson_inline simdjson_result<std::string_view> simdjson_result<lsx::ondemand::document>::raw_json() noexcept {
+ if (error()) { return error(); }
+ return first.raw_json();
+}
+
simdjson_inline simdjson_result<lsx::ondemand::value> simdjson_result<lsx::ondemand::document>::at_pointer(std::string_view json_pointer) noexcept {
if (error()) { return error(); }
return first.at_pointer(json_pointer);
@@ -198406,6 +194757,11 @@ simdjson_inline simdjson_result<std::string_view> simdjson_result<lsx::ondemand:
return first.raw_json_token();
}
+simdjson_inline simdjson_result<std::string_view> simdjson_result<lsx::ondemand::document_reference>::raw_json() noexcept {
+ if (error()) { return error(); }
+ return first.raw_json();
+}
+
simdjson_inline simdjson_result<lsx::ondemand::value> simdjson_result<lsx::ondemand::document_reference>::at_pointer(std::string_view json_pointer) noexcept {
if (error()) { return error(); }
return first.at_pointer(json_pointer);
@@ -198797,6 +195153,13 @@ simdjson_inline bool document_stream::skip_to_delimiter(uint8_t delimiter) noexc
}
inline void document_stream::next_document() noexcept {
+ // Iteration stops at the first document that met a fatal error. The error
+ // may have abandoned the document's iterator, which then no longer has a
+ // parser to walk the rest of the document with.
+ if (!doc.iter.is_alive() || doc.iter.error) {
+ error = doc.iter.error ? doc.iter.error : TAPE_ERROR;
+ return;
+ }
// A delimiter that cannot occur inside a document tells us where the current
// one ends, so we can jump there instead of walking every structural. Only
// valid while the iterator is still inside the document: a consumed document
@@ -198946,7 +195309,10 @@ simdjson_inline std::string_view document_stream::iterator::source() const noexc
cur_struct_index++;
}
- while (cur_struct_index <= static_cast<int64_t>(stream->parser->implementation->n_structural_indexes)) {
+ // The structural index at n_structural_indexes is the end of the batch, not
+ // a token: it must not be read as a closing bracket.
+ const int64_t n_structural_indexes = static_cast<int64_t>(stream->parser->implementation->n_structural_indexes);
+ while (cur_struct_index < n_structural_indexes) {
switch (stream->buf[stream->batch_start + stream->parser->implementation->structural_indexes[cur_struct_index]]) {
case '{': case '[':
depth++;
@@ -198958,6 +195324,11 @@ simdjson_inline std::string_view document_stream::iterator::source() const noexc
if (depth == 0) { break; }
cur_struct_index++;
}
+ // An invalid last document such as "[1 2" is never closed: it runs to the
+ // end of the batch, and not one byte past it.
+ if (cur_struct_index >= n_structural_indexes) {
+ return std::string_view(reinterpret_cast<const char*>(stream->buf) + current_index(), stream->batch_start + stream->parser->implementation->structural_indexes[cur_struct_index] - current_index());
+ }
return std::string_view(reinterpret_cast<const char*>(stream->buf) + current_index(), stream->parser->implementation->structural_indexes[cur_struct_index] - current_index() + stream->batch_start + 1);;
}
@@ -198974,7 +195345,12 @@ inline void document_stream::load_from_stage1_thread() noexcept {
// enough memory to swap to, as well.
std::swap(stage1_thread_parser,*parser);
error = stage1_thread_error;
- if (error) { return; }
+ // An EMPTY batch (no complete document in it, e.g. a run of white space at
+ // least batch_size long) is not the end of the stream: next() moves on to
+ // the batch after it, so queue that batch now as we would after a success.
+ // Returning here would leave no job for the next call, which then swaps
+ // the stale previous batch back in and ends the stream early.
+ if (error && error != EMPTY) { return; }
// If there's anything left, start the stage 1 thread!
if (next_batch_start() < len) {
@@ -199086,12 +195462,14 @@ simdjson_inline raw_json_string field::key() const noexcept {
simdjson_inline std::string_view field::key_raw_json_token() const noexcept {
SIMDJSON_ASSUME(first.buf != nullptr); // We would like to call .alive() by Visual Studio won't let us.
- return std::string_view(reinterpret_cast<const char*>(first.buf-1), second.iter._json_iter->token.peek(-1) - first.buf + 1);
+ // The ':' is the token just before the value. Use the value's start position
+ // rather than the current position, which moves once the value is consumed.
+ return std::string_view(reinterpret_cast<const char*>(first.buf-1), second.iter._json_iter->token.peek(second.iter.start_position() - 1) - first.buf + 1);
}
simdjson_inline std::string_view field::escaped_key() const noexcept {
SIMDJSON_ASSUME(first.buf != nullptr); // We would like to call .alive() by Visual Studio won't let us.
- auto end_quote = second.iter._json_iter->token.peek(-1);
+ auto end_quote = second.iter._json_iter->token.peek(second.iter.start_position() - 1);
while(*end_quote != '"') end_quote--;
return std::string_view(reinterpret_cast<const char*>(first.buf), end_quote - first.buf);
}
@@ -199262,11 +195640,14 @@ simdjson_inline json_iterator::json_iterator(const uint8_t *buf, ondemand::parse
}
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
-inline void json_iterator::rewind() noexcept {
+simdjson_inline error_code json_iterator::rewind() noexcept {
+ // abandon() let go of the parser: the iterator cannot be walked again.
+ if (!is_alive()) { return error ? error : UNINITIALIZED; }
token.set_position( root_position() );
logger::log_headers(); // We start again
_string_buf_loc = parser->string_buf.get();
_depth = 1;
+ return SUCCESS;
}
inline bool json_iterator::balanced() const noexcept {
@@ -199290,6 +195671,18 @@ inline bool json_iterator::balanced() const noexcept {
}
+// Depth change of each structural character when skipping: +1 for [ and {, -1 for ] and }.
+static constexpr int8_t skip_depth_delta[256] = {
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,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,
+};
+
// GCC 7 warns when the first line of this function is inlined away into oblivion due to the caller
// relating depth and parent_depth, which is a desired effect. The warning does not show up if the
// skip_child() function is not marked inline).
@@ -199345,7 +195738,30 @@ simdjson_warn_unused simdjson_inline error_code json_iterator::skip_child(depth_
break;
}
- // Now that we've considered the first value, we only increment/decrement for arrays/objects
+ // Now that we've considered the first value, we only increment/decrement for arrays/objects.
+ //
+ // Without logging, look up the depth change of each token in a table instead of branching
+ // on it: the next byte is hard to predict, so a switch mispredicts often. The loop also keeps
+ // the position and depth in locals, because the byte loads are char loads that may alias
+ // token._position and _depth, which would force a store of both on every token.
+ if (!logger::LOG_ENABLED) {
+ token_position pos = token.position();
+ const token_position end = end_position();
+ const uint8_t *const json = token.buf;
+ depth_t depth = _depth;
+ while (pos < end) {
+ depth += skip_depth_delta[json[*pos++]];
+ if (depth <= parent_depth) {
+ token.set_position(pos);
+ _depth = depth;
+ return SUCCESS;
+ }
+ }
+ token.set_position(pos);
+ _depth = depth;
+ return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "not enough close braces");
+ }
+
while (position() < end_position()) {
switch (*return_current_and_advance()) {
case '[': case '{':
@@ -199630,6 +196046,17 @@ simdjson_warn_unused simdjson_inline error_code json_iterator::optional_error(er
simdjson_warn_unused simdjson_inline bool json_iterator::copy_to_buffer(const uint8_t *json, uint32_t max_len, uint8_t *tmpbuf, size_t N) noexcept {
// This function is not expected to be called in performance-sensitive settings.
+ // peek_root_length() stops at the next structural index. Stage 1 omits the
+ // comma or RS (0x1E) that separates stream documents, so that byte and the
+ // whitespace around it remain in the span. RS is not a valid number finisher.
+ while (max_len > 0) {
+ const uint8_t last = json[max_len - 1];
+ if (last != ' ' && last != '\t' && last != '\n' && last != '\r' &&
+ last != ',' && last != 0x1E) {
+ break;
+ }
+ max_len--;
+ }
// Let us guard against silly cases:
if((N < max_len) || (N == 0)) { return false; }
// Copy to the buffer.
@@ -200271,7 +196698,9 @@ simdjson_inline simdjson_result<std::string_view> object::raw_json() noexcept {
const uint8_t * starting_point{iter.peek_start()};
auto error = consume();
if(error) { return error; }
- const uint8_t * final_point{iter._json_iter->peek()};
+ // After 'consume()', the last token consumed is the closing brace: we end on it
+ // rather than on the next token, which may follow white space.
+ const uint8_t * final_point{iter._json_iter->token.peek(-1) + 1};
return std::string_view(reinterpret_cast<const char*>(starting_point), size_t(final_point - starting_point));
}
@@ -200364,9 +196793,21 @@ template <typename Func>
template <typename Func>
#endif
inline error_code object::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
- auto result_pair = get_next_key_and_json_path(json_path);
- std::string_view key = result_pair.first;
- std::string_view remaining_path = result_pair.second;
+ // The path is split once: every field reached by a wildcard sees the same
+ // remaining path, so re-parsing it at each one would be quadratic.
+ const std::vector<internal::json_path_segment> path = internal::split_json_path(json_path);
+ // A path that cannot be parsed matches nothing, whatever the document: say
+ // so rather than letting the walk skip it as a mismatch below a wildcard.
+ if (path.back().key.empty()) {
+ return INVALID_JSON_POINTER;
+ }
+ return for_each_at_path_segments(path, 0, callback);
+}
+
+template <typename Func>
+inline error_code object::for_each_at_path_segments(const std::vector<internal::json_path_segment> &path, size_t index, Func &callback) noexcept {
+ std::string_view key = path[index].key;
+ const bool last = index + 1 == path.size();
// Empty key means the path segment could not be parsed (including malformed
// bracket-quoted keys). Match DOM at_path_with_wildcard and avoid treating
// "no progress" as a recursive lookup of "".
@@ -200379,11 +196820,11 @@ inline error_code object::for_each_at_path_with_wildcard(std::string_view json_p
for (auto field : *this) {
value val;
SIMDJSON_TRY(field.value().get(val));
- if (remaining_path.empty()) {
+ if (last) {
callback(val);
} else {
// A field that does not match the rest of the path is skipped.
- error_code err = val.for_each_at_path_with_wildcard(remaining_path, callback);
+ error_code err = val.for_each_at_path_segments(path, index + 1, callback);
if (err && !internal::is_matching_failure_error(err)) { return err; }
}
}
@@ -200392,17 +196833,17 @@ inline error_code object::for_each_at_path_with_wildcard(std::string_view json_p
value val;
error_code error = find_field(key).get(val);
if (error) {
- if (error == NO_SUCH_FIELD && internal::json_path_has_wildcard(remaining_path)) {
+ if (error == NO_SUCH_FIELD && path[index].wildcard_follows) {
return SUCCESS;
}
return error;
}
- if (remaining_path.empty()) {
+ if (last) {
callback(val);
return SUCCESS;
} else {
- return val.for_each_at_path_with_wildcard(remaining_path, callback);
+ return val.for_each_at_path_segments(path, index + 1, callback);
}
}
}
@@ -201055,6 +197496,7 @@ simdjson_inline simdjson_warn_unused bool parser::string_buffer_overflow(const u
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(padded_string_view json) & noexcept {
if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
+ json.use_trailing_whitespace_as_padding();
json.remove_utf8_bom();
_document_len = json.length();
@@ -201072,6 +197514,7 @@ simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(p
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate_allow_incomplete_json(padded_string_view json) & noexcept {
if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
+ json.use_trailing_whitespace_as_padding();
json.remove_utf8_bom();
_document_len = json.length();
@@ -201127,6 +197570,7 @@ simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(c
simdjson_warn_unused simdjson_inline simdjson_result<json_iterator> parser::iterate_raw(padded_string_view json) & noexcept {
if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
+ json.use_trailing_whitespace_as_padding();
json.remove_utf8_bom();
@@ -201153,6 +197597,7 @@ inline simdjson_result<document_stream> parser::iterate_many(const char *buf, si
}
inline simdjson_result<document_stream> parser::iterate_many(padded_string_view s, size_t batch_size) noexcept {
if (!s.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
+ s.use_trailing_whitespace_as_padding();
return iterate_many(s.data(), s.length(), batch_size);
}
inline simdjson_result<document_stream> parser::iterate_many(const padded_string &s, size_t batch_size) noexcept {
@@ -201188,6 +197633,7 @@ inline simdjson_result<document_stream> parser::iterate_many(const char *buf, si
}
inline simdjson_result<document_stream> parser::iterate_many(padded_string_view s, size_t batch_size, bool allow_comma_separated) noexcept {
if (!s.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
+ s.use_trailing_whitespace_as_padding();
return iterate_many(s.data(), s.length(), batch_size, allow_comma_separated);
}
inline simdjson_result<document_stream> parser::iterate_many(const padded_string &s, size_t batch_size, bool allow_comma_separated) noexcept {
@@ -201233,6 +197679,7 @@ inline simdjson_result<document_stream> parser::iterate_many(const char *buf, si
}
inline simdjson_result<document_stream> parser::iterate_many(padded_string_view s, size_t batch_size, stream_format format) noexcept {
if (!s.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
+ s.use_trailing_whitespace_as_padding();
return iterate_many(s.data(), s.length(), batch_size, format);
}
inline simdjson_result<document_stream> parser::iterate_many(const std::string &s, size_t batch_size, stream_format format) noexcept {
@@ -201387,17 +197834,18 @@ simdjson_inline bool raw_json_string::is_free_from_unescaped_quote(const char* t
return true;
}
-
simdjson_inline bool raw_json_string::unsafe_is_equal(size_t length, std::string_view target) const noexcept {
// If we are going to call memcmp, then we must know something about the length of the raw_json_string.
- return (length >= target.size()) && (raw()[target.size()] == '"') && !memcmp(raw(), target.data(), target.size());
+ return (length >= target.size()) && (raw()[target.size()] == '"') && !memcmp(raw(), target.data(), target.size())
+ && !escapes_trailing_quote(target);
}
simdjson_inline bool raw_json_string::unsafe_is_equal(std::string_view target) const noexcept {
// Assumptions: does not contain unescaped quote characters("), and
// the raw content is quote terminated within a valid JSON string.
if(target.size() <= SIMDJSON_PADDING) {
- return (raw()[target.size()] == '"') && !memcmp(raw(), target.data(), target.size());
+ return (raw()[target.size()] == '"') && !memcmp(raw(), target.data(), target.size())
+ && !escapes_trailing_quote(target);
}
// Past SIMDJSON_PADDING we can no longer rely on the padding to keep the
// comparison in bounds, so we must stop at the quote terminating the key.
@@ -201423,7 +197871,7 @@ simdjson_inline bool raw_json_string::is_equal(std::string_view target) const no
escaping = false;
}
}
- if(r[pos] != '"') { return false; }
+ if((r[pos] != '"') || escaping) { return false; }
return true;
}
@@ -201436,7 +197884,7 @@ simdjson_inline bool raw_json_string::unsafe_is_equal(const char * target) const
for(;target[pos];pos++) {
if(r[pos] != target[pos]) { return false; }
}
- if(r[pos] != '"') { return false; }
+ if((r[pos] != '"') || escapes_trailing_quote(std::string_view(target, pos))) { return false; }
return true;
}
@@ -201461,7 +197909,7 @@ simdjson_inline bool raw_json_string::is_equal(const char* target) const noexcep
escaping = false;
}
}
- if(r[pos] != '"') { return false; }
+ if((r[pos] != '"') || escaping) { return false; }
return true;
}
@@ -201573,7 +198021,11 @@ simdjson_inline uint32_t token_iterator::peek_index(token_position position) con
return *position;
}
simdjson_inline uint32_t token_iterator::peek_length(token_position position) const noexcept {
- return *(position+1) - *position;
+ // In the last batch of a document_stream, the index past the end of the
+ // structural indexes holds the start of the truncated bytes (see
+ // document_stream::truncated_bytes()), which can come before the end of the
+ // input: a token at the end is then empty.
+ return *(position+1) > *position ? *(position+1) - *position : 0;
}
simdjson_inline uint32_t token_iterator::peek_root_length(token_position position) const noexcept {
@@ -201588,7 +198040,7 @@ simdjson_inline uint32_t token_iterator::peek_index(int32_t delta) const noexcep
return *(_position+delta);
}
simdjson_inline uint32_t token_iterator::peek_length(int32_t delta) const noexcept {
- return *(_position+delta+1) - *(_position+delta);
+ return peek_length(_position+delta);
}
simdjson_inline token_position token_iterator::position() const noexcept {
@@ -201647,6 +198099,8 @@ simdjson_inline simdjson_result<lsx::ondemand::token_iterator>::simdjson_result(
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
+#include <cstring> // std::memset (zeroing the root-number scratch padding)
+
namespace simdjson {
namespace lsx {
namespace ondemand {
@@ -201693,6 +198147,10 @@ simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::start
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::started_object() noexcept {
assert_at_container_start();
#if SIMDJSON_DEVELOPMENT_CHECKS
+ // The development checks record the start of every open container in a
+ // buffer of max_depth() entries: a document nested deeper than that is
+ // rejected, as the DOM parser does.
+ if (size_t(_depth) >= _json_iter->parser->max_depth()) { return report_error(DEPTH_ERROR, "Exceeded max depth!"); }
_json_iter->set_start_position(_depth, start_position());
#endif
if (*_json_iter->peek() == '}') {
@@ -202102,6 +198560,10 @@ inline std::string value_iterator::to_string() const noexcept {
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::started_array() noexcept {
assert_at_container_start();
+#if SIMDJSON_DEVELOPMENT_CHECKS
+ // See started_object().
+ if (size_t(_depth) >= _json_iter->parser->max_depth()) { return report_error(DEPTH_ERROR, "Exceeded max depth!"); }
+#endif
if (*_json_iter->peek() == ']') {
logger::log_value(*_json_iter, "empty array");
_json_iter->return_current_and_advance();
@@ -202307,8 +198769,10 @@ simdjson_inline simdjson_result<lsx::number_type> value_iterator::get_root_numbe
// Per https://www.exploringbinary.com/maximum-number-of-decimal-digits-in-binary-floating-point-numbers/,
// 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest
// number: -0.<fraction>e-308.
- uint8_t tmpbuf[1074+8+1+1];
- tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
+ // +SIMDJSON_PADDING: the digit scan reads up to 7 bytes past the terminator.
+ uint8_t tmpbuf[1074+8+1+1+SIMDJSON_PADDING];
+ // Null terminate, and zero the padding as allocate_padded_buffer does.
+ std::memset(tmpbuf+1074+8+1, 0, 1+SIMDJSON_PADDING);
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
if(numberparsing::check_if_integer(json, max_len)) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
@@ -202329,8 +198793,10 @@ simdjson_inline simdjson_result<number> value_iterator::get_root_number(bool che
// 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest
// number: -0.<fraction>e-308.
// NOTE: the current approach doesn't work for very big integer numbers containing more than 1074 digits.
- uint8_t tmpbuf[1074+8+1+1];
- tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
+ // +SIMDJSON_PADDING: the digit scan reads up to 7 bytes past the terminator.
+ uint8_t tmpbuf[1074+8+1+1+SIMDJSON_PADDING];
+ // Null terminate, and zero the padding as allocate_padded_buffer does.
+ std::memset(tmpbuf+1074+8+1, 0, 1+SIMDJSON_PADDING);
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
if(numberparsing::check_if_integer(json, max_len)) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
@@ -202400,10 +198866,10 @@ simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> value_iterator::g
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> value_iterator::get_root_uint64_in_string(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("uint64");
- uint8_t tmpbuf[20+1+1]{}; // <20 digits> is the longest possible unsigned integer
- tmpbuf[20+1] = '\0'; // make sure that buffer is always null terminated.
- if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 20+1)) {
- logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 20 characters");
+ uint8_t tmpbuf[20+2+1]{}; // "<20 digits>" is the longest possible unsigned integer
+ tmpbuf[20+2] = '\0'; // make sure that buffer is always null terminated.
+ if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 20+2)) {
+ logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 22 characters");
return NUMBER_ERROR;
}
auto result = numberparsing::parse_unsigned_in_string(tmpbuf);
@@ -202433,10 +198899,10 @@ simdjson_warn_unused simdjson_inline simdjson_result<int64_t> value_iterator::ge
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> value_iterator::get_root_int64_in_string(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("int64");
- uint8_t tmpbuf[20+1+1]; // -<19 digits> is the longest possible integer
- tmpbuf[20+1] = '\0'; // make sure that buffer is always null terminated.
- if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 20+1)) {
- logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 20 characters");
+ uint8_t tmpbuf[20+2+1]; // "-<19 digits>" is the longest possible integer
+ tmpbuf[20+2] = '\0'; // make sure that buffer is always null terminated.
+ if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 20+2)) {
+ logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 22 characters");
return NUMBER_ERROR;
}
@@ -202453,8 +198919,10 @@ simdjson_warn_unused simdjson_inline simdjson_result<double> value_iterator::get
// Per https://www.exploringbinary.com/maximum-number-of-decimal-digits-in-binary-floating-point-numbers/,
// 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest
// number: -0.<fraction>e-308.
- uint8_t tmpbuf[1074+8+1+1]; // +1 for null termination.
- tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
+ // +SIMDJSON_PADDING: the digit scan reads up to 7 bytes past the terminator.
+ uint8_t tmpbuf[1074+8+1+1+SIMDJSON_PADDING]; // +1 for null termination.
+ // Null terminate, and zero the padding as allocate_padded_buffer does.
+ std::memset(tmpbuf+1074+8+1, 0, 1+SIMDJSON_PADDING);
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters");
return NUMBER_ERROR;
@@ -202473,8 +198941,10 @@ simdjson_warn_unused simdjson_inline simdjson_result<double> value_iterator::get
// Per https://www.exploringbinary.com/maximum-number-of-decimal-digits-in-binary-floating-point-numbers/,
// 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest
// number: -0.<fraction>e-308.
- uint8_t tmpbuf[1074+8+1+1]; // +1 for null termination.
- tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
+ // +SIMDJSON_PADDING: the digit scan reads up to 7 bytes past the terminator.
+ uint8_t tmpbuf[1074+8+1+1+SIMDJSON_PADDING]; // +1 for null termination.
+ // Null terminate, and zero the padding as allocate_padded_buffer does.
+ std::memset(tmpbuf+1074+8+1, 0, 1+SIMDJSON_PADDING);
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters");
return NUMBER_ERROR;
@@ -202493,8 +198963,10 @@ simdjson_warn_unused simdjson_inline simdjson_result<float> value_iterator::get_
// We use the same buffer size as get_root_double: the number of significant
// digits that matter is smaller for binary32, but the JSON document may still
// spell out a long number that we must parse (and round) faithfully.
- uint8_t tmpbuf[1074+8+1+1]; // +1 for null termination.
- tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
+ // +SIMDJSON_PADDING: the digit scan reads up to 7 bytes past the terminator.
+ uint8_t tmpbuf[1074+8+1+1+SIMDJSON_PADDING]; // +1 for null termination.
+ // Null terminate, and zero the padding as allocate_padded_buffer does.
+ std::memset(tmpbuf+1074+8+1, 0, 1+SIMDJSON_PADDING);
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters");
return NUMBER_ERROR;
@@ -202510,8 +198982,10 @@ simdjson_warn_unused simdjson_inline simdjson_result<float> value_iterator::get_
simdjson_warn_unused simdjson_inline simdjson_result<float> value_iterator::get_root_float_in_string(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("float");
- uint8_t tmpbuf[1074+8+1+1]; // +1 for null termination.
- tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
+ // +SIMDJSON_PADDING: the digit scan reads up to 7 bytes past the terminator.
+ uint8_t tmpbuf[1074+8+1+1+SIMDJSON_PADDING]; // +1 for null termination.
+ // Null terminate, and zero the padding as allocate_padded_buffer does.
+ std::memset(tmpbuf+1074+8+1, 0, 1+SIMDJSON_PADDING);
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters");
return NUMBER_ERROR;
@@ -202532,7 +199006,7 @@ simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::get_r
// "true" and "false" are 4 and 5 characters long, respectively.
bool value_true = (max_len >= 4 && !atomparsing::str4ncmp(json, "true") &&
(max_len == 4 || jsoncharutils::is_structural_or_whitespace(json[4])));
- bool value_false = (max_len >= 5 && !atomparsing::str4ncmp(json, "false") &&
+ bool value_false = (max_len >= 5 && !atomparsing::str4ncmp(json, "fals") && json[4] == 'e' &&
(max_len == 5 || jsoncharutils::is_structural_or_whitespace(json[5])));
if(value_true == false && value_false == false) { return incorrect_type_error("Not a boolean"); }
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
@@ -203172,49 +199646,6 @@ namespace json_path {
using ::simdjson::lsx::ondemand::value;
-// Path step types
-enum class step_type {
- field, // .field_name or ["field_name"]
- array_index // [index]
-};
-
-// Represents a single step in a JSON path
-template<std::size_t N>
-struct path_step {
- step_type type;
- char key[N]; // Field name (empty for array indices)
- std::size_t index; // Array index (0 for field access)
-
- constexpr path_step(step_type t, const char (&k)[N], std::size_t idx = 0)
- : type(t), index(idx) {
- for (std::size_t i = 0; i < N; ++i) {
- key[i] = k[i];
- }
- }
-
- constexpr std::string_view key_view() const {
- return {key, N - 1};
- }
-};
-
-// Helper to create field step
-template<std::size_t N>
-consteval auto make_field_step(const char (&name)[N]) {
- return path_step<N>(step_type::field, name, 0);
-}
-
-// Helper to create array index step
-consteval auto make_index_step(std::size_t idx) {
- return path_step<1>(step_type::array_index, "", idx);
-}
-
-// Parse state for compile-time JSON path parsing
-struct parse_result {
- bool success;
- std::size_t pos;
- std::string_view error_msg;
-};
-
// Compile-time JSON path parser
// Supports subset: .field, ["field"], [index], nested combinations
template<constevalutil::fixed_string Path>
@@ -203228,77 +199659,6 @@ struct json_path_parser {
}
return 0;
}
-
- // Count the number of steps in the path at compile time
- static consteval std::size_t count_steps() {
- std::size_t count = 0;
- std::size_t i = skip_root();
-
- while (i < path_str.size()) {
- if (path_str[i] == '.') {
- // Field access: .field
- ++i;
- if (i >= path_str.size()) break;
-
- // Skip field name
- while (i < path_str.size() && path_str[i] != '.' && path_str[i] != '[') {
- ++i;
- }
- ++count;
- } else if (path_str[i] == '[') {
- // Array or bracket notation
- ++i;
- if (i >= path_str.size()) break;
-
- if (path_str[i] == '"' || path_str[i] == '\'') {
- // Field access: ["field"] or ['field']
- char quote = path_str[i];
- ++i;
- while (i < path_str.size() && path_str[i] != quote) {
- ++i;
- }
- if (i < path_str.size()) ++i; // skip closing quote
- if (i < path_str.size() && path_str[i] == ']') ++i;
- } else {
- // Array index: [0], [123]
- while (i < path_str.size() && path_str[i] != ']') {
- ++i;
- }
- if (i < path_str.size()) ++i; // skip ]
- }
- ++count;
- } else {
- ++i;
- }
- }
-
- return count;
- }
-
- // Parse a field name at compile time
- static consteval std::size_t parse_field_name(std::size_t start, char* out, std::size_t max_len) {
- std::size_t len = 0;
- std::size_t i = start;
-
- while (i < path_str.size() && path_str[i] != '.' && path_str[i] != '[' && len < max_len - 1) {
- out[len++] = path_str[i++];
- }
- out[len] = '\0';
- return i;
- }
-
- // Parse an array index at compile time
- static consteval std::pair<std::size_t, std::size_t> parse_array_index(std::size_t start) {
- std::size_t index = 0;
- std::size_t i = start;
-
- while (i < path_str.size() && path_str[i] >= '0' && path_str[i] <= '9') {
- index = index * 10 + (path_str[i] - '0');
- ++i;
- }
-
- return {i, index};
- }
};
// Compile-time path accessor generator
@@ -203307,7 +199667,6 @@ struct path_accessor {
using value = ::simdjson::lsx::ondemand::value;
static constexpr auto parser = json_path_parser<Path>();
- static constexpr std::size_t num_steps = parser.count_steps();
static constexpr std::string_view path_view = Path.view();
// Compile-time accessor generation
@@ -203422,93 +199781,6 @@ private:
return current_type;
}
-private:
- // Walk path and extract directly into final field using compile-time reflection
- template<std::meta::info CurrentType, std::size_t PathPos, typename TargetType>
- static inline error_code extract_with_reflection(simdjson_result<value> current, TargetType& target_ref) noexcept {
- if (current.error()) return current.error();
-
- // Base case: end of path - extract into target
- if constexpr (PathPos >= path_view.size()) {
- return current.get(target_ref);
- }
- // Field access: .field_name
- else if constexpr (path_view[PathPos] == '.') {
- constexpr auto field_info = parse_next_field(PathPos);
- constexpr std::string_view field_name = std::get<0>(field_info);
- constexpr std::size_t next_pos = std::get<1>(field_info);
-
- constexpr auto member_info = find_member_by_name(CurrentType, field_name);
- static_assert(member_info != ^^void, "Field not found in struct");
-
- constexpr auto member_type = std::meta::type_of(member_info);
-
- auto obj_result = current.get_object();
- if (obj_result.error()) return obj_result.error();
- auto obj = obj_result.value_unsafe();
- auto field_value = obj.find_field_unordered(field_name);
-
- if constexpr (next_pos >= path_view.size()) {
- return field_value.get(target_ref);
- } else {
- return extract_with_reflection<member_type, next_pos>(field_value, target_ref);
- }
- }
- // Bracket notation: [index] or ["field"]
- else if constexpr (path_view[PathPos] == '[') {
- constexpr auto bracket_info = parse_bracket(PathPos);
- constexpr bool is_field = std::get<0>(bracket_info);
- constexpr std::size_t next_pos = std::get<2>(bracket_info);
-
- if constexpr (is_field) {
- constexpr std::string_view field_name = std::get<1>(bracket_info);
- constexpr auto member_info = find_member_by_name(CurrentType, field_name);
- static_assert(member_info != ^^void, "Field not found in struct");
- constexpr auto member_type = std::meta::type_of(member_info);
-
- auto obj_result = current.get_object();
- if (obj_result.error()) return obj_result.error();
- auto obj = obj_result.value_unsafe();
- auto field_value = obj.find_field_unordered(field_name);
-
- if constexpr (next_pos >= path_view.size()) {
- return field_value.get(target_ref);
- } else {
- return extract_with_reflection<member_type, next_pos>(field_value, target_ref);
- }
- } else {
- constexpr std::size_t index = std::get<3>(bracket_info);
- constexpr auto elem_type = get_element_type_reflected(CurrentType);
- static_assert(elem_type != ^^void, "Could not determine array element type");
-
- auto arr_result = current.get_array();
- if (arr_result.error()) return arr_result.error();
- auto arr = arr_result.value_unsafe();
- auto elem_value = arr.at(index);
-
- if constexpr (next_pos >= path_view.size()) {
- return elem_value.get(target_ref);
- } else {
- return extract_with_reflection<elem_type, next_pos>(elem_value, target_ref);
- }
- }
- }
- // Skip unexpected characters and continue
- else {
- return extract_with_reflection<CurrentType, PathPos + 1>(current, target_ref);
- }
- }
-
- // Find member by name in reflected type
- static consteval std::meta::info find_member_by_name(std::meta::info type_refl, std::string_view name) {
- auto members = std::meta::nonstatic_data_members_of(type_refl, std::meta::access_context::unchecked());
- for (auto mem : members) {
- if (std::meta::identifier_of(mem) == name) {
- return mem;
- }
- }
- }
-
// Generate compile-time accessor code by walking the path
template<std::size_t PathPos>
static inline simdjson_result<value> access_impl(simdjson_result<value> current) noexcept {
@@ -203638,55 +199910,6 @@ public:
}
private:
- // Check if type has member with given name
- template<typename Type>
- static consteval bool has_member(std::string_view member_name) {
- constexpr auto members = std::meta::nonstatic_data_members_of(^^Type, std::meta::access_context::unchecked());
- for (auto mem : members) {
- if (std::meta::identifier_of(mem) == member_name) {
- return true;
- }
- }
- return false;
- }
-
- // Get type of member by name
- template<typename Type>
- static consteval auto get_member_type(std::string_view member_name) {
- constexpr auto members = std::meta::nonstatic_data_members_of(^^Type, std::meta::access_context::unchecked());
- for (auto mem : members) {
- if (std::meta::identifier_of(mem) == member_name) {
- return std::meta::type_of(mem);
- }
- }
- return ^^void;
- }
-
- // Check if non-reflected type is array-like
- template<typename Type>
- static consteval bool is_container_type() {
- using BaseType = std::remove_cvref_t<Type>;
- if constexpr (requires { typename BaseType::value_type; }) {
- return true;
- }
- if constexpr (std::is_array_v<BaseType>) {
- return true;
- }
- return false;
- }
-
- // Extract element type from non-reflected container
- template<typename Type>
- using extract_element_type = std::conditional_t<
- requires { typename std::remove_cvref_t<Type>::value_type; },
- typename std::remove_cvref_t<Type>::value_type,
- std::conditional_t<
- std::is_array_v<std::remove_cvref_t<Type>>,
- std::remove_extent_t<std::remove_cvref_t<Type>>,
- void
- >
- >;
-
// Validate path matches struct definition
static consteval bool validate_path() {
if constexpr (!std::is_class_v<T>) {
@@ -204676,30 +200899,6 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin
}
-struct escaping {
- static constexpr uint32_t BYTES_PROCESSED = 16;
- simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
-
- simdjson_inline bool has_escape() { return escape_bits != 0; }
- simdjson_inline int escape_index() { return trailing_zeroes(escape_bits); }
-
- uint64_t escape_bits;
-}; // struct escaping
-
-
-
-simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
- static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes");
- simd8<uint8_t> v(src);
- v.store(dst);
- simd8<bool> is_quote = (v == '"');
- simd8<bool> is_backslash = (v == '\\');
- simd8<bool> is_control = (v < 32);
- return {
- static_cast<uint64_t>((is_backslash | is_quote | is_control).to_bitmask())
- };
-}
-
} // unnamed namespace
} // namespace lasx
} // namespace simdjson
@@ -204913,11 +201112,6 @@ protected:
public:
simdjson_inline value_iterator() noexcept = default;
- /**
- * Denote that we're starting a document.
- */
- simdjson_inline void start_document() noexcept;
-
/**
* Skips a non-iterated or partially-iterated JSON value, whether it is a scalar, array or object.
*
@@ -205318,16 +201512,7 @@ protected:
simdjson_warn_unused simdjson_inline error_code start_container(uint8_t start_char, const char *incorrect_type_message, const char *type) noexcept;
simdjson_warn_unused simdjson_inline error_code end_container() noexcept;
- /**
- * Advance to a place expecting a value (increasing depth).
- *
- * @return The current token (the one left behind).
- * @error TAPE_ERROR If the document ended early.
- */
- simdjson_inline simdjson_result<const uint8_t *> advance_to_value() noexcept;
-
simdjson_warn_unused simdjson_inline error_code incorrect_type_error(const char *message) const noexcept;
- simdjson_warn_unused simdjson_inline error_code error_unless_more_tokens(uint32_t tokens=1) const noexcept;
simdjson_inline bool is_at_start() const noexcept;
/**
@@ -205410,6 +201595,7 @@ public:
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
+/* amalgamation skipped (editor-only): #include "simdjson/jsonpathutil.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/deserialize.h" */
/* amalgamation skipped (editor-only): #include <vector> */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
@@ -206263,11 +202449,19 @@ protected:
*/
simdjson_inline simdjson_result<object> start_or_resume_object() noexcept;
+ /**
+ * The recursive step of for_each_at_path_with_wildcard: matches
+ * path[index] at this level and what follows below it.
+ */
+ template <typename Func>
+ inline error_code for_each_at_path_segments(const std::vector<simdjson::internal::json_path_segment> &path, size_t index, Func &callback) noexcept;
+
// simdjson_inline void log_value(const char *type) const noexcept;
// simdjson_inline void log_error(const char *message) const noexcept;
value_iterator iter{};
+ friend class array;
friend class document;
friend class array_iterator;
friend class field;
@@ -206956,6 +203150,7 @@ public:
/**
* Take an input in json containing max_len characters and attempt to copy it over to tmpbuf, a buffer with
* N bytes of capacity. It will return false if N is too small (smaller than max_len) of if it is zero.
+ * Trailing whitespace, commas, and record separators (0x1E) do not count toward max_len.
* The buffer (tmpbuf) is padded with space characters.
*/
simdjson_warn_unused simdjson_inline bool copy_to_buffer(const uint8_t *json, uint32_t max_len, uint8_t *tmpbuf, size_t N) noexcept;
@@ -206988,8 +203183,11 @@ public:
/**
* Updates this json iterator so that it is back at the beginning of the document,
* as if it had just been created.
+ *
+ * An iterator that is no longer alive (e.g., abandoned after a fatal error) has no
+ * document to go back to: it is left as is and its error is returned.
*/
- inline void rewind() noexcept;
+ simdjson_inline error_code rewind() noexcept;
/**
* This checks whether the {,},[,] are balanced so that the document
* ends with proper zero depth. This requires scanning the whole document
@@ -207344,6 +203542,28 @@ public:
private:
+ /**
+ * Returns true if target ends with an odd number of backslashes. The last one
+ * then escapes the quote that follows the matching bytes in the raw string, so
+ * that quote does not end the string. Not inlined: callers check the last byte
+ * first, and keys ending in a backslash are rare.
+ */
+ static simdjson_never_inline bool ends_with_unpaired_backslash(std::string_view target) noexcept {
+ size_t backslash_count{0};
+ while(backslash_count < target.size() && target[target.size() - 1 - backslash_count] == '\\') {
+ backslash_count++;
+ }
+ return backslash_count % 2 == 1;
+ }
+
+ /**
+ * True when a byte-for-byte match must be rejected because target escapes the
+ * closing quote. The last-byte test keeps the counting loop off the common path.
+ */
+ static simdjson_inline bool escapes_trailing_quote(std::string_view target) noexcept {
+ return simdjson_unlikely(!target.empty() && target[target.size() - 1] == '\\')
+ && ends_with_unpaired_backslash(target);
+ }
/**
* This will set the inner pointer to zero, effectively making
@@ -207917,6 +204137,7 @@ public:
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
+/* amalgamation skipped (editor-only): #include "simdjson/jsonpathutil.h" */
/* amalgamation skipped (editor-only): #include <vector> */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
@@ -208109,6 +204330,13 @@ protected:
*/
simdjson_warn_unused simdjson_inline error_code consume() noexcept;
+ /**
+ * The recursive step of for_each_at_path_with_wildcard: matches
+ * path[index] at this level and what follows below it.
+ */
+ template <typename Func>
+ inline error_code for_each_at_path_segments(const std::vector<simdjson::internal::json_path_segment> &path, size_t index, Func &callback) noexcept;
+
/**
* Begin array iteration.
*
@@ -209118,6 +205346,9 @@ public:
* Reset the iterator inside the document instance so we are pointing back at the
* beginning of the document, as if it had just been created. It invalidates all
* values, objects and arrays that you have created so far (including unescaped strings).
+ *
+ * A document that is no longer alive (see is_alive()) cannot be rewound: it is
+ * left as is.
*/
inline void rewind() noexcept;
/**
@@ -209191,6 +205422,8 @@ public:
* - INCORRECT_TYPE if a non-integer is used to access an array
* - INVALID_JSON_POINTER if the JSON pointer is invalid and cannot be parsed
* - SCALAR_DOCUMENT_AS_VALUE if the json_pointer is empty and the document is not a scalar (see is_scalar() function).
+ * - the fatal error (e.g., TAPE_ERROR) that ended an earlier call, as the document can
+ * no longer be rewound (see is_alive()).
*/
simdjson_inline simdjson_result<value> at_pointer(std::string_view json_pointer) noexcept;
@@ -209214,6 +205447,8 @@ public:
* - NO_SUCH_FIELD if a field does not exist in an object
* - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length
* - INCORRECT_TYPE if a non-integer is used to access an array
+ * - the fatal error (e.g., TAPE_ERROR) that ended an earlier call, as the document can
+ * no longer be rewound (see is_alive()).
*/
simdjson_inline simdjson_result<value> at_path(std::string_view json_path) noexcept;
@@ -209240,7 +205475,9 @@ public:
/**
* Consumes the document and returns a string_view instance corresponding to the
* document as represented in JSON. It points inside the original byte array containing
- * the JSON document.
+ * the JSON document. It starts at the first byte of the document and ends at its last
+ * byte: it does not include the white space that follows the document, nor, in a
+ * document_stream, the separators or the documents that follow it.
*/
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
@@ -209567,134 +205804,134 @@ public:
using lasx::implementation_simdjson_result_base<lasx::ondemand::document>::operator*;
using lasx::implementation_simdjson_result_base<lasx::ondemand::document>::operator->;
- template <class T, typename std::enable_if<std::is_same<T, lasx::ondemand::document>::value == false>::type>
- explicit simdjson_inline operator T() noexcept(false);
- simdjson_inline operator lasx::ondemand::array() & noexcept(false);
- simdjson_inline operator lasx::ondemand::object() & noexcept(false);
- explicit simdjson_inline operator uint64_t() noexcept(false);
- explicit simdjson_inline operator int64_t() noexcept(false);
- explicit simdjson_inline operator double() noexcept(false);
- explicit simdjson_inline operator std::string_view() noexcept(false);
- explicit simdjson_inline operator lasx::ondemand::raw_json_string() noexcept(false);
- explicit simdjson_inline operator bool() noexcept(false);
- simdjson_inline operator lasx::ondemand::value() noexcept(false);
-#endif
- simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
- simdjson_inline simdjson_result<size_t> count_fields() & noexcept;
- simdjson_inline simdjson_result<lasx::ondemand::value> at(size_t index) & noexcept;
- simdjson_inline simdjson_result<lasx::ondemand::array_iterator> begin() & noexcept;
- simdjson_inline simdjson_result<lasx::ondemand::array_iterator> end() & noexcept;
- simdjson_inline simdjson_result<lasx::ondemand::value> find_field(std::string_view key) & noexcept;
- simdjson_inline simdjson_result<lasx::ondemand::value> find_field(const char *key) & noexcept;
- simdjson_inline simdjson_result<lasx::ondemand::value> operator[](std::string_view key) & noexcept;
- simdjson_inline simdjson_result<lasx::ondemand::value> operator[](const char *key) & noexcept;
- simdjson_result<lasx::ondemand::value> operator[](int) & noexcept = delete;
- simdjson_inline simdjson_result<lasx::ondemand::value> find_field_unordered(std::string_view key) & noexcept;
- simdjson_inline simdjson_result<lasx::ondemand::value> find_field_unordered(const char *key) & noexcept;
- simdjson_inline simdjson_result<lasx::ondemand::json_type> type() noexcept;
- simdjson_inline simdjson_result<bool> is_scalar() noexcept;
- simdjson_inline simdjson_result<bool> is_string() noexcept;
- simdjson_inline simdjson_result<const char *> current_location() noexcept;
- simdjson_inline int32_t current_depth() const noexcept;
- simdjson_inline bool at_end() const noexcept;
- simdjson_inline bool is_negative() noexcept;
- simdjson_inline simdjson_result<bool> is_integer() noexcept;
- simdjson_inline simdjson_result<lasx::number_type> get_number_type() noexcept;
- simdjson_inline simdjson_result<lasx::ondemand::number> get_number() noexcept;
- /** @copydoc simdjson_inline std::string_view document::raw_json_token() const noexcept */
- simdjson_inline simdjson_result<std::string_view> raw_json_token() noexcept;
-
- simdjson_inline simdjson_result<lasx::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
- simdjson_inline simdjson_result<lasx::ondemand::value> at_path(std::string_view json_path) noexcept;
-#if SIMDJSON_SUPPORTS_CONCEPTS
- template <typename Func>
- requires std::invocable<Func, lasx::ondemand::value>
-#else
- template <typename Func>
-#endif
- simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
-#if SIMDJSON_STATIC_REFLECTION
- template<constevalutil::fixed_string... FieldNames, typename T>
- requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
- simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept;
-#endif // SIMDJSON_STATIC_REFLECTION
-};
-
-
-} // namespace simdjson
-
-
-
-namespace simdjson {
-
-template<>
-struct simdjson_result<lasx::ondemand::document_reference> : public lasx::implementation_simdjson_result_base<lasx::ondemand::document_reference> {
-public:
- simdjson_inline simdjson_result(lasx::ondemand::document_reference value, error_code error) noexcept;
- simdjson_inline simdjson_result() noexcept = default;
- simdjson_inline error_code rewind() noexcept;
-
- simdjson_inline simdjson_result<lasx::ondemand::array> get_array() & noexcept;
- simdjson_inline simdjson_result<lasx::ondemand::object> get_object() & noexcept;
- simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
- simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
- simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
- simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
- simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
- simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
- simdjson_inline simdjson_result<uint16_t> get_uint16() noexcept;
- simdjson_inline simdjson_result<int16_t> get_int16() noexcept;
- simdjson_inline simdjson_result<uint8_t> get_uint8() noexcept;
- simdjson_inline simdjson_result<int8_t> get_int8() noexcept;
- simdjson_inline simdjson_result<double> get_double() noexcept;
- simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
- simdjson_inline simdjson_result<float> get_float() noexcept;
- simdjson_inline simdjson_result<float> get_float_in_string() noexcept;
-#if SIMDJSON_SUPPORTS_FLOAT32_T
- simdjson_inline simdjson_result<std::float32_t> get_float32() noexcept;
-#endif // SIMDJSON_SUPPORTS_FLOAT32_T
-#if SIMDJSON_SUPPORTS_FLOAT64_T
- simdjson_inline simdjson_result<std::float64_t> get_float64() noexcept;
-#endif // SIMDJSON_SUPPORTS_FLOAT64_T
- simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
-#if SIMDJSON_SUPPORTS_CHAR8_T
- simdjson_inline simdjson_result<std::u8string_view> get_u8string(bool allow_replacement = false) noexcept;
-#endif // SIMDJSON_SUPPORTS_CHAR8_T
- template <typename string_type>
- simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
- simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
- simdjson_inline simdjson_result<lasx::ondemand::raw_json_string> get_raw_json_string() noexcept;
- simdjson_inline simdjson_result<bool> get_bool() noexcept;
- simdjson_inline simdjson_result<lasx::ondemand::value> get_value() noexcept;
- simdjson_inline simdjson_result<bool> is_null() noexcept;
-
- template<typename T> simdjson_inline simdjson_result<T> get() &
-#if SIMDJSON_SUPPORTS_CONCEPTS
- noexcept(nothrow_gettable<T, lasx::ondemand::document_reference>);
-#else
- noexcept;
-#endif
- template<typename T> simdjson_inline simdjson_result<T> get() &&
-#if SIMDJSON_SUPPORTS_CONCEPTS
- noexcept(nothrow_gettable<T, lasx::ondemand::document_reference>);
-#else
- noexcept;
-#endif
-
- template<typename T> simdjson_inline error_code get(T &out) &
-#if SIMDJSON_SUPPORTS_CONCEPTS
- noexcept(nothrow_gettable<T, lasx::ondemand::document_reference>);
-#else
- noexcept;
-#endif
- template<typename T> simdjson_inline error_code get(T &out) &&
-#if SIMDJSON_SUPPORTS_CONCEPTS
- noexcept(nothrow_gettable<T, lasx::ondemand::document_reference>);
-#else
- noexcept;
-#endif
-#if SIMDJSON_EXCEPTIONS
- template <class T>
+ simdjson_inline operator lasx::ondemand::array() & noexcept(false);
+ simdjson_inline operator lasx::ondemand::object() & noexcept(false);
+ explicit simdjson_inline operator uint64_t() noexcept(false);
+ explicit simdjson_inline operator int64_t() noexcept(false);
+ explicit simdjson_inline operator double() noexcept(false);
+ explicit simdjson_inline operator std::string_view() noexcept(false);
+ explicit simdjson_inline operator lasx::ondemand::raw_json_string() noexcept(false);
+ explicit simdjson_inline operator bool() noexcept(false);
+ simdjson_inline operator lasx::ondemand::value() noexcept(false);
+#endif
+ simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
+ simdjson_inline simdjson_result<size_t> count_fields() & noexcept;
+ simdjson_inline simdjson_result<lasx::ondemand::value> at(size_t index) & noexcept;
+ simdjson_inline simdjson_result<lasx::ondemand::array_iterator> begin() & noexcept;
+ simdjson_inline simdjson_result<lasx::ondemand::array_iterator> end() & noexcept;
+ simdjson_inline simdjson_result<lasx::ondemand::value> find_field(std::string_view key) & noexcept;
+ simdjson_inline simdjson_result<lasx::ondemand::value> find_field(const char *key) & noexcept;
+ simdjson_inline simdjson_result<lasx::ondemand::value> operator[](std::string_view key) & noexcept;
+ simdjson_inline simdjson_result<lasx::ondemand::value> operator[](const char *key) & noexcept;
+ simdjson_result<lasx::ondemand::value> operator[](int) & noexcept = delete;
+ simdjson_inline simdjson_result<lasx::ondemand::value> find_field_unordered(std::string_view key) & noexcept;
+ simdjson_inline simdjson_result<lasx::ondemand::value> find_field_unordered(const char *key) & noexcept;
+ simdjson_inline simdjson_result<lasx::ondemand::json_type> type() noexcept;
+ simdjson_inline simdjson_result<bool> is_scalar() noexcept;
+ simdjson_inline simdjson_result<bool> is_string() noexcept;
+ simdjson_inline simdjson_result<const char *> current_location() noexcept;
+ simdjson_inline int32_t current_depth() const noexcept;
+ simdjson_inline bool at_end() const noexcept;
+ simdjson_inline bool is_negative() noexcept;
+ simdjson_inline simdjson_result<bool> is_integer() noexcept;
+ simdjson_inline simdjson_result<lasx::number_type> get_number_type() noexcept;
+ simdjson_inline simdjson_result<lasx::ondemand::number> get_number() noexcept;
+ /** @copydoc simdjson_inline std::string_view document::raw_json_token() const noexcept */
+ simdjson_inline simdjson_result<std::string_view> raw_json_token() noexcept;
+ /** @copydoc simdjson_inline simdjson_result<std::string_view> document::raw_json() noexcept */
+ simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
+
+ simdjson_inline simdjson_result<lasx::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
+ simdjson_inline simdjson_result<lasx::ondemand::value> at_path(std::string_view json_path) noexcept;
+#if SIMDJSON_SUPPORTS_CONCEPTS
+ template <typename Func>
+ requires std::invocable<Func, lasx::ondemand::value>
+#else
+ template <typename Func>
+#endif
+ simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
+#if SIMDJSON_STATIC_REFLECTION
+ template<constevalutil::fixed_string... FieldNames, typename T>
+ requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
+ simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept;
+#endif // SIMDJSON_STATIC_REFLECTION
+};
+
+
+} // namespace simdjson
+
+
+
+namespace simdjson {
+
+template<>
+struct simdjson_result<lasx::ondemand::document_reference> : public lasx::implementation_simdjson_result_base<lasx::ondemand::document_reference> {
+public:
+ simdjson_inline simdjson_result(lasx::ondemand::document_reference value, error_code error) noexcept;
+ simdjson_inline simdjson_result() noexcept = default;
+ simdjson_inline error_code rewind() noexcept;
+
+ simdjson_inline simdjson_result<lasx::ondemand::array> get_array() & noexcept;
+ simdjson_inline simdjson_result<lasx::ondemand::object> get_object() & noexcept;
+ simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
+ simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
+ simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
+ simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
+ simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
+ simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
+ simdjson_inline simdjson_result<uint16_t> get_uint16() noexcept;
+ simdjson_inline simdjson_result<int16_t> get_int16() noexcept;
+ simdjson_inline simdjson_result<uint8_t> get_uint8() noexcept;
+ simdjson_inline simdjson_result<int8_t> get_int8() noexcept;
+ simdjson_inline simdjson_result<double> get_double() noexcept;
+ simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
+ simdjson_inline simdjson_result<float> get_float() noexcept;
+ simdjson_inline simdjson_result<float> get_float_in_string() noexcept;
+#if SIMDJSON_SUPPORTS_FLOAT32_T
+ simdjson_inline simdjson_result<std::float32_t> get_float32() noexcept;
+#endif // SIMDJSON_SUPPORTS_FLOAT32_T
+#if SIMDJSON_SUPPORTS_FLOAT64_T
+ simdjson_inline simdjson_result<std::float64_t> get_float64() noexcept;
+#endif // SIMDJSON_SUPPORTS_FLOAT64_T
+ simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
+#if SIMDJSON_SUPPORTS_CHAR8_T
+ simdjson_inline simdjson_result<std::u8string_view> get_u8string(bool allow_replacement = false) noexcept;
+#endif // SIMDJSON_SUPPORTS_CHAR8_T
+ template <typename string_type>
+ simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
+ simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
+ simdjson_inline simdjson_result<lasx::ondemand::raw_json_string> get_raw_json_string() noexcept;
+ simdjson_inline simdjson_result<bool> get_bool() noexcept;
+ simdjson_inline simdjson_result<lasx::ondemand::value> get_value() noexcept;
+ simdjson_inline simdjson_result<bool> is_null() noexcept;
+
+ template<typename T> simdjson_inline simdjson_result<T> get() &
+#if SIMDJSON_SUPPORTS_CONCEPTS
+ noexcept(nothrow_gettable<T, lasx::ondemand::document_reference>);
+#else
+ noexcept;
+#endif
+ template<typename T> simdjson_inline simdjson_result<T> get() &&
+#if SIMDJSON_SUPPORTS_CONCEPTS
+ noexcept(nothrow_gettable<T, lasx::ondemand::document_reference>);
+#else
+ noexcept;
+#endif
+
+ template<typename T> simdjson_inline error_code get(T &out) &
+#if SIMDJSON_SUPPORTS_CONCEPTS
+ noexcept(nothrow_gettable<T, lasx::ondemand::document_reference>);
+#else
+ noexcept;
+#endif
+ template<typename T> simdjson_inline error_code get(T &out) &&
+#if SIMDJSON_SUPPORTS_CONCEPTS
+ noexcept(nothrow_gettable<T, lasx::ondemand::document_reference>);
+#else
+ noexcept;
+#endif
+#if SIMDJSON_EXCEPTIONS
+ template <class T>
explicit simdjson_inline operator T() noexcept(false);
simdjson_inline operator lasx::ondemand::array() & noexcept(false);
simdjson_inline operator lasx::ondemand::object() & noexcept(false);
@@ -209729,6 +205966,8 @@ public:
simdjson_inline simdjson_result<lasx::ondemand::number> get_number() noexcept;
/** @copydoc simdjson_inline std::string_view document_reference::raw_json_token() const noexcept */
simdjson_inline simdjson_result<std::string_view> raw_json_token() noexcept;
+ /** @copydoc simdjson_inline simdjson_result<std::string_view> document::raw_json() noexcept */
+ simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
simdjson_inline simdjson_result<lasx::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
simdjson_inline simdjson_result<lasx::ondemand::value> at_path(std::string_view json_path) noexcept;
@@ -211690,6 +207929,7 @@ concept key_selector_type = key_selector_detail::is_key_selector<T>::value;
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
+/* amalgamation skipped (editor-only): #include "simdjson/jsonpathutil.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/key_selector.h" */
/* amalgamation skipped (editor-only): #include <vector> */
/* amalgamation skipped (editor-only): #if SIMDJSON_STATIC_REFLECTION && SIMDJSON_SUPPORTS_CONCEPTS */
@@ -212234,6 +208474,13 @@ protected:
simdjson_warn_unused simdjson_inline error_code find_field_raw(const std::string_view key) noexcept;
+ /**
+ * The recursive step of for_each_at_path_with_wildcard: matches
+ * path[index] at this level and what follows below it.
+ */
+ template <typename Func>
+ inline error_code for_each_at_path_segments(const std::vector<simdjson::internal::json_path_segment> &path, size_t index, Func &callback) noexcept;
+
value_iterator iter{};
#if SIMDJSON_DEVELOPMENT_CHECKS
bool locked{false};
@@ -213098,6 +209345,14 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) {
std::is_default_constructible_v<element_type>,
"The specified type inside the unique_ptr must default constructible.");
+ // A null pointer is serialized as null.
+ bool is_null_value;
+ SIMDJSON_TRY( val.is_null().get(is_null_value) );
+ if (is_null_value) {
+ out.reset();
+ return SUCCESS;
+ }
+
// Own the allocation before get(): a user tag_invoke may throw.
std::unique_ptr<element_type> ptr(new (std::nothrow) element_type());
if (!ptr) {
@@ -213146,6 +209401,8 @@ constexpr bool user_defined_type = (std::is_class_v<T>
#endif // SIMDJSON_SUPPORTS_CHAR8_T
&& !concepts::optional_type<T> &&
!concepts::appendable_containers<T>
+// Smart pointers are handled by the generic smart-pointer tag_invoke.
+&& !concepts::smart_pointer<T>
// simdjson's own types (array, object, value, raw_json_string, number,
// document, document_reference) have dedicated get<T>() specializations and
// must never go through reflection.
@@ -213738,251 +209995,8 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept {
#endif
}
-template <typename simdjson_value, typename T>
- requires(user_defined_type<std::remove_cvref_t<T>>)
-error_code tag_invoke(deserialize_tag, simdjson_value &val, std::unique_ptr<T> &out) noexcept(false) {
- std::unique_ptr<T> ptr(new (std::nothrow) T());
- if (!ptr) {
- return MEMALLOC;
- }
- SIMDJSON_TRY(val.get(*ptr));
- out = std::move(ptr);
- return SUCCESS;
-}
-
-template <typename simdjson_value, typename T>
- requires(user_defined_type<std::remove_cvref_t<T>>)
-error_code tag_invoke(deserialize_tag, simdjson_value &val, std::shared_ptr<T> &out) noexcept(false) {
- std::shared_ptr<T> ptr(new (std::nothrow) T());
- if (!ptr) {
- return MEMALLOC;
- }
- SIMDJSON_TRY(val.get(*ptr));
- out = std::move(ptr);
- return SUCCESS;
-}
-
#endif // SIMDJSON_STATIC_REFLECTION
-////////////////////////////////////////
-// Unique pointers
-////////////////////////////////////////
-error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<bool> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_unique<bool>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_bool().get(*out));
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<int64_t> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_unique<int64_t>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_int64().get(*out));
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<uint64_t> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_unique<uint64_t>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_uint64().get(*out));
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<double> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_unique<double>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_double().get(*out));
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<std::string_view> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_unique<std::string_view>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_string().get(*out));
- return SUCCESS;
-}
-
-
-////////////////////////////////////////
-// Shared pointers
-////////////////////////////////////////
-error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<bool> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_shared<bool>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_bool().get(*out));
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<int64_t> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_shared<int64_t>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_int64().get(*out));
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<uint64_t> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_shared<uint64_t>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_uint64().get(*out));
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<double> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_shared<double>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_double().get(*out));
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<std::string_view> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_shared<std::string_view>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_string().get(*out));
- return SUCCESS;
-}
-
-
-////////////////////////////////////////
-// Explicit optional specializations
-////////////////////////////////////////
-
-////////////////////////////////////////
-// Explicit smart pointer specializations for string and int types
-////////////////////////////////////////
-error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<std::string> &out) noexcept {
- // Check if the value is null
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset(); // Set to nullptr
- return SUCCESS;
- }
-
- if (!out) {
- out = std::make_unique<std::string>();
- }
- std::string_view str;
- SIMDJSON_TRY(val.get_string().get(str));
- *out = std::string{str};
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<std::string> &out) noexcept {
- // Check if the value is null
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset(); // Set to nullptr
- return SUCCESS;
- }
-
- if (!out) {
- out = std::make_shared<std::string>();
- }
- std::string_view str;
- SIMDJSON_TRY(val.get_string().get(str));
- *out = std::string{str};
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<int> &out) noexcept {
- // Check if the value is null
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset(); // Set to nullptr
- return SUCCESS;
- }
-
- if (!out) {
- out = std::make_unique<int>();
- }
- int64_t temp;
- SIMDJSON_TRY(val.get_int64().get(temp));
- *out = static_cast<int>(temp);
- return SUCCESS;
-}
-
} // namespace simdjson
#endif // SIMDJSON_ONDEMAND_DESERIALIZE_H
@@ -214097,10 +210111,9 @@ simdjson_inline simdjson_result<std::string_view> array::raw_json() noexcept {
const uint8_t * starting_point{iter.peek_start()};
auto error = consume();
if(error) { return error; }
- // After 'consume()', we could be left pointing just beyond the document, but that
- // is ok because we are not going to dereference the final pointer position, we just
- // use it to compute the length in bytes.
- const uint8_t * final_point{iter._json_iter->unsafe_pointer()};
+ // After 'consume()', the last token consumed is the closing bracket: we end on it
+ // rather than on the next token, which may follow white space.
+ const uint8_t * final_point{iter._json_iter->token.peek(-1) + 1};
return std::string_view(reinterpret_cast<const char*>(starting_point), size_t(final_point - starting_point));
}
@@ -214184,12 +210197,23 @@ template <typename Func>
template <typename Func>
#endif
inline error_code array::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
- auto result_pair = get_next_key_and_json_path(json_path);
- std::string_view key = result_pair.first;
- std::string_view remaining_path = result_pair.second;
+ // The path is split once: every element reached by a wildcard sees the same
+ // remaining path, so re-parsing it at each one would be quadratic.
+ const std::vector<internal::json_path_segment> path = internal::split_json_path(json_path);
+ // A path that cannot be parsed matches nothing, whatever the document: say
+ // so rather than letting the walk skip it as a mismatch below a wildcard.
+ if (path.back().key.empty()) {
+ return INVALID_JSON_POINTER;
+ }
+ return for_each_at_path_segments(path, 0, callback);
+}
+
+template <typename Func>
+inline error_code array::for_each_at_path_segments(const std::vector<internal::json_path_segment> &path, size_t index, Func &callback) noexcept {
+ std::string_view key = path[index].key;
+ const bool last = index + 1 == path.size();
// Empty key means the path segment could not be parsed (including malformed
- // bracket-quoted keys). Without this check, an empty key is treated as index
- // 0 and remaining_path may be unchanged, causing infinite recursion.
+ // bracket-quoted keys).
if (key.empty()) {
return INVALID_JSON_POINTER;
}
@@ -214198,11 +210222,11 @@ inline error_code array::for_each_at_path_with_wildcard(std::string_view json_pa
for(auto element: *this) {
value val;
SIMDJSON_TRY(element.get(val));
- if (remaining_path.empty()) {
+ if (last) {
callback(val);
} else {
// An element that does not match the rest of the path is skipped.
- error_code err = val.for_each_at_path_with_wildcard(remaining_path, callback);
+ error_code err = val.for_each_at_path_segments(path, index + 1, callback);
if (err && !internal::is_matching_failure_error(err)) { return err; }
}
}
@@ -214221,16 +210245,16 @@ inline error_code array::for_each_at_path_with_wildcard(std::string_view json_pa
value val;
if (!error) { error = at(idx).get(val); }
if (error) {
- if (error == INDEX_OUT_OF_BOUNDS && internal::json_path_has_wildcard(remaining_path)) {
+ if (error == INDEX_OUT_OF_BOUNDS && path[index].wildcard_follows) {
return SUCCESS;
}
return error;
}
- if (remaining_path.empty()){
+ if (last){
callback(val);
return SUCCESS;
} else {
- return val.for_each_at_path_with_wildcard(remaining_path, callback);
+ return val.for_each_at_path_segments(path, index + 1, callback);
}
}
}
@@ -214888,6 +210912,19 @@ template <typename Func>
template <typename Func>
#endif
inline error_code value::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
+ // The path is split once: every value reached by a wildcard sees the same
+ // remaining path, so re-parsing it at each one would be quadratic.
+ const std::vector<internal::json_path_segment> path = internal::split_json_path(json_path);
+ // A path that cannot be parsed matches nothing, whatever the document: say
+ // so rather than letting the walk skip it as a mismatch below a wildcard.
+ if (path.back().key.empty()) {
+ return INVALID_JSON_POINTER;
+ }
+ return for_each_at_path_segments(path, 0, callback);
+}
+
+template <typename Func>
+inline error_code value::for_each_at_path_segments(const std::vector<internal::json_path_segment> &path, size_t index, Func &callback) noexcept {
// Every recursive step of for_each_at_path_with_wildcard goes through this
// function, and each one descends one level into the document. A path with
// many segments applied to a deeply nested document would otherwise recurse
@@ -214896,10 +210933,16 @@ inline error_code value::for_each_at_path_with_wildcard(std::string_view json_pa
json_type t;
SIMDJSON_TRY(type().get(t));
switch (t) {
- case json_type::array:
- return (*this).get_array().for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
- case json_type::object:
- return (*this).get_object().for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
+ case json_type::array: {
+ array a;
+ SIMDJSON_TRY(get_array().get(a));
+ return a.for_each_at_path_segments(path, index, callback);
+ }
+ case json_type::object: {
+ object o;
+ SIMDJSON_TRY(get_object().get(o));
+ return o.for_each_at_path_segments(path, index, callback);
+ }
default:
return INVALID_JSON_POINTER;
}
@@ -215272,7 +211315,8 @@ simdjson_inline document document::start(json_iterator &&iter) noexcept {
}
inline void document::rewind() noexcept {
- iter.rewind();
+ // A document that is no longer alive is left as it is (see is_alive()).
+ static_cast<void>(iter.rewind());
}
inline std::string document::to_debug_string() noexcept {
@@ -215572,7 +211616,13 @@ simdjson_warn_unused simdjson_inline error_code document::consume() noexcept {
auto error = is_scalar().get(scalar);
if(error) { return error; }
if(scalar) {
- iter.return_current_and_advance();
+ // Step over the scalar and leave depth 0, as skip_child() does: otherwise a
+ // document_stream takes the next document as the rest of this one. At depth
+ // 0 the scalar has already been consumed (e.g., by get_int64()).
+ if(iter.depth() > 0) {
+ iter.return_current_and_advance();
+ iter.ascend_to(0);
+ }
return SUCCESS;
}
error = iter.skip_child(0);
@@ -215585,10 +211635,36 @@ simdjson_inline simdjson_result<std::string_view> document::raw_json() noexcept
const uint8_t * starting_point{_iter.peek_start()};
auto error = consume();
if(error) { return error; }
- // After 'consume()', we could be left pointing just beyond the document, but that
- // is ok because we are not going to dereference the final pointer position, we just
- // use it to compute the length in bytes.
- const uint8_t * final_point{iter.unsafe_pointer()};
+ // After 'consume()', the iterator points at the token after the document. In a
+ // document_stream, that is the next document, past white space and separators, or
+ // the end of the input when a truncated document follows. We end on the last byte
+ // of this document instead.
+ const uint8_t * final_point;
+ if(*starting_point == '{' || *starting_point == '[') {
+ // The last token consumed is the closing brace or bracket.
+ final_point = iter.token.peek(-1) + 1;
+ } else {
+ // A scalar is a single token: a string ends at its closing quote, anything else
+ // at the first white space, structural character, quote, RS (0x1E) or NUL.
+ const uint8_t * next_token{iter.unsafe_pointer()};
+ final_point = starting_point + 1;
+ if(*starting_point == '"') {
+ while(final_point < next_token) {
+ uint8_t c = *final_point++;
+ if(c == '\\') {
+ final_point++;
+ } else if(c == '"') {
+ break;
+ }
+ }
+ if(final_point > next_token) { final_point = next_token; }
+ } else {
+ while(final_point < next_token && !jsoncharutils::is_structural_or_whitespace(*final_point)
+ && *final_point != '"' && *final_point != 0x1E && *final_point != 0) {
+ final_point++;
+ }
+ }
+ }
return std::string_view(reinterpret_cast<const char*>(starting_point), size_t(final_point - starting_point));
}
@@ -215635,7 +211711,9 @@ simdjson_inline simdjson_result<std::string_view> document::raw_json_token() noe
}
simdjson_inline simdjson_result<value> document::at_pointer(std::string_view json_pointer) noexcept {
- rewind(); // Rewind the document each time at_pointer is called
+ // Rewind the document each time at_pointer is called. After a fatal error,
+ // the document cannot be walked again: we report the error instead.
+ SIMDJSON_TRY(iter.rewind());
if (json_pointer.empty()) {
return this->get_value();
}
@@ -215653,7 +211731,9 @@ simdjson_inline simdjson_result<value> document::at_pointer(std::string_view jso
}
simdjson_inline simdjson_result<value> document::at_path(std::string_view json_path) noexcept {
- rewind(); // Rewind the document each time at_pointer is called
+ // Rewind the document each time at_path is called. After a fatal error,
+ // the document cannot be walked again: we report the error instead.
+ SIMDJSON_TRY(iter.rewind());
if (json_path.empty()) {
return this->get_value();
}
@@ -215676,7 +211756,9 @@ template <typename Func>
template <typename Func>
#endif
simdjson_inline error_code document::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
- rewind(); // Rewind the document each time for_each_at_path_with_wildcard is called
+ // Rewind the document each time for_each_at_path_with_wildcard is called. After
+ // a fatal error, the document cannot be walked again: we report the error instead.
+ SIMDJSON_TRY(iter.rewind());
if (json_path.empty()) {
return INVALID_JSON_POINTER;
}
@@ -216014,11 +212096,6 @@ simdjson_inline simdjson_result<lasx::ondemand::number> simdjson_result<lasx::on
#if SIMDJSON_EXCEPTIONS
-template <class T, typename std::enable_if<std::is_same<T, lasx::ondemand::document>::value == false>::type>
-simdjson_inline simdjson_result<lasx::ondemand::document>::operator T() noexcept(false) {
- if (error()) { throw simdjson_error(error()); }
- return first;
-}
simdjson_inline simdjson_result<lasx::ondemand::document>::operator lasx::ondemand::array() & noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
@@ -216079,6 +212156,11 @@ simdjson_inline simdjson_result<std::string_view> simdjson_result<lasx::ondemand
return first.raw_json_token();
}
+simdjson_inline simdjson_result<std::string_view> simdjson_result<lasx::ondemand::document>::raw_json() noexcept {
+ if (error()) { return error(); }
+ return first.raw_json();
+}
+
simdjson_inline simdjson_result<lasx::ondemand::value> simdjson_result<lasx::ondemand::document>::at_pointer(std::string_view json_pointer) noexcept {
if (error()) { return error(); }
return first.at_pointer(json_pointer);
@@ -216565,6 +212647,11 @@ simdjson_inline simdjson_result<std::string_view> simdjson_result<lasx::ondemand
return first.raw_json_token();
}
+simdjson_inline simdjson_result<std::string_view> simdjson_result<lasx::ondemand::document_reference>::raw_json() noexcept {
+ if (error()) { return error(); }
+ return first.raw_json();
+}
+
simdjson_inline simdjson_result<lasx::ondemand::value> simdjson_result<lasx::ondemand::document_reference>::at_pointer(std::string_view json_pointer) noexcept {
if (error()) { return error(); }
return first.at_pointer(json_pointer);
@@ -216956,6 +213043,13 @@ simdjson_inline bool document_stream::skip_to_delimiter(uint8_t delimiter) noexc
}
inline void document_stream::next_document() noexcept {
+ // Iteration stops at the first document that met a fatal error. The error
+ // may have abandoned the document's iterator, which then no longer has a
+ // parser to walk the rest of the document with.
+ if (!doc.iter.is_alive() || doc.iter.error) {
+ error = doc.iter.error ? doc.iter.error : TAPE_ERROR;
+ return;
+ }
// A delimiter that cannot occur inside a document tells us where the current
// one ends, so we can jump there instead of walking every structural. Only
// valid while the iterator is still inside the document: a consumed document
@@ -217105,7 +213199,10 @@ simdjson_inline std::string_view document_stream::iterator::source() const noexc
cur_struct_index++;
}
- while (cur_struct_index <= static_cast<int64_t>(stream->parser->implementation->n_structural_indexes)) {
+ // The structural index at n_structural_indexes is the end of the batch, not
+ // a token: it must not be read as a closing bracket.
+ const int64_t n_structural_indexes = static_cast<int64_t>(stream->parser->implementation->n_structural_indexes);
+ while (cur_struct_index < n_structural_indexes) {
switch (stream->buf[stream->batch_start + stream->parser->implementation->structural_indexes[cur_struct_index]]) {
case '{': case '[':
depth++;
@@ -217117,6 +213214,11 @@ simdjson_inline std::string_view document_stream::iterator::source() const noexc
if (depth == 0) { break; }
cur_struct_index++;
}
+ // An invalid last document such as "[1 2" is never closed: it runs to the
+ // end of the batch, and not one byte past it.
+ if (cur_struct_index >= n_structural_indexes) {
+ return std::string_view(reinterpret_cast<const char*>(stream->buf) + current_index(), stream->batch_start + stream->parser->implementation->structural_indexes[cur_struct_index] - current_index());
+ }
return std::string_view(reinterpret_cast<const char*>(stream->buf) + current_index(), stream->parser->implementation->structural_indexes[cur_struct_index] - current_index() + stream->batch_start + 1);;
}
@@ -217133,7 +213235,12 @@ inline void document_stream::load_from_stage1_thread() noexcept {
// enough memory to swap to, as well.
std::swap(stage1_thread_parser,*parser);
error = stage1_thread_error;
- if (error) { return; }
+ // An EMPTY batch (no complete document in it, e.g. a run of white space at
+ // least batch_size long) is not the end of the stream: next() moves on to
+ // the batch after it, so queue that batch now as we would after a success.
+ // Returning here would leave no job for the next call, which then swaps
+ // the stale previous batch back in and ends the stream early.
+ if (error && error != EMPTY) { return; }
// If there's anything left, start the stage 1 thread!
if (next_batch_start() < len) {
@@ -217245,12 +213352,14 @@ simdjson_inline raw_json_string field::key() const noexcept {
simdjson_inline std::string_view field::key_raw_json_token() const noexcept {
SIMDJSON_ASSUME(first.buf != nullptr); // We would like to call .alive() by Visual Studio won't let us.
- return std::string_view(reinterpret_cast<const char*>(first.buf-1), second.iter._json_iter->token.peek(-1) - first.buf + 1);
+ // The ':' is the token just before the value. Use the value's start position
+ // rather than the current position, which moves once the value is consumed.
+ return std::string_view(reinterpret_cast<const char*>(first.buf-1), second.iter._json_iter->token.peek(second.iter.start_position() - 1) - first.buf + 1);
}
simdjson_inline std::string_view field::escaped_key() const noexcept {
SIMDJSON_ASSUME(first.buf != nullptr); // We would like to call .alive() by Visual Studio won't let us.
- auto end_quote = second.iter._json_iter->token.peek(-1);
+ auto end_quote = second.iter._json_iter->token.peek(second.iter.start_position() - 1);
while(*end_quote != '"') end_quote--;
return std::string_view(reinterpret_cast<const char*>(first.buf), end_quote - first.buf);
}
@@ -217421,11 +213530,14 @@ simdjson_inline json_iterator::json_iterator(const uint8_t *buf, ondemand::parse
}
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
-inline void json_iterator::rewind() noexcept {
+simdjson_inline error_code json_iterator::rewind() noexcept {
+ // abandon() let go of the parser: the iterator cannot be walked again.
+ if (!is_alive()) { return error ? error : UNINITIALIZED; }
token.set_position( root_position() );
logger::log_headers(); // We start again
_string_buf_loc = parser->string_buf.get();
_depth = 1;
+ return SUCCESS;
}
inline bool json_iterator::balanced() const noexcept {
@@ -217449,6 +213561,18 @@ inline bool json_iterator::balanced() const noexcept {
}
+// Depth change of each structural character when skipping: +1 for [ and {, -1 for ] and }.
+static constexpr int8_t skip_depth_delta[256] = {
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,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,
+};
+
// GCC 7 warns when the first line of this function is inlined away into oblivion due to the caller
// relating depth and parent_depth, which is a desired effect. The warning does not show up if the
// skip_child() function is not marked inline).
@@ -217504,7 +213628,30 @@ simdjson_warn_unused simdjson_inline error_code json_iterator::skip_child(depth_
break;
}
- // Now that we've considered the first value, we only increment/decrement for arrays/objects
+ // Now that we've considered the first value, we only increment/decrement for arrays/objects.
+ //
+ // Without logging, look up the depth change of each token in a table instead of branching
+ // on it: the next byte is hard to predict, so a switch mispredicts often. The loop also keeps
+ // the position and depth in locals, because the byte loads are char loads that may alias
+ // token._position and _depth, which would force a store of both on every token.
+ if (!logger::LOG_ENABLED) {
+ token_position pos = token.position();
+ const token_position end = end_position();
+ const uint8_t *const json = token.buf;
+ depth_t depth = _depth;
+ while (pos < end) {
+ depth += skip_depth_delta[json[*pos++]];
+ if (depth <= parent_depth) {
+ token.set_position(pos);
+ _depth = depth;
+ return SUCCESS;
+ }
+ }
+ token.set_position(pos);
+ _depth = depth;
+ return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "not enough close braces");
+ }
+
while (position() < end_position()) {
switch (*return_current_and_advance()) {
case '[': case '{':
@@ -217789,6 +213936,17 @@ simdjson_warn_unused simdjson_inline error_code json_iterator::optional_error(er
simdjson_warn_unused simdjson_inline bool json_iterator::copy_to_buffer(const uint8_t *json, uint32_t max_len, uint8_t *tmpbuf, size_t N) noexcept {
// This function is not expected to be called in performance-sensitive settings.
+ // peek_root_length() stops at the next structural index. Stage 1 omits the
+ // comma or RS (0x1E) that separates stream documents, so that byte and the
+ // whitespace around it remain in the span. RS is not a valid number finisher.
+ while (max_len > 0) {
+ const uint8_t last = json[max_len - 1];
+ if (last != ' ' && last != '\t' && last != '\n' && last != '\r' &&
+ last != ',' && last != 0x1E) {
+ break;
+ }
+ max_len--;
+ }
// Let us guard against silly cases:
if((N < max_len) || (N == 0)) { return false; }
// Copy to the buffer.
@@ -218430,7 +214588,9 @@ simdjson_inline simdjson_result<std::string_view> object::raw_json() noexcept {
const uint8_t * starting_point{iter.peek_start()};
auto error = consume();
if(error) { return error; }
- const uint8_t * final_point{iter._json_iter->peek()};
+ // After 'consume()', the last token consumed is the closing brace: we end on it
+ // rather than on the next token, which may follow white space.
+ const uint8_t * final_point{iter._json_iter->token.peek(-1) + 1};
return std::string_view(reinterpret_cast<const char*>(starting_point), size_t(final_point - starting_point));
}
@@ -218523,9 +214683,21 @@ template <typename Func>
template <typename Func>
#endif
inline error_code object::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
- auto result_pair = get_next_key_and_json_path(json_path);
- std::string_view key = result_pair.first;
- std::string_view remaining_path = result_pair.second;
+ // The path is split once: every field reached by a wildcard sees the same
+ // remaining path, so re-parsing it at each one would be quadratic.
+ const std::vector<internal::json_path_segment> path = internal::split_json_path(json_path);
+ // A path that cannot be parsed matches nothing, whatever the document: say
+ // so rather than letting the walk skip it as a mismatch below a wildcard.
+ if (path.back().key.empty()) {
+ return INVALID_JSON_POINTER;
+ }
+ return for_each_at_path_segments(path, 0, callback);
+}
+
+template <typename Func>
+inline error_code object::for_each_at_path_segments(const std::vector<internal::json_path_segment> &path, size_t index, Func &callback) noexcept {
+ std::string_view key = path[index].key;
+ const bool last = index + 1 == path.size();
// Empty key means the path segment could not be parsed (including malformed
// bracket-quoted keys). Match DOM at_path_with_wildcard and avoid treating
// "no progress" as a recursive lookup of "".
@@ -218538,11 +214710,11 @@ inline error_code object::for_each_at_path_with_wildcard(std::string_view json_p
for (auto field : *this) {
value val;
SIMDJSON_TRY(field.value().get(val));
- if (remaining_path.empty()) {
+ if (last) {
callback(val);
} else {
// A field that does not match the rest of the path is skipped.
- error_code err = val.for_each_at_path_with_wildcard(remaining_path, callback);
+ error_code err = val.for_each_at_path_segments(path, index + 1, callback);
if (err && !internal::is_matching_failure_error(err)) { return err; }
}
}
@@ -218551,17 +214723,17 @@ inline error_code object::for_each_at_path_with_wildcard(std::string_view json_p
value val;
error_code error = find_field(key).get(val);
if (error) {
- if (error == NO_SUCH_FIELD && internal::json_path_has_wildcard(remaining_path)) {
+ if (error == NO_SUCH_FIELD && path[index].wildcard_follows) {
return SUCCESS;
}
return error;
}
- if (remaining_path.empty()) {
+ if (last) {
callback(val);
return SUCCESS;
} else {
- return val.for_each_at_path_with_wildcard(remaining_path, callback);
+ return val.for_each_at_path_segments(path, index + 1, callback);
}
}
}
@@ -219214,6 +215386,7 @@ simdjson_inline simdjson_warn_unused bool parser::string_buffer_overflow(const u
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(padded_string_view json) & noexcept {
if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
+ json.use_trailing_whitespace_as_padding();
json.remove_utf8_bom();
_document_len = json.length();
@@ -219231,6 +215404,7 @@ simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(p
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate_allow_incomplete_json(padded_string_view json) & noexcept {
if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
+ json.use_trailing_whitespace_as_padding();
json.remove_utf8_bom();
_document_len = json.length();
@@ -219286,6 +215460,7 @@ simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(c
simdjson_warn_unused simdjson_inline simdjson_result<json_iterator> parser::iterate_raw(padded_string_view json) & noexcept {
if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
+ json.use_trailing_whitespace_as_padding();
json.remove_utf8_bom();
@@ -219312,6 +215487,7 @@ inline simdjson_result<document_stream> parser::iterate_many(const char *buf, si
}
inline simdjson_result<document_stream> parser::iterate_many(padded_string_view s, size_t batch_size) noexcept {
if (!s.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
+ s.use_trailing_whitespace_as_padding();
return iterate_many(s.data(), s.length(), batch_size);
}
inline simdjson_result<document_stream> parser::iterate_many(const padded_string &s, size_t batch_size) noexcept {
@@ -219347,6 +215523,7 @@ inline simdjson_result<document_stream> parser::iterate_many(const char *buf, si
}
inline simdjson_result<document_stream> parser::iterate_many(padded_string_view s, size_t batch_size, bool allow_comma_separated) noexcept {
if (!s.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
+ s.use_trailing_whitespace_as_padding();
return iterate_many(s.data(), s.length(), batch_size, allow_comma_separated);
}
inline simdjson_result<document_stream> parser::iterate_many(const padded_string &s, size_t batch_size, bool allow_comma_separated) noexcept {
@@ -219392,6 +215569,7 @@ inline simdjson_result<document_stream> parser::iterate_many(const char *buf, si
}
inline simdjson_result<document_stream> parser::iterate_many(padded_string_view s, size_t batch_size, stream_format format) noexcept {
if (!s.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
+ s.use_trailing_whitespace_as_padding();
return iterate_many(s.data(), s.length(), batch_size, format);
}
inline simdjson_result<document_stream> parser::iterate_many(const std::string &s, size_t batch_size, stream_format format) noexcept {
@@ -219546,17 +215724,18 @@ simdjson_inline bool raw_json_string::is_free_from_unescaped_quote(const char* t
return true;
}
-
simdjson_inline bool raw_json_string::unsafe_is_equal(size_t length, std::string_view target) const noexcept {
// If we are going to call memcmp, then we must know something about the length of the raw_json_string.
- return (length >= target.size()) && (raw()[target.size()] == '"') && !memcmp(raw(), target.data(), target.size());
+ return (length >= target.size()) && (raw()[target.size()] == '"') && !memcmp(raw(), target.data(), target.size())
+ && !escapes_trailing_quote(target);
}
simdjson_inline bool raw_json_string::unsafe_is_equal(std::string_view target) const noexcept {
// Assumptions: does not contain unescaped quote characters("), and
// the raw content is quote terminated within a valid JSON string.
if(target.size() <= SIMDJSON_PADDING) {
- return (raw()[target.size()] == '"') && !memcmp(raw(), target.data(), target.size());
+ return (raw()[target.size()] == '"') && !memcmp(raw(), target.data(), target.size())
+ && !escapes_trailing_quote(target);
}
// Past SIMDJSON_PADDING we can no longer rely on the padding to keep the
// comparison in bounds, so we must stop at the quote terminating the key.
@@ -219582,7 +215761,7 @@ simdjson_inline bool raw_json_string::is_equal(std::string_view target) const no
escaping = false;
}
}
- if(r[pos] != '"') { return false; }
+ if((r[pos] != '"') || escaping) { return false; }
return true;
}
@@ -219595,7 +215774,7 @@ simdjson_inline bool raw_json_string::unsafe_is_equal(const char * target) const
for(;target[pos];pos++) {
if(r[pos] != target[pos]) { return false; }
}
- if(r[pos] != '"') { return false; }
+ if((r[pos] != '"') || escapes_trailing_quote(std::string_view(target, pos))) { return false; }
return true;
}
@@ -219620,7 +215799,7 @@ simdjson_inline bool raw_json_string::is_equal(const char* target) const noexcep
escaping = false;
}
}
- if(r[pos] != '"') { return false; }
+ if((r[pos] != '"') || escaping) { return false; }
return true;
}
@@ -219732,7 +215911,11 @@ simdjson_inline uint32_t token_iterator::peek_index(token_position position) con
return *position;
}
simdjson_inline uint32_t token_iterator::peek_length(token_position position) const noexcept {
- return *(position+1) - *position;
+ // In the last batch of a document_stream, the index past the end of the
+ // structural indexes holds the start of the truncated bytes (see
+ // document_stream::truncated_bytes()), which can come before the end of the
+ // input: a token at the end is then empty.
+ return *(position+1) > *position ? *(position+1) - *position : 0;
}
simdjson_inline uint32_t token_iterator::peek_root_length(token_position position) const noexcept {
@@ -219747,7 +215930,7 @@ simdjson_inline uint32_t token_iterator::peek_index(int32_t delta) const noexcep
return *(_position+delta);
}
simdjson_inline uint32_t token_iterator::peek_length(int32_t delta) const noexcept {
- return *(_position+delta+1) - *(_position+delta);
+ return peek_length(_position+delta);
}
simdjson_inline token_position token_iterator::position() const noexcept {
@@ -219806,6 +215989,8 @@ simdjson_inline simdjson_result<lasx::ondemand::token_iterator>::simdjson_result
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
+#include <cstring> // std::memset (zeroing the root-number scratch padding)
+
namespace simdjson {
namespace lasx {
namespace ondemand {
@@ -219852,6 +216037,10 @@ simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::start
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::started_object() noexcept {
assert_at_container_start();
#if SIMDJSON_DEVELOPMENT_CHECKS
+ // The development checks record the start of every open container in a
+ // buffer of max_depth() entries: a document nested deeper than that is
+ // rejected, as the DOM parser does.
+ if (size_t(_depth) >= _json_iter->parser->max_depth()) { return report_error(DEPTH_ERROR, "Exceeded max depth!"); }
_json_iter->set_start_position(_depth, start_position());
#endif
if (*_json_iter->peek() == '}') {
@@ -220261,6 +216450,10 @@ inline std::string value_iterator::to_string() const noexcept {
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::started_array() noexcept {
assert_at_container_start();
+#if SIMDJSON_DEVELOPMENT_CHECKS
+ // See started_object().
+ if (size_t(_depth) >= _json_iter->parser->max_depth()) { return report_error(DEPTH_ERROR, "Exceeded max depth!"); }
+#endif
if (*_json_iter->peek() == ']') {
logger::log_value(*_json_iter, "empty array");
_json_iter->return_current_and_advance();
@@ -220466,8 +216659,10 @@ simdjson_inline simdjson_result<lasx::number_type> value_iterator::get_root_numb
// Per https://www.exploringbinary.com/maximum-number-of-decimal-digits-in-binary-floating-point-numbers/,
// 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest
// number: -0.<fraction>e-308.
- uint8_t tmpbuf[1074+8+1+1];
- tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
+ // +SIMDJSON_PADDING: the digit scan reads up to 7 bytes past the terminator.
+ uint8_t tmpbuf[1074+8+1+1+SIMDJSON_PADDING];
+ // Null terminate, and zero the padding as allocate_padded_buffer does.
+ std::memset(tmpbuf+1074+8+1, 0, 1+SIMDJSON_PADDING);
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
if(numberparsing::check_if_integer(json, max_len)) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
@@ -220488,8 +216683,10 @@ simdjson_inline simdjson_result<number> value_iterator::get_root_number(bool che
// 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest
// number: -0.<fraction>e-308.
// NOTE: the current approach doesn't work for very big integer numbers containing more than 1074 digits.
- uint8_t tmpbuf[1074+8+1+1];
- tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
+ // +SIMDJSON_PADDING: the digit scan reads up to 7 bytes past the terminator.
+ uint8_t tmpbuf[1074+8+1+1+SIMDJSON_PADDING];
+ // Null terminate, and zero the padding as allocate_padded_buffer does.
+ std::memset(tmpbuf+1074+8+1, 0, 1+SIMDJSON_PADDING);
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
if(numberparsing::check_if_integer(json, max_len)) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
@@ -220559,10 +216756,10 @@ simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> value_iterator::g
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> value_iterator::get_root_uint64_in_string(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("uint64");
- uint8_t tmpbuf[20+1+1]{}; // <20 digits> is the longest possible unsigned integer
- tmpbuf[20+1] = '\0'; // make sure that buffer is always null terminated.
- if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 20+1)) {
- logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 20 characters");
+ uint8_t tmpbuf[20+2+1]{}; // "<20 digits>" is the longest possible unsigned integer
+ tmpbuf[20+2] = '\0'; // make sure that buffer is always null terminated.
+ if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 20+2)) {
+ logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 22 characters");
return NUMBER_ERROR;
}
auto result = numberparsing::parse_unsigned_in_string(tmpbuf);
@@ -220592,10 +216789,10 @@ simdjson_warn_unused simdjson_inline simdjson_result<int64_t> value_iterator::ge
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> value_iterator::get_root_int64_in_string(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("int64");
- uint8_t tmpbuf[20+1+1]; // -<19 digits> is the longest possible integer
- tmpbuf[20+1] = '\0'; // make sure that buffer is always null terminated.
- if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 20+1)) {
- logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 20 characters");
+ uint8_t tmpbuf[20+2+1]; // "-<19 digits>" is the longest possible integer
+ tmpbuf[20+2] = '\0'; // make sure that buffer is always null terminated.
+ if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 20+2)) {
+ logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 22 characters");
return NUMBER_ERROR;
}
@@ -220612,8 +216809,10 @@ simdjson_warn_unused simdjson_inline simdjson_result<double> value_iterator::get
// Per https://www.exploringbinary.com/maximum-number-of-decimal-digits-in-binary-floating-point-numbers/,
// 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest
// number: -0.<fraction>e-308.
- uint8_t tmpbuf[1074+8+1+1]; // +1 for null termination.
- tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
+ // +SIMDJSON_PADDING: the digit scan reads up to 7 bytes past the terminator.
+ uint8_t tmpbuf[1074+8+1+1+SIMDJSON_PADDING]; // +1 for null termination.
+ // Null terminate, and zero the padding as allocate_padded_buffer does.
+ std::memset(tmpbuf+1074+8+1, 0, 1+SIMDJSON_PADDING);
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters");
return NUMBER_ERROR;
@@ -220632,8 +216831,10 @@ simdjson_warn_unused simdjson_inline simdjson_result<double> value_iterator::get
// Per https://www.exploringbinary.com/maximum-number-of-decimal-digits-in-binary-floating-point-numbers/,
// 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest
// number: -0.<fraction>e-308.
- uint8_t tmpbuf[1074+8+1+1]; // +1 for null termination.
- tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
+ // +SIMDJSON_PADDING: the digit scan reads up to 7 bytes past the terminator.
+ uint8_t tmpbuf[1074+8+1+1+SIMDJSON_PADDING]; // +1 for null termination.
+ // Null terminate, and zero the padding as allocate_padded_buffer does.
+ std::memset(tmpbuf+1074+8+1, 0, 1+SIMDJSON_PADDING);
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters");
return NUMBER_ERROR;
@@ -220652,8 +216853,10 @@ simdjson_warn_unused simdjson_inline simdjson_result<float> value_iterator::get_
// We use the same buffer size as get_root_double: the number of significant
// digits that matter is smaller for binary32, but the JSON document may still
// spell out a long number that we must parse (and round) faithfully.
- uint8_t tmpbuf[1074+8+1+1]; // +1 for null termination.
- tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
+ // +SIMDJSON_PADDING: the digit scan reads up to 7 bytes past the terminator.
+ uint8_t tmpbuf[1074+8+1+1+SIMDJSON_PADDING]; // +1 for null termination.
+ // Null terminate, and zero the padding as allocate_padded_buffer does.
+ std::memset(tmpbuf+1074+8+1, 0, 1+SIMDJSON_PADDING);
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters");
return NUMBER_ERROR;
@@ -220669,8 +216872,10 @@ simdjson_warn_unused simdjson_inline simdjson_result<float> value_iterator::get_
simdjson_warn_unused simdjson_inline simdjson_result<float> value_iterator::get_root_float_in_string(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("float");
- uint8_t tmpbuf[1074+8+1+1]; // +1 for null termination.
- tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
+ // +SIMDJSON_PADDING: the digit scan reads up to 7 bytes past the terminator.
+ uint8_t tmpbuf[1074+8+1+1+SIMDJSON_PADDING]; // +1 for null termination.
+ // Null terminate, and zero the padding as allocate_padded_buffer does.
+ std::memset(tmpbuf+1074+8+1, 0, 1+SIMDJSON_PADDING);
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters");
return NUMBER_ERROR;
@@ -220691,7 +216896,7 @@ simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::get_r
// "true" and "false" are 4 and 5 characters long, respectively.
bool value_true = (max_len >= 4 && !atomparsing::str4ncmp(json, "true") &&
(max_len == 4 || jsoncharutils::is_structural_or_whitespace(json[4])));
- bool value_false = (max_len >= 5 && !atomparsing::str4ncmp(json, "false") &&
+ bool value_false = (max_len >= 5 && !atomparsing::str4ncmp(json, "fals") && json[4] == 'e' &&
(max_len == 5 || jsoncharutils::is_structural_or_whitespace(json[5])));
if(value_true == false && value_false == false) { return incorrect_type_error("Not a boolean"); }
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
@@ -221331,49 +217536,6 @@ namespace json_path {
using ::simdjson::lasx::ondemand::value;
-// Path step types
-enum class step_type {
- field, // .field_name or ["field_name"]
- array_index // [index]
-};
-
-// Represents a single step in a JSON path
-template<std::size_t N>
-struct path_step {
- step_type type;
- char key[N]; // Field name (empty for array indices)
- std::size_t index; // Array index (0 for field access)
-
- constexpr path_step(step_type t, const char (&k)[N], std::size_t idx = 0)
- : type(t), index(idx) {
- for (std::size_t i = 0; i < N; ++i) {
- key[i] = k[i];
- }
- }
-
- constexpr std::string_view key_view() const {
- return {key, N - 1};
- }
-};
-
-// Helper to create field step
-template<std::size_t N>
-consteval auto make_field_step(const char (&name)[N]) {
- return path_step<N>(step_type::field, name, 0);
-}
-
-// Helper to create array index step
-consteval auto make_index_step(std::size_t idx) {
- return path_step<1>(step_type::array_index, "", idx);
-}
-
-// Parse state for compile-time JSON path parsing
-struct parse_result {
- bool success;
- std::size_t pos;
- std::string_view error_msg;
-};
-
// Compile-time JSON path parser
// Supports subset: .field, ["field"], [index], nested combinations
template<constevalutil::fixed_string Path>
@@ -221387,77 +217549,6 @@ struct json_path_parser {
}
return 0;
}
-
- // Count the number of steps in the path at compile time
- static consteval std::size_t count_steps() {
- std::size_t count = 0;
- std::size_t i = skip_root();
-
- while (i < path_str.size()) {
- if (path_str[i] == '.') {
- // Field access: .field
- ++i;
- if (i >= path_str.size()) break;
-
- // Skip field name
- while (i < path_str.size() && path_str[i] != '.' && path_str[i] != '[') {
- ++i;
- }
- ++count;
- } else if (path_str[i] == '[') {
- // Array or bracket notation
- ++i;
- if (i >= path_str.size()) break;
-
- if (path_str[i] == '"' || path_str[i] == '\'') {
- // Field access: ["field"] or ['field']
- char quote = path_str[i];
- ++i;
- while (i < path_str.size() && path_str[i] != quote) {
- ++i;
- }
- if (i < path_str.size()) ++i; // skip closing quote
- if (i < path_str.size() && path_str[i] == ']') ++i;
- } else {
- // Array index: [0], [123]
- while (i < path_str.size() && path_str[i] != ']') {
- ++i;
- }
- if (i < path_str.size()) ++i; // skip ]
- }
- ++count;
- } else {
- ++i;
- }
- }
-
- return count;
- }
-
- // Parse a field name at compile time
- static consteval std::size_t parse_field_name(std::size_t start, char* out, std::size_t max_len) {
- std::size_t len = 0;
- std::size_t i = start;
-
- while (i < path_str.size() && path_str[i] != '.' && path_str[i] != '[' && len < max_len - 1) {
- out[len++] = path_str[i++];
- }
- out[len] = '\0';
- return i;
- }
-
- // Parse an array index at compile time
- static consteval std::pair<std::size_t, std::size_t> parse_array_index(std::size_t start) {
- std::size_t index = 0;
- std::size_t i = start;
-
- while (i < path_str.size() && path_str[i] >= '0' && path_str[i] <= '9') {
- index = index * 10 + (path_str[i] - '0');
- ++i;
- }
-
- return {i, index};
- }
};
// Compile-time path accessor generator
@@ -221466,7 +217557,6 @@ struct path_accessor {
using value = ::simdjson::lasx::ondemand::value;
static constexpr auto parser = json_path_parser<Path>();
- static constexpr std::size_t num_steps = parser.count_steps();
static constexpr std::string_view path_view = Path.view();
// Compile-time accessor generation
@@ -221581,93 +217671,6 @@ private:
return current_type;
}
-private:
- // Walk path and extract directly into final field using compile-time reflection
- template<std::meta::info CurrentType, std::size_t PathPos, typename TargetType>
- static inline error_code extract_with_reflection(simdjson_result<value> current, TargetType& target_ref) noexcept {
- if (current.error()) return current.error();
-
- // Base case: end of path - extract into target
- if constexpr (PathPos >= path_view.size()) {
- return current.get(target_ref);
- }
- // Field access: .field_name
- else if constexpr (path_view[PathPos] == '.') {
- constexpr auto field_info = parse_next_field(PathPos);
- constexpr std::string_view field_name = std::get<0>(field_info);
- constexpr std::size_t next_pos = std::get<1>(field_info);
-
- constexpr auto member_info = find_member_by_name(CurrentType, field_name);
- static_assert(member_info != ^^void, "Field not found in struct");
-
- constexpr auto member_type = std::meta::type_of(member_info);
-
- auto obj_result = current.get_object();
- if (obj_result.error()) return obj_result.error();
- auto obj = obj_result.value_unsafe();
- auto field_value = obj.find_field_unordered(field_name);
-
- if constexpr (next_pos >= path_view.size()) {
- return field_value.get(target_ref);
- } else {
- return extract_with_reflection<member_type, next_pos>(field_value, target_ref);
- }
- }
- // Bracket notation: [index] or ["field"]
- else if constexpr (path_view[PathPos] == '[') {
- constexpr auto bracket_info = parse_bracket(PathPos);
- constexpr bool is_field = std::get<0>(bracket_info);
- constexpr std::size_t next_pos = std::get<2>(bracket_info);
-
- if constexpr (is_field) {
- constexpr std::string_view field_name = std::get<1>(bracket_info);
- constexpr auto member_info = find_member_by_name(CurrentType, field_name);
- static_assert(member_info != ^^void, "Field not found in struct");
- constexpr auto member_type = std::meta::type_of(member_info);
-
- auto obj_result = current.get_object();
- if (obj_result.error()) return obj_result.error();
- auto obj = obj_result.value_unsafe();
- auto field_value = obj.find_field_unordered(field_name);
-
- if constexpr (next_pos >= path_view.size()) {
- return field_value.get(target_ref);
- } else {
- return extract_with_reflection<member_type, next_pos>(field_value, target_ref);
- }
- } else {
- constexpr std::size_t index = std::get<3>(bracket_info);
- constexpr auto elem_type = get_element_type_reflected(CurrentType);
- static_assert(elem_type != ^^void, "Could not determine array element type");
-
- auto arr_result = current.get_array();
- if (arr_result.error()) return arr_result.error();
- auto arr = arr_result.value_unsafe();
- auto elem_value = arr.at(index);
-
- if constexpr (next_pos >= path_view.size()) {
- return elem_value.get(target_ref);
- } else {
- return extract_with_reflection<elem_type, next_pos>(elem_value, target_ref);
- }
- }
- }
- // Skip unexpected characters and continue
- else {
- return extract_with_reflection<CurrentType, PathPos + 1>(current, target_ref);
- }
- }
-
- // Find member by name in reflected type
- static consteval std::meta::info find_member_by_name(std::meta::info type_refl, std::string_view name) {
- auto members = std::meta::nonstatic_data_members_of(type_refl, std::meta::access_context::unchecked());
- for (auto mem : members) {
- if (std::meta::identifier_of(mem) == name) {
- return mem;
- }
- }
- }
-
// Generate compile-time accessor code by walking the path
template<std::size_t PathPos>
static inline simdjson_result<value> access_impl(simdjson_result<value> current) noexcept {
@@ -221797,55 +217800,6 @@ public:
}
private:
- // Check if type has member with given name
- template<typename Type>
- static consteval bool has_member(std::string_view member_name) {
- constexpr auto members = std::meta::nonstatic_data_members_of(^^Type, std::meta::access_context::unchecked());
- for (auto mem : members) {
- if (std::meta::identifier_of(mem) == member_name) {
- return true;
- }
- }
- return false;
- }
-
- // Get type of member by name
- template<typename Type>
- static consteval auto get_member_type(std::string_view member_name) {
- constexpr auto members = std::meta::nonstatic_data_members_of(^^Type, std::meta::access_context::unchecked());
- for (auto mem : members) {
- if (std::meta::identifier_of(mem) == member_name) {
- return std::meta::type_of(mem);
- }
- }
- return ^^void;
- }
-
- // Check if non-reflected type is array-like
- template<typename Type>
- static consteval bool is_container_type() {
- using BaseType = std::remove_cvref_t<Type>;
- if constexpr (requires { typename BaseType::value_type; }) {
- return true;
- }
- if constexpr (std::is_array_v<BaseType>) {
- return true;
- }
- return false;
- }
-
- // Extract element type from non-reflected container
- template<typename Type>
- using extract_element_type = std::conditional_t<
- requires { typename std::remove_cvref_t<Type>::value_type; },
- typename std::remove_cvref_t<Type>::value_type,
- std::conditional_t<
- std::is_array_v<std::remove_cvref_t<Type>>,
- std::remove_extent_t<std::remove_cvref_t<Type>>,
- void
- >
- >;
-
// Validate path matches struct definition
static consteval bool validate_path() {
if constexpr (!std::is_class_v<T>) {
@@ -222787,23 +218741,6 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin
return { (v == '\\').to_bitmask(), (v == '"').to_bitmask() };
}
-struct escaping {
- static constexpr uint64_t BYTES_PROCESSED = sizeof(simd8<uint8_t>);
- simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
-
- simdjson_inline bool has_escape() { return escape_bits != 0; }
- simdjson_inline int escape_index() { return trailing_zeroes(escape_bits) / 4; }
-
- uint64_t escape_bits;
-}; // struct escaping
-
-simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
- static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes");
- simd8<uint8_t> v(src);
- v.store(dst);
- return { ((v == '"') | (v == '\\') | (v == 32)).to_bitmask() };
-}
-
} // unnamed namespace
} // namespace rvv_vls
@@ -223075,11 +219012,6 @@ protected:
public:
simdjson_inline value_iterator() noexcept = default;
- /**
- * Denote that we're starting a document.
- */
- simdjson_inline void start_document() noexcept;
-
/**
* Skips a non-iterated or partially-iterated JSON value, whether it is a scalar, array or object.
*
@@ -223480,16 +219412,7 @@ protected:
simdjson_warn_unused simdjson_inline error_code start_container(uint8_t start_char, const char *incorrect_type_message, const char *type) noexcept;
simdjson_warn_unused simdjson_inline error_code end_container() noexcept;
- /**
- * Advance to a place expecting a value (increasing depth).
- *
- * @return The current token (the one left behind).
- * @error TAPE_ERROR If the document ended early.
- */
- simdjson_inline simdjson_result<const uint8_t *> advance_to_value() noexcept;
-
simdjson_warn_unused simdjson_inline error_code incorrect_type_error(const char *message) const noexcept;
- simdjson_warn_unused simdjson_inline error_code error_unless_more_tokens(uint32_t tokens=1) const noexcept;
simdjson_inline bool is_at_start() const noexcept;
/**
@@ -223572,6 +219495,7 @@ public:
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
+/* amalgamation skipped (editor-only): #include "simdjson/jsonpathutil.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/deserialize.h" */
/* amalgamation skipped (editor-only): #include <vector> */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
@@ -224425,11 +220349,19 @@ protected:
*/
simdjson_inline simdjson_result<object> start_or_resume_object() noexcept;
+ /**
+ * The recursive step of for_each_at_path_with_wildcard: matches
+ * path[index] at this level and what follows below it.
+ */
+ template <typename Func>
+ inline error_code for_each_at_path_segments(const std::vector<simdjson::internal::json_path_segment> &path, size_t index, Func &callback) noexcept;
+
// simdjson_inline void log_value(const char *type) const noexcept;
// simdjson_inline void log_error(const char *message) const noexcept;
value_iterator iter{};
+ friend class array;
friend class document;
friend class array_iterator;
friend class field;
@@ -225118,6 +221050,7 @@ public:
/**
* Take an input in json containing max_len characters and attempt to copy it over to tmpbuf, a buffer with
* N bytes of capacity. It will return false if N is too small (smaller than max_len) of if it is zero.
+ * Trailing whitespace, commas, and record separators (0x1E) do not count toward max_len.
* The buffer (tmpbuf) is padded with space characters.
*/
simdjson_warn_unused simdjson_inline bool copy_to_buffer(const uint8_t *json, uint32_t max_len, uint8_t *tmpbuf, size_t N) noexcept;
@@ -225150,8 +221083,11 @@ public:
/**
* Updates this json iterator so that it is back at the beginning of the document,
* as if it had just been created.
+ *
+ * An iterator that is no longer alive (e.g., abandoned after a fatal error) has no
+ * document to go back to: it is left as is and its error is returned.
*/
- inline void rewind() noexcept;
+ simdjson_inline error_code rewind() noexcept;
/**
* This checks whether the {,},[,] are balanced so that the document
* ends with proper zero depth. This requires scanning the whole document
@@ -225506,6 +221442,28 @@ public:
private:
+ /**
+ * Returns true if target ends with an odd number of backslashes. The last one
+ * then escapes the quote that follows the matching bytes in the raw string, so
+ * that quote does not end the string. Not inlined: callers check the last byte
+ * first, and keys ending in a backslash are rare.
+ */
+ static simdjson_never_inline bool ends_with_unpaired_backslash(std::string_view target) noexcept {
+ size_t backslash_count{0};
+ while(backslash_count < target.size() && target[target.size() - 1 - backslash_count] == '\\') {
+ backslash_count++;
+ }
+ return backslash_count % 2 == 1;
+ }
+
+ /**
+ * True when a byte-for-byte match must be rejected because target escapes the
+ * closing quote. The last-byte test keeps the counting loop off the common path.
+ */
+ static simdjson_inline bool escapes_trailing_quote(std::string_view target) noexcept {
+ return simdjson_unlikely(!target.empty() && target[target.size() - 1] == '\\')
+ && ends_with_unpaired_backslash(target);
+ }
/**
* This will set the inner pointer to zero, effectively making
@@ -226079,6 +222037,7 @@ public:
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
+/* amalgamation skipped (editor-only): #include "simdjson/jsonpathutil.h" */
/* amalgamation skipped (editor-only): #include <vector> */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
@@ -226271,6 +222230,13 @@ protected:
*/
simdjson_warn_unused simdjson_inline error_code consume() noexcept;
+ /**
+ * The recursive step of for_each_at_path_with_wildcard: matches
+ * path[index] at this level and what follows below it.
+ */
+ template <typename Func>
+ inline error_code for_each_at_path_segments(const std::vector<simdjson::internal::json_path_segment> &path, size_t index, Func &callback) noexcept;
+
/**
* Begin array iteration.
*
@@ -227280,6 +223246,9 @@ public:
* Reset the iterator inside the document instance so we are pointing back at the
* beginning of the document, as if it had just been created. It invalidates all
* values, objects and arrays that you have created so far (including unescaped strings).
+ *
+ * A document that is no longer alive (see is_alive()) cannot be rewound: it is
+ * left as is.
*/
inline void rewind() noexcept;
/**
@@ -227353,6 +223322,8 @@ public:
* - INCORRECT_TYPE if a non-integer is used to access an array
* - INVALID_JSON_POINTER if the JSON pointer is invalid and cannot be parsed
* - SCALAR_DOCUMENT_AS_VALUE if the json_pointer is empty and the document is not a scalar (see is_scalar() function).
+ * - the fatal error (e.g., TAPE_ERROR) that ended an earlier call, as the document can
+ * no longer be rewound (see is_alive()).
*/
simdjson_inline simdjson_result<value> at_pointer(std::string_view json_pointer) noexcept;
@@ -227376,6 +223347,8 @@ public:
* - NO_SUCH_FIELD if a field does not exist in an object
* - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length
* - INCORRECT_TYPE if a non-integer is used to access an array
+ * - the fatal error (e.g., TAPE_ERROR) that ended an earlier call, as the document can
+ * no longer be rewound (see is_alive()).
*/
simdjson_inline simdjson_result<value> at_path(std::string_view json_path) noexcept;
@@ -227402,7 +223375,9 @@ public:
/**
* Consumes the document and returns a string_view instance corresponding to the
* document as represented in JSON. It points inside the original byte array containing
- * the JSON document.
+ * the JSON document. It starts at the first byte of the document and ends at its last
+ * byte: it does not include the white space that follows the document, nor, in a
+ * document_stream, the separators or the documents that follow it.
*/
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
@@ -227729,8 +223704,6 @@ public:
using rvv_vls::implementation_simdjson_result_base<rvv_vls::ondemand::document>::operator*;
using rvv_vls::implementation_simdjson_result_base<rvv_vls::ondemand::document>::operator->;
- template <class T, typename std::enable_if<std::is_same<T, rvv_vls::ondemand::document>::value == false>::type>
- explicit simdjson_inline operator T() noexcept(false);
simdjson_inline operator rvv_vls::ondemand::array() & noexcept(false);
simdjson_inline operator rvv_vls::ondemand::object() & noexcept(false);
explicit simdjson_inline operator uint64_t() noexcept(false);
@@ -227765,6 +223738,8 @@ public:
simdjson_inline simdjson_result<rvv_vls::ondemand::number> get_number() noexcept;
/** @copydoc simdjson_inline std::string_view document::raw_json_token() const noexcept */
simdjson_inline simdjson_result<std::string_view> raw_json_token() noexcept;
+ /** @copydoc simdjson_inline simdjson_result<std::string_view> document::raw_json() noexcept */
+ simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
simdjson_inline simdjson_result<rvv_vls::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
simdjson_inline simdjson_result<rvv_vls::ondemand::value> at_path(std::string_view json_path) noexcept;
@@ -227891,6 +223866,8 @@ public:
simdjson_inline simdjson_result<rvv_vls::ondemand::number> get_number() noexcept;
/** @copydoc simdjson_inline std::string_view document_reference::raw_json_token() const noexcept */
simdjson_inline simdjson_result<std::string_view> raw_json_token() noexcept;
+ /** @copydoc simdjson_inline simdjson_result<std::string_view> document::raw_json() noexcept */
+ simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
simdjson_inline simdjson_result<rvv_vls::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
simdjson_inline simdjson_result<rvv_vls::ondemand::value> at_path(std::string_view json_path) noexcept;
@@ -229852,6 +225829,7 @@ concept key_selector_type = key_selector_detail::is_key_selector<T>::value;
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
+/* amalgamation skipped (editor-only): #include "simdjson/jsonpathutil.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/key_selector.h" */
/* amalgamation skipped (editor-only): #include <vector> */
/* amalgamation skipped (editor-only): #if SIMDJSON_STATIC_REFLECTION && SIMDJSON_SUPPORTS_CONCEPTS */
@@ -230396,6 +226374,13 @@ protected:
simdjson_warn_unused simdjson_inline error_code find_field_raw(const std::string_view key) noexcept;
+ /**
+ * The recursive step of for_each_at_path_with_wildcard: matches
+ * path[index] at this level and what follows below it.
+ */
+ template <typename Func>
+ inline error_code for_each_at_path_segments(const std::vector<simdjson::internal::json_path_segment> &path, size_t index, Func &callback) noexcept;
+
value_iterator iter{};
#if SIMDJSON_DEVELOPMENT_CHECKS
bool locked{false};
@@ -231260,6 +227245,14 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) {
std::is_default_constructible_v<element_type>,
"The specified type inside the unique_ptr must default constructible.");
+ // A null pointer is serialized as null.
+ bool is_null_value;
+ SIMDJSON_TRY( val.is_null().get(is_null_value) );
+ if (is_null_value) {
+ out.reset();
+ return SUCCESS;
+ }
+
// Own the allocation before get(): a user tag_invoke may throw.
std::unique_ptr<element_type> ptr(new (std::nothrow) element_type());
if (!ptr) {
@@ -231308,6 +227301,8 @@ constexpr bool user_defined_type = (std::is_class_v<T>
#endif // SIMDJSON_SUPPORTS_CHAR8_T
&& !concepts::optional_type<T> &&
!concepts::appendable_containers<T>
+// Smart pointers are handled by the generic smart-pointer tag_invoke.
+&& !concepts::smart_pointer<T>
// simdjson's own types (array, object, value, raw_json_string, number,
// document, document_reference) have dedicated get<T>() specializations and
// must never go through reflection.
@@ -231900,251 +227895,8 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept {
#endif
}
-template <typename simdjson_value, typename T>
- requires(user_defined_type<std::remove_cvref_t<T>>)
-error_code tag_invoke(deserialize_tag, simdjson_value &val, std::unique_ptr<T> &out) noexcept(false) {
- std::unique_ptr<T> ptr(new (std::nothrow) T());
- if (!ptr) {
- return MEMALLOC;
- }
- SIMDJSON_TRY(val.get(*ptr));
- out = std::move(ptr);
- return SUCCESS;
-}
-
-template <typename simdjson_value, typename T>
- requires(user_defined_type<std::remove_cvref_t<T>>)
-error_code tag_invoke(deserialize_tag, simdjson_value &val, std::shared_ptr<T> &out) noexcept(false) {
- std::shared_ptr<T> ptr(new (std::nothrow) T());
- if (!ptr) {
- return MEMALLOC;
- }
- SIMDJSON_TRY(val.get(*ptr));
- out = std::move(ptr);
- return SUCCESS;
-}
-
#endif // SIMDJSON_STATIC_REFLECTION
-////////////////////////////////////////
-// Unique pointers
-////////////////////////////////////////
-error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<bool> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_unique<bool>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_bool().get(*out));
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<int64_t> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_unique<int64_t>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_int64().get(*out));
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<uint64_t> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_unique<uint64_t>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_uint64().get(*out));
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<double> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_unique<double>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_double().get(*out));
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<std::string_view> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_unique<std::string_view>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_string().get(*out));
- return SUCCESS;
-}
-
-
-////////////////////////////////////////
-// Shared pointers
-////////////////////////////////////////
-error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<bool> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_shared<bool>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_bool().get(*out));
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<int64_t> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_shared<int64_t>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_int64().get(*out));
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<uint64_t> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_shared<uint64_t>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_uint64().get(*out));
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<double> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_shared<double>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_double().get(*out));
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<std::string_view> &out) noexcept {
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset();
- return SUCCESS;
- }
- if (!out) {
- out = std::make_shared<std::string_view>();
- if (!out) { return MEMALLOC; }
- }
- SIMDJSON_TRY(val.get_string().get(*out));
- return SUCCESS;
-}
-
-
-////////////////////////////////////////
-// Explicit optional specializations
-////////////////////////////////////////
-
-////////////////////////////////////////
-// Explicit smart pointer specializations for string and int types
-////////////////////////////////////////
-error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<std::string> &out) noexcept {
- // Check if the value is null
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset(); // Set to nullptr
- return SUCCESS;
- }
-
- if (!out) {
- out = std::make_unique<std::string>();
- }
- std::string_view str;
- SIMDJSON_TRY(val.get_string().get(str));
- *out = std::string{str};
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<std::string> &out) noexcept {
- // Check if the value is null
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset(); // Set to nullptr
- return SUCCESS;
- }
-
- if (!out) {
- out = std::make_shared<std::string>();
- }
- std::string_view str;
- SIMDJSON_TRY(val.get_string().get(str));
- *out = std::string{str};
- return SUCCESS;
-}
-
-error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<int> &out) noexcept {
- // Check if the value is null
- bool is_null_value;
- SIMDJSON_TRY( val.is_null().get(is_null_value) );
- if (is_null_value) {
- out.reset(); // Set to nullptr
- return SUCCESS;
- }
-
- if (!out) {
- out = std::make_unique<int>();
- }
- int64_t temp;
- SIMDJSON_TRY(val.get_int64().get(temp));
- *out = static_cast<int>(temp);
- return SUCCESS;
-}
-
} // namespace simdjson
#endif // SIMDJSON_ONDEMAND_DESERIALIZE_H
@@ -232259,10 +228011,9 @@ simdjson_inline simdjson_result<std::string_view> array::raw_json() noexcept {
const uint8_t * starting_point{iter.peek_start()};
auto error = consume();
if(error) { return error; }
- // After 'consume()', we could be left pointing just beyond the document, but that
- // is ok because we are not going to dereference the final pointer position, we just
- // use it to compute the length in bytes.
- const uint8_t * final_point{iter._json_iter->unsafe_pointer()};
+ // After 'consume()', the last token consumed is the closing bracket: we end on it
+ // rather than on the next token, which may follow white space.
+ const uint8_t * final_point{iter._json_iter->token.peek(-1) + 1};
return std::string_view(reinterpret_cast<const char*>(starting_point), size_t(final_point - starting_point));
}
@@ -232346,12 +228097,23 @@ template <typename Func>
template <typename Func>
#endif
inline error_code array::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
- auto result_pair = get_next_key_and_json_path(json_path);
- std::string_view key = result_pair.first;
- std::string_view remaining_path = result_pair.second;
+ // The path is split once: every element reached by a wildcard sees the same
+ // remaining path, so re-parsing it at each one would be quadratic.
+ const std::vector<internal::json_path_segment> path = internal::split_json_path(json_path);
+ // A path that cannot be parsed matches nothing, whatever the document: say
+ // so rather than letting the walk skip it as a mismatch below a wildcard.
+ if (path.back().key.empty()) {
+ return INVALID_JSON_POINTER;
+ }
+ return for_each_at_path_segments(path, 0, callback);
+}
+
+template <typename Func>
+inline error_code array::for_each_at_path_segments(const std::vector<internal::json_path_segment> &path, size_t index, Func &callback) noexcept {
+ std::string_view key = path[index].key;
+ const bool last = index + 1 == path.size();
// Empty key means the path segment could not be parsed (including malformed
- // bracket-quoted keys). Without this check, an empty key is treated as index
- // 0 and remaining_path may be unchanged, causing infinite recursion.
+ // bracket-quoted keys).
if (key.empty()) {
return INVALID_JSON_POINTER;
}
@@ -232360,11 +228122,11 @@ inline error_code array::for_each_at_path_with_wildcard(std::string_view json_pa
for(auto element: *this) {
value val;
SIMDJSON_TRY(element.get(val));
- if (remaining_path.empty()) {
+ if (last) {
callback(val);
} else {
// An element that does not match the rest of the path is skipped.
- error_code err = val.for_each_at_path_with_wildcard(remaining_path, callback);
+ error_code err = val.for_each_at_path_segments(path, index + 1, callback);
if (err && !internal::is_matching_failure_error(err)) { return err; }
}
}
@@ -232383,16 +228145,16 @@ inline error_code array::for_each_at_path_with_wildcard(std::string_view json_pa
value val;
if (!error) { error = at(idx).get(val); }
if (error) {
- if (error == INDEX_OUT_OF_BOUNDS && internal::json_path_has_wildcard(remaining_path)) {
+ if (error == INDEX_OUT_OF_BOUNDS && path[index].wildcard_follows) {
return SUCCESS;
}
return error;
}
- if (remaining_path.empty()){
+ if (last){
callback(val);
return SUCCESS;
} else {
- return val.for_each_at_path_with_wildcard(remaining_path, callback);
+ return val.for_each_at_path_segments(path, index + 1, callback);
}
}
}
@@ -233050,6 +228812,19 @@ template <typename Func>
template <typename Func>
#endif
inline error_code value::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
+ // The path is split once: every value reached by a wildcard sees the same
+ // remaining path, so re-parsing it at each one would be quadratic.
+ const std::vector<internal::json_path_segment> path = internal::split_json_path(json_path);
+ // A path that cannot be parsed matches nothing, whatever the document: say
+ // so rather than letting the walk skip it as a mismatch below a wildcard.
+ if (path.back().key.empty()) {
+ return INVALID_JSON_POINTER;
+ }
+ return for_each_at_path_segments(path, 0, callback);
+}
+
+template <typename Func>
+inline error_code value::for_each_at_path_segments(const std::vector<internal::json_path_segment> &path, size_t index, Func &callback) noexcept {
// Every recursive step of for_each_at_path_with_wildcard goes through this
// function, and each one descends one level into the document. A path with
// many segments applied to a deeply nested document would otherwise recurse
@@ -233058,10 +228833,16 @@ inline error_code value::for_each_at_path_with_wildcard(std::string_view json_pa
json_type t;
SIMDJSON_TRY(type().get(t));
switch (t) {
- case json_type::array:
- return (*this).get_array().for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
- case json_type::object:
- return (*this).get_object().for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
+ case json_type::array: {
+ array a;
+ SIMDJSON_TRY(get_array().get(a));
+ return a.for_each_at_path_segments(path, index, callback);
+ }
+ case json_type::object: {
+ object o;
+ SIMDJSON_TRY(get_object().get(o));
+ return o.for_each_at_path_segments(path, index, callback);
+ }
default:
return INVALID_JSON_POINTER;
}
@@ -233434,7 +229215,8 @@ simdjson_inline document document::start(json_iterator &&iter) noexcept {
}
inline void document::rewind() noexcept {
- iter.rewind();
+ // A document that is no longer alive is left as it is (see is_alive()).
+ static_cast<void>(iter.rewind());
}
inline std::string document::to_debug_string() noexcept {
@@ -233734,7 +229516,13 @@ simdjson_warn_unused simdjson_inline error_code document::consume() noexcept {
auto error = is_scalar().get(scalar);
if(error) { return error; }
if(scalar) {
- iter.return_current_and_advance();
+ // Step over the scalar and leave depth 0, as skip_child() does: otherwise a
+ // document_stream takes the next document as the rest of this one. At depth
+ // 0 the scalar has already been consumed (e.g., by get_int64()).
+ if(iter.depth() > 0) {
+ iter.return_current_and_advance();
+ iter.ascend_to(0);
+ }
return SUCCESS;
}
error = iter.skip_child(0);
@@ -233747,10 +229535,36 @@ simdjson_inline simdjson_result<std::string_view> document::raw_json() noexcept
const uint8_t * starting_point{_iter.peek_start()};
auto error = consume();
if(error) { return error; }
- // After 'consume()', we could be left pointing just beyond the document, but that
- // is ok because we are not going to dereference the final pointer position, we just
- // use it to compute the length in bytes.
- const uint8_t * final_point{iter.unsafe_pointer()};
+ // After 'consume()', the iterator points at the token after the document. In a
+ // document_stream, that is the next document, past white space and separators, or
+ // the end of the input when a truncated document follows. We end on the last byte
+ // of this document instead.
+ const uint8_t * final_point;
+ if(*starting_point == '{' || *starting_point == '[') {
+ // The last token consumed is the closing brace or bracket.
+ final_point = iter.token.peek(-1) + 1;
+ } else {
+ // A scalar is a single token: a string ends at its closing quote, anything else
+ // at the first white space, structural character, quote, RS (0x1E) or NUL.
+ const uint8_t * next_token{iter.unsafe_pointer()};
+ final_point = starting_point + 1;
+ if(*starting_point == '"') {
+ while(final_point < next_token) {
+ uint8_t c = *final_point++;
+ if(c == '\\') {
+ final_point++;
+ } else if(c == '"') {
+ break;
+ }
+ }
+ if(final_point > next_token) { final_point = next_token; }
+ } else {
+ while(final_point < next_token && !jsoncharutils::is_structural_or_whitespace(*final_point)
+ && *final_point != '"' && *final_point != 0x1E && *final_point != 0) {
+ final_point++;
+ }
+ }
+ }
return std::string_view(reinterpret_cast<const char*>(starting_point), size_t(final_point - starting_point));
}
@@ -233797,7 +229611,9 @@ simdjson_inline simdjson_result<std::string_view> document::raw_json_token() noe
}
simdjson_inline simdjson_result<value> document::at_pointer(std::string_view json_pointer) noexcept {
- rewind(); // Rewind the document each time at_pointer is called
+ // Rewind the document each time at_pointer is called. After a fatal error,
+ // the document cannot be walked again: we report the error instead.
+ SIMDJSON_TRY(iter.rewind());
if (json_pointer.empty()) {
return this->get_value();
}
@@ -233815,7 +229631,9 @@ simdjson_inline simdjson_result<value> document::at_pointer(std::string_view jso
}
simdjson_inline simdjson_result<value> document::at_path(std::string_view json_path) noexcept {
- rewind(); // Rewind the document each time at_pointer is called
+ // Rewind the document each time at_path is called. After a fatal error,
+ // the document cannot be walked again: we report the error instead.
+ SIMDJSON_TRY(iter.rewind());
if (json_path.empty()) {
return this->get_value();
}
@@ -233838,7 +229656,9 @@ template <typename Func>
template <typename Func>
#endif
simdjson_inline error_code document::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
- rewind(); // Rewind the document each time for_each_at_path_with_wildcard is called
+ // Rewind the document each time for_each_at_path_with_wildcard is called. After
+ // a fatal error, the document cannot be walked again: we report the error instead.
+ SIMDJSON_TRY(iter.rewind());
if (json_path.empty()) {
return INVALID_JSON_POINTER;
}
@@ -234176,11 +229996,6 @@ simdjson_inline simdjson_result<rvv_vls::ondemand::number> simdjson_result<rvv_v
#if SIMDJSON_EXCEPTIONS
-template <class T, typename std::enable_if<std::is_same<T, rvv_vls::ondemand::document>::value == false>::type>
-simdjson_inline simdjson_result<rvv_vls::ondemand::document>::operator T() noexcept(false) {
- if (error()) { throw simdjson_error(error()); }
- return first;
-}
simdjson_inline simdjson_result<rvv_vls::ondemand::document>::operator rvv_vls::ondemand::array() & noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
@@ -234241,6 +230056,11 @@ simdjson_inline simdjson_result<std::string_view> simdjson_result<rvv_vls::ondem
return first.raw_json_token();
}
+simdjson_inline simdjson_result<std::string_view> simdjson_result<rvv_vls::ondemand::document>::raw_json() noexcept {
+ if (error()) { return error(); }
+ return first.raw_json();
+}
+
simdjson_inline simdjson_result<rvv_vls::ondemand::value> simdjson_result<rvv_vls::ondemand::document>::at_pointer(std::string_view json_pointer) noexcept {
if (error()) { return error(); }
return first.at_pointer(json_pointer);
@@ -234727,6 +230547,11 @@ simdjson_inline simdjson_result<std::string_view> simdjson_result<rvv_vls::ondem
return first.raw_json_token();
}
+simdjson_inline simdjson_result<std::string_view> simdjson_result<rvv_vls::ondemand::document_reference>::raw_json() noexcept {
+ if (error()) { return error(); }
+ return first.raw_json();
+}
+
simdjson_inline simdjson_result<rvv_vls::ondemand::value> simdjson_result<rvv_vls::ondemand::document_reference>::at_pointer(std::string_view json_pointer) noexcept {
if (error()) { return error(); }
return first.at_pointer(json_pointer);
@@ -235118,6 +230943,13 @@ simdjson_inline bool document_stream::skip_to_delimiter(uint8_t delimiter) noexc
}
inline void document_stream::next_document() noexcept {
+ // Iteration stops at the first document that met a fatal error. The error
+ // may have abandoned the document's iterator, which then no longer has a
+ // parser to walk the rest of the document with.
+ if (!doc.iter.is_alive() || doc.iter.error) {
+ error = doc.iter.error ? doc.iter.error : TAPE_ERROR;
+ return;
+ }
// A delimiter that cannot occur inside a document tells us where the current
// one ends, so we can jump there instead of walking every structural. Only
// valid while the iterator is still inside the document: a consumed document
@@ -235267,7 +231099,10 @@ simdjson_inline std::string_view document_stream::iterator::source() const noexc
cur_struct_index++;
}
- while (cur_struct_index <= static_cast<int64_t>(stream->parser->implementation->n_structural_indexes)) {
+ // The structural index at n_structural_indexes is the end of the batch, not
+ // a token: it must not be read as a closing bracket.
+ const int64_t n_structural_indexes = static_cast<int64_t>(stream->parser->implementation->n_structural_indexes);
+ while (cur_struct_index < n_structural_indexes) {
switch (stream->buf[stream->batch_start + stream->parser->implementation->structural_indexes[cur_struct_index]]) {
case '{': case '[':
depth++;
@@ -235279,6 +231114,11 @@ simdjson_inline std::string_view document_stream::iterator::source() const noexc
if (depth == 0) { break; }
cur_struct_index++;
}
+ // An invalid last document such as "[1 2" is never closed: it runs to the
+ // end of the batch, and not one byte past it.
+ if (cur_struct_index >= n_structural_indexes) {
+ return std::string_view(reinterpret_cast<const char*>(stream->buf) + current_index(), stream->batch_start + stream->parser->implementation->structural_indexes[cur_struct_index] - current_index());
+ }
return std::string_view(reinterpret_cast<const char*>(stream->buf) + current_index(), stream->parser->implementation->structural_indexes[cur_struct_index] - current_index() + stream->batch_start + 1);;
}
@@ -235295,7 +231135,12 @@ inline void document_stream::load_from_stage1_thread() noexcept {
// enough memory to swap to, as well.
std::swap(stage1_thread_parser,*parser);
error = stage1_thread_error;
- if (error) { return; }
+ // An EMPTY batch (no complete document in it, e.g. a run of white space at
+ // least batch_size long) is not the end of the stream: next() moves on to
+ // the batch after it, so queue that batch now as we would after a success.
+ // Returning here would leave no job for the next call, which then swaps
+ // the stale previous batch back in and ends the stream early.
+ if (error && error != EMPTY) { return; }
// If there's anything left, start the stage 1 thread!
if (next_batch_start() < len) {
@@ -235407,12 +231252,14 @@ simdjson_inline raw_json_string field::key() const noexcept {
simdjson_inline std::string_view field::key_raw_json_token() const noexcept {
SIMDJSON_ASSUME(first.buf != nullptr); // We would like to call .alive() by Visual Studio won't let us.
- return std::string_view(reinterpret_cast<const char*>(first.buf-1), second.iter._json_iter->token.peek(-1) - first.buf + 1);
+ // The ':' is the token just before the value. Use the value's start position
+ // rather than the current position, which moves once the value is consumed.
+ return std::string_view(reinterpret_cast<const char*>(first.buf-1), second.iter._json_iter->token.peek(second.iter.start_position() - 1) - first.buf + 1);
}
simdjson_inline std::string_view field::escaped_key() const noexcept {
SIMDJSON_ASSUME(first.buf != nullptr); // We would like to call .alive() by Visual Studio won't let us.
- auto end_quote = second.iter._json_iter->token.peek(-1);
+ auto end_quote = second.iter._json_iter->token.peek(second.iter.start_position() - 1);
while(*end_quote != '"') end_quote--;
return std::string_view(reinterpret_cast<const char*>(first.buf), end_quote - first.buf);
}
@@ -235583,11 +231430,14 @@ simdjson_inline json_iterator::json_iterator(const uint8_t *buf, ondemand::parse
}
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
-inline void json_iterator::rewind() noexcept {
+simdjson_inline error_code json_iterator::rewind() noexcept {
+ // abandon() let go of the parser: the iterator cannot be walked again.
+ if (!is_alive()) { return error ? error : UNINITIALIZED; }
token.set_position( root_position() );
logger::log_headers(); // We start again
_string_buf_loc = parser->string_buf.get();
_depth = 1;
+ return SUCCESS;
}
inline bool json_iterator::balanced() const noexcept {
@@ -235611,6 +231461,18 @@ inline bool json_iterator::balanced() const noexcept {
}
+// Depth change of each structural character when skipping: +1 for [ and {, -1 for ] and }.
+static constexpr int8_t skip_depth_delta[256] = {
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,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,
+};
+
// GCC 7 warns when the first line of this function is inlined away into oblivion due to the caller
// relating depth and parent_depth, which is a desired effect. The warning does not show up if the
// skip_child() function is not marked inline).
@@ -235666,7 +231528,30 @@ simdjson_warn_unused simdjson_inline error_code json_iterator::skip_child(depth_
break;
}
- // Now that we've considered the first value, we only increment/decrement for arrays/objects
+ // Now that we've considered the first value, we only increment/decrement for arrays/objects.
+ //
+ // Without logging, look up the depth change of each token in a table instead of branching
+ // on it: the next byte is hard to predict, so a switch mispredicts often. The loop also keeps
+ // the position and depth in locals, because the byte loads are char loads that may alias
+ // token._position and _depth, which would force a store of both on every token.
+ if (!logger::LOG_ENABLED) {
+ token_position pos = token.position();
+ const token_position end = end_position();
+ const uint8_t *const json = token.buf;
+ depth_t depth = _depth;
+ while (pos < end) {
+ depth += skip_depth_delta[json[*pos++]];
+ if (depth <= parent_depth) {
+ token.set_position(pos);
+ _depth = depth;
+ return SUCCESS;
+ }
+ }
+ token.set_position(pos);
+ _depth = depth;
+ return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "not enough close braces");
+ }
+
while (position() < end_position()) {
switch (*return_current_and_advance()) {
case '[': case '{':
@@ -235951,6 +231836,17 @@ simdjson_warn_unused simdjson_inline error_code json_iterator::optional_error(er
simdjson_warn_unused simdjson_inline bool json_iterator::copy_to_buffer(const uint8_t *json, uint32_t max_len, uint8_t *tmpbuf, size_t N) noexcept {
// This function is not expected to be called in performance-sensitive settings.
+ // peek_root_length() stops at the next structural index. Stage 1 omits the
+ // comma or RS (0x1E) that separates stream documents, so that byte and the
+ // whitespace around it remain in the span. RS is not a valid number finisher.
+ while (max_len > 0) {
+ const uint8_t last = json[max_len - 1];
+ if (last != ' ' && last != '\t' && last != '\n' && last != '\r' &&
+ last != ',' && last != 0x1E) {
+ break;
+ }
+ max_len--;
+ }
// Let us guard against silly cases:
if((N < max_len) || (N == 0)) { return false; }
// Copy to the buffer.
@@ -236592,7 +232488,9 @@ simdjson_inline simdjson_result<std::string_view> object::raw_json() noexcept {
const uint8_t * starting_point{iter.peek_start()};
auto error = consume();
if(error) { return error; }
- const uint8_t * final_point{iter._json_iter->peek()};
+ // After 'consume()', the last token consumed is the closing brace: we end on it
+ // rather than on the next token, which may follow white space.
+ const uint8_t * final_point{iter._json_iter->token.peek(-1) + 1};
return std::string_view(reinterpret_cast<const char*>(starting_point), size_t(final_point - starting_point));
}
@@ -236685,9 +232583,21 @@ template <typename Func>
template <typename Func>
#endif
inline error_code object::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
- auto result_pair = get_next_key_and_json_path(json_path);
- std::string_view key = result_pair.first;
- std::string_view remaining_path = result_pair.second;
+ // The path is split once: every field reached by a wildcard sees the same
+ // remaining path, so re-parsing it at each one would be quadratic.
+ const std::vector<internal::json_path_segment> path = internal::split_json_path(json_path);
+ // A path that cannot be parsed matches nothing, whatever the document: say
+ // so rather than letting the walk skip it as a mismatch below a wildcard.
+ if (path.back().key.empty()) {
+ return INVALID_JSON_POINTER;
+ }
+ return for_each_at_path_segments(path, 0, callback);
+}
+
+template <typename Func>
+inline error_code object::for_each_at_path_segments(const std::vector<internal::json_path_segment> &path, size_t index, Func &callback) noexcept {
+ std::string_view key = path[index].key;
+ const bool last = index + 1 == path.size();
// Empty key means the path segment could not be parsed (including malformed
// bracket-quoted keys). Match DOM at_path_with_wildcard and avoid treating
// "no progress" as a recursive lookup of "".
@@ -236700,11 +232610,11 @@ inline error_code object::for_each_at_path_with_wildcard(std::string_view json_p
for (auto field : *this) {
value val;
SIMDJSON_TRY(field.value().get(val));
- if (remaining_path.empty()) {
+ if (last) {
callback(val);
} else {
// A field that does not match the rest of the path is skipped.
- error_code err = val.for_each_at_path_with_wildcard(remaining_path, callback);
+ error_code err = val.for_each_at_path_segments(path, index + 1, callback);
if (err && !internal::is_matching_failure_error(err)) { return err; }
}
}
@@ -236713,17 +232623,17 @@ inline error_code object::for_each_at_path_with_wildcard(std::string_view json_p
value val;
error_code error = find_field(key).get(val);
if (error) {
- if (error == NO_SUCH_FIELD && internal::json_path_has_wildcard(remaining_path)) {
+ if (error == NO_SUCH_FIELD && path[index].wildcard_follows) {
return SUCCESS;
}
return error;
}
- if (remaining_path.empty()) {
+ if (last) {
callback(val);
return SUCCESS;
} else {
- return val.for_each_at_path_with_wildcard(remaining_path, callback);
+ return val.for_each_at_path_segments(path, index + 1, callback);
}
}
}
@@ -237376,6 +233286,7 @@ simdjson_inline simdjson_warn_unused bool parser::string_buffer_overflow(const u
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(padded_string_view json) & noexcept {
if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
+ json.use_trailing_whitespace_as_padding();
json.remove_utf8_bom();
_document_len = json.length();
@@ -237393,6 +233304,7 @@ simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(p
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate_allow_incomplete_json(padded_string_view json) & noexcept {
if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
+ json.use_trailing_whitespace_as_padding();
json.remove_utf8_bom();
_document_len = json.length();
@@ -237448,6 +233360,7 @@ simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(c
simdjson_warn_unused simdjson_inline simdjson_result<json_iterator> parser::iterate_raw(padded_string_view json) & noexcept {
if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
+ json.use_trailing_whitespace_as_padding();
json.remove_utf8_bom();
@@ -237474,6 +233387,7 @@ inline simdjson_result<document_stream> parser::iterate_many(const char *buf, si
}
inline simdjson_result<document_stream> parser::iterate_many(padded_string_view s, size_t batch_size) noexcept {
if (!s.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
+ s.use_trailing_whitespace_as_padding();
return iterate_many(s.data(), s.length(), batch_size);
}
inline simdjson_result<document_stream> parser::iterate_many(const padded_string &s, size_t batch_size) noexcept {
@@ -237509,6 +233423,7 @@ inline simdjson_result<document_stream> parser::iterate_many(const char *buf, si
}
inline simdjson_result<document_stream> parser::iterate_many(padded_string_view s, size_t batch_size, bool allow_comma_separated) noexcept {
if (!s.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
+ s.use_trailing_whitespace_as_padding();
return iterate_many(s.data(), s.length(), batch_size, allow_comma_separated);
}
inline simdjson_result<document_stream> parser::iterate_many(const padded_string &s, size_t batch_size, bool allow_comma_separated) noexcept {
@@ -237554,6 +233469,7 @@ inline simdjson_result<document_stream> parser::iterate_many(const char *buf, si
}
inline simdjson_result<document_stream> parser::iterate_many(padded_string_view s, size_t batch_size, stream_format format) noexcept {
if (!s.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
+ s.use_trailing_whitespace_as_padding();
return iterate_many(s.data(), s.length(), batch_size, format);
}
inline simdjson_result<document_stream> parser::iterate_many(const std::string &s, size_t batch_size, stream_format format) noexcept {
@@ -237708,17 +233624,18 @@ simdjson_inline bool raw_json_string::is_free_from_unescaped_quote(const char* t
return true;
}
-
simdjson_inline bool raw_json_string::unsafe_is_equal(size_t length, std::string_view target) const noexcept {
// If we are going to call memcmp, then we must know something about the length of the raw_json_string.
- return (length >= target.size()) && (raw()[target.size()] == '"') && !memcmp(raw(), target.data(), target.size());
+ return (length >= target.size()) && (raw()[target.size()] == '"') && !memcmp(raw(), target.data(), target.size())
+ && !escapes_trailing_quote(target);
}
simdjson_inline bool raw_json_string::unsafe_is_equal(std::string_view target) const noexcept {
// Assumptions: does not contain unescaped quote characters("), and
// the raw content is quote terminated within a valid JSON string.
if(target.size() <= SIMDJSON_PADDING) {
- return (raw()[target.size()] == '"') && !memcmp(raw(), target.data(), target.size());
+ return (raw()[target.size()] == '"') && !memcmp(raw(), target.data(), target.size())
+ && !escapes_trailing_quote(target);
}
// Past SIMDJSON_PADDING we can no longer rely on the padding to keep the
// comparison in bounds, so we must stop at the quote terminating the key.
@@ -237744,7 +233661,7 @@ simdjson_inline bool raw_json_string::is_equal(std::string_view target) const no
escaping = false;
}
}
- if(r[pos] != '"') { return false; }
+ if((r[pos] != '"') || escaping) { return false; }
return true;
}
@@ -237757,7 +233674,7 @@ simdjson_inline bool raw_json_string::unsafe_is_equal(const char * target) const
for(;target[pos];pos++) {
if(r[pos] != target[pos]) { return false; }
}
- if(r[pos] != '"') { return false; }
+ if((r[pos] != '"') || escapes_trailing_quote(std::string_view(target, pos))) { return false; }
return true;
}
@@ -237782,7 +233699,7 @@ simdjson_inline bool raw_json_string::is_equal(const char* target) const noexcep
escaping = false;
}
}
- if(r[pos] != '"') { return false; }
+ if((r[pos] != '"') || escaping) { return false; }
return true;
}
@@ -237894,7 +233811,11 @@ simdjson_inline uint32_t token_iterator::peek_index(token_position position) con
return *position;
}
simdjson_inline uint32_t token_iterator::peek_length(token_position position) const noexcept {
- return *(position+1) - *position;
+ // In the last batch of a document_stream, the index past the end of the
+ // structural indexes holds the start of the truncated bytes (see
+ // document_stream::truncated_bytes()), which can come before the end of the
+ // input: a token at the end is then empty.
+ return *(position+1) > *position ? *(position+1) - *position : 0;
}
simdjson_inline uint32_t token_iterator::peek_root_length(token_position position) const noexcept {
@@ -237909,7 +233830,7 @@ simdjson_inline uint32_t token_iterator::peek_index(int32_t delta) const noexcep
return *(_position+delta);
}
simdjson_inline uint32_t token_iterator::peek_length(int32_t delta) const noexcept {
- return *(_position+delta+1) - *(_position+delta);
+ return peek_length(_position+delta);
}
simdjson_inline token_position token_iterator::position() const noexcept {
@@ -237968,6 +233889,8 @@ simdjson_inline simdjson_result<rvv_vls::ondemand::token_iterator>::simdjson_res
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
+#include <cstring> // std::memset (zeroing the root-number scratch padding)
+
namespace simdjson {
namespace rvv_vls {
namespace ondemand {
@@ -238014,6 +233937,10 @@ simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::start
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::started_object() noexcept {
assert_at_container_start();
#if SIMDJSON_DEVELOPMENT_CHECKS
+ // The development checks record the start of every open container in a
+ // buffer of max_depth() entries: a document nested deeper than that is
+ // rejected, as the DOM parser does.
+ if (size_t(_depth) >= _json_iter->parser->max_depth()) { return report_error(DEPTH_ERROR, "Exceeded max depth!"); }
_json_iter->set_start_position(_depth, start_position());
#endif
if (*_json_iter->peek() == '}') {
@@ -238423,6 +234350,10 @@ inline std::string value_iterator::to_string() const noexcept {
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::started_array() noexcept {
assert_at_container_start();
+#if SIMDJSON_DEVELOPMENT_CHECKS
+ // See started_object().
+ if (size_t(_depth) >= _json_iter->parser->max_depth()) { return report_error(DEPTH_ERROR, "Exceeded max depth!"); }
+#endif
if (*_json_iter->peek() == ']') {
logger::log_value(*_json_iter, "empty array");
_json_iter->return_current_and_advance();
@@ -238628,8 +234559,10 @@ simdjson_inline simdjson_result<rvv_vls::number_type> value_iterator::get_root_n
// Per https://www.exploringbinary.com/maximum-number-of-decimal-digits-in-binary-floating-point-numbers/,
// 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest
// number: -0.<fraction>e-308.
- uint8_t tmpbuf[1074+8+1+1];
- tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
+ // +SIMDJSON_PADDING: the digit scan reads up to 7 bytes past the terminator.
+ uint8_t tmpbuf[1074+8+1+1+SIMDJSON_PADDING];
+ // Null terminate, and zero the padding as allocate_padded_buffer does.
+ std::memset(tmpbuf+1074+8+1, 0, 1+SIMDJSON_PADDING);
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
if(numberparsing::check_if_integer(json, max_len)) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
@@ -238650,8 +234583,10 @@ simdjson_inline simdjson_result<number> value_iterator::get_root_number(bool che
// 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest
// number: -0.<fraction>e-308.
// NOTE: the current approach doesn't work for very big integer numbers containing more than 1074 digits.
- uint8_t tmpbuf[1074+8+1+1];
- tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
+ // +SIMDJSON_PADDING: the digit scan reads up to 7 bytes past the terminator.
+ uint8_t tmpbuf[1074+8+1+1+SIMDJSON_PADDING];
+ // Null terminate, and zero the padding as allocate_padded_buffer does.
+ std::memset(tmpbuf+1074+8+1, 0, 1+SIMDJSON_PADDING);
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
if(numberparsing::check_if_integer(json, max_len)) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
@@ -238721,10 +234656,10 @@ simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> value_iterator::g
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> value_iterator::get_root_uint64_in_string(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("uint64");
- uint8_t tmpbuf[20+1+1]{}; // <20 digits> is the longest possible unsigned integer
- tmpbuf[20+1] = '\0'; // make sure that buffer is always null terminated.
- if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 20+1)) {
- logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 20 characters");
+ uint8_t tmpbuf[20+2+1]{}; // "<20 digits>" is the longest possible unsigned integer
+ tmpbuf[20+2] = '\0'; // make sure that buffer is always null terminated.
+ if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 20+2)) {
+ logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 22 characters");
return NUMBER_ERROR;
}
auto result = numberparsing::parse_unsigned_in_string(tmpbuf);
@@ -238754,10 +234689,10 @@ simdjson_warn_unused simdjson_inline simdjson_result<int64_t> value_iterator::ge
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> value_iterator::get_root_int64_in_string(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("int64");
- uint8_t tmpbuf[20+1+1]; // -<19 digits> is the longest possible integer
- tmpbuf[20+1] = '\0'; // make sure that buffer is always null terminated.
- if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 20+1)) {
- logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 20 characters");
+ uint8_t tmpbuf[20+2+1]; // "-<19 digits>" is the longest possible integer
+ tmpbuf[20+2] = '\0'; // make sure that buffer is always null terminated.
+ if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 20+2)) {
+ logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 22 characters");
return NUMBER_ERROR;
}
@@ -238774,8 +234709,10 @@ simdjson_warn_unused simdjson_inline simdjson_result<double> value_iterator::get
// Per https://www.exploringbinary.com/maximum-number-of-decimal-digits-in-binary-floating-point-numbers/,
// 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest
// number: -0.<fraction>e-308.
- uint8_t tmpbuf[1074+8+1+1]; // +1 for null termination.
- tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
+ // +SIMDJSON_PADDING: the digit scan reads up to 7 bytes past the terminator.
+ uint8_t tmpbuf[1074+8+1+1+SIMDJSON_PADDING]; // +1 for null termination.
+ // Null terminate, and zero the padding as allocate_padded_buffer does.
+ std::memset(tmpbuf+1074+8+1, 0, 1+SIMDJSON_PADDING);
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters");
return NUMBER_ERROR;
@@ -238794,8 +234731,10 @@ simdjson_warn_unused simdjson_inline simdjson_result<double> value_iterator::get
// Per https://www.exploringbinary.com/maximum-number-of-decimal-digits-in-binary-floating-point-numbers/,
// 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest
// number: -0.<fraction>e-308.
- uint8_t tmpbuf[1074+8+1+1]; // +1 for null termination.
- tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
+ // +SIMDJSON_PADDING: the digit scan reads up to 7 bytes past the terminator.
+ uint8_t tmpbuf[1074+8+1+1+SIMDJSON_PADDING]; // +1 for null termination.
+ // Null terminate, and zero the padding as allocate_padded_buffer does.
+ std::memset(tmpbuf+1074+8+1, 0, 1+SIMDJSON_PADDING);
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters");
return NUMBER_ERROR;
@@ -238814,8 +234753,10 @@ simdjson_warn_unused simdjson_inline simdjson_result<float> value_iterator::get_
// We use the same buffer size as get_root_double: the number of significant
// digits that matter is smaller for binary32, but the JSON document may still
// spell out a long number that we must parse (and round) faithfully.
- uint8_t tmpbuf[1074+8+1+1]; // +1 for null termination.
- tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
+ // +SIMDJSON_PADDING: the digit scan reads up to 7 bytes past the terminator.
+ uint8_t tmpbuf[1074+8+1+1+SIMDJSON_PADDING]; // +1 for null termination.
+ // Null terminate, and zero the padding as allocate_padded_buffer does.
+ std::memset(tmpbuf+1074+8+1, 0, 1+SIMDJSON_PADDING);
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters");
return NUMBER_ERROR;
@@ -238831,8 +234772,10 @@ simdjson_warn_unused simdjson_inline simdjson_result<float> value_iterator::get_
simdjson_warn_unused simdjson_inline simdjson_result<float> value_iterator::get_root_float_in_string(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("float");
- uint8_t tmpbuf[1074+8+1+1]; // +1 for null termination.
- tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
+ // +SIMDJSON_PADDING: the digit scan reads up to 7 bytes past the terminator.
+ uint8_t tmpbuf[1074+8+1+1+SIMDJSON_PADDING]; // +1 for null termination.
+ // Null terminate, and zero the padding as allocate_padded_buffer does.
+ std::memset(tmpbuf+1074+8+1, 0, 1+SIMDJSON_PADDING);
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters");
return NUMBER_ERROR;
@@ -238853,7 +234796,7 @@ simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::get_r
// "true" and "false" are 4 and 5 characters long, respectively.
bool value_true = (max_len >= 4 && !atomparsing::str4ncmp(json, "true") &&
(max_len == 4 || jsoncharutils::is_structural_or_whitespace(json[4])));
- bool value_false = (max_len >= 5 && !atomparsing::str4ncmp(json, "false") &&
+ bool value_false = (max_len >= 5 && !atomparsing::str4ncmp(json, "fals") && json[4] == 'e' &&
(max_len == 5 || jsoncharutils::is_structural_or_whitespace(json[5])));
if(value_true == false && value_false == false) { return incorrect_type_error("Not a boolean"); }
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
@@ -239493,49 +235436,6 @@ namespace json_path {
using ::simdjson::rvv_vls::ondemand::value;
-// Path step types
-enum class step_type {
- field, // .field_name or ["field_name"]
- array_index // [index]
-};
-
-// Represents a single step in a JSON path
-template<std::size_t N>
-struct path_step {
- step_type type;
- char key[N]; // Field name (empty for array indices)
- std::size_t index; // Array index (0 for field access)
-
- constexpr path_step(step_type t, const char (&k)[N], std::size_t idx = 0)
- : type(t), index(idx) {
- for (std::size_t i = 0; i < N; ++i) {
- key[i] = k[i];
- }
- }
-
- constexpr std::string_view key_view() const {
- return {key, N - 1};
- }
-};
-
-// Helper to create field step
-template<std::size_t N>
-consteval auto make_field_step(const char (&name)[N]) {
- return path_step<N>(step_type::field, name, 0);
-}
-
-// Helper to create array index step
-consteval auto make_index_step(std::size_t idx) {
- return path_step<1>(step_type::array_index, "", idx);
-}
-
-// Parse state for compile-time JSON path parsing
-struct parse_result {
- bool success;
- std::size_t pos;
- std::string_view error_msg;
-};
-
// Compile-time JSON path parser
// Supports subset: .field, ["field"], [index], nested combinations
template<constevalutil::fixed_string Path>
@@ -239549,77 +235449,6 @@ struct json_path_parser {
}
return 0;
}
-
- // Count the number of steps in the path at compile time
- static consteval std::size_t count_steps() {
- std::size_t count = 0;
- std::size_t i = skip_root();
-
- while (i < path_str.size()) {
- if (path_str[i] == '.') {
- // Field access: .field
- ++i;
- if (i >= path_str.size()) break;
-
- // Skip field name
- while (i < path_str.size() && path_str[i] != '.' && path_str[i] != '[') {
- ++i;
- }
- ++count;
- } else if (path_str[i] == '[') {
- // Array or bracket notation
- ++i;
- if (i >= path_str.size()) break;
-
- if (path_str[i] == '"' || path_str[i] == '\'') {
- // Field access: ["field"] or ['field']
- char quote = path_str[i];
- ++i;
- while (i < path_str.size() && path_str[i] != quote) {
- ++i;
- }
- if (i < path_str.size()) ++i; // skip closing quote
- if (i < path_str.size() && path_str[i] == ']') ++i;
- } else {
- // Array index: [0], [123]
- while (i < path_str.size() && path_str[i] != ']') {
- ++i;
- }
- if (i < path_str.size()) ++i; // skip ]
- }
- ++count;
- } else {
- ++i;
- }
- }
-
- return count;
- }
-
- // Parse a field name at compile time
- static consteval std::size_t parse_field_name(std::size_t start, char* out, std::size_t max_len) {
- std::size_t len = 0;
- std::size_t i = start;
-
- while (i < path_str.size() && path_str[i] != '.' && path_str[i] != '[' && len < max_len - 1) {
- out[len++] = path_str[i++];
- }
- out[len] = '\0';
- return i;
- }
-
- // Parse an array index at compile time
- static consteval std::pair<std::size_t, std::size_t> parse_array_index(std::size_t start) {
- std::size_t index = 0;
- std::size_t i = start;
-
- while (i < path_str.size() && path_str[i] >= '0' && path_str[i] <= '9') {
- index = index * 10 + (path_str[i] - '0');
- ++i;
- }
-
- return {i, index};
- }
};
// Compile-time path accessor generator
@@ -239628,7 +235457,6 @@ struct path_accessor {
using value = ::simdjson::rvv_vls::ondemand::value;
static constexpr auto parser = json_path_parser<Path>();
- static constexpr std::size_t num_steps = parser.count_steps();
static constexpr std::string_view path_view = Path.view();
// Compile-time accessor generation
@@ -239743,93 +235571,6 @@ private:
return current_type;
}
-private:
- // Walk path and extract directly into final field using compile-time reflection
- template<std::meta::info CurrentType, std::size_t PathPos, typename TargetType>
- static inline error_code extract_with_reflection(simdjson_result<value> current, TargetType& target_ref) noexcept {
- if (current.error()) return current.error();
-
- // Base case: end of path - extract into target
- if constexpr (PathPos >= path_view.size()) {
- return current.get(target_ref);
- }
- // Field access: .field_name
- else if constexpr (path_view[PathPos] == '.') {
- constexpr auto field_info = parse_next_field(PathPos);
- constexpr std::string_view field_name = std::get<0>(field_info);
- constexpr std::size_t next_pos = std::get<1>(field_info);
-
- constexpr auto member_info = find_member_by_name(CurrentType, field_name);
- static_assert(member_info != ^^void, "Field not found in struct");
-
- constexpr auto member_type = std::meta::type_of(member_info);
-
- auto obj_result = current.get_object();
- if (obj_result.error()) return obj_result.error();
- auto obj = obj_result.value_unsafe();
- auto field_value = obj.find_field_unordered(field_name);
-
- if constexpr (next_pos >= path_view.size()) {
- return field_value.get(target_ref);
- } else {
- return extract_with_reflection<member_type, next_pos>(field_value, target_ref);
- }
- }
- // Bracket notation: [index] or ["field"]
- else if constexpr (path_view[PathPos] == '[') {
- constexpr auto bracket_info = parse_bracket(PathPos);
- constexpr bool is_field = std::get<0>(bracket_info);
- constexpr std::size_t next_pos = std::get<2>(bracket_info);
-
- if constexpr (is_field) {
- constexpr std::string_view field_name = std::get<1>(bracket_info);
- constexpr auto member_info = find_member_by_name(CurrentType, field_name);
- static_assert(member_info != ^^void, "Field not found in struct");
- constexpr auto member_type = std::meta::type_of(member_info);
-
- auto obj_result = current.get_object();
- if (obj_result.error()) return obj_result.error();
- auto obj = obj_result.value_unsafe();
- auto field_value = obj.find_field_unordered(field_name);
-
- if constexpr (next_pos >= path_view.size()) {
- return field_value.get(target_ref);
- } else {
- return extract_with_reflection<member_type, next_pos>(field_value, target_ref);
- }
- } else {
- constexpr std::size_t index = std::get<3>(bracket_info);
- constexpr auto elem_type = get_element_type_reflected(CurrentType);
- static_assert(elem_type != ^^void, "Could not determine array element type");
-
- auto arr_result = current.get_array();
- if (arr_result.error()) return arr_result.error();
- auto arr = arr_result.value_unsafe();
- auto elem_value = arr.at(index);
-
- if constexpr (next_pos >= path_view.size()) {
- return elem_value.get(target_ref);
- } else {
- return extract_with_reflection<elem_type, next_pos>(elem_value, target_ref);
- }
- }
- }
- // Skip unexpected characters and continue
- else {
- return extract_with_reflection<CurrentType, PathPos + 1>(current, target_ref);
- }
- }
-
- // Find member by name in reflected type
- static consteval std::meta::info find_member_by_name(std::meta::info type_refl, std::string_view name) {
- auto members = std::meta::nonstatic_data_members_of(type_refl, std::meta::access_context::unchecked());
- for (auto mem : members) {
- if (std::meta::identifier_of(mem) == name) {
- return mem;
- }
- }
- }
-
// Generate compile-time accessor code by walking the path
template<std::size_t PathPos>
static inline simdjson_result<value> access_impl(simdjson_result<value> current) noexcept {
@@ -239959,55 +235700,6 @@ public:
}
private:
- // Check if type has member with given name
- template<typename Type>
- static consteval bool has_member(std::string_view member_name) {
- constexpr auto members = std::meta::nonstatic_data_members_of(^^Type, std::meta::access_context::unchecked());
- for (auto mem : members) {
- if (std::meta::identifier_of(mem) == member_name) {
- return true;
- }
- }
- return false;
- }
-
- // Get type of member by name
- template<typename Type>
- static consteval auto get_member_type(std::string_view member_name) {
- constexpr auto members = std::meta::nonstatic_data_members_of(^^Type, std::meta::access_context::unchecked());
- for (auto mem : members) {
- if (std::meta::identifier_of(mem) == member_name) {
- return std::meta::type_of(mem);
- }
- }
- return ^^void;
- }
-
- // Check if non-reflected type is array-like
- template<typename Type>
- static consteval bool is_container_type() {
- using BaseType = std::remove_cvref_t<Type>;
- if constexpr (requires { typename BaseType::value_type; }) {
- return true;
- }
- if constexpr (std::is_array_v<BaseType>) {
- return true;
- }
- return false;
- }
-
- // Extract element type from non-reflected container
- template<typename Type>
- using extract_element_type = std::conditional_t<
- requires { typename std::remove_cvref_t<Type>::value_type; },
- typename std::remove_cvref_t<Type>::value_type,
- std::conditional_t<
- std::is_array_v<std::remove_cvref_t<Type>>,
- std::remove_extent_t<std::remove_cvref_t<Type>>,
- void
- >
- >;
-
// Validate path matches struct definition
static consteval bool validate_path() {
if constexpr (!std::is_class_v<T>) {
@@ -240416,9 +236108,9 @@ namespace simdjson {
inline std::string to_json_string(const T& obj) {
builder::string_builder str_builder;
append(str_builder, obj);
- std::string_view view;
- if (str_builder.view().get(view) == SUCCESS) {
- return std::string(view);
+ std::string result;
+ if (str_builder.move_to(result) == SUCCESS) {
+ return result;
}
return "";
}
@@ -245906,17 +241598,12 @@ from_chars_advanced(UC const *first, UC const *last, T &value,
#endif
/* end file simdjson/internal/fast_float.h */
-#include <array>
-#include <cstdint>
-#include <meta>
-#include <limits>
-#include <string_view>
-
#include <algorithm>
#include <array>
#include <charconv>
#include <cstdint>
#include <expected>
+#include <limits>
#include <meta>
#include <string>
#include <string_view>
@@ -245978,34 +241665,6 @@ consteval auto make_data_member_options(auto&& name_str) {
return options;
}
-// JSON string may contain embedded nulls, and C++26 reflection does not yet
-// support std::string_view as a data member type. As a workaround, we define
-// a custom type that holds a const char* and a size.
-template <char... Vals> struct fixed_json_string {
- // Statically-allocated array to hold the characters and a null terminator
- static constexpr char inner_data[] = {Vals..., '\0'};
-
- // Constant for the length of the string view (excluding the null terminator)
- static constexpr std::size_t inner_size = sizeof...(Vals);
-
- // The std::string_view over the data.
- // We use data and size to avoid including the null terminator in the view.
- static constexpr std::string_view view = {inner_data, inner_size};
-
- constexpr operator std::string_view() { return view; }
- constexpr const char *c_str() { return view.data(); }
- constexpr const char *data() { return view.data(); }
- constexpr size_t size() { return view.size(); }
-};
-
-consteval std::meta::info to_fixed_json_string(std::string_view in) {
- std::vector<std::meta::info> Args = {};
- for (char c : in) {
- Args.push_back(std::meta::reflect_constant(c));
- }
- return std::meta::substitute(^^fixed_json_string, Args);
-}
-
/**
* @brief Helper struct for substitute() pattern
*/