Coverage Report

Created: 2025-08-29 06:18

/src/glaze/include/glaze/json/write.hpp
Line
Count
Source (jump to first uncovered line)
1
// Glaze Library
2
// For the license information refer to glaze.hpp
3
4
#pragma once
5
6
#include <charconv>
7
#include <iterator>
8
#include <ostream>
9
#include <variant>
10
11
#if !defined(GLZ_DISABLE_SIMD) && (defined(__x86_64__) || defined(_M_X64))
12
#if defined(_MSC_VER)
13
#include <intrin.h>
14
#pragma warning(push)
15
#pragma warning( \
16
   disable : 4702) // disable "unreachable code" warnings, which are often invalid due to constexpr branching
17
#else
18
#include <immintrin.h>
19
#endif
20
21
#if defined(__AVX2__)
22
#define GLZ_USE_AVX2
23
#endif
24
#endif
25
26
#include "glaze/core/opts.hpp"
27
#include "glaze/core/reflect.hpp"
28
#include "glaze/core/to.hpp"
29
#include "glaze/core/write.hpp"
30
#include "glaze/core/write_chars.hpp"
31
#include "glaze/json/ptr.hpp"
32
#include "glaze/util/dump.hpp"
33
#include "glaze/util/for_each.hpp"
34
#include "glaze/util/itoa.hpp"
35
36
namespace glz
37
{
38
   // This serialize<JSON> indirection only exists to call std::remove_cvref_t on the type
39
   // so that type matching doesn't depend on qualifiers.
40
   // It is recommended to directly call to<JSON, std::remove_cvref_t<T>> to reduce compilation overhead.
41
   // TODO: Long term this can probably be [[deprecated]]
42
   // but it is useful for when getting the value type would be verbose
43
   template <>
44
   struct serialize<JSON>
45
   {
46
      template <auto Opts, class T, is_context Ctx, class B, class IX>
47
      GLZ_ALWAYS_INLINE static void op(T&& value, Ctx&& ctx, B&& b, IX&& ix)
48
      {
49
         to<JSON, std::remove_cvref_t<T>>::template op<Opts>(std::forward<T>(value), std::forward<Ctx>(ctx),
50
                                                             std::forward<B>(b), std::forward<IX>(ix));
51
      }
52
   };
53
54
   template <auto& Partial, auto Opts, class T, class Ctx, class B, class IX>
55
   concept write_json_partial_invocable = requires(T&& value, Ctx&& ctx, B&& b, IX&& ix) {
56
      to_partial<JSON, std::remove_cvref_t<T>>::template op<Partial, Opts>(
57
         std::forward<T>(value), std::forward<Ctx>(ctx), std::forward<B>(b), std::forward<IX>(ix));
58
   };
59
60
   template <>
61
   struct serialize_partial<JSON>
62
   {
63
      template <auto& Partial, auto Opts, class T, is_context Ctx, class B, class IX>
64
      GLZ_ALWAYS_INLINE static void op(T&& value, Ctx&& ctx, B&& b, IX&& ix)
65
      {
66
         if constexpr (std::count(Partial.begin(), Partial.end(), "") > 0) {
67
            serialize<JSON>::op<Opts>(value, ctx, b, ix);
68
         }
69
         else if constexpr (write_json_partial_invocable<Partial, Opts, T, Ctx, B, IX>) {
70
            to_partial<JSON, std::remove_cvref_t<T>>::template op<Partial, Opts>(
71
               std::forward<T>(value), std::forward<Ctx>(ctx), std::forward<B>(b), std::forward<IX>(ix));
72
         }
73
         else {
74
            static_assert(false_v<T>, "Glaze metadata is probably needed for your type");
75
         }
76
      }
77
   };
78
79
   template <auto Opts, bool minified_check = true, class B>
80
      requires(Opts.format == JSON || Opts.format == NDJSON)
81
   GLZ_ALWAYS_INLINE void write_object_entry_separator(is_context auto&& ctx, B&& b, auto&& ix)
82
0
   {
83
0
      if constexpr (Opts.prettify) {
84
0
         if constexpr (vector_like<B>) {
85
0
            if (const auto k = ix + ctx.indentation_level + write_padding_bytes; k > b.size()) [[unlikely]] {
86
0
               b.resize(2 * k);
87
0
            }
88
0
         }
89
0
         std::memcpy(&b[ix], ",\n", 2);
90
0
         ix += 2;
91
0
         std::memset(&b[ix], Opts.indentation_char, ctx.indentation_level);
92
0
         ix += ctx.indentation_level;
93
0
      }
94
0
      else {
95
0
         if constexpr (vector_like<B>) {
96
0
            if constexpr (minified_check) {
97
0
               if (ix >= b.size()) [[unlikely]] {
98
0
                  b.resize(2 * ix);
99
0
               }
100
0
            }
101
0
         }
102
0
         std::memcpy(&b[ix], ",", 1);
103
0
         ++ix;
104
0
      }
105
0
   }
106
107
   // Only object types are supported for partial
108
   template <class T>
109
      requires(glaze_object_t<T> || writable_map_t<T> || reflectable<T>)
110
   struct to_partial<JSON, T> final
111
   {
112
      template <auto& Partial, auto Opts, class... Args>
113
      static void op(auto&& value, is_context auto&& ctx, auto&& b, auto&& ix)
114
      {
115
         if constexpr (!check_opening_handled(Opts)) {
116
            dump<'{'>(b, ix);
117
            if constexpr (Opts.prettify) {
118
               ctx.indentation_level += Opts.indentation_width;
119
               dump<'\n'>(b, ix);
120
               dumpn<Opts.indentation_char>(ctx.indentation_level, b, ix);
121
            }
122
         }
123
124
         static constexpr auto sorted = sort_json_ptrs(Partial);
125
         static constexpr auto groups = glz::group_json_ptrs<sorted>();
126
         static constexpr auto N = glz::tuple_size_v<std::decay_t<decltype(groups)>>;
127
128
         static constexpr auto num_members = reflect<T>::size;
129
130
         if constexpr ((num_members > 0) && (glaze_object_t<T> || reflectable<T>)) {
131
            for_each<N>([&]<size_t I>() {
132
               if (bool(ctx.error)) [[unlikely]] {
133
                  return;
134
               }
135
136
               static constexpr auto group = glz::get<I>(groups);
137
138
               static constexpr auto key = get<0>(group);
139
               static constexpr auto quoted_key = quoted_key_v<key, Opts.prettify>;
140
               dump<quoted_key>(b, ix);
141
142
               static constexpr auto sub_partial = get<1>(group);
143
               static constexpr auto index = key_index<T>(key);
144
               static_assert(index < num_members, "Invalid key passed to partial write");
145
               if constexpr (glaze_object_t<T>) {
146
                  static constexpr auto member = get<index>(reflect<T>::values);
147
148
                  serialize_partial<JSON>::op<sub_partial, Opts>(get_member(value, member), ctx, b, ix);
149
                  if constexpr (I != N - 1) {
150
                     write_object_entry_separator<Opts>(ctx, b, ix);
151
                  }
152
               }
153
               else {
154
                  serialize_partial<JSON>::op<sub_partial, Opts>(get_member(value, get<index>(to_tie(value))), ctx, b,
155
                                                                 ix);
156
                  if constexpr (I != N - 1) {
157
                     write_object_entry_separator<Opts>(ctx, b, ix);
158
                  }
159
               }
160
            });
161
         }
162
         else if constexpr (writable_map_t<T>) {
163
            for_each<N>([&]<size_t I>() {
164
               if (bool(ctx.error)) [[unlikely]] {
165
                  return;
166
               }
167
168
               static constexpr auto group = glz::get<I>(groups);
169
170
               static constexpr auto key = std::get<0>(group);
171
               static constexpr auto quoted_key = quoted_key_v<key, Opts.prettify>;
172
               dump<key>(b, ix);
173
174
               static constexpr auto sub_partial = std::get<1>(group);
175
               if constexpr (findable<std::decay_t<T>, decltype(key)>) {
176
                  auto it = value.find(key);
177
                  if (it != value.end()) {
178
                     serialize_partial<JSON>::op<sub_partial, Opts>(it->second, ctx, b, ix);
179
                  }
180
                  else {
181
                     ctx.error = error_code::invalid_partial_key;
182
                     return;
183
                  }
184
               }
185
               else {
186
                  static thread_local auto k = typename std::decay_t<T>::key_type(key);
187
                  auto it = value.find(k);
188
                  if (it != value.end()) {
189
                     serialize_partial<JSON>::op<sub_partial, Opts>(it->second, ctx, b, ix);
190
                  }
191
                  else {
192
                     ctx.error = error_code::invalid_partial_key;
193
                     return;
194
                  }
195
               }
196
               if constexpr (I != N - 1) {
197
                  write_object_entry_separator<Opts>(ctx, b, ix);
198
               }
199
            });
200
         }
201
202
         if (not bool(ctx.error)) [[likely]] {
203
            dump<'}'>(b, ix);
204
         }
205
      }
206
   };
207
208
   template <class T>
209
      requires(glaze_value_t<T> && !custom_write<T>)
210
   struct to<JSON, T>
211
   {
212
      template <auto Opts, class Value, is_context Ctx, class B, class IX>
213
      GLZ_ALWAYS_INLINE static void op(Value&& value, Ctx&& ctx, B&& b, IX&& ix)
214
0
      {
215
0
         using V = std::remove_cvref_t<decltype(get_member(std::declval<Value>(), meta_wrapper_v<T>))>;
216
0
         to<JSON, V>::template op<Opts>(get_member(std::forward<Value>(value), meta_wrapper_v<T>),
217
0
                                        std::forward<Ctx>(ctx), std::forward<B>(b), std::forward<IX>(ix));
218
0
      }
219
   };
220
221
   // Returns 0 if we cannot determine the required padding,
222
   // in which case the `to` specialization must allocate buffer space
223
   // Some types like numbers must have space to be quoted
224
   // All types must have space for a trailing comma
225
   template <class T>
226
   constexpr size_t required_padding()
227
0
   {
228
0
      constexpr auto value = []() -> size_t {
229
0
         if constexpr (boolean_like<T>) {
230
0
            return 8;
231
0
         }
232
0
         else if constexpr (num_t<T>) {
233
0
            if constexpr (std::floating_point<T>) {
234
0
               if constexpr (sizeof(T) > 8) {
235
0
                  return 64;
236
0
               }
237
0
               else if constexpr (sizeof(T) > 4) {
238
0
                  return 32;
239
0
               }
240
0
               else {
241
0
                  return 24;
242
0
               }
243
0
            }
244
0
            else if constexpr (sizeof(T) > 4) {
245
0
               return 24;
246
0
            }
247
0
            else if constexpr (sizeof(T) > 2) {
248
0
               return 16;
249
0
            }
250
0
            else {
251
0
               return 8;
252
0
            }
253
0
         }
254
0
         else if constexpr (nullable_like<T>) {
255
0
            if constexpr (has_value_type<T>) {
256
0
               return required_padding<typename T::value_type>();
257
0
            }
258
0
            else if constexpr (has_element_type<T>) {
259
0
               return required_padding<typename T::element_type>();
260
0
            }
261
0
            else {
262
0
               return 0;
263
0
            }
264
0
         }
265
0
         else if constexpr (always_null_t<T>) {
266
0
            return 8;
267
0
         }
268
0
         else {
269
0
            return 0;
270
0
         }
271
0
      }();
272
0
273
0
      if constexpr (value >= (write_padding_bytes - 16)) {
274
0
         // we always require 16 bytes available from write_padding_bytes
275
0
         // for opening and closing characters
276
0
         return 0;
277
0
      }
278
0
      return value;
279
0
   }
Unexecuted instantiation: unsigned long glz::required_padding<double>()
Unexecuted instantiation: unsigned long glz::required_padding<glz::json_t>()
280
281
   template <is_bitset T>
282
   struct to<JSON, T>
283
   {
284
      template <auto Opts, class B>
285
      static void op(auto&& value, auto&&, B&& b, auto&& ix)
286
      {
287
         if constexpr (vector_like<B>) {
288
            const auto n = ix + 2 + value.size(); // 2 quotes + spaces for character
289
            if (n >= b.size()) [[unlikely]] {
290
               b.resize(2 * n);
291
            }
292
         }
293
294
         std::memcpy(&b[ix], "\"", 1);
295
         ++ix;
296
         for (size_t i = value.size(); i > 0; --i) {
297
            if (value[i - 1]) {
298
               std::memcpy(&b[ix], "1", 1);
299
            }
300
            else {
301
               std::memcpy(&b[ix], "0", 1);
302
            }
303
            ++ix;
304
         }
305
         std::memcpy(&b[ix], "\"", 1);
306
         ++ix;
307
      }
308
   };
309
310
   template <glaze_flags_t T>
311
   struct to<JSON, T>
312
   {
313
      template <auto Opts, class B>
314
      static void op(auto&& value, is_context auto&&, B&& b, auto&& ix)
315
      {
316
         static constexpr auto N = reflect<T>::size;
317
318
         static constexpr auto max_length = [] {
319
            size_t length{};
320
            [&]<size_t... I>(std::index_sequence<I...>) {
321
               ((length += reflect<T>::keys[I].size()), ...);
322
            }(std::make_index_sequence<N>{});
323
            return length;
324
         }() + 4 + 4 * N; // add extra characters
325
326
         if constexpr (vector_like<B>) {
327
            if (const auto n = ix + max_length; n > b.size()) [[unlikely]] {
328
               b.resize(2 * n);
329
            }
330
         }
331
332
         std::memcpy(&b[ix], "[", 1);
333
         ++ix;
334
335
         for_each<N>([&]<size_t I>() {
336
            if (get_member(value, get<I>(reflect<T>::values))) {
337
               std::memcpy(&b[ix], "\"", 1);
338
               ++ix;
339
               constexpr auto& key = reflect<T>::keys[I];
340
               if constexpr (not key.empty()) {
341
                  constexpr auto n = key.size();
342
                  std::memcpy(&b[ix], key.data(), n);
343
                  ix += n;
344
               }
345
               std::memcpy(&b[ix], "\",", 2);
346
               ix += 2;
347
            }
348
         });
349
350
         if (b[ix - 1] == ',') {
351
            b[ix - 1] = ']';
352
         }
353
         else {
354
            std::memcpy(&b[ix], "]", 1);
355
            ++ix;
356
         }
357
      }
358
   };
359
360
   template <is_member_function_pointer T>
361
   struct to<JSON, T>
362
   {
363
      template <auto Opts>
364
      static void op(auto&&, is_context auto&&, auto&&...) noexcept
365
      {}
366
   };
367
368
   template <is_reference_wrapper T>
369
   struct to<JSON, T>
370
   {
371
      template <auto Opts, class... Args>
372
      GLZ_ALWAYS_INLINE static void op(auto&& value, Args&&... args)
373
      {
374
         using V = std::remove_cvref_t<decltype(value.get())>;
375
         to<JSON, V>::template op<Opts>(value.get(), std::forward<Args>(args)...);
376
      }
377
   };
378
379
   template <complex_t T>
380
   struct to<JSON, T>
381
   {
382
      template <auto Opts, class B>
383
      GLZ_ALWAYS_INLINE static void op(auto&& value, is_context auto&& ctx, B&& b, auto&& ix)
384
      {
385
         static_assert(num_t<typename T::value_type>);
386
         // we need to know it is a number type to allocate buffer space
387
388
         if constexpr (vector_like<B>) {
389
            static constexpr size_t max_length = 256;
390
            if (const auto n = ix + max_length; n > b.size()) [[unlikely]] {
391
               b.resize(2 * n);
392
            }
393
         }
394
395
         static constexpr auto O = write_unchecked_on<Opts>();
396
397
         std::memcpy(&b[ix], "[", 1);
398
         ++ix;
399
         using Value = core_t<typename T::value_type>;
400
         to<JSON, Value>::template op<O>(value.real(), ctx, b, ix);
401
         std::memcpy(&b[ix], ",", 1);
402
         ++ix;
403
         to<JSON, Value>::template op<O>(value.imag(), ctx, b, ix);
404
         std::memcpy(&b[ix], "]", 1);
405
         ++ix;
406
      }
407
   };
408
409
   template <boolean_like T>
410
   struct to<JSON, T>
411
   {
412
      template <auto Opts, class B>
413
      GLZ_ALWAYS_INLINE static void op(const bool value, is_context auto&&, B&& b, auto&& ix)
414
0
      {
415
0
         static constexpr auto checked = not check_write_unchecked(Opts);
416
0
         if constexpr (checked && vector_like<B>) {
417
0
            if (const auto n = ix + 8; n > b.size()) [[unlikely]] {
418
0
               b.resize(2 * n);
419
0
            }
420
0
         }
421
0
422
0
         if constexpr (Opts.bools_as_numbers) {
423
0
            if (value) {
424
0
               std::memcpy(&b[ix], "1", 1);
425
0
            }
426
0
            else {
427
0
               std::memcpy(&b[ix], "0", 1);
428
0
            }
429
0
            ++ix;
430
0
         }
431
0
         else {
432
0
            if (value) {
433
0
               std::memcpy(&b[ix], "true", 4);
434
0
               ix += 4;
435
0
            }
436
0
            else {
437
0
               std::memcpy(&b[ix], "false", 5);
438
0
               ix += 5;
439
0
            }
440
0
         }
441
0
      }
442
   };
443
444
   template <num_t T>
445
   struct to<JSON, T>
446
   {
447
      template <auto Opts, class B>
448
      GLZ_ALWAYS_INLINE static void op(auto&& value, is_context auto&& ctx, B&& b, auto&& ix)
449
0
      {
450
0
         if constexpr (not check_write_unchecked(Opts) && vector_like<B>) {
451
0
            static_assert(required_padding<T>());
452
0
            if (const auto n = ix + required_padding<T>(); n > b.size()) [[unlikely]] {
453
0
               b.resize(2 * n);
454
0
            }
455
0
         }
456
0
457
0
         static constexpr auto O = write_unchecked_on<Opts>();
458
0
459
0
         if constexpr (Opts.quoted_num) {
460
0
            std::memcpy(&b[ix], "\"", 1);
461
0
            ++ix;
462
0
            write_chars::op<O>(value, ctx, b, ix);
463
0
            std::memcpy(&b[ix], "\"", 1);
464
0
            ++ix;
465
0
         }
466
0
         else {
467
0
            write_chars::op<O>(value, ctx, b, ix);
468
0
         }
469
0
      }
470
   };
471
472
   inline constexpr std::array<uint16_t, 256> char_escape_table = [] {
473
      auto combine = [](const char chars[2]) -> uint16_t { return uint16_t(chars[0]) | (uint16_t(chars[1]) << 8); };
474
475
      std::array<uint16_t, 256> t{};
476
      t['\b'] = combine(R"(\b)");
477
      t['\t'] = combine(R"(\t)");
478
      t['\n'] = combine(R"(\n)");
479
      t['\f'] = combine(R"(\f)");
480
      t['\r'] = combine(R"(\r)");
481
      t['\"'] = combine(R"(\")");
482
      t['\\'] = combine(R"(\\)");
483
      return t;
484
   }();
485
486
   template <class T>
487
      requires str_t<T> || char_t<T>
488
   struct to<JSON, T>
489
   {
490
      template <auto Opts, class B>
491
      static void op(auto&& value, is_context auto&&, B&& b, auto&& ix)
492
0
      {
493
0
         if constexpr (Opts.number) {
494
0
            dump_maybe_empty(value, b, ix);
495
0
         }
496
0
         else if constexpr (char_t<T>) {
497
0
            if constexpr (Opts.raw) {
498
0
               dump(value, b, ix);
499
0
            }
500
0
            else {
501
0
               if constexpr (resizable<B>) {
502
0
                  const auto k = ix + 8; // 4 characters is enough for quotes and escaped character
503
0
                  if (k > b.size()) [[unlikely]] {
504
0
                     b.resize(2 * k);
505
0
                  }
506
0
               }
507
0
508
0
               std::memcpy(&b[ix], "\"", 1);
509
0
               ++ix;
510
0
               if (const auto escaped = char_escape_table[uint8_t(value)]; escaped) {
511
0
                  std::memcpy(&b[ix], &escaped, 2);
512
0
                  ix += 2;
513
0
               }
514
0
               else if (value == '\0') {
515
0
                  // null character treated as empty string
516
0
               }
517
0
               else if constexpr (check_escape_control_characters(Opts)) {
518
0
                  if (uint8_t(value) < 0x20) {
519
0
                     // Write as \uXXXX format
520
0
                     char unicode_escape[6] = {'\\', 'u', '0', '0', '0', '0'};
521
0
                     constexpr char hex_digits[] = "0123456789ABCDEF";
522
0
                     unicode_escape[4] = hex_digits[(value >> 4) & 0xF];
523
0
                     unicode_escape[5] = hex_digits[value & 0xF];
524
0
                     std::memcpy(&b[ix], unicode_escape, 6);
525
0
                     ix += 6;
526
0
                  }
527
0
                  else {
528
0
                     std::memcpy(&b[ix], &value, 1);
529
0
                     ++ix;
530
0
                  }
531
0
               }
532
0
               else {
533
0
                  std::memcpy(&b[ix], &value, 1);
534
0
                  ++ix;
535
0
               }
536
0
               std::memcpy(&b[ix], "\"", 1);
537
0
               ++ix;
538
0
            }
539
0
         }
540
0
         else {
541
0
            if constexpr (Opts.raw_string) {
542
0
               const sv str = [&]() -> const sv {
543
0
                  if constexpr (!char_array_t<T> && std::is_pointer_v<std::decay_t<T>>) {
544
0
                     return value ? value : "";
545
0
                  }
546
0
                  else {
547
0
                     return value;
548
0
                  }
549
0
               }();
550
0
551
0
               // We need space for quotes and the string length: 2 + n.
552
0
               // Use +8 for extra buffer
553
0
               if constexpr (resizable<B>) {
554
0
                  const auto n = str.size();
555
0
                  const auto k = ix + 8 + n;
556
0
                  if (k > b.size()) [[unlikely]] {
557
0
                     b.resize(2 * k);
558
0
                  }
559
0
               }
560
0
               // now we don't have to check writing
561
0
562
0
               std::memcpy(&b[ix], "\"", 1);
563
0
               ++ix;
564
0
               if (str.size()) [[likely]] {
565
0
                  const auto n = str.size();
566
0
                  std::memcpy(&b[ix], str.data(), n);
567
0
                  ix += n;
568
0
               }
569
0
               std::memcpy(&b[ix], "\"", 1);
570
0
               ++ix;
571
0
            }
572
0
            else {
573
0
               const sv str = [&]() -> const sv {
574
0
                  if constexpr (!char_array_t<T> && std::is_pointer_v<std::decay_t<T>>) {
575
0
                     return value ? value : "";
576
0
                  }
577
0
                  else if constexpr (array_char_t<T>) {
578
0
                     return *value.data() ? sv{value.data()} : "";
579
0
                  }
580
0
                  else {
581
0
                     return value;
582
0
                  }
583
0
               }();
584
0
               const auto n = str.size();
585
0
586
0
               // In the case n == 0 we need two characters for quotes.
587
0
               // For each individual character we need room for two characters to handle escapes.
588
0
               // When using Unicode escapes, we might need up to 6 characters (\uXXXX) per character
589
0
               if constexpr (check_escape_control_characters(Opts)) {
590
0
                  if constexpr (resizable<B>) {
591
0
                     // We need 2 + 6 * n characters in the worst case (all control chars)
592
0
                     const auto k = ix + 10 + 6 * n;
593
0
                     if (k > b.size()) [[unlikely]] {
594
0
                        b.resize(2 * k);
595
0
                     }
596
0
                  }
597
0
               }
598
0
               else {
599
0
                  // Using the original sizing
600
0
                  if constexpr (resizable<B>) {
601
0
                     const auto k = ix + 10 + 2 * n;
602
0
                     if (k > b.size()) [[unlikely]] {
603
0
                        b.resize(2 * k);
604
0
                     }
605
0
                  }
606
0
               }
607
0
               // now we don't have to check writing
608
0
609
0
               if constexpr (Opts.raw) {
610
0
                  if (n) {
611
0
                     std::memcpy(&b[ix], str.data(), n);
612
0
                     ix += n;
613
0
                  }
614
0
               }
615
0
               else {
616
0
                  std::memcpy(&b[ix], "\"", 1);
617
0
                  ++ix;
618
0
619
0
                  const auto* c = str.data();
620
0
                  const auto* const e = c + n;
621
0
                  const auto start = &b[ix];
622
0
                  auto data = start;
623
0
624
0
                  // We don't check for writing out invalid characters as this can be tested by the user if
625
0
                  // necessary. In the case of invalid JSON characters we write out null characters to
626
0
                  // showcase the error and make the JSON invalid. These would then be detected upon reading
627
0
                  // the JSON.
628
0
629
0
#if defined(GLZ_USE_AVX2)
630
0
                  // Optimization for systems with AVX2 support
631
0
                  if (n > 31) {
632
0
                     const __m256i lo7_mask = _mm256_set1_epi8(0b01111111);
633
0
                     const __m256i quote_char = _mm256_set1_epi8('"');
634
0
                     const __m256i backslash_char = _mm256_set1_epi8('\\');
635
0
                     const __m256i less_32_mask = _mm256_set1_epi8(0b01100000);
636
0
                     const __m256i high_bit_mask = _mm256_set1_epi8(static_cast<int8_t>(0b10000000));
637
0
638
0
                     for (const char* end_m31 = e - 31; c < end_m31;) {
639
0
                        __m256i v = _mm256_loadu_si256(reinterpret_cast<const __m256i*>(c));
640
0
641
0
                        _mm256_storeu_si256(reinterpret_cast<__m256i*>(data), v);
642
0
643
0
                        const __m256i lo7 = _mm256_and_si256(v, lo7_mask);
644
0
                        const __m256i quote = _mm256_add_epi8(_mm256_xor_si256(lo7, quote_char), lo7_mask);
645
0
                        const __m256i backslash = _mm256_add_epi8(_mm256_xor_si256(lo7, backslash_char), lo7_mask);
646
0
                        const __m256i less_32 = _mm256_add_epi8(_mm256_and_si256(v, less_32_mask), lo7_mask);
647
0
648
0
                        __m256i temp = _mm256_and_si256(quote, backslash);
649
0
                        temp = _mm256_and_si256(temp, less_32);
650
0
                        temp = _mm256_or_si256(temp, v);
651
0
                        __m256i next = _mm256_andnot_si256(temp, _mm256_set1_epi8(-1)); // Equivalent to ~temp
652
0
                        next = _mm256_and_si256(next, high_bit_mask);
653
0
654
0
                        uint32_t mask = _mm256_movemask_epi8(next);
655
0
656
0
                        if (mask == 0) {
657
0
                           data += 32;
658
0
                           c += 32;
659
0
                           continue;
660
0
                        }
661
0
662
0
                        uint32_t length = countr_zero(mask);
663
0
664
0
                        c += length;
665
0
                        data += length;
666
0
667
0
                        if constexpr (check_escape_control_characters(Opts)) {
668
0
                           if (const auto escaped = char_escape_table[uint8_t(*c)]; escaped) {
669
0
                              std::memcpy(data, &escaped, 2);
670
0
                              data += 2;
671
0
                           }
672
0
                           else {
673
0
                              // Write as \uXXXX format for control characters
674
0
                              char unicode_escape[6] = {'\\', 'u', '0', '0', '0', '0'};
675
0
                              constexpr char hex_digits[] = "0123456789ABCDEF";
676
0
                              unicode_escape[4] = hex_digits[(uint8_t(*c) >> 4) & 0xF];
677
0
                              unicode_escape[5] = hex_digits[uint8_t(*c) & 0xF];
678
0
                              std::memcpy(data, unicode_escape, 6);
679
0
                              data += 6;
680
0
                           }
681
0
                        }
682
0
                        else {
683
0
                           std::memcpy(data, &char_escape_table[uint8_t(*c)], 2);
684
0
                           data += 2;
685
0
                        }
686
0
                        ++c;
687
0
                     }
688
0
                  }
689
0
#endif
690
0
691
0
                  if (n > 7) {
692
0
                     for (const auto end_m7 = e - 7; c < end_m7;) {
693
0
                        std::memcpy(data, c, 8);
694
0
                        uint64_t swar;
695
0
                        std::memcpy(&swar, c, 8);
696
0
697
0
                        constexpr uint64_t lo7_mask = repeat_byte8(0b01111111);
698
0
                        const uint64_t lo7 = swar & lo7_mask;
699
0
                        const uint64_t quote = (lo7 ^ repeat_byte8('"')) + lo7_mask;
700
0
                        const uint64_t backslash = (lo7 ^ repeat_byte8('\\')) + lo7_mask;
701
0
                        const uint64_t less_32 = (swar & repeat_byte8(0b01100000)) + lo7_mask;
702
0
                        uint64_t next = ~((quote & backslash & less_32) | swar);
703
0
704
0
                        next &= repeat_byte8(0b10000000);
705
0
                        if (next == 0) {
706
0
                           data += 8;
707
0
                           c += 8;
708
0
                           continue;
709
0
                        }
710
0
711
0
                        const auto length = (countr_zero(next) >> 3);
712
0
                        c += length;
713
0
                        data += length;
714
0
715
0
                        if constexpr (check_escape_control_characters(Opts)) {
716
0
                           if (const auto escaped = char_escape_table[uint8_t(*c)]; escaped) {
717
0
                              std::memcpy(data, &escaped, 2);
718
0
                              data += 2;
719
0
                           }
720
0
                           else {
721
0
                              // Write as \uXXXX format for control characters
722
0
                              char unicode_escape[6] = {'\\', 'u', '0', '0', '0', '0'};
723
0
                              constexpr char hex_digits[] = "0123456789ABCDEF";
724
0
                              unicode_escape[4] = hex_digits[(uint8_t(*c) >> 4) & 0xF];
725
0
                              unicode_escape[5] = hex_digits[uint8_t(*c) & 0xF];
726
0
                              std::memcpy(data, unicode_escape, 6);
727
0
                              data += 6;
728
0
                           }
729
0
                        }
730
0
                        else {
731
0
                           std::memcpy(data, &char_escape_table[uint8_t(*c)], 2);
732
0
                           data += 2;
733
0
                        }
734
0
                        ++c;
735
0
                     }
736
0
                  }
737
0
738
0
                  // Tail end of buffer. Uncommon for long strings.
739
0
                  for (; c < e; ++c) {
740
0
                     if (const auto escaped = char_escape_table[uint8_t(*c)]; escaped) {
741
0
                        std::memcpy(data, &escaped, 2);
742
0
                        data += 2;
743
0
                     }
744
0
                     else if constexpr (check_escape_control_characters(Opts)) {
745
0
                        if (uint8_t(*c) < 0x20) {
746
0
                           // Write as \uXXXX format for control characters
747
0
                           char unicode_escape[6] = {'\\', 'u', '0', '0', '0', '0'};
748
0
                           constexpr char hex_digits[] = "0123456789ABCDEF";
749
0
                           unicode_escape[4] = hex_digits[(uint8_t(*c) >> 4) & 0xF];
750
0
                           unicode_escape[5] = hex_digits[uint8_t(*c) & 0xF];
751
0
                           std::memcpy(data, unicode_escape, 6);
752
0
                           data += 6;
753
0
                        }
754
0
                        else {
755
0
                           std::memcpy(data, c, 1);
756
0
                           ++data;
757
0
                        }
758
0
                     }
759
0
                     else {
760
0
                        std::memcpy(data, c, 1);
761
0
                        ++data;
762
0
                     }
763
0
                  }
764
0
765
0
                  ix += size_t(data - start);
766
0
767
0
                  std::memcpy(&b[ix], "\"", 1);
768
0
                  ++ix;
769
0
               }
770
0
            }
771
0
         }
772
0
      }
773
   };
774
775
   template <class T>
776
      requires((glaze_enum_t<T> || (meta_keys<T> && std::is_enum_v<std::decay_t<T>>)) && not custom_write<T>)
777
   struct to<JSON, T>
778
   {
779
      template <auto Opts, class... Args>
780
      GLZ_ALWAYS_INLINE static void op(auto&& value, is_context auto&& ctx, Args&&... args)
781
      {
782
         // TODO: Use new hashing approach for better performance
783
         // TODO: Check if sequenced and use the value as the index if so
784
         using key_t = std::underlying_type_t<T>;
785
         static constexpr auto frozen_map = make_enum_to_string_map<T>();
786
         const auto& member_it = frozen_map.find(static_cast<key_t>(value));
787
         if (member_it != frozen_map.end()) {
788
            const sv str = {member_it->second.data(), member_it->second.size()};
789
            // TODO: Assumes people dont use strings with chars that need to be escaped for their enum names
790
            // TODO: Could create a pre quoted map for better performance
791
            if constexpr (not Opts.raw) {
792
               dump<'"'>(args...);
793
            }
794
            dump_maybe_empty(str, args...);
795
            if constexpr (not Opts.raw) {
796
               dump<'"'>(args...);
797
            }
798
         }
799
         else [[unlikely]] {
800
            // What do we want to happen if the value doesn't have a mapped string
801
            serialize<JSON>::op<Opts>(static_cast<std::underlying_type_t<T>>(value), ctx, std::forward<Args>(args)...);
802
         }
803
      }
804
   };
805
806
   template <class T>
807
      requires(!meta_keys<T> && std::is_enum_v<std::decay_t<T>> && !glaze_enum_t<T> && !custom_write<T>)
808
   struct to<JSON, T>
809
   {
810
      template <auto Opts, class... Args>
811
      GLZ_ALWAYS_INLINE static void op(auto&& value, is_context auto&& ctx, Args&&... args)
812
      {
813
         // serialize as underlying number
814
         serialize<JSON>::op<Opts>(static_cast<std::underlying_type_t<std::decay_t<T>>>(value), ctx,
815
                                   std::forward<Args>(args)...);
816
      }
817
   };
818
819
   template <func_t T>
820
   struct to<JSON, T>
821
   {
822
      template <auto Opts, class B>
823
      GLZ_ALWAYS_INLINE static void op(auto&& value, is_context auto&&, B&& b, auto&& ix)
824
      {
825
         static constexpr auto name = name_v<std::decay_t<decltype(value)>>;
826
         constexpr auto n = name.size();
827
828
         if constexpr (vector_like<B>) {
829
            if (const auto k = ix + 8 + n; k > b.size()) [[unlikely]] {
830
               b.resize(2 * k);
831
            }
832
         }
833
834
         std::memcpy(&b[ix], "\"", 1);
835
         ++ix;
836
         if constexpr (not name.empty()) {
837
            std::memcpy(&b[ix], name.data(), n);
838
            ix += n;
839
         }
840
         std::memcpy(&b[ix], "\"", 1);
841
         ++ix;
842
      }
843
   };
844
845
   template <class T>
846
   struct to<JSON, basic_raw_json<T>>
847
   {
848
      template <auto Opts, class B>
849
      GLZ_ALWAYS_INLINE static void op(auto&& value, is_context auto&&, B&& b, auto&& ix)
850
      {
851
         const auto n = value.str.size();
852
         if (n) {
853
            if constexpr (vector_like<B>) {
854
               if (const auto k = ix + n + write_padding_bytes; k > b.size()) [[unlikely]] {
855
                  b.resize(2 * k);
856
               }
857
            }
858
859
            std::memcpy(&b[ix], value.str.data(), n);
860
            ix += n;
861
         }
862
      }
863
   };
864
865
   template <class T>
866
   struct to<JSON, basic_text<T>>
867
   {
868
      template <auto Opts, class B>
869
      GLZ_ALWAYS_INLINE static void op(auto&& value, is_context auto&&, B&& b, auto&& ix)
870
      {
871
         const auto n = value.str.size();
872
         if (n) {
873
            if constexpr (vector_like<B>) {
874
               if (const auto k = ix + n + write_padding_bytes; k > b.size()) [[unlikely]] {
875
                  b.resize(2 * k);
876
               }
877
            }
878
879
            std::memcpy(&b[ix], value.str.data(), n);
880
            ix += n;
881
         }
882
      }
883
   };
884
885
   template <auto Opts, bool minified_check = true, class B>
886
   GLZ_ALWAYS_INLINE void write_array_entry_separator(is_context auto&& ctx, B&& b, auto&& ix)
887
0
   {
888
0
      if constexpr (Opts.prettify) {
889
0
         if constexpr (vector_like<B>) {
890
0
            if (const auto k = ix + ctx.indentation_level + write_padding_bytes; k > b.size()) [[unlikely]] {
891
0
               b.resize(2 * k);
892
0
            }
893
0
         }
894
0
         if constexpr (Opts.new_lines_in_arrays) {
895
0
            std::memcpy(&b[ix], ",\n", 2);
896
0
            ix += 2;
897
0
            std::memset(&b[ix], Opts.indentation_char, ctx.indentation_level);
898
0
            ix += ctx.indentation_level;
899
0
         }
900
0
         else {
901
0
            std::memcpy(&b[ix], ", ", 2);
902
0
            ix += 2;
903
0
         }
904
0
      }
905
0
      else {
906
0
         if constexpr (vector_like<B>) {
907
0
            if constexpr (minified_check) {
908
0
               if (ix >= b.size()) [[unlikely]] {
909
0
                  b.resize(2 * ix);
910
0
               }
911
0
            }
912
0
         }
913
0
         std::memcpy(&b[ix], ",", 1);
914
0
         ++ix;
915
0
      }
916
0
   }
917
918
   // "key":value pair output
919
   template <auto Opts, class Key, class Value, is_context Ctx, class B>
920
   GLZ_ALWAYS_INLINE void write_pair_content(const Key& key, Value&& value, Ctx& ctx, B&& b, auto&& ix)
921
0
   {
922
0
      if constexpr (str_t<Key> || char_t<Key> || glaze_enum_t<Key> || Opts.quoted_num) {
923
0
         to<JSON, core_t<Key>>::template op<Opts>(key, ctx, b, ix);
924
0
      }
925
0
      else if constexpr (num_t<Key>) {
926
0
         serialize<JSON>::op<opt_true<Opts, &opts::quoted_num>>(key, ctx, b, ix);
927
0
      }
928
0
      else {
929
0
         serialize<JSON>::op<opt_false<Opts, &opts::raw_string>>(quoted_t<const Key>{key}, ctx, b, ix);
930
0
      }
931
0
      if constexpr (Opts.prettify) {
932
0
         dump<": ">(b, ix);
933
0
      }
934
0
      else {
935
0
         dump<':'>(b, ix);
936
0
      }
937
0
938
0
      using V = core_t<decltype(value)>;
939
0
      to<JSON, V>::template op<opening_and_closing_handled_off<Opts>()>(value, ctx, b, ix);
940
0
   }
941
942
   template <class T>
943
   concept array_padding_known =
944
      requires { typename T::value_type; } && (required_padding<typename T::value_type>() > 0);
945
946
   template <class T>
947
      requires(writable_array_t<T> || writable_map_t<T>)
948
   struct to<JSON, T>
949
   {
950
      static constexpr bool map_like_array = writable_array_t<T> && pair_t<range_value_t<T>>;
951
952
      template <auto Opts, class B>
953
         requires(writable_array_t<T> && (map_like_array ? check_concatenate(Opts) == false : true))
954
      GLZ_ALWAYS_INLINE static void op(auto&& value, is_context auto&& ctx, B&& b, auto&& ix)
955
0
      {
956
0
         if (empty_range(value)) {
957
0
            dump<"[]">(b, ix);
958
0
         }
959
0
         else {
960
0
            if constexpr (has_size<T> && array_padding_known<T>) {
961
0
               const auto n = value.size();
962
0
963
0
               static constexpr auto value_padding = required_padding<typename T::value_type>();
964
0
965
0
               if constexpr (Opts.prettify) {
966
0
                  if constexpr (Opts.new_lines_in_arrays) {
967
0
                     ctx.indentation_level += Opts.indentation_width;
968
0
                  }
969
0
970
0
                  if constexpr (vector_like<B>) {
971
0
                     // add space for '\n' and ',' characters for each element, hence `+ 2`
972
0
                     // use n + 1 because we put the end array character after the last element with whitespace
973
0
                     if (const auto k =
974
0
                            ix + (n + 1) * (value_padding + ctx.indentation_level + 2) + write_padding_bytes;
975
0
                         k > b.size()) {
976
0
                        b.resize(2 * k);
977
0
                     }
978
0
                  }
979
0
980
0
                  if constexpr (Opts.new_lines_in_arrays) {
981
0
                     std::memcpy(&b[ix], "[\n", 2);
982
0
                     ix += 2;
983
0
                     std::memset(&b[ix], Opts.indentation_char, ctx.indentation_level);
984
0
                     ix += ctx.indentation_level;
985
0
                  }
986
0
                  else {
987
0
                     std::memcpy(&b[ix], "[", 1);
988
0
                     ++ix;
989
0
                  }
990
0
               }
991
0
               else {
992
0
                  if constexpr (vector_like<B>) {
993
0
                     static constexpr auto comma_padding = 1;
994
0
                     if (const auto k = ix + n * (value_padding + comma_padding) + write_padding_bytes; k > b.size())
995
0
                        [[unlikely]] {
996
0
                        b.resize(2 * k);
997
0
                     }
998
0
                  }
999
0
                  std::memcpy(&b[ix], "[", 1);
1000
0
                  ++ix;
1001
0
               }
1002
0
1003
0
               auto it = std::begin(value);
1004
0
               using val_t = std::remove_cvref_t<decltype(*it)>;
1005
0
               to<JSON, val_t>::template op<write_unchecked_on<Opts>()>(*it, ctx, b, ix);
1006
0
1007
0
               ++it;
1008
0
               for (const auto fin = std::end(value); it != fin; ++it) {
1009
0
                  if constexpr (Opts.prettify) {
1010
0
                     if constexpr (Opts.new_lines_in_arrays) {
1011
0
                        std::memcpy(&b[ix], ",\n", 2);
1012
0
                        ix += 2;
1013
0
                        std::memset(&b[ix], Opts.indentation_char, ctx.indentation_level);
1014
0
                        ix += ctx.indentation_level;
1015
0
                     }
1016
0
                     else {
1017
0
                        std::memcpy(&b[ix], ", ", 2);
1018
0
                        ix += 2;
1019
0
                     }
1020
0
                  }
1021
0
                  else {
1022
0
                     std::memcpy(&b[ix], ",", 1);
1023
0
                     ++ix;
1024
0
                  }
1025
0
1026
0
                  to<JSON, val_t>::template op<write_unchecked_on<Opts>()>(*it, ctx, b, ix);
1027
0
               }
1028
0
               if constexpr (Opts.prettify && Opts.new_lines_in_arrays) {
1029
0
                  ctx.indentation_level -= Opts.indentation_width;
1030
0
                  std::memcpy(&b[ix], "\n", 1);
1031
0
                  ++ix;
1032
0
                  std::memset(&b[ix], Opts.indentation_char, ctx.indentation_level);
1033
0
                  ix += ctx.indentation_level;
1034
0
               }
1035
0
1036
0
               std::memcpy(&b[ix], "]", 1);
1037
0
               ++ix;
1038
0
            }
1039
0
            else {
1040
0
               // we either can't get the size (std::forward_list) or we cannot compute the allocation size
1041
0
1042
0
               if constexpr (Opts.prettify) {
1043
0
                  if constexpr (Opts.new_lines_in_arrays) {
1044
0
                     ctx.indentation_level += Opts.indentation_width;
1045
0
                  }
1046
0
1047
0
                  if constexpr (vector_like<B>) {
1048
0
                     if (const auto k = ix + ctx.indentation_level + write_padding_bytes; k > b.size()) [[unlikely]] {
1049
0
                        b.resize(2 * k);
1050
0
                     }
1051
0
                  }
1052
0
1053
0
                  if constexpr (Opts.new_lines_in_arrays) {
1054
0
                     std::memcpy(&b[ix], "[\n", 2);
1055
0
                     ix += 2;
1056
0
                     std::memset(&b[ix], Opts.indentation_char, ctx.indentation_level);
1057
0
                     ix += ctx.indentation_level;
1058
0
                  }
1059
0
                  else {
1060
0
                     std::memcpy(&b[ix], "[", 1);
1061
0
                     ++ix;
1062
0
                  }
1063
0
               }
1064
0
               else {
1065
0
                  if constexpr (vector_like<B>) {
1066
0
                     if (const auto k = ix + write_padding_bytes; k > b.size()) [[unlikely]] {
1067
0
                        b.resize(2 * k);
1068
0
                     }
1069
0
                  }
1070
0
                  std::memcpy(&b[ix], "[", 1);
1071
0
                  ++ix;
1072
0
               }
1073
0
1074
0
               auto it = std::begin(value);
1075
0
               using val_t = std::remove_cvref_t<decltype(*it)>;
1076
0
               if constexpr (required_padding<val_t>()) {
1077
0
                  to<JSON, val_t>::template op<write_unchecked_on<Opts>()>(*it, ctx, b, ix);
1078
0
               }
1079
0
               else {
1080
0
                  to<JSON, val_t>::template op<Opts>(*it, ctx, b, ix);
1081
0
               }
1082
0
1083
0
               ++it;
1084
0
               for (const auto fin = std::end(value); it != fin; ++it) {
1085
0
                  if constexpr (required_padding<val_t>()) {
1086
0
                     if constexpr (vector_like<B>) {
1087
0
                        if constexpr (Opts.prettify) {
1088
0
                           if (const auto k = ix + ctx.indentation_level + write_padding_bytes; k > b.size())
1089
0
                              [[unlikely]] {
1090
0
                              b.resize(2 * k);
1091
0
                           }
1092
0
                        }
1093
0
                        else {
1094
0
                           if (const auto k = ix + write_padding_bytes; k > b.size()) [[unlikely]] {
1095
0
                              b.resize(2 * k);
1096
0
                           }
1097
0
                        }
1098
0
                     }
1099
0
1100
0
                     if constexpr (Opts.prettify) {
1101
0
                        if constexpr (Opts.new_lines_in_arrays) {
1102
0
                           std::memcpy(&b[ix], ",\n", 2);
1103
0
                           ix += 2;
1104
0
                           std::memset(&b[ix], Opts.indentation_char, ctx.indentation_level);
1105
0
                           ix += ctx.indentation_level;
1106
0
                        }
1107
0
                        else {
1108
0
                           std::memcpy(&b[ix], ", ", 2);
1109
0
                           ix += 2;
1110
0
                        }
1111
0
                     }
1112
0
                     else {
1113
0
                        std::memcpy(&b[ix], ",", 1);
1114
0
                        ++ix;
1115
0
                     }
1116
0
1117
0
                     to<JSON, val_t>::template op<write_unchecked_on<Opts>()>(*it, ctx, b, ix);
1118
0
                  }
1119
0
                  else {
1120
0
                     write_array_entry_separator<Opts>(ctx, b, ix);
1121
0
                     to<JSON, val_t>::template op<Opts>(*it, ctx, b, ix);
1122
0
                  }
1123
0
               }
1124
0
               if constexpr (Opts.prettify && Opts.new_lines_in_arrays) {
1125
0
                  ctx.indentation_level -= Opts.indentation_width;
1126
0
                  dump_newline_indent<Opts.indentation_char>(ctx.indentation_level, b, ix);
1127
0
               }
1128
0
1129
0
               dump<']'>(b, ix);
1130
0
            }
1131
0
         }
1132
0
      }
1133
1134
      template <auto Opts, class B>
1135
         requires(writable_map_t<T> || (map_like_array && check_concatenate(Opts) == true))
1136
      static void op(auto&& value, is_context auto&& ctx, B&& b, auto&& ix)
1137
0
      {
1138
0
         if constexpr (not check_opening_handled(Opts)) {
1139
0
            dump<'{'>(b, ix);
1140
0
         }
1141
0
1142
0
         if (!empty_range(value)) {
1143
0
            if constexpr (!check_opening_handled(Opts)) {
1144
0
               if constexpr (Opts.prettify) {
1145
0
                  ctx.indentation_level += Opts.indentation_width;
1146
0
                  if constexpr (vector_like<B>) {
1147
0
                     if (const auto k = ix + ctx.indentation_level + write_padding_bytes; k > b.size()) [[unlikely]] {
1148
0
                        b.resize(2 * k);
1149
0
                     }
1150
0
                  }
1151
0
                  std::memcpy(&b[ix], "\n", 1);
1152
0
                  ++ix;
1153
0
                  std::memset(&b[ix], Opts.indentation_char, ctx.indentation_level);
1154
0
                  ix += ctx.indentation_level;
1155
0
               }
1156
0
            }
1157
0
1158
0
            using val_t = detail::iterator_second_type<T>; // the type of value in each [key, value] pair
1159
0
1160
0
            if constexpr (not always_skipped<val_t>) {
1161
0
               if constexpr (null_t<val_t> && Opts.skip_null_members) {
1162
0
                  auto write_first_entry = [&](auto&& it) {
1163
0
                     auto&& [key, entry_val] = *it;
1164
0
                     if (skip_member<Opts>(entry_val)) {
1165
0
                        return true;
1166
0
                     }
1167
0
                     write_pair_content<Opts>(key, entry_val, ctx, b, ix);
1168
0
                     return false;
1169
0
                  };
1170
0
1171
0
                  auto it = std::begin(value);
1172
0
                  bool first = write_first_entry(it);
1173
0
                  ++it;
1174
0
                  for (const auto end = std::end(value); it != end; ++it) {
1175
0
                     auto&& [key, entry_val] = *it;
1176
0
                     if (skip_member<Opts>(entry_val)) {
1177
0
                        continue;
1178
0
                     }
1179
0
1180
0
                     // When Opts.skip_null_members, *any* entry may be skipped, meaning separator dumping must be
1181
0
                     // conditional for every entry.
1182
0
                     // Alternatively, write separator after each entry except last but then branch is permanent
1183
0
                     if (not first) {
1184
0
                        write_object_entry_separator<Opts>(ctx, b, ix);
1185
0
                     }
1186
0
1187
0
                     write_pair_content<Opts>(key, entry_val, ctx, b, ix);
1188
0
1189
0
                     first = false;
1190
0
                  }
1191
0
               }
1192
0
               else {
1193
0
                  auto write_first_entry = [&](auto&& it) {
1194
0
                     auto&& [key, entry_val] = *it;
1195
0
                     write_pair_content<Opts>(key, entry_val, ctx, b, ix);
1196
0
                  };
1197
0
1198
0
                  auto it = std::begin(value);
1199
0
                  write_first_entry(it);
1200
0
                  ++it;
1201
0
                  for (const auto end = std::end(value); it != end; ++it) {
1202
0
                     auto&& [key, entry_val] = *it;
1203
0
                     write_object_entry_separator<Opts>(ctx, b, ix);
1204
0
                     write_pair_content<Opts>(key, entry_val, ctx, b, ix);
1205
0
                  }
1206
0
               }
1207
0
            }
1208
0
1209
0
            if constexpr (!check_closing_handled(Opts)) {
1210
0
               if constexpr (Opts.prettify) {
1211
0
                  ctx.indentation_level -= Opts.indentation_width;
1212
0
                  if constexpr (vector_like<B>) {
1213
0
                     if (const auto k = ix + ctx.indentation_level + write_padding_bytes; k > b.size()) [[unlikely]] {
1214
0
                        b.resize(2 * k);
1215
0
                     }
1216
0
                  }
1217
0
                  std::memcpy(&b[ix], "\n", 1);
1218
0
                  ++ix;
1219
0
                  std::memset(&b[ix], Opts.indentation_char, ctx.indentation_level);
1220
0
                  ix += ctx.indentation_level;
1221
0
               }
1222
0
            }
1223
0
         }
1224
0
1225
0
         if constexpr (!check_closing_handled(Opts)) {
1226
0
            dump<'}'>(b, ix);
1227
0
         }
1228
0
      }
1229
   };
1230
1231
   template <pair_t T>
1232
   struct to<JSON, T>
1233
   {
1234
      template <auto Opts, class B, class Ix>
1235
      static void op(const T& value, is_context auto&& ctx, B&& b, Ix&& ix)
1236
      {
1237
         const auto& [key, val] = value;
1238
         if (skip_member<Opts>(val)) {
1239
            return dump<"{}">(b, ix);
1240
         }
1241
1242
         if constexpr (Opts.prettify) {
1243
            ctx.indentation_level += Opts.indentation_width;
1244
            if constexpr (vector_like<B>) {
1245
               if (const auto k = ix + ctx.indentation_level + 2; k > b.size()) [[unlikely]] {
1246
                  b.resize(2 * k);
1247
               }
1248
            }
1249
            dump<"{\n", false>(b, ix);
1250
            std::memset(&b[ix], Opts.indentation_char, ctx.indentation_level);
1251
            ix += ctx.indentation_level;
1252
         }
1253
         else {
1254
            dump<'{'>(b, ix);
1255
         }
1256
1257
         write_pair_content<Opts>(key, val, ctx, b, ix);
1258
1259
         if constexpr (Opts.prettify) {
1260
            ctx.indentation_level -= Opts.indentation_width;
1261
            dump_newline_indent<Opts.indentation_char>(ctx.indentation_level, b, ix);
1262
            dump<'}', false>(b, ix);
1263
         }
1264
         else {
1265
            dump<'}'>(b, ix);
1266
         }
1267
      }
1268
   };
1269
1270
   template <is_expected T>
1271
   struct to<JSON, T>
1272
   {
1273
      template <auto Opts, class... Args>
1274
      GLZ_ALWAYS_INLINE static void op(auto&& value, is_context auto&& ctx, Args&&... args)
1275
      {
1276
         if (value) {
1277
            serialize<JSON>::op<Opts>(*value, ctx, std::forward<Args>(args)...);
1278
         }
1279
         else {
1280
            serialize<JSON>::op<Opts>(unexpected_wrapper{&value.error()}, ctx, std::forward<Args>(args)...);
1281
         }
1282
      }
1283
   };
1284
1285
   // for C style arrays
1286
   template <nullable_t T>
1287
      requires(std::is_array_v<T>)
1288
   struct to<JSON, T>
1289
   {
1290
      template <auto Opts, class V, size_t N, class... Args>
1291
      GLZ_ALWAYS_INLINE static void op(const V (&value)[N], is_context auto&& ctx, Args&&... args)
1292
      {
1293
         serialize<JSON>::op<Opts>(std::span{value, N}, ctx, std::forward<Args>(args)...);
1294
      }
1295
   };
1296
1297
   template <nullable_like T>
1298
   struct to<JSON, T>
1299
   {
1300
      template <auto Opts>
1301
      GLZ_ALWAYS_INLINE static void op(auto&& value, is_context auto&& ctx, auto&& b, auto&& ix)
1302
      {
1303
         if (value) {
1304
            if constexpr (required_padding<T>()) {
1305
               serialize<JSON>::op<Opts>(*value, ctx, b, ix);
1306
            }
1307
            else {
1308
               serialize<JSON>::op<write_unchecked_off<Opts>()>(*value, ctx, b, ix);
1309
            }
1310
         }
1311
         else {
1312
            dump<"null", not check_write_unchecked(Opts)>(b, ix);
1313
         }
1314
      }
1315
   };
1316
1317
   template <class T>
1318
      requires(nullable_value_t<T> && not nullable_like<T> && not is_expected<T>)
1319
   struct to<JSON, T>
1320
   {
1321
      template <auto Opts>
1322
      GLZ_ALWAYS_INLINE static void op(auto&& value, is_context auto&& ctx, auto&& b, auto&& ix)
1323
      {
1324
         if (value.has_value()) {
1325
            serialize<JSON>::op<Opts>(value.value(), ctx, b, ix);
1326
         }
1327
         else {
1328
            dump<"null">(b, ix);
1329
         }
1330
      }
1331
   };
1332
1333
   template <always_null_t T>
1334
   struct to<JSON, T>
1335
   {
1336
      template <auto Opts, class B>
1337
      GLZ_ALWAYS_INLINE static void op(auto&&, is_context auto&&, B&& b, auto&& ix)
1338
0
      {
1339
0
         if constexpr (not check_write_unchecked(Opts)) {
1340
0
            if (const auto k = ix + 4; k > b.size()) [[unlikely]] {
1341
0
               b.resize(2 * k);
1342
0
            }
1343
0
         }
1344
0
         static constexpr uint32_t null_v = 1819047278;
1345
0
         std::memcpy(&b[ix], &null_v, 4);
1346
0
         ix += 4;
1347
0
      }
1348
   };
1349
1350
   template <is_variant T>
1351
   struct to<JSON, T>
1352
   {
1353
      template <auto Opts, class B>
1354
      static void op(auto&& value, is_context auto&& ctx, B&& b, auto&& ix)
1355
0
      {
1356
0
         std::visit(
1357
0
            [&](auto&& val) {
1358
0
               using V = std::decay_t<decltype(val)>;
1359
0
1360
0
               if constexpr (check_write_type_info(Opts) && not tag_v<T>.empty() &&
1361
0
                             (glaze_object_t<V> || (reflectable<V> && !has_member_with_name<V>(tag_v<T>)))) {
1362
0
                  constexpr auto N = reflect<V>::size;
1363
0
1364
0
                  // must first write out type
1365
0
                  if constexpr (Opts.prettify) {
1366
0
                     dump<"{\n">(b, ix);
1367
0
                     ctx.indentation_level += Opts.indentation_width;
1368
0
                     dumpn<Opts.indentation_char>(ctx.indentation_level, b, ix);
1369
0
                     dump<'"'>(b, ix);
1370
0
                     dump_maybe_empty(tag_v<T>, b, ix);
1371
0
1372
0
                     using id_type = std::decay_t<decltype(ids_v<T>[value.index()])>;
1373
0
1374
0
                     if constexpr (std::integral<id_type>) {
1375
0
                        dump<"\": ">(b, ix);
1376
0
                        serialize<JSON>::op<Opts>(ids_v<T>[value.index()], ctx, b, ix);
1377
0
                        if constexpr (N == 0) {
1378
0
                           dump<"\n">(b, ix);
1379
0
                        }
1380
0
                        else {
1381
0
                           dump<",\n">(b, ix);
1382
0
                        }
1383
0
                        dumpn<Opts.indentation_char>(ctx.indentation_level, b, ix);
1384
0
                     }
1385
0
                     else {
1386
0
                        dump<"\": \"">(b, ix);
1387
0
                        dump_maybe_empty(ids_v<T>[value.index()], b, ix);
1388
0
                        if constexpr (N == 0) {
1389
0
                           dump<"\"\n">(b, ix);
1390
0
                        }
1391
0
                        else {
1392
0
                           dump<"\",\n">(b, ix);
1393
0
                        }
1394
0
                        dumpn<Opts.indentation_char>(ctx.indentation_level, b, ix);
1395
0
                     }
1396
0
                  }
1397
0
                  else {
1398
0
                     using id_type = std::decay_t<decltype(ids_v<T>[value.index()])>;
1399
0
1400
0
                     dump<"{\"">(b, ix);
1401
0
                     dump_maybe_empty(tag_v<T>, b, ix);
1402
0
1403
0
                     if constexpr (std::integral<id_type>) {
1404
0
                        dump<"\":">(b, ix);
1405
0
                        serialize<JSON>::op<Opts>(ids_v<T>[value.index()], ctx, b, ix);
1406
0
                        if constexpr (N > 0) {
1407
0
                           dump<R"(,)">(b, ix);
1408
0
                        }
1409
0
                     }
1410
0
                     else {
1411
0
                        dump<"\":\"">(b, ix);
1412
0
                        dump_maybe_empty(ids_v<T>[value.index()], b, ix);
1413
0
                        if constexpr (N == 0) {
1414
0
                           dump<R"(")">(b, ix);
1415
0
                        }
1416
0
                        else {
1417
0
                           dump<R"(",)">(b, ix);
1418
0
                        }
1419
0
                     }
1420
0
                  }
1421
0
                  to<JSON, V>::template op<opening_and_closing_handled<Opts>()>(val, ctx, b, ix);
1422
0
                  // If we skip everything then we may have an extra comma, which we want to revert
1423
0
                  if constexpr (Opts.skip_null_members) {
1424
0
                     if (b[ix - 1] == ',') {
1425
0
                        --ix;
1426
0
                     }
1427
0
                  }
1428
0
1429
0
                  if constexpr (Opts.prettify) {
1430
0
                     ctx.indentation_level -= Opts.indentation_width;
1431
0
                     if constexpr (vector_like<B>) {
1432
0
                        if (const auto k = ix + ctx.indentation_level + write_padding_bytes; k > b.size())
1433
0
                           [[unlikely]] {
1434
0
                           b.resize(2 * k);
1435
0
                        }
1436
0
                     }
1437
0
                     std::memcpy(&b[ix], "\n", 1);
1438
0
                     ++ix;
1439
0
                     std::memset(&b[ix], Opts.indentation_char, ctx.indentation_level);
1440
0
                     ix += ctx.indentation_level;
1441
0
                     std::memcpy(&b[ix], "}", 1);
1442
0
                     ++ix;
1443
0
                  }
1444
0
                  else {
1445
0
                     dump<'}'>(b, ix);
1446
0
                  }
1447
0
               }
1448
0
               else {
1449
0
                  to<JSON, V>::template op<Opts>(val, ctx, b, ix);
1450
0
               }
1451
0
            },
Unexecuted instantiation: _ZZN3glz2toILj10ENSt3__17variantIJDndNS1_12basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEbNS1_6vectorINS_6json_tENS6_ISA_EEEENS1_3mapIS8_SA_NS1_4lessIvEENS6_INS1_4pairIKS8_SA_EEEEEEEEEE2opITnDaXtlNS_4optsELj10ELb1ELb0ELb1ELb1ELb0ELb0ELc32ELh3ELb1EEERS8_RKSL_TkNS_10is_contextERNS_7contextERmEEvOT1_OT2_OT0_OT3_ENKUlOT_E_clIRKDnEEDaS14_
Unexecuted instantiation: _ZZN3glz2toILj10ENSt3__17variantIJDndNS1_12basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEbNS1_6vectorINS_6json_tENS6_ISA_EEEENS1_3mapIS8_SA_NS1_4lessIvEENS6_INS1_4pairIKS8_SA_EEEEEEEEEE2opITnDaXtlNS_4optsELj10ELb1ELb0ELb1ELb1ELb0ELb0ELc32ELh3ELb1EEERS8_RKSL_TkNS_10is_contextERNS_7contextERmEEvOT1_OT2_OT0_OT3_ENKUlOT_E_clIRKdEEDaS14_
Unexecuted instantiation: _ZZN3glz2toILj10ENSt3__17variantIJDndNS1_12basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEbNS1_6vectorINS_6json_tENS6_ISA_EEEENS1_3mapIS8_SA_NS1_4lessIvEENS6_INS1_4pairIKS8_SA_EEEEEEEEEE2opITnDaXtlNS_4optsELj10ELb1ELb0ELb1ELb1ELb0ELb0ELc32ELh3ELb1EEERS8_RKSL_TkNS_10is_contextERNS_7contextERmEEvOT1_OT2_OT0_OT3_ENKUlOT_E_clIRSH_EEDaS14_
Unexecuted instantiation: _ZZN3glz2toILj10ENSt3__17variantIJDndNS1_12basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEbNS1_6vectorINS_6json_tENS6_ISA_EEEENS1_3mapIS8_SA_NS1_4lessIvEENS6_INS1_4pairIKS8_SA_EEEEEEEEEE2opITnDaXtlNS_4optsELj10ELb1ELb0ELb1ELb1ELb0ELb0ELc32ELh3ELb1EEERS8_RKSL_TkNS_10is_contextERNS_7contextERmEEvOT1_OT2_OT0_OT3_ENKUlOT_E_clIRKbEEDaS14_
Unexecuted instantiation: _ZZN3glz2toILj10ENSt3__17variantIJDndNS1_12basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEbNS1_6vectorINS_6json_tENS6_ISA_EEEENS1_3mapIS8_SA_NS1_4lessIvEENS6_INS1_4pairIKS8_SA_EEEEEEEEEE2opITnDaXtlNS_4optsELj10ELb1ELb0ELb1ELb1ELb0ELb0ELc32ELh3ELb1EEERS8_RKSL_TkNS_10is_contextERNS_7contextERmEEvOT1_OT2_OT0_OT3_ENKUlOT_E_clIRKSC_EEDaS14_
Unexecuted instantiation: _ZZN3glz2toILj10ENSt3__17variantIJDndNS1_12basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEbNS1_6vectorINS_6json_tENS6_ISA_EEEENS1_3mapIS8_SA_NS1_4lessIvEENS6_INS1_4pairIKS8_SA_EEEEEEEEEE2opITnDaXtlNS_4optsELj10ELb1ELb0ELb1ELb1ELb0ELb0ELc32ELh3ELb1EEERS8_RKSL_TkNS_10is_contextERNS_7contextERmEEvOT1_OT2_OT0_OT3_ENKUlOT_E_clIRKSK_EEDaS14_
1452
0
            value);
1453
0
      }
