src/serializer.cpp

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