simdjson 5.0.2
Ridiculously Fast JSON
Loading...
Searching...
No Matches
json_string_builder-inl.h
1#include <array>
2#include <cmath>
3#include <cstring>
4#include <limits>
5#include <type_traits>
6#ifndef SIMDJSON_GENERIC_STRING_BUILDER_INL_H
7
8#ifndef SIMDJSON_CONDITIONAL_INCLUDE
9#define SIMDJSON_GENERIC_STRING_BUILDER_INL_H
10#include "simdjson/generic/builder/json_string_builder.h"
11#endif // SIMDJSON_CONDITIONAL_INCLUDE
12
13/*
14 * Empirically, we have found that an inlined optimization is important for
15 * performance. The following macros are not ideal. We should find a better
16 * way to inline the code.
17 */
18
19#if defined(__SSE2__) || defined(__x86_64__) || defined(__x86_64) || \
20 (defined(_M_AMD64) || defined(_M_X64) || \
21 (defined(_M_IX86_FP) && _M_IX86_FP == 2))
22#ifndef SIMDJSON_EXPERIMENTAL_HAS_SSE2
23#define SIMDJSON_EXPERIMENTAL_HAS_SSE2 1
24#endif
25#endif
26
27#if defined(__aarch64__) || defined(_M_ARM64)
28#ifndef SIMDJSON_EXPERIMENTAL_HAS_NEON
29#define SIMDJSON_EXPERIMENTAL_HAS_NEON 1
30#endif
31#endif
32#if defined(__loongarch_sx)
33#ifndef SIMDJSON_EXPERIMENTAL_HAS_LSX
34#define SIMDJSON_EXPERIMENTAL_HAS_LSX 1
35#endif
36#endif
37#if defined(__loongarch_asx)
38#ifndef SIMDJSON_EXPERIMENTAL_HAS_LASX
39#define SIMDJSON_EXPERIMENTAL_HAS_LASX 1
40#endif
41#endif
42#if defined(__riscv_v_intrinsic) && __riscv_v_intrinsic >= 11000 && \
43 defined(__riscv_vector)
44#ifndef SIMDJSON_EXPERIMENTAL_HAS_RVV
45#define SIMDJSON_EXPERIMENTAL_HAS_RVV 1
46#endif
47#endif
48#if (defined(__PPC64__) || defined(_M_PPC64)) && defined(__ALTIVEC__) && defined(__POWER8_VECTOR__)
49#ifndef SIMDJSON_EXPERIMENTAL_HAS_PPC64
50#define SIMDJSON_EXPERIMENTAL_HAS_PPC64 1
51#endif
52#endif
53#if SIMDJSON_EXPERIMENTAL_HAS_NEON
54#include <arm_neon.h>
55#ifdef _MSC_VER
56#include <intrin.h>
57#endif
58#endif
59#if SIMDJSON_EXPERIMENTAL_HAS_SSE2
60#include <emmintrin.h>
61#if defined(__AVX2__)
62#include <immintrin.h>
63#endif
64#ifdef _MSC_VER
65#include <intrin.h>
66#endif
67#endif
68#if SIMDJSON_EXPERIMENTAL_HAS_LSX
69#include <lsxintrin.h>
70#endif
71#if SIMDJSON_EXPERIMENTAL_HAS_LASX
72#include <lasxintrin.h>
73#endif
74#if SIMDJSON_EXPERIMENTAL_HAS_RVV
75#include <riscv_vector.h>
76#endif
77#if SIMDJSON_EXPERIMENTAL_HAS_PPC64
78#include <altivec.h>
79#ifdef bool
80#undef bool
81#endif
82#ifdef vector
83#undef vector
84#endif
85#endif
86
87
88namespace simdjson {
89namespace SIMDJSON_IMPLEMENTATION {
90namespace builder {
91
92static SIMDJSON_CONSTEXPR_LAMBDA std::array<uint8_t, 256>
93 json_quotable_character = {
94 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
95 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
96 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
97 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
98 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
99 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
100 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
101 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
102 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
103 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
104 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
105
123// Scalar fallback for finding next quotable character
124SIMDJSON_CONSTEXPR_LAMBDA simdjson_inline size_t
125find_next_json_quotable_character_scalar(const std::string_view view,
126 size_t location) noexcept {
127 for (auto pos = view.begin() + location; pos != view.end(); ++pos) {
128 if (json_quotable_character[static_cast<uint8_t>(*pos)]) {
129 return pos - view.begin();
130 }
131 }
132 return size_t(view.size());
133}
134
135// SIMD-accelerated position finding that directly locates the first quotable
136// character, combining detection and position extraction in a single pass to
137// minimize redundant work.
138#if SIMDJSON_EXPERIMENTAL_HAS_NEON
139simdjson_inline size_t
140find_next_json_quotable_character(const std::string_view view,
141 size_t location) noexcept {
142 const size_t len = view.size();
143 const uint8_t *ptr =
144 reinterpret_cast<const uint8_t *>(view.data()) + location;
145 size_t remaining = len - location;
146
147 // SIMD constants for characters requiring escape
148 uint8x16_t v34 = vdupq_n_u8(34); // '"'
149 uint8x16_t v92 = vdupq_n_u8(92); // '\\'
150 uint8x16_t v32 = vdupq_n_u8(32); // control char threshold
151
152 while (remaining >= 16) {
153 uint8x16_t word = vld1q_u8(ptr);
154
155 // Check for quotable characters: '"', '\\', or control chars (< 32)
156 uint8x16_t needs_escape = vceqq_u8(word, v34);
157 needs_escape = vorrq_u8(needs_escape, vceqq_u8(word, v92));
158 needs_escape = vorrq_u8(needs_escape, vcltq_u8(word, v32));
159
160 const uint8x8_t res = vshrn_n_u16(vreinterpretq_u16_u8(needs_escape), 4);
161 const uint64_t mask = vget_lane_u64(vreinterpret_u64_u8(res), 0);
162 if(mask != 0) {
163 size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
164 auto trailing_zero = trailing_zeroes(mask);
165 return offset + (trailing_zero >> 2);
166 }
167 ptr += 16;
168 remaining -= 16;
169 }
170
171 // Scalar fallback for remaining bytes
172 size_t current = len - remaining;
173 return find_next_json_quotable_character_scalar(view, current);
174}
175#elif SIMDJSON_EXPERIMENTAL_HAS_SSE2
176simdjson_inline size_t
177find_next_json_quotable_character(const std::string_view view,
178 size_t location) noexcept {
179 const size_t len = view.size();
180 const uint8_t *ptr =
181 reinterpret_cast<const uint8_t *>(view.data()) + location;
182 size_t remaining = len - location;
183
184 // SIMD constants
185 __m128i v34 = _mm_set1_epi8(34); // '"'
186 __m128i v92 = _mm_set1_epi8(92); // '\\'
187 __m128i v31 = _mm_set1_epi8(31); // for control char detection
188
189 while (remaining >= 16) {
190 __m128i word = _mm_loadu_si128(reinterpret_cast<const __m128i *>(ptr));
191
192 // Check for quotable characters
193 __m128i needs_escape = _mm_cmpeq_epi8(word, v34);
194 needs_escape = _mm_or_si128(needs_escape, _mm_cmpeq_epi8(word, v92));
195 needs_escape = _mm_or_si128(
196 needs_escape,
197 _mm_cmpeq_epi8(_mm_subs_epu8(word, v31), _mm_setzero_si128()));
198
199 int mask = _mm_movemask_epi8(needs_escape);
200 if (mask != 0) {
201 // Found quotable character - use trailing zero count to find position
202 size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
203 return offset + trailing_zeroes(mask);
204 }
205 ptr += 16;
206 remaining -= 16;
207 }
208
209 // Scalar fallback for remaining bytes
210 size_t current = len - remaining;
211 return find_next_json_quotable_character_scalar(view, current);
212}
213#elif SIMDJSON_EXPERIMENTAL_HAS_LASX
214simdjson_inline size_t
215find_next_json_quotable_character(const std::string_view view,
216 size_t location) noexcept {
217 const size_t len = view.size();
218 const uint8_t *ptr =
219 reinterpret_cast<const uint8_t *>(view.data()) + location;
220 size_t remaining = len - location;
221
222 // SIMD constants for characters requiring escape
223 __m256i v34 = __lasx_xvreplgr2vr_b(34); // '"'
224 __m256i v92 = __lasx_xvreplgr2vr_b(92); // '\\'
225 __m256i v32 = __lasx_xvreplgr2vr_b(32); // control char threshold
226
227 while (remaining >= 32) {
228 __m256i word = __lasx_xvld(ptr, 0);
229
230 // Check for quotable characters: '"', '\\', or control chars (< 32)
231 __m256i needs_escape = __lasx_xvseq_b(word, v34);
232 needs_escape = __lasx_xvor_v(needs_escape, __lasx_xvseq_b(word, v92));
233 needs_escape = __lasx_xvor_v(needs_escape, __lasx_xvslt_bu(word, v32));
234
235 if (!__lasx_xbz_v(needs_escape)) {
236 // Found a quotable character - locate it via the four 64-bit lanes
237 uint64_t lane0 = __lasx_xvpickve2gr_du(needs_escape, 0);
238 uint64_t lane1 = __lasx_xvpickve2gr_du(needs_escape, 1);
239 uint64_t lane2 = __lasx_xvpickve2gr_du(needs_escape, 2);
240 uint64_t lane3 = __lasx_xvpickve2gr_du(needs_escape, 3);
241 size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
242 if (lane0 != 0) {
243 return offset + trailing_zeroes(lane0) / 8;
244 } else if (lane1 != 0) {
245 return offset + 8 + trailing_zeroes(lane1) / 8;
246 } else if (lane2 != 0) {
247 return offset + 16 + trailing_zeroes(lane2) / 8;
248 } else {
249 return offset + 24 + trailing_zeroes(lane3) / 8;
250 }
251 }
252 ptr += 32;
253 remaining -= 32;
254 }
255 size_t current = len - remaining;
256 return find_next_json_quotable_character_scalar(view, current);
257}
258#elif SIMDJSON_EXPERIMENTAL_HAS_LSX
259simdjson_inline size_t
260find_next_json_quotable_character(const std::string_view view,
261 size_t location) noexcept {
262 const size_t len = view.size();
263 const uint8_t *ptr =
264 reinterpret_cast<const uint8_t *>(view.data()) + location;
265 size_t remaining = len - location;
266
267 //SIMD constants for characters requiring escape
268 __m128i v34 = __lsx_vreplgr2vr_b(34); // '"'
269 __m128i v92 = __lsx_vreplgr2vr_b(92); // '\\'
270 __m128i v32 = __lsx_vreplgr2vr_b(32); // control char threshold
271
272 while (remaining >= 16){
273 __m128i word = __lsx_vld(ptr, 0);
274
275 //Check for the quotable characters: '"', '\\', or control char (<32)
276 __m128i needs_escape = __lsx_vseq_b(word, v34);
277 needs_escape = __lsx_vor_v(needs_escape, __lsx_vseq_b(word, v92));
278 needs_escape = __lsx_vor_v(needs_escape, __lsx_vslt_bu(word, v32));
279
280 if (!__lsx_bz_v(needs_escape)){
281
282 //Found quotable character - extract exact byte position
283 uint64_t lo = __lsx_vpickve2gr_du(needs_escape,0);
284 uint64_t hi = __lsx_vpickve2gr_du(needs_escape,1);
285 size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
286 if ( lo != 0) {
287 return offset + trailing_zeroes(lo) / 8;
288 } else {
289 return offset + 8 + trailing_zeroes(hi) / 8;
290 }
291 }
292 ptr += 16;
293 remaining -= 16;
294 }
295 size_t current = len - remaining;
296 return find_next_json_quotable_character_scalar(view, current);
297}
298#elif SIMDJSON_EXPERIMENTAL_HAS_RVV
299simdjson_inline size_t
300find_next_json_quotable_character(const std::string_view view,
301 size_t location) noexcept {
302 const size_t len = view.size();
303 const uint8_t *ptr =
304 reinterpret_cast<const uint8_t *>(view.data()) + location;
305 size_t remaining = len - location;
306
307 while (remaining > 0) {
308 size_t vl = __riscv_vsetvl_e8m1(remaining);
309 vuint8m1_t word = __riscv_vle8_v_u8m1(ptr, vl);
310
311 // Check for quotable characters: '"', '\\', or control chars (< 32)
312 vbool8_t needs_escape = __riscv_vmseq(word, (uint8_t)34, vl);
313 needs_escape = __riscv_vmor(needs_escape,
314 __riscv_vmseq(word, (uint8_t)92, vl), vl);
315 needs_escape = __riscv_vmor(needs_escape,
316 __riscv_vmsltu(word, (uint8_t)32, vl), vl);
317
318 long first = __riscv_vfirst(needs_escape, vl);
319 if (first >= 0) {
320 size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
321 return offset + first;
322 }
323 ptr += vl;
324 remaining -= vl;
325 }
326
327 return len;
328}
329#elif SIMDJSON_EXPERIMENTAL_HAS_PPC64
330simdjson_inline size_t
331find_next_json_quotable_character(const std::string_view view,
332 size_t location) noexcept {
333 const size_t len = view.size();
334 const uint8_t *ptr =
335 reinterpret_cast<const uint8_t *>(view.data()) + location;
336 size_t remaining = len - location;
337
338 // SIMD constants for characters requiring escape
339 __vector unsigned char v34 = vec_splats((unsigned char)34); // '"'
340 __vector unsigned char v92 = vec_splats((unsigned char)92); // '\\'
341 __vector unsigned char v32 = vec_splats((unsigned char)32); // control char threshold
342
343 // Bitmask for vec_vbpermq to extract one bit per byte
344 const __vector unsigned char perm_mask = {0x78, 0x70, 0x68, 0x60, 0x58, 0x50,
345 0x48, 0x40, 0x38, 0x30, 0x28, 0x20,
346 0x18, 0x10, 0x08, 0x00};
347
348 while (remaining >= 16) {
349 __vector unsigned char word =
350 vec_vsx_ld(0, reinterpret_cast<const unsigned char *>(ptr));
351
352 // Check for quotable characters: '"', '\\', or control chars (< 32)
353 __vector unsigned char needs_escape =
354 (__vector unsigned char)vec_cmpeq(word, v34);
355 needs_escape = vec_or(needs_escape,
356 (__vector unsigned char)vec_cmpeq(word, v92));
357 needs_escape = vec_or(needs_escape,
358 (__vector unsigned char)vec_cmplt(word, v32));
359
360 __vector unsigned long long result =
361 (__vector unsigned long long)vec_vbpermq(needs_escape, perm_mask);
362#ifdef __LITTLE_ENDIAN__
363 unsigned int mask = static_cast<unsigned int>(result[1]);
364#else
365 unsigned int mask = static_cast<unsigned int>(result[0]);
366#endif
367 if (mask != 0) {
368 size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
369 return offset + __builtin_ctz(mask);
370 }
371 ptr += 16;
372 remaining -= 16;
373 }
374
375 // Scalar fallback for remaining bytes
376 size_t current = len - remaining;
377 return find_next_json_quotable_character_scalar(view, current);
378}
379#else
380SIMDJSON_CONSTEXPR_LAMBDA simdjson_inline size_t
381find_next_json_quotable_character(const std::string_view view,
382 size_t location) noexcept {
383 return find_next_json_quotable_character_scalar(view, location);
384}
385#endif
386
387SIMDJSON_CONSTEXPR_LAMBDA static std::string_view control_chars[] = {
388 "\\u0000", "\\u0001", "\\u0002", "\\u0003", "\\u0004", "\\u0005", "\\u0006",
389 "\\u0007", "\\b", "\\t", "\\n", "\\u000b", "\\f", "\\r",
390 "\\u000e", "\\u000f", "\\u0010", "\\u0011", "\\u0012", "\\u0013", "\\u0014",
391 "\\u0015", "\\u0016", "\\u0017", "\\u0018", "\\u0019", "\\u001a", "\\u001b",
392 "\\u001c", "\\u001d", "\\u001e", "\\u001f"};
393
394// All Unicode characters may be placed within the quotation marks, except for
395// the characters that MUST be escaped: quotation mark, reverse solidus, and the
396// control characters (U+0000 through U+001F). There are two-character sequence
397// escape representations of some popular characters:
398// \", \\, \b, \f, \n, \r, \t.
399SIMDJSON_CONSTEXPR_LAMBDA simdjson_inline void escape_json_char(char c, char *&out) {
400 if (c == '"') {
401 memcpy(out, "\\\"", 2);
402 out += 2;
403 } else if (c == '\\') {
404 memcpy(out, "\\\\", 2);
405 out += 2;
406 } else {
407 std::string_view v = control_chars[uint8_t(c)];
408 memcpy(out, v.data(), v.size());
409 out += v.size();
410 }
411}
412
413#if SIMDJSON_EXPERIMENTAL_HAS_SSE2 || SIMDJSON_EXPERIMENTAL_HAS_NEON
414#define SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE 1
415#endif
416
417#if SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE
418
419#if SIMDJSON_EXPERIMENTAL_HAS_SSE2
420
421using escape_vector = __m128i;
422// Mask bits that each input byte contributes to escape_bitmask().
423static constexpr unsigned escape_mask_bits = 1;
424
425simdjson_inline escape_vector escape_load16(const uint8_t *p) noexcept {
426 return _mm_loadu_si128(reinterpret_cast<const __m128i *>(p));
427}
428
429simdjson_inline void escape_store16(char *out, escape_vector v) noexcept {
430 _mm_storeu_si128(reinterpret_cast<__m128i *>(out), v);
431}
432
433// Builds a vector whose bytes 0..7 come from a and bytes 8..15 from b.
434simdjson_inline escape_vector escape_load8x2(const uint8_t *a,
435 const uint8_t *b) noexcept {
436 return _mm_unpacklo_epi64(
437 _mm_loadl_epi64(reinterpret_cast<const __m128i *>(a)),
438 _mm_loadl_epi64(reinterpret_cast<const __m128i *>(b)));
439}
440
441// Builds a vector whose bytes 0..3 come from a and bytes 4..7 from b. The
442// upper half is unspecified; callers only look at the low eight bits.
443simdjson_inline escape_vector escape_load4x2(const uint8_t *a,
444 const uint8_t *b) noexcept {
445 int32_t a32, b32;
446 memcpy(&a32, a, 4);
447 memcpy(&b32, b, 4);
448 return _mm_unpacklo_epi32(_mm_cvtsi32_si128(a32), _mm_cvtsi32_si128(b32));
449}
450
451// Sets every bit of byte k when byte k of v is a quotable character ('"',
452// '\\' or a control character).
453simdjson_inline escape_vector escape_flags(escape_vector v) noexcept {
454 const __m128i v34 = _mm_set1_epi8(34); // '"'
455 const __m128i v92 = _mm_set1_epi8(92); // '\\'
456 const __m128i v31 = _mm_set1_epi8(31); // for control char detection
457 __m128i needs_escape = _mm_cmpeq_epi8(v, v34);
458 needs_escape = _mm_or_si128(needs_escape, _mm_cmpeq_epi8(v, v92));
459 return _mm_or_si128(
460 needs_escape, _mm_cmpeq_epi8(_mm_subs_epu8(v, v31), _mm_setzero_si128()));
461}
462
463// True when any byte needs escaping. Kept separate from escape_bitmask
464// because some instruction sets can answer it without leaving the vector
465// register file; on SSE2 the compiler folds the two together.
466simdjson_inline bool escape_any(escape_vector flags) noexcept {
467 return _mm_movemask_epi8(flags) != 0;
468}
469
470// A 16-bit mask with bit k set when byte k needed escaping.
471simdjson_inline uint64_t escape_bitmask(escape_vector flags) noexcept {
472 return uint64_t(uint32_t(_mm_movemask_epi8(flags)));
473}
474
475#else // SIMDJSON_EXPERIMENTAL_HAS_NEON
476
477using escape_vector = uint8x16_t;
478static constexpr unsigned escape_mask_bits = 4;
479
480simdjson_inline escape_vector escape_load16(const uint8_t *p) noexcept {
481 return vld1q_u8(p);
482}
483
484simdjson_inline void escape_store16(char *out, escape_vector v) noexcept {
485 vst1q_u8(reinterpret_cast<uint8_t *>(out), v);
486}
487
488simdjson_inline escape_vector escape_load8x2(const uint8_t *a,
489 const uint8_t *b) noexcept {
490 uint64_t a64, b64;
491 memcpy(&a64, a, 8);
492 memcpy(&b64, b, 8);
493 return vreinterpretq_u8_u64(vsetq_lane_u64(b64, vdupq_n_u64(a64), 1));
494}
495
496simdjson_inline escape_vector escape_load4x2(const uint8_t *a,
497 const uint8_t *b) noexcept {
498 uint32_t a32, b32;
499 memcpy(&a32, a, 4);
500 memcpy(&b32, b, 4);
501 return vreinterpretq_u8_u32(vsetq_lane_u32(b32, vdupq_n_u32(a32), 1));
502}
503
504simdjson_inline escape_vector escape_flags(escape_vector v) noexcept {
505 uint8x16_t needs_escape = vceqq_u8(v, vdupq_n_u8(34)); // '"'
506 needs_escape = vorrq_u8(needs_escape, vceqq_u8(v, vdupq_n_u8(92))); // '\\'
507 return vorrq_u8(needs_escape, vcltq_u8(v, vdupq_n_u8(32)));
508}
509
510simdjson_inline bool escape_any(escape_vector flags) noexcept {
511 return vmaxvq_u32(vreinterpretq_u32_u8(flags)) != 0;
512}
513
514// Four bits per byte rather than one: escape_block and the tail paths scale
515// their shifts by escape_mask_bits to match.
516simdjson_inline uint64_t escape_bitmask(escape_vector flags) noexcept {
517 uint8x8_t narrowed = vshrn_n_u16(vreinterpretq_u16_u8(flags), 4);
518 return vget_lane_u64(vreinterpret_u64_u8(narrowed), 0);
519}
520
521#endif // instruction set selection
522
523// The escape bitmask of a 16-byte block.
524simdjson_inline uint64_t escape_mask(escape_vector v) noexcept {
525 return escape_bitmask(escape_flags(v));
526}
527
528// Copies n bytes with n < 16, using overlapping loads and stores. It never
529// reads more than n bytes from src, nor writes more than n bytes to dst.
530simdjson_inline void copy_lt16(char *dst, const uint8_t *src,
531 size_t n) noexcept {
532 if (n >= 8) {
533 memcpy(dst, src, 8);
534 memcpy(dst + n - 8, src + n - 8, 8);
535 } else if (n >= 4) {
536 memcpy(dst, src, 4);
537 memcpy(dst + n - 4, src + n - 4, 4);
538 } else if (n > 0) {
539 dst[0] = char(src[0]);
540 dst[n >> 1] = char(src[n >> 1]);
541 dst[n - 1] = char(src[n - 1]);
542 }
543}
544
545// Escapes the bytes of src in the range [i, blockend), given that m is the
546// (non-zero) escape mask for that range: the escape_mask_bits-wide lane of m
547// at byte k is non-zero when src[i + k] requires escaping. Returns the updated
548// output pointer.
549simdjson_never_inline char *escape_block(const uint8_t *src, char *out,
550 size_t i, size_t blockend,
551 uint64_t m) noexcept {
552 constexpr uint64_t lane = (uint64_t(1) << escape_mask_bits) - 1;
553
554 size_t pos = i; // first byte not yet copied
555 while (m) {
556 const size_t tz = trailing_zeroes(m);
557 const size_t next = i + tz / escape_mask_bits;
558 // Copy the run of safe bytes that precedes this escape.
559 copy_lt16(out, src + pos, next - pos);
560 out += next - pos;
561 escape_json_char(char(src[next]), out);
562 pos = next + 1;
563 m &= ~(lane << tz);
564 }
565 // Copy whatever follows the last escape.
566 copy_lt16(out, src + pos, blockend - pos);
567 return out + (blockend - pos);
568}
569
570// Writes the escaped version of input to out, returning the number of bytes
571// written.
572simdjson_really_inline size_t write_string_escaped(const std::string_view input, char *out) {
573 const size_t len = input.size();
574 const uint8_t *src = reinterpret_cast<const uint8_t *>(input.data());
575 const char *const initout = out;
576
577 size_t i = 0;
578#if SIMDJSON_EXPERIMENTAL_HAS_SSE2 && defined(__AVX2__)
579 while (i + 32 <= len) {
580 const __m256i word = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(src + i));
581 const __m256i flags = _mm256_or_si256(
582 _mm256_or_si256(_mm256_cmpeq_epi8(word, _mm256_set1_epi8(34)), // '"'
583 _mm256_cmpeq_epi8(word, _mm256_set1_epi8(92))), // '\\'
584 _mm256_cmpeq_epi8(_mm256_subs_epu8(word, _mm256_set1_epi8(31)),
585 _mm256_setzero_si256())); // control
586 const uint32_t mask = uint32_t(_mm256_movemask_epi8(flags));
587 if (simdjson_likely(mask == 0)) {
588 _mm256_storeu_si256(reinterpret_cast<__m256i *>(out), word);
589 out += 32;
590 } else {
591 for (size_t half = 0; half < 32; half += 16) {
592 const uint64_t m = (mask >> half) & 0xFFFF;
593 if (m == 0) {
594 escape_store16(out, escape_load16(src + i + half));
595 out += 16;
596 } else {
597 out = escape_block(src, out, i + half, i + half + 16, m);
598 }
599 }
600 }
601 i += 32;
602 }
603#endif
604 while (i + 16 <= len) {
605 escape_vector word = escape_load16(src + i);
606 escape_vector flags = escape_flags(word);
607 if (simdjson_likely(!escape_any(flags))) {
608 escape_store16(out, word);
609 out += 16;
610 } else {
611 out = escape_block(src, out, i, i + 16, escape_bitmask(flags));
612 }
613 i += 16;
614 }
615 if (i < len) {
616 const size_t rem = len - i;
617 uint64_t m;
618 if (len >= 16) {
619 // The last 16 bytes of the input are in bounds. Bit k of that block's
620 // mask belongs to input position len - 16 + k, so shift it down to align
621 // bit 0 with position i.
622 m = escape_mask(escape_load16(src + len - 16)) >>
623 (escape_mask_bits * (16 - rem));
624 } else if (len >= 8) {
625 // Here i == 0 and rem == len. Two overlapping 8-byte loads cover
626 // [0, 8) and [len - 8, len), which is the whole input since len < 16.
627 uint64_t mm = escape_mask(escape_load8x2(src, src + len - 8));
628 constexpr uint64_t low8 = (uint64_t(1) << (escape_mask_bits * 8)) - 1;
629 m = (mm & low8) |
630 ((mm >> (escape_mask_bits * 8)) << (escape_mask_bits * (len - 8)));
631 } else if (len >= 4) {
632 // Same idea with two overlapping 4-byte loads.
633 uint64_t mm = escape_mask(escape_load4x2(src, src + len - 4));
634 constexpr uint64_t low4 = (uint64_t(1) << (escape_mask_bits * 4)) - 1;
635 m = (mm & low4) | (((mm >> (escape_mask_bits * 4)) & low4)
636 << (escape_mask_bits * (len - 4)));
637 } else {
638 // Fewer than 4 bytes: at most three table lookups, no need for SIMD.
639 for (size_t k = 0; k < len; k++) {
640 uint8_t c = src[k];
641 if (json_quotable_character[c]) {
642 escape_json_char(char(c), out);
643 } else {
644 *out++ = char(c);
645 }
646 }
647 return size_t(out - initout);
648 }
649 if (m == 0) {
650 copy_lt16(out, src + i, rem);
651 out += rem;
652 } else {
653 out = escape_block(src, out, i, len, m);
654 }
655 }
656 return size_t(out - initout);
657}
658
659#else // SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE
660
661// Writes the escaped version of input to out, returning the number of bytes
662// written. Uses SIMD position finding to locate quotable characters efficiently.
663inline size_t write_string_escaped(const std::string_view input, char *out) {
664 size_t mysize = input.size();
665
666 // Use SIMD position finder directly - it returns mysize if no escape needed
667 size_t location = find_next_json_quotable_character(input, 0);
668 if (location == mysize) {
669 // Fast path: no escaping needed
670 memcpy(out, input.data(), input.size());
671 return input.size();
672 }
673
674 const char *const initout = out;
675 memcpy(out, input.data(), location);
676 out += location;
677 escape_json_char(input[location], out);
678 location += 1;
679 while (location < mysize) {
680 size_t newlocation = find_next_json_quotable_character(input, location);
681 memcpy(out, input.data() + location, newlocation - location);
682 out += newlocation - location;
683 location = newlocation;
684 if (location == mysize) {
685 break;
686 }
687 escape_json_char(input[location], out);
688 location += 1;
689 }
690 return size_t(out - initout);
691}
692
693#endif // SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE
694#undef SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE
695
696
697simdjson_inline string_builder::string_builder(size_t initial_capacity)
698 : buffer(new(std::nothrow) char[initial_capacity]), position(0),
699 capacity(buffer.get() != nullptr ? initial_capacity : 0),
700 is_valid(buffer.get() != nullptr) {}
701
702simdjson_inline bool string_builder::capacity_check(size_t upcoming_bytes) {
703 // We use the convention that when is_valid is false, then the capacity and
704 // the position are 0.
705 // Most of the time, this function will return true.
706 if (simdjson_likely(upcoming_bytes <= capacity - position)) {
707 return true;
708 }
709 // check for overflow, most of the time there is no overflow
710 if (simdjson_unlikely(position + upcoming_bytes < position)) {
711 return false;
712 }
713 // We will rarely get here.
714 grow_buffer((std::max)(capacity * 2, position + upcoming_bytes));
715 // If the buffer allocation failed, we set is_valid to false.
716 return is_valid;
717}
718
719inline void string_builder::grow_buffer(size_t desired_capacity) {
720 if (!is_valid) {
721 return;
722 }
723 std::unique_ptr<char[]> new_buffer(new (std::nothrow) char[desired_capacity]);
724 if (new_buffer.get() == nullptr) {
725 set_valid(false);
726 return;
727 }
728 std::memcpy(new_buffer.get(), buffer.get(), position);
729 buffer.swap(new_buffer);
730 capacity = desired_capacity;
731}
732
733simdjson_inline void string_builder::set_valid(bool valid) noexcept {
734 if (!valid) {
735 is_valid = false;
736 capacity = 0;
737 position = 0;
738 buffer.reset();
739 } else {
740 is_valid = true;
741 }
742}
743
744simdjson_inline size_t string_builder::size() const noexcept {
745 return position;
746}
747
748simdjson_inline void string_builder::append(char c) noexcept {
749 if (capacity_check(1)) {
750 buffer.get()[position++] = c;
751 }
752}
753
754simdjson_inline void string_builder::append_null() noexcept {
755 constexpr char null_literal[] = "null";
756 constexpr size_t null_len = sizeof(null_literal) - 1;
757 if (capacity_check(null_len)) {
758 std::memcpy(buffer.get() + position, null_literal, null_len);
759 position += null_len;
760 }
761}
762
763simdjson_inline void string_builder::clear() noexcept {
764 position = 0;
765 // if it was invalid, we should try to repair it
766 if (!is_valid) {
767 capacity = 0;
768 buffer.reset();
769 is_valid = true;
770 }
771}
772
773namespace internal {
774
775// Integer to decimal: James Edward Anhalt III's algorithm
776static const char jeaiii_dd[201] =
777 "00010203040506070809101112131415161718192021222324252627282930313233343536373839"
778 "40414243444546474849505152535455565758596061626364656667686970717273747576777879"
779 "8081828384858687888990919293949596979899";
780static const char jeaiii_fd[201] =
781 "0\0" "1\0" "2\0" "3\0" "4\0" "5\0" "6\0" "7\0" "8\0" "9\0"
782 "10111213141516171819202122232425262728293031323334353637383940414243444546474849"
783 "50515253545556575859606162636465666768697071727374757677787980818283848586878889"
784 "90919293949596979899";
785
786simdjson_really_inline void jeaiii_write_dd(char *p, uint64_t k) noexcept {
787 std::memcpy(p, &jeaiii_dd[2 * k], 2);
788}
789simdjson_really_inline void jeaiii_write_fd(char *p, uint64_t k) noexcept {
790 std::memcpy(p, &jeaiii_fd[2 * k], 2);
791}
792
793// Caller guarantees n < 10^8. Writes 1 to 8 digits.
794simdjson_really_inline char *jeaiii_lt1e8(char *b, uint32_t n) noexcept {
795 constexpr uint64_t mask24 = (uint64_t(1) << 24) - 1;
796 constexpr uint64_t mask32 = (uint64_t(1) << 32) - 1;
797 if (n < 100) {
798 jeaiii_write_fd(b, n);
799 return n < 10 ? b + 1 : b + 2;
800 }
801 if (n < 1000000) {
802 if (n < 10000) {
803 const uint32_t f0 = uint32_t(10 * (1 << 24) / 1e3 + 1) * n;
804 jeaiii_write_fd(b, f0 >> 24);
805 b -= n < 1000;
806 const uint32_t f2 = uint32_t(f0 & mask24) * 100;
807 jeaiii_write_dd(b + 2, f2 >> 24);
808 return b + 4;
809 }
810 const uint64_t f0 = uint64_t(10 * (1ull << 32) / 1e5 + 1) * n;
811 jeaiii_write_fd(b, f0 >> 32);
812 b -= n < 100000;
813 const uint64_t f2 = (f0 & mask32) * 100;
814 jeaiii_write_dd(b + 2, f2 >> 32);
815 const uint64_t f4 = (f2 & mask32) * 100;
816 jeaiii_write_dd(b + 4, f4 >> 32);
817 return b + 6;
818 }
819 const uint64_t f0 = uint64_t(10 * (1ull << 48) / 1e7 + 1) * n >> 16;
820 jeaiii_write_fd(b, f0 >> 32);
821 b -= n < 10000000;
822 const uint64_t f2 = (f0 & mask32) * 100;
823 jeaiii_write_dd(b + 2, f2 >> 32);
824 const uint64_t f4 = (f2 & mask32) * 100;
825 jeaiii_write_dd(b + 4, f4 >> 32);
826 const uint64_t f6 = (f4 & mask32) * 100;
827 jeaiii_write_dd(b + 6, f6 >> 32);
828 return b + 8;
829}
830
831// Caller guarantees z < 10^8. Always writes exactly 8 digits.
832simdjson_really_inline char *jeaiii_8_digits(char *b, uint32_t z) noexcept {
833 constexpr uint64_t mask32 = (uint64_t(1) << 32) - 1;
834 const uint64_t f0 = (uint64_t((1ull << 48) / 1e6 + 1) * z >> 16) + 1;
835 jeaiii_write_dd(b, f0 >> 32);
836 const uint64_t f2 = (f0 & mask32) * 100;
837 jeaiii_write_dd(b + 2, f2 >> 32);
838 const uint64_t f4 = (f2 & mask32) * 100;
839 jeaiii_write_dd(b + 4, f4 >> 32);
840 const uint64_t f6 = (f4 & mask32) * 100;
841 jeaiii_write_dd(b + 6, f6 >> 32);
842 return b + 8;
843}
844
845// Caller guarantees 10^8 <= n < 2^32. Writes 9 or 10 digits.
846simdjson_really_inline char *jeaiii_9_or_10(char *b, uint64_t n) noexcept {
847 constexpr uint64_t mask57 = (uint64_t(1) << 57) - 1;
848 const uint64_t f0 = uint64_t(10 * (1ull << 57) / 1e9 + 1) * n;
849 jeaiii_write_fd(b, f0 >> 57);
850 b -= n < 1000000000;
851 const uint64_t f2 = (f0 & mask57) * 100;
852 jeaiii_write_dd(b + 2, f2 >> 57);
853 const uint64_t f4 = (f2 & mask57) * 100;
854 jeaiii_write_dd(b + 4, f4 >> 57);
855 const uint64_t f6 = (f4 & mask57) * 100;
856 jeaiii_write_dd(b + 6, f6 >> 57);
857 const uint64_t f8 = (f6 & mask57) * 100;
858 jeaiii_write_dd(b + 8, f8 >> 57);
859 return b + 10;
860}
861
862simdjson_really_inline char *write_uint_jeaiii(char *b, uint64_t n) noexcept {
863 if (n < 100000000) {
864 return jeaiii_lt1e8(b, uint32_t(n));
865 }
866 if (n < (uint64_t(1) << 32)) {
867 return jeaiii_9_or_10(b, n);
868 }
869 // At least 10 digits: the low 8 digits, and 2 to 12 digits above them.
870 const uint32_t z = uint32_t(n % 100000000);
871 uint64_t u = n / 100000000;
872 if (u < 100000000) {
873 // u has 2 to 8 digits (if u < 10, n would be below 2^32).
874 b = jeaiii_lt1e8(b, uint32_t(u));
875 } else if (u < (uint64_t(1) << 32)) {
876 b = jeaiii_9_or_10(b, u);
877 } else {
878 // u has 11 or 12 digits: split off 8 more.
879 const uint32_t y = uint32_t(u % 100000000);
880 u /= 100000000;
881 b = jeaiii_lt1e8(b, uint32_t(u)); // 3 or 4 digits
882 b = jeaiii_8_digits(b, y);
883 }
884 return jeaiii_8_digits(b, z);
885}
886
887// Writes v at p, which must have to_chars_buffer_size bytes available, and
888// returns the end of what was written.
889simdjson_inline char *write_double(char *p, double v) noexcept {
890#if SIMDJSON_ENABLE_NAN_INF
891 if (simdjson_unlikely(!std::isfinite(v))) {
892 if (std::isnan(v)) {
893 std::memcpy(p, "NaN", 3);
894 return p + 3;
895 }
896 if (v < 0) {
897 *p++ = '-';
898 }
899 std::memcpy(p, "Infinity", 8);
900 return p + 8;
901 }
902#endif
903 return simdjson::internal::to_chars(p, nullptr, v);
904}
905} // namespace internal
906
907template <typename number_type, typename>
908simdjson_inline void string_builder::append(number_type v) noexcept {
909 static_assert(std::is_same<number_type, bool>::value ||
910 std::is_integral<number_type>::value ||
911 std::is_floating_point<number_type>::value,
912 "Unsupported number type");
913 // If C++17 is available, we can 'if constexpr' here.
914 SIMDJSON_IF_CONSTEXPR(std::is_same<number_type, bool>::value) {
915 if (v) {
916 constexpr char true_literal[] = "true";
917 constexpr size_t true_len = sizeof(true_literal) - 1;
918 if (capacity_check(true_len)) {
919 std::memcpy(buffer.get() + position, true_literal, true_len);
920 position += true_len;
921 }
922 } else {
923 constexpr char false_literal[] = "false";
924 constexpr size_t false_len = sizeof(false_literal) - 1;
925 if (capacity_check(false_len)) {
926 std::memcpy(buffer.get() + position, false_literal, false_len);
927 position += false_len;
928 }
929 }
930 }
931 else SIMDJSON_IF_CONSTEXPR(std::is_unsigned<number_type>::value) {
932 constexpr size_t max_number_size = 20;
933 if (capacity_check(max_number_size)) {
934 using unsigned_type = typename std::make_unsigned<number_type>::type;
935 char* end = internal::write_uint_jeaiii(
936 buffer.get() + position,
937 static_cast<uint64_t>(static_cast<unsigned_type>(v)));
938 position = end - buffer.get();
939 }
940 }
941 else SIMDJSON_IF_CONSTEXPR(std::is_integral<number_type>::value) {
942 // 19 digits (max abs value of int64_t) + optional minus sign.
943 constexpr size_t max_number_size = 20;
944 if (capacity_check(max_number_size)) {
945 using unsigned_type = typename std::make_unsigned<number_type>::type;
946 bool negative = v < 0;
947 // 0 - pv (rather than -pv) avoids an MSVC unary-minus warning.
948 unsigned_type pv = negative
949 ? unsigned_type(0) - static_cast<unsigned_type>(v)
950 : static_cast<unsigned_type>(v);
951 // Branchless: always write '-', advance only if negative.
952 buffer.get()[position] = '-';
953 position += negative;
954 char* end = internal::write_uint_jeaiii(
955 buffer.get() + position, static_cast<uint64_t>(pv));
956 position = end - buffer.get();
957 }
958 }
959 else SIMDJSON_IF_CONSTEXPR(std::is_floating_point<number_type>::value) {
960 // Must reserve to_chars_buffer_size (40): only ~24 chars are emitted,
961 // but to_chars over-writes with fixed-size 16/17-byte copies so the
962 // compiler can inline mem* (see simdjson::internal::to_chars_buffer_size).
963 constexpr size_t max_number_size = simdjson::internal::to_chars_buffer_size;
964 if (capacity_check(max_number_size)) {
965#if SIMDJSON_ENABLE_NAN_INF
966 // Check if the input might be NaN or infinity
967 if (simdjson_unlikely(!std::isfinite(v))) {
968 if (std::isnan(v)) {
969 constexpr char nan_literal[] = "NaN";
970 constexpr size_t nan_len = sizeof(nan_literal) - 1;
971
972 std::memcpy(buffer.get() + position, nan_literal, nan_len);
973 position += nan_len;
974 } else {
975 constexpr char inf_literal[] = "Infinity";
976 constexpr size_t inf_len = sizeof(inf_literal) - 1;
977 if (v < 0) {
978 buffer.get()[position] = '-';
979 ++position;
980 }
981 std::memcpy(buffer.get() + position, inf_literal, inf_len);
982 position += inf_len;
983 }
984 return;
985 }
986#endif
987
988 // We could specialize for float.
989 char *end = simdjson::internal::to_chars(buffer.get() + position, nullptr,
990 double(v));
991 position = end - buffer.get();
992 }
993 }
994}
995
996simdjson_inline void
997string_builder::escape_and_append(std::string_view input) noexcept {
998 // escaping might turn a control character into \x00xx so 6 characters.
999 // Guard against size_t overflow in the multiplication below.
1000 if (input.size() > (std::numeric_limits<size_t>::max)() / 6) {
1001 set_valid(false);
1002 return;
1003 }
1004 if (capacity_check(6 * input.size())) {
1005 position += write_string_escaped(input, buffer.get() + position);
1006 }
1007}
1008
1009simdjson_inline void
1010string_builder::escape_and_append_with_quotes(std::string_view input) noexcept {
1011 // escaping might turn a control character into \x00xx so 6 characters.
1012 // Guard against size_t overflow in the arithmetic below.
1013 if (input.size() > ((std::numeric_limits<size_t>::max)() - 2) / 6) {
1014 set_valid(false);
1015 return;
1016 }
1017 if (capacity_check(2 + 6 * input.size())) {
1018 buffer.get()[position++] = '"';
1019 position += write_string_escaped(input, buffer.get() + position);
1020 buffer.get()[position++] = '"';
1021 }
1022}
1023
1024simdjson_inline void
1025string_builder::escape_and_append_with_quotes(char input) noexcept {
1026 // escaping might turn a control character into \x00xx so 6 characters.
1027 if (capacity_check(2 + 6 * 1)) {
1028 buffer.get()[position++] = '"';
1029 std::string_view cinput(&input, 1);
1030 position += write_string_escaped(cinput, buffer.get() + position);
1031 buffer.get()[position++] = '"';
1032 }
1033}
1034
1035simdjson_inline void
1036string_builder::escape_and_append_with_quotes(const char *input) noexcept {
1037 std::string_view cinput(input);
1038 escape_and_append_with_quotes(cinput);
1039}
1040#if SIMDJSON_SUPPORTS_CONCEPTS
1041template <constevalutil::fixed_string key>
1042simdjson_inline void string_builder::escape_and_append_with_quotes() noexcept {
1043 escape_and_append_with_quotes(constevalutil::string_constant<key>::value);
1044}
1045#endif
1046
1047simdjson_inline void string_builder::append_raw(const char *c) noexcept {
1048 // char_traits::length is constexpr; lets the compiler fold the length
1049 // when called with a pointer to a compile-time-constant string.
1050 size_t len = std::char_traits<char>::length(c);
1051 append_raw(c, len);
1052}
1053
1054simdjson_inline void
1055string_builder::append_raw(std::string_view input) noexcept {
1056 if (capacity_check(input.size())) {
1057 std::memcpy(buffer.get() + position, input.data(), input.size());
1058 position += input.size();
1059 }
1060}
1061
1062simdjson_inline void string_builder::append_raw(const char *str,
1063 size_t len) noexcept {
1064 if (capacity_check(len)) {
1065 std::memcpy(buffer.get() + position, str, len);
1066 position += len;
1067 }
1068}
1069
1070template <size_t N>
1071simdjson_inline void string_builder::append_raw_n(const char *str) noexcept {
1072 if (capacity_check(N)) {
1073 std::memcpy(buffer.get() + position, str, N);
1074 position += N;
1075 }
1076}
1077#if SIMDJSON_SUPPORTS_CONCEPTS
1078// Support for optional types (std::optional, etc.)
1079template <concepts::optional_type T>
1080 requires(!require_custom_serialization<T>)
1081simdjson_inline void string_builder::append(const T &opt) {
1082 if (opt) {
1083 append(*opt);
1084 } else {
1085 append_null();
1086 }
1087}
1088
1089template <typename T>
1090 requires(require_custom_serialization<T>)
1091simdjson_inline void string_builder::append(T &&val) {
1092 serialize(*this, std::forward<T>(val));
1093}
1094
1095template <typename T>
1096 requires(std::is_convertible<T, std::string_view>::value ||
1097 std::is_same<T, const char *>::value)
1098simdjson_inline void string_builder::append(const T &value) {
1099 escape_and_append_with_quotes(value);
1100}
1101#endif
1102
1103#if SIMDJSON_SUPPORTS_RANGES && SIMDJSON_SUPPORTS_CONCEPTS
1104// Support for range-based appending (std::ranges::view, etc.)
1105template <std::ranges::range R>
1106 requires(!std::is_convertible<R, std::string_view>::value && !concepts::optional_type<R> && !require_custom_serialization<R>)
1107simdjson_inline void string_builder::append(const R &range) noexcept {
1108 auto it = std::ranges::begin(range);
1109 auto end = std::ranges::end(range);
1110 if constexpr (concepts::is_pair<std::ranges::range_value_t<R>>) {
1111 start_object();
1112
1113 if (it == end) {
1114 end_object();
1115 return; // Handle empty range
1116 }
1117 // Append first item without leading comma
1118 append_key_value(it->first, it->second);
1119 ++it;
1120
1121 // Append remaining items with preceding commas
1122 for (; it != end; ++it) {
1123 append_comma();
1124 append_key_value(it->first, it->second);
1125 }
1126 end_object();
1127 } else {
1128 start_array();
1129 if (it == end) {
1130 end_array();
1131 return; // Handle empty range
1132 }
1133
1134 // Append first item without leading comma
1135 append(*it);
1136 ++it;
1137
1138 // Append remaining items with preceding commas
1139 for (; it != end; ++it) {
1140 append_comma();
1141 append(*it);
1142 }
1143 end_array();
1144 }
1145}
1146
1147#endif
1148
1149#if SIMDJSON_EXCEPTIONS
1150simdjson_inline string_builder::operator std::string() const noexcept(false) {
1151 return std::string(operator std::string_view());
1152}
1153
1154simdjson_inline string_builder::operator std::string_view() const
1155 noexcept(false) simdjson_lifetime_bound {
1156 return view();
1157}
1158#endif
1159
1161string_builder::view() const noexcept {
1162 if (!is_valid) {
1164 }
1165 return std::string_view(buffer.get(), position);
1166}
1167
1168simdjson_inline simdjson_result<const char *> string_builder::c_str() noexcept {
1169 if (capacity_check(1)) {
1170 buffer.get()[position] = '\0';
1171 return buffer.get();
1172 }
1174}
1175
1176simdjson_inline bool string_builder::validate_unicode() const noexcept {
1177 return simdjson::validate_utf8(buffer.get(), position);
1178}
1179
1180simdjson_inline void string_builder::start_object() noexcept {
1181 if (capacity_check(1)) {
1182 buffer.get()[position++] = '{';
1183 }
1184}
1185
1186simdjson_inline void string_builder::end_object() noexcept {
1187 if (capacity_check(1)) {
1188 buffer.get()[position++] = '}';
1189 }
1190}
1191
1192simdjson_inline void string_builder::start_array() noexcept {
1193 if (capacity_check(1)) {
1194 buffer.get()[position++] = '[';
1195 }
1196}
1197
1198simdjson_inline void string_builder::end_array() noexcept {
1199 if (capacity_check(1)) {
1200 buffer.get()[position++] = ']';
1201 }
1202}
1203
1204simdjson_inline void string_builder::append_comma() noexcept {
1205 if (capacity_check(1)) {
1206 buffer.get()[position++] = ',';
1207 }
1208}
1209
1210simdjson_inline void string_builder::append_colon() noexcept {
1211 if (capacity_check(1)) {
1212 buffer.get()[position++] = ':';
1213 }
1214}
1215
1216template <typename key_type, typename value_type>
1217simdjson_inline void
1218string_builder::append_key_value(key_type key, value_type value) noexcept {
1219 static_assert(std::is_same<key_type, const char *>::value ||
1220 std::is_convertible<key_type, std::string_view>::value,
1221 "Unsupported key type");
1222 escape_and_append_with_quotes(key);
1223 append_colon();
1224 SIMDJSON_IF_CONSTEXPR(std::is_same<value_type, std::nullptr_t>::value) {
1225 append_null();
1226 }
1227 else SIMDJSON_IF_CONSTEXPR(std::is_same<value_type, char>::value) {
1228 escape_and_append_with_quotes(value);
1229 }
1230 else SIMDJSON_IF_CONSTEXPR(
1231 std::is_convertible<value_type, std::string_view>::value) {
1232 escape_and_append_with_quotes(value);
1233 }
1234 else SIMDJSON_IF_CONSTEXPR(std::is_same<value_type, const char *>::value) {
1235 escape_and_append_with_quotes(value);
1236 }
1237 else {
1238 append(value);
1239 }
1240}
1241
1242#if SIMDJSON_SUPPORTS_CONCEPTS
1243template <constevalutil::fixed_string key, typename value_type>
1244simdjson_inline void
1245string_builder::append_key_value(value_type value) noexcept {
1246 escape_and_append_with_quotes<key>();
1247 append_colon();
1248 SIMDJSON_IF_CONSTEXPR(std::is_same<value_type, std::nullptr_t>::value) {
1249 append_null();
1250 }
1251 else SIMDJSON_IF_CONSTEXPR(std::is_same<value_type, char>::value) {
1252 escape_and_append_with_quotes(value);
1253 }
1254 else SIMDJSON_IF_CONSTEXPR(
1255 std::is_convertible<value_type, std::string_view>::value) {
1256 escape_and_append_with_quotes(value);
1257 }
1258 else SIMDJSON_IF_CONSTEXPR(std::is_same<value_type, const char *>::value) {
1259 escape_and_append_with_quotes(value);
1260 }
1261 else {
1262 append(value);
1263 }
1264}
1265#endif
1266
1267} // namespace builder
1268} // namespace SIMDJSON_IMPLEMENTATION
1269} // namespace simdjson
1270
1271#endif // SIMDJSON_GENERIC_STRING_BUILDER_INL_H
The top level simdjson namespace, containing everything the library provides.
Definition base.h:8
simdjson_warn_unused bool validate_utf8(const char *buf, size_t len) noexcept
Validate the UTF-8 string.
@ OUT_OF_CAPACITY
The capacity was exceeded, we cannot allocate enough memory.
Definition error.h:52
The result of a simdjson operation that could fail.
Definition error.h:281
simdjson_warn_unused simdjson_inline error_code get(T &value) &&noexcept
Move the value to the provided variable.
Definition error-inl.h:163