1454
   };
1455
1456
   template <class T>
1457
   struct to<JSON, array_variant_wrapper<T>>
1458
   {
1459
      template <auto Opts, class... Args>
1460
      static void op(auto&& wrapper, is_context auto&& ctx, Args&&... args)
1461
      {
1462
         auto& value = wrapper.value;
1463
         dump<'['>(args...);
1464
         if constexpr (Opts.prettify) {
1465
            ctx.indentation_level += Opts.indentation_width;
1466
            dump_newline_indent<Opts.indentation_char>(ctx.indentation_level, args...);
1467
         }
1468
         dump<'"'>(args...);
1469
         dump_maybe_empty(ids_v<T>[value.index()], args...);
1470
         dump<"\",">(args...);
1471
         if constexpr (Opts.prettify) {
1472
            dump_newline_indent<Opts.indentation_char>(ctx.indentation_level, args...);
1473
         }
1474
         std::visit([&](auto&& v) { serialize<JSON>::op<Opts>(v, ctx, args...); }, value);
1475
         if constexpr (Opts.prettify) {
1476
            ctx.indentation_level -= Opts.indentation_width;
1477
            dump_newline_indent<Opts.indentation_char>(ctx.indentation_level, args...);
1478
         }
1479
         dump<']'>(args...);
1480
      }
1481
   };
