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/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 <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 : system::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 : capy::circular_dynamic_buffer out_;
297 : capy::circular_dynamic_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 : BOOST_HTTP_RETURN_EC(
355 : error::expect_100_continue);
356 : }
357 :
358 384 : if(!filter_)
359 : {
360 384 : switch(style_)
361 : {
362 6 : case style::empty:
363 6 : break;
364 :
365 378 : case style::stream:
366 378 : if(out_.size() == 0 && is_header_done() && more_input_)
367 118 : BOOST_HTTP_RETURN_EC(
368 : error::need_data);
369 260 : break;
370 : }
371 : }
372 : else // filter
373 : {
374 MIS 0 : switch(style_)
375 : {
376 0 : case style::empty:
377 : {
378 0 : if(out_capacity() == 0 || filter_done_)
379 0 : break;
380 :
381 0 : const auto rs = filter_->process(
382 0 : detail::make_span(out_prepare()),
383 : {}, // empty input
384 : false);
385 :
386 0 : if(rs.ec)
387 : {
388 0 : ws_.clear();
389 0 : state_ = state::reset;
390 0 : return rs.ec;
391 : }
392 :
393 0 : out_commit(rs.out_bytes);
394 :
395 0 : if(rs.finished)
396 : {
397 0 : filter_done_ = true;
398 0 : out_finish();
399 : }
400 :
401 0 : break;
402 : }
403 :
404 0 : case style::stream:
405 : {
406 0 : if(out_capacity() == 0 || filter_done_)
407 0 : break;
408 :
409 0 : const auto rs = filter_->process(
410 0 : detail::make_span(out_prepare()),
411 : in_.data(),
412 0 : more_input_);
413 :
414 0 : if(rs.ec)
415 : {
416 0 : ws_.clear();
417 0 : state_ = state::reset;
418 0 : return rs.ec;
419 : }
420 :
421 0 : in_.consume(rs.in_bytes);
422 0 : out_commit(rs.out_bytes);
423 :
424 0 : if(rs.finished)
425 : {
426 0 : filter_done_ = true;
427 0 : out_finish();
428 : }
429 :
430 0 : if(out_.size() == 0 && is_header_done() && more_input_)
431 0 : BOOST_HTTP_RETURN_EC(
432 : error::need_data);
433 0 : break;
434 : }
435 : }
436 : }
437 :
438 HIT 266 : prepped_.reset(!is_header_done());
439 798 : for(auto const& cb : out_.data())
440 : {
441 532 : if(cb.size() != 0)
442 168 : prepped_.append(cb);
443 : }
444 266 : return detail::make_span(prepped_);
445 : }
446 :
447 : void
448 1902 : consume(
449 : std::size_t n)
450 : {
451 : // Precondition violation
452 1902 : if(state_ < state::header)
453 1 : detail::throw_logic_error();
454 :
455 1901 : if(!is_header_done())
456 : {
457 : const auto header_remain =
458 132 : prepped_[0].size();
459 132 : if(n < header_remain)
460 : {
461 48 : prepped_.consume(n);
462 48 : return;
463 : }
464 84 : n -= header_remain;
465 84 : prepped_.consume(header_remain);
466 84 : state_ = state::body;
467 : }
468 :
469 1853 : prepped_.consume(n);
470 :
471 : // no-op when out_ is not in use
472 1853 : out_.consume(n);
473 :
474 1853 : if(!prepped_.empty())
475 1692 : return;
476 :
477 161 : if(more_input_)
478 109 : return;
479 :
480 52 : if(filter_ && !filter_done_)
481 MIS 0 : return;
482 :
483 HIT 52 : if(needs_exp100_continue_)
484 2 : return;
485 :
486 : // ready for next message
487 50 : reset();
488 : }
489 :
490 : void
491 159 : start_init(
492 : message_base const& m)
493 : {
494 : // Precondition violation
495 159 : if(state_ != state::start)
496 MIS 0 : detail::throw_logic_error();
497 :
498 : // TODO: To uphold the strong exception guarantee,
499 : // `state_` must be reset to `state::start` if an
500 : // exception is thrown during the start operation.
501 HIT 159 : state_ = state::header;
502 :
503 : // VFALCO what do we do with
504 : // metadata error code failures?
505 : // m.h_.md.maybe_throw();
506 :
507 159 : auto const& md = m.metadata();
508 159 : needs_exp100_continue_ = md.expect.is_100_continue;
509 :
510 : // Transfer-Encoding
511 159 : is_chunked_ = md.transfer_encoding.is_chunked;
512 :
513 : // Content-Encoding
514 159 : switch (md.content_encoding.coding)
515 : {
516 MIS 0 : case content_coding::deflate:
517 0 : if(!cfg_->apply_deflate_encoder)
518 0 : goto no_filter;
519 0 : if(auto* svc = capy::get_system_context().find_service<http::zlib::deflate_service>())
520 : {
521 0 : filter_.reset(new zlib_filter(
522 : *svc,
523 0 : cfg_->zlib_comp_level,
524 0 : cfg_->zlib_window_bits,
525 0 : cfg_->zlib_mem_level));
526 0 : filter_done_ = false;
527 : }
528 0 : break;
529 :
530 0 : case content_coding::gzip:
531 0 : if(!cfg_->apply_gzip_encoder)
532 0 : goto no_filter;
533 0 : if(auto* svc = capy::get_system_context().find_service<http::zlib::deflate_service>())
534 : {
535 0 : filter_.reset(new zlib_filter(
536 : *svc,
537 0 : cfg_->zlib_comp_level,
538 0 : cfg_->zlib_window_bits + 16,
539 0 : cfg_->zlib_mem_level));
540 0 : filter_done_ = false;
541 : }
542 0 : break;
543 :
544 0 : case content_coding::br:
545 0 : if(!cfg_->apply_brotli_encoder)
546 0 : goto no_filter;
547 0 : if(auto* svc = capy::get_system_context().find_service<http::brotli::encode_service>())
548 : {
549 0 : filter_.reset(new brotli_filter(
550 : *svc,
551 0 : cfg_->brotli_comp_quality,
552 0 : cfg_->brotli_comp_window));
553 0 : filter_done_ = false;
554 : }
555 0 : break;
556 :
557 0 : no_filter:
558 HIT 159 : default:
559 159 : filter_.reset();
560 159 : break;
561 : }
562 159 : }
563 :
564 : void
565 6 : start_empty(
566 : message_base const& m)
567 : {
568 6 : start_init(m);
569 6 : style_ = style::empty;
570 :
571 6 : prepped_ = make_array(
572 : 1 + // header
573 : 2); // out buffer pairs
574 :
575 6 : out_init();
576 :
577 6 : if(!filter_)
578 6 : out_finish();
579 :
580 6 : prepped_.append({ m.h_.cbuf, m.h_.size });
581 6 : more_input_ = false;
582 6 : }
583 :
584 : void
585 78 : start_stream(message_base const& m)
586 : {
587 78 : start_init(m);
588 78 : style_ = style::stream;
589 :
590 78 : prepped_ = make_array(
591 : 1 + // header
592 : 2); // out buffer pairs
593 :
594 78 : if(filter_)
595 : {
596 : // TODO: smarter buffer distribution
597 MIS 0 : auto const n = (ws_.size() - 1) / 2;
598 0 : in_ = { ws_.reserve_front(n), n };
599 : }
600 :
601 HIT 78 : out_init();
602 :
603 78 : prepped_.append({ m.h_.cbuf, m.h_.size });
604 78 : more_input_ = true;
605 78 : }
606 :
607 : // Like start_stream but without in_ allocation.
608 : // Entire workspace is used for output buffering.
609 : void
610 75 : start_buffers_direct(message_base const& m)
611 : {
612 75 : start_init(m);
613 75 : style_ = style::stream;
614 :
615 75 : prepped_ = make_array(
616 : 1 + // header
617 : 2); // out buffer pairs
618 :
619 75 : out_init();
620 :
621 75 : prepped_.append({ m.h_.cbuf, m.h_.size });
622 75 : more_input_ = true;
623 75 : }
624 :
625 : std::size_t
626 182 : stream_capacity() const
627 : {
628 182 : if(filter_)
629 MIS 0 : return in_.capacity();
630 HIT 182 : return out_capacity();
631 : }
632 :
633 : std::array<capy::mutable_buffer, 2>
634 129 : stream_prepare()
635 : {
636 129 : if(state_ == state::start)
637 : {
638 MIS 0 : if(!msg_)
639 0 : detail::throw_logic_error();
640 0 : start_stream(*msg_);
641 : }
642 HIT 129 : if(filter_)
643 MIS 0 : return in_.prepare(in_.capacity());
644 HIT 129 : return out_prepare();
645 : }
646 :
647 : void
648 165 : stream_commit(std::size_t n)
649 : {
650 165 : if(n > stream_capacity())
651 1 : detail::throw_invalid_argument();
652 :
653 164 : if(filter_)
654 MIS 0 : return in_.commit(n);
655 :
656 HIT 164 : out_commit(n);
657 : }
658 :
659 : void
660 75 : stream_close() noexcept
661 : {
662 75 : if(!filter_)
663 75 : out_finish();
664 :
665 75 : more_input_ = false;
666 75 : }
667 :
668 : bool
669 464 : is_done() const noexcept
670 : {
671 464 : return state_ == state::start;
672 : }
673 :
674 : bool
675 249 : is_start() const noexcept
676 : {
677 249 : return state_ == state::start;
678 : }
679 :
680 : detail::workspace&
681 MIS 0 : ws() noexcept
682 : {
683 0 : return ws_;
684 : }
685 :
686 : private:
687 : bool
688 HIT 2384 : is_header_done() const noexcept
689 : {
690 2384 : return state_ == state::body;
691 : }
692 :
693 : detail::array_of_const_buffers
694 159 : make_array(std::size_t n)
695 : {
696 159 : BOOST_ASSERT(n <= std::uint16_t(-1));
697 :
698 : return {
699 159 : ws_.push_array(n,
700 MIS 0 : capy::const_buffer{}),
701 HIT 159 : static_cast<std::uint16_t>(n) };
702 : }
703 :
704 : void
705 159 : out_init()
706 : {
707 : // use all the remaining buffer
708 159 : auto const n = ws_.size() - 1;
709 159 : out_ = { ws_.reserve_front(n), n };
710 159 : chunk_header_len_ =
711 159 : chunk_header_len(out_.capacity());
712 159 : if(out_capacity() == 0)
713 MIS 0 : detail::throw_length_error();
714 HIT 159 : }
715 :
716 : std::array<capy::mutable_buffer, 2>
717 129 : out_prepare() noexcept
718 : {
719 129 : auto mbp = out_.prepare(out_.capacity());
720 129 : if(is_chunked_)
721 : {
722 69 : trim_prefix_pair(
723 69 : mbp, chunk_header_len_);
724 69 : trim_suffix_pair(
725 : mbp, crlf_and_final_chunk.size());
726 : }
727 129 : return mbp;
728 : }
729 :
730 : void
731 164 : out_commit(
732 : std::size_t n) noexcept
733 : {
734 164 : if(is_chunked_)
735 : {
736 87 : if(n == 0)
737 19 : return;
738 :
739 68 : write_chunk_header(out_.prepare(chunk_header_len_), n);
740 68 : out_.commit(chunk_header_len_);
741 :
742 68 : out_.prepare(n);
743 68 : out_.commit(n);
744 :
745 68 : capy::buffer_copy(out_.prepare(crlf.size()), crlf);
746 68 : out_.commit(crlf.size());
747 : }
748 : else
749 : {
750 77 : out_.commit(n);
751 : }
752 : }
753 :
754 : std::size_t
755 341 : out_capacity() const noexcept
756 : {
757 341 : if(is_chunked_)
758 : {
759 174 : auto const overhead = chunk_header_len_ +
760 174 : crlf_and_final_chunk.size();
761 174 : if(out_.capacity() < overhead)
762 1 : return 0;
763 173 : return out_.capacity() - overhead;
764 : }
765 167 : return out_.capacity();
766 : }
767 :
768 : void
769 81 : out_finish() noexcept
770 : {
771 81 : if(is_chunked_)
772 : {
773 41 : capy::buffer_copy(
774 41 : out_.prepare(final_chunk.size()), final_chunk);
775 41 : out_.commit(final_chunk.size());
776 : }
777 81 : }
778 : };
779 :
780 : //------------------------------------------------
781 :
782 163 : serializer::
783 : ~serializer()
784 : {
785 163 : delete impl_;
786 163 : }
787 :
788 1 : serializer::
789 1 : serializer(serializer&& other) noexcept
790 1 : : impl_(other.impl_)
791 : {
792 1 : other.impl_ = nullptr;
793 1 : }
794 :
795 : serializer&
796 2 : serializer::
797 : operator=(serializer&& other) noexcept
798 : {
799 2 : if(this != &other)
800 : {
801 2 : delete impl_;
802 2 : impl_ = other.impl_;
803 2 : other.impl_ = nullptr;
804 : }
805 2 : return *this;
806 : }
807 :
808 158 : serializer::
809 : serializer(
810 158 : std::shared_ptr<serializer_config_impl const> cfg)
811 158 : : impl_(new impl(std::move(cfg)))
812 : {
813 158 : }
814 :
815 : void
816 4 : serializer::
817 : reset() noexcept
818 : {
819 4 : BOOST_ASSERT(impl_);
820 4 : impl_->reset();
821 4 : }
822 :
823 : void
824 159 : serializer::
825 : set_message(message_base const& m) noexcept
826 : {
827 159 : BOOST_ASSERT(impl_);
828 159 : impl_->msg_ = &m;
829 159 : }
830 :
831 : void
832 6 : serializer::
833 : start()
834 : {
835 6 : if(!impl_ || !impl_->msg_)
836 MIS 0 : detail::throw_logic_error();
837 HIT 6 : impl_->start_empty(*impl_->msg_);
838 6 : }
839 :
840 : void
841 MIS 0 : serializer::
842 : start_stream()
843 : {
844 0 : if(!impl_ || !impl_->msg_)
845 0 : detail::throw_logic_error();
846 0 : impl_->start_stream(*impl_->msg_);
847 0 : }
848 :
849 : void
850 HIT 78 : serializer::
851 : start_writes()
852 : {
853 78 : if(!impl_ || !impl_->msg_)
854 MIS 0 : detail::throw_logic_error();
855 HIT 78 : impl_->start_stream(*impl_->msg_);
856 78 : }
857 :
858 : void
859 75 : serializer::
860 : start_buffers()
861 : {
862 75 : if(!impl_ || !impl_->msg_)
863 MIS 0 : detail::throw_logic_error();
864 HIT 75 : impl_->start_buffers_direct(*impl_->msg_);
865 75 : }
866 :
867 : auto
868 389 : serializer::
869 : prepare() ->
870 : system::result<const_buffers_type>
871 : {
872 389 : BOOST_ASSERT(impl_);
873 389 : return impl_->prepare();
874 : }
875 :
876 : void
877 1902 : serializer::
878 : consume(std::size_t n)
879 : {
880 1902 : BOOST_ASSERT(impl_);
881 1902 : impl_->consume(n);
882 1901 : }
883 :
884 : bool
885 464 : serializer::
886 : is_done() const noexcept
887 : {
888 464 : BOOST_ASSERT(impl_);
889 464 : return impl_->is_done();
890 : }
891 :
892 : bool
893 249 : serializer::
894 : is_start() const noexcept
895 : {
896 249 : BOOST_ASSERT(impl_);
897 249 : return impl_->is_start();
898 : }
899 :
900 : //------------------------------------------------
901 :
902 : detail::workspace&
903 MIS 0 : serializer::
904 : ws()
905 : {
906 0 : BOOST_ASSERT(impl_);
907 0 : return impl_->ws();
908 : }
909 :
910 : //------------------------------------------------
911 :
912 : std::size_t
913 HIT 17 : serializer::
914 : stream_capacity() const
915 : {
916 17 : BOOST_ASSERT(impl_);
917 17 : return impl_->stream_capacity();
918 : }
919 :
920 : auto
921 129 : serializer::
922 : stream_prepare() ->
923 : mutable_buffers_type
924 : {
925 129 : BOOST_ASSERT(impl_);
926 129 : return impl_->stream_prepare();
927 : }
928 :
929 : void
930 165 : serializer::
931 : stream_commit(std::size_t n)
932 : {
933 165 : BOOST_ASSERT(impl_);
934 165 : impl_->stream_commit(n);
935 164 : }
936 :
937 : void
938 75 : serializer::
939 : stream_close() noexcept
940 : {
941 75 : BOOST_ASSERT(impl_);
942 75 : impl_->stream_close();
943 75 : }
944 :
945 : } // http
946 : } // boost
|