impl/serializer.ipp

100.0% Lines (261/261) 100.0% Functions (36/36) 94.3% Branches (116/123)
impl/serializer.ipp
Line Branch Hits Source Code
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 4091 write_buffer(writer& w, stream& ss0, F f)
95 {
96
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 4091 times.
4091 BOOST_ASSERT( w.st_.empty() );
97
98 4091 local_stream ss(ss0);
99
2/2
✓ Branch 1 taken 2957 times.
✓ Branch 2 taken 1134 times.
4091 if(BOOST_JSON_LIKELY( ss.remain() >= detail::max_number_chars ))
100 {
101 2957 ss.advance( f(ss.data()) );
102 2957 return true;
103 }
104
105 1134 w.cs0_ = { w.buf_, f(w.buf_) };
106 1134 return write_buffer(w, ss);
107 4091 }
108
109 template<literals Lit>
110 bool
111 4725 write_literal(writer& w, stream& ss)
112 {
113 4725 constexpr std::size_t index = literal_index(Lit);
114 4725 constexpr char const* literal = literal_strings[index];
115 4725 constexpr std::size_t sz = literal_sizes[index];
116
117 4725 std::size_t const n = ss.remain();
118
2/2
✓ Branch 0 taken 4613 times.
✓ Branch 1 taken 112 times.
4725 if(BOOST_JSON_LIKELY( n >= sz ))
119 {
120 4613 ss.append( literal, sz );
121 4613 return true;
122 }
123
124 112 ss.append(literal, n);
125
126 112 w.cs0_ = {literal + n, sz - n};
127 112 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 44303 do_write_string(writer& w, stream& ss0)
181 {
182 44303 local_stream ss(ss0);
183 44303 local_const_stream cs(w.cs0_);
184
1/2
✓ Branch 1 taken 9812 times.
✗ Branch 2 not taken.
9812 if(! StackEmpty && ! w.st_.empty())
185 {
186 writer::state st;
187
1/1
✓ Branch 1 taken 9812 times.
9812 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.
9812 switch(st)
189 {
190 170 default:
191 170 case writer::state::str1: goto do_str1;
192 268 case writer::state::str2: goto do_str2;
193 9082 case writer::state::str3: goto do_str3;
194 52 case writer::state::esc1: goto do_esc1;
195 48 case writer::state::utf1: goto do_utf1;
196 48 case writer::state::utf2: goto do_utf2;
197 48 case writer::state::utf3: goto do_utf3;
198 48 case writer::state::utf4: goto do_utf4;
199 48 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 34491 do_str1:
215
2/2
✓ Branch 1 taken 34491 times.
✓ Branch 2 taken 170 times.
34661 if(BOOST_JSON_LIKELY(ss))
216 34491 ss.append('\x22'); // '"'
217 else
218
1/1
✓ Branch 1 taken 170 times.
170 return w.suspend(writer::state::str1);
219
220 // fast loop,
221 // copy unescaped
222 34759 do_str2:
223
4/4
✓ Branch 1 taken 353 times.
✓ Branch 1 taken 34150 times.
✓ Branch 2 taken 85 times.
✓ Branch 2 taken 171 times.
34759 if(BOOST_JSON_LIKELY(ss))
224 {
225 34503 std::size_t n = cs.remain();
226
2/2
✓ Branch 0 taken 34459 times.
✓ Branch 1 taken 44 times.
34503 if(BOOST_JSON_LIKELY(n > 0))
227 {
228
2/2
✓ Branch 1 taken 25784 times.
✓ Branch 2 taken 8675 times.
34459 if(ss.remain() > n)
229 25784 n = detail::count_unescaped(
230 cs.data(), n);
231 else
232 8675 n = detail::count_unescaped(
233 cs.data(), ss.remain());
234
2/2
✓ Branch 0 taken 25225 times.
✓ Branch 1 taken 9234 times.
34459 if(n > 0)
235 {
236 25225 ss.append(cs.data(), n);
237 25225 cs.skip(n);
238
2/2
✓ Branch 1 taken 25194 times.
✓ Branch 2 taken 31 times.
25225 if(! ss)
239
1/1
✓ Branch 1 taken 12 times.
12 return w.suspend(writer::state::str2);
240 }
241 }
242 else
243 {
244 44 ss.append('\x22'); // '"'
245 44 return true;
246 }
247 }
248 else
249 {
250
1/1
✓ Branch 1 taken 256 times.
256 return w.suspend(writer::state::str2);
251 }
252
253 // slow loop,
254 // handle escapes
255 43725 do_str3:
256
2/2
✓ Branch 1 taken 31452012 times.
✓ Branch 2 taken 9082 times.
31461094 while(BOOST_JSON_LIKELY(ss))
257 {
258
2/2
✓ Branch 1 taken 31417565 times.
✓ Branch 2 taken 34447 times.
31452012 if(BOOST_JSON_LIKELY(cs))
259 {
260 31417565 auto const ch = *cs;
261 31417565 auto const c = esc[static_cast<
262 unsigned char>(ch)];
263 31417565 ++cs;
264
2/2
✓ Branch 0 taken 31416837 times.
✓ Branch 1 taken 728 times.
31417565 if(! c)
265 {
266 31416837 ss.append(ch);
267 }
268
2/2
✓ Branch 0 taken 376 times.
✓ Branch 1 taken 352 times.
728 else if(c != 'u')
269 {
270 376 ss.append('\\');
271
2/2
✓ Branch 1 taken 324 times.
✓ Branch 2 taken 52 times.
376 if(BOOST_JSON_LIKELY(ss))
272 {
273 324 ss.append(c);
274 }
275 else
276 {
277 52 w.buf_[0] = c;
278
1/1
✓ Branch 1 taken 52 times.
52 return w.suspend(
279 52 writer::state::esc1);
280 }
281 }
282 else
283 {
284
2/2
✓ Branch 1 taken 208 times.
✓ Branch 2 taken 144 times.
352 if(BOOST_JSON_LIKELY(
285 ss.remain() >= 6))
286 {
287 208 ss.append("\\u00", 4);
288 208 ss.append(hex[static_cast<
289 208 unsigned char>(ch) >> 4]);
290 208 ss.append(hex[static_cast<
291 208 unsigned char>(ch) & 15]);
292 }
293 else
294 {
295 144 ss.append('\\');
296 144 w.buf_[0] = hex[static_cast<
297 144 unsigned char>(ch) >> 4];
298 144 w.buf_[1] = hex[static_cast<
299 144 unsigned char>(ch) & 15];
300 144 goto do_utf1;
301 }
302 }
303 }
304 else
305 {
306 34447 ss.append('\x22'); // '"'
307 34447 return true;
308 }
309 }
310
1/1
✓ Branch 1 taken 9082 times.
9082 return w.suspend(writer::state::str3);
311
312 52 do_esc1:
313
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 52 times.
52 BOOST_ASSERT(ss);
314 52 ss.append(w.buf_[0]);
315 52 goto do_str3;
316
317 192 do_utf1:
318
2/2
✓ Branch 1 taken 144 times.
✓ Branch 2 taken 48 times.
192 if(BOOST_JSON_LIKELY(ss))
319 144 ss.append('u');
320 else
321
1/1
✓ Branch 1 taken 48 times.
48 return w.suspend(writer::state::utf1);
322 192 do_utf2:
323
4/4
✓ Branch 1 taken 72 times.
✓ Branch 1 taken 72 times.
✓ Branch 2 taken 24 times.
✓ Branch 2 taken 24 times.
192 if(BOOST_JSON_LIKELY(ss))
324 144 ss.append('0');
325 else
326
1/1
✓ Branch 1 taken 48 times.
48 return w.suspend(writer::state::utf2);
327 192 do_utf3:
328
4/4
✓ Branch 1 taken 96 times.
✓ Branch 1 taken 48 times.
✓ Branch 2 taken 24 times.
✓ Branch 2 taken 24 times.
192 if(BOOST_JSON_LIKELY(ss))
329 144 ss.append('0');
330 else
331
1/1
✓ Branch 1 taken 48 times.
48 return w.suspend(writer::state::utf3);
332 192 do_utf4:
333
4/4
✓ Branch 1 taken 120 times.
✓ Branch 1 taken 24 times.
✓ Branch 2 taken 24 times.
✓ Branch 2 taken 24 times.
192 if(BOOST_JSON_LIKELY(ss))
334 144 ss.append(w.buf_[0]);
335 else
336
1/1
✓ Branch 1 taken 48 times.
48 return w.suspend(writer::state::utf4);
337 192 do_utf5:
338
3/4
✓ Branch 1 taken 144 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 24 times.
✓ Branch 2 taken 24 times.
192 if(BOOST_JSON_LIKELY(ss))
339 144 ss.append(w.buf_[1]);
340 else
341
1/1
✓ Branch 1 taken 48 times.
48 return w.suspend(writer::state::utf5);
342 144 goto do_str3;
343 44303 }
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 6043 write_array(writer& w, stream& ss)
372 {
373 6042 return write_impl<array, StackEmpty>(sequence_conversion_tag(), w, ss);
374 }
375
376 template<bool StackEmpty>
377 bool
378 27196 write_object(writer& w, stream& ss)
379 {
380 27196 return write_impl<object, StackEmpty>(map_like_conversion_tag(), w, ss);
381 }
382
383 template<bool StackEmpty>
384 bool
385 66944 write_value(writer& w, stream& ss)
386 {
387
2/2
✓ Branch 1 taken 481 times.
✓ Branch 2 taken 22424 times.
22905 if(StackEmpty || w.st_.empty())
388 {
389
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 44520 times.
44520 BOOST_ASSERT( w.p_ );
390 44520 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.
44520 switch(pv->kind())
392 {
393 18085 default:
394 case kind::object:
395 18085 w.p_ = &pv->get_object();
396 18085 return write_object<true>(w, ss);
397
398 3402 case kind::array:
399 3402 w.p_ = &pv->get_array();
400 3402 return write_array<true>(w, ss);
401
402 14655 case kind::string:
403 {
404 14655 auto const& js = pv->get_string();
405 14655 w.cs0_ = { js.data(), js.size() };
406 14655 return do_write_string<true>(w, ss);
407 }
408
409 3183 case kind::int64:
410 3183 return write_int64( w, ss, pv->get_int64() );
411 91 case kind::uint64:
412 91 return write_uint64( w, ss, pv->get_uint64() );
413 467 case kind::double_:
414 467 return write_double( w, ss, pv->get_double() );
415
416 306 case kind::bool_:
417
2/2
✓ Branch 1 taken 139 times.
✓ Branch 2 taken 167 times.
306 if( pv->get_bool() )
418 139 return write_true(w, ss);
419 else
420 167 return write_false(w, ss);
421
422 4331 case kind::null:
423 4331 return write_null(w, ss);
424 }
425 }
426 else
427 {
428 writer::state st;
429 22424 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.
22424 switch(st)
431 {
432 1324 default:
433 case writer::state::lit:
434
1/1
✓ Branch 1 taken 1324 times.
1324 return resume_buffer(w, ss);
435
436 9404 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.
9404 return do_write_string<false>(w, ss);
442
443 2636 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.
2636 return write_array<StackEmpty>(w, ss);
446
447 9060 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.
9060 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 time.
✓ 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