1482
1483
   template <class T>
1484
      requires is_specialization_v<T, arr>
1485
   struct to<JSON, T>
1486
   {
1487
      template <auto Opts, class... Args>
1488
      static void op(auto&& value, is_context auto&& ctx, Args&&... args)
1489
      {
1490
         using V = std::decay_t<decltype(value.value)>;
1491
         static constexpr auto N = glz::tuple_size_v<V>;
1492
1493
         dump<'['>(args...);
1494
         if constexpr (N > 0 && Opts.prettify) {
1495
            if constexpr (Opts.new_lines_in_arrays) {
1496
               ctx.indentation_level += Opts.indentation_width;
1497
               dump_newline_indent<Opts.indentation_char>(ctx.indentation_level, args...);
1498
            }
1499
         }
1500
         for_each<N>([&]<size_t I>() {
1501
            if constexpr (glaze_array_t<V>) {
1502
               serialize<JSON>::op<Opts>(get_member(value.value, glz::get<I>(meta_v<T>)), ctx, args...);
1503
            }
1504
            else {
1505
               serialize<JSON>::op<Opts>(glz::get<I>(value.value), ctx, args...);
1506
            }
1507
            constexpr bool needs_comma = I < N - 1;
1508
            if constexpr (needs_comma) {
1509
               write_array_entry_separator<Opts>(ctx, args...);
1510
            }
1511
         });
1512
         if constexpr (N > 0 && Opts.prettify) {
1513
            if constexpr (Opts.new_lines_in_arrays) {
1514
               ctx.indentation_level -= Opts.indentation_width;
1515
               dump_newline_indent<Opts.indentation_char>(ctx.indentation_level, args...);
1516
            }
1517
         }
1518
         dump<']'>(args...);
1519
      }
1520
   };
