LCOV - code coverage report
Current view: top level - src - serializer.cpp (source / functions) Coverage Total Hit
Test: coverage_remapped.info Lines: 63.3 % 373 236
Test Date: 2026-02-09 01:37:05 Functions: 80.0 % 50 40

            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 <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          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           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            0 :     zlib_filter(
      96              :         http::zlib::deflate_service& svc,
      97              :         int comp_level,
      98              :         int window_bits,
      99              :         int mem_level)
     100            0 :         : svc_(svc)
     101              :     {
     102            0 :         system::error_code ec = static_cast<http::zlib::error>(svc_.init2(
     103            0 :             strm_,
     104              :             comp_level,
     105              :             http::zlib::deflated,
     106              :             window_bits,
     107              :             mem_level,
     108            0 :             http::zlib::default_strategy));
     109            0 :         if(ec != http::zlib::error::ok)
     110            0 :             detail::throw_system_error(ec);
     111            0 :     }
     112              : 
     113              : private:
     114              :     virtual
     115              :     std::size_t
     116            0 :     min_out_buffer() const noexcept override
     117              :     {
     118            0 :         return 8;
     119              :     }
     120              : 
     121              :     virtual
     122              :     results
     123            0 :     do_process(
     124              :         capy::mutable_buffer out,
     125              :         capy::const_buffer in,
     126              :         bool more) noexcept override
     127              :     {
     128            0 :         strm_.next_out  = static_cast<unsigned char*>(out.data());
     129            0 :         strm_.avail_out = saturate_cast(out.size());
     130            0 :         strm_.next_in   = static_cast<unsigned char*>(const_cast<void *>(in.data()));
     131            0 :         strm_.avail_in  = saturate_cast(in.size());
     132              : 
     133              :         auto rs = static_cast<http::zlib::error>(
     134            0 :             svc_.deflate(
     135            0 :                 strm_,
     136              :                 more ? http::zlib::no_flush : http::zlib::finish));
     137              : 
     138            0 :         results rv;
     139            0 :         rv.out_bytes = saturate_cast(out.size()) - strm_.avail_out;
     140            0 :         rv.in_bytes  = saturate_cast(in.size()) - strm_.avail_in;
     141            0 :         rv.finished  = (rs == http::zlib::error::stream_end);
     142              : 
     143            0 :         if(rs < http::zlib::error::ok && rs != http::zlib::error::buf_err)
     144            0 :             rv.ec = rs;
     145              : 
     146            0 :         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            0 :     brotli_filter(
     158              :         http::brotli::encode_service& svc,
     159              :         std::uint32_t comp_quality,
     160              :         std::uint32_t comp_window)
     161            0 :         : svc_(svc)
     162              :     {
     163            0 :         state_ = svc_.create_instance(nullptr, nullptr, nullptr);
     164            0 :         if(!state_)
     165            0 :             detail::throw_bad_alloc();
     166              :         using encoder_parameter = http::brotli::encoder_parameter;
     167            0 :         svc_.set_parameter(state_, encoder_parameter::quality, comp_quality);
     168            0 :         svc_.set_parameter(state_, encoder_parameter::lgwin, comp_window);
     169            0 :     }
     170              : 
     171            0 :     ~brotli_filter()
     172            0 :     {
     173            0 :         svc_.destroy_instance(state_);
     174            0 :     }
     175              : 
     176              : private:
     177              :     virtual
     178              :     results
     179            0 :     do_process(
     180              :         capy::mutable_buffer out,
     181              :         capy::const_buffer in,
     182              :         bool more) noexcept override
     183              :     {
     184            0 :         auto* next_in = reinterpret_cast<const std::uint8_t*>(in.data());
     185            0 :         auto available_in = in.size();
     186            0 :         auto* next_out = reinterpret_cast<std::uint8_t*>(out.data());
     187            0 :         auto available_out = out.size();
     188              : 
     189              :         using encoder_operation = 
     190              :             http::brotli::encoder_operation;
     191              : 
     192            0 :         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            0 :         results rv;
     202            0 :         rv.in_bytes  = in.size()  - available_in;
     203            0 :         rv.out_bytes = out.size() - available_out;
     204            0 :         rv.finished  = svc_.is_finished(state_);
     205              : 
     206            0 :         if(rs == false)
     207            0 :             rv.ec = error::bad_payload;
     208              : 
     209            0 :         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          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          512 :         if(state_ < state::header)
     296            1 :             detail::throw_logic_error();
     297              : 
     298              :         // Expect: 100-continue
     299          511 :         if(needs_exp100_continue_)
     300              :         {
     301            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          507 :         if(!filter_)
     313              :         {
     314          507 :             switch(style_)
     315              :             {
     316            6 :             case style::empty:
     317            6 :                 break;
     318              : 
     319          501 :             case style::stream:
     320          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            0 :             switch(style_)
     329              :             {
     330            0 :             case style::empty:
     331              :             {
     332            0 :                 if(out_capacity() == 0 || filter_done_)
     333            0 :                     break;
     334              : 
     335            0 :                 const auto rs = filter_->process(
     336            0 :                     detail::make_span(out_prepare()),
     337              :                     {}, // empty input
     338              :                     false);
     339              : 
     340            0 :                 if(rs.ec)
     341              :                 {
     342            0 :                     ws_.clear();
     343            0 :                     state_ = state::reset;
     344            0 :                     return rs.ec;
     345              :                 }
     346              : 
     347            0 :                 out_commit(rs.out_bytes);
     348              : 
     349            0 :                 if(rs.finished)
     350              :                 {
     351            0 :                     filter_done_ = true;
     352            0 :                     out_finish();
     353              :                 }
     354              : 
     355            0 :                 break;
     356              :             }
     357              : 
     358            0 :             case style::stream:
     359              :             {
     360            0 :                 if(out_capacity() == 0 || filter_done_)
     361            0 :                     break;
     362              : 
     363            0 :                 const auto rs = filter_->process(
     364            0 :                     detail::make_span(out_prepare()),
     365              :                     in_.data(),
     366            0 :                     more_input_);
     367              : 
     368            0 :                 if(rs.ec)
     369              :                 {
     370            0 :                     ws_.clear();
     371            0 :                     state_ = state::reset;
     372            0 :                     return rs.ec;
     373              :                 }
     374              : 
     375            0 :                 in_.consume(rs.in_bytes);
     376            0 :                 out_commit(rs.out_bytes);
     377              : 
     378            0 :                 if(rs.finished)
     379              :                 {
     380            0 :                     filter_done_ = true;
     381            0 :                     out_finish();
     382              :                 }
     383              : 
     384            0 :                 if(out_.size() == 0 && is_header_done() && more_input_)
     385            0 :                     BOOST_HTTP_RETURN_EC(
     386              :                         error::need_data);
     387            0 :                 break;
     388              :             }
     389              :             }
     390              :         }
     391              : 
     392          370 :         prepped_.reset(!is_header_done());
     393         1110 :         for(auto const& cb : out_.data())
     394              :         {
     395          740 :             if(cb.size() != 0)
     396          211 :                 prepped_.append(cb);
     397              :         }
     398          370 :         return detail::make_span(prepped_);
     399              :     }
     400              : 
     401              :     void
     402         1996 :     consume(
     403              :         std::size_t n)
     404              :     {
     405              :         // Precondition violation
     406         1996 :         if(state_ < state::header)
     407            1 :             detail::throw_logic_error();
     408              : 
     409         1995 :         if(!is_header_done())
     410              :         {
     411              :             const auto header_remain =
     412          207 :                 prepped_[0].size();
     413          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         1917 :         if(!prepped_.empty())
     429         1692 :             return;
     430              : 
     431          225 :         if(more_input_)
     432          128 :             return;
     433              : 
     434           97 :         if(filter_ && !filter_done_)
     435            0 :             return;
     436              : 
     437           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          224 :         if(state_ != state::start)
     450            0 :             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          224 :         switch (md.content_encoding.coding)
     469              :         {
     470            0 :         case content_coding::deflate:
     471            0 :             if(!cfg_->apply_deflate_encoder)
     472            0 :                 goto no_filter;
     473            0 :             if(auto* svc = capy::get_system_context().find_service<http::zlib::deflate_service>())
     474              :             {
     475            0 :                 filter_.reset(new zlib_filter(
     476              :                     *svc,
     477            0 :                     cfg_->zlib_comp_level,
     478            0 :                     cfg_->zlib_window_bits,
     479            0 :                     cfg_->zlib_mem_level));
     480            0 :                 filter_done_ = false;
     481              :             }
     482            0 :             break;
     483              : 
     484            0 :         case content_coding::gzip:
     485            0 :             if(!cfg_->apply_gzip_encoder)
     486            0 :                 goto no_filter;
     487            0 :             if(auto* svc = capy::get_system_context().find_service<http::zlib::deflate_service>())
     488              :             {
     489            0 :                 filter_.reset(new zlib_filter(
     490              :                     *svc,
     491            0 :                     cfg_->zlib_comp_level,
     492            0 :                     cfg_->zlib_window_bits + 16,
     493            0 :                     cfg_->zlib_mem_level));
     494            0 :                 filter_done_ = false;
     495              :             }
     496            0 :             break;
     497              : 
     498            0 :         case content_coding::br:
     499            0 :             if(!cfg_->apply_brotli_encoder)
     500            0 :                 goto no_filter;
     501            0 :             if(auto* svc = capy::get_system_context().find_service<http::brotli::encode_service>())
     502              :             {
     503            0 :                 filter_.reset(new brotli_filter(
     504              :                     *svc,
     505            0 :                     cfg_->brotli_comp_quality,
     506            0 :                     cfg_->brotli_comp_window));
     507            0 :                 filter_done_ = false;
     508              :             }
     509            0 :             break;
     510              : 
     511            0 :         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            6 :         prepped_ = make_array(
     526              :             1 + // header
     527              :             2); // out buffer pairs
     528              : 
     529            6 :         out_init();
     530              : 
     531            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          143 :         prepped_ = make_array(
     545              :             1 + // header
     546              :             2); // out buffer pairs
     547              : 
     548          143 :         if(filter_)
     549              :         {
     550              :             // TODO: smarter buffer distribution
     551            0 :             auto const n = (ws_.size() - 1) / 2;
     552            0 :             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           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          286 :         if(filter_)
     583            0 :             return in_.capacity();
     584          286 :         return out_capacity();
     585              :     }
     586              : 
     587              :     capy::mutable_buffer_pair
     588          160 :     stream_prepare()
     589              :     {
     590          160 :         if(state_ == state::start)
     591              :         {
     592            0 :             if(!msg_)
     593            0 :                 detail::throw_logic_error();
     594            0 :             start_stream(*msg_);
     595              :         }
     596          160 :         if(filter_)
     597            0 :             return in_.prepare(in_.capacity());
     598          160 :         return out_prepare();
     599              :     }
     600              : 
     601              :     void
     602          269 :     stream_commit(std::size_t n)
     603              :     {
     604          269 :         if(n > stream_capacity())
     605            1 :             detail::throw_invalid_argument();
     606              : 
     607          268 :         if(filter_)
     608            0 :             return in_.commit(n);
     609              : 
     610          268 :         out_commit(n);
     611              :     }
     612              : 
     613              :     void
     614          148 :     stream_close() noexcept
     615              :     {
     616          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            0 :     ws() noexcept
     636              :     {
     637            0 :         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          224 :         BOOST_ASSERT(n <= std::uint16_t(-1));
     651              : 
     652              :         return {
     653          224 :             ws_.push_array(n,
     654            0 :                 capy::const_buffer{}),
     655          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          224 :         out_ = { ws_.reserve_front(n), n };
     664          224 :         chunk_header_len_ =
     665          224 :             chunk_header_len(out_.capacity());
     666          224 :         if(out_capacity() == 0)
     667            0 :             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          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          268 :         if(is_chunked_)
     689              :         {
     690           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          510 :         if(is_chunked_)
     712              :         {
     713          174 :             auto const overhead = chunk_header_len_ +
     714          174 :                 crlf_and_final_chunk.size();
     715          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          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          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            2 :     if(this != &other)
     754              :     {
     755            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          224 :     : impl_(new impl(std::move(cfg)))
     766              : {
     767          224 : }
     768              : 
     769              : void
     770           70 : serializer::
     771              : reset() noexcept
     772              : {
     773           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          225 :     BOOST_ASSERT(impl_);
     782          225 :     impl_->msg_ = &m;
     783          225 : }
     784              : 
     785              : void
     786            6 : serializer::
     787              : start()
     788              : {
     789            6 :     if(!impl_ || !impl_->msg_)
     790            0 :         detail::throw_logic_error();
     791            6 :     impl_->start_empty(*impl_->msg_);
     792            6 : }
     793              : 
     794              : void
     795            0 : serializer::
     796              : start_stream()
     797              : {
     798            0 :     if(!impl_ || !impl_->msg_)
     799            0 :         detail::throw_logic_error();
     800            0 :     impl_->start_stream(*impl_->msg_);
     801            0 : }
     802              : 
     803              : void
     804          143 : serializer::
     805              : start_writes()
     806              : {
     807          143 :     if(!impl_ || !impl_->msg_)
     808            0 :         detail::throw_logic_error();
     809          143 :     impl_->start_stream(*impl_->msg_);
     810          143 : }
     811              : 
     812              : void
     813           75 : serializer::
     814              : start_buffers()
     815              : {
     816           75 :     if(!impl_ || !impl_->msg_)
     817            0 :         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          512 :     BOOST_ASSERT(impl_);
     827          512 :     return impl_->prepare();
     828              : }
     829              : 
     830              : void
     831         1996 : serializer::
     832              : consume(std::size_t n)
     833              : {
     834         1996 :     BOOST_ASSERT(impl_);
     835         1996 :     impl_->consume(n);
     836         1995 : }
     837              : 
     838              : bool
     839          632 : serializer::
     840              : is_done() const noexcept
     841              : {
     842          632 :     BOOST_ASSERT(impl_);
     843          632 :     return impl_->is_done();
     844              : }
     845              : 
     846              : bool
     847          384 : serializer::
     848              : is_start() const noexcept
     849              : {
     850          384 :     BOOST_ASSERT(impl_);
     851          384 :     return impl_->is_start();
     852              : }
     853              : 
     854              : //------------------------------------------------
     855              : 
     856              : detail::workspace&
     857            0 : serializer::
     858              : ws()
     859              : {
     860            0 :     BOOST_ASSERT(impl_);
     861            0 :     return impl_->ws();
     862              : }
     863              : 
     864              : //------------------------------------------------
     865              : 
     866              : std::size_t
     867           17 : serializer::
     868              : stream_capacity() const
     869              : {
     870           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          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          269 :     BOOST_ASSERT(impl_);
     888          269 :     impl_->stream_commit(n);
     889          268 : }
     890              : 
     891              : void
     892          148 : serializer::
     893              : stream_close() noexcept
     894              : {
     895          148 :     BOOST_ASSERT(impl_);
     896          148 :     impl_->stream_close();
     897          148 : }
     898              : 
     899              : } // http
     900              : } // boost
        

Generated by: LCOV version 2.3