libs/http/src/serializer.cpp

63.3% Lines (236/373) 81.6% Functions (40/49) 42.7% Branches (91/213)
libs/http/src/serializer.cpp
Line Branch Hits Source Code
1 //
2 // Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
3 // Copyright (c) 2024 Christian Mazakas
4 // Copyright (c) 2024 Mohammad Nejati
5 //
6 // Distributed under the Boost Software License, Version 1.0. (See accompanying
7 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
8 //
9 // Official repository: https://github.com/cppalliance/http
10 //
11
12 #include <boost/http/detail/except.hpp>
13 #include <boost/http/detail/header.hpp>
14 #include <boost/http/message_base.hpp>
15 #include <boost/http/serializer.hpp>
16
17 #include "src/detail/array_of_const_buffers.hpp"
18 #include "src/detail/brotli_filter_base.hpp"
19 #include "src/detail/buffer_utils.hpp"
20 #include "src/detail/zlib_filter_base.hpp"
21
22 #include <boost/capy/buffers/circular_dynamic_buffer.hpp>
23 #include <boost/capy/buffers/buffer_copy.hpp>
24 #include <boost/capy/ex/system_context.hpp>
25 #include <boost/core/bit.hpp>
26 #include <boost/core/ignore_unused.hpp>
27 #include <boost/http/brotli/encode.hpp>
28 #include <boost/http/zlib/compression_method.hpp>
29 #include <boost/http/zlib/compression_strategy.hpp>
30 #include <boost/http/zlib/deflate.hpp>
31 #include <boost/http/zlib/error.hpp>
32 #include <boost/http/zlib/flush.hpp>
33
34 #include <memory>
35 #include <stddef.h>
36
37 namespace boost {
38 namespace http {
39
40 namespace {
41
42 const
43 capy::const_buffer
44 crlf_and_final_chunk = {"\r\n0\r\n\r\n", 7};
45
46 const
47 capy::const_buffer
48 crlf = {"\r\n", 2};
49
50 const
51 capy::const_buffer
52 final_chunk = {"0\r\n\r\n", 5};
53
54 constexpr
55 std::uint8_t
56 224 chunk_header_len(
57 std::size_t max_chunk_size) noexcept
58 {
59 return
60 static_cast<uint8_t>(
61 224 (core::bit_width(max_chunk_size) + 3) / 4 +
62 224 2); // crlf
63 };
64
65 void
66 68 write_chunk_header(
67 const capy::mutable_buffer_pair& mbs,
68 std::size_t size) noexcept
69 {
70 static constexpr char hexdig[] =
71 "0123456789ABCDEF";
72 char buf[18];
73 68 auto p = buf + 16;
74 68 auto const n = capy::buffer_size(mbs);
75
2/2
✓ Branch 0 taken 272 times.
✓ Branch 1 taken 68 times.
340 for(std::size_t i = n - 2; i--;)
76 {
77 272 *--p = hexdig[size & 0xf];
78 272 size >>= 4;
79 }
80 68 buf[16] = '\r';
81 68 buf[17] = '\n';
82 68 auto copied = capy::buffer_copy(
83 mbs,
84 136 capy::const_buffer(p, n));
85 ignore_unused(copied);
86
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 68 times.
68 BOOST_ASSERT(copied == n);
87 68 }
88
89 class zlib_filter
90 : public detail::zlib_filter_base
91 {
92 http::zlib::deflate_service& svc_;
93
94 public:
95 zlib_filter(
96 http::zlib::deflate_service& svc,
97 int comp_level,
98 int window_bits,
99 int mem_level)
100 : svc_(svc)
101 {
102 system::error_code ec = static_cast<http::zlib::error>(svc_.init2(
103 strm_,
104 comp_level,
105 http::zlib::deflated,
106 window_bits,
107 mem_level,
108 http::zlib::default_strategy));
109 if(ec != http::zlib::error::ok)
110 detail::throw_system_error(ec);
111 }
112
113 private:
114 virtual
115 std::size_t
116 min_out_buffer() const noexcept override
117 {
118 return 8;
119 }
120
121 virtual
122 results
123 do_process(
124 capy::mutable_buffer out,
125 capy::const_buffer in,
126 bool more) noexcept override
127 {
128 strm_.next_out = static_cast<unsigned char*>(out.data());
129 strm_.avail_out = saturate_cast(out.size());
130 strm_.next_in = static_cast<unsigned char*>(const_cast<void *>(in.data()));
131 strm_.avail_in = saturate_cast(in.size());
132
133 auto rs = static_cast<http::zlib::error>(
134 svc_.deflate(
135 strm_,
136 more ? http::zlib::no_flush : http::zlib::finish));
137
138 results rv;
139 rv.out_bytes = saturate_cast(out.size()) - strm_.avail_out;
140 rv.in_bytes = saturate_cast(in.size()) - strm_.avail_in;
141 rv.finished = (rs == http::zlib::error::stream_end);
142
143 if(rs < http::zlib::error::ok && rs != http::zlib::error::buf_err)
144 rv.ec = rs;
145
146 return rv;
147 }
148 };
149
150 class brotli_filter
151 : public detail::brotli_filter_base
152 {
153 http::brotli::encode_service& svc_;
154 http::brotli::encoder_state* state_;
155
156 public:
157 brotli_filter(
158 http::brotli::encode_service& svc,
159 std::uint32_t comp_quality,
160 std::uint32_t comp_window)
161 : svc_(svc)
162 {
163 state_ = svc_.create_instance(nullptr, nullptr, nullptr);
164 if(!state_)
165 detail::throw_bad_alloc();
166 using encoder_parameter = http::brotli::encoder_parameter;
167 svc_.set_parameter(state_, encoder_parameter::quality, comp_quality);
168 svc_.set_parameter(state_, encoder_parameter::lgwin, comp_window);
169 }
170
171 ~brotli_filter()
172 {
173 svc_.destroy_instance(state_);
174 }
175
176 private:
177 virtual
178 results
179 do_process(
180 capy::mutable_buffer out,
181 capy::const_buffer in,
182 bool more) noexcept override
183 {
184 auto* next_in = reinterpret_cast<const std::uint8_t*>(in.data());
185 auto available_in = in.size();
186 auto* next_out = reinterpret_cast<std::uint8_t*>(out.data());
187 auto available_out = out.size();
188
189 using encoder_operation =
190 http::brotli::encoder_operation;
191
192 bool rs = svc_.compress_stream(
193 state_,
194 more ? encoder_operation::process : encoder_operation::finish,
195 &available_in,
196 &next_in,
197 &available_out,
198 &next_out,
199 nullptr);
200
201 results rv;
202 rv.in_bytes = in.size() - available_in;
203 rv.out_bytes = out.size() - available_out;
204 rv.finished = svc_.is_finished(state_);
205
206 if(rs == false)
207 rv.ec = error::bad_payload;
208
209 return rv;
210 }
211 };
212
213 template<class UInt>
214 std::size_t
215 clamp(
216 UInt x,
217 std::size_t limit = (std::numeric_limits<
218 std::size_t>::max)()) noexcept
219 {
220 if(x >= limit)
221 return limit;
222 return static_cast<std::size_t>(x);
223 }
224
225 } // namespace
226
227 //------------------------------------------------
228
229 class serializer::impl
230 {
231 enum class state
232 {
233 reset,
234 start,
235 header,
236 body
237 };
238
239 enum class style
240 {
241 empty,
242 stream
243 };
244
245 std::shared_ptr<serializer_config_impl const> cfg_;
246 detail::workspace ws_;
247
248 std::unique_ptr<detail::filter> filter_;
249
250 capy::circular_dynamic_buffer out_;
251 capy::circular_dynamic_buffer in_;
252 detail::array_of_const_buffers prepped_;
253 capy::const_buffer tmp_;
254
255 state state_ = state::start;
256 style style_ = style::empty;
257 uint8_t chunk_header_len_ = 0;
258 bool more_input_ = false;
259 bool is_chunked_ = false;
260 bool needs_exp100_continue_ = false;
261 bool filter_done_ = false;
262
263 public:
264 message_base const* msg_ = nullptr;
265
266 explicit
267 224 impl(std::shared_ptr<serializer_config_impl const> cfg)
268 224 : cfg_(std::move(cfg))
269
1/1
✓ Branch 2 taken 224 times.
224 , ws_(cfg_->space_needed)
270 {
271 224 }
272
273 impl(
274 std::shared_ptr<serializer_config_impl const> cfg,
275 message_base const& msg)
276 : cfg_(std::move(cfg))
277 , ws_(cfg_->space_needed)
278 , msg_(&msg)
279 {
280 }
281
282 void
283 165 reset() noexcept
284 {
285 165 filter_.reset();
286 165 ws_.clear();
287 165 state_ = state::start;
288 165 }
289
290 auto
291 512 prepare() ->
292 system::result<const_buffers_type>
293 {
294 // Precondition violation
295
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 511 times.
512 if(state_ < state::header)
296 1 detail::throw_logic_error();
297
298 // Expect: 100-continue
299
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 507 times.
511 if(needs_exp100_continue_)
300 {
301
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 2 times.
4 if(!is_header_done())
302 4 return const_buffers_type(
303 prepped_.begin(),
304 2 1); // limit to header
305
306 2 needs_exp100_continue_ = false;
307
308 2 BOOST_HTTP_RETURN_EC(
309 error::expect_100_continue);
310 }
311
312
1/2
✓ Branch 1 taken 507 times.
✗ Branch 2 not taken.
507 if(!filter_)
313 {
314
2/3
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 501 times.
✗ Branch 2 not taken.
507 switch(style_)
315 {
316 6 case style::empty:
317 6 break;
318
319 501 case style::stream:
320
8/8
✓ Branch 1 taken 293 times.
✓ Branch 2 taken 208 times.
✓ Branch 4 taken 174 times.
✓ Branch 5 taken 119 times.
✓ Branch 6 taken 137 times.
✓ Branch 7 taken 37 times.
✓ Branch 8 taken 137 times.
✓ Branch 9 taken 364 times.
501 if(out_.size() == 0 && is_header_done() && more_input_)
321 137 BOOST_HTTP_RETURN_EC(
322 error::need_data);
323 364 break;
324 }
325 }
326 else // filter
327 {
328 switch(style_)
329 {
330 case style::empty:
331 {
332 if(out_capacity() == 0 || filter_done_)
333 break;
334
335 const auto rs = filter_->process(
336 detail::make_span(out_prepare()),
337 {}, // empty input
338 false);
339
340 if(rs.ec)
341 {
342 ws_.clear();
343 state_ = state::reset;
344 return rs.ec;
345 }
346
347 out_commit(rs.out_bytes);
348
349 if(rs.finished)
350 {
351 filter_done_ = true;
352 out_finish();
353 }
354
355 break;
356 }
357
358 case style::stream:
359 {
360 if(out_capacity() == 0 || filter_done_)
361 break;
362
363 const auto rs = filter_->process(
364 detail::make_span(out_prepare()),
365 in_.data(),
366 more_input_);
367
368 if(rs.ec)
369 {
370 ws_.clear();
371 state_ = state::reset;
372 return rs.ec;
373 }
374
375 in_.consume(rs.in_bytes);
376 out_commit(rs.out_bytes);
377
378 if(rs.finished)
379 {
380 filter_done_ = true;
381 out_finish();
382 }
383
384 if(out_.size() == 0 && is_header_done() && more_input_)
385 BOOST_HTTP_RETURN_EC(
386 error::need_data);
387 break;
388 }
389 }
390 }
391
392 370 prepped_.reset(!is_header_done());
393
2/2
✓ Branch 1 taken 740 times.
✓ Branch 2 taken 370 times.
1110 for(auto const& cb : out_.data())
394 {
395
2/2
✓ Branch 1 taken 211 times.
✓ Branch 2 taken 529 times.
740 if(cb.size() != 0)
396 211 prepped_.append(cb);
397 }
398
1/1
✓ Branch 1 taken 370 times.
370 return detail::make_span(prepped_);
399 }
400
401 void
402 1996 consume(
403 std::size_t n)
404 {
405 // Precondition violation
406
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1995 times.
1996 if(state_ < state::header)
407 1 detail::throw_logic_error();
408
409
2/2
✓ Branch 1 taken 207 times.
✓ Branch 2 taken 1788 times.
1995 if(!is_header_done())
410 {
411 const auto header_remain =
412 207 prepped_[0].size();
413
2/2
✓ Branch 0 taken 78 times.
✓ Branch 1 taken 129 times.
207 if(n < header_remain)
414 {
415 78 prepped_.consume(n);
416 78 return;
417 }
418 129 n -= header_remain;
419 129 prepped_.consume(header_remain);
420 129 state_ = state::body;
421 }
422
423 1917 prepped_.consume(n);
424
425 // no-op when out_ is not in use
426 1917 out_.consume(n);
427
428
2/2
✓ Branch 1 taken 1692 times.
✓ Branch 2 taken 225 times.
1917 if(!prepped_.empty())
429 1692 return;
430
431
2/2
✓ Branch 0 taken 128 times.
✓ Branch 1 taken 97 times.
225 if(more_input_)
432 128 return;
433
434
2/6
✗ Branch 1 not taken.
✓ Branch 2 taken 97 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 97 times.
97 if(filter_ && !filter_done_)
435 return;
436
437
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 95 times.
97 if(needs_exp100_continue_)
438 2 return;
439
440 // ready for next message
441 95 reset();
442 }
443
444 void
445 224 start_init(
446 message_base const& m)
447 {
448 // Precondition violation
449
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 224 times.
224 if(state_ != state::start)
450 detail::throw_logic_error();
451
452 // TODO: To uphold the strong exception guarantee,
453 // `state_` must be reset to `state::start` if an
454 // exception is thrown during the start operation.
455 224 state_ = state::header;
456
457 // VFALCO what do we do with
458 // metadata error code failures?
459 // m.h_.md.maybe_throw();
460
461 224 auto const& md = m.metadata();
462 224 needs_exp100_continue_ = md.expect.is_100_continue;
463
464 // Transfer-Encoding
465 224 is_chunked_ = md.transfer_encoding.is_chunked;
466
467 // Content-Encoding
468
1/4
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 224 times.
224 switch (md.content_encoding.coding)
469 {
470 case content_coding::deflate:
471 if(!cfg_->apply_deflate_encoder)
472 goto no_filter;
473 if(auto* svc = capy::get_system_context().find_service<http::zlib::deflate_service>())
474 {
475 filter_.reset(new zlib_filter(
476 *svc,
477 cfg_->zlib_comp_level,
478 cfg_->zlib_window_bits,
479 cfg_->zlib_mem_level));
480 filter_done_ = false;
481 }
482 break;
483
484 case content_coding::gzip:
485 if(!cfg_->apply_gzip_encoder)
486 goto no_filter;
487 if(auto* svc = capy::get_system_context().find_service<http::zlib::deflate_service>())
488 {
489 filter_.reset(new zlib_filter(
490 *svc,
491 cfg_->zlib_comp_level,
492 cfg_->zlib_window_bits + 16,
493 cfg_->zlib_mem_level));
494 filter_done_ = false;
495 }
496 break;
497
498 case content_coding::br:
499 if(!cfg_->apply_brotli_encoder)
500 goto no_filter;
501 if(auto* svc = capy::get_system_context().find_service<http::brotli::encode_service>())
502 {
503 filter_.reset(new brotli_filter(
504 *svc,
505 cfg_->brotli_comp_quality,
506 cfg_->brotli_comp_window));
507 filter_done_ = false;
508 }
509 break;
510
511 no_filter:
512 224 default:
513 224 filter_.reset();
514 224 break;
515 }
516 224 }
517
518 void
519 6 start_empty(
520 message_base const& m)
521 {
522 6 start_init(m);
523 6 style_ = style::empty;
524
525
1/1
✓ Branch 1 taken 6 times.
6 prepped_ = make_array(
526 1 + // header
527 2); // out buffer pairs
528
529 6 out_init();
530
531
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 if(!filter_)
532 6 out_finish();
533
534 6 prepped_.append({ m.h_.cbuf, m.h_.size });
535 6 more_input_ = false;
536 6 }
537
538 void
539 143 start_stream(message_base const& m)
540 {
541 143 start_init(m);
542 143 style_ = style::stream;
543
544
1/1
✓ Branch 1 taken 143 times.
143 prepped_ = make_array(
545 1 + // header
546 2); // out buffer pairs
547
548
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 143 times.
143 if(filter_)
549 {
550 // TODO: smarter buffer distribution
551 auto const n = (ws_.size() - 1) / 2;
552 in_ = { ws_.reserve_front(n), n };
553 }
554
555 143 out_init();
556
557 143 prepped_.append({ m.h_.cbuf, m.h_.size });
558 143 more_input_ = true;
559 143 }
560
561 // Like start_stream but without in_ allocation.
562 // Entire workspace is used for output buffering.
563 void
564 75 start_buffers_direct(message_base const& m)
565 {
566 75 start_init(m);
567 75 style_ = style::stream;
568
569
1/1
✓ Branch 1 taken 75 times.
75 prepped_ = make_array(
570 1 + // header
571 2); // out buffer pairs
572
573 75 out_init();
574
575 75 prepped_.append({ m.h_.cbuf, m.h_.size });
576 75 more_input_ = true;
577 75 }
578
579 std::size_t
580 286 stream_capacity() const
581 {
582
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 286 times.
286 if(filter_)
583 return in_.capacity();
584 286 return out_capacity();
585 }
586
587 capy::mutable_buffer_pair
588 160 stream_prepare()
589 {
590
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 160 times.
160 if(state_ == state::start)
591 {
592 if(!msg_)
593 detail::throw_logic_error();
594 start_stream(*msg_);
595 }
596
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 160 times.
160 if(filter_)
597 return in_.prepare(in_.capacity());
598 160 return out_prepare();
599 }
600
601 void
602 269 stream_commit(std::size_t n)
603 {
604
2/2
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 268 times.
269 if(n > stream_capacity())
605 1 detail::throw_invalid_argument();
606
607
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 268 times.
268 if(filter_)
608 return in_.commit(n);
609
610 268 out_commit(n);
611 }
612
613 void
614 148 stream_close() noexcept
615 {
616
1/2
✓ Branch 1 taken 148 times.
✗ Branch 2 not taken.
148 if(!filter_)
617 148 out_finish();
618
619 148 more_input_ = false;
620 148 }
621
622 bool
623 632 is_done() const noexcept
624 {
625 632 return state_ == state::start;
626 }
627
628 bool
629 384 is_start() const noexcept
630 {
631 384 return state_ == state::start;
632 }
633
634 detail::workspace&
635 ws() noexcept
636 {
637 return ws_;
638 }
639
640 private:
641 bool
642 2662 is_header_done() const noexcept
643 {
644 2662 return state_ == state::body;
645 }
646
647 detail::array_of_const_buffers
648 224 make_array(std::size_t n)
649 {
650
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 224 times.
224 BOOST_ASSERT(n <= std::uint16_t(-1));
651
652 return {
653 224 ws_.push_array(n,
654 capy::const_buffer{}),
655
1/1
✓ Branch 2 taken 224 times.
224 static_cast<std::uint16_t>(n) };
656 }
657
658 void
659 224 out_init()
660 {
661 // use all the remaining buffer
662 224 auto const n = ws_.size() - 1;
663
1/1
✓ Branch 1 taken 224 times.
224 out_ = { ws_.reserve_front(n), n };
664 224 chunk_header_len_ =
665 224 chunk_header_len(out_.capacity());
666
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 224 times.
224 if(out_capacity() == 0)
667 detail::throw_length_error();
668 224 }
669
670 capy::mutable_buffer_pair
671 160 out_prepare() noexcept
672 {
673 160 auto mbp = out_.prepare(out_.capacity());
674
2/2
✓ Branch 0 taken 69 times.
✓ Branch 1 taken 91 times.
160 if(is_chunked_)
675 {
676 69 capy::remove_prefix(
677 69 mbp, chunk_header_len_);
678 69 capy::remove_suffix(
679 mbp, crlf_and_final_chunk.size());
680 }
681 160 return mbp;
682 }
683
684 void
685 268 out_commit(
686 std::size_t n) noexcept
687 {
688
2/2
✓ Branch 0 taken 87 times.
✓ Branch 1 taken 181 times.
268 if(is_chunked_)
689 {
690
2/2
✓ Branch 0 taken 19 times.
✓ Branch 1 taken 68 times.
87 if(n == 0)
691 19 return;
692
693 68 write_chunk_header(out_.prepare(chunk_header_len_), n);
694 68 out_.commit(chunk_header_len_);
695
696 68 out_.prepare(n);
697 68 out_.commit(n);
698
699 68 capy::buffer_copy(out_.prepare(crlf.size()), crlf);
700 68 out_.commit(crlf.size());
701 }
702 else
703 {
704 181 out_.commit(n);
705 }
706 }
707
708 std::size_t
709 510 out_capacity() const noexcept
710 {
711
2/2
✓ Branch 0 taken 174 times.
✓ Branch 1 taken 336 times.
510 if(is_chunked_)
712 {
713 174 auto const overhead = chunk_header_len_ +
714 174 crlf_and_final_chunk.size();
715
2/2
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 173 times.
174 if(out_.capacity() < overhead)
716 1 return 0;
717 173 return out_.capacity() - overhead;
718 }
719 336 return out_.capacity();
720 }
721
722 void
723 154 out_finish() noexcept
724 {
725
2/2
✓ Branch 0 taken 41 times.
✓ Branch 1 taken 113 times.
154 if(is_chunked_)
726 {
727 41 capy::buffer_copy(
728 41 out_.prepare(final_chunk.size()), final_chunk);
729 41 out_.commit(final_chunk.size());
730 }
731 154 }
732 };
733
734 //------------------------------------------------
735
736 229 serializer::
737 ~serializer()
738 {
739
2/2
✓ Branch 0 taken 224 times.
✓ Branch 1 taken 5 times.
229 delete impl_;
740 229 }
741
742 1 serializer::
743 1 serializer(serializer&& other) noexcept
744 1 : impl_(other.impl_)
745 {
746 1 other.impl_ = nullptr;
747 1 }
748
749 serializer&
750 2 serializer::
751 operator=(serializer&& other) noexcept
752 {
753
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if(this != &other)
754 {
755
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 delete impl_;
756 2 impl_ = other.impl_;
757 2 other.impl_ = nullptr;
758 }
759 2 return *this;
760 }
761
762 224 serializer::
763 serializer(
764 224 std::shared_ptr<serializer_config_impl const> cfg)
765
1/3
✓ Branch 4 taken 224 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
224 : impl_(new impl(std::move(cfg)))
766 {
767 224 }
768
769 void
770 70 serializer::
771 reset() noexcept
772 {
773
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 70 times.
70 BOOST_ASSERT(impl_);
774 70 impl_->reset();
775 70 }
776
777 void
778 225 serializer::
779 set_message(message_base const& m) noexcept
780 {
781
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 225 times.
225 BOOST_ASSERT(impl_);
782 225 impl_->msg_ = &m;
783 225 }
784
785 void
786 6 serializer::
787 start()
788 {
789
2/4
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 6 times.
6 if(!impl_ || !impl_->msg_)
790 detail::throw_logic_error();
791 6 impl_->start_empty(*impl_->msg_);
792 6 }
793
794 void
795 serializer::
796 start_stream()
797 {
798 if(!impl_ || !impl_->msg_)
799 detail::throw_logic_error();
800 impl_->start_stream(*impl_->msg_);
801 }
802
803 void
804 143 serializer::
805 start_writes()
806 {
807
2/4
✓ Branch 0 taken 143 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 143 times.
143 if(!impl_ || !impl_->msg_)
808 detail::throw_logic_error();
809 143 impl_->start_stream(*impl_->msg_);
810 143 }
811
812 void
813 75 serializer::
814 start_buffers()
815 {
816
2/4
✓ Branch 0 taken 75 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 75 times.
75 if(!impl_ || !impl_->msg_)
817 detail::throw_logic_error();
818 75 impl_->start_buffers_direct(*impl_->msg_);
819 75 }
820
821 auto
822 512 serializer::
823 prepare() ->
824 system::result<const_buffers_type>
825 {
826
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 512 times.
512 BOOST_ASSERT(impl_);
827 512 return impl_->prepare();
828 }
829
830 void
831 1996 serializer::
832 consume(std::size_t n)
833 {
834
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1996 times.
1996 BOOST_ASSERT(impl_);
835 1996 impl_->consume(n);
836 1995 }
837
838 bool
839 632 serializer::
840 is_done() const noexcept
841 {
842
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 632 times.
632 BOOST_ASSERT(impl_);
843 632 return impl_->is_done();
844 }
845
846 bool
847 384 serializer::
848 is_start() const noexcept
849 {
850
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 384 times.
384 BOOST_ASSERT(impl_);
851 384 return impl_->is_start();
852 }
853
854 //------------------------------------------------
855
856 detail::workspace&
857 serializer::
858 ws()
859 {
860 BOOST_ASSERT(impl_);
861 return impl_->ws();
862 }
863
864 //------------------------------------------------
865
866 std::size_t
867 17 serializer::
868 stream_capacity() const
869 {
870
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
17 BOOST_ASSERT(impl_);
871 17 return impl_->stream_capacity();
872 }
873
874 auto
875 160 serializer::
876 stream_prepare() ->
877 mutable_buffers_type
878 {
879
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 160 times.
160 BOOST_ASSERT(impl_);
880 160 return impl_->stream_prepare();
881 }
882
883 void
884 269 serializer::
885 stream_commit(std::size_t n)
886 {
887
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 269 times.
269 BOOST_ASSERT(impl_);
888 269 impl_->stream_commit(n);
889 268 }
890
891 void
892 148 serializer::
893 stream_close() noexcept
894 {
895
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 148 times.
148 BOOST_ASSERT(impl_);
896 148 impl_->stream_close();
897 148 }
898
899 } // http
900 } // boost
901