1521
1522
   template <class T>
1523
      requires glaze_array_t<T> || tuple_t<std::decay_t<T>> || is_std_tuple<T>
1524
   struct to<JSON, T>
1525
   {
1526
      template <auto Opts, class... Args>
1527
      static void op(auto&& value, is_context auto&& ctx, Args&&... args)
1528
      {
1529
         static constexpr auto N = []() constexpr {
1530
            if constexpr (glaze_array_t<std::decay_t<T>>) {
1531
               return glz::tuple_size_v<meta_t<std::decay_t<T>>>;
1532
            }
1533
            else {
1534
               return glz::tuple_size_v<std::decay_t<T>>;
1535
            }
1536
         }();
1537
1538
         dump<'['>(args...);
1539
         if constexpr (N > 0 && Opts.prettify) {
1540
            if constexpr (Opts.new_lines_in_arrays) {
1541
               ctx.indentation_level += Opts.indentation_width;
1542
               dump_newline_indent<Opts.indentation_char>(ctx.indentation_level, args...);
1543
            }
1544
         }
1545
         using V = std::decay_t<T>;
1546
         for_each<N>([&]<size_t I>() {
1547
            if constexpr (glaze_array_t<V>) {
1548
               serialize<JSON>::op<Opts>(get_member(value, glz::get<I>(meta_v<T>)), ctx, args...);
1549
            }
1550
            else if constexpr (is_std_tuple<T>) {
1551
               using Value = core_t<decltype(std::get<I>(value))>;
1552
               to<JSON, Value>::template op<Opts>(std::get<I>(value), ctx, args...);
1553
            }
1554
            else {
1555
               using Value = core_t<decltype(glz::get<I>(value))>;
1556
               to<JSON, Value>::template op<Opts>(glz::get<I>(value), ctx, args...);
1557
            }
1558
            constexpr bool needs_comma = I < N - 1;
1559
            if constexpr (needs_comma) {
1560
               write_array_entry_separator<Opts>(ctx, args...);
1561
            }
1562
         });
1563
         if constexpr (N > 0 && Opts.prettify) {
1564
            if constexpr (Opts.new_lines_in_arrays) {
1565
               ctx.indentation_level -= Opts.indentation_width;
1566
               dump_newline_indent<Opts.indentation_char>(ctx.indentation_level, args...);
1567
            }
1568
         }
1569
         dump<']'>(args...);
1570
      }
1571
   };
