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