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
  */