1572
1573
   template <is_includer T>
1574
   struct to<JSON, T>
1575
   {
1576
      template <auto Opts, class... Args>
1577
      GLZ_ALWAYS_INLINE static void op(auto&&, is_context auto&&, Args&&... args)
1578
      {
1579
         dump<R"("")">(args...); // dump an empty string
1580
      }
1581
   };
1582
1583
   template <const std::string_view& S>
1584
   GLZ_ALWAYS_INLINE constexpr auto array_from_sv() noexcept
1585
   {
1586
      constexpr auto N = S.size();
1587
      std::array<char, N> arr;
1588
      std::copy_n(S.data(), N, arr.data());
1589
      return arr;
1590
   }
1591
1592
   template <class T>
1593
      requires is_specialization_v<T, glz::obj> || is_specialization_v<T, glz::obj_copy>
1594
   struct to<JSON, T>
1595
   {
1596
      template <auto Options>
1597
      static void op(auto&& value, is_context auto&& ctx, auto&& b, auto&& ix)
1598
      {
1599
         if constexpr (!check_opening_handled(Options)) {
1600
            dump<'{'>(b, ix);
1601
            if constexpr (Options.prettify) {
1602
               ctx.indentation_level += Options.indentation_width;
1603
               dump<'\n'>(b, ix);
1604
               dumpn<Options.indentation_char>(ctx.indentation_level, b, ix);
1605
            }
1606
         }
1607
1608
         using V = std::decay_t<decltype(value.value)>;
1609
         static constexpr auto N = glz::tuple_size_v<V> / 2;
1610
1611
         bool first = true;
1612
         for_each<N>([&]<size_t I>() {
1613
            constexpr auto Opts = opening_and_closing_handled_off<ws_handled_off<Options>()>();
1614
            decltype(auto) item = glz::get<2 * I + 1>(value.value);
1615
            using val_t = std::decay_t<decltype(item)>;
1616
1617
            if (skip_member<Opts>(item)) {
1618
               return;
1619
            }
1620
1621
            // skip
1622
            if constexpr (always_skipped<val_t>) {
1623
               return;
1624
            }
1625
            else {
1626
               if (first) {
1627
                  first = false;
1628
               }
1629
               else {
1630
                  // Null members may be skipped so we can't just write it out for all but the last member unless
1631
                  // trailing commas are allowed
1632
                  write_object_entry_separator<Opts>(ctx, b, ix);
1633
               }
1634
1635
               using Key = typename std::decay_t<glz::tuple_element_t<2 * I, V>>;
1636
1637
               if constexpr (str_t<Key> || char_t<Key>) {
1638
                  const sv key = glz::get<2 * I>(value.value);
1639
                  to<JSON, decltype(key)>::template op<Opts>(key, ctx, b, ix);
1640
                  dump<':'>(b, ix);
1641
                  if constexpr (Opts.prettify) {
1642
                     dump<' '>(b, ix);
1643
                  }
1644
               }
1645
               else {
1646
                  dump<'"'>(b, ix);
1647
                  to<JSON, val_t>::template op<Opts>(item, ctx, b, ix);
1648
                  dump_not_empty(Opts.prettify ? "\": " : "\":", b, ix);
1649
               }
1650
1651
               to<JSON, val_t>::template op<Opts>(item, ctx, b, ix);
1652
            }
1653
         });
1654
1655
         if constexpr (!check_closing_handled(Options)) {
1656
            if constexpr (Options.prettify) {
1657
               ctx.indentation_level -= Options.indentation_width;
1658
               dump<'\n'>(b, ix);
1659
               dumpn<Options.indentation_char>(ctx.indentation_level, b, ix);
1660
            }
1661
            dump<'}'>(b, ix);
1662
         }
1663
      }
1664
   };
