GCC Code Coverage Report


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