GCC Code Coverage Report


Directory: libs/json/include/boost/json/
File: impl/serializer.ipp
Date: 2026-01-06 06:25:57
Exec Total Coverage
Lines: 261 261 100.0%
Functions: 36 36 100.0%
Branches: 107 113 94.7%

Line Branch Exec Source
1 //
2 // Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
3 //
4 // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 //
7 // Official repository: https://github.com/boostorg/json
8 //
9
10 #ifndef BOOST_JSON_IMPL_SERIALIZER_IPP
11 #define BOOST_JSON_IMPL_SERIALIZER_IPP
12
13 #include <boost/core/detail/static_assert.hpp>
14 #include <boost/json/serializer.hpp>
15 #include <boost/json/detail/format.hpp>
16 #include <boost/json/detail/sse2.hpp>
17
18 #ifdef _MSC_VER
19 #pragma warning(push)
20 #pragma warning(disable: 4127) // conditional expression is constant
21 #endif
22
23 namespace boost {
24 namespace json {
25 namespace detail {
26
27 struct int64_formatter
28 {
29 std::int64_t i;
30
31 std::size_t
32 3188 operator()(char* dst) const noexcept
33 {
34 3188 return format_int64(dst, i);
35 }
36 };
37
38 struct uint64_formatter
39 {
40 std::uint64_t u;
41
42 std::size_t
43 425 operator()(char* dst) const noexcept
44 {
45 425 return format_uint64(dst, u);
46 }
47 };
48
49 struct double_formatter
50 {
51 double d;
52 bool allow_infinity_and_nan;
53
54 std::size_t
55 477 operator()(char* dst) const noexcept
56 {
57 477 return format_double(dst, d, allow_infinity_and_nan);
58 }
59 };
60
61 21245 writer::
62 writer(
63 storage_ptr sp,
64 unsigned char* buf,
65 std::size_t buf_size,
66 21245 serialize_options const& opts) noexcept
67 21245 : st_(
68 21245 std::move(sp),
69 buf,
70 buf_size)
71 21245 , opts_(opts)
72 {
73 // ensure room for \uXXXX escape plus one
74 BOOST_CORE_STATIC_ASSERT( sizeof(buf_) >= 7 );
75 21245 }
76
77 bool
78 BOOST_FORCEINLINE
79 write_buffer(writer& w, stream& ss0)
80 {
81 1444 local_stream ss(ss0);
82 2578 auto const n = ss.remain();
83
8/8
✓ Branch 1 taken 178 times.
✓ Branch 2 taken 9 times.
✓ Branch 4 taken 103 times.
✓ Branch 5 taken 227 times.
✓ Branch 7 taken 472 times.
✓ Branch 8 taken 145 times.
✓ Branch 10 taken 581 times.
✓ Branch 11 taken 863 times.
2578 if( n < w.cs0_.remain() )
84 {
85 1334 ss.append(w.cs0_.data(), n);
86 1334 w.cs0_.skip(n);
87
4/4
✓ Branch 1 taken 178 times.
✓ Branch 4 taken 103 times.
✓ Branch 7 taken 472 times.
✓ Branch 10 taken 581 times.
1334 return w.suspend(writer::state::lit);
88 }
89 1244 ss.append( w.cs0_.data(), w.cs0_.remain() );
90 1244 return true;
91 2578 }
92
93 template< class F >
94 bool
95 8180 write_buffer(writer& w, stream& ss0, F f)
96 {
97
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 4090 times.
8180 BOOST_ASSERT( w.st_.empty() );
98
99 8180 local_stream ss(ss0);
100
2/2
✓ Branch 1 taken 2956 times.
✓ Branch 2 taken 1134 times.
8180 if(BOOST_JSON_LIKELY( ss.remain() >= detail::max_number_chars ))
101 {
102 5912 ss.advance( f(ss.data()) );
103 5912 return true;
104 }
105
106 2268 w.cs0_ = { w.buf_, f(w.buf_) };
107 2268 return write_buffer(w, ss);
108 8180 }
109
110 template<literals Lit>
111 bool
112 9450 write_literal(writer& w, stream& ss)
113 {
114 9450 constexpr std::size_t index = literal_index(Lit);
115 9450 constexpr char const* literal = literal_strings[index];
116 9450 constexpr std::size_t sz = literal_sizes[index];
117
118 9450 std::size_t const n = ss.remain();
119
2/2
✓ Branch 0 taken 4613 times.
✓ Branch 1 taken 112 times.
9450 if(BOOST_JSON_LIKELY( n >= sz ))
120 {
121 9226 ss.append( literal, sz );
122 9226 return true;
123 }
124
125 224 ss.append(literal, n);
126
127 224 w.cs0_ = {literal + n, sz - n};
128 224 return w.suspend(writer::state::lit);
129 }
130
131 bool
132 197 write_true(writer& w, stream& ss)
133 {
134 197 return write_literal<literals::true_>(w, ss);
135 }
136
137 bool
138 176 write_false(writer& w, stream& ss)
139 {
140 176 return write_literal<literals::false_>(w, ss);
141 }
142
143 bool
144 4352 write_null(writer& w, stream& ss)
145 {
146 4352 return write_literal<literals::null>(w, ss);
147 }
148
149 bool
150 3188 write_int64(writer& w, stream& ss0, std::int64_t i)
151 {
152 3188 return write_buffer( w, ss0, int64_formatter{i} );
153 }
154
155 bool
156 425 write_uint64(writer& w, stream& ss0, std::uint64_t u)
157 {
158 425 return write_buffer( w, ss0, uint64_formatter{u} );
159 }
160
161 bool
162 477 write_double(writer& w, stream& ss0, double d)
163 {
164 954 return write_buffer(
165 477 w, ss0, double_formatter{d, w.opts_.allow_infinity_and_nan} );
166 }
167
168 bool
169 1444 resume_buffer(writer& w, stream& ss0)
170 {
171
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1444 times.
1444 BOOST_ASSERT( !w.st_.empty() );
172 writer::state st;
173
1/1
✓ Branch 1 taken 1444 times.
1444 w.st_.pop(st);
174
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1444 times.
1444 BOOST_ASSERT(st == writer::state::lit);
175
176 2888 return write_buffer(w, ss0);
177 }
178
179 template<bool StackEmpty>
180 bool
181 88570 do_write_string(writer& w, stream& ss0)
182 {
183 88570 local_stream ss(ss0);
184 88570 local_const_stream cs(w.cs0_);
185
1/2
✓ Branch 1 taken 9812 times.
✗ Branch 2 not taken.
19624 if(! StackEmpty && ! w.st_.empty())
186 {
187 writer::state st;
188
1/1
✓ Branch 1 taken 9812 times.
19624 w.st_.pop(st);
189
9/9
✓ Branch 0 taken 170 times.
✓ Branch 1 taken 268 times.
✓ Branch 2 taken 9082 times.
✓ Branch 3 taken 52 times.
✓ Branch 4 taken 48 times.
✓ Branch 5 taken 48 times.
✓ Branch 6 taken 48 times.
✓ Branch 7 taken 48 times.
✓ Branch 8 taken 48 times.
19624 switch(st)
190 {
191 340 default:
192 340 case writer::state::str1: goto do_str1;
193 536 case writer::state::str2: goto do_str2;
194 18164 case writer::state::str3: goto do_str3;
195 104 case writer::state::esc1: goto do_esc1;
196 96 case writer::state::utf1: goto do_utf1;
197 96 case writer::state::utf2: goto do_utf2;
198 96 case writer::state::utf3: goto do_utf3;
199 96 case writer::state::utf4: goto do_utf4;
200 96 case writer::state::utf5: goto do_utf5;
201 }
202 }
203 static constexpr char hex[] = "0123456789abcdef";
204 static constexpr char esc[] =
205 "uuuuuuuubtnufruuuuuuuuuuuuuuuuuu"
206 "\0\0\"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
207 "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\\\0\0\0"
208 "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
209 "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
210 "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
211 "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
212 "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
213
214 // opening quote
215 68946 do_str1:
216
2/2
✓ Branch 1 taken 34473 times.
✓ Branch 2 taken 170 times.
69286 if(BOOST_JSON_LIKELY(ss))
217 68946 ss.append('\x22'); // '"'
218 else
219
1/1
✓ Branch 1 taken 170 times.
340 return w.suspend(writer::state::str1);
220
221 // fast loop,
222 // copy unescaped
223 69482 do_str2:
224
2/2
✓ Branch 1 taken 34485 times.
✓ Branch 2 taken 256 times.
69482 if(BOOST_JSON_LIKELY(ss))
225 {
226 68970 std::size_t n = cs.remain();
227
2/2
✓ Branch 0 taken 34441 times.
✓ Branch 1 taken 44 times.
68970 if(BOOST_JSON_LIKELY(n > 0))
228 {
229
2/2
✓ Branch 1 taken 25766 times.
✓ Branch 2 taken 8675 times.
68882 if(ss.remain() > n)
230 51532 n = detail::count_unescaped(
231 cs.data(), n);
232 else
233 17350 n = detail::count_unescaped(
234 cs.data(), ss.remain());
235
2/2
✓ Branch 0 taken 25225 times.
✓ Branch 1 taken 9216 times.
68882 if(n > 0)
236 {
237 50450 ss.append(cs.data(), n);
238 50450 cs.skip(n);
239
2/2
✓ Branch 1 taken 25194 times.
✓ Branch 2 taken 31 times.
50450 if(! ss)
240
1/1
✓ Branch 1 taken 12 times.
24 return w.suspend(writer::state::str2);
241 }
242 }
243 else
244 {
245 88 ss.append('\x22'); // '"'
246 88 return true;
247 }
248 }
249 else
250 {
251
1/1
✓ Branch 1 taken 256 times.
512 return w.suspend(writer::state::str2);
252 }
253
254 // slow loop,
255 // handle escapes
256 87414 do_str3:
257
2/2
✓ Branch 1 taken 31451934 times.
✓ Branch 2 taken 9082 times.
62922032 while(BOOST_JSON_LIKELY(ss))
258 {
259
2/2
✓ Branch 1 taken 31417505 times.
✓ Branch 2 taken 34429 times.
62903868 if(BOOST_JSON_LIKELY(cs))
260 {
261 62835010 auto const ch = *cs;
262 62835010 auto const c = esc[static_cast<
263 unsigned char>(ch)];
264 62835010 ++cs;
265
2/2
✓ Branch 0 taken 31416777 times.
✓ Branch 1 taken 728 times.
62835010 if(! c)
266 {
267 62833554 ss.append(ch);
268 }
269
2/2
✓ Branch 0 taken 376 times.
✓ Branch 1 taken 352 times.
1456 else if(c != 'u')
270 {
271 752 ss.append('\\');
272
2/2
✓ Branch 1 taken 324 times.
✓ Branch 2 taken 52 times.
752 if(BOOST_JSON_LIKELY(ss))
273 {
274 648 ss.append(c);
275 }
276 else
277 {
278 104 w.buf_[0] = c;
279
1/1
✓ Branch 1 taken 52 times.
104 return w.suspend(
280 104 writer::state::esc1);
281 }
282 }
283 else
284 {
285
2/2
✓ Branch 1 taken 208 times.
✓ Branch 2 taken 144 times.
704 if(BOOST_JSON_LIKELY(
286 ss.remain() >= 6))
287 {
288 416 ss.append("\\u00", 4);
289 416 ss.append(hex[static_cast<
290 416 unsigned char>(ch) >> 4]);
291 416 ss.append(hex[static_cast<
292 416 unsigned char>(ch) & 15]);
293 }
294 else
295 {
296 288 ss.append('\\');
297 288 w.buf_[0] = hex[static_cast<
298 288 unsigned char>(ch) >> 4];
299 288 w.buf_[1] = hex[static_cast<
300 288 unsigned char>(ch) & 15];
301 288 goto do_utf1;
302 }
303 }
304 }
305 else
306 {
307 68858 ss.append('\x22'); // '"'
308 68858 return true;
309 }
310 }
311
1/1
✓ Branch 1 taken 9082 times.
18164 return w.suspend(writer::state::str3);
312
313 104 do_esc1:
314
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 52 times.
104 BOOST_ASSERT(ss);
315 104 ss.append(w.buf_[0]);
316 104 goto do_str3;
317
318 384 do_utf1:
319
2/2
✓ Branch 1 taken 144 times.
✓ Branch 2 taken 48 times.
384 if(BOOST_JSON_LIKELY(ss))
320 288 ss.append('u');
321 else
322
1/1
✓ Branch 1 taken 48 times.
96 return w.suspend(writer::state::utf1);
323 384 do_utf2:
324
2/2
✓ Branch 1 taken 144 times.
✓ Branch 2 taken 48 times.
384 if(BOOST_JSON_LIKELY(ss))
325 288 ss.append('0');
326 else
327
1/1
✓ Branch 1 taken 48 times.
96 return w.suspend(writer::state::utf2);
328 384 do_utf3:
329
2/2
✓ Branch 1 taken 144 times.
✓ Branch 2 taken 48 times.
384 if(BOOST_JSON_LIKELY(ss))
330 288 ss.append('0');
331 else
332
1/1
✓ Branch 1 taken 48 times.
96 return w.suspend(writer::state::utf3);
333 384 do_utf4:
334
2/2
✓ Branch 1 taken 144 times.
✓ Branch 2 taken 48 times.
384 if(BOOST_JSON_LIKELY(ss))
335 288 ss.append(w.buf_[0]);
336 else
337
1/1
✓ Branch 1 taken 48 times.
96 return w.suspend(writer::state::utf4);
338 384 do_utf5:
339
2/2
✓ Branch 1 taken 144 times.
✓ Branch 2 taken 48 times.
384 if(BOOST_JSON_LIKELY(ss))
340 288 ss.append(w.buf_[1]);
341 else
342
1/1
✓ Branch 1 taken 48 times.
96 return w.suspend(writer::state::utf5);
343 288 goto do_str3;
344 88570 }
345
346 bool
347 19827 write_string(writer& w, stream& ss0)
348 {
349 19827 return do_write_string<true>(w, ss0);
350 }
351
352 bool
353 408 resume_string(writer& w, stream& ss0)
354 {
355 408 return do_write_string<false>(w, ss0);
356 }
357
358 template<bool StackEmpty>
359 bool
360 write_value(writer& w, stream& ss);
361
362 template< class T, bool StackEmpty >
363 BOOST_FORCEINLINE
364 bool
365 write_impl(no_conversion_tag, writer& w, stream& ss)
366 {
367 34536 return write_value<StackEmpty>(w, ss);
368 }
369
370 template<bool StackEmpty>
371 bool
372 12082 write_array(writer& w, stream& ss)
373 {
374 12080 return write_impl<array, StackEmpty>(sequence_conversion_tag(), w, ss);
375 }
376
377 template<bool StackEmpty>
378 bool
379 54388 write_object(writer& w, stream& ss)
380 {
381 54388 return write_impl<object, StackEmpty>(map_like_conversion_tag(), w, ss);
382 }
383
384 template<bool StackEmpty>
385 bool
386 133854 write_value(writer& w, stream& ss)
387 {
388
2/2
✓ Branch 1 taken 481 times.
✓ Branch 2 taken 22424 times.
45810 if(StackEmpty || w.st_.empty())
389 {
390
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 44503 times.
89006 BOOST_ASSERT( w.p_ );
391 89006 auto const pv = reinterpret_cast<value const*>(w.p_);
392
8/8
✓ Branch 1 taken 18083 times.
✓ Branch 2 taken 3400 times.
✓ Branch 3 taken 14643 times.
✓ Branch 4 taken 3182 times.
✓ Branch 5 taken 91 times.
✓ Branch 6 taken 467 times.
✓ Branch 7 taken 306 times.
✓ Branch 8 taken 4331 times.
89006 switch(pv->kind())
393 {
394 36166 default:
395 case kind::object:
396 36166 w.p_ = &pv->get_object();
397 36166 return write_object<true>(w, ss);
398
399 6800 case kind::array:
400 6800 w.p_ = &pv->get_array();
401 6800 return write_array<true>(w, ss);
402
403 29286 case kind::string:
404 {
405 29286 auto const& js = pv->get_string();
406 29286 w.cs0_ = { js.data(), js.size() };
407 29286 return do_write_string<true>(w, ss);
408 }
409
410 6364 case kind::int64:
411 6364 return write_int64( w, ss, pv->get_int64() );
412 182 case kind::uint64:
413 182 return write_uint64( w, ss, pv->get_uint64() );
414 934 case kind::double_:
415 934 return write_double( w, ss, pv->get_double() );
416
417 612 case kind::bool_:
418
2/2
✓ Branch 1 taken 139 times.
✓ Branch 2 taken 167 times.
612 if( pv->get_bool() )
419 278 return write_true(w, ss);
420 else
421 334 return write_false(w, ss);
422
423 8662 case kind::null:
424 8662 return write_null(w, ss);
425 }
426 }
427 else
428 {
429 writer::state st;
430 44848 w.st_.peek(st);
431
4/4
✓ Branch 0 taken 1324 times.
✓ Branch 1 taken 9404 times.
✓ Branch 2 taken 2636 times.
✓ Branch 3 taken 9060 times.
44848 switch(st)
432 {
433 2648 default:
434 case writer::state::lit:
435
1/1
✓ Branch 1 taken 1324 times.
2648 return resume_buffer(w, ss);
436
437 18808 case writer::state::str1: case writer::state::str2:
438 case writer::state::str3: case writer::state::esc1:
439 case writer::state::utf1: case writer::state::utf2:
440 case writer::state::utf3: case writer::state::utf4:
441 case writer::state::utf5:
442
1/1
✓ Branch 1 taken 9404 times.
18808 return do_write_string<false>(w, ss);
443
444 5272 case writer::state::arr1: case writer::state::arr2:
445 case writer::state::arr3: case writer::state::arr4:
446
1/1
✓ Branch 1 taken 2636 times.
5272 return write_array<StackEmpty>(w, ss);
447
448 18120 case writer::state::obj1: case writer::state::obj2:
449 case writer::state::obj3: case writer::state::obj4:
450 case writer::state::obj5: case writer::state::obj6:
451
1/1
✓ Branch 1 taken 9060 times.
18120 return write_object<StackEmpty>(w, ss);
452 }
453 }
454 }
455
456 } // namespace detail
457
458 2348 serializer::
459 2348 serializer(serialize_options const& opts) noexcept
460 2348 : serializer({}, nullptr, 0, opts)
461 2348 {}
462
463 21245 serializer::
464 serializer(
465 storage_ptr sp,
466 unsigned char* buf,
467 std::size_t buf_size,
468 21245 serialize_options const& opts) noexcept
469 21245 : detail::writer(std::move(sp), buf, buf_size, opts)
470 21245 {}
471
472 void
473 20961 serializer::
474 reset(value const* p) noexcept
475 {
476 20961 p_ = p;
477 20961 fn0_ = &detail::write_value<true>;
478 20961 fn1_ = &detail::write_value<false>;
479 20961 st_.clear();
480 20961 done_ = false;
481 20961 }
482
483 void
484 5 serializer::
485 reset(array const* p) noexcept
486 {
487 5 p_ = p;
488 5 fn0_ = &detail::write_array<true>;
489 5 fn1_ = &detail::write_array<false>;
490 5 st_.clear();
491 5 done_ = false;
492 5 }
493
494 void
495 51 serializer::
496 reset(object const* p) noexcept
497 {
498 51 p_ = p;
499 51 fn0_ = &detail::write_object<true>;
500 51 fn1_ = &detail::write_object<false>;
501 51 st_.clear();
502 51 done_ = false;
503 51 }
504
505 void
506 2 serializer::
507 reset(string const* p) noexcept
508 {
509 2 cs0_ = { p->data(), p->size() };
510 2 fn0_ = &detail::do_write_string<true>;
511 2 fn1_ = &detail::do_write_string<false>;
512 2 st_.clear();
513 2 done_ = false;
514 2 }
515
516 void
517 1 serializer::
518 reset(string_view sv) noexcept
519 {
520 1 cs0_ = { sv.data(), sv.size() };
521 1 fn0_ = &detail::do_write_string<true>;
522 1 fn1_ = &detail::do_write_string<false>;
523 1 st_.clear();
524 1 done_ = false;
525 1 }
526
527 void
528 6 serializer::reset(std::nullptr_t) noexcept
529 {
530 6 p_ = nullptr;
531 6 fn0_ = &detail::write_impl<std::nullptr_t, true>;
532 6 fn1_ = &detail::write_impl<std::nullptr_t, false>;
533 6 st_.clear();
534 6 done_ = false;
535 6 }
536
537 string_view
538 32983 serializer::
539 read(char* dest, std::size_t size)
540 {
541
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 32977 times.
32983 if( !fn0_ )
542 6 reset(nullptr);
543
544
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 32982 times.
32983 if(BOOST_JSON_UNLIKELY(size == 0))
545 1 return {dest, 0};
546
547 32982 detail::stream ss(dest, size);
548
2/2
✓ Branch 1 taken 21244 times.
✓ Branch 2 taken 11738 times.
32982 if(st_.empty())
549
1/1
✓ Branch 1 taken 21242 times.
21244 fn0_(*this, ss);
550 else
551
1/1
✓ Branch 1 taken 11738 times.
11738 fn1_(*this, ss);
552
2/2
✓ Branch 1 taken 21242 times.
✓ Branch 2 taken 11738 times.
32980 if(st_.empty())
553 {
554 21242 done_ = true;
555 21242 fn0_ = nullptr;
556 21242 p_ = nullptr;
557 }
558 32980 return string_view(
559 32980 dest, ss.used(dest));
560 }
561
562 } // namespace json
563 } // namespace boost
564
565 #ifdef _MSC_VER
566 #pragma warning(pop)
567 #endif
568
569 #endif
570