1665
1666
   template <class T>
1667
      requires is_specialization_v<T, glz::merge>
1668
   struct to<JSON, T>
1669
   {
1670
      template <auto Options>
1671
      static void op(auto&& value, is_context auto&& ctx, auto&& b, auto&& ix)
1672
      {
1673
         if constexpr (!check_opening_handled(Options)) {
1674
            dump<'{'>(b, ix);
1675
            if constexpr (Options.prettify) {
1676
               ctx.indentation_level += Options.indentation_width;
1677
               dump<'\n'>(b, ix);
1678
               dumpn<Options.indentation_char>(ctx.indentation_level, b, ix);
1679
            }
1680
         }
1681
1682
         using V = std::decay_t<decltype(value.value)>;
1683
         static constexpr auto N = glz::tuple_size_v<V>;
1684
1685
         [[maybe_unused]] static constexpr auto Opts = opening_and_closing_handled<Options>();
1686
1687
         // When merging it is possible that objects are completed empty
1688
         // and therefore behave like skipped members even when skip_null_members is off
1689
1690
         for_each<N>([&]<size_t I>() {
1691
            // We don't want to dump a comma when nothing is written
1692
            const auto ix_start = ix;
1693
            using Value = core_t<decltype(get<I>(value.value))>;
1694
            to<JSON, Value>::template op<Opts>(get<I>(value.value), ctx, b, ix);
1695
            if (ix > ix_start) // we wrote something
1696
            {
1697
               dump<','>(b, ix);
1698
            }
1699
         });
1700
1701
         // we may have a trailing comma, which needs to be removed
1702
         if (b[ix - 1] == ',') {
1703
            --ix;
1704
         }
1705
1706
         if constexpr (Options.prettify) {
1707
            ctx.indentation_level -= Options.indentation_width;
1708
            dump<'\n'>(b, ix);
1709
            dumpn<Options.indentation_char>(ctx.indentation_level, b, ix);
1710
         }
1711
         dump<'}'>(b, ix);
1712
      }
1713
   };
