Coverage Report

Created: 2026-10-17 06:12

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/glaze/include/glaze/json/read.hpp
Line
Count
Source
1
// Glaze Library
2
// For the license information refer to glaze.hpp
3
4
#pragma once
5
6
#include <charconv>
7
#include <climits>
8
#include <cwchar>
9
#include <filesystem>
10
#include <iterator>
11
#include <ranges>
12
#include <type_traits>
13
14
#include "glaze/core/chrono.hpp"
15
#include "glaze/core/common.hpp"
16
#include "glaze/core/custom_meta.hpp"
17
#include "glaze/core/opts.hpp"
18
#include "glaze/core/read.hpp"
19
#include "glaze/core/reflect.hpp"
20
#include "glaze/file/file_ops.hpp"
21
#include "glaze/json/json_concepts.hpp"
22
#include "glaze/json/skip.hpp"
23
#include "glaze/util/for_each.hpp"
24
#include "glaze/util/glaze_fast_float.hpp"
25
#include "glaze/util/nullable_traits.hpp"
26
#include "glaze/util/simple_float.hpp"
27
#include "glaze/util/type_traits.hpp"
28
#include "glaze/util/variant.hpp"
29
30
#if defined(_MSC_VER) && !defined(__clang__)
31
// Turn off MSVC warning for unreachable code due to constexpr branching
32
#pragma warning(push)
33
#pragma warning(disable : 4702)
34
#endif
35
36
namespace glz
37
{
38
   // forward declare from json/wrappers.hpp to avoid circular include
39
   template <class T>
40
   struct quoted_t;
41
42
   // Note: custom_num_t, custom_str_t, custom_bool_t concepts are defined in core/custom_meta.hpp
43
44
   template <>
45
   struct parse<JSON>
46
   {
47
      template <auto Opts, class T, is_context Ctx, class It0, class It1>
48
      GLZ_ALWAYS_INLINE static void op(T&& value, Ctx&& ctx, It0&& it, It1 end)
49
3.59k
      {
50
         if constexpr (const_value_v<T>) {
51
            if constexpr (check_error_on_const_read(Opts)) {
52
               ctx.error = error_code::attempt_const_read;
53
            }
54
            else {
55
               // do not read anything into the const value
56
               skip_value<JSON>::op<Opts>(std::forward<Ctx>(ctx), std::forward<It0>(it), end);
57
            }
58
         }
59
3.59k
         else {
60
3.59k
            using V = std::remove_cvref_t<T>;
61
3.59k
            from<JSON, V>::template op<Opts>(std::forward<T>(value), std::forward<Ctx>(ctx), std::forward<It0>(it),
62
3.59k
                                             end);
63
3.59k
         }
64
3.59k
      }
_ZN3glz5parseILj10EE2opITnDaXtlNS_4optsELj10ELb1ELb0ELb1ELb1EEER6PersonTkNS_10is_contextERNS_7contextERPKcS9_EEvOT0_OT1_OT2_T3_
Line
Count
Source
49
1.76k
      {
50
         if constexpr (const_value_v<T>) {
51
            if constexpr (check_error_on_const_read(Opts)) {
52
               ctx.error = error_code::attempt_const_read;
53
            }
54
            else {
55
               // do not read anything into the const value
56
               skip_value<JSON>::op<Opts>(std::forward<Ctx>(ctx), std::forward<It0>(it), end);
57
            }
58
         }
59
1.76k
         else {
60
1.76k
            using V = std::remove_cvref_t<T>;
61
1.76k
            from<JSON, V>::template op<Opts>(std::forward<T>(value), std::forward<Ctx>(ctx), std::forward<It0>(it),
62
1.76k
                                             end);
63
1.76k
         }
64
1.76k
      }
_ZN3glz5parseILj10EE2opITnDaXtlNS_4optsELj10ELb0ELb0ELb1ELb1EEER6PersonTkNS_10is_contextERNS_7contextERPKcS9_EEvOT0_OT1_OT2_T3_
Line
Count
Source
49
1.82k
      {
50
         if constexpr (const_value_v<T>) {
51
            if constexpr (check_error_on_const_read(Opts)) {
52
               ctx.error = error_code::attempt_const_read;
53
            }
54
            else {
55
               // do not read anything into the const value
56
               skip_value<JSON>::op<Opts>(std::forward<Ctx>(ctx), std::forward<It0>(it), end);
57
            }
58
         }
59
1.82k
         else {
60
1.82k
            using V = std::remove_cvref_t<T>;
61
1.82k
            from<JSON, V>::template op<Opts>(std::forward<T>(value), std::forward<Ctx>(ctx), std::forward<It0>(it),
62
1.82k
                                             end);
63
1.82k
         }
64
1.82k
      }
65
66
      // This unknown key handler should not be given unescaped keys, that is for the user to handle.
67
      template <auto Opts, class T, is_context Ctx, class It0, class It1>
68
      static void handle_unknown(const sv& key, T&& value, Ctx&& ctx, It0&& it, It1 end)
69
      {
70
         using ValueType = std::decay_t<decltype(value)>;
71
         if constexpr (has_unknown_reader<ValueType>) {
72
            constexpr auto& reader = meta_unknown_read_v<ValueType>;
73
            using ReaderType = meta_unknown_read_t<ValueType>;
74
            if constexpr (std::is_member_object_pointer_v<ReaderType>) {
75
               using MemberType = typename member_value<ReaderType>::type;
76
               if constexpr (map_subscriptable<MemberType>) {
77
                  // Convert the `sv` key to the map's key_type when needed
78
                  using Key = typename MemberType::key_type;
79
                  if constexpr (std::is_same_v<Key, sv>) {
80
                     parse<JSON>::op<Opts>((value.*reader)[key], ctx, it, end);
81
                  }
82
                  else if constexpr (std::is_constructible_v<Key, sv>) {
83
                     parse<JSON>::op<Opts>((value.*reader)[Key{key}], ctx, it, end);
84
                  }
85
                  else if constexpr (std::is_constructible_v<Key, const char*, size_t>) {
86
                     parse<JSON>::op<Opts>((value.*reader)[Key{key.data(), key.size()}], ctx, it, end);
87
                  }
88
                  else if constexpr (std::is_constructible_v<Key, std::string_view>) {
89
                     parse<JSON>::op<Opts>((value.*reader)[Key{std::string_view{key}}], ctx, it, end);
90
                  }
91
                  else {
92
                     static_assert(false_v<T>, "unknown_read key type not handled");
93
                  }
94
               }
95
               else {
96
                  static_assert(false_v<T>, "target must have subscript operator");
97
               }
98
            }
99
            else if constexpr (std::is_member_function_pointer_v<ReaderType>) {
100
               using ReturnType = typename return_type<ReaderType>::type;
101
               if constexpr (std::is_void_v<ReturnType>) {
102
                  using TupleType = typename inputs_as_tuple<ReaderType>::type;
103
                  if constexpr (glz::tuple_size_v<TupleType> == 2) {
104
                     std::decay_t<glz::tuple_element_t<1, TupleType>> input{};
105
                     parse<JSON>::op<Opts>(input, ctx, it, end);
106
                     if (parse_failed(ctx.error)) [[unlikely]]
107
                        return;
108
                     // Convert `sv` key to the method's first parameter type when needed
109
                     using KeyParam = std::decay_t<glz::tuple_element_t<0, TupleType>>;
110
                     if constexpr (std::is_same_v<KeyParam, sv>) {
111
                        (value.*reader)(key, input);
112
                     }
113
                     else if constexpr (std::is_constructible_v<KeyParam, sv>) {
114
                        (value.*reader)(KeyParam{key}, input);
115
                     }
116
                     else if constexpr (std::is_constructible_v<KeyParam, const char*, size_t>) {
117
                        (value.*reader)(KeyParam{key.data(), key.size()}, input);
118
                     }
119
                     else if constexpr (std::is_constructible_v<KeyParam, std::string_view>) {
120
                        (value.*reader)(KeyParam{std::string_view{key}}, input);
121
                     }
122
                     else {
123
                        static_assert(false_v<T>, "unknown_read key parameter type not handled");
124
                     }
125
                  }
126
                  else {
127
                     static_assert(false_v<T>, "method must have 2 args");
128
                  }
129
               }
130
               else {
131
                  static_assert(false_v<T>, "method must have void return");
132
               }
133
            }
134
            else {
135
               static_assert(false_v<T>, "unknown_read type not handled");
136
            }
137
         }
138
         else {
139
            skip_value<JSON>::op<Opts>(ctx, it, end);
140
         }
141
      }
142
   };
143
144
   template <class T>
145
      requires(glaze_value_t<T> && !custom_read<T>)
146
   struct from<JSON, T>
147
   {
148
      template <auto Opts, class Value, is_context Ctx, class It0, class It1>
149
      GLZ_ALWAYS_INLINE static void op(Value&& value, Ctx&& ctx, It0&& it, It1 end)
150
      {
151
         using V = std::decay_t<decltype(get_member(std::declval<Value>(), meta_wrapper_v<T>))>;
152
         from<JSON, V>::template op<Opts>(get_member(std::forward<Value>(value), meta_wrapper_v<T>),
153
                                          std::forward<Ctx>(ctx), std::forward<It0>(it), end);
154
      }
155
   };
156
157
   template <auto Opts>
158
   GLZ_ALWAYS_INLINE bool parse_ws_colon(is_context auto& ctx, auto&& it, auto end) noexcept
159
   {
160
      if (skip_ws<Opts>(ctx, it, end)) {
161
         return true;
162
      }
163
      if (match_invalid_end<':', Opts>(ctx, it, end)) {
164
         return true;
165
      }
166
      if (skip_ws<Opts>(ctx, it, end)) {
167
         return true;
168
      }
169
      return false;
170
   }
171
172
   // Extracted from decode_index to reduce code duplication across instantiations.
173
   // This path handles unknown keys and doesn't depend on the field index I.
174
   template <auto Opts, class T, class Value>
175
      requires(glaze_object_t<T> || reflectable<T>)
176
   void decode_index_unknown_key(Value&& value, is_context auto&& ctx, auto&& it, auto end)
177
294
   {
178
294
      if constexpr (Opts.error_on_unknown_keys) {
179
294
         ctx.error = error_code::unknown_key;
180
      }
181
      else {
182
         auto* start = it;
183
         uint64_t ascii_acc{};
184
         skip_string_view(ctx, it, end, ascii_acc);
185
         if (bool(ctx.error)) [[unlikely]]
186
            return;
187
         if (validate_utf8_span<Opts>(ctx, start, it, ascii_acc)) [[unlikely]]
188
            return;
189
         const sv key = {start, size_t(it - start)};
190
         ++it;
191
         if constexpr (not Opts.null_terminated) {
192
            if (it == end) [[unlikely]] {
193
               ctx.error = error_code::unexpected_end;
194
               return;
195
            }
196
         }
197
198
         if (parse_ws_colon<Opts>(ctx, it, end)) {
199
            return;
200
         }
201
202
         parse<JSON>::handle_unknown<Opts>(key, value, ctx, it, end);
203
      }
204
294
   }
_ZN3glz24decode_index_unknown_keyITnDaXtlNS_4optsELj10ELb1ELb0ELb1ELb1EEE6PersonRS2_TkNS_10is_contextERNS_7contextERPKcS7_Qoo14glaze_object_tIT0_E11reflectableIS9_EEEvOT1_OT2_OT3_T4_
Line
Count
Source
177
134
   {
178
134
      if constexpr (Opts.error_on_unknown_keys) {
179
134
         ctx.error = error_code::unknown_key;
180
      }
181
      else {
182
         auto* start = it;
183
         uint64_t ascii_acc{};
184
         skip_string_view(ctx, it, end, ascii_acc);
185
         if (bool(ctx.error)) [[unlikely]]
186
            return;
187
         if (validate_utf8_span<Opts>(ctx, start, it, ascii_acc)) [[unlikely]]
188
            return;
189
         const sv key = {start, size_t(it - start)};
190
         ++it;
191
         if constexpr (not Opts.null_terminated) {
192
            if (it == end) [[unlikely]] {
193
               ctx.error = error_code::unexpected_end;
194
               return;
195
            }
196
         }
197
198
         if (parse_ws_colon<Opts>(ctx, it, end)) {
199
            return;
200
         }
201
202
         parse<JSON>::handle_unknown<Opts>(key, value, ctx, it, end);
203
      }
204
134
   }
_ZN3glz24decode_index_unknown_keyITnDaXtlNS_4optsELj10ELb0ELb0ELb1ELb1EEE6PersonRS2_TkNS_10is_contextERNS_7contextERPKcS7_Qoo14glaze_object_tIT0_E11reflectableIS9_EEEvOT1_OT2_OT3_T4_
Line
Count
Source
177
160
   {
178
160
      if constexpr (Opts.error_on_unknown_keys) {
179
160
         ctx.error = error_code::unknown_key;
180
      }
181
      else {
182
         auto* start = it;
183
         uint64_t ascii_acc{};
184
         skip_string_view(ctx, it, end, ascii_acc);
185
         if (bool(ctx.error)) [[unlikely]]
186
            return;
187
         if (validate_utf8_span<Opts>(ctx, start, it, ascii_acc)) [[unlikely]]
188
            return;
189
         const sv key = {start, size_t(it - start)};
190
         ++it;
191
         if constexpr (not Opts.null_terminated) {
192
            if (it == end) [[unlikely]] {
193
               ctx.error = error_code::unexpected_end;
194
               return;
195
            }
196
         }
197
198
         if (parse_ws_colon<Opts>(ctx, it, end)) {
199
            return;
200
         }
201
202
         parse<JSON>::handle_unknown<Opts>(key, value, ctx, it, end);
203
      }
204
160
   }
205
206
   // Linear search for key matching - eliminates hash tables for smaller binaries
207
   // Returns the index of the matching key, or N if not found
208
   // Advances it past the key and closing quote
209
   template <class T>
210
   GLZ_ALWAYS_INLINE size_t decode_linear(is_context auto&& ctx, auto&& it, auto&& end)
211
   {
212
      static constexpr auto N = reflect<T>::size;
213
      static constexpr auto& keys = reflect<T>::keys;
214
215
      auto key_start = it;
216
      skip_string_view(ctx, it, end);
217
      if (bool(ctx.error)) [[unlikely]]
218
         return N;
219
      const sv key{key_start, size_t(it - key_start)};
220
      ++it; // skip closing quote
221
222
      // Linear search through known keys
223
      for (size_t i = 0; i < N; ++i) {
224
         if (keys[i] == key) {
225
            return i;
226
         }
227
      }
228
229
      return N; // not found
230
   }
231
232
   // Parse the value of field I once the ':' and the whitespace around it have been consumed.
233
   // Shared by the hash-dispatch and the linear-search object paths. `FieldOpts` is what the field's
234
   // own parser receives: the hash path has already consumed the leading whitespace (ws_handled),
235
   // the linear path passes Opts through unchanged to avoid a second set of instantiations.
236
   //
237
   // `meta::skip` is a compile-time decision, so the field's reader lives in the `else` branch rather
238
   // than after an early `return`. A `return` inside an `if constexpr` stops the field from being read
239
   // at runtime, but the code that follows it is still instantiated -- which defeats skipping a field
240
   // precisely because its type has no reader (no `from` specialization, or a non-owning view that
241
   // GLZ_ASSERT_OWNS_ITS_BYTES rejects for a streaming read).
242
   template <auto Opts, auto FieldOpts, class T, size_t I, class Value, class... SelectedIndex>
243
      requires(glaze_object_t<T> || reflectable<T>)
244
   GLZ_ALWAYS_INLINE void decode_field_value(Value&& value, is_context auto&& ctx, auto&& it, auto&& end,
245
                                             SelectedIndex&&... selected_index)
246
20.4k
   {
247
      // Check for null value skipping on read
248
      if constexpr (check_skip_null_members_on_read(Opts)) {
249
         if (*it == 'n') {
250
            ++it;
251
            if constexpr (not Opts.null_terminated) {
252
               if (it == end) [[unlikely]] {
253
                  ctx.error = error_code::unexpected_end;
254
                  return;
255
               }
256
            }
257
            match<"ull", Opts>(ctx, it, end);
258
            if (bool(ctx.error)) [[unlikely]]
259
               return;
260
            // Successfully matched "null", skip it
261
            if constexpr (Opts.error_on_missing_keys || Opts.partial_read) {
262
               ((selected_index = I), ...); // Mark as handled even if skipped
263
            }
264
            return;
265
         }
266
      }
267
268
      if constexpr (skipped_by_meta<T, I, operation::parse>) {
269
         skip_value<JSON>::op<Opts>(ctx, it, end);
270
         if (bool(ctx.error)) [[unlikely]]
271
            return; // Propagate error from skip_value
272
      }
273
20.4k
      else {
274
20.4k
         using V = refl_t<T, I>;
275
276
         if constexpr (const_value_v<V>) {
277
            if constexpr (check_error_on_const_read(Opts)) {
278
               ctx.error = error_code::attempt_const_read;
279
            }
280
            else {
281
               // do not read anything into the const value
282
               skip_value<JSON>::op<Opts>(ctx, it, end);
283
            }
284
         }
285
         else if constexpr (is_function_ptr_or_ref<V>) {
286
            // Function pointers cannot be deserialized from JSON.
287
            // When write_function_pointers is enabled the writer emits a type-name string,
288
            // but there is nothing meaningful to reconstruct on read — just skip the value.
289
            // When write_function_pointers is off the key is never written, so this branch
290
            // is unreachable in that case.
291
            skip_value<JSON>::op<Opts>(ctx, it, end);
292
         }
293
         else if constexpr (glaze_object_t<T>) {
294
            from<JSON, std::remove_cvref_t<V>>::template op<FieldOpts>(get_member(value, get<I>(reflect<T>::values)),
295
                                                                       ctx, it, end);
296
         }
297
20.4k
         else {
298
20.4k
            from<JSON, std::remove_cvref_t<V>>::template op<FieldOpts>(get_member(value, get<I>(to_tie(value))), ctx,
299
20.4k
                                                                       it, end);
300
20.4k
         }
301
20.4k
      }
302
303
      // The key was present and handled, even when the value itself was skipped
304
      if constexpr (Opts.error_on_missing_keys || Opts.partial_read) {
305
         ((selected_index = I), ...);
306
      }
307
20.4k
   }
_ZN3glz18decode_field_valueITnDaXtlNS_4optsELj10ELb1ELb0ELb1ELb1EEETnDaXtlS1_Lj10ELb1ELb0ELb1ELb1ELb0ELb0ELb0ELb0ELj4EEE6PersonLm0ERS2_JETkNS_10is_contextERNS_7contextERPKcS8_Qoo14glaze_object_tIT1_E11reflectableIS9_EEEvOT3_OT5_OT6_OT7_DpOT4_
Line
Count
Source
246
929
   {
247
      // Check for null value skipping on read
248
      if constexpr (check_skip_null_members_on_read(Opts)) {
249
         if (*it == 'n') {
250
            ++it;
251
            if constexpr (not Opts.null_terminated) {
252
               if (it == end) [[unlikely]] {
253
                  ctx.error = error_code::unexpected_end;
254
                  return;
255
               }
256
            }
257
            match<"ull", Opts>(ctx, it, end);
258
            if (bool(ctx.error)) [[unlikely]]
259
               return;
260
            // Successfully matched "null", skip it
261
            if constexpr (Opts.error_on_missing_keys || Opts.partial_read) {
262
               ((selected_index = I), ...); // Mark as handled even if skipped
263
            }
264
            return;
265
         }
266
      }
267
268
      if constexpr (skipped_by_meta<T, I, operation::parse>) {
269
         skip_value<JSON>::op<Opts>(ctx, it, end);
270
         if (bool(ctx.error)) [[unlikely]]
271
            return; // Propagate error from skip_value
272
      }
273
929
      else {
274
929
         using V = refl_t<T, I>;
275
276
         if constexpr (const_value_v<V>) {
277
            if constexpr (check_error_on_const_read(Opts)) {
278
               ctx.error = error_code::attempt_const_read;
279
            }
280
            else {
281
               // do not read anything into the const value
282
               skip_value<JSON>::op<Opts>(ctx, it, end);
283
            }
284
         }
285
         else if constexpr (is_function_ptr_or_ref<V>) {
286
            // Function pointers cannot be deserialized from JSON.
287
            // When write_function_pointers is enabled the writer emits a type-name string,
288
            // but there is nothing meaningful to reconstruct on read — just skip the value.
289
            // When write_function_pointers is off the key is never written, so this branch
290
            // is unreachable in that case.
291
            skip_value<JSON>::op<Opts>(ctx, it, end);
292
         }
293
         else if constexpr (glaze_object_t<T>) {
294
            from<JSON, std::remove_cvref_t<V>>::template op<FieldOpts>(get_member(value, get<I>(reflect<T>::values)),
295
                                                                       ctx, it, end);
296
         }
297
929
         else {
298
929
            from<JSON, std::remove_cvref_t<V>>::template op<FieldOpts>(get_member(value, get<I>(to_tie(value))), ctx,
299
929
                                                                       it, end);
300
929
         }
301
929
      }
302
303
      // The key was present and handled, even when the value itself was skipped
304
      if constexpr (Opts.error_on_missing_keys || Opts.partial_read) {
305
         ((selected_index = I), ...);
306
      }
307
929
   }
_ZN3glz18decode_field_valueITnDaXtlNS_4optsELj10ELb1ELb0ELb1ELb1EEETnDaXtlS1_Lj10ELb1ELb0ELb1ELb1ELb0ELb0ELb0ELb0ELj4EEE6PersonLm1ERS2_JETkNS_10is_contextERNS_7contextERPKcS8_Qoo14glaze_object_tIT1_E11reflectableIS9_EEEvOT3_OT5_OT6_OT7_DpOT4_
Line
Count
Source
246
3.64k
   {
247
      // Check for null value skipping on read
248
      if constexpr (check_skip_null_members_on_read(Opts)) {
249
         if (*it == 'n') {
250
            ++it;
251
            if constexpr (not Opts.null_terminated) {
252
               if (it == end) [[unlikely]] {
253
                  ctx.error = error_code::unexpected_end;
254
                  return;
255
               }
256
            }
257
            match<"ull", Opts>(ctx, it, end);
258
            if (bool(ctx.error)) [[unlikely]]
259
               return;
260
            // Successfully matched "null", skip it
261
            if constexpr (Opts.error_on_missing_keys || Opts.partial_read) {
262
               ((selected_index = I), ...); // Mark as handled even if skipped
263
            }
264
            return;
265
         }
266
      }
267
268
      if constexpr (skipped_by_meta<T, I, operation::parse>) {
269
         skip_value<JSON>::op<Opts>(ctx, it, end);
270
         if (bool(ctx.error)) [[unlikely]]
271
            return; // Propagate error from skip_value
272
      }
273
3.64k
      else {
274
3.64k
         using V = refl_t<T, I>;
275
276
         if constexpr (const_value_v<V>) {
277
            if constexpr (check_error_on_const_read(Opts)) {
278
               ctx.error = error_code::attempt_const_read;
279
            }
280
            else {
281
               // do not read anything into the const value
282
               skip_value<JSON>::op<Opts>(ctx, it, end);
283
            }
284
         }
285
         else if constexpr (is_function_ptr_or_ref<V>) {
286
            // Function pointers cannot be deserialized from JSON.
287
            // When write_function_pointers is enabled the writer emits a type-name string,
288
            // but there is nothing meaningful to reconstruct on read — just skip the value.
289
            // When write_function_pointers is off the key is never written, so this branch
290
            // is unreachable in that case.
291
            skip_value<JSON>::op<Opts>(ctx, it, end);
292
         }
293
         else if constexpr (glaze_object_t<T>) {
294
            from<JSON, std::remove_cvref_t<V>>::template op<FieldOpts>(get_member(value, get<I>(reflect<T>::values)),
295
                                                                       ctx, it, end);
296
         }
297
3.64k
         else {
298
3.64k
            from<JSON, std::remove_cvref_t<V>>::template op<FieldOpts>(get_member(value, get<I>(to_tie(value))), ctx,
299
3.64k
                                                                       it, end);
300
3.64k
         }
301
3.64k
      }
302
303
      // The key was present and handled, even when the value itself was skipped
304
      if constexpr (Opts.error_on_missing_keys || Opts.partial_read) {
305
         ((selected_index = I), ...);
306
      }
307
3.64k
   }
_ZN3glz18decode_field_valueITnDaXtlNS_4optsELj10ELb1ELb0ELb1ELb1EEETnDaXtlS1_Lj10ELb1ELb0ELb1ELb1ELb0ELb0ELb0ELb0ELj4EEE6PersonLm2ERS2_JETkNS_10is_contextERNS_7contextERPKcS8_Qoo14glaze_object_tIT1_E11reflectableIS9_EEEvOT3_OT5_OT6_OT7_DpOT4_
Line
Count
Source
246
6.02k
   {
247
      // Check for null value skipping on read
248
      if constexpr (check_skip_null_members_on_read(Opts)) {
249
         if (*it == 'n') {
250
            ++it;
251
            if constexpr (not Opts.null_terminated) {
252
               if (it == end) [[unlikely]] {
253
                  ctx.error = error_code::unexpected_end;
254
                  return;
255
               }
256
            }
257
            match<"ull", Opts>(ctx, it, end);
258
            if (bool(ctx.error)) [[unlikely]]
259
               return;
260
            // Successfully matched "null", skip it
261
            if constexpr (Opts.error_on_missing_keys || Opts.partial_read) {
262
               ((selected_index = I), ...); // Mark as handled even if skipped
263
            }
264
            return;
265
         }
266
      }
267
268
      if constexpr (skipped_by_meta<T, I, operation::parse>) {
269
         skip_value<JSON>::op<Opts>(ctx, it, end);
270
         if (bool(ctx.error)) [[unlikely]]
271
            return; // Propagate error from skip_value
272
      }
273
6.02k
      else {
274
6.02k
         using V = refl_t<T, I>;
275
276
         if constexpr (const_value_v<V>) {
277
            if constexpr (check_error_on_const_read(Opts)) {
278
               ctx.error = error_code::attempt_const_read;
279
            }
280
            else {
281
               // do not read anything into the const value
282
               skip_value<JSON>::op<Opts>(ctx, it, end);
283
            }
284
         }
285
         else if constexpr (is_function_ptr_or_ref<V>) {
286
            // Function pointers cannot be deserialized from JSON.
287
            // When write_function_pointers is enabled the writer emits a type-name string,
288
            // but there is nothing meaningful to reconstruct on read — just skip the value.
289
            // When write_function_pointers is off the key is never written, so this branch
290
            // is unreachable in that case.
291
            skip_value<JSON>::op<Opts>(ctx, it, end);
292
         }
293
         else if constexpr (glaze_object_t<T>) {
294
            from<JSON, std::remove_cvref_t<V>>::template op<FieldOpts>(get_member(value, get<I>(reflect<T>::values)),
295
                                                                       ctx, it, end);
296
         }
297
6.02k
         else {
298
6.02k
            from<JSON, std::remove_cvref_t<V>>::template op<FieldOpts>(get_member(value, get<I>(to_tie(value))), ctx,
299
6.02k
                                                                       it, end);
300
6.02k
         }
301
6.02k
      }
302
303
      // The key was present and handled, even when the value itself was skipped
304
      if constexpr (Opts.error_on_missing_keys || Opts.partial_read) {
305
         ((selected_index = I), ...);
306
      }
307
6.02k
   }
_ZN3glz18decode_field_valueITnDaXtlNS_4optsELj10ELb0ELb0ELb1ELb1EEETnDaXtlS1_Lj10ELb0ELb0ELb1ELb1ELb0ELb0ELb0ELb0ELj4EEE6PersonLm0ERS2_JETkNS_10is_contextERNS_7contextERPKcS8_Qoo14glaze_object_tIT1_E11reflectableIS9_EEEvOT3_OT5_OT6_OT7_DpOT4_
Line
Count
Source
246
651
   {
247
      // Check for null value skipping on read
248
      if constexpr (check_skip_null_members_on_read(Opts)) {
249
         if (*it == 'n') {
250
            ++it;
251
            if constexpr (not Opts.null_terminated) {
252
               if (it == end) [[unlikely]] {
253
                  ctx.error = error_code::unexpected_end;
254
                  return;
255
               }
256
            }
257
            match<"ull", Opts>(ctx, it, end);
258
            if (bool(ctx.error)) [[unlikely]]
259
               return;
260
            // Successfully matched "null", skip it
261
            if constexpr (Opts.error_on_missing_keys || Opts.partial_read) {
262
               ((selected_index = I), ...); // Mark as handled even if skipped
263
            }
264
            return;
265
         }
266
      }
267
268
      if constexpr (skipped_by_meta<T, I, operation::parse>) {
269
         skip_value<JSON>::op<Opts>(ctx, it, end);
270
         if (bool(ctx.error)) [[unlikely]]
271
            return; // Propagate error from skip_value
272
      }
273
651
      else {
274
651
         using V = refl_t<T, I>;
275
276
         if constexpr (const_value_v<V>) {
277
            if constexpr (check_error_on_const_read(Opts)) {
278
               ctx.error = error_code::attempt_const_read;
279
            }
280
            else {
281
               // do not read anything into the const value
282
               skip_value<JSON>::op<Opts>(ctx, it, end);
283
            }
284
         }
285
         else if constexpr (is_function_ptr_or_ref<V>) {
286
            // Function pointers cannot be deserialized from JSON.
287
            // When write_function_pointers is enabled the writer emits a type-name string,
288
            // but there is nothing meaningful to reconstruct on read — just skip the value.
289
            // When write_function_pointers is off the key is never written, so this branch
290
            // is unreachable in that case.
291
            skip_value<JSON>::op<Opts>(ctx, it, end);
292
         }
293
         else if constexpr (glaze_object_t<T>) {
294
            from<JSON, std::remove_cvref_t<V>>::template op<FieldOpts>(get_member(value, get<I>(reflect<T>::values)),
295
                                                                       ctx, it, end);
296
         }
297
651
         else {
298
651
            from<JSON, std::remove_cvref_t<V>>::template op<FieldOpts>(get_member(value, get<I>(to_tie(value))), ctx,
299
651
                                                                       it, end);
300
651
         }
301
651
      }
302
303
      // The key was present and handled, even when the value itself was skipped
304
      if constexpr (Opts.error_on_missing_keys || Opts.partial_read) {
305
         ((selected_index = I), ...);
306
      }
307
651
   }
_ZN3glz18decode_field_valueITnDaXtlNS_4optsELj10ELb0ELb0ELb1ELb1EEETnDaXtlS1_Lj10ELb0ELb0ELb1ELb1ELb0ELb0ELb0ELb0ELj4EEE6PersonLm1ERS2_JETkNS_10is_contextERNS_7contextERPKcS8_Qoo14glaze_object_tIT1_E11reflectableIS9_EEEvOT3_OT5_OT6_OT7_DpOT4_
Line
Count
Source
246
3.64k
   {
247
      // Check for null value skipping on read
248
      if constexpr (check_skip_null_members_on_read(Opts)) {
249
         if (*it == 'n') {
250
            ++it;
251
            if constexpr (not Opts.null_terminated) {
252
               if (it == end) [[unlikely]] {
253
                  ctx.error = error_code::unexpected_end;
254
                  return;
255
               }
256
            }
257
            match<"ull", Opts>(ctx, it, end);
258
            if (bool(ctx.error)) [[unlikely]]
259
               return;
260
            // Successfully matched "null", skip it
261
            if constexpr (Opts.error_on_missing_keys || Opts.partial_read) {
262
               ((selected_index = I), ...); // Mark as handled even if skipped
263
            }
264
            return;
265
         }
266
      }
267
268
      if constexpr (skipped_by_meta<T, I, operation::parse>) {
269
         skip_value<JSON>::op<Opts>(ctx, it, end);
270
         if (bool(ctx.error)) [[unlikely]]
271
            return; // Propagate error from skip_value
272
      }
273
3.64k
      else {
274
3.64k
         using V = refl_t<T, I>;
275
276
         if constexpr (const_value_v<V>) {
277
            if constexpr (check_error_on_const_read(Opts)) {
278
               ctx.error = error_code::attempt_const_read;
279
            }
280
            else {
281
               // do not read anything into the const value
282
               skip_value<JSON>::op<Opts>(ctx, it, end);
283
            }
284
         }
285
         else if constexpr (is_function_ptr_or_ref<V>) {
286
            // Function pointers cannot be deserialized from JSON.
287
            // When write_function_pointers is enabled the writer emits a type-name string,
288
            // but there is nothing meaningful to reconstruct on read — just skip the value.
289
            // When write_function_pointers is off the key is never written, so this branch
290
            // is unreachable in that case.
291
            skip_value<JSON>::op<Opts>(ctx, it, end);
292
         }
293
         else if constexpr (glaze_object_t<T>) {
294
            from<JSON, std::remove_cvref_t<V>>::template op<FieldOpts>(get_member(value, get<I>(reflect<T>::values)),
295
                                                                       ctx, it, end);
296
         }
297
3.64k
         else {
298
3.64k
            from<JSON, std::remove_cvref_t<V>>::template op<FieldOpts>(get_member(value, get<I>(to_tie(value))), ctx,
299
3.64k
                                                                       it, end);
300
3.64k
         }
301
3.64k
      }
302
303
      // The key was present and handled, even when the value itself was skipped
304
      if constexpr (Opts.error_on_missing_keys || Opts.partial_read) {
305
         ((selected_index = I), ...);
306
      }
307
3.64k
   }
_ZN3glz18decode_field_valueITnDaXtlNS_4optsELj10ELb0ELb0ELb1ELb1EEETnDaXtlS1_Lj10ELb0ELb0ELb1ELb1ELb0ELb0ELb0ELb0ELj4EEE6PersonLm2ERS2_JETkNS_10is_contextERNS_7contextERPKcS8_Qoo14glaze_object_tIT1_E11reflectableIS9_EEEvOT3_OT5_OT6_OT7_DpOT4_
Line
Count
Source
246
5.56k
   {
247
      // Check for null value skipping on read
248
      if constexpr (check_skip_null_members_on_read(Opts)) {
249
         if (*it == 'n') {
250
            ++it;
251
            if constexpr (not Opts.null_terminated) {
252
               if (it == end) [[unlikely]] {
253
                  ctx.error = error_code::unexpected_end;
254
                  return;
255
               }
256
            }
257
            match<"ull", Opts>(ctx, it, end);
258
            if (bool(ctx.error)) [[unlikely]]
259
               return;
260
            // Successfully matched "null", skip it
261
            if constexpr (Opts.error_on_missing_keys || Opts.partial_read) {
262
               ((selected_index = I), ...); // Mark as handled even if skipped
263
            }
264
            return;
265
         }
266
      }
267
268
      if constexpr (skipped_by_meta<T, I, operation::parse>) {
269
         skip_value<JSON>::op<Opts>(ctx, it, end);
270
         if (bool(ctx.error)) [[unlikely]]
271
            return; // Propagate error from skip_value
272
      }
273
5.56k
      else {
274
5.56k
         using V = refl_t<T, I>;
275
276
         if constexpr (const_value_v<V>) {
277
            if constexpr (check_error_on_const_read(Opts)) {
278
               ctx.error = error_code::attempt_const_read;
279
            }
280
            else {
281
               // do not read anything into the const value
282
               skip_value<JSON>::op<Opts>(ctx, it, end);
283
            }
284
         }
285
         else if constexpr (is_function_ptr_or_ref<V>) {
286
            // Function pointers cannot be deserialized from JSON.
287
            // When write_function_pointers is enabled the writer emits a type-name string,
288
            // but there is nothing meaningful to reconstruct on read — just skip the value.
289
            // When write_function_pointers is off the key is never written, so this branch
290
            // is unreachable in that case.
291
            skip_value<JSON>::op<Opts>(ctx, it, end);
292
         }
293
         else if constexpr (glaze_object_t<T>) {
294
            from<JSON, std::remove_cvref_t<V>>::template op<FieldOpts>(get_member(value, get<I>(reflect<T>::values)),
295
                                                                       ctx, it, end);
296
         }
297
5.56k
         else {
298
5.56k
            from<JSON, std::remove_cvref_t<V>>::template op<FieldOpts>(get_member(value, get<I>(to_tie(value))), ctx,
299
5.56k
                                                                       it, end);
300
5.56k
         }
301
5.56k
      }
302
303
      // The key was present and handled, even when the value itself was skipped
304
      if constexpr (Opts.error_on_missing_keys || Opts.partial_read) {
305
         ((selected_index = I), ...);
306
      }
307
5.56k
   }
308
309
   template <auto Opts, class T, size_t I, class Value, class... SelectedIndex>
310
      requires(glaze_object_t<T> || reflectable<T>)
311
   void decode_index(Value&& value, is_context auto&& ctx, auto&& it, auto&& end, SelectedIndex&&... selected_index)
312
20.8k
   {
313
20.8k
      static constexpr auto Key = get<I>(reflect<T>::keys);
314
20.8k
      static constexpr auto KeyWithEndQuote = join_v<Key, chars<"\"">>;
315
20.8k
      static constexpr auto Length = KeyWithEndQuote.size();
316
317
      // Compared as a count rather than by forming `it + Length` first: a pointer past one-past-the-end
318
      // is undefined even when it is never dereferenced, and a key near the end of a short buffer
319
      // reaches this with fewer than `Length` bytes left. Signed, so a cursor that has overshot
320
      // `end` does not read as room.
321
20.8k
      if (((end - it) > std::ptrdiff_t(Length)) && comparitor<KeyWithEndQuote>(it)) [[likely]] {
322
20.5k
         it += Length;
323
20.5k
         if constexpr (not Opts.null_terminated) {
324
9.94k
            if (it == end) [[unlikely]] {
325
0
               ctx.error = error_code::unexpected_end;
326
0
               return;
327
0
            }
328
9.94k
         }
329
330
20.5k
         if (skip_ws<Opts>(ctx, it, end)) {
331
24
            return;
332
24
         }
333
20.5k
         if (match_invalid_end<':', Opts>(ctx, it, end)) {
334
91
            return;
335
91
         }
336
20.4k
         if (skip_ws<Opts>(ctx, it, end)) {
337
24
            return;
338
24
         }
339
340
         // The hash path has already consumed the whitespace before the value
341
20.4k
         decode_field_value<Opts, ws_handled<Opts>(), T, I>(value, ctx, it, end, selected_index...);
342
20.4k
      }
343
294
      else [[unlikely]] {
344
294
         decode_index_unknown_key<Opts, T>(value, ctx, it, end);
345
294
      }
346
20.8k
   }
_ZN3glz12decode_indexITnDaXtlNS_4optsELj10ELb1ELb0ELb1ELb1EEE6PersonLm0ERS2_JETkNS_10is_contextERNS_7contextERPKcS8_Qoo14glaze_object_tIT0_E11reflectableIS9_EEEvOT2_OT4_OT5_OT6_DpOT3_
Line
Count
Source
312
970
   {
313
970
      static constexpr auto Key = get<I>(reflect<T>::keys);
314
970
      static constexpr auto KeyWithEndQuote = join_v<Key, chars<"\"">>;
315
970
      static constexpr auto Length = KeyWithEndQuote.size();
316
317
      // Compared as a count rather than by forming `it + Length` first: a pointer past one-past-the-end
318
      // is undefined even when it is never dereferenced, and a key near the end of a short buffer
319
      // reaches this with fewer than `Length` bytes left. Signed, so a cursor that has overshot
320
      // `end` does not read as room.
321
970
      if (((end - it) > std::ptrdiff_t(Length)) && comparitor<KeyWithEndQuote>(it)) [[likely]] {
322
945
         it += Length;
323
         if constexpr (not Opts.null_terminated) {
324
            if (it == end) [[unlikely]] {
325
               ctx.error = error_code::unexpected_end;
326
               return;
327
            }
328
         }
329
330
945
         if (skip_ws<Opts>(ctx, it, end)) {
331
0
            return;
332
0
         }
333
945
         if (match_invalid_end<':', Opts>(ctx, it, end)) {
334
16
            return;
335
16
         }
336
929
         if (skip_ws<Opts>(ctx, it, end)) {
337
0
            return;
338
0
         }
339
340
         // The hash path has already consumed the whitespace before the value
341
929
         decode_field_value<Opts, ws_handled<Opts>(), T, I>(value, ctx, it, end, selected_index...);
342
929
      }
343
25
      else [[unlikely]] {
344
25
         decode_index_unknown_key<Opts, T>(value, ctx, it, end);
345
25
      }
346
970
   }
_ZN3glz12decode_indexITnDaXtlNS_4optsELj10ELb1ELb0ELb1ELb1EEE6PersonLm1ERS2_JETkNS_10is_contextERNS_7contextERPKcS8_Qoo14glaze_object_tIT0_E11reflectableIS9_EEEvOT2_OT4_OT5_OT6_DpOT3_
Line
Count
Source
312
3.70k
   {
313
3.70k
      static constexpr auto Key = get<I>(reflect<T>::keys);
314
3.70k
      static constexpr auto KeyWithEndQuote = join_v<Key, chars<"\"">>;
315
3.70k
      static constexpr auto Length = KeyWithEndQuote.size();
316
317
      // Compared as a count rather than by forming `it + Length` first: a pointer past one-past-the-end
318
      // is undefined even when it is never dereferenced, and a key near the end of a short buffer
319
      // reaches this with fewer than `Length` bytes left. Signed, so a cursor that has overshot
320
      // `end` does not read as room.
321
3.70k
      if (((end - it) > std::ptrdiff_t(Length)) && comparitor<KeyWithEndQuote>(it)) [[likely]] {
322
3.65k
         it += Length;
323
         if constexpr (not Opts.null_terminated) {
324
            if (it == end) [[unlikely]] {
325
               ctx.error = error_code::unexpected_end;
326
               return;
327
            }
328
         }
329
330
3.65k
         if (skip_ws<Opts>(ctx, it, end)) {
331
0
            return;
332
0
         }
333
3.65k
         if (match_invalid_end<':', Opts>(ctx, it, end)) {
334
16
            return;
335
16
         }
336
3.64k
         if (skip_ws<Opts>(ctx, it, end)) {
337
0
            return;
338
0
         }
339
340
         // The hash path has already consumed the whitespace before the value
341
3.64k
         decode_field_value<Opts, ws_handled<Opts>(), T, I>(value, ctx, it, end, selected_index...);
342
3.64k
      }
343
48
      else [[unlikely]] {
344
48
         decode_index_unknown_key<Opts, T>(value, ctx, it, end);
345
48
      }
346
3.70k
   }
_ZN3glz12decode_indexITnDaXtlNS_4optsELj10ELb1ELb0ELb1ELb1EEE6PersonLm2ERS2_JETkNS_10is_contextERNS_7contextERPKcS8_Qoo14glaze_object_tIT0_E11reflectableIS9_EEEvOT2_OT4_OT5_OT6_DpOT3_
Line
Count
Source
312
6.10k
   {
313
6.10k
      static constexpr auto Key = get<I>(reflect<T>::keys);
314
6.10k
      static constexpr auto KeyWithEndQuote = join_v<Key, chars<"\"">>;
315
6.10k
      static constexpr auto Length = KeyWithEndQuote.size();
316
317
      // Compared as a count rather than by forming `it + Length` first: a pointer past one-past-the-end
318
      // is undefined even when it is never dereferenced, and a key near the end of a short buffer
319
      // reaches this with fewer than `Length` bytes left. Signed, so a cursor that has overshot
320
      // `end` does not read as room.
321
6.10k
      if (((end - it) > std::ptrdiff_t(Length)) && comparitor<KeyWithEndQuote>(it)) [[likely]] {
322
6.04k
         it += Length;
323
         if constexpr (not Opts.null_terminated) {
324
            if (it == end) [[unlikely]] {
325
               ctx.error = error_code::unexpected_end;
326
               return;
327
            }
328
         }
329
330
6.04k
         if (skip_ws<Opts>(ctx, it, end)) {
331
0
            return;
332
0
         }
333
6.04k
         if (match_invalid_end<':', Opts>(ctx, it, end)) {
334
20
            return;
335
20
         }
336
6.02k
         if (skip_ws<Opts>(ctx, it, end)) {
337
0
            return;
338
0
         }
339
340
         // The hash path has already consumed the whitespace before the value
341
6.02k
         decode_field_value<Opts, ws_handled<Opts>(), T, I>(value, ctx, it, end, selected_index...);
342
6.02k
      }
343
61
      else [[unlikely]] {
344
61
         decode_index_unknown_key<Opts, T>(value, ctx, it, end);
345
61
      }
346
6.10k
   }
_ZN3glz12decode_indexITnDaXtlNS_4optsELj10ELb0ELb0ELb1ELb1EEE6PersonLm0ERS2_JETkNS_10is_contextERNS_7contextERPKcS8_Qoo14glaze_object_tIT0_E11reflectableIS9_EEEvOT2_OT4_OT5_OT6_DpOT3_
Line
Count
Source
312
704
   {
313
704
      static constexpr auto Key = get<I>(reflect<T>::keys);
314
704
      static constexpr auto KeyWithEndQuote = join_v<Key, chars<"\"">>;
315
704
      static constexpr auto Length = KeyWithEndQuote.size();
316
317
      // Compared as a count rather than by forming `it + Length` first: a pointer past one-past-the-end
318
      // is undefined even when it is never dereferenced, and a key near the end of a short buffer
319
      // reaches this with fewer than `Length` bytes left. Signed, so a cursor that has overshot
320
      // `end` does not read as room.
321
704
      if (((end - it) > std::ptrdiff_t(Length)) && comparitor<KeyWithEndQuote>(it)) [[likely]] {
322
678
         it += Length;
323
678
         if constexpr (not Opts.null_terminated) {
324
678
            if (it == end) [[unlikely]] {
325
0
               ctx.error = error_code::unexpected_end;
326
0
               return;
327
0
            }
328
678
         }
329
330
678
         if (skip_ws<Opts>(ctx, it, end)) {
331
8
            return;
332
8
         }
333
670
         if (match_invalid_end<':', Opts>(ctx, it, end)) {
334
11
            return;
335
11
         }
336
659
         if (skip_ws<Opts>(ctx, it, end)) {
337
8
            return;
338
8
         }
339
340
         // The hash path has already consumed the whitespace before the value
341
651
         decode_field_value<Opts, ws_handled<Opts>(), T, I>(value, ctx, it, end, selected_index...);
342
651
      }
343
26
      else [[unlikely]] {
344
26
         decode_index_unknown_key<Opts, T>(value, ctx, it, end);
345
26
      }
346
704
   }
_ZN3glz12decode_indexITnDaXtlNS_4optsELj10ELb0ELb0ELb1ELb1EEE6PersonLm1ERS2_JETkNS_10is_contextERNS_7contextERPKcS8_Qoo14glaze_object_tIT0_E11reflectableIS9_EEEvOT2_OT4_OT5_OT6_DpOT3_
Line
Count
Source
312
3.74k
   {
313
3.74k
      static constexpr auto Key = get<I>(reflect<T>::keys);
314
3.74k
      static constexpr auto KeyWithEndQuote = join_v<Key, chars<"\"">>;
315
3.74k
      static constexpr auto Length = KeyWithEndQuote.size();
316
317
      // Compared as a count rather than by forming `it + Length` first: a pointer past one-past-the-end
318
      // is undefined even when it is never dereferenced, and a key near the end of a short buffer
319
      // reaches this with fewer than `Length` bytes left. Signed, so a cursor that has overshot
320
      // `end` does not read as room.
321
3.74k
      if (((end - it) > std::ptrdiff_t(Length)) && comparitor<KeyWithEndQuote>(it)) [[likely]] {
322
3.67k
         it += Length;
323
3.67k
         if constexpr (not Opts.null_terminated) {
324
3.67k
            if (it == end) [[unlikely]] {
325
0
               ctx.error = error_code::unexpected_end;
326
0
               return;
327
0
            }
328
3.67k
         }
329
330
3.67k
         if (skip_ws<Opts>(ctx, it, end)) {
331
8
            return;
332
8
         }
333
3.67k
         if (match_invalid_end<':', Opts>(ctx, it, end)) {
334
15
            return;
335
15
         }
336
3.65k
         if (skip_ws<Opts>(ctx, it, end)) {
337
8
            return;
338
8
         }
339
340
         // The hash path has already consumed the whitespace before the value
341
3.64k
         decode_field_value<Opts, ws_handled<Opts>(), T, I>(value, ctx, it, end, selected_index...);
342
3.64k
      }
343
66
      else [[unlikely]] {
344
66
         decode_index_unknown_key<Opts, T>(value, ctx, it, end);
345
66
      }
346
3.74k
   }
_ZN3glz12decode_indexITnDaXtlNS_4optsELj10ELb0ELb0ELb1ELb1EEE6PersonLm2ERS2_JETkNS_10is_contextERNS_7contextERPKcS8_Qoo14glaze_object_tIT0_E11reflectableIS9_EEEvOT2_OT4_OT5_OT6_DpOT3_
Line
Count
Source
312
5.65k
   {
313
5.65k
      static constexpr auto Key = get<I>(reflect<T>::keys);
314
5.65k
      static constexpr auto KeyWithEndQuote = join_v<Key, chars<"\"">>;
315
5.65k
      static constexpr auto Length = KeyWithEndQuote.size();
316
317
      // Compared as a count rather than by forming `it + Length` first: a pointer past one-past-the-end
318
      // is undefined even when it is never dereferenced, and a key near the end of a short buffer
319
      // reaches this with fewer than `Length` bytes left. Signed, so a cursor that has overshot
320
      // `end` does not read as room.
321
5.65k
      if (((end - it) > std::ptrdiff_t(Length)) && comparitor<KeyWithEndQuote>(it)) [[likely]] {
322
5.58k
         it += Length;
323
5.58k
         if constexpr (not Opts.null_terminated) {
324
5.58k
            if (it == end) [[unlikely]] {
325
0
               ctx.error = error_code::unexpected_end;
326
0
               return;
327
0
            }
328
5.58k
         }
329
330
5.58k
         if (skip_ws<Opts>(ctx, it, end)) {
331
8
            return;
332
8
         }
333
5.58k
         if (match_invalid_end<':', Opts>(ctx, it, end)) {
334
13
            return;
335
13
         }
336
5.56k
         if (skip_ws<Opts>(ctx, it, end)) {
337
8
            return;
338
8
         }
339
340
         // The hash path has already consumed the whitespace before the value
341
5.56k
         decode_field_value<Opts, ws_handled<Opts>(), T, I>(value, ctx, it, end, selected_index...);
342
5.56k
      }
343
68
      else [[unlikely]] {
344
68
         decode_index_unknown_key<Opts, T>(value, ctx, it, end);
345
68
      }
346
5.65k
   }
347
348
   template <auto Opts, class T, size_t I, class Value>
349
      requires(glaze_enum_t<T> || (meta_keys<T> && std::is_enum_v<T>))
350
   void decode_index(Value&& value, is_context auto&& ctx, auto&& it, auto end) noexcept
351
   {
352
      static constexpr auto TargetKey = glz::get<I>(reflect<T>::keys);
353
      static constexpr auto Length = TargetKey.size();
354
355
      // A count, not a formed pointer -- see the note in the object overload above.
356
      if (((end - it) > std::ptrdiff_t(Length)) && comparitor<TargetKey>(it)) [[likely]] {
357
         it += Length;
358
         if (*it != '"') [[unlikely]] {
359
            ctx.error = error_code::unexpected_enum;
360
            return;
361
         }
362
         value = get<I>(reflect<T>::values);
363
364
         ++it;
365
         if constexpr (not Opts.null_terminated) {
366
            if (it == end) {
367
               ctx.error = error_code::end_reached;
368
               return;
369
            }
370
         }
371
      }
372
      else [[unlikely]] {
373
         ctx.error = error_code::unexpected_enum;
374
         return;
375
      }
376
   }
377
378
   template <auto Opts, class T, class Value, class... SelectedIndex>
379
      requires(glaze_object_t<T> || reflectable<T>)
380
   GLZ_ALWAYS_INLINE constexpr void parse_and_invoke(Value&& value, is_context auto&& ctx, auto&& it, auto&& end,
381
                                                     SelectedIndex&&... selected_index)
382
20.9k
   {
383
20.9k
      constexpr auto N = reflect<T>::size;
384
385
      if constexpr (N == 1) {
386
         decode_index<Opts, T, 0>(value, ctx, it, end, selected_index...);
387
      }
388
      else if constexpr (check_linear_search(Opts)) {
389
         // Linear search path
390
         auto key_start = it;
391
         const auto index = decode_linear<T>(ctx, it, end);
392
         if (bool(ctx.error)) [[unlikely]]
393
            return;
394
395
         if (index >= N) [[unlikely]] {
396
            if constexpr (Opts.error_on_unknown_keys) {
397
               ctx.error = error_code::unknown_key;
398
               return;
399
            }
400
            else {
401
               // it sits one past the closing quote, so the raw key spans [key_start, it - 1)
402
               if (validate_utf8_span<Opts>(ctx, key_start, it - 1)) [[unlikely]] {
403
                  return;
404
               }
405
               const sv key{key_start, size_t(it - key_start - 1)};
406
               if constexpr (not Opts.null_terminated) {
407
                  if (it == end) [[unlikely]] {
408
                     ctx.error = error_code::unexpected_end;
409
                     return;
410
                  }
411
               }
412
               if (parse_ws_colon<Opts>(ctx, it, end)) {
413
                  return;
414
               }
415
               parse<JSON>::handle_unknown<Opts>(key, value, ctx, it, end);
416
               return;
417
            }
418
         }
419
420
         if constexpr (not Opts.null_terminated) {
421
            if (it == end) [[unlikely]] {
422
               ctx.error = error_code::unexpected_end;
423
               return;
424
            }
425
         }
426
         if (skip_ws<Opts>(ctx, it, end)) {
427
            return;
428
         }
429
         if (match_invalid_end<':', Opts>(ctx, it, end)) {
430
            return;
431
         }
432
         if (skip_ws<Opts>(ctx, it, end)) {
433
            return;
434
         }
435
436
         // Fold expression dispatch - avoids jump table overhead for smaller binary size.
437
         // For linear_search, pass Opts through to the field parser to avoid duplicate template
438
         // instantiations; the whitespace before the value has already been skipped either way.
439
         [&]<size_t... Is>(std::index_sequence<Is...>) {
440
            (void)(((index == Is ? (decode_field_value<Opts, Opts, T, Is>(value, ctx, it, end, selected_index...), true)
441
                                 : false) ||
442
                    ...));
443
         }(std::make_index_sequence<N>{});
444
      }
445
20.9k
      else {
446
         // Hash-based lookup
447
20.9k
         constexpr auto& HashInfo = hash_info<T>;
448
20.9k
         constexpr auto type = HashInfo.type;
449
20.9k
         static_assert(bool(type), "invalid hash algorithm");
450
451
20.9k
         const auto index = decode_hash<JSON, T, HashInfo, HashInfo.type>::op(it, end);
452
453
20.9k
         if (index >= N) [[unlikely]] {
454
67
            if constexpr (Opts.error_on_unknown_keys) {
455
67
               ctx.error = error_code::unknown_key;
456
67
               return;
457
            }
458
            else {
459
               auto start = it;
460
               skip_string_view(ctx, it, end);
461
               if (bool(ctx.error)) [[unlikely]]
462
                  return;
463
               if (validate_utf8_span<Opts>(ctx, start, it)) [[unlikely]]
464
                  return;
465
               const sv key = {start, size_t(it - start)};
466
               ++it; // skip the quote
467
               if constexpr (not Opts.null_terminated) {
468
                  if (it == end) [[unlikely]] {
469
                     ctx.error = error_code::unexpected_end;
470
                     return;
471
                  }
472
               }
473
               if (parse_ws_colon<Opts>(ctx, it, end)) {
474
                  return;
475
               }
476
               parse<JSON>::handle_unknown<Opts>(key, value, ctx, it, end);
477
               return;
478
            }
479
67
         }
480
481
         if constexpr (N == 2) {
482
            if (index == 0) {
483
               decode_index<Opts, T, 0>(value, ctx, it, end, selected_index...);
484
            }
485
            else {
486
               decode_index<Opts, T, 1>(value, ctx, it, end, selected_index...);
487
            }
488
         }
489
20.9k
         else {
490
20.9k
            visit<N>([&]<size_t I>() { decode_index<Opts, T, I>(value, ctx, it, end, selected_index...); }, index);
_ZZN3glz16parse_and_invokeITnDaXtlNS_4optsELj10ELb1ELb0ELb1ELb1EEE6PersonRS2_JETkNS_10is_contextERNS_7contextERPKcS8_Qoo14glaze_object_tIT0_E11reflectableIS9_EEEvOT1_OT3_OT4_OT5_DpOT2_ENKUlTnmvE_clILm0EEEDav
Line
Count
Source
490
970
            visit<N>([&]<size_t I>() { decode_index<Opts, T, I>(value, ctx, it, end, selected_index...); }, index);
_ZZN3glz16parse_and_invokeITnDaXtlNS_4optsELj10ELb1ELb0ELb1ELb1EEE6PersonRS2_JETkNS_10is_contextERNS_7contextERPKcS8_Qoo14glaze_object_tIT0_E11reflectableIS9_EEEvOT1_OT3_OT4_OT5_DpOT2_ENKUlTnmvE_clILm1EEEDav
Line
Count
Source
490
3.70k
            visit<N>([&]<size_t I>() { decode_index<Opts, T, I>(value, ctx, it, end, selected_index...); }, index);
_ZZN3glz16parse_and_invokeITnDaXtlNS_4optsELj10ELb1ELb0ELb1ELb1EEE6PersonRS2_JETkNS_10is_contextERNS_7contextERPKcS8_Qoo14glaze_object_tIT0_E11reflectableIS9_EEEvOT1_OT3_OT4_OT5_DpOT2_ENKUlTnmvE_clILm2EEEDav
Line
Count
Source
490
6.10k
            visit<N>([&]<size_t I>() { decode_index<Opts, T, I>(value, ctx, it, end, selected_index...); }, index);
_ZZN3glz16parse_and_invokeITnDaXtlNS_4optsELj10ELb0ELb0ELb1ELb1EEE6PersonRS2_JETkNS_10is_contextERNS_7contextERPKcS8_Qoo14glaze_object_tIT0_E11reflectableIS9_EEEvOT1_OT3_OT4_OT5_DpOT2_ENKUlTnmvE_clILm0EEEDav
Line
Count
Source
490
704
            visit<N>([&]<size_t I>() { decode_index<Opts, T, I>(value, ctx, it, end, selected_index...); }, index);
_ZZN3glz16parse_and_invokeITnDaXtlNS_4optsELj10ELb0ELb0ELb1ELb1EEE6PersonRS2_JETkNS_10is_contextERNS_7contextERPKcS8_Qoo14glaze_object_tIT0_E11reflectableIS9_EEEvOT1_OT3_OT4_OT5_DpOT2_ENKUlTnmvE_clILm1EEEDav
Line
Count
Source
490
3.74k
            visit<N>([&]<size_t I>() { decode_index<Opts, T, I>(value, ctx, it, end, selected_index...); }, index);
_ZZN3glz16parse_and_invokeITnDaXtlNS_4optsELj10ELb0ELb0ELb1ELb1EEE6PersonRS2_JETkNS_10is_contextERNS_7contextERPKcS8_Qoo14glaze_object_tIT0_E11reflectableIS9_EEEvOT1_OT3_OT4_OT5_DpOT2_ENKUlTnmvE_clILm2EEEDav
Line
Count
Source
490
5.65k
            visit<N>([&]<size_t I>() { decode_index<Opts, T, I>(value, ctx, it, end, selected_index...); }, index);
491
20.9k
         }
492
20.9k
      }
493
20.9k
   }
_ZN3glz16parse_and_invokeITnDaXtlNS_4optsELj10ELb1ELb0ELb1ELb1EEE6PersonRS2_JETkNS_10is_contextERNS_7contextERPKcS8_Qoo14glaze_object_tIT0_E11reflectableIS9_EEEvOT1_OT3_OT4_OT5_DpOT2_
Line
Count
Source
382
10.8k
   {
383
10.8k
      constexpr auto N = reflect<T>::size;
384
385
      if constexpr (N == 1) {
386
         decode_index<Opts, T, 0>(value, ctx, it, end, selected_index...);
387
      }
388
      else if constexpr (check_linear_search(Opts)) {
389
         // Linear search path
390
         auto key_start = it;
391
         const auto index = decode_linear<T>(ctx, it, end);
392
         if (bool(ctx.error)) [[unlikely]]
393
            return;
394
395
         if (index >= N) [[unlikely]] {
396
            if constexpr (Opts.error_on_unknown_keys) {
397
               ctx.error = error_code::unknown_key;
398
               return;
399
            }
400
            else {
401
               // it sits one past the closing quote, so the raw key spans [key_start, it - 1)
402
               if (validate_utf8_span<Opts>(ctx, key_start, it - 1)) [[unlikely]] {
403
                  return;
404
               }
405
               const sv key{key_start, size_t(it - key_start - 1)};
406
               if constexpr (not Opts.null_terminated) {
407
                  if (it == end) [[unlikely]] {
408
                     ctx.error = error_code::unexpected_end;
409
                     return;
410
                  }
411
               }
412
               if (parse_ws_colon<Opts>(ctx, it, end)) {
413
                  return;
414
               }
415
               parse<JSON>::handle_unknown<Opts>(key, value, ctx, it, end);
416
               return;
417
            }
418
         }
419
420
         if constexpr (not Opts.null_terminated) {
421
            if (it == end) [[unlikely]] {
422
               ctx.error = error_code::unexpected_end;
423
               return;
424
            }
425
         }
426
         if (skip_ws<Opts>(ctx, it, end)) {
427
            return;
428
         }
429
         if (match_invalid_end<':', Opts>(ctx, it, end)) {
430
            return;
431
         }
432
         if (skip_ws<Opts>(ctx, it, end)) {
433
            return;
434
         }
435
436
         // Fold expression dispatch - avoids jump table overhead for smaller binary size.
437
         // For linear_search, pass Opts through to the field parser to avoid duplicate template
438
         // instantiations; the whitespace before the value has already been skipped either way.
439
         [&]<size_t... Is>(std::index_sequence<Is...>) {
440
            (void)(((index == Is ? (decode_field_value<Opts, Opts, T, Is>(value, ctx, it, end, selected_index...), true)
441
                                 : false) ||
442
                    ...));
443
         }(std::make_index_sequence<N>{});
444
      }
445
10.8k
      else {
446
         // Hash-based lookup
447
10.8k
         constexpr auto& HashInfo = hash_info<T>;
448
10.8k
         constexpr auto type = HashInfo.type;
449
10.8k
         static_assert(bool(type), "invalid hash algorithm");
450
451
10.8k
         const auto index = decode_hash<JSON, T, HashInfo, HashInfo.type>::op(it, end);
452
453
10.8k
         if (index >= N) [[unlikely]] {
454
38
            if constexpr (Opts.error_on_unknown_keys) {
455
38
               ctx.error = error_code::unknown_key;
456
38
               return;
457
            }
458
            else {
459
               auto start = it;
460
               skip_string_view(ctx, it, end);
461
               if (bool(ctx.error)) [[unlikely]]
462
                  return;
463
               if (validate_utf8_span<Opts>(ctx, start, it)) [[unlikely]]
464
                  return;
465
               const sv key = {start, size_t(it - start)};
466
               ++it; // skip the quote
467
               if constexpr (not Opts.null_terminated) {
468
                  if (it == end) [[unlikely]] {
469
                     ctx.error = error_code::unexpected_end;
470
                     return;
471
                  }
472
               }
473
               if (parse_ws_colon<Opts>(ctx, it, end)) {
474
                  return;
475
               }
476
               parse<JSON>::handle_unknown<Opts>(key, value, ctx, it, end);
477
               return;
478
            }
479
38
         }
480
481
         if constexpr (N == 2) {
482
            if (index == 0) {
483
               decode_index<Opts, T, 0>(value, ctx, it, end, selected_index...);
484
            }
485
            else {
486
               decode_index<Opts, T, 1>(value, ctx, it, end, selected_index...);
487
            }
488
         }
489
10.8k
         else {
490
10.8k
            visit<N>([&]<size_t I>() { decode_index<Opts, T, I>(value, ctx, it, end, selected_index...); }, index);
491
10.8k
         }
492
10.8k
      }
493
10.8k
   }
_ZN3glz16parse_and_invokeITnDaXtlNS_4optsELj10ELb0ELb0ELb1ELb1EEE6PersonRS2_JETkNS_10is_contextERNS_7contextERPKcS8_Qoo14glaze_object_tIT0_E11reflectableIS9_EEEvOT1_OT3_OT4_OT5_DpOT2_
Line
Count
Source
382
10.1k
   {
383
10.1k
      constexpr auto N = reflect<T>::size;
384
385
      if constexpr (N == 1) {
386
         decode_index<Opts, T, 0>(value, ctx, it, end, selected_index...);
387
      }
388
      else if constexpr (check_linear_search(Opts)) {
389
         // Linear search path
390
         auto key_start = it;
391
         const auto index = decode_linear<T>(ctx, it, end);
392
         if (bool(ctx.error)) [[unlikely]]
393
            return;
394
395
         if (index >= N) [[unlikely]] {
396
            if constexpr (Opts.error_on_unknown_keys) {
397
               ctx.error = error_code::unknown_key;
398
               return;
399
            }
400
            else {
401
               // it sits one past the closing quote, so the raw key spans [key_start, it - 1)
402
               if (validate_utf8_span<Opts>(ctx, key_start, it - 1)) [[unlikely]] {
403
                  return;
404
               }
405
               const sv key{key_start, size_t(it - key_start - 1)};
406
               if constexpr (not Opts.null_terminated) {
407
                  if (it == end) [[unlikely]] {
408
                     ctx.error = error_code::unexpected_end;
409
                     return;
410
                  }
411
               }
412
               if (parse_ws_colon<Opts>(ctx, it, end)) {
413
                  return;
414
               }
415
               parse<JSON>::handle_unknown<Opts>(key, value, ctx, it, end);
416
               return;
417
            }
418
         }
419
420
         if constexpr (not Opts.null_terminated) {
421
            if (it == end) [[unlikely]] {
422
               ctx.error = error_code::unexpected_end;
423
               return;
424
            }
425
         }
426
         if (skip_ws<Opts>(ctx, it, end)) {
427
            return;
428
         }
429
         if (match_invalid_end<':', Opts>(ctx, it, end)) {
430
            return;
431
         }
432
         if (skip_ws<Opts>(ctx, it, end)) {
433
            return;
434
         }
435
436
         // Fold expression dispatch - avoids jump table overhead for smaller binary size.
437
         // For linear_search, pass Opts through to the field parser to avoid duplicate template
438
         // instantiations; the whitespace before the value has already been skipped either way.
439
         [&]<size_t... Is>(std::index_sequence<Is...>) {
440
            (void)(((index == Is ? (decode_field_value<Opts, Opts, T, Is>(value, ctx, it, end, selected_index...), true)
441
                                 : false) ||
442
                    ...));
443
         }(std::make_index_sequence<N>{});
444
      }
445
10.1k
      else {
446
         // Hash-based lookup
447
10.1k
         constexpr auto& HashInfo = hash_info<T>;
448
10.1k
         constexpr auto type = HashInfo.type;
449
10.1k
         static_assert(bool(type), "invalid hash algorithm");
450
451
10.1k
         const auto index = decode_hash<JSON, T, HashInfo, HashInfo.type>::op(it, end);
452
453
10.1k
         if (index >= N) [[unlikely]] {
454
29
            if constexpr (Opts.error_on_unknown_keys) {
455
29
               ctx.error = error_code::unknown_key;
456
29
               return;
457
            }
458
            else {
459
               auto start = it;
460
               skip_string_view(ctx, it, end);
461
               if (bool(ctx.error)) [[unlikely]]
462
                  return;
463
               if (validate_utf8_span<Opts>(ctx, start, it)) [[unlikely]]
464
                  return;
465
               const sv key = {start, size_t(it - start)};
466
               ++it; // skip the quote
467
               if constexpr (not Opts.null_terminated) {
468
                  if (it == end) [[unlikely]] {
469
                     ctx.error = error_code::unexpected_end;
470
                     return;
471
                  }
472
               }
473
               if (parse_ws_colon<Opts>(ctx, it, end)) {
474
                  return;
475
               }
476
               parse<JSON>::handle_unknown<Opts>(key, value, ctx, it, end);
477
               return;
478
            }
479
29
         }
480
481
         if constexpr (N == 2) {
482
            if (index == 0) {
483
               decode_index<Opts, T, 0>(value, ctx, it, end, selected_index...);
484
            }
485
            else {
486
               decode_index<Opts, T, 1>(value, ctx, it, end, selected_index...);
487
            }
488
         }
489
10.1k
         else {
490
10.1k
            visit<N>([&]<size_t I>() { decode_index<Opts, T, I>(value, ctx, it, end, selected_index...); }, index);
491
10.1k
         }
492
10.1k
      }
493
10.1k
   }
494
495
   template <is_member_function_pointer T>
496
   struct from<JSON, T>
497
   {
498
      template <auto Opts>
499
      GLZ_ALWAYS_INLINE static void op(auto&&, is_context auto&& ctx, auto&&...) noexcept
500
      {
501
         ctx.error = error_code::attempt_member_func_read;
502
      }
503
   };
504
505
   template <is_includer T>
506
   struct from<JSON, T>
507
   {
508
      template <auto Opts, class... Args>
509
      static void op(auto&&, is_context auto&& ctx, auto&& it, auto end) noexcept
510
      {
511
         if constexpr (!check_ws_handled(Opts)) {
512
            if (skip_ws<Opts>(ctx, it, end)) {
513
               return;
514
            }
515
         }
516
517
         match<R"("")", Opts>(ctx, it, end);
518
      }
519
   };
520
521
   template <is_bitset T>
522
   struct from<JSON, T>
523
   {
524
      template <auto Opts>
525
      static void op(auto&& value, is_context auto&& ctx, auto&& it, auto end) noexcept
526
      {
527
         if (match_invalid_end<'"', Opts>(ctx, it, end)) {
528
            return;
529
         }
530
531
         const auto n = value.size();
532
         for (size_t i = 1; it < end; ++i, ++it) {
533
            if (*it == '"') {
534
               ++it;
535
               if constexpr (not Opts.null_terminated) {
536
                  if (it == end) {
537
                     ctx.error = error_code::end_reached;
538
                     return;
539
                  }
540
               }
541
               return;
542
            }
543
544
            if (i > n) {
545
               ctx.error = error_code::exceeded_static_array_size;
546
               return;
547
            }
548
549
            if (*it == '0') {
550
               value[n - i] = 0;
551
            }
552
            else if (*it == '1') {
553
               value[n - i] = 1;
554
            }
555
            else [[unlikely]] {
556
               ctx.error = error_code::syntax_error;
557
               return;
558
            }
559
         }
560
561
         ctx.error = error_code::expected_quote;
562
      }
563
   };
564
565
   template <>
566
   struct from<JSON, skip>
567
   {
568
      template <auto Opts>
569
      GLZ_ALWAYS_INLINE static void op(auto&&, is_context auto&& ctx, auto&&... args) noexcept
570
      {
571
         skip_value<JSON>::op<Opts>(ctx, args...);
572
      }
573
   };
574
575
   template <is_reference_wrapper T>
576
   struct from<JSON, T>
577
   {
578
      template <auto Opts, class... Args>
579
      GLZ_ALWAYS_INLINE static void op(auto&& value, Args&&... args)
580
      {
581
         using V = std::decay_t<decltype(value.get())>;
582
         from<JSON, V>::template op<Opts>(value.get(), std::forward<Args>(args)...);
583
      }
584
   };
585
586
   template <>
587
   struct from<JSON, hidden>
588
   {
589
      template <auto Opts>
590
      GLZ_ALWAYS_INLINE static void op(auto&&, is_context auto&& ctx, auto&&...) noexcept
591
      {
592
         ctx.error = error_code::attempt_read_hidden;
593
      }
594
   };
595
596
   template <complex_t T>
597
   struct from<JSON, T>
598
   {
599
      template <auto Options>
600
      static void op(auto&& v, is_context auto&& ctx, auto&& it, auto end) noexcept
601
      {
602
         constexpr auto Opts = ws_handled_off<Options>();
603
         if constexpr (!check_ws_handled(Options)) {
604
            if (skip_ws<Opts>(ctx, it, end)) {
605
               return;
606
            }
607
         }
608
         if (match_invalid_end<'[', Opts>(ctx, it, end)) {
609
            return;
610
         }
611
         // one nesting level (see enter_depth): counted in both modes so the limit binds,
612
         // released at each syntactic close below
613
         if (enter_depth(ctx)) [[unlikely]] {
614
            return;
615
         }
616
617
         auto* ptr = reinterpret_cast<typename T::value_type*>(&v);
618
         static_assert(sizeof(T) == sizeof(typename T::value_type) * 2);
619
         parse<JSON>::op<Opts>(ptr[0], ctx, it, end);
620
         if (bool(ctx.error)) [[unlikely]]
621
            return;
622
623
         if (skip_ws<Opts>(ctx, it, end)) {
624
            return;
625
         }
626
627
         if (match_invalid_end<',', Opts>(ctx, it, end)) {
628
            return;
629
         }
630
631
         parse<JSON>::op<Opts>(ptr[1], ctx, it, end);
632
         if (bool(ctx.error)) [[unlikely]]
633
            return;
634
635
         if (skip_ws<Opts>(ctx, it, end)) {
636
            return;
637
         }
638
         match<']'>(ctx, it);
639
         --ctx.depth;
640
      }
641
   };
642
643
   template <always_null_t T>
644
   struct from<JSON, T>
645
   {
646
      template <auto Opts>
647
      GLZ_ALWAYS_INLINE static void op(auto&&, is_context auto&& ctx, auto&& it, auto end) noexcept
648
      {
649
         if constexpr (!check_ws_handled(Opts)) {
650
            if (skip_ws<Opts>(ctx, it, end)) {
651
               return;
652
            }
653
         }
654
         static constexpr sv null_string = "null";
655
         if ((end - it < 4) || not comparitor<null_string>(it)) [[unlikely]] {
656
            ctx.error = error_code::syntax_error;
657
         }
658
         it += 4; // always advance for performance
659
      }
660
   };
661
662
   template <bool_t T>
663
   struct from<JSON, T>
664
   {
665
      template <auto Opts>
666
      static void op(bool_t auto&& value, is_context auto&& ctx, auto&& it, auto end) noexcept
667
      {
668
         if constexpr (check_quoted_num(Opts)) {
669
            if (skip_ws<Opts>(ctx, it, end)) {
670
               return;
671
            }
672
            if (match_invalid_end<'"', Opts>(ctx, it, end)) {
673
               return;
674
            }
675
         }
676
677
         if constexpr (!check_ws_handled(Opts)) {
678
            if (skip_ws<Opts>(ctx, it, end)) {
679
               return;
680
            }
681
         }
682
683
         if constexpr (check_bools_as_numbers(Opts)) {
684
            if (*it == '1') {
685
               value = true;
686
               ++it;
687
            }
688
            else if (*it == '0') {
689
               value = false;
690
               ++it;
691
            }
692
            else {
693
               ctx.error = error_code::syntax_error;
694
               return;
695
            }
696
         }
697
         else {
698
            if (end - it < 4) [[unlikely]] {
699
               ctx.error = error_code::expected_true_or_false;
700
               return;
701
            }
702
703
            uint32_t c;
704
            static constexpr uint32_t u_true = 0b01100101'01110101'01110010'01110100;
705
            static constexpr uint32_t u_fals = 0b01110011'01101100'01100001'01100110;
706
            std::memcpy(&c, it, 4);
707
            if constexpr (std::endian::native == std::endian::big) {
708
               c = std::byteswap(c);
709
            }
710
            it += 4;
711
            if (c == u_true) {
712
               value = true;
713
            }
714
            else if (it == end) [[unlikely]] {
715
               ctx.error = error_code::unexpected_end;
716
               return;
717
            }
718
            else {
719
               if (c == u_fals && (*it == 'e')) [[likely]] {
720
                  value = false;
721
                  ++it;
722
               }
723
               else [[unlikely]] {
724
                  ctx.error = error_code::expected_true_or_false;
725
                  return;
726
               }
727
            }
728
         }
729
730
         if constexpr (check_quoted_num(Opts)) {
731
            if constexpr (not Opts.null_terminated) {
732
               if (it == end) [[unlikely]] {
733
                  ctx.error = error_code::unexpected_end;
734
                  return;
735
               }
736
            }
737
            if (match<'"'>(ctx, it)) {
738
               return;
739
            }
740
            if constexpr (not Opts.null_terminated) {
741
               if (it == end) {
742
                  ctx.error = error_code::end_reached;
743
                  return;
744
               }
745
            }
746
         }
747
         else {
748
            if constexpr (not Opts.null_terminated) {
749
               if (it == end) {
750
                  ctx.error = error_code::end_reached;
751
                  return;
752
               }
753
            }
754
         }
755
      }
756
   };
757
758
   template <num_t T>
759
   struct from<JSON, T>
760
   {
761
      template <auto Opts, class It>
762
      GLZ_ALWAYS_INLINE static void op(auto&& value, is_context auto&& ctx, It&& it, auto end) noexcept
763
11.5k
      {
764
         if constexpr (check_quoted_num(Opts)) {
765
            if (skip_ws<Opts>(ctx, it, end)) {
766
               return;
767
            }
768
            if (match_invalid_end<'"', Opts>(ctx, it, end)) {
769
               return;
770
            }
771
         }
772
773
         if constexpr (!check_ws_handled(Opts)) {
774
            if (skip_ws<Opts>(ctx, it, end)) {
775
               return;
776
            }
777
         }
778
779
11.5k
         using V = std::decay_t<decltype(value)>;
780
11.5k
         if constexpr (int_t<V>) {
781
11.5k
            static_assert(sizeof(*it) == sizeof(char));
782
783
11.5k
            if constexpr (Opts.null_terminated) {
784
6.02k
               if (not glz::atoi(value, it)) [[unlikely]] {
785
116
                  ctx.error = error_code::parse_number_failure;
786
116
                  return;
787
116
               }
788
            }
789
5.56k
            else {
790
5.56k
               if (not glz::atoi(value, it, end)) [[unlikely]] {
791
90
                  ctx.error = error_code::parse_number_failure;
792
90
                  return;
793
90
               }
794
5.56k
            }
795
         }
796
         else {
797
// float128_t requires std::from_chars for floating-point, unavailable on older Apple platforms (iOS < 16.3)
798
#if !defined(_LIBCPP_VERSION) || defined(_LIBCPP_AVAILABILITY_HAS_TO_CHARS_FLOATING_POINT)
799
            if constexpr (is_float128<V>) {
800
               auto [ptr, ec] = std::from_chars(it, end, value);
801
               if (ec != std::errc()) {
802
                  ctx.error = error_code::parse_number_failure;
803
                  return;
804
               }
805
               it = ptr;
806
            }
807
            else
808
#endif
809
            {
810
               if constexpr (std::is_volatile_v<std::remove_reference_t<decltype(value)>>) {
811
                  // Hardware may interact with value changes, so we parse into a temporary and assign in one
812
                  // place
813
                  V temp;
814
                  if constexpr (is_size_optimized(Opts)) {
815
                     auto [ptr, ec] = simple_float::from_chars<Opts.null_terminated>(it, end, temp);
816
                     if (ec != std::errc{}) [[unlikely]] {
817
                        ctx.error = error_code::parse_number_failure;
818
                        return;
819
                     }
820
                     it = ptr;
821
                  }
822
                  else {
823
                     auto [ptr, ec] = glz::from_chars<Opts.null_terminated>(it, end, temp);
824
                     if (ec != std::errc()) [[unlikely]] {
825
                        ctx.error = error_code::parse_number_failure;
826
                        return;
827
                     }
828
                     it = ptr;
829
                  }
830
                  value = temp;
831
               }
832
               else {
833
                  if constexpr (is_size_optimized(Opts)) {
834
                     auto [ptr, ec] = simple_float::from_chars<Opts.null_terminated>(it, end, value);
835
                     if (ec != std::errc{}) [[unlikely]] {
836
                        ctx.error = error_code::parse_number_failure;
837
                        return;
838
                     }
839
                     it = ptr;
840
                  }
841
                  else {
842
                     auto [ptr, ec] = glz::from_chars<Opts.null_terminated>(it, end, value);
843
                     if (ec != std::errc()) [[unlikely]] {
844
                        ctx.error = error_code::parse_number_failure;
845
                        return;
846
                     }
847
                     it = ptr;
848
                  }
849
               }
850
            }
851
         }
852
853
         if constexpr (check_quoted_num(Opts)) {
854
            if constexpr (not Opts.null_terminated) {
855
               if (it == end) [[unlikely]] {
856
                  ctx.error = error_code::unexpected_end;
857
                  return;
858
               }
859
            }
860
            if (match<'"'>(ctx, it)) {
861
               return;
862
            }
863
            if constexpr (not Opts.null_terminated) {
864
               if (it == end) {
865
                  ctx.error = error_code::end_reached;
866
                  return;
867
               }
868
            }
869
         }
870
11.5k
         else {
871
11.5k
            if constexpr (not Opts.null_terminated) {
872
5.56k
               if (it == end) {
873
73
                  ctx.error = error_code::end_reached;
874
73
                  return;
875
73
               }
876
5.56k
            }
877
11.5k
         }
878
11.5k
      }
_ZN3glz4fromILj10EtE2opITnDaXtlNS_4optsELj10ELb1ELb0ELb1ELb1ELb0ELb0ELb0ELb0ELj4EEERPKcRtTkNS_10is_contextERNS_7contextES5_EEvOT1_OT2_OT0_T3_
Line
Count
Source
763
6.02k
      {
764
         if constexpr (check_quoted_num(Opts)) {
765
            if (skip_ws<Opts>(ctx, it, end)) {
766
               return;
767
            }
768
            if (match_invalid_end<'"', Opts>(ctx, it, end)) {
769
               return;
770
            }
771
         }
772
773
         if constexpr (!check_ws_handled(Opts)) {
774
            if (skip_ws<Opts>(ctx, it, end)) {
775
               return;
776
            }
777
         }
778
779
6.02k
         using V = std::decay_t<decltype(value)>;
780
6.02k
         if constexpr (int_t<V>) {
781
6.02k
            static_assert(sizeof(*it) == sizeof(char));
782
783
6.02k
            if constexpr (Opts.null_terminated) {
784
6.02k
               if (not glz::atoi(value, it)) [[unlikely]] {
785
116
                  ctx.error = error_code::parse_number_failure;
786
116
                  return;
787
116
               }
788
            }
789
            else {
790
               if (not glz::atoi(value, it, end)) [[unlikely]] {
791
                  ctx.error = error_code::parse_number_failure;
792
                  return;
793
               }
794
            }
795
         }
796
         else {
797
// float128_t requires std::from_chars for floating-point, unavailable on older Apple platforms (iOS < 16.3)
798
#if !defined(_LIBCPP_VERSION) || defined(_LIBCPP_AVAILABILITY_HAS_TO_CHARS_FLOATING_POINT)
799
            if constexpr (is_float128<V>) {
800
               auto [ptr, ec] = std::from_chars(it, end, value);
801
               if (ec != std::errc()) {
802
                  ctx.error = error_code::parse_number_failure;
803
                  return;
804
               }
805
               it = ptr;
806
            }
807
            else
808
#endif
809
            {
810
               if constexpr (std::is_volatile_v<std::remove_reference_t<decltype(value)>>) {
811
                  // Hardware may interact with value changes, so we parse into a temporary and assign in one
812
                  // place
813
                  V temp;
814
                  if constexpr (is_size_optimized(Opts)) {
815
                     auto [ptr, ec] = simple_float::from_chars<Opts.null_terminated>(it, end, temp);
816
                     if (ec != std::errc{}) [[unlikely]] {
817
                        ctx.error = error_code::parse_number_failure;
818
                        return;
819
                     }
820
                     it = ptr;
821
                  }
822
                  else {
823
                     auto [ptr, ec] = glz::from_chars<Opts.null_terminated>(it, end, temp);
824
                     if (ec != std::errc()) [[unlikely]] {
825
                        ctx.error = error_code::parse_number_failure;
826
                        return;
827
                     }
828
                     it = ptr;
829
                  }
830
                  value = temp;
831
               }
832
               else {
833
                  if constexpr (is_size_optimized(Opts)) {
834
                     auto [ptr, ec] = simple_float::from_chars<Opts.null_terminated>(it, end, value);
835
                     if (ec != std::errc{}) [[unlikely]] {
836
                        ctx.error = error_code::parse_number_failure;
837
                        return;
838
                     }
839
                     it = ptr;
840
                  }
841
                  else {
842
                     auto [ptr, ec] = glz::from_chars<Opts.null_terminated>(it, end, value);
843
                     if (ec != std::errc()) [[unlikely]] {
844
                        ctx.error = error_code::parse_number_failure;
845
                        return;
846
                     }
847
                     it = ptr;
848
                  }
849
               }
850
            }
851
         }
852
853
         if constexpr (check_quoted_num(Opts)) {
854
            if constexpr (not Opts.null_terminated) {
855
               if (it == end) [[unlikely]] {
856
                  ctx.error = error_code::unexpected_end;
857
                  return;
858
               }
859
            }
860
            if (match<'"'>(ctx, it)) {
861
               return;
862
            }
863
            if constexpr (not Opts.null_terminated) {
864
               if (it == end) {
865
                  ctx.error = error_code::end_reached;
866
                  return;
867
               }
868
            }
869
         }
870
6.02k
         else {
871
            if constexpr (not Opts.null_terminated) {
872
               if (it == end) {
873
                  ctx.error = error_code::end_reached;
874
                  return;
875
               }
876
            }
877
6.02k
         }
878
6.02k
      }
_ZN3glz4fromILj10EtE2opITnDaXtlNS_4optsELj10ELb0ELb0ELb1ELb1ELb0ELb0ELb0ELb0ELj4EEERPKcRtTkNS_10is_contextERNS_7contextES5_EEvOT1_OT2_OT0_T3_
Line
Count
Source
763
5.56k
      {
764
         if constexpr (check_quoted_num(Opts)) {
765
            if (skip_ws<Opts>(ctx, it, end)) {
766
               return;
767
            }
768
            if (match_invalid_end<'"', Opts>(ctx, it, end)) {
769
               return;
770
            }
771
         }
772
773
         if constexpr (!check_ws_handled(Opts)) {
774
            if (skip_ws<Opts>(ctx, it, end)) {
775
               return;
776
            }
777
         }
778
779
5.56k
         using V = std::decay_t<decltype(value)>;
780
5.56k
         if constexpr (int_t<V>) {
781
5.56k
            static_assert(sizeof(*it) == sizeof(char));
782
783
            if constexpr (Opts.null_terminated) {
784
               if (not glz::atoi(value, it)) [[unlikely]] {
785
                  ctx.error = error_code::parse_number_failure;
786
                  return;
787
               }
788
            }
789
5.56k
            else {
790
5.56k
               if (not glz::atoi(value, it, end)) [[unlikely]] {
791
90
                  ctx.error = error_code::parse_number_failure;
792
90
                  return;
793
90
               }
794
5.56k
            }
795
         }
796
         else {
797
// float128_t requires std::from_chars for floating-point, unavailable on older Apple platforms (iOS < 16.3)
798
#if !defined(_LIBCPP_VERSION) || defined(_LIBCPP_AVAILABILITY_HAS_TO_CHARS_FLOATING_POINT)
799
            if constexpr (is_float128<V>) {
800
               auto [ptr, ec] = std::from_chars(it, end, value);
801
               if (ec != std::errc()) {
802
                  ctx.error = error_code::parse_number_failure;
803
                  return;
804
               }
805
               it = ptr;
806
            }
807
            else
808
#endif
809
            {
810
               if constexpr (std::is_volatile_v<std::remove_reference_t<decltype(value)>>) {
811
                  // Hardware may interact with value changes, so we parse into a temporary and assign in one
812
                  // place
813
                  V temp;
814
                  if constexpr (is_size_optimized(Opts)) {
815
                     auto [ptr, ec] = simple_float::from_chars<Opts.null_terminated>(it, end, temp);
816
                     if (ec != std::errc{}) [[unlikely]] {
817
                        ctx.error = error_code::parse_number_failure;
818
                        return;
819
                     }
820
                     it = ptr;
821
                  }
822
                  else {
823
                     auto [ptr, ec] = glz::from_chars<Opts.null_terminated>(it, end, temp);
824
                     if (ec != std::errc()) [[unlikely]] {
825
                        ctx.error = error_code::parse_number_failure;
826
                        return;
827
                     }
828
                     it = ptr;
829
                  }
830
                  value = temp;
831
               }
832
               else {
833
                  if constexpr (is_size_optimized(Opts)) {
834
                     auto [ptr, ec] = simple_float::from_chars<Opts.null_terminated>(it, end, value);
835
                     if (ec != std::errc{}) [[unlikely]] {
836
                        ctx.error = error_code::parse_number_failure;
837
                        return;
838
                     }
839
                     it = ptr;
840
                  }
841
                  else {
842
                     auto [ptr, ec] = glz::from_chars<Opts.null_terminated>(it, end, value);
843
                     if (ec != std::errc()) [[unlikely]] {
844
                        ctx.error = error_code::parse_number_failure;
845
                        return;
846
                     }
847
                     it = ptr;
848
                  }
849
               }
850
            }
851
         }
852
853
         if constexpr (check_quoted_num(Opts)) {
854
            if constexpr (not Opts.null_terminated) {
855
               if (it == end) [[unlikely]] {
856
                  ctx.error = error_code::unexpected_end;
857
                  return;
858
               }
859
            }
860
            if (match<'"'>(ctx, it)) {
861
               return;
862
            }
863
            if constexpr (not Opts.null_terminated) {
864
               if (it == end) {
865
                  ctx.error = error_code::end_reached;
866
                  return;
867
               }
868
            }
869
         }
870
5.56k
         else {
871
5.56k
            if constexpr (not Opts.null_terminated) {
872
5.56k
               if (it == end) {
873
73
                  ctx.error = error_code::end_reached;
874
73
                  return;
875
73
               }
876
5.56k
            }
877
5.56k
         }
878
5.56k
      }
879
   };
880
881
   template <class T>
882
      requires(string_t<T> && !u8str_t<T>)
883
   struct from<JSON, T>
884
   {
885
      template <auto Opts, class It, class End>
886
      static void op(auto& value, is_context auto&& ctx, It&& it, End end)
887
8.87k
      {
888
         if constexpr (check_string_as_number(Opts)) {
889
            auto start = it;
890
            skip_number<Opts>(ctx, it, end);
891
            if (bool(ctx.error)) [[unlikely]] {
892
               return;
893
            }
894
            if (start == it) [[unlikely]] {
895
               ctx.error = error_code::parse_number_failure;
896
               return;
897
            }
898
            value.append(start, size_t(it - start));
899
         }
900
8.87k
         else {
901
8.87k
            if constexpr (!check_opening_handled(Opts)) {
902
               if constexpr (!check_ws_handled(Opts)) {
903
                  if (skip_ws<Opts>(ctx, it, end)) {
904
                     return;
905
                  }
906
               }
907
908
8.87k
               if (match_invalid_end<'"', Opts>(ctx, it, end)) {
909
63
                  return;
910
63
               }
911
8.87k
            }
912
913
8.87k
            if constexpr (not check_raw_string(Opts)) {
914
8.87k
               static constexpr auto string_padding_bytes = 8;
915
916
               // How much buffer has to remain for an eight byte load at the cursor to stay inside
917
               // it. One on a padded input, where the last chunk is free to straddle the end of the
918
               // document, so the byte tails below are unreachable; eight otherwise, which costs the
919
               // final bytes of the buffer their chunked path and nothing else.
920
8.87k
               const std::ptrdiff_t scan_min = chunk_min<8>(ctx.padded_input);
921
922
8.87k
               if (size_t(end - it) >= 8) {
923
                  // The bound as a pointer, so the loops below spend a compare per chunk rather
924
                  // than a subtract and a compare. Well defined here and nowhere else in this
925
                  // reader: the branch has just established that at least eight bytes remain, and
926
                  // `scan_min` is at most eight.
927
8.51k
                  const auto* const chunk_limit = end - scan_min;
928
929
8.51k
                  auto start = it;
930
8.51k
                  uint64_t ascii_acc{};
931
2.59M
                  while (it <= chunk_limit) {
932
2.59M
                     uint64_t chunk;
933
2.59M
                     std::memcpy(&chunk, it, 8);
934
                     if constexpr (std::endian::native == std::endian::big) {
935
                        chunk = std::byteswap(chunk);
936
                     }
937
2.59M
                     ascii_acc |= chunk;
938
2.59M
                     const uint64_t test_chars = has_quote(chunk);
939
2.59M
                     if (test_chars) {
940
8.73k
                        it += (countr_zero(test_chars) >> 3);
941
942
8.73k
                        auto* prev = it - 1;
943
11.3k
                        while (*prev == '\\') {
944
2.60k
                           --prev;
945
2.60k
                        }
946
8.73k
                        if (size_t(it - prev) % 2) {
947
7.98k
                           goto continue_decode;
948
7.98k
                        }
949
757
                        ++it; // skip the escaped quote
950
757
                     }
951
2.58M
                     else {
952
2.58M
                        it += 8;
953
2.58M
                     }
954
2.59M
                  }
955
956
1.64k
                  while (it[-1] == '\\') [[unlikely]] {
957
                     // if we ended on an escape character then we need to rewind
958
                     // because we lost our context
959
1.11k
                     --it;
960
1.11k
                  }
961
962
                  // The byte-wise tail keeps feeding the accumulator: it has to cover every byte of
963
                  // the string, or a non-ASCII byte reached here would be waved through unvalidated.
964
2.82k
                  for (; it < end; ++it) {
965
2.77k
                     ascii_acc |= uint8_t(*it);
966
2.77k
                     if (*it == '"') {
967
520
                        auto* prev = it - 1;
968
1.08k
                        while (*prev == '\\') {
969
560
                           --prev;
970
560
                        }
971
520
                        if (size_t(it - prev) % 2) {
972
484
                           goto continue_decode;
973
484
                        }
974
520
                     }
975
2.77k
                  }
976
977
50
                  ctx.error = error_code::unexpected_end;
978
50
                  return;
979
980
8.46k
               continue_decode:
981
982
8.46k
                  if (validate_utf8_span<Opts>(ctx, start, it, ascii_acc)) [[unlikely]] {
983
103
                     return;
984
103
                  }
985
986
8.36k
                  auto n = size_t(it - start);
987
988
                  // The padding lets the chunked copy below run up to the closing quote. Skip it when it
989
                  // would be the only reason to allocate, as for a string that fits the small string
990
                  // buffer but not with eight bytes to spare.
991
8.36k
                  bool pad = true;
992
8.36k
                  if constexpr (requires { value.capacity(); }) {
993
8.36k
                     const auto capacity = size_t(value.capacity());
994
8.36k
                     pad = capacity >= n + string_padding_bytes || capacity < n;
995
8.36k
                  }
996
8.36k
                  resize_unfilled(value, pad ? n + string_padding_bytes : n);
997
998
8.36k
                  auto* p = value.data();
999
1000
                  // An error below returns with the tail of `value` still unwritten. Leave the size at
1001
                  // what was actually written rather than at the padded length, which resize_unfilled
1002
                  // did not fill.
1003
8.36k
                  auto* const written_begin = p;
1004
1005
                  // Without the padding, copy a chunk only while eight bytes remain before the quote.
1006
                  // Unescaping never puts the output ahead of the input, so no store lands past the end.
1007
8.36k
                  const auto* const copy_end = pad ? it : it - (std::min)(n, size_t(7));
1008
1009
                  // Copy eight bytes at a time for as long as eight bytes are there to read, then
1010
                  // finish the span below. The chunked form reads past the character it is looking
1011
                  // at, so on an unpadded buffer it has to stop short of the end; splitting it this
1012
                  // way keeps it for the body of every string rather than giving it up whenever the
1013
                  // string happens to finish near the end of the buffer -- which, for a document
1014
                  // that is itself one string, is every string.
1015
976k
                  while (start < copy_end && start <= chunk_limit) {
1016
969k
                     std::memcpy(p, start, 8);
1017
969k
                     uint64_t swar;
1018
969k
                     std::memcpy(&swar, p, 8);
1019
                     if constexpr (std::endian::native == std::endian::big) {
1020
                        swar = std::byteswap(swar);
1021
                     }
1022
1023
969k
                     constexpr uint64_t lo7_mask = repeat_byte8(0b01111111);
1024
969k
                     const uint64_t lo7 = swar & lo7_mask;
1025
969k
                     const uint64_t backslash = (lo7 ^ repeat_byte8('\\')) + lo7_mask;
1026
969k
                     const uint64_t less_32 = (swar & repeat_byte8(0b01100000)) + lo7_mask;
1027
969k
                     uint64_t next = ~((backslash & less_32) | swar);
1028
1029
969k
                     next &= repeat_byte8(0b10000000);
1030
969k
                     if (next == 0) {
1031
935k
                        start += 8;
1032
935k
                        p += 8;
1033
935k
                        continue;
1034
935k
                     }
1035
1036
34.3k
                     next = countr_zero(next) >> 3;
1037
34.3k
                     start += next;
1038
34.3k
                     if (start >= it) {
1039
716
                        break;
1040
716
                     }
1041
1042
33.6k
                     if ((*start & 0b11100000) == 0) [[unlikely]] {
1043
125
                        ctx.error = error_code::syntax_error;
1044
125
                        value.resize(size_t(p - written_begin));
1045
125
                        return;
1046
125
                     }
1047
33.5k
                     ++start; // skip the escape
1048
33.5k
                     if (*start == 'u') {
1049
11.1k
                        ++start;
1050
11.1k
                        p += next;
1051
11.1k
                        const auto mark = start;
1052
11.1k
                        const auto decoded = handle_unicode_code_point(start, p, end);
1053
11.1k
                        if (decoded.written == 0) [[unlikely]] {
1054
131
                           ctx.error = decoded.truncated ? error_code::unexpected_end
1055
131
                                                         : error_code::unicode_escape_conversion_failure;
1056
131
                           value.resize(size_t(p - written_begin));
1057
131
                           return;
1058
131
                        }
1059
10.9k
                        n += decoded.written;
1060
                        // escape + u + unicode code points
1061
10.9k
                        n -= 2 + uint32_t(start - mark);
1062
10.9k
                     }
1063
22.3k
                     else {
1064
22.3k
                        p += next;
1065
22.3k
                        *p = char_unescape_table[uint8_t(*start)];
1066
22.3k
                        if (*p == 0) [[unlikely]] {
1067
30
                           ctx.error = error_code::invalid_escape;
1068
30
                           value.resize(size_t(p - written_begin));
1069
30
                           return;
1070
30
                        }
1071
22.3k
                        ++p;
1072
22.3k
                        ++start;
1073
22.3k
                        --n;
1074
22.3k
                     }
1075
33.5k
                  }
1076
1077
                  // Whatever the chunked copy could not reach. `p` and `start` stay in step across
1078
                  // that loop, so this picks up exactly where it stopped.
1079
14.0k
                  while (start < it) {
1080
6.12k
                     if ((*start & 0b11100000) == 0) [[unlikely]] {
1081
23
                        ctx.error = error_code::syntax_error;
1082
23
                        value.resize(size_t(p - written_begin));
1083
23
                        return;
1084
23
                     }
1085
6.09k
                     if (*start == '\\') {
1086
2.65k
                        ++start; // skip the escape
1087
2.65k
                        if (*start == 'u') {
1088
1.51k
                           ++start;
1089
1.51k
                           const auto mark = start;
1090
1.51k
                           const auto decoded = handle_unicode_code_point(start, p, end);
1091
1.51k
                           if (decoded.written == 0) [[unlikely]] {
1092
90
                              ctx.error = decoded.truncated ? error_code::unexpected_end
1093
90
                                                            : error_code::unicode_escape_conversion_failure;
1094
90
                              value.resize(size_t(p - written_begin));
1095
90
                              return;
1096
90
                           }
1097
1.42k
                           n += decoded.written;
1098
1.42k
                           n -= 2 + uint32_t(start - mark);
1099
1.42k
                        }
1100
1.14k
                        else {
1101
1.14k
                           *p = char_unescape_table[uint8_t(*start)];
1102
1.14k
                           if (*p == 0) [[unlikely]] {
1103
9
                              ctx.error = error_code::invalid_escape;
1104
9
                              value.resize(size_t(p - written_begin));
1105
9
                              return;
1106
9
                           }
1107
1.13k
                           ++p;
1108
1.13k
                           ++start;
1109
1.13k
                           --n;
1110
1.13k
                        }
1111
2.65k
                     }
1112
3.44k
                     else {
1113
3.44k
                        *p = *start;
1114
3.44k
                        ++p;
1115
3.44k
                        ++start;
1116
3.44k
                     }
1117
6.09k
                  }
1118
1119
7.95k
                  value.resize(n);
1120
7.95k
                  ++it;
1121
7.95k
               }
1122
356
               else {
1123
                  // For short strings
1124
1125
356
                  std::array<char, 8> buffer{};
1126
1127
356
                  auto* p = buffer.data();
1128
356
                  const auto* const utf8_start = it;
1129
1130
804
                  while (it < end) [[likely]] {
1131
688
                     *p = *it;
1132
688
                     if (*it == '"') {
1133
189
                        if (validate_utf8_span<Opts>(ctx, utf8_start, it)) [[unlikely]] {
1134
60
                           return;
1135
60
                        }
1136
129
                        const size_t n = size_t(p - buffer.data());
1137
129
#if __has_cpp_attribute(assume) >= 202207L
1138
129
                        [[assume(n <= sizeof(buffer))]];
1139
129
#endif
1140
129
                        value.assign(buffer.data(), n);
1141
129
                        ++it;
1142
129
                        if constexpr (not Opts.null_terminated) {
1143
80
                           if (it == end) {
1144
49
                              ctx.error = error_code::end_reached;
1145
49
                              return;
1146
49
                           }
1147
80
                        }
1148
31
                        return;
1149
189
                     }
1150
499
                     else if (*it == '\\') {
1151
                        // Bounded whatever `null_terminated` says: a document cut off inside an
1152
                        // escape has run out of input, and saying so beats blaming the terminator
1153
                        // the cut exposed -- which is what reading on would find, and would report
1154
                        // as a malformed escape.
1155
120
                        ++it; // skip the escape
1156
120
                        if (it == end) [[unlikely]] {
1157
6
                           ctx.error = error_code::unexpected_end;
1158
6
                           return;
1159
6
                        }
1160
114
                        if (*it == 'u') {
1161
94
                           ++it;
1162
94
                           if (it == end) [[unlikely]] {
1163
2
                              ctx.error = error_code::unexpected_end;
1164
2
                              return;
1165
2
                           }
1166
92
                           const auto decoded = handle_unicode_code_point(it, p, end);
1167
92
                           if (decoded.written == 0) [[unlikely]] {
1168
24
                              ctx.error = decoded.truncated ? error_code::unexpected_end
1169
24
                                                            : error_code::unicode_escape_conversion_failure;
1170
24
                              return;
1171
24
                           }
1172
92
                        }
1173
20
                        else {
1174
20
                           *p = char_unescape_table[uint8_t(*it)];
1175
20
                           if (*p == 0) [[unlikely]] {
1176
6
                              ctx.error = error_code::invalid_escape;
1177
6
                              return;
1178
6
                           }
1179
14
                           ++p;
1180
14
                           ++it;
1181
14
                        }
1182
114
                     }
1183
379
                     else if ((*it & 0b11100000) == 0) [[unlikely]] {
1184
13
                        ctx.error = error_code::syntax_error;
1185
13
                        return;
1186
13
                     }
1187
366
                     else {
1188
366
                        ++it;
1189
366
                        ++p;
1190
366
                     }
1191
688
                  }
1192
1193
116
                  ctx.error = error_code::unexpected_end;
1194
116
               }
1195
            }
1196
            else {
1197
               // raw_string
1198
               auto start = it;
1199
               skip_string_view(ctx, it, end);
1200
               if (bool(ctx.error)) [[unlikely]]
1201
                  return;
1202
1203
               if (validate_utf8_span<Opts>(ctx, start, it)) [[unlikely]] {
1204
                  return;
1205
               }
1206
1207
               value.assign(start, size_t(it - start));
1208
               ++it;
1209
            }
1210
8.87k
         }
1211
8.87k
      }
_ZN3glz4fromILj10ENSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEE2opITnDaXtlNS_4optsELj10ELb1ELb0ELb1ELb1ELb0ELb0ELb0ELb0ELj4EEERPKcSC_S7_TkNS_10is_contextERNS_7contextEEEvRT2_OT3_OT0_T1_
Line
Count
Source
887
4.57k
      {
888
         if constexpr (check_string_as_number(Opts)) {
889
            auto start = it;
890
            skip_number<Opts>(ctx, it, end);
891
            if (bool(ctx.error)) [[unlikely]] {
892
               return;
893
            }
894
            if (start == it) [[unlikely]] {
895
               ctx.error = error_code::parse_number_failure;
896
               return;
897
            }
898
            value.append(start, size_t(it - start));
899
         }
900
4.57k
         else {
901
4.57k
            if constexpr (!check_opening_handled(Opts)) {
902
               if constexpr (!check_ws_handled(Opts)) {
903
                  if (skip_ws<Opts>(ctx, it, end)) {
904
                     return;
905
                  }
906
               }
907
908
4.57k
               if (match_invalid_end<'"', Opts>(ctx, it, end)) {
909
44
                  return;
910
44
               }
911
4.57k
            }
912
913
4.57k
            if constexpr (not check_raw_string(Opts)) {
914
4.57k
               static constexpr auto string_padding_bytes = 8;
915
916
               // How much buffer has to remain for an eight byte load at the cursor to stay inside
917
               // it. One on a padded input, where the last chunk is free to straddle the end of the
918
               // document, so the byte tails below are unreachable; eight otherwise, which costs the
919
               // final bytes of the buffer their chunked path and nothing else.
920
4.57k
               const std::ptrdiff_t scan_min = chunk_min<8>(ctx.padded_input);
921
922
4.57k
               if (size_t(end - it) >= 8) {
923
                  // The bound as a pointer, so the loops below spend a compare per chunk rather
924
                  // than a subtract and a compare. Well defined here and nowhere else in this
925
                  // reader: the branch has just established that at least eight bytes remain, and
926
                  // `scan_min` is at most eight.
927
4.39k
                  const auto* const chunk_limit = end - scan_min;
928
929
4.39k
                  auto start = it;
930
4.39k
                  uint64_t ascii_acc{};
931
1.24M
                  while (it <= chunk_limit) {
932
1.24M
                     uint64_t chunk;
933
1.24M
                     std::memcpy(&chunk, it, 8);
934
                     if constexpr (std::endian::native == std::endian::big) {
935
                        chunk = std::byteswap(chunk);
936
                     }
937
1.24M
                     ascii_acc |= chunk;
938
1.24M
                     const uint64_t test_chars = has_quote(chunk);
939
1.24M
                     if (test_chars) {
940
4.60k
                        it += (countr_zero(test_chars) >> 3);
941
942
4.60k
                        auto* prev = it - 1;
943
6.69k
                        while (*prev == '\\') {
944
2.08k
                           --prev;
945
2.08k
                        }
946
4.60k
                        if (size_t(it - prev) % 2) {
947
4.12k
                           goto continue_decode;
948
4.12k
                        }
949
488
                        ++it; // skip the escaped quote
950
488
                     }
951
1.23M
                     else {
952
1.23M
                        it += 8;
953
1.23M
                     }
954
1.24M
                  }
955
956
808
                  while (it[-1] == '\\') [[unlikely]] {
957
                     // if we ended on an escape character then we need to rewind
958
                     // because we lost our context
959
537
                     --it;
960
537
                  }
961
962
                  // The byte-wise tail keeps feeding the accumulator: it has to cover every byte of
963
                  // the string, or a non-ASCII byte reached here would be waved through unvalidated.
964
1.45k
                  for (; it < end; ++it) {
965
1.43k
                     ascii_acc |= uint8_t(*it);
966
1.43k
                     if (*it == '"') {
967
263
                        auto* prev = it - 1;
968
539
                        while (*prev == '\\') {
969
276
                           --prev;
970
276
                        }
971
263
                        if (size_t(it - prev) % 2) {
972
245
                           goto continue_decode;
973
245
                        }
974
263
                     }
975
1.43k
                  }
976
977
26
                  ctx.error = error_code::unexpected_end;
978
26
                  return;
979
980
4.36k
               continue_decode:
981
982
4.36k
                  if (validate_utf8_span<Opts>(ctx, start, it, ascii_acc)) [[unlikely]] {
983
56
                     return;
984
56
                  }
985
986
4.30k
                  auto n = size_t(it - start);
987
988
                  // The padding lets the chunked copy below run up to the closing quote. Skip it when it
989
                  // would be the only reason to allocate, as for a string that fits the small string
990
                  // buffer but not with eight bytes to spare.
991
4.30k
                  bool pad = true;
992
4.30k
                  if constexpr (requires { value.capacity(); }) {
993
4.30k
                     const auto capacity = size_t(value.capacity());
994
4.30k
                     pad = capacity >= n + string_padding_bytes || capacity < n;
995
4.30k
                  }
996
4.30k
                  resize_unfilled(value, pad ? n + string_padding_bytes : n);
997
998
4.30k
                  auto* p = value.data();
999
1000
                  // An error below returns with the tail of `value` still unwritten. Leave the size at
1001
                  // what was actually written rather than at the padded length, which resize_unfilled
1002
                  // did not fill.
1003
4.30k
                  auto* const written_begin = p;
1004
1005
                  // Without the padding, copy a chunk only while eight bytes remain before the quote.
1006
                  // Unescaping never puts the output ahead of the input, so no store lands past the end.
1007
4.30k
                  const auto* const copy_end = pad ? it : it - (std::min)(n, size_t(7));
1008
1009
                  // Copy eight bytes at a time for as long as eight bytes are there to read, then
1010
                  // finish the span below. The chunked form reads past the character it is looking
1011
                  // at, so on an unpadded buffer it has to stop short of the end; splitting it this
1012
                  // way keeps it for the body of every string rather than giving it up whenever the
1013
                  // string happens to finish near the end of the buffer -- which, for a document
1014
                  // that is itself one string, is every string.
1015
380k
                  while (start < copy_end && start <= chunk_limit) {
1016
376k
                     std::memcpy(p, start, 8);
1017
376k
                     uint64_t swar;
1018
376k
                     std::memcpy(&swar, p, 8);
1019
                     if constexpr (std::endian::native == std::endian::big) {
1020
                        swar = std::byteswap(swar);
1021
                     }
1022
1023
376k
                     constexpr uint64_t lo7_mask = repeat_byte8(0b01111111);
1024
376k
                     const uint64_t lo7 = swar & lo7_mask;
1025
376k
                     const uint64_t backslash = (lo7 ^ repeat_byte8('\\')) + lo7_mask;
1026
376k
                     const uint64_t less_32 = (swar & repeat_byte8(0b01100000)) + lo7_mask;
1027
376k
                     uint64_t next = ~((backslash & less_32) | swar);
1028
1029
376k
                     next &= repeat_byte8(0b10000000);
1030
376k
                     if (next == 0) {
1031
368k
                        start += 8;
1032
368k
                        p += 8;
1033
368k
                        continue;
1034
368k
                     }
1035
1036
7.63k
                     next = countr_zero(next) >> 3;
1037
7.63k
                     start += next;
1038
7.63k
                     if (start >= it) {
1039
439
                        break;
1040
439
                     }
1041
1042
7.19k
                     if ((*start & 0b11100000) == 0) [[unlikely]] {
1043
63
                        ctx.error = error_code::syntax_error;
1044
63
                        value.resize(size_t(p - written_begin));
1045
63
                        return;
1046
63
                     }
1047
7.13k
                     ++start; // skip the escape
1048
7.13k
                     if (*start == 'u') {
1049
3.63k
                        ++start;
1050
3.63k
                        p += next;
1051
3.63k
                        const auto mark = start;
1052
3.63k
                        const auto decoded = handle_unicode_code_point(start, p, end);
1053
3.63k
                        if (decoded.written == 0) [[unlikely]] {
1054
57
                           ctx.error = decoded.truncated ? error_code::unexpected_end
1055
57
                                                         : error_code::unicode_escape_conversion_failure;
1056
57
                           value.resize(size_t(p - written_begin));
1057
57
                           return;
1058
57
                        }
1059
3.58k
                        n += decoded.written;
1060
                        // escape + u + unicode code points
1061
3.58k
                        n -= 2 + uint32_t(start - mark);
1062
3.58k
                     }
1063
3.49k
                     else {
1064
3.49k
                        p += next;
1065
3.49k
                        *p = char_unescape_table[uint8_t(*start)];
1066
3.49k
                        if (*p == 0) [[unlikely]] {
1067
16
                           ctx.error = error_code::invalid_escape;
1068
16
                           value.resize(size_t(p - written_begin));
1069
16
                           return;
1070
16
                        }
1071
3.47k
                        ++p;
1072
3.47k
                        ++start;
1073
3.47k
                        --n;
1074
3.47k
                     }
1075
7.13k
                  }
1076
1077
                  // Whatever the chunked copy could not reach. `p` and `start` stay in step across
1078
                  // that loop, so this picks up exactly where it stopped.
1079
7.29k
                  while (start < it) {
1080
3.19k
                     if ((*start & 0b11100000) == 0) [[unlikely]] {
1081
11
                        ctx.error = error_code::syntax_error;
1082
11
                        value.resize(size_t(p - written_begin));
1083
11
                        return;
1084
11
                     }
1085
3.18k
                     if (*start == '\\') {
1086
1.29k
                        ++start; // skip the escape
1087
1.29k
                        if (*start == 'u') {
1088
724
                           ++start;
1089
724
                           const auto mark = start;
1090
724
                           const auto decoded = handle_unicode_code_point(start, p, end);
1091
724
                           if (decoded.written == 0) [[unlikely]] {
1092
54
                              ctx.error = decoded.truncated ? error_code::unexpected_end
1093
54
                                                            : error_code::unicode_escape_conversion_failure;
1094
54
                              value.resize(size_t(p - written_begin));
1095
54
                              return;
1096
54
                           }
1097
670
                           n += decoded.written;
1098
670
                           n -= 2 + uint32_t(start - mark);
1099
670
                        }
1100
567
                        else {
1101
567
                           *p = char_unescape_table[uint8_t(*start)];
1102
567
                           if (*p == 0) [[unlikely]] {
1103
6
                              ctx.error = error_code::invalid_escape;
1104
6
                              value.resize(size_t(p - written_begin));
1105
6
                              return;
1106
6
                           }
1107
561
                           ++p;
1108
561
                           ++start;
1109
561
                           --n;
1110
561
                        }
1111
1.29k
                     }
1112
1.89k
                     else {
1113
1.89k
                        *p = *start;
1114
1.89k
                        ++p;
1115
1.89k
                        ++start;
1116
1.89k
                     }
1117
3.18k
                  }
1118
1119
4.10k
                  value.resize(n);
1120
4.10k
                  ++it;
1121
4.10k
               }
1122
180
               else {
1123
                  // For short strings
1124
1125
180
                  std::array<char, 8> buffer{};
1126
1127
180
                  auto* p = buffer.data();
1128
180
                  const auto* const utf8_start = it;
1129
1130
380
                  while (it < end) [[likely]] {
1131
309
                     *p = *it;
1132
309
                     if (*it == '"') {
1133
79
                        if (validate_utf8_span<Opts>(ctx, utf8_start, it)) [[unlikely]] {
1134
30
                           return;
1135
30
                        }
1136
49
                        const size_t n = size_t(p - buffer.data());
1137
49
#if __has_cpp_attribute(assume) >= 202207L
1138
49
                        [[assume(n <= sizeof(buffer))]];
1139
49
#endif
1140
49
                        value.assign(buffer.data(), n);
1141
49
                        ++it;
1142
                        if constexpr (not Opts.null_terminated) {
1143
                           if (it == end) {
1144
                              ctx.error = error_code::end_reached;
1145
                              return;
1146
                           }
1147
                        }
1148
49
                        return;
1149
79
                     }
1150
230
                     else if (*it == '\\') {
1151
                        // Bounded whatever `null_terminated` says: a document cut off inside an
1152
                        // escape has run out of input, and saying so beats blaming the terminator
1153
                        // the cut exposed -- which is what reading on would find, and would report
1154
                        // as a malformed escape.
1155
59
                        ++it; // skip the escape
1156
59
                        if (it == end) [[unlikely]] {
1157
4
                           ctx.error = error_code::unexpected_end;
1158
4
                           return;
1159
4
                        }
1160
55
                        if (*it == 'u') {
1161
44
                           ++it;
1162
44
                           if (it == end) [[unlikely]] {
1163
1
                              ctx.error = error_code::unexpected_end;
1164
1
                              return;
1165
1
                           }
1166
43
                           const auto decoded = handle_unicode_code_point(it, p, end);
1167
43
                           if (decoded.written == 0) [[unlikely]] {
1168
12
                              ctx.error = decoded.truncated ? error_code::unexpected_end
1169
12
                                                            : error_code::unicode_escape_conversion_failure;
1170
12
                              return;
1171
12
                           }
1172
43
                        }
1173
11
                        else {
1174
11
                           *p = char_unescape_table[uint8_t(*it)];
1175
11
                           if (*p == 0) [[unlikely]] {
1176
5
                              ctx.error = error_code::invalid_escape;
1177
5
                              return;
1178
5
                           }
1179
6
                           ++p;
1180
6
                           ++it;
1181
6
                        }
1182
55
                     }
1183
171
                     else if ((*it & 0b11100000) == 0) [[unlikely]] {
1184
8
                        ctx.error = error_code::syntax_error;
1185
8
                        return;
1186
8
                     }
1187
163
                     else {
1188
163
                        ++it;
1189
163
                        ++p;
1190
163
                     }
1191
309
                  }
1192
1193
71
                  ctx.error = error_code::unexpected_end;
1194
71
               }
1195
            }
1196
            else {
1197
               // raw_string
1198
               auto start = it;
1199
               skip_string_view(ctx, it, end);
1200
               if (bool(ctx.error)) [[unlikely]]
1201
                  return;
1202
1203
               if (validate_utf8_span<Opts>(ctx, start, it)) [[unlikely]] {
1204
                  return;
1205
               }
1206
1207
               value.assign(start, size_t(it - start));
1208
               ++it;
1209
            }
1210
4.57k
         }
1211
4.57k
      }
_ZN3glz4fromILj10ENSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEE2opITnDaXtlNS_4optsELj10ELb0ELb0ELb1ELb1ELb0ELb0ELb0ELb0ELj4EEERPKcSC_S7_TkNS_10is_contextERNS_7contextEEEvRT2_OT3_OT0_T1_
Line
Count
Source
887
4.29k
      {
888
         if constexpr (check_string_as_number(Opts)) {
889
            auto start = it;
890
            skip_number<Opts>(ctx, it, end);
891
            if (bool(ctx.error)) [[unlikely]] {
892
               return;
893
            }
894
            if (start == it) [[unlikely]] {
895
               ctx.error = error_code::parse_number_failure;
896
               return;
897
            }
898
            value.append(start, size_t(it - start));
899
         }
900
4.29k
         else {
901
4.29k
            if constexpr (!check_opening_handled(Opts)) {
902
               if constexpr (!check_ws_handled(Opts)) {
903
                  if (skip_ws<Opts>(ctx, it, end)) {
904
                     return;
905
                  }
906
               }
907
908
4.29k
               if (match_invalid_end<'"', Opts>(ctx, it, end)) {
909
19
                  return;
910
19
               }
911
4.29k
            }
912
913
4.29k
            if constexpr (not check_raw_string(Opts)) {
914
4.29k
               static constexpr auto string_padding_bytes = 8;
915
916
               // How much buffer has to remain for an eight byte load at the cursor to stay inside
917
               // it. One on a padded input, where the last chunk is free to straddle the end of the
918
               // document, so the byte tails below are unreachable; eight otherwise, which costs the
919
               // final bytes of the buffer their chunked path and nothing else.
920
4.29k
               const std::ptrdiff_t scan_min = chunk_min<8>(ctx.padded_input);
921
922
4.29k
               if (size_t(end - it) >= 8) {
923
                  // The bound as a pointer, so the loops below spend a compare per chunk rather
924
                  // than a subtract and a compare. Well defined here and nowhere else in this
925
                  // reader: the branch has just established that at least eight bytes remain, and
926
                  // `scan_min` is at most eight.
927
4.12k
                  const auto* const chunk_limit = end - scan_min;
928
929
4.12k
                  auto start = it;
930
4.12k
                  uint64_t ascii_acc{};
931
1.35M
                  while (it <= chunk_limit) {
932
1.35M
                     uint64_t chunk;
933
1.35M
                     std::memcpy(&chunk, it, 8);
934
                     if constexpr (std::endian::native == std::endian::big) {
935
                        chunk = std::byteswap(chunk);
936
                     }
937
1.35M
                     ascii_acc |= chunk;
938
1.35M
                     const uint64_t test_chars = has_quote(chunk);
939
1.35M
                     if (test_chars) {
940
4.12k
                        it += (countr_zero(test_chars) >> 3);
941
942
4.12k
                        auto* prev = it - 1;
943
4.65k
                        while (*prev == '\\') {
944
523
                           --prev;
945
523
                        }
946
4.12k
                        if (size_t(it - prev) % 2) {
947
3.86k
                           goto continue_decode;
948
3.86k
                        }
949
269
                        ++it; // skip the escaped quote
950
269
                     }
951
1.35M
                     else {
952
1.35M
                        it += 8;
953
1.35M
                     }
954
1.35M
                  }
955
956
837
                  while (it[-1] == '\\') [[unlikely]] {
957
                     // if we ended on an escape character then we need to rewind
958
                     // because we lost our context
959
574
                     --it;
960
574
                  }
961
962
                  // The byte-wise tail keeps feeding the accumulator: it has to cover every byte of
963
                  // the string, or a non-ASCII byte reached here would be waved through unvalidated.
964
1.36k
                  for (; it < end; ++it) {
965
1.34k
                     ascii_acc |= uint8_t(*it);
966
1.34k
                     if (*it == '"') {
967
257
                        auto* prev = it - 1;
968
541
                        while (*prev == '\\') {
969
284
                           --prev;
970
284
                        }
971
257
                        if (size_t(it - prev) % 2) {
972
239
                           goto continue_decode;
973
239
                        }
974
257
                     }
975
1.34k
                  }
976
977
24
                  ctx.error = error_code::unexpected_end;
978
24
                  return;
979
980
4.09k
               continue_decode:
981
982
4.09k
                  if (validate_utf8_span<Opts>(ctx, start, it, ascii_acc)) [[unlikely]] {
983
47
                     return;
984
47
                  }
985
986
4.05k
                  auto n = size_t(it - start);
987
988
                  // The padding lets the chunked copy below run up to the closing quote. Skip it when it
989
                  // would be the only reason to allocate, as for a string that fits the small string
990
                  // buffer but not with eight bytes to spare.
991
4.05k
                  bool pad = true;
992
4.05k
                  if constexpr (requires { value.capacity(); }) {
993
4.05k
                     const auto capacity = size_t(value.capacity());
994
4.05k
                     pad = capacity >= n + string_padding_bytes || capacity < n;
995
4.05k
                  }
996
4.05k
                  resize_unfilled(value, pad ? n + string_padding_bytes : n);
997
998
4.05k
                  auto* p = value.data();
999
1000
                  // An error below returns with the tail of `value` still unwritten. Leave the size at
1001
                  // what was actually written rather than at the padded length, which resize_unfilled
1002
                  // did not fill.
1003
4.05k
                  auto* const written_begin = p;
1004
1005
                  // Without the padding, copy a chunk only while eight bytes remain before the quote.
1006
                  // Unescaping never puts the output ahead of the input, so no store lands past the end.
1007
4.05k
                  const auto* const copy_end = pad ? it : it - (std::min)(n, size_t(7));
1008
1009
                  // Copy eight bytes at a time for as long as eight bytes are there to read, then
1010
                  // finish the span below. The chunked form reads past the character it is looking
1011
                  // at, so on an unpadded buffer it has to stop short of the end; splitting it this
1012
                  // way keeps it for the body of every string rather than giving it up whenever the
1013
                  // string happens to finish near the end of the buffer -- which, for a document
1014
                  // that is itself one string, is every string.
1015
596k
                  while (start < copy_end && start <= chunk_limit) {
1016
593k
                     std::memcpy(p, start, 8);
1017
593k
                     uint64_t swar;
1018
593k
                     std::memcpy(&swar, p, 8);
1019
                     if constexpr (std::endian::native == std::endian::big) {
1020
                        swar = std::byteswap(swar);
1021
                     }
1022
1023
593k
                     constexpr uint64_t lo7_mask = repeat_byte8(0b01111111);
1024
593k
                     const uint64_t lo7 = swar & lo7_mask;
1025
593k
                     const uint64_t backslash = (lo7 ^ repeat_byte8('\\')) + lo7_mask;
1026
593k
                     const uint64_t less_32 = (swar & repeat_byte8(0b01100000)) + lo7_mask;
1027
593k
                     uint64_t next = ~((backslash & less_32) | swar);
1028
1029
593k
                     next &= repeat_byte8(0b10000000);
1030
593k
                     if (next == 0) {
1031
566k
                        start += 8;
1032
566k
                        p += 8;
1033
566k
                        continue;
1034
566k
                     }
1035
1036
26.7k
                     next = countr_zero(next) >> 3;
1037
26.7k
                     start += next;
1038
26.7k
                     if (start >= it) {
1039
277
                        break;
1040
277
                     }
1041
1042
26.4k
                     if ((*start & 0b11100000) == 0) [[unlikely]] {
1043
62
                        ctx.error = error_code::syntax_error;
1044
62
                        value.resize(size_t(p - written_begin));
1045
62
                        return;
1046
62
                     }
1047
26.3k
                     ++start; // skip the escape
1048
26.3k
                     if (*start == 'u') {
1049
7.49k
                        ++start;
1050
7.49k
                        p += next;
1051
7.49k
                        const auto mark = start;
1052
7.49k
                        const auto decoded = handle_unicode_code_point(start, p, end);
1053
7.49k
                        if (decoded.written == 0) [[unlikely]] {
1054
74
                           ctx.error = decoded.truncated ? error_code::unexpected_end
1055
74
                                                         : error_code::unicode_escape_conversion_failure;
1056
74
                           value.resize(size_t(p - written_begin));
1057
74
                           return;
1058
74
                        }
1059
7.41k
                        n += decoded.written;
1060
                        // escape + u + unicode code points
1061
7.41k
                        n -= 2 + uint32_t(start - mark);
1062
7.41k
                     }
1063
18.8k
                     else {
1064
18.8k
                        p += next;
1065
18.8k
                        *p = char_unescape_table[uint8_t(*start)];
1066
18.8k
                        if (*p == 0) [[unlikely]] {
1067
14
                           ctx.error = error_code::invalid_escape;
1068
14
                           value.resize(size_t(p - written_begin));
1069
14
                           return;
1070
14
                        }
1071
18.8k
                        ++p;
1072
18.8k
                        ++start;
1073
18.8k
                        --n;
1074
18.8k
                     }
1075
26.3k
                  }
1076
1077
                  // Whatever the chunked copy could not reach. `p` and `start` stay in step across
1078
                  // that loop, so this picks up exactly where it stopped.
1079
6.77k
                  while (start < it) {
1080
2.92k
                     if ((*start & 0b11100000) == 0) [[unlikely]] {
1081
12
                        ctx.error = error_code::syntax_error;
1082
12
                        value.resize(size_t(p - written_begin));
1083
12
                        return;
1084
12
                     }
1085
2.91k
                     if (*start == '\\') {
1086
1.36k
                        ++start; // skip the escape
1087
1.36k
                        if (*start == 'u') {
1088
787
                           ++start;
1089
787
                           const auto mark = start;
1090
787
                           const auto decoded = handle_unicode_code_point(start, p, end);
1091
787
                           if (decoded.written == 0) [[unlikely]] {
1092
36
                              ctx.error = decoded.truncated ? error_code::unexpected_end
1093
36
                                                            : error_code::unicode_escape_conversion_failure;
1094
36
                              value.resize(size_t(p - written_begin));
1095
36
                              return;
1096
36
                           }
1097
751
                           n += decoded.written;
1098
751
                           n -= 2 + uint32_t(start - mark);
1099
751
                        }
1100
573
                        else {
1101
573
                           *p = char_unescape_table[uint8_t(*start)];
1102
573
                           if (*p == 0) [[unlikely]] {
1103
3
                              ctx.error = error_code::invalid_escape;
1104
3
                              value.resize(size_t(p - written_begin));
1105
3
                              return;
1106
3
                           }
1107
570
                           ++p;
1108
570
                           ++start;
1109
570
                           --n;
1110
570
                        }
1111
1.36k
                     }
1112
1.55k
                     else {
1113
1.55k
                        *p = *start;
1114
1.55k
                        ++p;
1115
1.55k
                        ++start;
1116
1.55k
                     }
1117
2.91k
                  }
1118
1119
3.85k
                  value.resize(n);
1120
3.85k
                  ++it;
1121
3.85k
               }
1122
176
               else {
1123
                  // For short strings
1124
1125
176
                  std::array<char, 8> buffer{};
1126
1127
176
                  auto* p = buffer.data();
1128
176
                  const auto* const utf8_start = it;
1129
1130
424
                  while (it < end) [[likely]] {
1131
379
                     *p = *it;
1132
379
                     if (*it == '"') {
1133
110
                        if (validate_utf8_span<Opts>(ctx, utf8_start, it)) [[unlikely]] {
1134
30
                           return;
1135
30
                        }
1136
80
                        const size_t n = size_t(p - buffer.data());
1137
80
#if __has_cpp_attribute(assume) >= 202207L
1138
80
                        [[assume(n <= sizeof(buffer))]];
1139
80
#endif
1140
80
                        value.assign(buffer.data(), n);
1141
80
                        ++it;
1142
80
                        if constexpr (not Opts.null_terminated) {
1143
80
                           if (it == end) {
1144
49
                              ctx.error = error_code::end_reached;
1145
49
                              return;
1146
49
                           }
1147
80
                        }
1148
31
                        return;
1149
110
                     }
1150
269
                     else if (*it == '\\') {
1151
                        // Bounded whatever `null_terminated` says: a document cut off inside an
1152
                        // escape has run out of input, and saying so beats blaming the terminator
1153
                        // the cut exposed -- which is what reading on would find, and would report
1154
                        // as a malformed escape.
1155
61
                        ++it; // skip the escape
1156
61
                        if (it == end) [[unlikely]] {
1157
2
                           ctx.error = error_code::unexpected_end;
1158
2
                           return;
1159
2
                        }
1160
59
                        if (*it == 'u') {
1161
50
                           ++it;
1162
50
                           if (it == end) [[unlikely]] {
1163
1
                              ctx.error = error_code::unexpected_end;
1164
1
                              return;
1165
1
                           }
1166
49
                           const auto decoded = handle_unicode_code_point(it, p, end);
1167
49
                           if (decoded.written == 0) [[unlikely]] {
1168
12
                              ctx.error = decoded.truncated ? error_code::unexpected_end
1169
12
                                                            : error_code::unicode_escape_conversion_failure;
1170
12
                              return;
1171
12
                           }
1172
49
                        }
1173
9
                        else {
1174
9
                           *p = char_unescape_table[uint8_t(*it)];
1175
9
                           if (*p == 0) [[unlikely]] {
1176
1
                              ctx.error = error_code::invalid_escape;
1177
1
                              return;
1178
1
                           }
1179
8
                           ++p;
1180
8
                           ++it;
1181
8
                        }
1182
59
                     }
1183
208
                     else if ((*it & 0b11100000) == 0) [[unlikely]] {
1184
5
                        ctx.error = error_code::syntax_error;
1185
5
                        return;
1186
5
                     }
1187
203
                     else {
1188
203
                        ++it;
1189
203
                        ++p;
1190
203
                     }
1191
379
                  }
1192
1193
45
                  ctx.error = error_code::unexpected_end;
1194
45
               }
1195
            }
1196
            else {
1197
               // raw_string
1198
               auto start = it;
1199
               skip_string_view(ctx, it, end);
1200
               if (bool(ctx.error)) [[unlikely]]
1201
                  return;
1202
1203
               if (validate_utf8_span<Opts>(ctx, start, it)) [[unlikely]] {
1204
                  return;
1205
               }
1206
1207
               value.assign(start, size_t(it - start));
1208
               ++it;
1209
            }
1210
4.29k
         }
1211
4.29k
      }
1212
   };
1213
1214
   // Specialization for u8string types (std::u8string)
1215
   template <class T>
1216
      requires(u8str_t<T> && !string_view_t<T>)
1217
   struct from<JSON, T>
1218
   {
1219
      template <auto Opts, class It, class End>
1220
      static void op(auto& value, is_context auto&& ctx, It&& it, End end)
1221
      {
1222
         if constexpr (check_string_as_number(Opts)) {
1223
            auto start = it;
1224
            skip_number<Opts>(ctx, it, end);
1225
            if (bool(ctx.error)) [[unlikely]] {
1226
               return;
1227
            }
1228
            if (start == it) [[unlikely]] {
1229
               ctx.error = error_code::parse_number_failure;
1230
               return;
1231
            }
1232
            value.append(reinterpret_cast<const char8_t*>(start), size_t(it - start));
1233
         }
1234
         else {
1235
            if constexpr (!check_opening_handled(Opts)) {
1236
               if constexpr (!check_ws_handled(Opts)) {
1237
                  if (skip_ws<Opts>(ctx, it, end)) {
1238
                     return;
1239
                  }
1240
               }
1241
1242
               if (match_invalid_end<'"', Opts>(ctx, it, end)) {
1243
                  return;
1244
               }
1245
            }
1246
1247
            if constexpr (not check_raw_string(Opts)) {
1248
               static constexpr auto string_padding_bytes = 8;
1249
1250
               // How much buffer has to remain for an eight byte load at the cursor to stay inside
1251
               // it. One on a padded input, where the last chunk is free to straddle the end of the
1252
               // document, so the byte tails below are unreachable; eight otherwise, which costs the
1253
               // final bytes of the buffer their chunked path and nothing else.
1254
               const std::ptrdiff_t scan_min = chunk_min<8>(ctx.padded_input);
1255
1256
               if (size_t(end - it) >= 8) {
1257
                  // The bound as a pointer, so the loops below spend a compare per chunk rather
1258
                  // than a subtract and a compare. Well defined here and nowhere else in this
1259
                  // reader: the branch has just established that at least eight bytes remain, and
1260
                  // `scan_min` is at most eight.
1261
                  const auto* const chunk_limit = end - scan_min;
1262
1263
                  auto start = it;
1264
                  uint64_t ascii_acc{};
1265
                  while (it <= chunk_limit) {
1266
                     uint64_t chunk;
1267
                     std::memcpy(&chunk, it, 8);
1268
                     if constexpr (std::endian::native == std::endian::big) {
1269
                        chunk = std::byteswap(chunk);
1270
                     }
1271
                     ascii_acc |= chunk;
1272
                     const uint64_t test_chars = has_quote(chunk);
1273
                     if (test_chars) {
1274
                        it += (countr_zero(test_chars) >> 3);
1275
1276
                        auto* prev = it - 1;
1277
                        while (*prev == '\\') {
1278
                           --prev;
1279
                        }
1280
                        if (size_t(it - prev) % 2) {
1281
                           goto continue_decode_u8;
1282
                        }
1283
                        ++it; // skip the escaped quote
1284
                     }
1285
                     else {
1286
                        it += 8;
1287
                     }
1288
                  }
1289
1290
                  while (it[-1] == '\\') [[unlikely]] {
1291
                     // if we ended on an escape character then we need to rewind
1292
                     // because we lost our context
1293
                     --it;
1294
                  }
1295
1296
                  // The byte-wise tail keeps feeding the accumulator: it has to cover every byte of
1297
                  // the string, or a non-ASCII byte reached here would be waved through unvalidated.
1298
                  for (; it < end; ++it) {
1299
                     ascii_acc |= uint8_t(*it);
1300
                     if (*it == '"') {
1301
                        auto* prev = it - 1;
1302
                        while (*prev == '\\') {
1303
                           --prev;
1304
                        }
1305
                        if (size_t(it - prev) % 2) {
1306
                           goto continue_decode_u8;
1307
                        }
1308
                     }
1309
                  }
1310
1311
                  ctx.error = error_code::unexpected_end;
1312
                  return;
1313
1314
               continue_decode_u8:
1315
1316
                  if (validate_utf8_span<Opts>(ctx, start, it, ascii_acc)) [[unlikely]] {
1317
                     return;
1318
                  }
1319
1320
                  auto n = size_t(it - start);
1321
1322
                  // The padding lets the chunked copy below run up to the closing quote. Skip it when it
1323
                  // would be the only reason to allocate, as for a string that fits the small string
1324
                  // buffer but not with eight bytes to spare.
1325
                  bool pad = true;
1326
                  if constexpr (requires { value.capacity(); }) {
1327
                     const auto capacity = size_t(value.capacity());
1328
                     pad = capacity >= n + string_padding_bytes || capacity < n;
1329
                  }
1330
                  resize_unfilled(value, pad ? n + string_padding_bytes : n);
1331
1332
                  auto* p = reinterpret_cast<char*>(value.data());
1333
1334
                  // An error below returns with the tail of `value` still unwritten. Leave the size at
1335
                  // what was actually written rather than at the padded length, which resize_unfilled
1336
                  // did not fill.
1337
                  auto* const written_begin = p;
1338
1339
                  // Without the padding, copy a chunk only while eight bytes remain before the quote.
1340
                  // Unescaping never puts the output ahead of the input, so no store lands past the end.
1341
                  const auto* const copy_end = pad ? it : it - (std::min)(n, size_t(7));
1342
1343
                  // Copy eight bytes at a time for as long as eight bytes are there to read, then
1344
                  // finish the span below. The chunked form reads past the character it is looking
1345
                  // at, so on an unpadded buffer it has to stop short of the end; splitting it this
1346
                  // way keeps it for the body of every string rather than giving it up whenever the
1347
                  // string happens to finish near the end of the buffer -- which, for a document
1348
                  // that is itself one string, is every string.
1349
                  while (start < copy_end && start <= chunk_limit) {
1350
                     std::memcpy(p, start, 8);
1351
                     uint64_t swar;
1352
                     std::memcpy(&swar, p, 8);
1353
                     if constexpr (std::endian::native == std::endian::big) {
1354
                        swar = std::byteswap(swar);
1355
                     }
1356
1357
                     constexpr uint64_t lo7_mask = repeat_byte8(0b01111111);
1358
                     const uint64_t lo7 = swar & lo7_mask;
1359
                     const uint64_t backslash = (lo7 ^ repeat_byte8('\\')) + lo7_mask;
1360
                     const uint64_t less_32 = (swar & repeat_byte8(0b01100000)) + lo7_mask;
1361
                     uint64_t next = ~((backslash & less_32) | swar);
1362
1363
                     next &= repeat_byte8(0b10000000);
1364
                     if (next == 0) {
1365
                        start += 8;
1366
                        p += 8;
1367
                        continue;
1368
                     }
1369
1370
                     next = countr_zero(next) >> 3;
1371
                     start += next;
1372
                     if (start >= it) {
1373
                        break;
1374
                     }
1375
1376
                     if ((*start & 0b11100000) == 0) [[unlikely]] {
1377
                        ctx.error = error_code::syntax_error;
1378
                        value.resize(size_t(p - written_begin));
1379
                        return;
1380
                     }
1381
                     ++start; // skip the escape
1382
                     if (*start == 'u') {
1383
                        ++start;
1384
                        p += next;
1385
                        const auto mark = start;
1386
                        const auto decoded = handle_unicode_code_point(start, p, end);
1387
                        if (decoded.written == 0) [[unlikely]] {
1388
                           ctx.error = decoded.truncated ? error_code::unexpected_end
1389
                                                         : error_code::unicode_escape_conversion_failure;
1390
                           value.resize(size_t(p - written_begin));
1391
                           return;
1392
                        }
1393
                        n += decoded.written;
1394
                        // escape + u + unicode code points
1395
                        n -= 2 + uint32_t(start - mark);
1396
                     }
1397
                     else {
1398
                        p += next;
1399
                        *p = char_unescape_table[uint8_t(*start)];
1400
                        if (*p == 0) [[unlikely]] {
1401
                           ctx.error = error_code::invalid_escape;
1402
                           value.resize(size_t(p - written_begin));
1403
                           return;
1404
                        }
1405
                        ++p;
1406
                        ++start;
1407
                        --n;
1408
                     }
1409
                  }
1410
1411
                  // Whatever the chunked copy could not reach. `p` and `start` stay in step across
1412
                  // that loop, so this picks up exactly where it stopped.
1413
                  while (start < it) {
1414
                     if ((*start & 0b11100000) == 0) [[unlikely]] {
1415
                        ctx.error = error_code::syntax_error;
1416
                        value.resize(size_t(p - written_begin));
1417
                        return;
1418
                     }
1419
                     if (*start == '\\') {
1420
                        ++start; // skip the escape
1421
                        if (*start == 'u') {
1422
                           ++start;
1423
                           const auto mark = start;
1424
                           const auto decoded = handle_unicode_code_point(start, p, end);
1425
                           if (decoded.written == 0) [[unlikely]] {
1426
                              ctx.error = decoded.truncated ? error_code::unexpected_end
1427
                                                            : error_code::unicode_escape_conversion_failure;
1428
                              value.resize(size_t(p - written_begin));
1429
                              return;
1430
                           }
1431
                           n += decoded.written;
1432
                           n -= 2 + uint32_t(start - mark);
1433
                        }
1434
                        else {
1435
                           *p = char_unescape_table[uint8_t(*start)];
1436
                           if (*p == 0) [[unlikely]] {
1437
                              ctx.error = error_code::invalid_escape;
1438
                              value.resize(size_t(p - written_begin));
1439
                              return;
1440
                           }
1441
                           ++p;
1442
                           ++start;
1443
                           --n;
1444
                        }
1445
                     }
1446
                     else {
1447
                        *p = *start;
1448
                        ++p;
1449
                        ++start;
1450
                     }
1451
                  }
1452
1453
                  value.resize(n);
1454
                  ++it;
1455
               }
1456
               else {
1457
                  // For short strings
1458
1459
                  std::array<char, 8> buffer{};
1460
1461
                  auto* p = buffer.data();
1462
                  const auto* const utf8_start = it;
1463
1464
                  while (it < end) [[likely]] {
1465
                     *p = *it;
1466
                     if (*it == '"') {
1467
                        if (validate_utf8_span<Opts>(ctx, utf8_start, it)) [[unlikely]] {
1468
                           return;
1469
                        }
1470
                        value.assign(reinterpret_cast<const char8_t*>(buffer.data()), size_t(p - buffer.data()));
1471
                        ++it;
1472
                        if constexpr (not Opts.null_terminated) {
1473
                           if (it == end) {
1474
                              ctx.error = error_code::end_reached;
1475
                              return;
1476
                           }
1477
                        }
1478
                        return;
1479
                     }
1480
                     else if (*it == '\\') {
1481
                        // Bounded whatever `null_terminated` says: a document cut off inside an
1482
                        // escape has run out of input, and saying so beats blaming the terminator
1483
                        // the cut exposed -- which is what reading on would find, and would report
1484
                        // as a malformed escape.
1485
                        ++it; // skip the escape
1486
                        if (it == end) [[unlikely]] {
1487
                           ctx.error = error_code::unexpected_end;
1488
                           return;
1489
                        }
1490
                        if (*it == 'u') {
1491
                           ++it;
1492
                           if (it == end) [[unlikely]] {
1493
                              ctx.error = error_code::unexpected_end;
1494
                              return;
1495
                           }
1496
                           const auto decoded = handle_unicode_code_point(it, p, end);
1497
                           if (decoded.written == 0) [[unlikely]] {
1498
                              ctx.error = decoded.truncated ? error_code::unexpected_end
1499
                                                            : error_code::unicode_escape_conversion_failure;
1500
                              return;
1501
                           }
1502
                        }
1503
                        else {
1504
                           *p = char_unescape_table[uint8_t(*it)];
1505
                           if (*p == 0) [[unlikely]] {
1506
                              ctx.error = error_code::invalid_escape;
1507
                              return;
1508
                           }
1509
                           ++p;
1510
                           ++it;
1511
                        }
1512
                     }
1513
                     else if ((*it & 0b11100000) == 0) [[unlikely]] {
1514
                        ctx.error = error_code::syntax_error;
1515
                        return;
1516
                     }
1517
                     else {
1518
                        ++it;
1519
                        ++p;
1520
                     }
1521
                  }
1522
1523
                  ctx.error = error_code::unexpected_end;
1524
               }
1525
            }
1526
            else {
1527
               // raw_string
1528
               auto start = it;
1529
               skip_string_view(ctx, it, end);
1530
               if (bool(ctx.error)) [[unlikely]]
1531
                  return;
1532
1533
               if (validate_utf8_span<Opts>(ctx, start, it)) [[unlikely]] {
1534
                  return;
1535
               }
1536
1537
               value.assign(reinterpret_cast<const char8_t*>(start), size_t(it - start));
1538
               ++it;
1539
            }
1540
         }
1541
      }
1542
   };
1543
1544
   // `Transient` says the view being filled is consumed before this reader returns, so it never
1545
   // outlives the window it points into and the streaming guard does not apply. Only readers that
1546
   // borrow a view of what they just parsed set it, through parse_transient_string_view below; a
1547
   // view the caller keeps leaves it false and is rejected under a streaming read.
1548
   template <class T>
1549
      requires(string_view_t<T> || char_array_t<T> || array_char_t<T> || static_string_t<T>)
1550
   struct from<JSON, T>
1551
   {
1552
      template <auto Opts, bool Transient = false, class It, class End>
1553
         requires(check_string_as_number(Opts))
1554
      GLZ_ALWAYS_INLINE static void op(auto& value, is_context auto&& ctx, It&& it, End end) noexcept
1555
      {
1556
         auto start = it;
1557
         skip_number<Opts>(ctx, it, end);
1558
         if (bool(ctx.error)) [[unlikely]]
1559
            return;
1560
         if (start == it) [[unlikely]] {
1561
            ctx.error = error_code::parse_number_failure;
1562
            return;
1563
         }
1564
1565
         const size_t n = size_t(it - start);
1566
         if constexpr (string_view_t<T>) {
1567
            if constexpr (!Transient) {
1568
               GLZ_ASSERT_OWNS_ITS_BYTES(decltype(ctx));
1569
            }
1570
            using value_type = typename std::decay_t<T>::value_type;
1571
            if constexpr (std::same_as<value_type, char8_t>) {
1572
               value = {reinterpret_cast<const char8_t*>(start), n};
1573
            }
1574
            else {
1575
               value = {start, n};
1576
            }
1577
         }
1578
         else if constexpr (char_array_t<T>) {
1579
            if ((n + 1) > sizeof(value)) {
1580
               ctx.error = error_code::unexpected_end;
1581
               return;
1582
            }
1583
            std::memcpy(value, start, n);
1584
            value[n] = '\0';
1585
         }
1586
         else if constexpr (array_char_t<T>) {
1587
            if ((n + 1) > value.size()) {
1588
               ctx.error = error_code::unexpected_end;
1589
               return;
1590
            }
1591
            std::memcpy(value.data(), start, n);
1592
            value[n] = '\0';
1593
         }
1594
         else if constexpr (static_string_t<T>) {
1595
            if (n > value.capacity()) {
1596
               ctx.error = error_code::unexpected_end;
1597
               return;
1598
            }
1599
            value.assign(start, n);
1600
         }
1601
      }
1602
1603
      template <auto Opts, bool Transient = false, class It, class End>
1604
         requires(!check_string_as_number(Opts))
1605
      GLZ_ALWAYS_INLINE static void op(auto& value, is_context auto&& ctx, It&& it, End end) noexcept
1606
      {
1607
         if constexpr (!check_opening_handled(Opts)) {
1608
            if constexpr (!check_ws_handled(Opts)) {
1609
               if (skip_ws<Opts>(ctx, it, end)) {
1610
                  return;
1611
               }
1612
            }
1613
1614
            if (match_invalid_end<'"', Opts>(ctx, it, end)) {
1615
               return;
1616
            }
1617
         }
1618
1619
         auto start = it;
1620
         skip_string_view(ctx, it, end);
1621
         if (bool(ctx.error)) [[unlikely]]
1622
            return;
1623
1624
         if (validate_utf8_span<Opts>(ctx, start, it)) [[unlikely]] {
1625
            return;
1626
         }
1627
1628
         if constexpr (string_view_t<T>) {
1629
            if constexpr (!Transient) {
1630
               GLZ_ASSERT_OWNS_ITS_BYTES(decltype(ctx));
1631
            }
1632
            using value_type = typename std::decay_t<T>::value_type;
1633
            if constexpr (std::same_as<value_type, char8_t>) {
1634
               value = {reinterpret_cast<const char8_t*>(start), size_t(it - start)};
1635
            }
1636
            else {
1637
               value = {start, size_t(it - start)};
1638
            }
1639
         }
1640
         else if constexpr (char_array_t<T>) {
1641
            const size_t n = it - start;
1642
            if ((n + 1) > sizeof(value)) {
1643
               ctx.error = error_code::unexpected_end;
1644
               return;
1645
            }
1646
            std::memcpy(value, start, n);
1647
            value[n] = '\0';
1648
         }
1649
         else if constexpr (array_char_t<T>) {
1650
            const size_t n = it - start;
1651
            if ((n + 1) > value.size()) {
1652
               ctx.error = error_code::unexpected_end;
1653
               return;
1654
            }
1655
            std::memcpy(value.data(), start, n);
1656
            value[n] = '\0';
1657
         }
1658
         else if constexpr (static_string_t<T>) {
1659
            const size_t n = it - start;
1660
            if (n > value.capacity()) {
1661
               ctx.error = error_code::unexpected_end;
1662
               return;
1663
            }
1664
            value.assign(start, n);
1665
         }
1666
         ++it; // skip closing quote
1667
         if constexpr (not Opts.null_terminated) {
1668
            if (it == end) {
1669
               ctx.error = error_code::end_reached;
1670
               return;
1671
            }
1672
         }
1673
      }
1674
   };
1675
1676
   // Reads a JSON string as a view of the input window, for a reader that consumes it before
1677
   // returning -- parsing a date out of it, say. Such a view cannot dangle: nothing refills the
1678
   // window between the read below and the caller's use of it, which is why this is exempt from the
1679
   // guard that rejects streaming into a view the caller keeps. Do not hand the view any further
1680
   // than the calling reader; store what it parsed, never the view itself.
1681
   template <auto Opts, class... Args>
1682
   GLZ_ALWAYS_INLINE void parse_transient_string_view(std::string_view& value, Args&&... args) noexcept
1683
   {
1684
      from<JSON, std::string_view>::template op<Opts, true>(value, std::forward<Args>(args)...);
1685
   }
1686
1687
   template <char_t T>
1688
   struct from<JSON, T>
1689
   {
1690
      template <auto Opts>
1691
      static void op(auto& value, is_context auto&& ctx, auto&& it, auto end) noexcept
1692
      {
1693
         if constexpr (!check_opening_handled(Opts)) {
1694
            if constexpr (!check_ws_handled(Opts)) {
1695
               if (skip_ws<Opts>(ctx, it, end)) {
1696
                  return;
1697
               }
1698
            }
1699
1700
            if (match_invalid_end<'"', Opts>(ctx, it, end)) {
1701
               return;
1702
            }
1703
         }
1704
1705
         // Handle empty string: "" should map to null character '\0'
1706
         if (*it == '"') {
1707
            value = '\0';
1708
            ++it; // consume closing quote
1709
            if constexpr (not Opts.null_terminated) {
1710
               if (it == end) {
1711
                  ctx.error = error_code::end_reached;
1712
                  return;
1713
               }
1714
            }
1715
            return;
1716
         }
1717
1718
         if (*it == '\\') [[unlikely]] {
1719
            ++it;
1720
            if constexpr (not Opts.null_terminated) {
1721
               if (it == end) [[unlikely]] {
1722
                  ctx.error = error_code::unexpected_end;
1723
                  return;
1724
               }
1725
            }
1726
            switch (*it) {
1727
            case '\0': {
1728
               ctx.error = error_code::unexpected_end;
1729
               return;
1730
            }
1731
            case '"':
1732
            case '\\':
1733
            case '/':
1734
               value = *it++;
1735
               break;
1736
            case 'b':
1737
               value = '\b';
1738
               ++it;
1739
               break;
1740
            case 'f':
1741
               value = '\f';
1742
               ++it;
1743
               break;
1744
            case 'n':
1745
               value = '\n';
1746
               ++it;
1747
               break;
1748
            case 'r':
1749
               value = '\r';
1750
               ++it;
1751
               break;
1752
            case 't':
1753
               value = '\t';
1754
               ++it;
1755
               break;
1756
            case 'u': {
1757
               ctx.error = error_code::unicode_escape_conversion_failure;
1758
               return;
1759
            }
1760
            default: {
1761
               ctx.error = error_code::invalid_escape;
1762
               return;
1763
            }
1764
            }
1765
         }
1766
         else {
1767
            if (it == end) [[unlikely]] {
1768
               ctx.error = error_code::unexpected_end;
1769
               return;
1770
            }
1771
            value = *it++;
1772
         }
1773
         if constexpr (not Opts.null_terminated) {
1774
            if (it == end) [[unlikely]] {
1775
               ctx.error = error_code::unexpected_end;
1776
               return;
1777
            }
1778
         }
1779
         if (match<'"'>(ctx, it)) {
1780
            return;
1781
         }
1782
         if constexpr (not Opts.null_terminated) {
1783
            if (it == end) {
1784
               ctx.error = error_code::end_reached;
1785
               return;
1786
            }
1787
         }
1788
      }
1789
   };
1790
1791
   template <class T>
1792
      requires(is_named_enum<T>)
1793
   struct from<JSON, T>
1794
   {
1795
      template <auto Opts>
1796
      static void op(auto& value, is_context auto&& ctx, auto&& it, auto end) noexcept
1797
      {
1798
         if constexpr (!check_ws_handled(Opts)) {
1799
            if (skip_ws<Opts>(ctx, it, end)) {
1800
               return;
1801
            }
1802
         }
1803
1804
         constexpr auto N = reflect<T>::size;
1805
1806
         if (*it != '"') [[unlikely]] {
1807
            ctx.error = error_code::expected_quote;
1808
            return;
1809
         }
1810
         ++it;
1811
         if constexpr (not Opts.null_terminated) {
1812
            if (it == end) [[unlikely]] {
1813
               ctx.error = error_code::unexpected_end;
1814
               return;
1815
            }
1816
         }
1817
1818
         if constexpr (N == 1) {
1819
            decode_index<Opts, T, 0>(value, ctx, it, end);
1820
         }
1821
         else if constexpr (check_linear_search(Opts)) {
1822
            // Linear search path for enums - decode_linear advances past closing quote
1823
            const auto index = decode_linear<T>(ctx, it, end);
1824
            if (bool(ctx.error)) [[unlikely]]
1825
               return;
1826
1827
            if (index >= N) [[unlikely]] {
1828
               ctx.error = error_code::unexpected_enum;
1829
               return;
1830
            }
1831
1832
            // Simply assign the enum value - fold expression dispatch
1833
            [&]<size_t... Is>(std::index_sequence<Is...>) {
1834
               (void)(((index == Is ? (value = get<Is>(reflect<T>::values), true) : false) || ...));
1835
            }(std::make_index_sequence<N>{});
1836
         }
1837
         else {
1838
            static constexpr auto HashInfo = hash_info<T>;
1839
1840
            const auto index = decode_hash<JSON, T, HashInfo, HashInfo.type>::op(it, end);
1841
1842
            if (index >= N) [[unlikely]] {
1843
               ctx.error = error_code::unexpected_enum;
1844
               return;
1845
            }
1846
1847
            visit<N>([&]<size_t I>() { decode_index<Opts, T, I>(value, ctx, it, end); }, index);
1848
         }
1849
      }
1850
   };
1851
1852
   // Fallback handler for enums without explicit glz::meta
1853
   // Reads as underlying integer unless reflect_enums option is enabled (P2996)
1854
   template <class T>
1855
      requires(std::is_enum_v<T> && !glaze_enum_t<T> && !meta_keys<T> && !custom_read<T>)
1856
   struct from<JSON, T>
1857
   {
1858
      template <auto Opts>
1859
      static void op(auto& value, is_context auto&& ctx, auto&& it, auto end) noexcept
1860
      {
1861
#if GLZ_REFLECTION26
1862
         if constexpr (check_reflect_enums(Opts)) {
1863
            // P2996 reflection using reflect_constant_array and expansion statements
1864
            if constexpr (!check_ws_handled(Opts)) {
1865
               if (skip_ws<Opts>(ctx, it, end)) {
1866
                  return;
1867
               }
1868
            }
1869
1870
            if (*it != '"') [[unlikely]] {
1871
               ctx.error = error_code::expected_quote;
1872
               return;
1873
            }
1874
            ++it;
1875
            if constexpr (not Opts.null_terminated) {
1876
               if (it == end) [[unlikely]] {
1877
                  ctx.error = error_code::unexpected_end;
1878
                  return;
1879
               }
1880
            }
1881
1882
            // Extract the string key
1883
            const auto start = it;
1884
            while (it != end && *it != '"') {
1885
               ++it;
1886
            }
1887
            if constexpr (not Opts.null_terminated) {
1888
               if (it == end) [[unlikely]] {
1889
                  ctx.error = error_code::unexpected_end;
1890
                  return;
1891
               }
1892
            }
1893
            const sv key{start, static_cast<size_t>(it - start)};
1894
            ++it; // skip closing quote
1895
1896
            using V = std::decay_t<T>;
1897
            auto result = string_to_enum<V>(key);
1898
            if (result) {
1899
               value = *result;
1900
            }
1901
            else [[unlikely]] {
1902
               ctx.error = error_code::unexpected_enum;
1903
            }
1904
         }
1905
         else
1906
#endif
1907
         {
1908
            // Fallback: read as underlying integer
1909
            std::underlying_type_t<std::decay_t<T>> x{};
1910
            parse<JSON>::op<Opts>(x, ctx, it, end);
1911
            value = static_cast<std::decay_t<T>>(x);
1912
         }
1913
      }
1914
   };
1915
1916
   template <func_t T>
1917
   struct from<JSON, T>
1918
   {
1919
      template <auto Opts>
1920
      static void op(auto& /*value*/, is_context auto&& ctx, auto&& it, auto end)
1921
      {
1922
         if constexpr (!check_ws_handled(Opts)) {
1923
            if (skip_ws<Opts>(ctx, it, end)) {
1924
               return;
1925
            }
1926
         }
1927
         if (match_invalid_end<'"', Opts>(ctx, it, end)) {
1928
            return;
1929
         }
1930
         skip_string_view(ctx, it, end);
1931
         if (bool(ctx.error)) [[unlikely]]
1932
            return;
1933
         if (match<'"'>(ctx, it)) {
1934
            return;
1935
         }
1936
         if constexpr (not Opts.null_terminated) {
1937
            if (it == end) {
1938
               ctx.error = error_code::end_reached;
1939
               return;
1940
            }
1941
         }
1942
      }
1943
   };
1944
1945
   template <class T>
1946
   struct from<JSON, basic_raw_json<T>>
1947
   {
1948
      template <auto Opts>
1949
      GLZ_ALWAYS_INLINE static void op(auto&& value, is_context auto&& ctx, auto&& it, auto end)
1950
      {
1951
         auto it_start = it;
1952
         if (*it == 'n') {
1953
            match<"null", Opts>(ctx, it, end);
1954
         }
1955
         else if (is_digit(uint8_t(*it))) {
1956
            skip_number<Opts>(ctx, it, end);
1957
         }
1958
         else {
1959
            skip_value<JSON>::op<Opts>(ctx, it, end);
1960
         }
1961
         if (bool(ctx.error)) [[unlikely]]
1962
            return;
1963
         if constexpr (string_view_t<T>) {
1964
            GLZ_ASSERT_OWNS_ITS_BYTES(decltype(ctx));
1965
         }
1966
         value.str = {it_start, static_cast<size_t>(it - it_start)};
1967
      }
1968
   };
1969
1970
   template <class T>
1971
   struct from<JSON, basic_text<T>>
1972
   {
1973
      template <auto Opts>
1974
      GLZ_ALWAYS_INLINE static void op(auto&& value, is_context auto&& ctx, auto&& it, auto end)
1975
      {
1976
         if constexpr (string_view_t<T>) {
1977
            GLZ_ASSERT_OWNS_ITS_BYTES(decltype(ctx));
1978
         }
1979
         value.str = {it, static_cast<size_t>(end - it)}; // read entire contents as string
1980
         it = end;
1981
      }
1982
   };
1983
1984
   // for set types
1985
   template <class T>
1986
      requires(readable_array_t<T> && !emplace_backable<T> && !resizable<T> && emplaceable<T>)
1987
   struct from<JSON, T>
1988
   {
1989
      template <auto Options>
1990
      static void op(auto& value, is_context auto&& ctx, auto&& it, auto end)
1991
      {
1992
         constexpr auto Opts = ws_handled_off<Options>();
1993
         if constexpr (!check_ws_handled(Options)) {
1994
            if (skip_ws<Opts>(ctx, it, end)) {
1995
               return;
1996
            }
1997
         }
1998
1999
         if (match_invalid_end<'[', Opts>(ctx, it, end)) {
2000
            return;
2001
         }
2002
         // one nesting level (see enter_depth): counted in both modes so the limit binds,
2003
         // released at each syntactic close below
2004
         if (enter_depth(ctx)) [[unlikely]] {
2005
            return;
2006
         }
2007
         if (skip_ws<Opts>(ctx, it, end)) {
2008
            return;
2009
         }
2010
2011
         value.clear();
2012
         if (*it == ']') [[unlikely]] {
2013
            --ctx.depth;
2014
            ++it;
2015
            return;
2016
         }
2017
2018
         while (true) {
2019
            using V = range_value_t<T>;
2020
            V v;
2021
            parse<JSON>::op<Opts>(v, ctx, it, end);
2022
            if (bool(ctx.error)) [[unlikely]]
2023
               return;
2024
            value.emplace(std::move(v));
2025
            if (skip_ws<Opts>(ctx, it, end)) {
2026
               return;
2027
            }
2028
            if (*it == ']') {
2029
               --ctx.depth;
2030
               ++it;
2031
               return;
2032
            }
2033
            if (match_invalid_end<',', Opts>(ctx, it, end)) {
2034
               return;
2035
            }
2036
            if (skip_ws<Opts>(ctx, it, end)) {
2037
               return;
2038
            }
2039
         }
2040
      }
2041
   };
2042
2043
   // for types like std::vector, std::array, std::deque, etc.
2044
   template <class T>
2045
      requires(readable_array_t<T> && (emplace_backable<T> || is_inplace_vector<T> || !resizable<T>) && !emplaceable<T>)
2046
   struct from<JSON, T>
2047
   {
2048
      template <auto Options>
2049
      static void op(auto&& value, is_context auto&& ctx, auto&& it, auto end)
2050
      {
2051
         constexpr auto Opts = ws_handled_off<Options>();
2052
         if constexpr (!check_ws_handled(Options)) {
2053
            if (skip_ws<Opts>(ctx, it, end)) {
2054
               return;
2055
            }
2056
         }
2057
2058
         if (match_invalid_end<'[', Opts>(ctx, it, end)) {
2059
            return;
2060
         }
2061
         // one nesting level (see enter_depth): counted in both modes so the limit binds,
2062
         // released at each syntactic close below
2063
         if (enter_depth(ctx)) [[unlikely]] {
2064
            return;
2065
         }
2066
2067
         const auto ws_start = it;
2068
         if (skip_ws<Opts>(ctx, it, end)) {
2069
            return;
2070
         }
2071
2072
         if (*it == ']') {
2073
            --ctx.depth;
2074
            ++it;
2075
            if constexpr ((resizable<T> || is_inplace_vector<T>) && not check_append_arrays(Opts)) {
2076
               value.clear();
2077
2078
               if constexpr (check_shrink_to_fit(Opts) && has_shrink_to_fit<T>) {
2079
                  value.shrink_to_fit();
2080
               }
2081
            }
2082
            return;
2083
         }
2084
2085
         const size_t ws_size = size_t(it - ws_start);
2086
2087
         static constexpr bool should_append = (resizable<T> || is_inplace_vector<T>) && check_append_arrays(Opts);
2088
         if constexpr (not should_append) {
2089
            const auto n = value.size();
2090
2091
            auto value_it = value.begin();
2092
2093
            for (size_t i = 0; i < n; ++i) {
2094
               parse<JSON>::op<ws_handled<Opts>()>(*value_it++, ctx, it, end);
2095
               if (bool(ctx.error)) [[unlikely]]
2096
                  return;
2097
               if (skip_ws<Opts>(ctx, it, end)) {
2098
                  return;
2099
               }
2100
               if (*it == ',') {
2101
                  ++it;
2102
2103
                  if constexpr (!Opts.minified && !Opts.comments) {
2104
                     if (ws_size && ws_size < size_t(end - it)) {
2105
                        skip_matching_ws(ws_start, it, ws_size);
2106
                     }
2107
                  }
2108
2109
                  if (skip_ws<Opts>(ctx, it, end)) {
2110
                     return;
2111
                  }
2112
               }
2113
               else if (*it == ']') {
2114
                  --ctx.depth;
2115
                  ++it;
2116
                  if constexpr (erasable<T>) {
2117
                     value.erase(value_it,
2118
                                 value.end()); // use erase rather than resize for non-default constructible elements
2119
2120
                     if constexpr (check_shrink_to_fit(Opts) && has_shrink_to_fit<T>) {
2121
                        value.shrink_to_fit();
2122
                     }
2123
                  }
2124
                  return;
2125
               }
2126
               else [[unlikely]] {
2127
                  ctx.error = error_code::expected_bracket;
2128
                  return;
2129
               }
2130
            }
2131
         }
2132
2133
         if constexpr (Opts.partial_read) {
2134
            // partial_read stops early on a success path, so give the level back like the
2135
            // syntactic-close paths do; otherwise it accumulates across reads sharing a context.
2136
            --ctx.depth;
2137
            return;
2138
         }
2139
         else {
2140
            // growing
2141
            if constexpr (emplace_backable<T> || has_try_emplace_back<T>) {
2142
               while (it < end) {
2143
                  // Streaming refill point: at start of each iteration, ensure buffer has data
2144
                  if constexpr (has_streaming_state<decltype(ctx)>) {
2145
                     if (ctx.stream.enabled()) {
2146
                        const size_t consumed = static_cast<size_t>(it - ctx.stream.data());
2147
                        const size_t remaining = ctx.stream.size() - consumed;
2148
                        // Refill when less than half of buffer remains to ensure space for next element
2149
                        if (remaining <= ctx.stream.size() / 2 || it >= end) {
2150
                           const char* new_it;
2151
                           const char* new_end;
2152
                           ctx.stream.consume_and_refill(consumed, new_it, new_end);
2153
                           it = new_it;
2154
                           end = new_end;
2155
                           if (it >= end && ctx.stream.at_eof()) {
2156
                              break; // No more data, exit loop normally
2157
                           }
2158
                        }
2159
                     }
2160
                  }
2161
2162
                  if constexpr (has_try_emplace_back<T>) {
2163
                     if (value.try_emplace_back())
2164
                        parse<JSON>::op<ws_handled<Opts>()>(value.back(), ctx, it, end);
2165
                     else
2166
                        ctx.error = error_code::exceeded_static_array_size;
2167
                  }
2168
                  else {
2169
                     parse<JSON>::op<ws_handled<Opts>()>(value.emplace_back(), ctx, it, end);
2170
                  }
2171
2172
                  if (bool(ctx.error)) [[unlikely]]
2173
                     return;
2174
2175
                  // Streaming refill point: after parsing each element, refill if buffer is low
2176
                  if constexpr (has_streaming_state<decltype(ctx)>) {
2177
                     if (ctx.stream.enabled()) {
2178
                        const size_t consumed = static_cast<size_t>(it - ctx.stream.data());
2179
                        // Refill when less than 25% of buffer remains to ensure enough space for next element
2180
                        if (ctx.stream.size() - consumed <= ctx.stream.size() / 4 || it >= end) {
2181
                           const char* new_it;
2182
                           const char* new_end;
2183
                           ctx.stream.consume_and_refill(consumed, new_it, new_end);
2184
                           it = new_it;
2185
                           end = new_end;
2186
                           if (it >= end && ctx.stream.at_eof()) {
2187
                              ctx.error = error_code::unexpected_end;
2188
                              return;
2189
                           }
2190
                        }
2191
                     }
2192
                  }
2193
2194
                  if (skip_ws<Opts>(ctx, it, end)) {
2195
                     return;
2196
                  }
2197
                  if (*it == ',') [[likely]] {
2198
                     ++it;
2199
2200
                     if constexpr (!Opts.minified && !Opts.comments) {
2201
                        if (ws_size && ws_size < size_t(end - it)) {
2202
                           skip_matching_ws(ws_start, it, ws_size);
2203
                        }
2204
                     }
2205
2206
                     if (skip_ws<Opts>(ctx, it, end)) {
2207
                        return;
2208
                     }
2209
                  }
2210
                  else if (*it == ']') {
2211
                     --ctx.depth;
2212
                     ++it;
2213
                     if constexpr (check_shrink_to_fit(Opts) && has_shrink_to_fit<T>) {
2214
                        value.shrink_to_fit();
2215
                     }
2216
                     return;
2217
                  }
2218
                  else [[unlikely]] {
2219
                     ctx.error = error_code::expected_bracket;
2220
                     return;
2221
                  }
2222
               }
2223
2224
               // A valid array always returns from inside the loop upon reaching ']'.
2225
               // For a null-terminated buffer the loop can only exit here by falling
2226
               // through when `it` reaches the terminator without a closing bracket,
2227
               // which means the input was truncated (e.g. "[", "[1," or "[1,2,").
2228
               // Non-null-terminated buffers surface this through skip_ws/depth
2229
               // tracking (or the streaming refill break), so this guard is limited
2230
               // to the null-terminated case.
2231
               if constexpr (Opts.null_terminated) {
2232
                  ctx.error = error_code::unexpected_end;
2233
               }
2234
            }
2235
            else {
2236
               ctx.error = error_code::exceeded_static_array_size;
2237
            }
2238
         }
2239
      }
2240
2241
      // for types like std::vector<std::pair...> that can't look up with operator[]
2242
      // Instead of hashing or linear searching, we just clear the input and overwrite the entire contents
2243
      template <auto Options>
2244
         requires(pair_t<range_value_t<T>> && check_concatenate(Options) == true)
2245
      static void op(auto&& value, is_context auto&& ctx, auto&& it, auto end)
2246
      {
2247
         static constexpr auto Opts = opening_handled_off<ws_handled_off<Options>()>();
2248
         if constexpr (!check_opening_handled(Options)) {
2249
            if constexpr (!check_ws_handled(Options)) {
2250
               if (skip_ws<Opts>(ctx, it, end)) {
2251
                  return;
2252
               }
2253
            }
2254
            if (match_invalid_end<'{', Opts>(ctx, it, end)) {
2255
               return;
2256
            }
2257
            if constexpr (not Opts.null_terminated) {
2258
               if (it == end) [[unlikely]] {
2259
                  ctx.error = error_code::unexpected_end;
2260
                  return;
2261
               }
2262
            }
2263
            // one nesting level (see enter_depth): counted in both modes so the limit binds,
2264
            // released at each syntactic close below
2265
            if (enter_depth(ctx)) [[unlikely]] {
2266
               return;
2267
            }
2268
         }
2269
2270
         // clear all contents and repopulate
2271
         value.clear();
2272
2273
         while (it < end) {
2274
            if (skip_ws<Opts>(ctx, it, end)) {
2275
               return;
2276
            }
2277
2278
            if (*it == '}') {
2279
               ++it;
2280
               --ctx.depth;
2281
               if constexpr (not Opts.null_terminated) {
2282
                  if (it == end) {
2283
                     ctx.error = error_code::end_reached;
2284
                     return;
2285
                  }
2286
               }
2287
               return;
2288
            }
2289
2290
            if constexpr (has_try_emplace_back<T>) {
2291
               if (not value.try_emplace_back()) [[unlikely]] {
2292
                  ctx.error = error_code::exceeded_static_array_size;
2293
                  return;
2294
               }
2295
            }
2296
            else {
2297
               value.emplace_back();
2298
            }
2299
            auto& item = value.back();
2300
2301
            using V = std::decay_t<decltype(item)>;
2302
2303
            if constexpr (str_t<typename V::first_type> ||
2304
                          (std::is_enum_v<typename V::first_type> && glaze_t<typename V::first_type>) ||
2305
                          mimics_str_t<typename V::first_type>) {
2306
               parse<JSON>::op<Opts>(item.first, ctx, it, end);
2307
               if (bool(ctx.error)) [[unlikely]]
2308
                  return;
2309
            }
2310
            else {
2311
               std::string_view key;
2312
               parse<JSON>::op<Opts>(key, ctx, it, end);
2313
               if (bool(ctx.error)) [[unlikely]]
2314
                  return;
2315
               if constexpr (Opts.null_terminated) {
2316
                  parse<JSON>::op<Opts>(item.first, ctx, key.data(), key.data() + key.size());
2317
               }
2318
               else {
2319
                  if (size_t(end - it) == key.size()) [[unlikely]] {
2320
                     ctx.error = error_code::unexpected_end;
2321
                     return;
2322
                  }
2323
                  // For the non-null terminated case we just want one more character so that we don't parse
2324
                  // until the end of the buffer and create an end_reached code (unless there is an error).
2325
                  parse<JSON>::op<Opts>(item.first, ctx, key.data(), key.data() + key.size() + 1);
2326
               }
2327
               if (bool(ctx.error)) [[unlikely]]
2328
                  return;
2329
            }
2330
2331
            if (parse_ws_colon<Opts>(ctx, it, end)) {
2332
               return;
2333
            }
2334
2335
            parse<JSON>::op<Opts>(item.second, ctx, it, end);
2336
            if (bool(ctx.error)) [[unlikely]]
2337
               return;
2338
2339
            if (skip_ws<Opts>(ctx, it, end)) {
2340
               return;
2341
            }
2342
2343
            if (*it == ',') {
2344
               ++it;
2345
               if constexpr (not Opts.null_terminated) {
2346
                  if (it == end) [[unlikely]] {
2347
                     ctx.error = error_code::unexpected_end;
2348
                     return;
2349
                  }
2350
               }
2351
            }
2352
         }
2353
2354
         ctx.error = error_code::unexpected_end;
2355
      }
2356
   };
2357
2358
   // counts the number of JSON array elements
2359
   // needed for classes that are resizable, but do not have an emplace_back
2360
   // 'it' is copied so that it does not actually progress the iterator
2361
   // expects the opening brace ([) to have already been consumed
2362
   template <auto Opts>
2363
   [[nodiscard]] size_t number_of_array_elements(is_context auto&& ctx, auto it, auto end) noexcept
2364
   {
2365
      skip_ws<Opts>(ctx, it, end);
2366
      if (bool(ctx.error)) [[unlikely]]
2367
         return {};
2368
2369
      if constexpr (not Opts.null_terminated) {
2370
         if (it == end) [[unlikely]] {
2371
            ctx.error = error_code::unexpected_end;
2372
            return {};
2373
         }
2374
      }
2375
      if (*it == ']') [[unlikely]] {
2376
         return 0;
2377
      }
2378
      size_t count = 1;
2379
      while (true) {
2380
         // A null-terminated buffer falls out through the '\0' case below; a non-null-terminated
2381
         // buffer has no sentinel, so bound the scan here before dereferencing.
2382
         if constexpr (not Opts.null_terminated) {
2383
            if (it == end) [[unlikely]] {
2384
               ctx.error = error_code::unexpected_end;
2385
               return {};
2386
            }
2387
         }
2388
         switch (*it) {
2389
         case ',': {
2390
            ++count;
2391
            ++it;
2392
            break;
2393
         }
2394
         case '/': {
2395
            skip_comment(ctx, it, end);
2396
            if (bool(ctx.error)) [[unlikely]]
2397
               return {};
2398
            break;
2399
         }
2400
         case '{':
2401
            ++it;
2402
            skip_until_closed<Opts, '{', '}'>(ctx, it, end);
2403
            if (bool(ctx.error)) [[unlikely]]
2404
               return {};
2405
            break;
2406
         case '[':
2407
            ++it;
2408
            skip_until_closed<Opts, '[', ']'>(ctx, it, end);
2409
            if (bool(ctx.error)) [[unlikely]]
2410
               return {};
2411
            break;
2412
         case '"': {
2413
            skip_string<Opts>(ctx, it, end);
2414
            if (bool(ctx.error)) [[unlikely]]
2415
               return {};
2416
            break;
2417
         }
2418
         case ']': {
2419
            return count;
2420
         }
2421
         case '\0': {
2422
            ctx.error = error_code::unexpected_end;
2423
            return {};
2424
         }
2425
         default:
2426
            ++it;
2427
         }
2428
      }
2429
      std::unreachable();
2430
   }
2431
2432
   // For types like std::forward_list
2433
   template <class T>
2434
      requires readable_array_t<T> && (!emplace_backable<T> && resizable<T>)
2435
   struct from<JSON, T>
2436
   {
2437
      template <auto Options>
2438
      static void op(auto& value, is_context auto&& ctx, auto&& it, auto end)
2439
      {
2440
         constexpr auto Opts = ws_handled_off<Options>();
2441
         if constexpr (!check_ws_handled(Options)) {
2442
            if (skip_ws<Opts>(ctx, it, end)) {
2443
               return;
2444
            }
2445
         }
2446
2447
         if (match_invalid_end<'[', Opts>(ctx, it, end)) {
2448
            return;
2449
         }
2450
         // one nesting level (see enter_depth): counted in both modes so the limit binds,
2451
         // released at each syntactic close below
2452
         if (enter_depth(ctx)) [[unlikely]] {
2453
            return;
2454
         }
2455
         const auto n = number_of_array_elements<Opts>(ctx, it, end);
2456
         if (bool(ctx.error)) [[unlikely]]
2457
            return;
2458
         value.resize(n);
2459
         size_t i = 0;
2460
         for (auto& x : value) {
2461
            parse<JSON>::op<Opts>(x, ctx, it, end);
2462
            if (bool(ctx.error)) [[unlikely]]
2463
               return;
2464
2465
            if (skip_ws<Opts>(ctx, it, end)) {
2466
               return;
2467
            }
2468
            if (i < n - 1) {
2469
               if (match_invalid_end<',', Opts>(ctx, it, end)) {
2470
                  return;
2471
               }
2472
            }
2473
            ++i;
2474
         }
2475
         match<']'>(ctx, it);
2476
         --ctx.depth;
2477
      }
2478
   };
2479
2480
   template <class T>
2481
      requires glaze_array_t<T> || tuple_t<T> || is_std_tuple<T>
2482
   struct from<JSON, T>
2483
   {
2484
      template <auto Opts>
2485
      static void op(auto& value, is_context auto&& ctx, auto&& it, auto end)
2486
      {
2487
         static constexpr auto N = []() constexpr {
2488
            if constexpr (glaze_array_t<T>) {
2489
               return reflect<T>::size;
2490
            }
2491
            else {
2492
               return glz::tuple_size_v<T>;
2493
            }
2494
         }();
2495
2496
         if constexpr (!check_ws_handled(Opts)) {
2497
            if (skip_ws<Opts>(ctx, it, end)) {
2498
               return;
2499
            }
2500
         }
2501
2502
         if (match_invalid_end<'[', Opts>(ctx, it, end)) {
2503
            return;
2504
         }
2505
         // one nesting level (see enter_depth): counted in both modes so the limit binds,
2506
         // released at each syntactic close below
2507
         if (enter_depth(ctx)) [[unlikely]] {
2508
            return;
2509
         }
2510
         if (skip_ws<Opts>(ctx, it, end)) {
2511
            return;
2512
         }
2513
2514
         for_each_short_circuit<N>([&]<size_t I>() -> bool {
2515
            if (bool(ctx.error)) [[unlikely]]
2516
               return true;
2517
2518
            if (*it == ']') {
2519
               if constexpr (check_error_on_missing_array_elements(Opts)) {
2520
                  ctx.error = error_code::array_element_not_found;
2521
               }
2522
               return true;
2523
            }
2524
            if constexpr (I != 0) {
2525
               if (match_invalid_end<',', Opts>(ctx, it, end)) {
2526
                  return true;
2527
               }
2528
               if (skip_ws<Opts>(ctx, it, end)) {
2529
                  return true;
2530
               }
2531
            }
2532
            if constexpr (is_std_tuple<T>) {
2533
               parse<JSON>::op<ws_handled<Opts>()>(std::get<I>(value), ctx, it, end);
2534
               if (bool(ctx.error)) [[unlikely]]
2535
                  return true;
2536
            }
2537
            else if constexpr (glaze_array_t<T>) {
2538
               parse<JSON>::op<ws_handled<Opts>()>(get_member(value, glz::get<I>(meta_v<T>)), ctx, it, end);
2539
               if (bool(ctx.error)) [[unlikely]]
2540
                  return true;
2541
            }
2542
            else {
2543
               parse<JSON>::op<ws_handled<Opts>()>(glz::get<I>(value), ctx, it, end);
2544
               if (bool(ctx.error)) [[unlikely]]
2545
                  return true;
2546
            }
2547
            if (skip_ws<Opts>(ctx, it, end)) {
2548
               return true;
2549
            }
2550
            return false;
2551
         });
2552
2553
         if constexpr (Opts.partial_read) {
2554
            --ctx.depth; // as above: an early success still closes this level
2555
            return;
2556
         }
2557
         else {
2558
            if (bool(ctx.error)) [[unlikely]]
2559
               return;
2560
            match<']'>(ctx, it);
2561
            --ctx.depth;
2562
            if constexpr (not Opts.null_terminated) {
2563
               if (it == end) {
2564
                  ctx.error = error_code::end_reached;
2565
                  return;
2566
               }
2567
            }
2568
         }
2569
      }
2570
   };
2571
2572
   template <glaze_flags_t T>
2573
   struct from<JSON, T>
2574
   {
2575
      template <auto Opts>
2576
      static void op(auto&& value, is_context auto&& ctx, auto&& it, auto end)
2577
      {
2578
         if constexpr (!check_ws_handled(Opts)) {
2579
            if (skip_ws<Opts>(ctx, it, end)) {
2580
               return;
2581
            }
2582
         }
2583
2584
         if (match_invalid_end<'[', Opts>(ctx, it, end)) {
2585
            return;
2586
         }
2587
         // one nesting level (see enter_depth): counted in both modes so the limit binds,
2588
         // released at each syntactic close below
2589
         if (enter_depth(ctx)) [[unlikely]] {
2590
            return;
2591
         }
2592
2593
         constexpr auto& HashInfo = hash_info<T>;
2594
         static_assert(bool(HashInfo.type));
2595
2596
         while (true) {
2597
            parse<JSON>::op<ws_handled_off<Opts>()>(ctx.scratch, ctx, it, end);
2598
            if (bool(ctx.error)) [[unlikely]]
2599
               return;
2600
2601
            const auto index = decode_hash_with_size<JSON, T, HashInfo, HashInfo.type>::op(
2602
               ctx.scratch.data(), ctx.scratch.data() + ctx.scratch.size(), ctx.scratch.size());
2603
2604
            constexpr auto N = reflect<T>::size;
2605
            if (index < N) [[likely]] {
2606
               visit<N>([&]<size_t I>() { get_member(value, get<I>(reflect<T>::values)) = true; }, index);
2607
            }
2608
            else [[unlikely]] {
2609
               ctx.error = error_code::invalid_flag_input;
2610
               return;
2611
            }
2612
2613
            if (skip_ws<Opts>(ctx, it, end)) {
2614
               return;
2615
            }
2616
            if (*it == ']') {
2617
               --ctx.depth;
2618
               ++it;
2619
               if constexpr (not Opts.null_terminated) {
2620
                  if (it == end) {
2621
                     ctx.error = error_code::end_reached;
2622
                     return;
2623
                  }
2624
               }
2625
               return;
2626
            }
2627
            if (match_invalid_end<',', Opts>(ctx, it, end)) {
2628
               return;
2629
            }
2630
         }
2631
      }
2632
   };
2633
2634
   template <class T>
2635
   struct from<JSON, includer<T>>
2636
   {
2637
      template <auto Options>
2638
      static void op(auto&& value, is_context auto&& ctx, auto&& it, auto end)
2639
      {
2640
         constexpr auto Opts = ws_handled_off<Options>();
2641
         std::string buffer{};
2642
         parse<JSON>::op<Opts>(buffer, ctx, it, end);
2643
         if (bool(ctx.error)) [[unlikely]]
2644
            return;
2645
2646
         const auto file_path = relativize_if_not_absolute(std::filesystem::path(ctx.current_file).parent_path(),
2647
                                                           std::filesystem::path{buffer});
2648
2649
         const auto string_file_path = file_path.string();
2650
         const auto ec = file_to_buffer(buffer, string_file_path);
2651
2652
         if (bool(ec)) [[unlikely]] {
2653
            ctx.error = error_code::includer_error;
2654
            auto& error_msg = error_buffer();
2655
            error_msg = "file failed to open: " + string_file_path;
2656
            ctx.custom_error_message = error_msg;
2657
            return;
2658
         }
2659
2660
         auto outer_file = std::exchange(ctx.current_file, string_file_path);
2661
         const bool outer_padded = ctx.padded_input;
2662
2663
         // We need to allocate a new buffer here because we could call another includer that uses the buffer
2664
         std::string nested_buffer = buffer;
2665
         // is_padded is the caller's promise about their own buffer; this one was sized to the file.
2666
         static constexpr auto NestedOpts = opt_true<is_padded_off<Opts>(), &opts::null_terminated>;
2667
         // The included file fills in part of the object we belong to, so its keys count toward
2668
         // that object's missing key check rather than being required all over again here.
2669
         const include_key_scope include_keys{ctx, &include_key_tag<std::remove_cvref_t<decltype(value.value)>>};
2670
         const auto ecode = glz::read<NestedOpts>(value.value, nested_buffer, ctx);
2671
         // The nested read set these for the included file; the parse resumes over the caller's.
2672
         ctx.current_file = std::move(outer_file);
2673
         ctx.padded_input = outer_padded;
2674
         if (bool(ctx.error)) [[unlikely]] {
2675
            ctx.error = error_code::includer_error;
2676
            auto& error_msg = error_buffer();
2677
            error_msg = glz::format_error(ecode, nested_buffer);
2678
            ctx.custom_error_message = error_msg;
2679
            return;
2680
         }
2681
      }
2682
   };
2683
2684
   template <pair_t T>
2685
   struct from<JSON, T>
2686
   {
2687
      template <auto Options, string_literal tag = "">
2688
      static void op(T& value, is_context auto&& ctx, auto&& it, auto end)
2689
      {
2690
         constexpr auto Opts = opening_handled_off<ws_handled_off<Options>()>();
2691
         if constexpr (!check_opening_handled(Options)) {
2692
            if constexpr (!check_ws_handled(Options)) {
2693
               if (skip_ws<Opts>(ctx, it, end)) {
2694
                  return;
2695
               }
2696
            }
2697
            if (match_invalid_end<'{', Opts>(ctx, it, end)) {
2698
               return;
2699
            }
2700
            // one nesting level (see enter_depth): counted in both modes so the limit binds,
2701
            // released at each syntactic close below
2702
            if (enter_depth(ctx)) [[unlikely]] {
2703
               return;
2704
            }
2705
         }
2706
         if (skip_ws<Opts>(ctx, it, end)) {
2707
            return;
2708
         }
2709
2710
         if (*it == '}') {
2711
            --ctx.depth;
2712
            if constexpr (Opts.error_on_missing_keys) {
2713
               ctx.error = error_code::missing_key;
2714
            }
2715
            return;
2716
         }
2717
2718
         using first_type = typename T::first_type;
2719
         if constexpr (str_t<first_type> || is_named_enum<first_type> || mimics_str_t<first_type>) {
2720
            parse<JSON>::op<Opts>(value.first, ctx, it, end);
2721
            if (bool(ctx.error)) [[unlikely]]
2722
               return;
2723
         }
2724
         else {
2725
            std::string_view key;
2726
            parse<JSON>::op<Opts>(key, ctx, it, end);
2727
            if (bool(ctx.error)) [[unlikely]]
2728
               return;
2729
            if constexpr (Opts.null_terminated) {
2730
               parse<JSON>::op<Opts>(value.first, ctx, key.data(), key.data() + key.size());
2731
            }
2732
            else {
2733
               if (size_t(end - it) == key.size()) [[unlikely]] {
2734
                  ctx.error = error_code::unexpected_end;
2735
                  return;
2736
               }
2737
               // For the non-null terminated case we just want one more character so that we don't parse
2738
               // until the end of the buffer and create an end_reached code (unless there is an error).
2739
               parse<JSON>::op<Opts>(value.first, ctx, key.data(), key.data() + key.size() + 1);
2740
            }
2741
            if (bool(ctx.error)) [[unlikely]]
2742
               return;
2743
         }
2744
2745
         if (parse_ws_colon<Opts>(ctx, it, end)) {
2746
            return;
2747
         }
2748
2749
         parse<JSON>::op<Opts>(value.second, ctx, it, end);
2750
         if (bool(ctx.error)) [[unlikely]]
2751
            return;
2752
2753
         if (skip_ws<Opts>(ctx, it, end)) {
2754
            return;
2755
         }
2756
2757
         match<'}'>(ctx, it);
2758
         --ctx.depth;
2759
         if constexpr (not Opts.null_terminated) {
2760
            if (it == end) {
2761
               ctx.error = error_code::end_reached;
2762
               return;
2763
            }
2764
         }
2765
      }
2766
   };
2767
2768
   template <class T, string_literal Tag>
2769
   inline consteval bool contains_tag()
2770
   {
2771
      // Only object-like types expose reflect<T>::keys. Custom-read types (and other
2772
      // non-reflectable variant alternatives) have no fields, so the tag can never be
2773
      // one of their members and we must not touch reflect<T> (it may be incomplete).
2774
      if constexpr (requires { reflect<T>::keys; }) {
2775
         auto& keys = reflect<T>::keys;
2776
         for (size_t i = 0; i < keys.size(); ++i) {
2777
            if (Tag.sv() == keys[i]) {
2778
               return true;
2779
            }
2780
         }
2781
      }
2782
      return false;
2783
   }
2784
2785
   template <class T>
2786
      requires((readable_map_t<T> || glaze_object_t<T> || reflectable<T>) && not custom_read<T>)
2787
   struct from<JSON, T>
2788
   {
2789
      template <auto Options, string_literal tag = "">
2790
      static void op(auto&& value, is_context auto&& ctx, auto&& it, auto end)
2791
3.59k
      {
2792
3.59k
         static constexpr auto num_members = reflect<T>::size;
2793
2794
3.59k
         static constexpr auto Opts = opening_handled_off<ws_handled_off<Options>()>();
2795
3.59k
         if constexpr (!check_opening_handled(Options)) {
2796
3.59k
            if constexpr (!check_ws_handled(Options)) {
2797
3.59k
               if (skip_ws<Opts>(ctx, it, end)) {
2798
15
                  return;
2799
15
               }
2800
3.59k
            }
2801
3.59k
            if (match_invalid_end<'{', Opts>(ctx, it, end)) {
2802
480
               return;
2803
480
            }
2804
3.11k
            if constexpr (not Opts.null_terminated) {
2805
1.60k
               if (it == end) [[unlikely]] {
2806
0
                  ctx.error = error_code::unexpected_end;
2807
0
                  return;
2808
0
               }
2809
1.60k
            }
2810
            // one nesting level (see enter_depth): counted in both modes so the limit binds,
2811
            // released at each syntactic close below
2812
3.11k
            if (enter_depth(ctx)) [[unlikely]] {
2813
0
               return;
2814
0
            }
2815
3.11k
         }
2816
3.11k
         const auto ws_start = it;
2817
3.59k
         if (skip_ws<Opts>(ctx, it, end)) {
2818
8
            return;
2819
8
         }
2820
3.58k
         const size_t ws_size = size_t(it - ws_start);
2821
2822
         if constexpr ((glaze_object_t<T> || reflectable<T>) && num_members == 0 && Opts.error_on_unknown_keys) {
2823
            if constexpr (not tag.sv().empty()) {
2824
               if (*it == '"') {
2825
                  ++it;
2826
                  if constexpr (not Opts.null_terminated) {
2827
                     if (it == end) [[unlikely]] {
2828
                        ctx.error = error_code::unexpected_end;
2829
                        return;
2830
                     }
2831
                  }
2832
2833
                  const auto start = it;
2834
                  skip_string_view(ctx, it, end);
2835
                  if (bool(ctx.error)) [[unlikely]]
2836
                     return;
2837
                  if (validate_utf8_span<Opts>(ctx, start, it)) [[unlikely]]
2838
                     return;
2839
                  const sv key{start, size_t(it - start)};
2840
                  ++it;
2841
                  if constexpr (not Opts.null_terminated) {
2842
                     if (it == end) [[unlikely]] {
2843
                        ctx.error = error_code::unexpected_end;
2844
                        return;
2845
                     }
2846
                  }
2847
2848
                  if (key == tag.sv()) {
2849
                     if (parse_ws_colon<Opts>(ctx, it, end)) {
2850
                        return;
2851
                     }
2852
2853
                     parse<JSON>::handle_unknown<Opts>(key, value, ctx, it, end);
2854
                     if (bool(ctx.error)) [[unlikely]]
2855
                        return;
2856
2857
                     if (skip_ws<Opts>(ctx, it, end)) {
2858
                        return;
2859
                     }
2860
                  }
2861
                  else {
2862
                     ctx.error = error_code::unknown_key;
2863
                     return;
2864
                  }
2865
               }
2866
            }
2867
2868
            if (*it == '}') [[likely]] {
2869
               --ctx.depth;
2870
               if constexpr (glaze_object_t<T> || reflectable<T>) {
2871
                  if constexpr (has_self_constraint_v<T> && !check_skip_self_constraint(Opts)) {
2872
                     auto wrapper = self_constraint_v<T>(value);
2873
                     from<JSON, decltype(wrapper)>::template op<ws_handled<Opts>()>(wrapper, ctx, it, end);
2874
                     if (bool(ctx.error)) {
2875
                        return;
2876
                     }
2877
                  }
2878
               }
2879
               ++it;
2880
               if constexpr (not Opts.null_terminated) {
2881
                  if (it == end) {
2882
                     ctx.error = error_code::end_reached;
2883
                     return;
2884
                  }
2885
               }
2886
               if constexpr (Opts.partial_read) {
2887
                  ctx.error = error_code::partial_read_complete;
2888
               }
2889
               return;
2890
            }
2891
            ctx.error = error_code::unknown_key;
2892
            return;
2893
         }
2894
3.58k
         else {
2895
3.58k
            decltype(auto) fields = [&]() -> decltype(auto) {
2896
               if constexpr ((glaze_object_t<T> || reflectable<T>) &&
2897
                             (Opts.error_on_missing_keys || Opts.partial_read)) {
2898
                  return bit_array<num_members>{};
2899
               }
2900
3.09k
               else {
2901
3.09k
                  return nullptr;
2902
3.09k
               }
2903
3.09k
            }();
_ZZN3glz4fromILj10E6PersonE2opITnDaXtlNS_4optsELj10ELb1ELb0ELb1ELb1EEETnNS_14string_literalEXtlNS5_ILm1EEEEERS1_TkNS_10is_contextERNS_7contextERPKcSB_EEvOT1_OT2_OT3_T4_ENKUlvE_clEv
Line
Count
Source
2895
1.51k
            decltype(auto) fields = [&]() -> decltype(auto) {
2896
               if constexpr ((glaze_object_t<T> || reflectable<T>) &&
2897
                             (Opts.error_on_missing_keys || Opts.partial_read)) {
2898
                  return bit_array<num_members>{};
2899
               }
2900
1.51k
               else {
2901
1.51k
                  return nullptr;
2902
1.51k
               }
2903
1.51k
            }();
_ZZN3glz4fromILj10E6PersonE2opITnDaXtlNS_4optsELj10ELb0ELb0ELb1ELb1EEETnNS_14string_literalEXtlNS5_ILm1EEEEERS1_TkNS_10is_contextERNS_7contextERPKcSB_EEvOT1_OT2_OT3_T4_ENKUlvE_clEv
Line
Count
Source
2895
1.57k
            decltype(auto) fields = [&]() -> decltype(auto) {
2896
               if constexpr ((glaze_object_t<T> || reflectable<T>) &&
2897
                             (Opts.error_on_missing_keys || Opts.partial_read)) {
2898
                  return bit_array<num_members>{};
2899
               }
2900
1.57k
               else {
2901
1.57k
                  return nullptr;
2902
1.57k
               }
2903
1.57k
            }();
2904
2905
            // A file include merges an external document into this object, so with
2906
            // error_on_missing_keys the keys have to be counted across both documents (see
2907
            // context::key_bits). While this object parses, its bits are published for its
2908
            // includer members to hand down; and if this parse is itself an included document,
2909
            // it claims the bits of the object that included us and merges into them at the
2910
            // closing brace instead of running a check of its own.
2911
3.58k
            static constexpr bool tracks_include_keys = [] {
2912
               // Nested so that reflecting over the members is only asked for when it can matter
2913
3.58k
               if constexpr ((glaze_object_t<T> || reflectable<T>) && Opts.error_on_missing_keys) {
2914
3.58k
                  return has_includer_member<T>;
2915
3.58k
               }
2916
3.58k
               else {
2917
3.58k
                  return false;
2918
3.58k
               }
2919
3.58k
            }();
2920
3.58k
            [[maybe_unused]] bit_array<num_members>* include_bits{};
2921
            if constexpr (tracks_include_keys) {
2922
               if (ctx.include_key_type == &include_key_tag<T>) {
2923
                  include_bits = static_cast<bit_array<num_members>*>(ctx.include_key_bits);
2924
                  ctx.include_key_bits = nullptr;
2925
                  ctx.include_key_type = nullptr;
2926
               }
2927
            }
2928
3.58k
            [[maybe_unused]] const std::conditional_t<tracks_include_keys,
2929
3.58k
                                                      key_bits_scope<std::remove_reference_t<decltype(ctx)>>,
2930
3.58k
                                                      inert_key_bits_scope> published_bits{
2931
3.58k
               ctx, [&]() -> void* {
2932
                  if constexpr (tracks_include_keys) {
2933
                     return &fields;
2934
                  }
2935
3.09k
                  else {
2936
3.09k
                     return nullptr;
2937
3.09k
                  }
2938
3.09k
               }()};
_ZZN3glz4fromILj10E6PersonE2opITnDaXtlNS_4optsELj10ELb1ELb0ELb1ELb1EEETnNS_14string_literalEXtlNS5_ILm1EEEEERS1_TkNS_10is_contextERNS_7contextERPKcSB_EEvOT1_OT2_OT3_T4_ENKUlvE1_clEv
Line
Count
Source
2931
1.51k
               ctx, [&]() -> void* {
2932
                  if constexpr (tracks_include_keys) {
2933
                     return &fields;
2934
                  }
2935
1.51k
                  else {
2936
1.51k
                     return nullptr;
2937
1.51k
                  }
2938
1.51k
               }()};
_ZZN3glz4fromILj10E6PersonE2opITnDaXtlNS_4optsELj10ELb0ELb0ELb1ELb1EEETnNS_14string_literalEXtlNS5_ILm1EEEEERS1_TkNS_10is_contextERNS_7contextERPKcSB_EEvOT1_OT2_OT3_T4_ENKUlvE1_clEv
Line
Count
Source
2931
1.57k
               ctx, [&]() -> void* {
2932
                  if constexpr (tracks_include_keys) {
2933
                     return &fields;
2934
                  }
2935
1.57k
                  else {
2936
1.57k
                     return nullptr;
2937
1.57k
                  }
2938
1.57k
               }()};
2939
2940
3.58k
            size_t read_count{}; // for partial_read
2941
2942
3.58k
            bool first = true;
2943
25.0k
            while (true) {
2944
               if constexpr ((glaze_object_t<T> || reflectable<T>) && Opts.partial_read) {
2945
                  static constexpr bit_array<num_members> all_fields = [] {
2946
                     bit_array<num_members> arr{};
2947
                     for (size_t i = 0; i < num_members; ++i) {
2948
                        arr[i] = true;
2949
                     }
2950
                     return arr;
2951
                  }();
2952
2953
                  if ((all_fields & fields) == all_fields) {
2954
                     ctx.error = error_code::partial_read_complete;
2955
                     --ctx.depth; // as above: an early success still closes this level
2956
                     return;
2957
                  }
2958
               }
2959
2960
               // Streaming refill point: at start of each iteration, ensure buffer has data
2961
               if constexpr (has_streaming_state<decltype(ctx)>) {
2962
                  if (ctx.stream.enabled()) {
2963
                     const size_t consumed = static_cast<size_t>(it - ctx.stream.data());
2964
                     const size_t remaining = ctx.stream.size() - consumed;
2965
                     // Refill when less than half of buffer remains to ensure space for next key-value pair
2966
                     if (remaining <= ctx.stream.size() / 2 || it >= end) {
2967
                        const char* new_it;
2968
                        const char* new_end;
2969
                        ctx.stream.consume_and_refill(consumed, new_it, new_end);
2970
                        it = new_it;
2971
                        end = new_end;
2972
                        if (it >= end && ctx.stream.at_eof()) {
2973
                           ctx.error = error_code::unexpected_end;
2974
                           return;
2975
                        }
2976
                     }
2977
                  }
2978
               }
2979
2980
22.2k
               if (*it == '}') {
2981
24
                  --ctx.depth;
2982
                  if constexpr ((glaze_object_t<T> || reflectable<T>) && Opts.error_on_missing_keys) {
2983
                     const bool defer_to_includer = [&] {
2984
                        if constexpr (tracks_include_keys) {
2985
                           if (include_bits) {
2986
                              // An included document is a fragment: hand the keys it supplied to
2987
                              // the object that included it, which checks the union of both.
2988
                              *include_bits |= fields;
2989
                              return true;
2990
                           }
2991
                        }
2992
                        return false;
2993
                     }();
2994
                     constexpr auto req_fields = required_fields<T, Opts>();
2995
                     if (not defer_to_includer && (req_fields & fields) != req_fields) {
2996
                        for (size_t i = 0; i < num_members; ++i) {
2997
                           if (not fields[i] && req_fields[i]) {
2998
                              ctx.custom_error_message = reflect<T>::keys[i];
2999
                              // We just return the first missing key in order to avoid heap allocations
3000
                              break;
3001
                           }
3002
                        }
3003
3004
                        ctx.error = error_code::missing_key;
3005
                        return;
3006
                     }
3007
                  }
3008
24
                  if constexpr (glaze_object_t<T> || reflectable<T>) {
3009
                     if constexpr (has_self_constraint_v<T> && !check_skip_self_constraint(Opts)) {
3010
                        auto wrapper = self_constraint_v<T>(value);
3011
                        from<JSON, decltype(wrapper)>::template op<ws_handled<Opts>()>(wrapper, ctx, it, end);
3012
                        if (bool(ctx.error)) {
3013
                           return;
3014
                        }
3015
                     }
3016
24
                  }
3017
24
                  ++it; // Increment after checking for missing keys so errors are within buffer bounds
3018
24
                  if constexpr (not Opts.null_terminated) {
3019
13
                     if (it == end) {
3020
3
                        ctx.error = error_code::end_reached;
3021
3
                        return;
3022
3
                     }
3023
13
                  }
3024
10
                  return;
3025
24
               }
3026
22.2k
               else if (first) {
3027
3.06k
                  first = false;
3028
3.06k
               }
3029
19.1k
               else {
3030
19.1k
                  if (match_invalid_end<',', Opts>(ctx, it, end)) {
3031
585
                     return;
3032
585
                  }
3033
18.5k
                  if constexpr (not Opts.null_terminated) {
3034
8.95k
                     if (it == end) [[unlikely]] {
3035
0
                        ctx.error = error_code::unexpected_end;
3036
0
                        return;
3037
0
                     }
3038
8.95k
                  }
3039
3040
                  if constexpr ((not Opts.minified) && (num_members > 1 || not Opts.error_on_unknown_keys) &&
3041
18.5k
                                (!Opts.comments)) {
3042
18.5k
                     if (ws_size && ws_size < size_t(end - it)) {
3043
5.38k
                        skip_matching_ws(ws_start, it, ws_size);
3044
5.38k
                     }
3045
18.5k
                  }
3046
3047
18.5k
                  if (skip_ws<Opts>(ctx, it, end)) {
3048
34
                     return;
3049
34
                  }
3050
18.5k
               }
3051
3052
21.6k
               constexpr auto reflection_type = glaze_object_t<T> || reflectable<T>;
3053
3054
               if constexpr (reflection_type && (num_members == 0)) {
3055
                  if constexpr (Opts.error_on_unknown_keys) {
3056
                     static_assert(false_v<T>, "This should be unreachable");
3057
                  }
3058
                  else {
3059
                     if (match_invalid_end<'"', Opts>(ctx, it, end)) {
3060
                        return;
3061
                     }
3062
3063
                     // parsing to an empty object, but at this point the JSON presents keys
3064
3065
                     // Unknown key handler does not unescape keys. Unknown escaped keys are
3066
                     // handled by the user.
3067
3068
                     const auto start = it;
3069
                     uint64_t ascii_acc{};
3070
                     skip_string_view(ctx, it, end, ascii_acc);
3071
                     if (bool(ctx.error)) [[unlikely]]
3072
                        return;
3073
                     if (validate_utf8_span<Opts>(ctx, start, it, ascii_acc)) [[unlikely]]
3074
                        return;
3075
                     const sv key{start, size_t(it - start)};
3076
                     ++it;
3077
                     if constexpr (not Opts.null_terminated) {
3078
                        if (it == end) [[unlikely]] {
3079
                           ctx.error = error_code::unexpected_end;
3080
                           return;
3081
                        }
3082
                     }
3083
3084
                     if (parse_ws_colon<Opts>(ctx, it, end)) {
3085
                        return;
3086
                     }
3087
3088
                     // Check if this key is the variant tag that should be skipped
3089
                     if constexpr (not tag.sv().empty()) {
3090
                        if (key == tag.sv()) {
3091
                           // Skip the tag value
3092
                           skip_value<JSON>::op<Opts>(ctx, it, end);
3093
                           if (bool(ctx.error)) [[unlikely]]
3094
                              return;
3095
                           resync_window_end(ctx, end); // a streaming skip refills
3096
                           if (skip_ws<Opts>(ctx, it, end)) {
3097
                              return;
3098
                           }
3099
                           continue;
3100
                        }
3101
                     }
3102
3103
                     parse<JSON>::handle_unknown<Opts>(key, value, ctx, it, end);
3104
                     if (bool(ctx.error)) [[unlikely]]
3105
                        return;
3106
                     if constexpr (not Opts.null_terminated) {
3107
                        if (it == end) [[unlikely]] {
3108
                           ctx.error = error_code::unexpected_end;
3109
                           return;
3110
                        }
3111
                     }
3112
                     if (skip_ws<Opts>(ctx, it, end)) {
3113
                        return;
3114
                     }
3115
                  }
3116
               }
3117
21.6k
               else if constexpr (reflection_type) {
3118
21.6k
                  static_assert(bool(hash_info<T>.type));
3119
3120
21.6k
                  if (*it != '"') [[unlikely]] {
3121
654
                     ctx.error = error_code::expected_quote;
3122
654
                     return;
3123
654
                  }
3124
20.9k
                  ++it;
3125
20.9k
                  if constexpr (not Opts.null_terminated) {
3126
10.1k
                     if (it == end) [[unlikely]] {
3127
16
                        ctx.error = error_code::unexpected_end;
3128
16
                        return;
3129
16
                     }
3130
10.1k
                  }
3131
3132
                  if constexpr (not tag.sv().empty() && not contains_tag<T, tag>()) {
3133
                     // For tagged variants we first check to see if the key matches the tag
3134
                     // We only need to do this if the tag is not part of the keys
3135
3136
                     const auto start = it;
3137
                     uint64_t ascii_acc{};
3138
                     skip_string_view(ctx, it, end, ascii_acc);
3139
                     if (bool(ctx.error)) [[unlikely]]
3140
                        return;
3141
                     if (validate_utf8_span<Opts>(ctx, start, it, ascii_acc)) [[unlikely]]
3142
                        return;
3143
                     const sv key{start, size_t(it - start)};
3144
                     ++it;
3145
                     if constexpr (not Opts.null_terminated) {
3146
                        if (it == end) [[unlikely]] {
3147
                           ctx.error = error_code::unexpected_end;
3148
                           return;
3149
                        }
3150
                     }
3151
3152
                     if (key == tag.sv()) {
3153
                        if (parse_ws_colon<Opts>(ctx, it, end)) {
3154
                           return;
3155
                        }
3156
3157
                        parse<JSON>::handle_unknown<Opts>(key, value, ctx, it, end);
3158
                        if (bool(ctx.error)) [[unlikely]]
3159
                           return;
3160
3161
                        if (skip_ws<Opts>(ctx, it, end)) {
3162
                           return;
3163
                        }
3164
                        continue;
3165
                     }
3166
                     else {
3167
                        it = start; // reset the iterator
3168
                     }
3169
                  }
3170
3171
                  if constexpr (Opts.error_on_missing_keys || Opts.partial_read) {
3172
                     size_t index = num_members;
3173
                     parse_and_invoke<Opts, T>(value, ctx, it, end, index);
3174
                     if (bool(ctx.error)) [[unlikely]]
3175
                        return;
3176
                     if (index < num_members) {
3177
                        fields[index] = true;
3178
                     }
3179
                  }
3180
20.9k
                  else {
3181
20.9k
                     parse_and_invoke<Opts, T>(value, ctx, it, end);
3182
20.9k
                     if (bool(ctx.error)) [[unlikely]]
3183
1.61k
                        return;
3184
20.9k
                  }
3185
               }
3186
               else {
3187
                  // For types like std::map, std::unordered_map
3188
3189
                  auto reading = [&](auto&& key) {
3190
                     if constexpr (Opts.partial_read) {
3191
                        if (auto element = value.find(key); element != value.end()) {
3192
                           ++read_count;
3193
                           parse<JSON>::op<ws_handled<Opts>()>(element->second, ctx, it, end);
3194
                        }
3195
                        else {
3196
                           skip_value<JSON>::op<Opts>(ctx, it, end);
3197
                        }
3198
                     }
3199
                     else {
3200
                        parse<JSON>::op<ws_handled<Opts>()>(value[key], ctx, it, end);
3201
                     }
3202
                  };
3203
3204
                  // using Key = std::conditional_t<heterogeneous_map<T>, sv, typename T::key_type>;
3205
                  using Key = typename T::key_type;
3206
                  if constexpr (std::is_same_v<Key, std::string>) {
3207
                     ctx.scratch.clear();
3208
                     parse<JSON>::op<Opts>(ctx.scratch, ctx, it, end);
3209
                     if (bool(ctx.error)) [[unlikely]]
3210
                        return;
3211
3212
                     if (parse_ws_colon<Opts>(ctx, it, end)) {
3213
                        return;
3214
                     }
3215
3216
                     reading(ctx.scratch);
3217
                     if constexpr (Opts.partial_read) {
3218
                        if (read_count == value.size()) {
3219
                           return;
3220
                        }
3221
                     }
3222
                     if (bool(ctx.error)) [[unlikely]]
3223
                        return;
3224
                  }
3225
                  else if constexpr (str_t<Key>) {
3226
                     Key key;
3227
                     parse<JSON>::op<Opts>(key, ctx, it, end);
3228
                     if (bool(ctx.error)) [[unlikely]]
3229
                        return;
3230
3231
                     if (parse_ws_colon<Opts>(ctx, it, end)) {
3232
                        return;
3233
                     }
3234
3235
                     reading(key);
3236
                     if constexpr (Opts.partial_read) {
3237
                        if (read_count == value.size()) {
3238
                           return;
3239
                        }
3240
                     }
3241
                     if (bool(ctx.error)) [[unlikely]]
3242
                        return;
3243
                  }
3244
                  else {
3245
                     Key key_value{};
3246
                     if constexpr (is_named_enum<Key> || mimics_str_t<Key>) {
3247
                        parse<JSON>::op<Opts>(key_value, ctx, it, end);
3248
                     }
3249
                     else if constexpr (std::is_arithmetic_v<Key>) {
3250
                        // prefer over quoted_t below to avoid double parsing of quoted_t
3251
                        parse<JSON>::op<opt_true<Opts, quoted_num_opt_tag{}>>(key_value, ctx, it, end);
3252
                     }
3253
                     else {
3254
                        parse<JSON>::op<opt_false<Opts, raw_string_opt_tag{}>>(quoted_t<Key>{key_value}, ctx, it, end);
3255
                     }
3256
                     if (bool(ctx.error)) [[unlikely]]
3257
                        return;
3258
3259
                     if (parse_ws_colon<Opts>(ctx, it, end)) {
3260
                        return;
3261
                     }
3262
3263
                     reading(key_value);
3264
                     if constexpr (Opts.partial_read) {
3265
                        if (read_count == value.size()) {
3266
                           return;
3267
                        }
3268
                     }
3269
                     if (bool(ctx.error)) [[unlikely]]
3270
                        return;
3271
                  }
3272
               }
3273
3274
               // Streaming refill point: after parsing each key-value pair, refill if buffer is low
3275
               if constexpr (has_streaming_state<decltype(ctx)>) {
3276
                  if (ctx.stream.enabled()) {
3277
                     const size_t consumed = static_cast<size_t>(it - ctx.stream.data());
3278
                     // Refill when less than 25% of buffer remains to ensure enough space for next element
3279
                     if (ctx.stream.size() - consumed <= ctx.stream.size() / 4 || it >= end) {
3280
                        const char* new_it;
3281
                        const char* new_end;
3282
                        ctx.stream.consume_and_refill(consumed, new_it, new_end);
3283
                        it = new_it;
3284
                        end = new_end;
3285
                        if (it >= end && ctx.stream.at_eof()) {
3286
                           ctx.error = error_code::unexpected_end;
3287
                           return;
3288
                        }
3289
                     }
3290
                  }
3291
               }
3292
3293
21.6k
               if (skip_ws<Opts>(ctx, it, end)) {
3294
163
                  return;
3295
163
               }
3296
21.6k
            }
3297
3.58k
         }
3298
3.58k
      }
_ZN3glz4fromILj10E6PersonE2opITnDaXtlNS_4optsELj10ELb1ELb0ELb1ELb1EEETnNS_14string_literalEXtlNS5_ILm1EEEEERS1_TkNS_10is_contextERNS_7contextERPKcSB_EEvOT1_OT2_OT3_T4_
Line
Count
Source
2791
1.76k
      {
2792
1.76k
         static constexpr auto num_members = reflect<T>::size;
2793
2794
1.76k
         static constexpr auto Opts = opening_handled_off<ws_handled_off<Options>()>();
2795
1.76k
         if constexpr (!check_opening_handled(Options)) {
2796
1.76k
            if constexpr (!check_ws_handled(Options)) {
2797
1.76k
               if (skip_ws<Opts>(ctx, it, end)) {
2798
0
                  return;
2799
0
               }
2800
1.76k
            }
2801
1.76k
            if (match_invalid_end<'{', Opts>(ctx, it, end)) {
2802
253
               return;
2803
253
            }
2804
            if constexpr (not Opts.null_terminated) {
2805
               if (it == end) [[unlikely]] {
2806
                  ctx.error = error_code::unexpected_end;
2807
                  return;
2808
               }
2809
            }
2810
            // one nesting level (see enter_depth): counted in both modes so the limit binds,
2811
            // released at each syntactic close below
2812
1.51k
            if (enter_depth(ctx)) [[unlikely]] {
2813
0
               return;
2814
0
            }
2815
1.51k
         }
2816
1.51k
         const auto ws_start = it;
2817
1.76k
         if (skip_ws<Opts>(ctx, it, end)) {
2818
0
            return;
2819
0
         }
2820
1.76k
         const size_t ws_size = size_t(it - ws_start);
2821
2822
         if constexpr ((glaze_object_t<T> || reflectable<T>) && num_members == 0 && Opts.error_on_unknown_keys) {
2823
            if constexpr (not tag.sv().empty()) {
2824
               if (*it == '"') {
2825
                  ++it;
2826
                  if constexpr (not Opts.null_terminated) {
2827
                     if (it == end) [[unlikely]] {
2828
                        ctx.error = error_code::unexpected_end;
2829
                        return;
2830
                     }
2831
                  }
2832
2833
                  const auto start = it;
2834
                  skip_string_view(ctx, it, end);
2835
                  if (bool(ctx.error)) [[unlikely]]
2836
                     return;
2837
                  if (validate_utf8_span<Opts>(ctx, start, it)) [[unlikely]]
2838
                     return;
2839
                  const sv key{start, size_t(it - start)};
2840
                  ++it;
2841
                  if constexpr (not Opts.null_terminated) {
2842
                     if (it == end) [[unlikely]] {
2843
                        ctx.error = error_code::unexpected_end;
2844
                        return;
2845
                     }
2846
                  }
2847
2848
                  if (key == tag.sv()) {
2849
                     if (parse_ws_colon<Opts>(ctx, it, end)) {
2850
                        return;
2851
                     }
2852
2853
                     parse<JSON>::handle_unknown<Opts>(key, value, ctx, it, end);
2854
                     if (bool(ctx.error)) [[unlikely]]
2855
                        return;
2856
2857
                     if (skip_ws<Opts>(ctx, it, end)) {
2858
                        return;
2859
                     }
2860
                  }
2861
                  else {
2862
                     ctx.error = error_code::unknown_key;
2863
                     return;
2864
                  }
2865
               }
2866
            }
2867
2868
            if (*it == '}') [[likely]] {
2869
               --ctx.depth;
2870
               if constexpr (glaze_object_t<T> || reflectable<T>) {
2871
                  if constexpr (has_self_constraint_v<T> && !check_skip_self_constraint(Opts)) {
2872
                     auto wrapper = self_constraint_v<T>(value);
2873
                     from<JSON, decltype(wrapper)>::template op<ws_handled<Opts>()>(wrapper, ctx, it, end);
2874
                     if (bool(ctx.error)) {
2875
                        return;
2876
                     }
2877
                  }
2878
               }
2879
               ++it;
2880
               if constexpr (not Opts.null_terminated) {
2881
                  if (it == end) {
2882
                     ctx.error = error_code::end_reached;
2883
                     return;
2884
                  }
2885
               }
2886
               if constexpr (Opts.partial_read) {
2887
                  ctx.error = error_code::partial_read_complete;
2888
               }
2889
               return;
2890
            }
2891
            ctx.error = error_code::unknown_key;
2892
            return;
2893
         }
2894
1.76k
         else {
2895
1.76k
            decltype(auto) fields = [&]() -> decltype(auto) {
2896
1.76k
               if constexpr ((glaze_object_t<T> || reflectable<T>) &&
2897
1.76k
                             (Opts.error_on_missing_keys || Opts.partial_read)) {
2898
1.76k
                  return bit_array<num_members>{};
2899
1.76k
               }
2900
1.76k
               else {
2901
1.76k
                  return nullptr;
2902
1.76k
               }
2903
1.76k
            }();
2904
2905
            // A file include merges an external document into this object, so with
2906
            // error_on_missing_keys the keys have to be counted across both documents (see
2907
            // context::key_bits). While this object parses, its bits are published for its
2908
            // includer members to hand down; and if this parse is itself an included document,
2909
            // it claims the bits of the object that included us and merges into them at the
2910
            // closing brace instead of running a check of its own.
2911
1.76k
            static constexpr bool tracks_include_keys = [] {
2912
               // Nested so that reflecting over the members is only asked for when it can matter
2913
1.76k
               if constexpr ((glaze_object_t<T> || reflectable<T>) && Opts.error_on_missing_keys) {
2914
1.76k
                  return has_includer_member<T>;
2915
1.76k
               }
2916
1.76k
               else {
2917
1.76k
                  return false;
2918
1.76k
               }
2919
1.76k
            }();
2920
1.76k
            [[maybe_unused]] bit_array<num_members>* include_bits{};
2921
            if constexpr (tracks_include_keys) {
2922
               if (ctx.include_key_type == &include_key_tag<T>) {
2923
                  include_bits = static_cast<bit_array<num_members>*>(ctx.include_key_bits);
2924
                  ctx.include_key_bits = nullptr;
2925
                  ctx.include_key_type = nullptr;
2926
               }
2927
            }
2928
1.76k
            [[maybe_unused]] const std::conditional_t<tracks_include_keys,
2929
1.76k
                                                      key_bits_scope<std::remove_reference_t<decltype(ctx)>>,
2930
1.76k
                                                      inert_key_bits_scope> published_bits{
2931
1.76k
               ctx, [&]() -> void* {
2932
1.76k
                  if constexpr (tracks_include_keys) {
2933
1.76k
                     return &fields;
2934
1.76k
                  }
2935
1.76k
                  else {
2936
1.76k
                     return nullptr;
2937
1.76k
                  }
2938
1.76k
               }()};
2939
2940
1.76k
            size_t read_count{}; // for partial_read
2941
2942
1.76k
            bool first = true;
2943
12.9k
            while (true) {
2944
               if constexpr ((glaze_object_t<T> || reflectable<T>) && Opts.partial_read) {
2945
                  static constexpr bit_array<num_members> all_fields = [] {
2946
                     bit_array<num_members> arr{};
2947
                     for (size_t i = 0; i < num_members; ++i) {
2948
                        arr[i] = true;
2949
                     }
2950
                     return arr;
2951
                  }();
2952
2953
                  if ((all_fields & fields) == all_fields) {
2954
                     ctx.error = error_code::partial_read_complete;
2955
                     --ctx.depth; // as above: an early success still closes this level
2956
                     return;
2957
                  }
2958
               }
2959
2960
               // Streaming refill point: at start of each iteration, ensure buffer has data
2961
               if constexpr (has_streaming_state<decltype(ctx)>) {
2962
                  if (ctx.stream.enabled()) {
2963
                     const size_t consumed = static_cast<size_t>(it - ctx.stream.data());
2964
                     const size_t remaining = ctx.stream.size() - consumed;
2965
                     // Refill when less than half of buffer remains to ensure space for next key-value pair
2966
                     if (remaining <= ctx.stream.size() / 2 || it >= end) {
2967
                        const char* new_it;
2968
                        const char* new_end;
2969
                        ctx.stream.consume_and_refill(consumed, new_it, new_end);
2970
                        it = new_it;
2971
                        end = new_end;
2972
                        if (it >= end && ctx.stream.at_eof()) {
2973
                           ctx.error = error_code::unexpected_end;
2974
                           return;
2975
                        }
2976
                     }
2977
                  }
2978
               }
2979
2980
11.5k
               if (*it == '}') {
2981
11
                  --ctx.depth;
2982
                  if constexpr ((glaze_object_t<T> || reflectable<T>) && Opts.error_on_missing_keys) {
2983
                     const bool defer_to_includer = [&] {
2984
                        if constexpr (tracks_include_keys) {
2985
                           if (include_bits) {
2986
                              // An included document is a fragment: hand the keys it supplied to
2987
                              // the object that included it, which checks the union of both.
2988
                              *include_bits |= fields;
2989
                              return true;
2990
                           }
2991
                        }
2992
                        return false;
2993
                     }();
2994
                     constexpr auto req_fields = required_fields<T, Opts>();
2995
                     if (not defer_to_includer && (req_fields & fields) != req_fields) {
2996
                        for (size_t i = 0; i < num_members; ++i) {
2997
                           if (not fields[i] && req_fields[i]) {
2998
                              ctx.custom_error_message = reflect<T>::keys[i];
2999
                              // We just return the first missing key in order to avoid heap allocations
3000
                              break;
3001
                           }
3002
                        }
3003
3004
                        ctx.error = error_code::missing_key;
3005
                        return;
3006
                     }
3007
                  }
3008
11
                  if constexpr (glaze_object_t<T> || reflectable<T>) {
3009
                     if constexpr (has_self_constraint_v<T> && !check_skip_self_constraint(Opts)) {
3010
                        auto wrapper = self_constraint_v<T>(value);
3011
                        from<JSON, decltype(wrapper)>::template op<ws_handled<Opts>()>(wrapper, ctx, it, end);
3012
                        if (bool(ctx.error)) {
3013
                           return;
3014
                        }
3015
                     }
3016
11
                  }
3017
11
                  ++it; // Increment after checking for missing keys so errors are within buffer bounds
3018
                  if constexpr (not Opts.null_terminated) {
3019
                     if (it == end) {
3020
                        ctx.error = error_code::end_reached;
3021
                        return;
3022
                     }
3023
                  }
3024
11
                  return;
3025
11
               }
3026
11.5k
               else if (first) {
3027
1.50k
                  first = false;
3028
1.50k
               }
3029
10.0k
               else {
3030
10.0k
                  if (match_invalid_end<',', Opts>(ctx, it, end)) {
3031
427
                     return;
3032
427
                  }
3033
                  if constexpr (not Opts.null_terminated) {
3034
                     if (it == end) [[unlikely]] {
3035
                        ctx.error = error_code::unexpected_end;
3036
                        return;
3037
                     }
3038
                  }
3039
3040
                  if constexpr ((not Opts.minified) && (num_members > 1 || not Opts.error_on_unknown_keys) &&
3041
9.63k
                                (!Opts.comments)) {
3042
9.63k
                     if (ws_size && ws_size < size_t(end - it)) {
3043
2.60k
                        skip_matching_ws(ws_start, it, ws_size);
3044
2.60k
                     }
3045
9.63k
                  }
3046
3047
9.63k
                  if (skip_ws<Opts>(ctx, it, end)) {
3048
0
                     return;
3049
0
                  }
3050
9.63k
               }
3051
3052
11.1k
               constexpr auto reflection_type = glaze_object_t<T> || reflectable<T>;
3053
3054
               if constexpr (reflection_type && (num_members == 0)) {
3055
                  if constexpr (Opts.error_on_unknown_keys) {
3056
                     static_assert(false_v<T>, "This should be unreachable");
3057
                  }
3058
                  else {
3059
                     if (match_invalid_end<'"', Opts>(ctx, it, end)) {
3060
                        return;
3061
                     }
3062
3063
                     // parsing to an empty object, but at this point the JSON presents keys
3064
3065
                     // Unknown key handler does not unescape keys. Unknown escaped keys are
3066
                     // handled by the user.
3067
3068
                     const auto start = it;
3069
                     uint64_t ascii_acc{};
3070
                     skip_string_view(ctx, it, end, ascii_acc);
3071
                     if (bool(ctx.error)) [[unlikely]]
3072
                        return;
3073
                     if (validate_utf8_span<Opts>(ctx, start, it, ascii_acc)) [[unlikely]]
3074
                        return;
3075
                     const sv key{start, size_t(it - start)};
3076
                     ++it;
3077
                     if constexpr (not Opts.null_terminated) {
3078
                        if (it == end) [[unlikely]] {
3079
                           ctx.error = error_code::unexpected_end;
3080
                           return;
3081
                        }
3082
                     }
3083
3084
                     if (parse_ws_colon<Opts>(ctx, it, end)) {
3085
                        return;
3086
                     }
3087
3088
                     // Check if this key is the variant tag that should be skipped
3089
                     if constexpr (not tag.sv().empty()) {
3090
                        if (key == tag.sv()) {
3091
                           // Skip the tag value
3092
                           skip_value<JSON>::op<Opts>(ctx, it, end);
3093
                           if (bool(ctx.error)) [[unlikely]]
3094
                              return;
3095
                           resync_window_end(ctx, end); // a streaming skip refills
3096
                           if (skip_ws<Opts>(ctx, it, end)) {
3097
                              return;
3098
                           }
3099
                           continue;
3100
                        }
3101
                     }
3102
3103
                     parse<JSON>::handle_unknown<Opts>(key, value, ctx, it, end);
3104
                     if (bool(ctx.error)) [[unlikely]]
3105
                        return;
3106
                     if constexpr (not Opts.null_terminated) {
3107
                        if (it == end) [[unlikely]] {
3108
                           ctx.error = error_code::unexpected_end;
3109
                           return;
3110
                        }
3111
                     }
3112
                     if (skip_ws<Opts>(ctx, it, end)) {
3113
                        return;
3114
                     }
3115
                  }
3116
               }
3117
11.1k
               else if constexpr (reflection_type) {
3118
11.1k
                  static_assert(bool(hash_info<T>.type));
3119
3120
11.1k
                  if (*it != '"') [[unlikely]] {
3121
316
                     ctx.error = error_code::expected_quote;
3122
316
                     return;
3123
316
                  }
3124
10.8k
                  ++it;
3125
                  if constexpr (not Opts.null_terminated) {
3126
                     if (it == end) [[unlikely]] {
3127
                        ctx.error = error_code::unexpected_end;
3128
                        return;
3129
                     }
3130
                  }
3131
3132
                  if constexpr (not tag.sv().empty() && not contains_tag<T, tag>()) {
3133
                     // For tagged variants we first check to see if the key matches the tag
3134
                     // We only need to do this if the tag is not part of the keys
3135
3136
                     const auto start = it;
3137
                     uint64_t ascii_acc{};
3138
                     skip_string_view(ctx, it, end, ascii_acc);
3139
                     if (bool(ctx.error)) [[unlikely]]
3140
                        return;
3141
                     if (validate_utf8_span<Opts>(ctx, start, it, ascii_acc)) [[unlikely]]
3142
                        return;
3143
                     const sv key{start, size_t(it - start)};
3144
                     ++it;
3145
                     if constexpr (not Opts.null_terminated) {
3146
                        if (it == end) [[unlikely]] {
3147
                           ctx.error = error_code::unexpected_end;
3148
                           return;
3149
                        }
3150
                     }
3151
3152
                     if (key == tag.sv()) {
3153
                        if (parse_ws_colon<Opts>(ctx, it, end)) {
3154
                           return;
3155
                        }
3156
3157
                        parse<JSON>::handle_unknown<Opts>(key, value, ctx, it, end);
3158
                        if (bool(ctx.error)) [[unlikely]]
3159
                           return;
3160
3161
                        if (skip_ws<Opts>(ctx, it, end)) {
3162
                           return;
3163
                        }
3164
                        continue;
3165
                     }
3166
                     else {
3167
                        it = start; // reset the iterator
3168
                     }
3169
                  }
3170
3171
                  if constexpr (Opts.error_on_missing_keys || Opts.partial_read) {
3172
                     size_t index = num_members;
3173
                     parse_and_invoke<Opts, T>(value, ctx, it, end, index);
3174
                     if (bool(ctx.error)) [[unlikely]]
3175
                        return;
3176
                     if (index < num_members) {
3177
                        fields[index] = true;
3178
                     }
3179
                  }
3180
10.8k
                  else {
3181
10.8k
                     parse_and_invoke<Opts, T>(value, ctx, it, end);
3182
10.8k
                     if (bool(ctx.error)) [[unlikely]]
3183
760
                        return;
3184
10.8k
                  }
3185
               }
3186
               else {
3187
                  // For types like std::map, std::unordered_map
3188
3189
                  auto reading = [&](auto&& key) {
3190
                     if constexpr (Opts.partial_read) {
3191
                        if (auto element = value.find(key); element != value.end()) {
3192
                           ++read_count;
3193
                           parse<JSON>::op<ws_handled<Opts>()>(element->second, ctx, it, end);
3194
                        }
3195
                        else {
3196
                           skip_value<JSON>::op<Opts>(ctx, it, end);
3197
                        }
3198
                     }
3199
                     else {
3200
                        parse<JSON>::op<ws_handled<Opts>()>(value[key], ctx, it, end);
3201
                     }
3202
                  };
3203
3204
                  // using Key = std::conditional_t<heterogeneous_map<T>, sv, typename T::key_type>;
3205
                  using Key = typename T::key_type;
3206
                  if constexpr (std::is_same_v<Key, std::string>) {
3207
                     ctx.scratch.clear();
3208
                     parse<JSON>::op<Opts>(ctx.scratch, ctx, it, end);
3209
                     if (bool(ctx.error)) [[unlikely]]
3210
                        return;
3211
3212
                     if (parse_ws_colon<Opts>(ctx, it, end)) {
3213
                        return;
3214
                     }
3215
3216
                     reading(ctx.scratch);
3217
                     if constexpr (Opts.partial_read) {
3218
                        if (read_count == value.size()) {
3219
                           return;
3220
                        }
3221
                     }
3222
                     if (bool(ctx.error)) [[unlikely]]
3223
                        return;
3224
                  }
3225
                  else if constexpr (str_t<Key>) {
3226
                     Key key;
3227
                     parse<JSON>::op<Opts>(key, ctx, it, end);
3228
                     if (bool(ctx.error)) [[unlikely]]
3229
                        return;
3230
3231
                     if (parse_ws_colon<Opts>(ctx, it, end)) {
3232
                        return;
3233
                     }
3234
3235
                     reading(key);
3236
                     if constexpr (Opts.partial_read) {
3237
                        if (read_count == value.size()) {
3238
                           return;
3239
                        }
3240
                     }
3241
                     if (bool(ctx.error)) [[unlikely]]
3242
                        return;
3243
                  }
3244
                  else {
3245
                     Key key_value{};
3246
                     if constexpr (is_named_enum<Key> || mimics_str_t<Key>) {
3247
                        parse<JSON>::op<Opts>(key_value, ctx, it, end);
3248
                     }
3249
                     else if constexpr (std::is_arithmetic_v<Key>) {
3250
                        // prefer over quoted_t below to avoid double parsing of quoted_t
3251
                        parse<JSON>::op<opt_true<Opts, quoted_num_opt_tag{}>>(key_value, ctx, it, end);
3252
                     }
3253
                     else {
3254
                        parse<JSON>::op<opt_false<Opts, raw_string_opt_tag{}>>(quoted_t<Key>{key_value}, ctx, it, end);
3255
                     }
3256
                     if (bool(ctx.error)) [[unlikely]]
3257
                        return;
3258
3259
                     if (parse_ws_colon<Opts>(ctx, it, end)) {
3260
                        return;
3261
                     }
3262
3263
                     reading(key_value);
3264
                     if constexpr (Opts.partial_read) {
3265
                        if (read_count == value.size()) {
3266
                           return;
3267
                        }
3268
                     }
3269
                     if (bool(ctx.error)) [[unlikely]]
3270
                        return;
3271
                  }
3272
               }
3273
3274
               // Streaming refill point: after parsing each key-value pair, refill if buffer is low
3275
               if constexpr (has_streaming_state<decltype(ctx)>) {
3276
                  if (ctx.stream.enabled()) {
3277
                     const size_t consumed = static_cast<size_t>(it - ctx.stream.data());
3278
                     // Refill when less than 25% of buffer remains to ensure enough space for next element
3279
                     if (ctx.stream.size() - consumed <= ctx.stream.size() / 4 || it >= end) {
3280
                        const char* new_it;
3281
                        const char* new_end;
3282
                        ctx.stream.consume_and_refill(consumed, new_it, new_end);
3283
                        it = new_it;
3284
                        end = new_end;
3285
                        if (it >= end && ctx.stream.at_eof()) {
3286
                           ctx.error = error_code::unexpected_end;
3287
                           return;
3288
                        }
3289
                     }
3290
                  }
3291
               }
3292
3293
11.1k
               if (skip_ws<Opts>(ctx, it, end)) {
3294
0
                  return;
3295
0
               }
3296
11.1k
            }
3297
1.76k
         }
3298
1.76k
      }
_ZN3glz4fromILj10E6PersonE2opITnDaXtlNS_4optsELj10ELb0ELb0ELb1ELb1EEETnNS_14string_literalEXtlNS5_ILm1EEEEERS1_TkNS_10is_contextERNS_7contextERPKcSB_EEvOT1_OT2_OT3_T4_
Line
Count
Source
2791
1.82k
      {
2792
1.82k
         static constexpr auto num_members = reflect<T>::size;
2793
2794
1.82k
         static constexpr auto Opts = opening_handled_off<ws_handled_off<Options>()>();
2795
1.82k
         if constexpr (!check_opening_handled(Options)) {
2796
1.82k
            if constexpr (!check_ws_handled(Options)) {
2797
1.82k
               if (skip_ws<Opts>(ctx, it, end)) {
2798
15
                  return;
2799
15
               }
2800
1.82k
            }
2801
1.82k
            if (match_invalid_end<'{', Opts>(ctx, it, end)) {
2802
227
               return;
2803
227
            }
2804
1.60k
            if constexpr (not Opts.null_terminated) {
2805
1.60k
               if (it == end) [[unlikely]] {
2806
0
                  ctx.error = error_code::unexpected_end;
2807
0
                  return;
2808
0
               }
2809
1.60k
            }
2810
            // one nesting level (see enter_depth): counted in both modes so the limit binds,
2811
            // released at each syntactic close below
2812
1.60k
            if (enter_depth(ctx)) [[unlikely]] {
2813
0
               return;
2814
0
            }
2815
1.60k
         }
2816
1.60k
         const auto ws_start = it;
2817
1.82k
         if (skip_ws<Opts>(ctx, it, end)) {
2818
8
            return;
2819
8
         }
2820
1.82k
         const size_t ws_size = size_t(it - ws_start);
2821
2822
         if constexpr ((glaze_object_t<T> || reflectable<T>) && num_members == 0 && Opts.error_on_unknown_keys) {
2823
            if constexpr (not tag.sv().empty()) {
2824
               if (*it == '"') {
2825
                  ++it;
2826
                  if constexpr (not Opts.null_terminated) {
2827
                     if (it == end) [[unlikely]] {
2828
                        ctx.error = error_code::unexpected_end;
2829
                        return;
2830
                     }
2831
                  }
2832
2833
                  const auto start = it;
2834
                  skip_string_view(ctx, it, end);
2835
                  if (bool(ctx.error)) [[unlikely]]
2836
                     return;
2837
                  if (validate_utf8_span<Opts>(ctx, start, it)) [[unlikely]]
2838
                     return;
2839
                  const sv key{start, size_t(it - start)};
2840
                  ++it;
2841
                  if constexpr (not Opts.null_terminated) {
2842
                     if (it == end) [[unlikely]] {
2843
                        ctx.error = error_code::unexpected_end;
2844
                        return;
2845
                     }
2846
                  }
2847
2848
                  if (key == tag.sv()) {
2849
                     if (parse_ws_colon<Opts>(ctx, it, end)) {
2850
                        return;
2851
                     }
2852
2853
                     parse<JSON>::handle_unknown<Opts>(key, value, ctx, it, end);
2854
                     if (bool(ctx.error)) [[unlikely]]
2855
                        return;
2856
2857
                     if (skip_ws<Opts>(ctx, it, end)) {
2858
                        return;
2859
                     }
2860
                  }
2861
                  else {
2862
                     ctx.error = error_code::unknown_key;
2863
                     return;
2864
                  }
2865
               }
2866
            }
2867
2868
            if (*it == '}') [[likely]] {
2869
               --ctx.depth;
2870
               if constexpr (glaze_object_t<T> || reflectable<T>) {
2871
                  if constexpr (has_self_constraint_v<T> && !check_skip_self_constraint(Opts)) {
2872
                     auto wrapper = self_constraint_v<T>(value);
2873
                     from<JSON, decltype(wrapper)>::template op<ws_handled<Opts>()>(wrapper, ctx, it, end);
2874
                     if (bool(ctx.error)) {
2875
                        return;
2876
                     }
2877
                  }
2878
               }
2879
               ++it;
2880
               if constexpr (not Opts.null_terminated) {
2881
                  if (it == end) {
2882
                     ctx.error = error_code::end_reached;
2883
                     return;
2884
                  }
2885
               }
2886
               if constexpr (Opts.partial_read) {
2887
                  ctx.error = error_code::partial_read_complete;
2888
               }
2889
               return;
2890
            }
2891
            ctx.error = error_code::unknown_key;
2892
            return;
2893
         }
2894
1.82k
         else {
2895
1.82k
            decltype(auto) fields = [&]() -> decltype(auto) {
2896
1.82k
               if constexpr ((glaze_object_t<T> || reflectable<T>) &&
2897
1.82k
                             (Opts.error_on_missing_keys || Opts.partial_read)) {
2898
1.82k
                  return bit_array<num_members>{};
2899
1.82k
               }
2900
1.82k
               else {
2901
1.82k
                  return nullptr;
2902
1.82k
               }
2903
1.82k
            }();
2904
2905
            // A file include merges an external document into this object, so with
2906
            // error_on_missing_keys the keys have to be counted across both documents (see
2907
            // context::key_bits). While this object parses, its bits are published for its
2908
            // includer members to hand down; and if this parse is itself an included document,
2909
            // it claims the bits of the object that included us and merges into them at the
2910
            // closing brace instead of running a check of its own.
2911
1.82k
            static constexpr bool tracks_include_keys = [] {
2912
               // Nested so that reflecting over the members is only asked for when it can matter
2913
1.82k
               if constexpr ((glaze_object_t<T> || reflectable<T>) && Opts.error_on_missing_keys) {
2914
1.82k
                  return has_includer_member<T>;
2915
1.82k
               }
2916
1.82k
               else {
2917
1.82k
                  return false;
2918
1.82k
               }
2919
1.82k
            }();
2920
1.82k
            [[maybe_unused]] bit_array<num_members>* include_bits{};
2921
            if constexpr (tracks_include_keys) {
2922
               if (ctx.include_key_type == &include_key_tag<T>) {
2923
                  include_bits = static_cast<bit_array<num_members>*>(ctx.include_key_bits);
2924
                  ctx.include_key_bits = nullptr;
2925
                  ctx.include_key_type = nullptr;
2926
               }
2927
            }
2928
1.82k
            [[maybe_unused]] const std::conditional_t<tracks_include_keys,
2929
1.82k
                                                      key_bits_scope<std::remove_reference_t<decltype(ctx)>>,
2930
1.82k
                                                      inert_key_bits_scope> published_bits{
2931
1.82k
               ctx, [&]() -> void* {
2932
1.82k
                  if constexpr (tracks_include_keys) {
2933
1.82k
                     return &fields;
2934
1.82k
                  }
2935
1.82k
                  else {
2936
1.82k
                     return nullptr;
2937
1.82k
                  }
2938
1.82k
               }()};
2939
2940
1.82k
            size_t read_count{}; // for partial_read
2941
2942
1.82k
            bool first = true;
2943
12.1k
            while (true) {
2944
               if constexpr ((glaze_object_t<T> || reflectable<T>) && Opts.partial_read) {
2945
                  static constexpr bit_array<num_members> all_fields = [] {
2946
                     bit_array<num_members> arr{};
2947
                     for (size_t i = 0; i < num_members; ++i) {
2948
                        arr[i] = true;
2949
                     }
2950
                     return arr;
2951
                  }();
2952
2953
                  if ((all_fields & fields) == all_fields) {
2954
                     ctx.error = error_code::partial_read_complete;
2955
                     --ctx.depth; // as above: an early success still closes this level
2956
                     return;
2957
                  }
2958
               }
2959
2960
               // Streaming refill point: at start of each iteration, ensure buffer has data
2961
               if constexpr (has_streaming_state<decltype(ctx)>) {
2962
                  if (ctx.stream.enabled()) {
2963
                     const size_t consumed = static_cast<size_t>(it - ctx.stream.data());
2964
                     const size_t remaining = ctx.stream.size() - consumed;
2965
                     // Refill when less than half of buffer remains to ensure space for next key-value pair
2966
                     if (remaining <= ctx.stream.size() / 2 || it >= end) {
2967
                        const char* new_it;
2968
                        const char* new_end;
2969
                        ctx.stream.consume_and_refill(consumed, new_it, new_end);
2970
                        it = new_it;
2971
                        end = new_end;
2972
                        if (it >= end && ctx.stream.at_eof()) {
2973
                           ctx.error = error_code::unexpected_end;
2974
                           return;
2975
                        }
2976
                     }
2977
                  }
2978
               }
2979
2980
10.6k
               if (*it == '}') {
2981
13
                  --ctx.depth;
2982
                  if constexpr ((glaze_object_t<T> || reflectable<T>) && Opts.error_on_missing_keys) {
2983
                     const bool defer_to_includer = [&] {
2984
                        if constexpr (tracks_include_keys) {
2985
                           if (include_bits) {
2986
                              // An included document is a fragment: hand the keys it supplied to
2987
                              // the object that included it, which checks the union of both.
2988
                              *include_bits |= fields;
2989
                              return true;
2990
                           }
2991
                        }
2992
                        return false;
2993
                     }();
2994
                     constexpr auto req_fields = required_fields<T, Opts>();
2995
                     if (not defer_to_includer && (req_fields & fields) != req_fields) {
2996
                        for (size_t i = 0; i < num_members; ++i) {
2997
                           if (not fields[i] && req_fields[i]) {
2998
                              ctx.custom_error_message = reflect<T>::keys[i];
2999
                              // We just return the first missing key in order to avoid heap allocations
3000
                              break;
3001
                           }
3002
                        }
3003
3004
                        ctx.error = error_code::missing_key;
3005
                        return;
3006
                     }
3007
                  }
3008
13
                  if constexpr (glaze_object_t<T> || reflectable<T>) {
3009
                     if constexpr (has_self_constraint_v<T> && !check_skip_self_constraint(Opts)) {
3010
                        auto wrapper = self_constraint_v<T>(value);
3011
                        from<JSON, decltype(wrapper)>::template op<ws_handled<Opts>()>(wrapper, ctx, it, end);
3012
                        if (bool(ctx.error)) {
3013
                           return;
3014
                        }
3015
                     }
3016
13
                  }
3017
13
                  ++it; // Increment after checking for missing keys so errors are within buffer bounds
3018
13
                  if constexpr (not Opts.null_terminated) {
3019
13
                     if (it == end) {
3020
3
                        ctx.error = error_code::end_reached;
3021
3
                        return;
3022
3
                     }
3023
13
                  }
3024
10
                  return;
3025
13
               }
3026
10.6k
               else if (first) {
3027
1.56k
                  first = false;
3028
1.56k
               }
3029
9.11k
               else {
3030
9.11k
                  if (match_invalid_end<',', Opts>(ctx, it, end)) {
3031
158
                     return;
3032
158
                  }
3033
8.95k
                  if constexpr (not Opts.null_terminated) {
3034
8.95k
                     if (it == end) [[unlikely]] {
3035
0
                        ctx.error = error_code::unexpected_end;
3036
0
                        return;
3037
0
                     }
3038
8.95k
                  }
3039
3040
                  if constexpr ((not Opts.minified) && (num_members > 1 || not Opts.error_on_unknown_keys) &&
3041
8.95k
                                (!Opts.comments)) {
3042
8.95k
                     if (ws_size && ws_size < size_t(end - it)) {
3043
2.78k
                        skip_matching_ws(ws_start, it, ws_size);
3044
2.78k
                     }
3045
8.95k
                  }
3046
3047
8.95k
                  if (skip_ws<Opts>(ctx, it, end)) {
3048
34
                     return;
3049
34
                  }
3050
8.95k
               }
3051
3052
10.4k
               constexpr auto reflection_type = glaze_object_t<T> || reflectable<T>;
3053
3054
               if constexpr (reflection_type && (num_members == 0)) {
3055
                  if constexpr (Opts.error_on_unknown_keys) {
3056
                     static_assert(false_v<T>, "This should be unreachable");
3057
                  }
3058
                  else {
3059
                     if (match_invalid_end<'"', Opts>(ctx, it, end)) {
3060
                        return;
3061
                     }
3062
3063
                     // parsing to an empty object, but at this point the JSON presents keys
3064
3065
                     // Unknown key handler does not unescape keys. Unknown escaped keys are
3066
                     // handled by the user.
3067
3068
                     const auto start = it;
3069
                     uint64_t ascii_acc{};
3070
                     skip_string_view(ctx, it, end, ascii_acc);
3071
                     if (bool(ctx.error)) [[unlikely]]
3072
                        return;
3073
                     if (validate_utf8_span<Opts>(ctx, start, it, ascii_acc)) [[unlikely]]
3074
                        return;
3075
                     const sv key{start, size_t(it - start)};
3076
                     ++it;
3077
                     if constexpr (not Opts.null_terminated) {
3078
                        if (it == end) [[unlikely]] {
3079
                           ctx.error = error_code::unexpected_end;
3080
                           return;
3081
                        }
3082
                     }
3083
3084
                     if (parse_ws_colon<Opts>(ctx, it, end)) {
3085
                        return;
3086
                     }
3087
3088
                     // Check if this key is the variant tag that should be skipped
3089
                     if constexpr (not tag.sv().empty()) {
3090
                        if (key == tag.sv()) {
3091
                           // Skip the tag value
3092
                           skip_value<JSON>::op<Opts>(ctx, it, end);
3093
                           if (bool(ctx.error)) [[unlikely]]
3094
                              return;
3095
                           resync_window_end(ctx, end); // a streaming skip refills
3096
                           if (skip_ws<Opts>(ctx, it, end)) {
3097
                              return;
3098
                           }
3099
                           continue;
3100
                        }
3101
                     }
3102
3103
                     parse<JSON>::handle_unknown<Opts>(key, value, ctx, it, end);
3104
                     if (bool(ctx.error)) [[unlikely]]
3105
                        return;
3106
                     if constexpr (not Opts.null_terminated) {
3107
                        if (it == end) [[unlikely]] {
3108
                           ctx.error = error_code::unexpected_end;
3109
                           return;
3110
                        }
3111
                     }
3112
                     if (skip_ws<Opts>(ctx, it, end)) {
3113
                        return;
3114
                     }
3115
                  }
3116
               }
3117
10.4k
               else if constexpr (reflection_type) {
3118
10.4k
                  static_assert(bool(hash_info<T>.type));
3119
3120
10.4k
                  if (*it != '"') [[unlikely]] {
3121
338
                     ctx.error = error_code::expected_quote;
3122
338
                     return;
3123
338
                  }
3124
10.1k
                  ++it;
3125
10.1k
                  if constexpr (not Opts.null_terminated) {
3126
10.1k
                     if (it == end) [[unlikely]] {
3127
16
                        ctx.error = error_code::unexpected_end;
3128
16
                        return;
3129
16
                     }
3130
10.1k
                  }
3131
3132
                  if constexpr (not tag.sv().empty() && not contains_tag<T, tag>()) {
3133
                     // For tagged variants we first check to see if the key matches the tag
3134
                     // We only need to do this if the tag is not part of the keys
3135
3136
                     const auto start = it;
3137
                     uint64_t ascii_acc{};
3138
                     skip_string_view(ctx, it, end, ascii_acc);
3139
                     if (bool(ctx.error)) [[unlikely]]
3140
                        return;
3141
                     if (validate_utf8_span<Opts>(ctx, start, it, ascii_acc)) [[unlikely]]
3142
                        return;
3143
                     const sv key{start, size_t(it - start)};
3144
                     ++it;
3145
                     if constexpr (not Opts.null_terminated) {
3146
                        if (it == end) [[unlikely]] {
3147
                           ctx.error = error_code::unexpected_end;
3148
                           return;
3149
                        }
3150
                     }
3151
3152
                     if (key == tag.sv()) {
3153
                        if (parse_ws_colon<Opts>(ctx, it, end)) {
3154
                           return;
3155
                        }
3156
3157
                        parse<JSON>::handle_unknown<Opts>(key, value, ctx, it, end);
3158
                        if (bool(ctx.error)) [[unlikely]]
3159
                           return;
3160
3161
                        if (skip_ws<Opts>(ctx, it, end)) {
3162
                           return;
3163
                        }
3164
                        continue;
3165
                     }
3166
                     else {
3167
                        it = start; // reset the iterator
3168
                     }
3169
                  }
3170
3171
                  if constexpr (Opts.error_on_missing_keys || Opts.partial_read) {
3172
                     size_t index = num_members;
3173
                     parse_and_invoke<Opts, T>(value, ctx, it, end, index);
3174
                     if (bool(ctx.error)) [[unlikely]]
3175
                        return;
3176
                     if (index < num_members) {
3177
                        fields[index] = true;
3178
                     }
3179
                  }
3180
10.1k
                  else {
3181
10.1k
                     parse_and_invoke<Opts, T>(value, ctx, it, end);
3182
10.1k
                     if (bool(ctx.error)) [[unlikely]]
3183
856
                        return;
3184
10.1k
                  }
3185
               }
3186
               else {
3187
                  // For types like std::map, std::unordered_map
3188
3189
                  auto reading = [&](auto&& key) {
3190
                     if constexpr (Opts.partial_read) {
3191
                        if (auto element = value.find(key); element != value.end()) {
3192
                           ++read_count;
3193
                           parse<JSON>::op<ws_handled<Opts>()>(element->second, ctx, it, end);
3194
                        }
3195
                        else {
3196
                           skip_value<JSON>::op<Opts>(ctx, it, end);
3197
                        }
3198
                     }
3199
                     else {
3200
                        parse<JSON>::op<ws_handled<Opts>()>(value[key], ctx, it, end);
3201
                     }
3202
                  };
3203
3204
                  // using Key = std::conditional_t<heterogeneous_map<T>, sv, typename T::key_type>;
3205
                  using Key = typename T::key_type;
3206
                  if constexpr (std::is_same_v<Key, std::string>) {
3207
                     ctx.scratch.clear();
3208
                     parse<JSON>::op<Opts>(ctx.scratch, ctx, it, end);
3209
                     if (bool(ctx.error)) [[unlikely]]
3210
                        return;
3211
3212
                     if (parse_ws_colon<Opts>(ctx, it, end)) {
3213
                        return;
3214
                     }
3215
3216
                     reading(ctx.scratch);
3217
                     if constexpr (Opts.partial_read) {
3218
                        if (read_count == value.size()) {
3219
                           return;
3220
                        }
3221
                     }
3222
                     if (bool(ctx.error)) [[unlikely]]
3223
                        return;
3224
                  }
3225
                  else if constexpr (str_t<Key>) {
3226
                     Key key;
3227
                     parse<JSON>::op<Opts>(key, ctx, it, end);
3228
                     if (bool(ctx.error)) [[unlikely]]
3229
                        return;
3230
3231
                     if (parse_ws_colon<Opts>(ctx, it, end)) {
3232
                        return;
3233
                     }
3234
3235
                     reading(key);
3236
                     if constexpr (Opts.partial_read) {
3237
                        if (read_count == value.size()) {
3238
                           return;
3239
                        }
3240
                     }
3241
                     if (bool(ctx.error)) [[unlikely]]
3242
                        return;
3243
                  }
3244
                  else {
3245
                     Key key_value{};
3246
                     if constexpr (is_named_enum<Key> || mimics_str_t<Key>) {
3247
                        parse<JSON>::op<Opts>(key_value, ctx, it, end);
3248
                     }
3249
                     else if constexpr (std::is_arithmetic_v<Key>) {
3250
                        // prefer over quoted_t below to avoid double parsing of quoted_t
3251
                        parse<JSON>::op<opt_true<Opts, quoted_num_opt_tag{}>>(key_value, ctx, it, end);
3252
                     }
3253
                     else {
3254
                        parse<JSON>::op<opt_false<Opts, raw_string_opt_tag{}>>(quoted_t<Key>{key_value}, ctx, it, end);
3255
                     }
3256
                     if (bool(ctx.error)) [[unlikely]]
3257
                        return;
3258
3259
                     if (parse_ws_colon<Opts>(ctx, it, end)) {
3260
                        return;
3261
                     }
3262
3263
                     reading(key_value);
3264
                     if constexpr (Opts.partial_read) {
3265
                        if (read_count == value.size()) {
3266
                           return;
3267
                        }
3268
                     }
3269
                     if (bool(ctx.error)) [[unlikely]]
3270
                        return;
3271
                  }
3272
               }
3273
3274
               // Streaming refill point: after parsing each key-value pair, refill if buffer is low
3275
               if constexpr (has_streaming_state<decltype(ctx)>) {
3276
                  if (ctx.stream.enabled()) {
3277
                     const size_t consumed = static_cast<size_t>(it - ctx.stream.data());
3278
                     // Refill when less than 25% of buffer remains to ensure enough space for next element
3279
                     if (ctx.stream.size() - consumed <= ctx.stream.size() / 4 || it >= end) {
3280
                        const char* new_it;
3281
                        const char* new_end;
3282
                        ctx.stream.consume_and_refill(consumed, new_it, new_end);
3283
                        it = new_it;
3284
                        end = new_end;
3285
                        if (it >= end && ctx.stream.at_eof()) {
3286
                           ctx.error = error_code::unexpected_end;
3287
                           return;
3288
                        }
3289
                     }
3290
                  }
3291
               }
3292
3293
10.4k
               if (skip_ws<Opts>(ctx, it, end)) {
3294
163
                  return;
3295
163
               }
3296
10.4k
            }
3297
1.82k
         }
3298
1.82k
      }
3299
   };
3300
3301
   template <is_variant T>
3302
   consteval auto variant_is_auto_deducible()
3303
   {
3304
      // Contains at most one each of the basic json types bool, numeric, string, object, array
3305
      // If all objects are meta-objects then we can attempt to deduce them as well either through a type tag or
3306
      // unique combinations of keys
3307
      // Also considers types with mimic declarations or custom read/write with inferable input types
3308
      int bools{}, numbers{}, strings{}, objects{}, meta_objects{}, arrays{};
3309
      constexpr auto N = std::variant_size_v<T>;
3310
      for_each<N>([&]<auto I>() {
3311
         using V = std::decay_t<std::variant_alternative_t<I, T>>;
3312
         // Custom takes precedence over mimic (consistent with runtime behavior)
3313
         if constexpr (has_custom_meta_v<V>) {
3314
            bools += custom_bool_t<V>;
3315
            numbers += custom_num_t<V>;
3316
            strings += custom_str_t<V>;
3317
         }
3318
         else {
3319
            bools += bool_t<V> || mimics_bool_t<V>;
3320
            numbers += num_t<V> || mimics_num_t<V>;
3321
            strings += str_t<V> || mimics_str_t<V>;
3322
            strings += glaze_enum_t<V>;
3323
         }
3324
         objects += pair_t<V>;
3325
         objects += (writable_map_t<V> || readable_map_t<V> || is_memory_object<V>);
3326
         objects += glaze_object_t<V>;
3327
         meta_objects += glaze_object_t<V> || reflectable<V> || is_memory_object<V>;
3328
         arrays += glaze_array_t<V>;
3329
         arrays += array_t<V>;
3330
         // TODO null
3331
      });
3332
      return bools < 2 && numbers < 2 && strings < 2 && (objects < 2 || meta_objects == objects) && arrays < 2;
3333
   }
3334
3335
   // ============================================================================
3336
   // Variant type category traits (index-based, avoids expensive tuple_cat)
3337
   // ============================================================================
3338
3339
   // Concepts for variant type classification
3340
   template <class T>
3341
   concept variant_bool_type = bool_t<remove_meta_wrapper_t<T>> || mimics_bool_t<T> || custom_bool_t<T>;
3342
3343
   template <class T>
3344
   concept variant_num_type = num_t<remove_meta_wrapper_t<T>> || mimics_num_t<T> || custom_num_t<T>;
3345
3346
   template <class T>
3347
   concept variant_str_type = str_t<remove_meta_wrapper_t<T>> || glaze_enum_t<remove_meta_wrapper_t<T>> ||
3348
                              glaze_enum_t<T> || mimics_str_t<T> || custom_str_t<T>;
3349
3350
   template <class T>
3351
   concept variant_object_type = json_object<T>;
3352
3353
   template <class T>
3354
   concept variant_array_type = array_t<remove_meta_wrapper_t<T>> || glaze_array_t<T> || tuple_t<T> || is_std_tuple<T>;
3355
3356
   template <class T>
3357
   concept variant_null_type = null_t<T>;
3358
3359
   template <class T>
3360
   concept variant_nullable_object_type = is_memory_object<T>;
3361
3362
   // Type traits wrapping concepts for template template parameter use
3363
   template <class T>
3364
   struct is_variant_bool : std::bool_constant<variant_bool_type<T>>
3365
   {};
3366
   template <class T>
3367
   struct is_variant_num : std::bool_constant<variant_num_type<T>>
3368
   {};
3369
   template <class T>
3370
   struct is_variant_str : std::bool_constant<variant_str_type<T>>
3371
   {};
3372
   template <class T>
3373
   struct is_variant_object : std::bool_constant<variant_object_type<T>>
3374
   {};
3375
   // Range-of-pairs alternatives parse as a JSON object only when the `concatenate` option is enabled (default).
3376
   template <class T>
3377
   struct is_variant_concat_object : std::bool_constant<json_pair_range_object<T>>
3378
   {};
3379
   template <class T>
3380
   struct is_variant_array : std::bool_constant<variant_array_type<T>>
3381
   {};
3382
   template <class T>
3383
   struct is_variant_null : std::bool_constant<variant_null_type<T>>
3384
   {};
3385
   template <class T>
3386
   struct is_variant_nullable_object : std::bool_constant<variant_nullable_object_type<T>>
3387
   {};
3388
3389
   // Count types in variant matching a trait (fold expression, very fast to compile)
3390
   // Using helper struct instead of IIFE for MSVC compatibility
3391
   template <class Variant, template <class> class Trait>
3392
   struct variant_count_impl;
3393
3394
   template <template <class> class Trait, class... Ts>
3395
   struct variant_count_impl<std::variant<Ts...>, Trait>
3396
   {
3397
      static constexpr size_t value = (size_t(Trait<Ts>::value) + ... + 0);
3398
   };
3399
3400
   template <class Variant, template <class> class Trait>
3401
   constexpr size_t variant_count_v = variant_count_impl<Variant, Trait>::value;
3402
3403
   // Get first index matching trait (or variant_npos if none)
3404
   // Using helper struct instead of IIFE for MSVC compatibility
3405
   template <class Variant, template <class> class Trait>
3406
   struct variant_first_index_impl;
3407
3408
   template <template <class> class Trait, class... Ts>
3409
   struct variant_first_index_impl<std::variant<Ts...>, Trait>
3410
   {
3411
      static constexpr size_t find()
3412
      {
3413
         size_t result = std::variant_npos;
3414
         size_t idx = 0;
3415
         // Short-circuit: find first match
3416
         ((Trait<Ts>::value && result == std::variant_npos ? (result = idx, ++idx) : ++idx), ...);
3417
         return result;
3418
      }
3419
      static constexpr size_t value = find();
3420
   };
3421
3422
   template <class Variant, template <class> class Trait>
3423
   constexpr size_t variant_first_index_v = variant_first_index_impl<Variant, Trait>::value;
3424
3425
   // Count types matching both category trait AND const/non-const filter
3426
   // Using helper struct instead of IIFE for MSVC compatibility
3427
   template <class Variant, template <class> class Trait, bool IsConst>
3428
   struct variant_filtered_count_impl;
3429
3430
   template <template <class> class Trait, bool IsConst, class... Ts>
3431
   struct variant_filtered_count_impl<std::variant<Ts...>, Trait, IsConst>
3432
   {
3433
      static constexpr size_t value = (size_t(Trait<Ts>::value && (glaze_const_value_t<Ts> == IsConst)) + ... + 0);
3434
   };
3435
3436
   template <class Variant, template <class> class Trait, bool IsConst>
3437
   constexpr size_t variant_filtered_count_v = variant_filtered_count_impl<Variant, Trait, IsConst>::value;
3438
3439
   // Variant type counts using fold expressions (replaces tuple-based variant_type_count)
3440
   template <class T>
3441
   struct variant_type_count
3442
   {
3443
      static constexpr auto n_bool = variant_count_v<T, is_variant_bool>;
3444
      static constexpr auto n_number = variant_count_v<T, is_variant_num>;
3445
      static constexpr auto n_string = variant_count_v<T, is_variant_str>;
3446
      static constexpr auto n_nullable_object = variant_count_v<T, is_variant_nullable_object>;
3447
      static constexpr auto n_object = variant_count_v<T, is_variant_object> + n_nullable_object;
3448
      static constexpr auto n_array = variant_count_v<T, is_variant_array>;
3449
      static constexpr auto n_null = variant_count_v<T, is_variant_null>;
3450
   };
3451
3452
   // Number of variant alternatives that should be treated as JSON objects under the active options.
3453
   // Includes range-of-pair alternatives only when `concatenate` is enabled (default).
3454
   //
3455
   // Note: this preserves the existing predicate used by the variant '{' fast path. Because
3456
   // `variant_type_count::n_object` already includes `n_nullable_object`, adding
3457
   // `n_nullable_object` again effectively excludes nullable-object alternatives from the
3458
   // single-candidate fast path (which only knows how to dispatch a non-nullable object index).
3459
   template <auto Opts, class T>
3460
   constexpr size_t variant_object_candidate_count_v =
3461
      variant_type_count<T>::n_object + variant_type_count<T>::n_nullable_object +
3462
      (check_concatenate(Opts) ? variant_count_v<T, is_variant_concat_object> : 0);
3463
3464
   // First variant index that is an object candidate under the active options. Prefers a true
3465
   // `is_variant_object` match; falls back to `is_variant_concat_object` when concatenate is enabled.
3466
   template <auto Opts, class T>
3467
   constexpr size_t variant_first_object_candidate_v = []() {
3468
      constexpr auto obj_idx = variant_first_index_v<T, is_variant_object>;
3469
      if constexpr (obj_idx != std::variant_npos)
3470
         return obj_idx;
3471
      else if constexpr (check_concatenate(Opts))
3472
         return variant_first_index_v<T, is_variant_concat_object>;
3473
      else
3474
         return std::variant_npos;
3475
   }();
3476
3477
   // Restores a nesting-depth snapshot after a rejected variant alternative, alongside the iterator
3478
   // rewind. A rejected alternative that bailed out inside a container left its level counted (see
3479
   // enter_depth), and without this, trying N alternatives could spend N levels of the recursion
3480
   // budget -- a long array of variants would eventually report exceeded_max_recursive_depth.
3481
   //
3482
   // The exception is a truncation signal on a non-null-terminated buffer: there ctx.depth is also
3483
   // the completion counter, and leaving that level counted is precisely what tells
3484
   // finalize_read_context the buffer ended mid-value rather than exactly at a clean close.
3485
   //
3486
   // Nesting past the limit is excluded in both modes, and for a third reason: handing the next
3487
   // alternative a fresh budget lets it re-descend to the limit and fail identically, so the retry
3488
   // re-parses the whole subtree once per alternative at every level of the nest, which is
3489
   // exponential. `variant_alternative_exhausted` below stops the retry outright.
3490
   template <auto Opts>
3491
   GLZ_ALWAYS_INLINE void rewind_depth(is_context auto& ctx, const uint32_t depth) noexcept
3492
   {
3493
      if (ctx.error == error_code::exceeded_max_recursive_depth) [[unlikely]] {
3494
         return;
3495
      }
3496
      if constexpr (Opts.null_terminated) {
3497
         ctx.depth = depth;
3498
      }
3499
      else {
3500
         if (ctx.error != error_code::end_reached && ctx.error != error_code::unexpected_end) {
3501
            ctx.depth = depth;
3502
         }
3503
      }
3504
   }
3505
3506
   // True once a failure means no further alternative is worth trying: nesting past the depth limit
3507
   // is a property of the input rather than of the alternative, so retrying only multiplies the work.
3508
   GLZ_ALWAYS_INLINE bool variant_alternative_exhausted(is_context auto& ctx) noexcept
3509
   {
3510
      return ctx.error == error_code::exceeded_max_recursive_depth;
3511
   }
3512
3513
   // Process variant alternatives by iterating directly over variant indices
3514
   // (replaces tuple_cat + tuple iteration approach)
3515
   template <class Variant, template <class> class Trait>
3516
   struct process_variant_alternatives
3517
   {
3518
      template <auto Options>
3519
      static void op(auto&& value, is_context auto&& ctx, auto&& it, auto end)
3520
      {
3521
         constexpr auto category_count = variant_count_v<Variant, Trait>;
3522
3523
         if constexpr (category_count == 0) {
3524
            ctx.error = error_code::no_matching_variant_type;
3525
         }
3526
         else {
3527
            constexpr auto N = std::variant_size_v<Variant>;
3528
            constexpr auto const_count = variant_filtered_count_v<Variant, Trait, true>;
3529
            constexpr auto non_const_count = variant_filtered_count_v<Variant, Trait, false>;
3530
3531
            bool found_match{};
3532
            // Set when the speculative parsing budget runs out; see charge_speculation. An ambiguous
3533
            // nest re-parses the same subtree per alternative at every level, which is exponential,
3534
            // so the budget stops it and the failure already in hand is reported.
3535
            bool exhausted{};
3536
3537
            // First pass: const glaze types in this category
3538
            if constexpr (const_count > 0) {
3539
               for_each<N>([&]<size_t I>() {
3540
                  if (found_match || exhausted || variant_alternative_exhausted(ctx)) {
3541
                     return;
3542
                  }
3543
                  using V = std::variant_alternative_t<I, Variant>;
3544
                  if constexpr (Trait<V>::value && glaze_const_value_t<V>) {
3545
                     // run time substitute to compare to const value
3546
                     std::remove_const_t<std::remove_pointer_t<std::remove_const_t<meta_wrapper_t<V>>>> substitute{};
3547
                     auto copy_it{it};
3548
                     // A rejected alternative may have bailed out inside a container, which leaves its
3549
                     // nesting level counted (see enter_depth). Rewind the depth with the iterator so
3550
                     // trying N alternatives cannot spend N levels of the recursion budget.
3551
                     const auto copy_depth{ctx.depth};
3552
                     parse<JSON>::op<ws_handled<Options>()>(substitute, ctx, it, end);
3553
                     static constexpr auto const_value{*meta_wrapper_v<V>};
3554
                     if (substitute == const_value) {
3555
                        found_match = true;
3556
                        if (!std::holds_alternative<V>(value)) {
3557
                           value = V{};
3558
                        }
3559
                     }
3560
                     else {
3561
                        // Rejected: charge the bytes it parsed before we rewind. See charge_speculation.
3562
                        if (!charge_speculation(ctx, size_t(it - copy_it))) {
3563
                           exhausted = true;
3564
                        }
3565
                        if constexpr (not Options.null_terminated) {
3566
                           if (ctx.error == error_code::end_reached && not exhausted) {
3567
                              ctx.error = error_code::none;
3568
                           }
3569
                        }
3570
                        it = copy_it;
3571
                        rewind_depth<Options>(ctx, copy_depth);
3572
                     }
3573
                  }
3574
               });
3575
               if (found_match) {
3576
                  return;
3577
               }
3578
            }
3579
3580
            // Second pass: non-const types in this category
3581
            if constexpr (non_const_count > 0) {
3582
               // Track position within matching types for "is last" check.
3583
               // We iterate over all variant indices but only process matching types,
3584
               // so we need runtime tracking (vs the old tuple-based approach where
3585
               // the loop index directly corresponded to position in filtered set).
3586
               // This has negligible cost: one increment per matching type.
3587
               size_t non_const_idx = 0;
3588
3589
               for_each<N>([&]<size_t I>() {
3590
                  if (found_match || exhausted || variant_alternative_exhausted(ctx)) {
3591
                     return;
3592
                  }
3593
                  using V = std::variant_alternative_t<I, Variant>;
3594
                  if constexpr (Trait<V>::value && !glaze_const_value_t<V>) {
3595
                     auto copy_it{it};
3596
                     const auto copy_depth{ctx.depth}; // rewound with `it`; see the note above
3597
                     if (!std::holds_alternative<V>(value)) {
3598
                        value = V{};
3599
                     }
3600
                     parse<JSON>::op<ws_handled<Options>()>(std::get<V>(value), ctx, it, end);
3601
                     if (bool(ctx.error) && !charge_speculation(ctx, size_t(it - copy_it))) {
3602
                        exhausted = true; // only rejected attempts are charged; see charge_speculation
3603
                     }
3604
                     if (!bool(ctx.error)) {
3605
                        found_match = true;
3606
                     }
3607
                     else if constexpr (not Options.null_terminated) {
3608
                        constexpr bool is_complete_type =
3609
                           num_t<V> || bool_t<V> || string_t<V> || std::is_enum_v<V> || tuple_t<V> || is_std_tuple<V>;
3610
3611
                        if constexpr (is_complete_type) {
3612
                           if (ctx.error == error_code::end_reached && it > copy_it && not speculation_exhausted(ctx)) {
3613
                              found_match = true;
3614
                              ctx.error = error_code::none;
3615
                           }
3616
                           else {
3617
                              it = copy_it;
3618
                              rewind_depth<Options>(ctx, copy_depth);
3619
                              if (non_const_idx + 1 < non_const_count && not exhausted &&
3620
                                  not variant_alternative_exhausted(ctx)) {
3621
                                 ctx.error = error_code::none;
3622
                              }
3623
                           }
3624
                        }
3625
                        else {
3626
                           it = copy_it;
3627
                           rewind_depth<Options>(ctx, copy_depth);
3628
                           if (non_const_idx + 1 < non_const_count && not exhausted &&
3629
                               not variant_alternative_exhausted(ctx)) {
3630
                              ctx.error = error_code::none;
3631
                           }
3632
                        }
3633
                     }
3634
                     else {
3635
                        it = copy_it;
3636
                        rewind_depth<Options>(ctx, copy_depth);
3637
                        if (non_const_idx + 1 < non_const_count && not exhausted &&
3638
                            not variant_alternative_exhausted(ctx)) {
3639
                           ctx.error = error_code::none;
3640
                        }
3641
                     }
3642
                     ++non_const_idx;
3643
                  }
3644
               });
3645
               if (!found_match) {
3646
                  if constexpr (non_const_count == 1) {
3647
                     // Keep the specific error from the single type we tried
3648
                  }
3649
                  else if (not exhausted && not variant_alternative_exhausted(ctx)) {
3650
                     // Over-nested input is not a failure of the alternative set; keep saying so.
3651
                     ctx.error = error_code::no_matching_variant_type;
3652
                  }
3653
               }
3654
            }
3655
            else {
3656
               ctx.error = error_code::no_matching_variant_type;
3657
            }
3658
         }
3659
      }
3660
   };
3661
3662
   template <is_variant T>
3663
      requires(not custom_read<T>)
3664
   struct from<JSON, T>
3665
   {
3666
      static constexpr size_t variant_size = std::variant_size_v<T>;
3667
3668
      // The tagging representation is a property of the variant, decided once for every alternative.
3669
      static constexpr auto tagging = variant_tagging_v<T>;
3670
3671
      // Adjacent tagging: {tag: id, content: value}. Nothing needs to be deduced here -- the
3672
      // discriminator names the alternative outright -- so this path is wholly independent of the
3673
      // structural deduction machinery below and works for alternatives of any shape, including
3674
      // several that share one. That is the point of the form: `variant<vector<double>,
3675
      // deque<double>>` round-trips under it, where internal tagging cannot even represent it.
3676
      //
3677
      // The two keys may arrive in either order, so the object is walked twice: once to find the
3678
      // discriminator, once to read the content into the alternative it named. Both walks are over
3679
      // an object that in practice has exactly two members, so the second costs one extra skip.
3680
      template <auto Options>
3681
      static void read_adjacent(auto&& value, is_context auto&& ctx, auto&& it, auto end)
3682
      {
3683
         constexpr auto Opts = ws_handled_off<Options>();
3684
         if constexpr (not check_ws_handled(Options)) {
3685
            if (skip_ws<Opts>(ctx, it, end)) {
3686
               return;
3687
            }
3688
         }
3689
         if (match_invalid_end<'{', Opts>(ctx, it, end)) {
3690
            return;
3691
         }
3692
3693
         // Adjacent values nest (the content of one variant may be another), so this reader has to
3694
         // count depth itself rather than leaning on the object parser it never calls. Counted by
3695
         // hand rather than with depth_guard: the non-null-terminated readers rely on depth staying
3696
         // elevated when a read bails out, because finalize_read_context only downgrades
3697
         // `end_reached` to success at depth 0. An RAII restore turns a truncated stream into a
3698
         // silent success with the destination untouched. Only the success path below decrements.
3699
         if (ctx.depth >= max_recursive_depth_limit) [[unlikely]] {
3700
            ctx.error = error_code::exceeded_max_recursive_depth;
3701
            return;
3702
         }
3703
         ++ctx.depth;
3704
3705
         // Adjacent tagging reads the object twice -- once to find the discriminator, once to read
3706
         // the content into the alternative it named -- so `walk` returns here at the start of each
3707
         // pass. Under a streaming read those two passes straddle refills, which is what the anchor
3708
         // is for; a raw pointer would address relocated bytes by the second one.
3709
         auto obj_start = make_rewind_anchor(ctx, it);
3710
3711
         // Walk the members, handing each key to `on_key`, which must consume that key's value.
3712
         // Leaves `it` just past the closing brace. Returns false once an error has been set.
3713
         const auto walk = [&](auto&& on_key) {
3714
            if (!obj_start.rewind(ctx, it, end)) {
3715
               return false;
3716
            }
3717
            if (skip_ws<Opts>(ctx, it, end)) {
3718
               return false;
3719
            }
3720
            bool first_key = true;
3721
            while (*it != '}') {
3722
               if (!first_key) {
3723
                  if (match_invalid_end<',', Opts>(ctx, it, end)) {
3724
                     return false;
3725
                  }
3726
                  if (skip_ws<Opts>(ctx, it, end)) {
3727
                     return false;
3728
                  }
3729
               }
3730
               if (match_invalid_end<'"', Opts>(ctx, it, end)) {
3731
                  return false;
3732
               }
3733
               auto* key_start = it;
3734
               skip_string_view(ctx, it, end);
3735
               if (bool(ctx.error)) [[unlikely]] {
3736
                  return false;
3737
               }
3738
               if (validate_utf8_span<Opts>(ctx, key_start, it)) [[unlikely]] {
3739
                  return false;
3740
               }
3741
               const sv key{key_start, size_t(it - key_start)};
3742
               if (match_invalid_end<'"', Opts>(ctx, it, end)) {
3743
                  return false;
3744
               }
3745
               if (parse_ws_colon<Opts>(ctx, it, end)) {
3746
                  return false;
3747
               }
3748
               if (!on_key(key)) {
3749
                  return false;
3750
               }
3751
               // on_key consumed a value, and a streaming read refills inside one, which leaves the
3752
               // window edge this frame is holding behind.
3753
               resync_window_end(ctx, end);
3754
               first_key = false;
3755
               if (skip_ws<Opts>(ctx, it, end)) {
3756
                  return false;
3757
               }
3758
            }
3759
            if (match<'}'>(ctx, it)) {
3760
               return false;
3761
            }
3762
            return true;
3763
         };
3764
3765
         using id_type = std::decay_t<decltype(ids_v<T>[0])>;
3766
         size_t type_index = ids_v<T>.size();
3767
         bool tag_seen = false;
3768
         bool content_scanned = false;
3769
3770
         if (!walk([&](const sv key) {
3771
                if (!tag_seen && key == tag_v<T>) {
3772
                   std::conditional_t<std::integral<id_type>, id_type, sv> type_id{};
3773
                   if constexpr (std::integral<id_type>) {
3774
                      from<JSON, id_type>::template op<ws_handled<Opts>()>(type_id, ctx, it, end);
3775
                   }
3776
                   else {
3777
                      // The id is turned into an index below and never leaves this reader, so the
3778
                      // view of the window it borrows cannot outlive that window.
3779
                      parse_transient_string_view<ws_handled<Opts>()>(type_id, ctx, it, end);
3780
                   }
3781
                   if (bool(ctx.error)) [[unlikely]] {
3782
                      return false;
3783
                   }
3784
                   if constexpr (std::integral<id_type>) {
3785
                      type_index = variant_id_to_index<T>::op(type_id);
3786
                   }
3787
                   else {
3788
                      type_index =
3789
                         variant_id_to_index<T>::op(type_id.data(), type_id.data() + type_id.size(), type_id.size());
3790
                   }
3791
                   tag_seen = true;
3792
                   return true;
3793
                }
3794
                if (!content_scanned && key == content_v<T>) {
3795
                   content_scanned = true;
3796
                   skip_value<JSON>::op<Opts>(ctx, it, end);
3797
                   return !bool(ctx.error);
3798
                }
3799
                if constexpr (Opts.error_on_unknown_keys) {
3800
                   // Only the two declared keys belong in an adjacently tagged object, and each only
3801
                   // once. Anything else is the caller reading data this variant did not describe.
3802
                   ctx.error = error_code::unknown_key;
3803
                   return false;
3804
                }
3805
                skip_value<JSON>::op<Opts>(ctx, it, end);
3806
                return !bool(ctx.error);
3807
             })) {
3808
            return;
3809
         }
3810
3811
         if (!tag_seen) [[unlikely]] {
3812
            ctx.error = error_code::missing_key;
3813
            ctx.custom_error_message = variant_ids_string_v<T>;
3814
            return;
3815
         }
3816
3817
         size_t resolved = variant_size;
3818
         if (type_index < ids_v<T>.size()) [[likely]] {
3819
            resolved = type_index;
3820
         }
3821
         else if constexpr (ids_v<T>.size() < variant_size) {
3822
            // Fewer ids than alternatives: the first unlabeled alternative is the default for an
3823
            // unrecognized id, matching the internally tagged reader.
3824
            resolved = ids_v<T>.size();
3825
         }
3826
         if (resolved >= variant_size) [[unlikely]] {
3827
            ctx.error = error_code::no_matching_variant_type;
3828
            ctx.custom_error_message = variant_ids_string_v<T>;
3829
            return;
3830
         }
3831
3832
         if (value.index() != resolved) {
3833
            emplace_runtime_variant(value, resolved);
3834
         }
3835
3836
         bool content_seen = false;
3837
         if (!walk([&](const sv key) {
3838
                if (!content_seen && key == content_v<T>) {
3839
                   content_seen = true;
3840
                   std::visit([&](auto&& v) { parse<JSON>::op<ws_handled<Opts>()>(v, ctx, it, end); }, value);
3841
                   return !bool(ctx.error);
3842
                }
3843
                skip_value<JSON>::op<Opts>(ctx, it, end);
3844
                return !bool(ctx.error);
3845
             })) {
3846
            return;
3847
         }
3848
3849
         if (!content_seen) [[unlikely]] {
3850
            ctx.error = error_code::missing_key;
3851
            ctx.custom_error_message = content_v<T>;
3852
            return;
3853
         }
3854
         --ctx.depth;
3855
      }
3856
3857
      // Note that items in the variant are required to be default constructable for us to switch types
3858
      template <auto Options>
3859
      static void op(auto&& value, is_context auto&& ctx, auto&& it, auto end)
3860
      {
3861
         if constexpr (tagging == variant_tagging_kind::adjacent) {
3862
            read_adjacent<Options>(value, ctx, it, end);
3863
         }
3864
         else {
3865
            read_deduced<Options>(value, ctx, it, end);
3866
         }
3867
      }
3868
3869
      template <auto Options>
3870
      static void read_deduced(auto&& value, is_context auto&& ctx, auto&& it, auto end)
3871
      {
3872
         constexpr auto Opts = ws_handled_off<Options>();
3873
         if constexpr (variant_is_auto_deducible<T>()) {
3874
            if constexpr (not check_ws_handled(Options)) {
3875
               if (skip_ws<Opts>(ctx, it, end)) {
3876
                  return;
3877
               }
3878
            }
3879
3880
            switch (*it) {
3881
            case '\0':
3882
               ctx.error = error_code::unexpected_end;
3883
               return;
3884
            case '{': {
3885
               // This branch consumes the brace itself and hands the object to a reader with
3886
               // `opening_handled`, which therefore does not count the level -- but does release it at
3887
               // the closing brace. So the level is taken here, in both buffer modes, and paid back by
3888
               // the reader below.
3889
               if (enter_depth(ctx)) [[unlikely]] {
3890
                  return;
3891
               }
3892
3893
               ++it;
3894
               if constexpr (not Opts.null_terminated) {
3895
                  if (it == end) [[unlikely]] {
3896
                     ctx.error = error_code::unexpected_end;
3897
                     return;
3898
                  }
3899
               }
3900
               using type_counts = variant_type_count<T>;
3901
               constexpr auto n_object_candidates = variant_object_candidate_count_v<Opts, T>;
3902
               if constexpr (n_object_candidates < 1) {
3903
                  ctx.error = error_code::no_matching_variant_type;
3904
                  return;
3905
               }
3906
               else if constexpr (n_object_candidates == 1 && tag_v<T>.empty()) {
3907
                  constexpr auto first_idx = variant_first_object_candidate_v<Opts, T>;
3908
                  using V = std::variant_alternative_t<first_idx, T>;
3909
                  if (!std::holds_alternative<V>(value)) value = V{};
3910
                  parse<JSON>::op<opening_handled<Opts>()>(std::get<V>(value), ctx, it, end);
3911
                  return;
3912
               }
3913
               else {
3914
                  auto possible_types = bit_array<std::variant_size_v<T>>{}.flip();
3915
                  static constexpr auto& deduction_bits = variant_deduction_bits<T>;
3916
                  static constexpr size_t deduction_key_count = variant_deduction_key_count<T>;
3917
                  static constexpr auto tag_literal = string_literal_from_view<tag_v<T>.size()>(tag_v<T>);
3918
3919
                  // Track if we've encountered a tag and what value it had
3920
                  std::optional<size_t> tag_specified_index{};
3921
3922
                  if (skip_ws<Opts>(ctx, it, end)) {
3923
                     return;
3924
                  }
3925
                  // Once the reader knows which alternative this object holds it may have to read
3926
                  // it again from here. Anchor the position rather than keep the pointer: the scan
3927
                  // below skips values, and a streaming skip refills, which moves the window out
3928
                  // from under a raw pointer (see rewind_anchor).
3929
                  auto start = make_rewind_anchor(ctx, it);
3930
                  bool first_key = true;
3931
                  // Parse bifurcation for tagged variants:
3932
                  //
3933
                  // Without this optimization, tagged variant parsing always uses two passes:
3934
                  //   1. Scan all keys to find the tag (e.g. "type"), skipping values
3935
                  //   2. Reset `it = start` and re-parse the entire object into the resolved type
3936
                  //
3937
                  // When the tag is the first key, the second pass redundantly re-reads every
3938
                  // field that follows it. This lambda avoids that: if the tag was the first key,
3939
                  // it advances past the trailing comma so the object parser picks up at the
3940
                  // second field. If the tag was found later, it falls back to the full re-parse.
3941
                  //
3942
                  // One exception: if the tag name is also a field on the resolved struct
3943
                  // (contains_tag), we must re-parse regardless so the value is stored in
3944
                  // the struct field. This override is applied inside each std::visit lambda.
3945
                  //
3946
                  // Either way the rewind can fail under a streaming read: the window only holds so
3947
                  // much, and an object wider than it has no start left to return to.
3948
                  auto position_after_tag = [&] {
3949
                     if (first_key) {
3950
                        if (*it == ',') ++it; // skip comma; handles tag-only objects where *it == '}'
3951
                        return true;
3952
                     }
3953
                     return start.rewind(ctx, it, end); // tag came later, re-parse from the beginning
3954
                  };
3955
                  // `first_key` also answers "were we reset to the object start", which is what
3956
                  // position_after_tag does for every key but the first.
3957
                  for (; *it != '}'; first_key = false) {
3958
                     if (!first_key) {
3959
                        if (match_invalid_end<',', Opts>(ctx, it, end)) {
3960
                           return;
3961
                        }
3962
                     }
3963
3964
                     if (skip_ws<Opts>(ctx, it, end)) {
3965
                        return;
3966
                     }
3967
                     if (match_invalid_end<'"', Opts>(ctx, it, end)) {
3968
                        return;
3969
                     }
3970
3971
                     auto* key_start = it;
3972
                     skip_string_view(ctx, it, end);
3973
                     if (bool(ctx.error)) [[unlikely]]
3974
                        return;
3975
                     if (validate_utf8_span<Opts>(ctx, key_start, it)) [[unlikely]]
3976
                        return;
3977
                     const sv key = {key_start, size_t(it - key_start)};
3978
3979
                     if (match_invalid_end<'"', Opts>(ctx, it, end)) {
3980
                        return;
3981
                     }
3982
3983
                     if constexpr (deduction_key_count > 0) {
3984
                        // We first check if a tag is defined and see if the key matches the tag
3985
                        if constexpr (not tag_v<T>.empty()) {
3986
                           if (key == tag_v<T>) {
3987
                              if (parse_ws_colon<Opts>(ctx, it, end)) {
3988
                                 return;
3989
                              }
3990
3991
                              using id_type = std::decay_t<decltype(ids_v<T>[0])>;
3992
3993
                              std::conditional_t<std::integral<id_type>, id_type, sv> type_id{};
3994
                              if constexpr (std::integral<id_type>) {
3995
                                 from<JSON, id_type>::template op<ws_handled<Opts>()>(type_id, ctx, it, end);
3996
                              }
3997
                              else {
3998
                                 // The id is turned into an index below and never leaves this
3999
                                 // reader, so the view of the window it borrows cannot outlive it.
4000
                                 parse_transient_string_view<ws_handled<Opts>()>(type_id, ctx, it, end);
4001
                              }
4002
                              if (bool(ctx.error)) [[unlikely]]
4003
                                 return;
4004
                              if (skip_ws<Opts>(ctx, it, end)) {
4005
                                 return;
4006
                              }
4007
                              if (!(*it == ',' || *it == '}')) [[unlikely]] {
4008
                                 ctx.error = error_code::syntax_error;
4009
                                 return;
4010
                              }
4011
4012
                              size_t type_index;
4013
                              if constexpr (std::integral<id_type>) {
4014
                                 type_index = variant_id_to_index<T>::op(type_id);
4015
                              }
4016
                              else {
4017
                                 type_index = variant_id_to_index<T>::op(
4018
                                    type_id.data(), type_id.data() + type_id.size(), type_id.size());
4019
                              }
4020
                              if (type_index < ids_v<T>.size()) [[likely]] {
4021
                                 // Check if the deduced types include the tag-specified type
4022
                                 // If not, the tag doesn't match the fields
4023
                                 // We're already inside if constexpr (deduction_registry.size()), so we know deduction
4024
                                 // is happening. At this point, possible_types has been narrowed by field deduction.
4025
                                 // Check if the tag-specified type is still possible
4026
                                 const bool type_is_possible = possible_types[type_index];
4027
                                 if (!type_is_possible) {
4028
                                    // Tag specifies a type that doesn't match the fields seen so far
4029
                                    ctx.error = error_code::no_matching_variant_type;
4030
                                    return;
4031
                                 }
4032
4033
                                 if (!position_after_tag()) return;
4034
                                 tag_specified_index = type_index; // Store the tag-specified type
4035
                                 if (value.index() != type_index) emplace_runtime_variant(value, type_index);
4036
                                 std::visit(
4037
                                    [&](auto&& v) {
4038
                                       using V = std::decay_t<decltype(v)>;
4039
                                       constexpr bool is_object =
4040
                                          (glaze_object_t<V> || reflectable<V>) && not custom_read<V>;
4041
                                       if constexpr (variant_unit_alternative<V>) {
4042
                                          // A unit alternative carries no data; its object holds only the
4043
                                          // discriminator. Consume that body through an empty reflected object so
4044
                                          // unknown-key policy and terminator handling match every other alternative.
4045
                                          detail::variant_unit_body unit_body{};
4046
                                          from<JSON, detail::variant_unit_body>::template op<opening_handled<Opts>(),
4047
                                                                                             tag_literal>(unit_body,
4048
                                                                                                          ctx, it, end);
4049
                                       }
4050
                                       else if constexpr (is_object) {
4051
                                          if constexpr (contains_tag<V, tag_literal>()) {
4052
                                             // tag is a struct field, must re-parse
4053
                                             if (!start.rewind(ctx, it, end)) return;
4054
                                          }
4055
                                          from<JSON, V>::template op<opening_handled<Opts>(), tag_literal>(v, ctx, it,
4056
                                                                                                           end);
4057
                                       }
4058
                                       else if constexpr (is_memory_object<V>) {
4059
                                          if (!v) {
4060
                                             if constexpr (is_specialization_v<V, std::optional>) {
4061
                                                if constexpr (requires { v.emplace(); }) {
4062
                                                   v.emplace();
4063
                                                }
4064
                                                else {
4065
                                                   v = typename V::value_type{};
4066
                                                }
4067
                                             }
4068
                                             else if constexpr (is_specialization_v<V, std::unique_ptr>)
4069
                                                v = std::make_unique<typename V::element_type>();
4070
                                             else if constexpr (is_specialization_v<V, std::shared_ptr>)
4071
                                                v = std::make_shared<typename V::element_type>();
4072
                                             else if constexpr (constructible<V>) {
4073
                                                v = meta_construct_v<V>();
4074
                                             }
4075
                                             else if constexpr (std::is_pointer_v<V> &&
4076
                                                                can_allocate_raw_pointer<Opts,
4077
                                                                                         std::decay_t<decltype(ctx)>>) {
4078
                                                if (!try_allocate_raw_pointer<Opts>(v, ctx)) {
4079
                                                   return;
4080
                                                }
4081
                                             }
4082
                                             else {
4083
                                                ctx.error = error_code::invalid_nullable_read;
4084
                                                return;
4085
                                                // Cannot read into unset nullable that is not std::optional,
4086
                                                // std::unique_ptr, or std::shared_ptr
4087
                                             }
4088
                                          }
4089
                                          if constexpr (contains_tag<memory_type<V>, tag_literal>()) {
4090
                                             // tag is a struct field, must re-parse
4091
                                             if (!start.rewind(ctx, it, end)) return;
4092
                                          }
4093
                                          from<JSON, memory_type<V>>::template op<opening_handled<Opts>(), tag_literal>(
4094
                                             *v, ctx, it, end);
4095
                                       }
4096
                                       else if constexpr (custom_read<V>) {
4097
                                          // Custom handlers parse the remaining body themselves and do not
4098
                                          // accept the tag template parameter; the tag was already consumed.
4099
                                          // This only works when the tag was the first key. If it came later,
4100
                                          // we were reset to the object start and the custom handler would
4101
                                          // absorb the tag, so error rather than corrupt the value.
4102
                                          if (!first_key) {
4103
                                             ctx.error = error_code::feature_not_supported;
4104
                                             ctx.custom_error_message =
4105
                                                "the tag must be the first key for a custom variant alternative";
4106
                                             return;
4107
                                          }
4108
                                          from<JSON, V>::template op<opening_handled<Opts>()>(v, ctx, it, end);
4109
                                       }
4110
                                    },
4111
                                    value);
4112
4113
                                 return; // we've decoded our target type
4114
                              }
4115
                              else [[unlikely]] {
4116
                                 // Check if we have a default type (ids array shorter than variant)
4117
                                 constexpr auto ids_size = ids_v<T>.size();
4118
                                 constexpr auto variant_size = std::variant_size_v<T>;
4119
                                 if constexpr (ids_size < variant_size) {
4120
                                    // Use the first unlabeled type as the default
4121
                                    const auto default_type_index = ids_size;
4122
4123
                                    if (!position_after_tag()) return;
4124
                                    tag_specified_index = default_type_index; // Store the default type index
4125
                                    if (value.index() != default_type_index)
4126
                                       emplace_runtime_variant(value, default_type_index);
4127
                                    std::visit(
4128
                                       [&](auto&& v) {
4129
                                          using V = std::decay_t<decltype(v)>;
4130
                                          constexpr bool is_object =
4131
                                             (glaze_object_t<V> || reflectable<V>) && not custom_read<V>;
4132
                                          if constexpr (variant_unit_alternative<V>) {
4133
                                             // A unit alternative carries no data; its object holds only the
4134
                                             // discriminator. Consume that body through an empty reflected object so
4135
                                             // unknown-key policy and terminator handling match every other
4136
                                             // alternative.
4137
                                             detail::variant_unit_body unit_body{};
4138
                                             from<JSON, detail::variant_unit_body>::template op<opening_handled<Opts>(),
4139
                                                                                                tag_literal>(
4140
                                                unit_body, ctx, it, end);
4141
                                          }
4142
                                          else if constexpr (is_object) {
4143
                                             if constexpr (contains_tag<V, tag_literal>()) {
4144
                                                // tag is a struct field, must re-parse
4145
                                                if (!start.rewind(ctx, it, end)) return;
4146
                                             }
4147
                                             from<JSON, V>::template op<opening_handled<Opts>()>(v, ctx, it, end);
4148
                                          }
4149
                                          else if constexpr (is_memory_object<V>) {
4150
                                             if (!v) {
4151
                                                if constexpr (is_specialization_v<V, std::optional>) {
4152
                                                   if constexpr (requires { v.emplace(); }) {
4153
                                                      v.emplace();
4154
                                                   }
4155
                                                   else {
4156
                                                      v = typename V::value_type{};
4157
                                                   }
4158
                                                }
4159
                                                else if constexpr (is_specialization_v<V, std::unique_ptr>)
4160
                                                   v = std::make_unique<typename V::element_type>();
4161
                                                else if constexpr (is_specialization_v<V, std::shared_ptr>)
4162
                                                   v = std::make_shared<typename V::element_type>();
4163
                                                else if constexpr (constructible<V>) {
4164
                                                   v = meta_construct_v<V>();
4165
                                                }
4166
                                                else if constexpr (std::is_pointer_v<V> &&
4167
                                                                   can_allocate_raw_pointer<
4168
                                                                      Opts, std::decay_t<decltype(ctx)>>) {
4169
                                                   if (!try_allocate_raw_pointer<Opts>(v, ctx)) {
4170
                                                      return;
4171
                                                   }
4172
                                                }
4173
                                                else {
4174
                                                   ctx.error = error_code::invalid_nullable_read;
4175
                                                   return;
4176
                                                }
4177
                                             }
4178
                                             if constexpr (contains_tag<memory_type<V>, tag_literal>()) {
4179
                                                // tag is a struct field, must re-parse
4180
                                                if (!start.rewind(ctx, it, end)) return;
4181
                                             }
4182
                                             from<JSON, memory_type<V>>::template op<opening_handled<Opts>()>(*v, ctx,
4183
                                                                                                              it, end);
4184
                                          }
4185
                                          else if constexpr (custom_read<V>) {
4186
                                             // Custom handlers parse the remaining body themselves; this only
4187
                                             // works when the tag was the first key (see above). If it came
4188
                                             // later we were reset to the object start, so error rather than
4189
                                             // let the custom handler absorb the tag.
4190
                                             if (!first_key) {
4191
                                                ctx.error = error_code::feature_not_supported;
4192
                                                ctx.custom_error_message =
4193
                                                   "the tag must be the first key for a custom variant alternative";
4194
                                                return;
4195
                                             }
4196
                                             from<JSON, V>::template op<opening_handled<Opts>()>(v, ctx, it, end);
4197
                                          }
4198
                                       },
4199
                                       value);
4200
4201
                                    return;
4202
                                 }
4203
                                 else {
4204
                                    ctx.error = error_code::no_matching_variant_type;
4205
                                    ctx.custom_error_message = variant_ids_string_v<T>;
4206
                                    return;
4207
                                 }
4208
                              }
4209
                           }
4210
                        }
4211
4212
                        // Inline decode_hash lookup for variant deduction
4213
                        using deduction_keys_t = keys_wrapper<variant_deduction_keys<T>>;
4214
                        constexpr auto& DeductionHashInfo = hash_info<deduction_keys_t>;
4215
                        const auto deduction_index =
4216
                           decode_hash_with_size<JSON, deduction_keys_t, DeductionHashInfo, DeductionHashInfo.type>::op(
4217
                              key.data(), key.data() + key.size(), key.size());
4218
                        if (deduction_index < deduction_key_count && variant_deduction_keys<T>[deduction_index] == key)
4219
                           [[likely]] {
4220
                           possible_types &= deduction_bits[deduction_index];
4221
                        }
4222
                        else if constexpr (Opts.error_on_unknown_keys) {
4223
                           ctx.error = error_code::unknown_key;
4224
                           return;
4225
                        }
4226
                     }
4227
                     else if constexpr (not tag_v<T>.empty()) {
4228
                        // empty object case for variant, if there are no normal elements
4229
                        if (key == tag_v<T>) {
4230
                           if (parse_ws_colon<Opts>(ctx, it, end)) {
4231
                              return;
4232
                           }
4233
4234
                           // The id is turned into an index below and never leaves this reader,
4235
                           // so the view of the window it borrows cannot outlive that window.
4236
                           std::string_view type_id{};
4237
                           parse_transient_string_view<ws_handled<Opts>()>(type_id, ctx, it, end);
4238
                           if (bool(ctx.error)) [[unlikely]]
4239
                              return;
4240
                           if (skip_ws<Opts>(ctx, it, end)) {
4241
                              return;
4242
                           }
4243
4244
                           const auto type_index = variant_id_to_index<T>::op(
4245
                              type_id.data(), type_id.data() + type_id.size(), type_id.size());
4246
                           if (type_index < ids_v<T>.size()) [[likely]] {
4247
                              if (!position_after_tag()) return;
4248
                              tag_specified_index = type_index; // Store the tag-specified type
4249
                              if (value.index() != type_index) emplace_runtime_variant(value, type_index);
4250
                           }
4251
                           else {
4252
                              // Check if we have a default type (ids array shorter than variant)
4253
                              constexpr auto ids_size = ids_v<T>.size();
4254
                              constexpr auto variant_size = std::variant_size_v<T>;
4255
                              if constexpr (ids_size < variant_size) {
4256
                                 // Use the first unlabeled type as the default
4257
                                 if (!position_after_tag()) return;
4258
                                 const auto default_index = ids_size;
4259
                                 tag_specified_index = default_index; // Store the default type index
4260
                                 if (value.index() != default_index) emplace_runtime_variant(value, default_index);
4261
                              }
4262
                              else {
4263
                                 ctx.error = error_code::no_matching_variant_type;
4264
                                 ctx.custom_error_message = variant_ids_string_v<T>;
4265
                                 return;
4266
                              }
4267
                           }
4268
                           // Parse the type (handles tag skipping and unknown keys)
4269
                           std::visit(
4270
                              [&](auto&& v) {
4271
                                 using V = std::decay_t<decltype(v)>;
4272
                                 if constexpr (custom_read<V>) {
4273
                                    // Custom handlers parse the remaining object body themselves and do not
4274
                                    // accept the tag template parameter that reflected objects use to skip an
4275
                                    // interior tag key. That only works when the tag is the first key (it was
4276
                                    // just consumed). If the tag came after other keys, position_after_tag()
4277
                                    // reset us to the object start and the custom handler would absorb the tag
4278
                                    // into its value, so error here rather than corrupt the result.
4279
                                    if (!first_key) {
4280
                                       ctx.error = error_code::feature_not_supported;
4281
                                       ctx.custom_error_message =
4282
                                          "the tag must be the first key for a custom variant alternative";
4283
                                       return;
4284
                                    }
4285
                                    from<JSON, V>::template op<opening_handled<Opts>()>(v, ctx, it, end);
4286
                                 }
4287
                                 else {
4288
                                    if constexpr (contains_tag<V, tag_literal>()) {
4289
                                       // tag is a struct field, must re-parse
4290
                                       if (!start.rewind(ctx, it, end)) return;
4291
                                    }
4292
                                    from<JSON, V>::template op<opening_handled<Opts>(), tag_literal>(v, ctx, it, end);
4293
                                 }
4294
                              },
4295
                              value);
4296
                           return;
4297
                        }
4298
                        else if constexpr (Opts.error_on_unknown_keys) {
4299
                           ctx.error = error_code::unknown_key;
4300
                           return;
4301
                        }
4302
                        else {
4303
                           // Skip unknown key's value (non-tag key in empty variant)
4304
                           if (parse_ws_colon<Opts>(ctx, it, end)) {
4305
                              return;
4306
                           }
4307
                           skip_value<JSON>::op<Opts>(ctx, it, end);
4308
                           if (bool(ctx.error)) [[unlikely]]
4309
                              return;
4310
                           resync_window_end(ctx, end); // a streaming skip refills
4311
                           if (skip_ws<Opts>(ctx, it, end)) {
4312
                              return;
4313
                           }
4314
                           continue; // Continue loop to find the tag
4315
                        }
4316
                     }
4317
                     else if constexpr (Opts.error_on_unknown_keys) {
4318
                        ctx.error = error_code::unknown_key;
4319
                        return;
4320
                     }
4321
4322
                     auto matching_types = possible_types.popcount();
4323
                     if (matching_types == 0) {
4324
                        ctx.error = error_code::no_matching_variant_type;
4325
                        return;
4326
                     }
4327
                     // Only short-circuit for variants without tags
4328
                     else if constexpr (tag_v<T>.empty()) {
4329
                        if (matching_types == 1) {
4330
                           if (!start.rewind(ctx, it, end)) return;
4331
                           const auto type_index = possible_types.countr_zero();
4332
4333
                           if (value.index() != static_cast<size_t>(type_index))
4334
                              emplace_runtime_variant(value, type_index);
4335
                           std::visit(
4336
                              [&](auto&& v) {
4337
                                 using V = std::decay_t<decltype(v)>;
4338
                                 constexpr bool is_object = (glaze_object_t<V> || reflectable<V>) && not custom_read<V>;
4339
                                 if constexpr (variant_unit_alternative<V>) {
4340
                                    // A unit alternative carries no data; its object holds only the discriminator.
4341
                                    // Consume that body through an empty reflected object so unknown-key policy and
4342
                                    // terminator handling match every other alternative.
4343
                                    detail::variant_unit_body unit_body{};
4344
                                    from<JSON, detail::variant_unit_body>::template op<opening_handled<Opts>(),
4345
                                                                                       tag_literal>(unit_body, ctx, it,
4346
                                                                                                    end);
4347
                                 }
4348
                                 else if constexpr (is_object) {
4349
                                    from<JSON, V>::template op<opening_handled<Opts>(), tag_literal>(v, ctx, it, end);
4350
                                 }
4351
                                 else if constexpr (is_memory_object<V>) {
4352
                                    if (!v) {
4353
                                       if constexpr (is_specialization_v<V, std::optional>) {
4354
                                          if constexpr (requires { v.emplace(); }) {
4355
                                             v.emplace();
4356
                                          }
4357
                                          else {
4358
                                             v = typename V::value_type{};
4359
                                          }
4360
                                       }
4361
                                       else if constexpr (is_specialization_v<V, std::unique_ptr>)
4362
                                          v = std::make_unique<typename V::element_type>();
4363
                                       else if constexpr (is_specialization_v<V, std::shared_ptr>)
4364
                                          v = std::make_shared<typename V::element_type>();
4365
                                       else if constexpr (constructible<V>) {
4366
                                          v = meta_construct_v<V>();
4367
                                       }
4368
                                       else if constexpr (std::is_pointer_v<V> &&
4369
                                                          can_allocate_raw_pointer<Opts, std::decay_t<decltype(ctx)>>) {
4370
                                          if (!try_allocate_raw_pointer<Opts>(v, ctx)) {
4371
                                             return;
4372
                                          }
4373
                                       }
4374
                                       else {
4375
                                          ctx.error = error_code::invalid_nullable_read;
4376
                                          return;
4377
                                          // Cannot read into unset nullable that is not std::optional,
4378
                                          // std::unique_ptr, or std::shared_ptr
4379
                                       }
4380
                                    }
4381
                                    from<JSON, memory_type<V>>::template op<opening_handled<Opts>(), tag_literal>(
4382
                                       *v, ctx, it, end);
4383
                                 }
4384
                                 else if constexpr (custom_read<V>) {
4385
                                    // Custom handlers parse the remaining body themselves.
4386
                                    from<JSON, V>::template op<opening_handled<Opts>()>(v, ctx, it, end);
4387
                                 }
4388
                              },
4389
                              value);
4390
4391
                           return; // we've decoded our target type
4392
                        }
4393
                     }
4394
                     // For tagged variants, continue processing to validate tags
4395
                     if (parse_ws_colon<Opts>(ctx, it, end)) {
4396
                        return;
4397
                     }
4398
4399
                     skip_value<JSON>::op<Opts>(ctx, it, end);
4400
                     if (bool(ctx.error)) [[unlikely]]
4401
                        return;
4402
                     resync_window_end(ctx, end); // a streaming skip refills
4403
                     if (skip_ws<Opts>(ctx, it, end)) {
4404
                        return;
4405
                     }
4406
                  }
4407
                  // Only apply ambiguous variant resolution if we have multiple object types
4408
                  if constexpr ((type_counts::n_object + type_counts::n_nullable_object) > 1) {
4409
                     // After parsing all keys, check if we have multiple matching types
4410
                     // If so, choose the one with the fewest fields
4411
                     auto final_matching = possible_types.popcount();
4412
                     if (final_matching == 0) {
4413
                        ctx.error = error_code::no_matching_variant_type;
4414
                     }
4415
                     else if (final_matching == 1) {
4416
                        // Single type remains after field deduction
4417
                        const auto type_index = possible_types.countr_zero();
4418
4419
                        // Validate against tag if one was specified
4420
                        if (tag_specified_index.has_value() &&
4421
                            tag_specified_index.value() != static_cast<size_t>(type_index)) {
4422
                           ctx.error = error_code::no_matching_variant_type;
4423
                           return;
4424
                        }
4425
4426
                        if (!start.rewind(ctx, it, end)) return;
4427
                        if (value.index() != static_cast<size_t>(type_index))
4428
                           emplace_runtime_variant(value, type_index);
4429
                        std::visit(
4430
                           [&](auto&& v) {
4431
                              using V = std::decay_t<decltype(v)>;
4432
                              constexpr bool is_object = (glaze_object_t<V> || reflectable<V>) && not custom_read<V>;
4433
                              if constexpr (variant_unit_alternative<V>) {
4434
                                 // A unit alternative carries no data; its object holds only the discriminator.
4435
                                 // Consume that body through an empty reflected object so unknown-key policy and
4436
                                 // terminator handling match every other alternative.
4437
                                 detail::variant_unit_body unit_body{};
4438
                                 from<JSON, detail::variant_unit_body>::template op<opening_handled<Opts>(),
4439
                                                                                    tag_literal>(unit_body, ctx, it,
4440
                                                                                                 end);
4441
                              }
4442
                              else if constexpr (is_object) {
4443
                                 from<JSON, V>::template op<opening_handled<Opts>(), tag_literal>(v, ctx, it, end);
4444
                              }
4445
                              else if constexpr (is_memory_object<V>) {
4446
                                 if (!v) {
4447
                                    if constexpr (is_specialization_v<V, std::optional>) {
4448
                                       if constexpr (requires { v.emplace(); }) {
4449
                                          v.emplace();
4450
                                       }
4451
                                       else {
4452
                                          v = typename V::value_type{};
4453
                                       }
4454
                                    }
4455
                                    else if constexpr (is_specialization_v<V, std::unique_ptr>)
4456
                                       v = std::make_unique<typename V::element_type>();
4457
                                    else if constexpr (is_specialization_v<V, std::shared_ptr>)
4458
                                       v = std::make_shared<typename V::element_type>();
4459
                                    else if constexpr (constructible<V>) {
4460
                                       v = meta_construct_v<V>();
4461
                                    }
4462
                                    else if constexpr (std::is_pointer_v<V> &&
4463
                                                       can_allocate_raw_pointer<Opts, std::decay_t<decltype(ctx)>>) {
4464
                                       if (!try_allocate_raw_pointer<Opts>(v, ctx)) {
4465
                                          return;
4466
                                       }
4467
                                    }
4468
                                    else {
4469
                                       ctx.error = error_code::invalid_nullable_read;
4470
                                       return;
4471
                                    }
4472
                                 }
4473
                                 from<JSON, memory_type<V>>::template op<opening_handled<Opts>(), tag_literal>(*v, ctx,
4474
                                                                                                               it, end);
4475
                              }
4476
                              else if constexpr (custom_read<V>) {
4477
                                 // Custom handlers parse the remaining body themselves.
4478
                                 from<JSON, V>::template op<opening_handled<Opts>()>(v, ctx, it, end);
4479
                              }
4480
                           },
4481
                           value);
4482
                     }
4483
                     else if (final_matching > 1) {
4484
                        constexpr auto N = std::variant_size_v<T>;
4485
4486
                        // Compile-time array of field counts for each variant type
4487
                        constexpr auto field_counts = []<size_t... I>(std::index_sequence<I...>) {
4488
                           return std::array<size_t, N> {
4489
                              ([]<size_t J = I>() -> size_t {
4490
                                 using V = std::decay_t<std::variant_alternative_t<J, T>>;
4491
                                 if constexpr (glaze_object_t<V> || reflectable<V>) {
4492
                                    return reflect<V>::size;
4493
                                 }
4494
                                 else if constexpr (is_memory_object<V>) {
4495
                                    using X = memory_type<V>;
4496
                                    if constexpr (glaze_object_t<X> || reflectable<X>) {
4497
                                       return reflect<X>::size;
4498
                                    }
4499
                                    else {
4500
                                       return (std::numeric_limits<size_t>::max)();
4501
                                    }
4502
                                 }
4503
                                 else {
4504
                                    return (std::numeric_limits<size_t>::max)();
4505
                                 }
4506
                              }.template operator()<I>())...
4507
                           };
4508
                        }(std::make_index_sequence<N>{});
4509
4510
                        // Find the type with minimum field count among the possible types
4511
                        size_t min_fields = (std::numeric_limits<size_t>::max)();
4512
                        size_t chosen_index = N; // Invalid index initially
4513
4514
                        for (size_t i = 0; i < N; ++i) {
4515
                           if (possible_types[i] && field_counts[i] < min_fields) {
4516
                              min_fields = field_counts[i];
4517
                              chosen_index = i;
4518
                           }
4519
                        }
4520
4521
                        if (chosen_index < N) {
4522
                           // Validate against tag if one was specified
4523
                           if (tag_specified_index.has_value() && tag_specified_index.value() != chosen_index) {
4524
                              ctx.error = error_code::no_matching_variant_type;
4525
                              return;
4526
                           }
4527
4528
                           if (!start.rewind(ctx, it, end)) return;
4529
                           if (value.index() != chosen_index) emplace_runtime_variant(value, chosen_index);
4530
                           std::visit(
4531
                              [&](auto&& v) {
4532
                                 using V = std::decay_t<decltype(v)>;
4533
                                 constexpr bool is_object = (glaze_object_t<V> || reflectable<V>) && not custom_read<V>;
4534
                                 if constexpr (variant_unit_alternative<V>) {
4535
                                    // A unit alternative carries no data; its object holds only the discriminator.
4536
                                    // Consume that body through an empty reflected object so unknown-key policy and
4537
                                    // terminator handling match every other alternative.
4538
                                    detail::variant_unit_body unit_body{};
4539
                                    from<JSON, detail::variant_unit_body>::template op<opening_handled<Opts>(),
4540
                                                                                       tag_literal>(unit_body, ctx, it,
4541
                                                                                                    end);
4542
                                 }
4543
                                 else if constexpr (is_object) {
4544
                                    from<JSON, V>::template op<opening_handled<Opts>(), tag_literal>(v, ctx, it, end);
4545
                                 }
4546
                                 else if constexpr (is_memory_object<V>) {
4547
                                    if (!v) {
4548
                                       if constexpr (is_specialization_v<V, std::optional>) {
4549
                                          if constexpr (requires { v.emplace(); }) {
4550
                                             v.emplace();
4551
                                          }
4552
                                          else {
4553
                                             v = typename V::value_type{};
4554
                                          }
4555
                                       }
4556
                                       else if constexpr (is_specialization_v<V, std::unique_ptr>)
4557
                                          v = std::make_unique<typename V::element_type>();
4558
                                       else if constexpr (is_specialization_v<V, std::shared_ptr>)
4559
                                          v = std::make_shared<typename V::element_type>();
4560
                                       else if constexpr (constructible<V>) {
4561
                                          v = meta_construct_v<V>();
4562
                                       }
4563
                                       else if constexpr (std::is_pointer_v<V> &&
4564
                                                          can_allocate_raw_pointer<Opts, std::decay_t<decltype(ctx)>>) {
4565
                                          if (!try_allocate_raw_pointer<Opts>(v, ctx)) {
4566
                                             return;
4567
                                          }
4568
                                       }
4569
                                       else {
4570
                                          ctx.error = error_code::invalid_nullable_read;
4571
                                          return;
4572
                                       }
4573
                                    }
4574
                                    from<JSON, memory_type<V>>::template op<opening_handled<Opts>(), tag_literal>(
4575
                                       *v, ctx, it, end);
4576
                                 }
4577
                                 else if constexpr (custom_read<V>) {
4578
                                    // Custom handlers parse the remaining body themselves.
4579
                                    from<JSON, V>::template op<opening_handled<Opts>()>(v, ctx, it, end);
4580
                                 }
4581
                              },
4582
                              value);
4583
                        }
4584
                        else {
4585
                           ctx.error = error_code::no_matching_variant_type;
4586
                        }
4587
                     }
4588
                  }
4589
                  else {
4590
                     // For variants with 0 or 1 object types, use the original error handling
4591
                     ctx.error = error_code::no_matching_variant_type;
4592
                  }
4593
               }
4594
               break;
4595
            }
4596
            case '[':
4597
               // As with '{' above: the array reader each alternative goes through counts the level.
4598
               process_variant_alternatives<T, is_variant_array>::template op<Opts>(value, ctx, it, end);
4599
               break;
4600
            case '"': {
4601
               process_variant_alternatives<T, is_variant_str>::template op<Opts>(value, ctx, it, end);
4602
               break;
4603
            }
4604
            case 't':
4605
            case 'f': {
4606
               process_variant_alternatives<T, is_variant_bool>::template op<Opts>(value, ctx, it, end);
4607
               break;
4608
            }
4609
            case 'n':
4610
               if constexpr (variant_count_v<T, is_variant_null> == 0) {
4611
                  ctx.error = error_code::no_matching_variant_type;
4612
               }
4613
               else {
4614
                  constexpr auto first_idx = variant_first_index_v<T, is_variant_null>;
4615
                  using V = std::variant_alternative_t<first_idx, T>;
4616
                  if (!std::holds_alternative<V>(value)) value = V{};
4617
                  match<"null", Opts>(ctx, it, end);
4618
               }
4619
               break;
4620
            default: {
4621
               // Not bool, string, object, or array so must be number or null
4622
               process_variant_alternatives<T, is_variant_num>::template op<Opts>(value, ctx, it, end);
4623
            }
4624
            }
4625
         }
4626
         else {
4627
            // For non-auto-deducible variants, try each type until one succeeds
4628
            constexpr auto N = std::variant_size_v<T>;
4629
            bool parsed = false;
4630
            bool exhausted = false; // speculation budget spent; see charge_speculation
4631
4632
            for_each<N>([&]<size_t I>() {
4633
               if (parsed || exhausted || variant_alternative_exhausted(ctx)) return;
4634
4635
               auto copy_it = it;
4636
               // A rejected alternative may have bailed out inside a container, which leaves its
4637
               // nesting level counted (see enter_depth), so the depth rewinds with the iterator.
4638
               const auto copy_depth = ctx.depth;
4639
4640
               // Try parsing as this type
4641
               if (value.index() != I) {
4642
                  emplace_runtime_variant(value, I);
4643
               }
4644
4645
               std::visit([&](auto&& v) { parse<JSON>::op<Options>(v, ctx, it, end); }, value);
4646
               if (bool(ctx.error) && !charge_speculation(ctx, size_t(it - copy_it))) {
4647
                  exhausted = true; // only rejected attempts are charged; see charge_speculation
4648
               }
4649
4650
               if (!bool(ctx.error)) {
4651
                  parsed = true;
4652
               }
4653
               else if constexpr (not Options.null_terminated) {
4654
                  // In non-null-terminated mode, if we hit end_reached after advancing the iterator,
4655
                  // it means we successfully parsed the value but couldn't skip trailing whitespace
4656
                  if (ctx.error == error_code::end_reached && it > copy_it && not speculation_exhausted(ctx)) {
4657
                     // We advanced the iterator, so we did parse something -- unless what advanced it
4658
                     // was an abandoned attempt, which is what a spent speculation budget means.
4659
                     parsed = true;
4660
                     ctx.error = error_code::none;
4661
                  }
4662
                  else {
4663
                     // Reset for next attempt (unless this is the last type)
4664
                     it = copy_it;
4665
                     rewind_depth<Options>(ctx, copy_depth);
4666
                     if constexpr (I + 1 < N) {
4667
                        if (not exhausted && not variant_alternative_exhausted(ctx)) {
4668
                           ctx.error = error_code::none; // Clear error for next attempt
4669
                        }
4670
                     }
4671
                  }
4672
               }
4673
               else {
4674
                  // Reset for next attempt (unless this is the last type)
4675
                  it = copy_it;
4676
                  rewind_depth<Options>(ctx, copy_depth);
4677
                  if constexpr (I + 1 < N) {
4678
                     if (not exhausted && not variant_alternative_exhausted(ctx)) {
4679
                        ctx.error = error_code::none; // Clear error for next attempt
4680
                     }
4681
                  }
4682
               }
4683
            });
4684
         }
4685
      }
4686
   };
4687
4688
   template <class T>
4689
   struct from<JSON, array_variant_wrapper<T>>
4690
   {
4691
      template <auto Options>
4692
      static void op(auto&& wrapper, is_context auto&& ctx, auto&& it, auto end)
4693
      {
4694
         auto& value = wrapper.value;
4695
4696
         constexpr auto Opts = ws_handled_off<Options>();
4697
         if constexpr (!check_ws_handled(Options)) {
4698
            if (skip_ws<Opts>(ctx, it, end)) {
4699
               return;
4700
            }
4701
         }
4702
4703
         if (match_invalid_end<'[', Opts>(ctx, it, end)) {
4704
            return;
4705
         }
4706
         // one nesting level (see enter_depth): counted in both modes so the limit binds,
4707
         // released at each syntactic close below
4708
         if (enter_depth(ctx)) [[unlikely]] {
4709
            return;
4710
         }
4711
         if (skip_ws<Opts>(ctx, it, end)) {
4712
            return;
4713
         }
4714
4715
         // TODO Use key parsing for compiletime known keys
4716
         if (match_invalid_end<'"', Opts>(ctx, it, end)) {
4717
            return;
4718
         }
4719
         auto start = it;
4720
         skip_string_view(ctx, it, end);
4721
         if (bool(ctx.error)) [[unlikely]]
4722
            return;
4723
         sv type_id = {start, size_t(it - start)};
4724
         if (match<'"'>(ctx, it)) {
4725
            return;
4726
         }
4727
         if constexpr (not Opts.null_terminated) {
4728
            if (it == end) {
4729
               ctx.error = error_code::end_reached;
4730
               return;
4731
            }
4732
         }
4733
4734
         const auto type_index =
4735
            variant_id_to_index<T>::op(type_id.data(), type_id.data() + type_id.size(), type_id.size());
4736
         if (type_index < ids_v<T>.size()) [[likely]] {
4737
            if (skip_ws<Opts>(ctx, it, end)) {
4738
               return;
4739
            }
4740
            if (match_invalid_end<',', Opts>(ctx, it, end)) {
4741
               return;
4742
            }
4743
            if (value.index() != type_index) emplace_runtime_variant(value, type_index);
4744
            std::visit([&](auto&& v) { parse<JSON>::op<Opts>(v, ctx, it, end); }, value);
4745
            if (bool(ctx.error)) [[unlikely]]
4746
               return;
4747
         }
4748
         else {
4749
            ctx.error = error_code::no_matching_variant_type;
4750
            return;
4751
         }
4752
4753
         if (skip_ws<Opts>(ctx, it, end)) {
4754
            return;
4755
         }
4756
         match<']'>(ctx, it);
4757
         --ctx.depth;
4758
      }
4759
   };
4760
4761
   template <is_expected T>
4762
   struct from<JSON, T>
4763
   {
4764
      template <auto Opts, class... Args>
4765
      static void op(auto&& value, is_context auto&& ctx, auto&& it, auto end)
4766
      {
4767
         if constexpr (!check_ws_handled(Opts)) {
4768
            if (skip_ws<Opts>(ctx, it, end)) {
4769
               return;
4770
            }
4771
         }
4772
4773
         auto parse_val = [&] {
4774
            if constexpr (not std::is_void_v<decltype(*value)>) {
4775
               if (value) {
4776
                  parse<JSON>::op<Opts>(*value, ctx, it, end);
4777
               }
4778
               else {
4779
                  value.emplace();
4780
                  parse<JSON>::op<Opts>(*value, ctx, it, end);
4781
               }
4782
            }
4783
            else {
4784
               value.emplace();
4785
            }
4786
         };
4787
4788
         if (*it == '{') {
4789
            // One level, held only while this reader scans the wrapper. Every path below releases it:
4790
            // the two that rewind to `start` do so before delegating, since the reader they hand the
4791
            // object to counts the level itself, and the {"unexpected": value} branch releases at the
4792
            // closing brace. Releasing on all three matters because this is the ordinary path -- a
4793
            // flat array of a few hundred expected values would otherwise spend the whole budget --
4794
            // and holding it on the error paths is what keeps a truncated wrapper from finalizing as
4795
            // success on a non-null-terminated buffer.
4796
            if (enter_depth(ctx)) [[unlikely]] {
4797
               return;
4798
            }
4799
            auto start = it;
4800
            ++it;
4801
            if constexpr (not Opts.null_terminated) {
4802
               if (it == end) [[unlikely]] {
4803
                  ctx.error = error_code::unexpected_end;
4804
                  return;
4805
               }
4806
            }
4807
            if (skip_ws<Opts>(ctx, it, end)) {
4808
               return;
4809
            }
4810
            if (*it == '}') {
4811
               --ctx.depth; // released before delegating; parse_val re-reads the object and counts it
4812
               it = start;
4813
               // empty object
4814
               parse_val();
4815
            }
4816
            else {
4817
               // either we have an unexpected value or we are decoding an object
4818
               ctx.scratch.clear();
4819
               parse<JSON>::op<Opts>(ctx.scratch, ctx, it, end);
4820
               if (bool(ctx.error)) [[unlikely]]
4821
                  return;
4822
               if (ctx.scratch == "unexpected") {
4823
                  if (skip_ws<Opts>(ctx, it, end)) {
4824
                     return;
4825
                  }
4826
                  if (match_invalid_end<':', Opts>(ctx, it, end)) {
4827
                     return;
4828
                  }
4829
                  // read in unexpected value
4830
                  if (!value) {
4831
                     parse<JSON>::op<Opts>(value.error(), ctx, it, end);
4832
                     if (bool(ctx.error)) [[unlikely]]
4833
                        return;
4834
                  }
4835
                  else {
4836
                     // set value to unexpected
4837
                     using error_ctx = typename std::decay_t<decltype(value)>::error_type;
4838
                     std::decay_t<error_ctx> error{};
4839
                     parse<JSON>::op<Opts>(error, ctx, it, end);
4840
                     if (bool(ctx.error)) [[unlikely]]
4841
                        return;
4842
                     value = glz::unexpected(error);
4843
                  }
4844
                  if (skip_ws<Opts>(ctx, it, end)) {
4845
                     return;
4846
                  }
4847
                  match<'}'>(ctx, it);
4848
                  --ctx.depth;
4849
               }
4850
               else {
4851
                  --ctx.depth; // as above: released before delegating
4852
                  it = start;
4853
                  parse_val();
4854
               }
4855
            }
4856
         }
4857
         else {
4858
            // this is not an object and therefore cannot be an unexpected value
4859
            parse_val();
4860
         }
4861
      }
4862
   };
4863
4864
   template <nullable_t T>
4865
      requires(std::is_array_v<T>)
4866
   struct from<JSON, T>
4867
   {
4868
      template <auto Opts, class V, size_t N>
4869
      GLZ_ALWAYS_INLINE static void op(V (&value)[N], is_context auto&& ctx, auto&& it, auto end) noexcept
4870
      {
4871
         parse<JSON>::op<Opts>(std::span{value, N}, ctx, it, end);
4872
      }
4873
   };
4874
4875
   template <class T>
4876
      requires((nullable_like<T> || nullable_value_t<T>) && not is_expected<T> && not std::is_array_v<T> &&
4877
               not custom_read<T>) // is_expected and is_array_v are redundant with nullable_like but needed for
4878
                                   // nullable_value_t
4879
   struct from<JSON, T>
4880
   {
4881
      template <auto Options>
4882
      static void op(auto&& value, is_context auto&& ctx, auto&& it, auto end)
4883
      {
4884
         constexpr auto Opts = ws_handled_off<Options>();
4885
         if constexpr (!check_ws_handled(Options)) {
4886
            if (skip_ws<Opts>(ctx, it, end)) {
4887
               return;
4888
            }
4889
         }
4890
4891
         if (*it == 'n') {
4892
            ++it;
4893
            if constexpr (not Opts.null_terminated) {
4894
               if (it == end) [[unlikely]] {
4895
                  ctx.error = error_code::unexpected_end;
4896
                  return;
4897
               }
4898
            }
4899
            match<"ull", Opts>(ctx, it, end);
4900
            if (bool(ctx.error)) [[unlikely]]
4901
               return;
4902
            if constexpr (requires { value.reset(); }) {
4903
               value.reset();
4904
            }
4905
         }
4906
         else {
4907
            if constexpr (nullable_value_t<T>) {
4908
               if (not value.has_value()) {
4909
                  if constexpr (constructible<T>) {
4910
                     value = meta_construct_v<T>();
4911
                  }
4912
                  else if constexpr (requires { value.emplace(); }) {
4913
                     value.emplace();
4914
                  }
4915
                  else {
4916
                     static_assert(false_v<T>,
4917
                                   "Your nullable type must have `emplace()` or be glz::meta constructible, or "
4918
                                   "create a custom glz::from specialization");
4919
                  }
4920
               }
4921
               parse<JSON>::op<Opts>(value.value(), ctx, it, end);
4922
            }
4923
            else {
4924
               if (!value) {
4925
                  if (!nullable_emplace<Opts>(value, ctx)) {
4926
                     return;
4927
                  }
4928
               }
4929
               parse<JSON>::op<Opts>(*value, ctx, it, end);
4930
            }
4931
         }
4932
      }
4933
   };
4934
4935
   template <filesystem_path T>
4936
   struct from<JSON, T>
4937
   {
4938
      template <auto Opts>
4939
      static void op(auto&& value, is_context auto&& ctx, auto&& it, auto end)
4940
      {
4941
         ctx.scratch.clear();
4942
         parse<JSON>::op<Opts>(ctx.scratch, ctx, it, end);
4943
         if (parse_failed(ctx.error)) [[unlikely]] {
4944
            return;
4945
         }
4946
         value = ctx.scratch;
4947
      }
4948
   };
4949
4950
   // ============================================
4951
   // std::chrono deserialization
4952
   // ============================================
4953
4954
   // Duration: parsed generically (from the bare rep count) by the
4955
   // from<uint32_t Format, is_duration T> specialization in core/chrono.hpp.
4956
4957
   // system_clock / utc_clock time_point: parse from ISO 8601 string (utc_clock also accepts a
4958
   // leap second, :60).
4959
   // Time points whose Duration period is exactly `days` (e.g. std::chrono::sys_days)
4960
   // also accept the bare "YYYY-MM-DD" date form; full ISO 8601 datetimes are still
4961
   // accepted in that case and floored to days precision. Coarser periods (weeks,
4962
   // months, years) intentionally fall through to the full ISO 8601 parser so that
4963
   // user-supplied dates are not silently snapped to a multi-day boundary.
4964
   template <is_calendar_time_point T>
4965
      requires(not custom_read<T>)
4966
   struct from<JSON, T>
4967
   {
4968
      template <auto Opts, class It0, class It1>
4969
      static void op(auto&& value, is_context auto&& ctx, It0&& it, It1 end) noexcept
4970
      {
4971
         std::string_view str;
4972
         parse_transient_string_view<Opts>(str, ctx, it, end);
4973
         if (bool(ctx.error)) [[unlikely]]
4974
            return;
4975
4976
         using Duration = typename std::remove_cvref_t<T>::duration;
4977
         using Period = typename Duration::period;
4978
         if constexpr (std::ratio_equal_v<Period, std::ratio<86400>>) {
4979
            if (str.size() == 10) {
4980
               std::chrono::year_month_day ymd{};
4981
               chrono_detail::parse_ymd(str, ymd, ctx.error);
4982
               if (bool(ctx.error)) [[unlikely]]
4983
                  return;
4984
               value = std::chrono::time_point_cast<Duration>(std::chrono::sys_days{ymd});
4985
               return;
4986
            }
4987
         }
4988
         chrono_detail::parse_iso8601(str, value, ctx.error);
4989
      }
4990
   };
4991
4992
   // year_month_day: parse from "YYYY-MM-DD" JSON string
4993
   template <is_year_month_day T>
4994
      requires(not custom_read<T>)
4995
   struct from<JSON, T>
4996
   {
4997
      template <auto Opts, class It0, class It1>
4998
      static void op(auto&& value, is_context auto&& ctx, It0&& it, It1 end) noexcept
4999
      {
5000
         std::string_view str;
5001
         parse_transient_string_view<Opts>(str, ctx, it, end);
5002
         if (bool(ctx.error)) [[unlikely]]
5003
            return;
5004
         chrono_detail::parse_ymd(str, value, ctx.error);
5005
      }
5006
   };
5007
5008
   // steady_clock / high_resolution_clock time_points: parsed generically (from the bare
5009
   // count) by the from<uint32_t Format, is_count_time_point T> specialization in core/chrono.hpp.
5010
5011
   // epoch_time wrapper: parse as numeric Unix timestamp
5012
   template <class Duration>
5013
      requires(not custom_read<epoch_time<Duration>>)
5014
   struct from<JSON, epoch_time<Duration>>
5015
   {
5016
      template <auto Opts, class It0, class It1>
5017
      GLZ_ALWAYS_INLINE static void op(auto&& wrapper, is_context auto&& ctx, It0&& it, It1 end) noexcept
5018
      {
5019
         using Rep = typename Duration::rep;
5020
         Rep count{};
5021
         from<JSON, Rep>::template op<Opts>(count, ctx, it, end);
5022
         if (bool(ctx.error)) [[unlikely]]
5023
            return;
5024
         // Use duration_cast to handle precision differences between Duration and system_clock::duration
5025
         using sys_duration = std::chrono::system_clock::duration;
5026
         wrapper.value =
5027
            std::chrono::system_clock::time_point{std::chrono::duration_cast<sys_duration>(Duration{count})};
5028
      }
5029
   };
5030
5031
   struct opts_validate : opts
5032
   {
5033
      bool validate_skipped = true;
5034
      bool validate_trailing_whitespace = true;
5035
   };
5036
5037
   template <is_buffer Buffer>
5038
   [[nodiscard]] error_ctx validate_json(Buffer&& buffer) noexcept
5039
   {
5040
      context ctx{};
5041
      glz::skip skip_value{};
5042
      return read<opts_validate{}>(skip_value, std::forward<Buffer>(buffer), ctx);
5043
   }
5044
5045
   template <is_buffer Buffer>
5046
   [[nodiscard]] error_ctx validate_jsonc(Buffer&& buffer) noexcept
5047
   {
5048
      context ctx{};
5049
      glz::skip skip_value{};
5050
      return read<opts_validate{{opts{.comments = true}}}>(skip_value, std::forward<Buffer>(buffer), ctx);
5051
   }
5052
5053
   template <read_supported<JSON> T, is_buffer Buffer>
5054
      requires(!is_input_streaming<std::remove_reference_t<Buffer>>)
5055
   [[nodiscard]] error_ctx read_json(T& value, Buffer&& buffer)
5056
   {
5057
      context ctx{};
5058
      return read<opts{}>(value, std::forward<Buffer>(buffer), ctx);
5059
   }
5060
5061
   // Overload for streaming input buffers (istream_buffer)
5062
   template <read_supported<JSON> T, class Buffer>
5063
      requires is_input_streaming<std::remove_reference_t<Buffer>>
5064
   [[nodiscard]] error_ctx read_json(T& value, Buffer&& buffer)
5065
   {
5066
      return read_streaming<opts{}>(value, std::forward<Buffer>(buffer));
5067
   }
5068
5069
   template <read_supported<JSON> T, is_buffer Buffer>
5070
      requires(!is_input_streaming<std::remove_reference_t<Buffer>>)
5071
   [[nodiscard]] expected<T, error_ctx> read_json(Buffer&& buffer)
5072
   {
5073
      T value{};
5074
      context ctx{};
5075
      const error_ctx ec = read<opts{}>(value, std::forward<Buffer>(buffer), ctx);
5076
      if (ec) {
5077
         return unexpected<error_ctx>(ec);
5078
      }
5079
      return value;
5080
   }
5081
5082
   // Overload for streaming input buffers (istream_buffer)
5083
   template <read_supported<JSON> T, class Buffer>
5084
      requires is_input_streaming<std::remove_reference_t<Buffer>>
5085
   [[nodiscard]] expected<T, error_ctx> read_json(Buffer&& buffer)
5086
   {
5087
      T value{};
5088
      const error_ctx ec = read_streaming<opts{}>(value, std::forward<Buffer>(buffer));
5089
      if (ec) {
5090
         return unexpected<error_ctx>(ec);
5091
      }
5092
      return value;
5093
   }
5094
5095
   template <read_supported<JSON> T, is_buffer Buffer>
5096
   [[nodiscard]] error_ctx read_jsonc(T& value, Buffer&& buffer)
5097
   {
5098
      context ctx{};
5099
      return read<opts{.comments = true}>(value, std::forward<Buffer>(buffer), ctx);
5100
   }
5101
5102
   template <read_supported<JSON> T, is_buffer Buffer>
5103
   [[nodiscard]] expected<T, error_ctx> read_jsonc(Buffer&& buffer)
5104
   {
5105
      T value{};
5106
      context ctx{};
5107
      const error_ctx ec = read<opts{.comments = true}>(value, std::forward<Buffer>(buffer), ctx);
5108
      if (ec) {
5109
         return unexpected<error_ctx>(ec);
5110
      }
5111
      return value;
5112
   }
5113
5114
   template <auto Opts = opts{}, read_supported<JSON> T, is_buffer Buffer>
5115
   [[nodiscard]] error_ctx read_file_json(T& value, const sv file_name, Buffer&& buffer)
5116
   {
5117
      context ctx{};
5118
      ctx.current_file = file_name;
5119
5120
      const auto ec = file_to_buffer(buffer, ctx.current_file);
5121
5122
      if (bool(ec)) {
5123
         return {0, ec};
5124
      }
5125
5126
      // The buffer was sized to the file, so the caller's is_padded promise does not cover it.
5127
      return read<is_padded_off<set_json<Opts>()>()>(value, buffer, ctx);
5128
   }
5129
5130
   template <auto Opts = opts{}, read_supported<JSON> T, is_buffer Buffer>
5131
   [[nodiscard]] error_ctx read_file_jsonc(T& value, const sv file_name, Buffer&& buffer)
5132
   {
5133
      context ctx{};
5134
      ctx.current_file = file_name;
5135
5136
      const auto ec = file_to_buffer(buffer, ctx.current_file);
5137
5138
      if (bool(ec)) {
5139
         return {0, ec};
5140
      }
5141
5142
      // The buffer was sized to the file, so the caller's is_padded promise does not cover it.
5143
      constexpr auto Options = opt_true<is_padded_off<set_json<Opts>()>(), &opts::comments>;
5144
      return read<Options>(value, buffer, ctx);
5145
   }
5146
}
5147
5148
#if defined(_MSC_VER) && !defined(__clang__)
5149
// restore disabled warnings
5150
#pragma warning(pop)
5151
#endif