1714
1715
   // Only use this if you are not prettifying
1716
   // Returns zero if the fixed size cannot be determined
1717
   template <class T>
1718
   inline constexpr size_t fixed_padding = [] {
1719
      constexpr auto N = reflect<T>::size;
1720
      size_t fixed = 2 + 16; // {} + extra padding
1721
      for_each_short_circuit<N>([&]<auto I>() -> bool {
1722
         using val_t = field_t<T, I>;
1723
         if constexpr (required_padding<val_t>()) {
1724
            fixed += required_padding<val_t>();
1725
            fixed += reflect<T>::keys[I].size() + 2; // quoted key length
1726
            fixed += 2; // colon and comma
1727
            return false; // continue
1728
         }
1729
         else {
1730
            fixed = 0;
1731
            return true; // break
1732
         }
1733
      });
1734
      if (fixed) {
1735
         fixed = round_up_to_nearest_16(fixed);
1736
      }
1737
      return fixed;
1738
   }();
1739
1740
   template <class T>
1741
      requires((glaze_object_t<T> || reflectable<T>) && not custom_write<T>)
1742
   struct to<JSON, T>
1743
   {
1744
      template <auto Options, class V, class B>
1745
         requires(not std::is_pointer_v<std::remove_cvref_t<V>>)
1746
      static void op(V&& value, is_context auto&& ctx, B&& b, auto&& ix)
1747
      {
1748
         using ValueType = std::decay_t<V>;
1749
         if constexpr (has_unknown_writer<ValueType> && not check_disable_write_unknown(Options)) {
1750
            constexpr auto& writer = meta_unknown_write_v<ValueType>;
1751
1752
            using WriterType = meta_unknown_write_t<ValueType>;
1753
            if constexpr (std::is_member_object_pointer_v<WriterType>) {
1754
               decltype(auto) unknown_writer = value.*writer;
1755
               if (unknown_writer.size() > 0) {
1756
                  // TODO: This intermediate is added to get GCC 14 to build
1757
                  decltype(auto) merged = glz::merge{value, unknown_writer};
1758
                  serialize<JSON>::op<disable_write_unknown_on<Options>()>(std::move(merged), ctx, b, ix);
1759
               }
1760
               else {
1761
                  serialize<JSON>::op<disable_write_unknown_on<Options>()>(value, ctx, b, ix);
1762
               }
1763
            }
1764
            else if constexpr (std::is_member_function_pointer_v<WriterType>) {
1765
               decltype(auto) unknown_writer = (value.*writer)();
1766
               if (unknown_writer.size() > 0) {
1767
                  // TODO: This intermediate is added to get GCC 14 to build
1768
                  decltype(auto) merged = glz::merge{value, unknown_writer};
1769
                  serialize<JSON>::op<disable_write_unknown_on<Options>()>(std::move(merged), ctx, b, ix);
1770
               }
1771
               else {
1772
                  serialize<JSON>::op<disable_write_unknown_on<Options>()>(value, ctx, b, ix);
1773
               }
1774
            }
1775
            else {
1776
               static_assert(false_v<T>, "unknown_write type not handled");
1777
            }
1778
         }
1779
         else {
1780
            // handles glaze_object_t without extra unknown fields
1781
            static constexpr auto Opts =
1782
               disable_write_unknown_off<opening_and_closing_handled_off<ws_handled_off<Options>()>()>();
1783
1784
            if constexpr (not check_opening_handled(Options)) {
1785
               if constexpr (Options.prettify) {
1786
                  ctx.indentation_level += Options.indentation_width;
1787
                  if constexpr (vector_like<B>) {
1788
                     if (const auto k = ix + ctx.indentation_level + write_padding_bytes; k > b.size()) [[unlikely]] {
1789
                        b.resize(2 * k);
1790
                     }
1791
                  }
1792
                  std::memcpy(&b[ix], "{\n", 2);
1793
                  ix += 2;
1794
                  std::memset(&b[ix], Opts.indentation_char, ctx.indentation_level);
1795
                  ix += ctx.indentation_level;
1796
               }
1797
               else {
1798
                  dump<'{'>(b, ix);
1799
               }
1800
            }
1801
1802
            static constexpr auto N = reflect<T>::size;
1803
1804
            decltype(auto) t = [&]() -> decltype(auto) {
1805
               if constexpr (reflectable<T>) {
1806
                  return to_tie(value);
1807
               }
1808
               else {
1809
                  return nullptr;
1810
               }
1811
            }();
1812
1813
            static constexpr auto padding = round_up_to_nearest_16(maximum_key_size<T> + write_padding_bytes);
1814
            if constexpr (maybe_skipped<Opts, T>) {
1815
               bool first = true;
1816
               for_each<N>([&]<size_t I>() {
1817
                  using val_t = field_t<T, I>;
1818
1819
                  if constexpr (meta_has_skip<T>) {
1820
                     static constexpr meta_context mctx{.op = operation::serialize};
1821
                     if constexpr (meta<T>::skip(reflect<T>::keys[I], mctx)) return;
1822
                  }
1823
1824
                  if constexpr (always_skipped<val_t>) {
1825
                     return;
1826
                  }
1827
                  else {
1828
                     if constexpr (null_t<val_t> && Opts.skip_null_members) {
1829
                        if constexpr (always_null_t<val_t>)
1830
                           return;
1831
                        else {
1832
                           const auto is_null = [&]() {
1833
                              decltype(auto) element = [&]() -> decltype(auto) {
1834
                                 if constexpr (reflectable<T>) {
1835
                                    return get<I>(t);
1836
                                 }
1837
                                 else {
1838
                                    return get<I>(reflect<T>::values);
1839
                                 }
1840
                              };
1841
1842
                              if constexpr (nullable_wrapper<val_t>) {
1843
                                 return !bool(element()(value).val);
1844
                              }
1845
                              else if constexpr (nullable_value_t<val_t>) {
1846
                                 return !get_member(value, element()).has_value();
1847
                              }
1848
                              else {
1849
                                 return !bool(get_member(value, element()));
1850
                              }
1851
                           }();
1852
                           if (is_null) return;
1853
                        }
1854
                     }
1855
1856
                     if constexpr (Opts.prettify) {
1857
                        maybe_pad(padding + ctx.indentation_level, b, ix);
1858
                     }
1859
                     else {
1860
                        maybe_pad<padding>(b, ix);
1861
                     }
1862
1863
                     if (first) {
1864
                        first = false;
1865
                     }
1866
                     else {
1867
                        // Null members may be skipped so we can't just write it out for all but the last member
1868
                        if constexpr (Opts.prettify) {
1869
                           std::memcpy(&b[ix], ",\n", 2);
1870
                           ix += 2;
1871
                           std::memset(&b[ix], Opts.indentation_char, ctx.indentation_level);
1872
                           ix += ctx.indentation_level;
1873
                        }
1874
                        else {
1875
                           std::memcpy(&b[ix], ",", 1);
1876
                           ++ix;
1877
                        }
1878
                     }
1879
1880
                     // MSVC requires get<I> rather than keys[I]
1881
                     static constexpr auto key = glz::get<I>(reflect<T>::keys); // GCC 14 requires auto here
1882
                     static constexpr auto quoted_key = quoted_key_v<key, Opts.prettify>;
1883
                     static constexpr auto n = quoted_key.size();
1884
                     std::memcpy(&b[ix], quoted_key.data(), n);
1885
                     ix += n;
1886
1887
                     static constexpr auto check_opts = required_padding<val_t>() ? write_unchecked_on<Opts>() : Opts;
1888
                     if constexpr (reflectable<T>) {
1889
                        to<JSON, val_t>::template op<check_opts>(get_member(value, get<I>(t)), ctx, b, ix);
1890
                     }
1891
                     else {
1892
                        to<JSON, val_t>::template op<check_opts>(get_member(value, get<I>(reflect<T>::values)), ctx, b,
1893
                                                                 ix);
1894
                     }
1895
                  }
1896
               });
1897
            }
1898
            else {
1899
               static constexpr size_t fixed_max_size = fixed_padding<T>;
1900
               if constexpr (fixed_max_size) {
1901
                  maybe_pad<fixed_max_size>(b, ix);
1902
               }
1903
1904
               for_each<N>([&]<size_t I>() {
1905
                  if constexpr (not fixed_max_size) {
1906
                     if constexpr (Opts.prettify) {
1907
                        maybe_pad(padding + ctx.indentation_level, b, ix);
1908
                     }
1909
                     else {
1910
                        maybe_pad<padding>(b, ix);
1911
                     }
1912
                  }
1913
1914
                  if constexpr (I != 0 && Opts.prettify) {
1915
                     std::memcpy(&b[ix], ",\n", 2);
1916
                     ix += 2;
1917
                     std::memset(&b[ix], Opts.indentation_char, ctx.indentation_level);
1918
                     ix += ctx.indentation_level;
1919
                  }
1920
1921
                  using val_t = field_t<T, I>;
1922
1923
                  // MSVC requires get<I> rather than keys[I]
1924
                  static constexpr auto key = glz::get<I>(reflect<T>::keys); // GCC 14 requires auto here
1925
                  if constexpr (always_null_t<val_t>) {
1926
                     if constexpr (I == 0 || Opts.prettify) {
1927
                        static constexpr auto quoted_key = join_v<quoted_key_v<key, Opts.prettify>, chars<"null">>;
1928
                        static constexpr auto n = quoted_key.size();
1929
                        std::memcpy(&b[ix], quoted_key.data(), n);
1930
                        ix += n;
1931
                     }
1932
                     else {
1933
                        static constexpr auto quoted_key = join_v<chars<",">, quoted_key_v<key>, chars<"null">>;
1934
                        static constexpr auto n = quoted_key.size();
1935
                        std::memcpy(&b[ix], quoted_key.data(), n);
1936
                        ix += n;
1937
                     }
1938
                  }
1939
                  else {
1940
                     if constexpr (I == 0 || Opts.prettify) {
1941
                        static constexpr auto quoted_key = quoted_key_v<key, Opts.prettify>;
1942
                        static constexpr auto n = quoted_key.size();
1943
                        std::memcpy(&b[ix], quoted_key.data(), n);
1944
                        ix += n;
1945
                     }
1946
                     else {
1947
                        static constexpr auto quoted_key = join_v<chars<",">, quoted_key_v<key>>;
1948
                        static constexpr auto n = quoted_key.size();
1949
                        std::memcpy(&b[ix], quoted_key.data(), n);
1950
                        ix += n;
1951
                     }
1952
1953
                     static constexpr auto check_opts = required_padding<val_t>() ? write_unchecked_on<Opts>() : Opts;
1954
                     if constexpr (reflectable<T>) {
1955
                        to<JSON, val_t>::template op<check_opts>(get_member(value, get<I>(t)), ctx, b, ix);
1956
                     }
1957
                     else {
1958
                        to<JSON, val_t>::template op<check_opts>(get_member(value, get<I>(reflect<T>::values)), ctx, b,
1959
                                                                 ix);
1960
                     }
1961
                  }
1962
               });
1963
            }
1964
1965
            // Options is required here, because it must be the top level
1966
            if constexpr (not check_closing_handled(Options)) {
1967
               if constexpr (Options.prettify) {
1968
                  ctx.indentation_level -= Options.indentation_width;
1969
                  if constexpr (vector_like<B>) {
1970
                     if (const auto k = ix + ctx.indentation_level + write_padding_bytes; k > b.size()) [[unlikely]] {
1971
                        b.resize(2 * k);
1972
                     }
1973
                  }
1974
                  std::memcpy(&b[ix], "\n", 1);
1975
                  ++ix;
1976
                  std::memset(&b[ix], Opts.indentation_char, ctx.indentation_level);
1977
                  ix += ctx.indentation_level;
1978
                  std::memcpy(&b[ix], "}", 1);
1979
                  ++ix;
1980
               }
1981
               else {
1982
                  dump<'}'>(b, ix);
1983
               }
1984
            }
1985
         }
1986
      }
1987
   };
1988
1989
   template <write_supported<JSON> T, output_buffer Buffer>
1990
   [[nodiscard]] error_ctx write_json(T&& value, Buffer&& buffer)
1991
   {
1992
      return write<opts{}>(std::forward<T>(value), std::forward<Buffer>(buffer));
1993
   }
1994
1995
   template <write_supported<JSON> T, raw_buffer Buffer>
1996
   [[nodiscard]] glz::expected<size_t, error_ctx> write_json(T&& value, Buffer&& buffer)
1997
   {
1998
      return write<opts{}>(std::forward<T>(value), std::forward<Buffer>(buffer));
1999
   }
2000
2001
   template <write_supported<JSON> T>
2002
   [[nodiscard]] glz::expected<std::string, error_ctx> write_json(T&& value)
2003
0
   {
2004
0
      return write<opts{}>(std::forward<T>(value));
2005
0
   }
2006
2007
   template <auto& Partial, write_supported<JSON> T, output_buffer Buffer>
2008
   [[nodiscard]] error_ctx write_json(T&& value, Buffer&& buffer)
2009
   {
2010
      return write<Partial, opts{}>(std::forward<T>(value), std::forward<Buffer>(buffer));
2011
   }
2012
2013
   template <auto& Partial, write_supported<JSON> T, raw_buffer Buffer>
2014
   [[nodiscard]] glz::expected<size_t, error_ctx> write_json(T&& value, Buffer&& buffer)
2015
   {
2016
      return write<Partial, opts{}>(std::forward<T>(value), std::forward<Buffer>(buffer));
2017
   }
2018
2019
   template <write_supported<JSON> T, class Buffer>
2020
   [[nodiscard]] error_ctx write_jsonc(T&& value, Buffer&& buffer)
2021
   {
2022
      return write<opts{.comments = true}>(std::forward<T>(value), std::forward<Buffer>(buffer));
2023
   }
2024
2025
   template <write_supported<JSON> T>
2026
   [[nodiscard]] glz::expected<std::string, error_ctx> write_jsonc(T&& value)
2027
   {
2028
      return write<opts{.comments = true}>(std::forward<T>(value));
2029
   }
2030
2031
   template <auto Opts = opts{}, write_supported<JSON> T>
2032
   [[nodiscard]] error_ctx write_file_json(T&& value, const sv file_name, auto&& buffer)
2033
   {
2034
      const auto ec = write<set_json<Opts>()>(std::forward<T>(value), buffer);
2035
      if (bool(ec)) [[unlikely]] {
2036
         return ec;
2037
      }
2038
      return {buffer_to_file(buffer, file_name)};
2039
   }
2040
}
2041
2042
#if defined(_MSC_VER)
2043
#pragma warning(pop)
2044
#endif