LCOV - code coverage report
Current view: top level - libs/http/src - serializer.cpp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 60.5 % 440 266
Test Date: 2026-02-02 17:02:49 Functions: 77.4 % 53 41

            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           82 : chunk_header_len(
      57              :     std::size_t max_chunk_size) noexcept
      58              : {
      59              :     return
      60              :         static_cast<uint8_t>(
      61           82 :             (core::bit_width(max_chunk_size) + 3) / 4 +
      62           82 :             2); // crlf
      63              : };
      64              : 
      65              : void
      66           35 : 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           35 :     auto p = buf + 16;
      74           35 :     auto const n = capy::buffer_size(mbs);
      75          174 :     for(std::size_t i = n - 2; i--;)
      76              :     {
      77          139 :         *--p = hexdig[size & 0xf];
      78          139 :         size >>= 4;
      79              :     }
      80           35 :     buf[16] = '\r';
      81           35 :     buf[17] = '\n';
      82           35 :     auto copied = capy::buffer_copy(
      83              :         mbs,
      84           70 :         capy::const_buffer(p, n));
      85              :     ignore_unused(copied);
      86           35 :     BOOST_ASSERT(copied == n);
      87           35 : }
      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           10 : clamp(
     216              :     UInt x,
     217              :     std::size_t limit = (std::numeric_limits<
     218              :         std::size_t>::max)()) noexcept
     219              : {
     220           10 :     if(x >= limit)
     221            2 :         return limit;
     222            8 :     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              :         buffers,
     243              :         stream
     244              :     };
     245              : 
     246              :     std::shared_ptr<serializer_config_impl const> cfg_;
     247              :     detail::workspace ws_;
     248              : 
     249              :     std::unique_ptr<detail::filter> filter_;
     250              :     cbs_gen* cbs_gen_ = nullptr;
     251              : 
     252              :     capy::circular_dynamic_buffer out_;
     253              :     capy::circular_dynamic_buffer in_;
     254              :     detail::array_of_const_buffers prepped_;
     255              :     capy::const_buffer tmp_;
     256              : 
     257              :     state state_ = state::start;
     258              :     style style_ = style::empty;
     259              :     uint8_t chunk_header_len_ = 0;
     260              :     bool more_input_ = false;
     261              :     bool is_chunked_ = false;
     262              :     bool needs_exp100_continue_ = false;
     263              :     bool filter_done_ = false;
     264              : 
     265              : public:
     266              :     message_base const* msg_ = nullptr;
     267              : 
     268              :     explicit
     269           87 :     impl(std::shared_ptr<serializer_config_impl const> cfg)
     270           87 :         : cfg_(std::move(cfg))
     271           87 :         , ws_(cfg_->space_needed)
     272              :     {
     273           87 :     }
     274              : 
     275            0 :     impl(
     276              :         std::shared_ptr<serializer_config_impl const> cfg,
     277              :         message_base const& msg)
     278            0 :         : cfg_(std::move(cfg))
     279            0 :         , ws_(cfg_->space_needed)
     280            0 :         , msg_(&msg)
     281              :     {
     282            0 :     }
     283              : 
     284              :     void
     285           43 :     reset() noexcept
     286              :     {
     287           43 :         filter_.reset();
     288           43 :         ws_.clear();
     289           43 :         state_ = state::start;
     290           43 :     }
     291              : 
     292              :     auto
     293          170 :     prepare() ->
     294              :         system::result<const_buffers_type>
     295              :     {
     296              :         // Precondition violation
     297          170 :         if(state_ < state::header)
     298            1 :             detail::throw_logic_error();
     299              : 
     300              :         // Expect: 100-continue
     301          169 :         if(needs_exp100_continue_)
     302              :         {
     303            4 :             if(!is_header_done())
     304            4 :                 return const_buffers_type(
     305              :                     prepped_.begin(),
     306            2 :                     1); // limit to header
     307              : 
     308            2 :             needs_exp100_continue_ = false;
     309              : 
     310            2 :             BOOST_HTTP_RETURN_EC(
     311              :                 error::expect_100_continue);
     312              :         }
     313              : 
     314          165 :         if(!filter_)
     315              :         {
     316          165 :             switch(style_)
     317              :             {
     318            6 :             case style::empty:
     319            6 :                 break;
     320              : 
     321           22 :             case style::buffers:
     322              :             {
     323              :                 // add more buffers if prepped_ is half empty.
     324           34 :                 if(more_input_ &&
     325           12 :                     prepped_.capacity() >= prepped_.size())
     326              :                 {
     327            6 :                     prepped_.slide_to_front();
     328           54 :                     while(prepped_.capacity() != 0)
     329              :                     {
     330           51 :                         auto buf = cbs_gen_->next();
     331           51 :                         if(buf.size() == 0)
     332            3 :                             break;
     333           48 :                         prepped_.append(buf);
     334              :                     }
     335            6 :                     if(cbs_gen_->is_empty())
     336              :                     {
     337            4 :                         if(is_chunked_)
     338              :                         {
     339            1 :                             if(prepped_.capacity() != 0)
     340              :                             {
     341            1 :                                 prepped_.append(
     342              :                                     crlf_and_final_chunk);
     343            1 :                                 more_input_ = false;
     344              :                             }
     345              :                         }
     346              :                         else
     347              :                         {
     348            3 :                             more_input_ = false;
     349              :                         }
     350              :                     }
     351              :                 }
     352           22 :                 return detail::make_span(prepped_);
     353              :             }
     354              : 
     355          137 :             case style::stream:
     356          137 :                 if(out_.size() == 0 && is_header_done() && more_input_)
     357           31 :                     BOOST_HTTP_RETURN_EC(
     358              :                         error::need_data);
     359          106 :                 break;
     360              :             }
     361              :         }
     362              :         else // filter
     363              :         {
     364            0 :             switch(style_)
     365              :             {
     366            0 :             case style::empty:
     367              :             {
     368            0 :                 if(out_capacity() == 0 || filter_done_)
     369            0 :                     break;
     370              : 
     371            0 :                 const auto rs = filter_->process(
     372            0 :                     detail::make_span(out_prepare()),
     373              :                     {}, // empty input
     374              :                     false);
     375              : 
     376            0 :                 if(rs.ec)
     377              :                 {
     378            0 :                     ws_.clear();
     379            0 :                     state_ = state::reset;
     380            0 :                     return rs.ec;
     381              :                 }
     382              : 
     383            0 :                 out_commit(rs.out_bytes);
     384              : 
     385            0 :                 if(rs.finished)
     386              :                 {
     387            0 :                     filter_done_ = true;
     388            0 :                     out_finish();
     389              :                 }
     390              : 
     391            0 :                 break;
     392              :             }
     393              : 
     394            0 :             case style::buffers:
     395              :             {
     396            0 :                 while(out_capacity() != 0 && !filter_done_)
     397              :                 {
     398            0 :                     if(more_input_ && tmp_.size() == 0)
     399              :                     {
     400            0 :                         tmp_ = cbs_gen_->next();
     401            0 :                         if(tmp_.size() == 0) // cbs_gen_ is empty
     402            0 :                             more_input_ = false;
     403              :                     }
     404              : 
     405            0 :                     const auto rs = filter_->process(
     406            0 :                         detail::make_span(out_prepare()),
     407              :                         {{ {tmp_}, {} }},
     408            0 :                         more_input_);
     409              : 
     410            0 :                     if(rs.ec)
     411              :                     {
     412            0 :                         ws_.clear();
     413            0 :                         state_ = state::reset;
     414            0 :                         return rs.ec;
     415              :                     }
     416              : 
     417            0 :                     capy::remove_prefix(tmp_, rs.in_bytes);
     418            0 :                     out_commit(rs.out_bytes);
     419              : 
     420            0 :                     if(rs.out_short)
     421            0 :                         break;
     422              : 
     423            0 :                     if(rs.finished)
     424              :                     {
     425            0 :                         filter_done_ = true;
     426            0 :                         out_finish();
     427              :                     }
     428              :                 }
     429            0 :                 break;
     430              :             }
     431              : 
     432            0 :             case style::stream:
     433              :             {
     434            0 :                 if(out_capacity() == 0 || filter_done_)
     435            0 :                     break;
     436              : 
     437            0 :                 const auto rs = filter_->process(
     438            0 :                     detail::make_span(out_prepare()),
     439              :                     in_.data(),
     440            0 :                     more_input_);
     441              : 
     442            0 :                 if(rs.ec)
     443              :                 {
     444            0 :                     ws_.clear();
     445            0 :                     state_ = state::reset;
     446            0 :                     return rs.ec;
     447              :                 }
     448              : 
     449            0 :                 in_.consume(rs.in_bytes);
     450            0 :                 out_commit(rs.out_bytes);
     451              : 
     452            0 :                 if(rs.finished)
     453              :                 {
     454            0 :                     filter_done_ = true;
     455            0 :                     out_finish();
     456              :                 }
     457              : 
     458            0 :                 if(out_.size() == 0 && is_header_done() && more_input_)
     459            0 :                     BOOST_HTTP_RETURN_EC(
     460              :                         error::need_data);
     461            0 :                 break;
     462              :             }
     463              :             }
     464              :         }
     465              : 
     466          112 :         prepped_.reset(!is_header_done());
     467          336 :         for(auto const& cb : out_.data())
     468              :         {
     469          224 :             if(cb.size() != 0)
     470           89 :                 prepped_.append(cb);
     471              :         }
     472          112 :         return detail::make_span(prepped_);
     473              :     }
     474              : 
     475              :     void
     476         1848 :     consume(
     477              :         std::size_t n)
     478              :     {
     479              :         // Precondition violation
     480         1848 :         if(state_ < state::header)
     481            1 :             detail::throw_logic_error();
     482              : 
     483         1847 :         if(!is_header_done())
     484              :         {
     485              :             const auto header_remain =
     486           77 :                 prepped_[0].size();
     487           77 :             if(n < header_remain)
     488              :             {
     489           32 :                 prepped_.consume(n);
     490           32 :                 return;
     491              :             }
     492           45 :             n -= header_remain;
     493           45 :             prepped_.consume(header_remain);
     494           45 :             state_ = state::body;
     495              :         }
     496              : 
     497         1815 :         prepped_.consume(n);
     498              : 
     499              :         // no-op when out_ is not in use
     500         1815 :         out_.consume(n);
     501              : 
     502         1815 :         if(!prepped_.empty())
     503         1744 :             return;
     504              : 
     505           71 :         if(more_input_)
     506           33 :             return;
     507              : 
     508           38 :         if(filter_ && !filter_done_)
     509            0 :             return;
     510              : 
     511           38 :         if(needs_exp100_continue_)
     512            1 :             return;
     513              : 
     514              :         // ready for next message
     515           37 :         reset();
     516              :     }
     517              : 
     518              :     void
     519           91 :     start_init(
     520              :         message_base const& m)
     521              :     {
     522              :         // Precondition violation
     523           91 :         if(state_ != state::start)
     524            0 :             detail::throw_logic_error();
     525              : 
     526              :         // TODO: To uphold the strong exception guarantee,
     527              :         // `state_` must be reset to `state::start` if an
     528              :         // exception is thrown during the start operation.
     529           91 :         state_ = state::header;
     530              : 
     531              :         // VFALCO what do we do with
     532              :         // metadata error code failures?
     533              :         // m.h_.md.maybe_throw();
     534              : 
     535           91 :         auto const& md = m.metadata();
     536           91 :         needs_exp100_continue_ = md.expect.is_100_continue;
     537              : 
     538              :         // Transfer-Encoding
     539           91 :         is_chunked_ = md.transfer_encoding.is_chunked;
     540              : 
     541              :         // Content-Encoding
     542           91 :         switch (md.content_encoding.coding)
     543              :         {
     544            0 :         case content_coding::deflate:
     545            0 :             if(!cfg_->apply_deflate_encoder)
     546            0 :                 goto no_filter;
     547            0 :             if(auto* svc = capy::get_system_context().find_service<http::zlib::deflate_service>())
     548              :             {
     549            0 :                 filter_.reset(new zlib_filter(
     550              :                     *svc,
     551            0 :                     cfg_->zlib_comp_level,
     552            0 :                     cfg_->zlib_window_bits,
     553            0 :                     cfg_->zlib_mem_level));
     554            0 :                 filter_done_ = false;
     555              :             }
     556            0 :             break;
     557              : 
     558            0 :         case content_coding::gzip:
     559            0 :             if(!cfg_->apply_gzip_encoder)
     560            0 :                 goto no_filter;
     561            0 :             if(auto* svc = capy::get_system_context().find_service<http::zlib::deflate_service>())
     562              :             {
     563            0 :                 filter_.reset(new zlib_filter(
     564              :                     *svc,
     565            0 :                     cfg_->zlib_comp_level,
     566            0 :                     cfg_->zlib_window_bits + 16,
     567            0 :                     cfg_->zlib_mem_level));
     568            0 :                 filter_done_ = false;
     569              :             }
     570            0 :             break;
     571              : 
     572            0 :         case content_coding::br:
     573            0 :             if(!cfg_->apply_brotli_encoder)
     574            0 :                 goto no_filter;
     575            0 :             if(auto* svc = capy::get_system_context().find_service<http::brotli::encode_service>())
     576              :             {
     577            0 :                 filter_.reset(new brotli_filter(
     578              :                     *svc,
     579            0 :                     cfg_->brotli_comp_quality,
     580            0 :                     cfg_->brotli_comp_window));
     581            0 :                 filter_done_ = false;
     582              :             }
     583            0 :             break;
     584              : 
     585            0 :         no_filter:
     586           91 :         default:
     587           91 :             filter_.reset();
     588           91 :             break;
     589              :         }
     590           91 :     }
     591              : 
     592              :     void
     593            6 :     start_empty(
     594              :         message_base const& m)
     595              :     {
     596            6 :         start_init(m);
     597            6 :         style_ = style::empty;
     598              : 
     599            6 :         prepped_ = make_array(
     600              :             1 + // header
     601              :             2); // out buffer pairs
     602              : 
     603            6 :         out_init();
     604              : 
     605            6 :         if(!filter_)
     606            6 :             out_finish();
     607              : 
     608            6 :         prepped_.append({ m.h_.cbuf, m.h_.size });
     609            6 :         more_input_ = false;
     610            6 :     }
     611              : 
     612              :     void
     613           10 :     start_buffers(
     614              :         message_base const& m,
     615              :         cbs_gen& cbs_gen)
     616              :     {
     617              :         // start_init() already called 
     618           10 :         style_ = style::buffers;
     619           10 :         cbs_gen_ = &cbs_gen;
     620              : 
     621           10 :         if(!filter_)
     622              :         {
     623           10 :             auto stats = cbs_gen_->stats();
     624           10 :             auto batch_size = clamp(stats.count, 16);
     625              : 
     626            0 :             prepped_ = make_array(
     627              :                 1 + // header
     628           10 :                 batch_size + // buffers
     629           10 :                 (is_chunked_ ? 2 : 0)); // chunk header + final chunk
     630              : 
     631           10 :             prepped_.append({ m.h_.cbuf, m.h_.size });
     632           10 :             more_input_ = (batch_size != 0);
     633              : 
     634           10 :             if(is_chunked_)
     635              :             {
     636            2 :                 if(!more_input_)
     637              :                 {
     638            1 :                     prepped_.append(final_chunk);
     639              :                 }
     640              :                 else
     641              :                 {
     642            1 :                     auto h_len = chunk_header_len(stats.size);
     643              :                     capy::mutable_buffer mb(
     644            1 :                         ws_.reserve_front(h_len), h_len);
     645            1 :                     write_chunk_header({{ {mb}, {} }}, stats.size);    
     646            1 :                     prepped_.append(mb);
     647              :                 }
     648              :             }
     649           10 :             return;
     650              :         }
     651              : 
     652              :         // filter
     653              : 
     654            0 :         prepped_ = make_array(
     655              :             1 + // header
     656              :             2); // out buffer pairs
     657              : 
     658            0 :         out_init();
     659              : 
     660            0 :         prepped_.append({ m.h_.cbuf, m.h_.size });
     661            0 :         tmp_ = {};
     662            0 :         more_input_ = true;
     663              :     }
     664              : 
     665              :     void
     666           75 :     start_stream(message_base const& m)
     667              :     {
     668           75 :         start_init(m);
     669           75 :         style_ = style::stream;
     670              : 
     671           75 :         prepped_ = make_array(
     672              :             1 + // header
     673              :             2); // out buffer pairs
     674              : 
     675           75 :         if(filter_)
     676              :         {
     677              :             // TODO: smarter buffer distribution
     678            0 :             auto const n = (ws_.size() - 1) / 2;
     679            0 :             in_ = { ws_.reserve_front(n), n };
     680              :         }
     681              : 
     682           75 :         out_init();
     683              : 
     684           75 :         prepped_.append({ m.h_.cbuf, m.h_.size });
     685           75 :         more_input_ = true;
     686           75 :     }
     687              : 
     688              :     std::size_t
     689           92 :     stream_capacity() const
     690              :     {
     691           92 :         if(filter_)
     692            0 :             return in_.capacity();
     693           92 :         return out_capacity();
     694              :     }
     695              : 
     696              :     capy::mutable_buffer_pair
     697           68 :     stream_prepare()
     698              :     {
     699           68 :         if(state_ == state::start)
     700              :         {
     701            0 :             if(!msg_)
     702            0 :                 detail::throw_logic_error();
     703            0 :             start_stream(*msg_);
     704              :         }
     705           68 :         if(filter_)
     706            0 :             return in_.prepare(in_.capacity());
     707           68 :         return out_prepare();
     708              :     }
     709              : 
     710              :     void
     711           75 :     stream_commit(std::size_t n)
     712              :     {
     713           75 :         if(n > stream_capacity())
     714            1 :             detail::throw_invalid_argument();
     715              : 
     716           74 :         if(filter_)
     717            0 :             return in_.commit(n);
     718              : 
     719           74 :         out_commit(n);
     720              :     }
     721              : 
     722              :     void
     723           40 :     stream_close() noexcept
     724              :     {
     725           40 :         if(!filter_)
     726           40 :             out_finish();
     727              : 
     728           40 :         more_input_ = false;
     729           40 :     }
     730              : 
     731              :     bool
     732          236 :     is_done() const noexcept
     733              :     {
     734          236 :         return state_ == state::start;
     735              :     }
     736              : 
     737              :     detail::workspace&
     738           10 :     ws() noexcept
     739              :     {
     740           10 :         return ws_;
     741              :     }
     742              : 
     743              : private:
     744              :     bool
     745         2014 :     is_header_done() const noexcept
     746              :     {
     747         2014 :         return state_ == state::body;
     748              :     }
     749              : 
     750              :     detail::array_of_const_buffers
     751           91 :     make_array(std::size_t n)
     752              :     {
     753           91 :         BOOST_ASSERT(n <= std::uint16_t(-1));
     754              : 
     755              :         return {
     756           91 :             ws_.push_array(n,
     757            0 :                 capy::const_buffer{}),
     758           91 :             static_cast<std::uint16_t>(n) };
     759              :     }
     760              : 
     761              :     void
     762           81 :     out_init()
     763              :     {
     764              :         // use all the remaining buffer
     765           81 :         auto const n = ws_.size() - 1;
     766           81 :         out_ = { ws_.reserve_front(n), n };
     767           81 :         chunk_header_len_ =
     768           81 :             chunk_header_len(out_.capacity());
     769           81 :         if(out_capacity() == 0)
     770            0 :             detail::throw_length_error();
     771           81 :     }
     772              : 
     773              :     capy::mutable_buffer_pair
     774           68 :     out_prepare() noexcept
     775              :     {
     776           68 :         auto mbp = out_.prepare(out_.capacity());
     777           68 :         if(is_chunked_)
     778              :         {
     779           35 :             capy::remove_prefix(
     780           35 :                 mbp, chunk_header_len_);
     781           35 :             capy::remove_suffix(
     782              :                 mbp, crlf_and_final_chunk.size());
     783              :         }
     784           68 :         return mbp;
     785              :     }
     786              : 
     787              :     void
     788           74 :     out_commit(
     789              :         std::size_t n) noexcept
     790              :     {
     791           74 :         if(is_chunked_)
     792              :         {
     793           35 :             if(n == 0)
     794            1 :                 return;
     795              : 
     796           34 :             write_chunk_header(out_.prepare(chunk_header_len_), n);
     797           34 :             out_.commit(chunk_header_len_);
     798              : 
     799           34 :             out_.prepare(n);
     800           34 :             out_.commit(n);
     801              : 
     802           34 :             capy::buffer_copy(out_.prepare(crlf.size()), crlf);
     803           34 :             out_.commit(crlf.size());
     804              :         }
     805              :         else
     806              :         {
     807           39 :             out_.commit(n);
     808              :         }
     809              :     }
     810              : 
     811              :     std::size_t
     812          173 :     out_capacity() const noexcept
     813              :     {
     814          173 :         if(is_chunked_)
     815              :         {
     816           82 :             auto const overhead = chunk_header_len_ +
     817           82 :                 crlf_and_final_chunk.size();
     818           82 :             if(out_.capacity() < overhead)
     819            1 :                 return 0;
     820           81 :             return out_.capacity() - overhead;
     821              :         }
     822           91 :         return out_.capacity();
     823              :     }
     824              : 
     825              :     void
     826           46 :     out_finish() noexcept
     827              :     {
     828           46 :         if(is_chunked_)
     829              :         {
     830           23 :             capy::buffer_copy(
     831           23 :                 out_.prepare(final_chunk.size()), final_chunk);
     832           23 :             out_.commit(final_chunk.size());
     833              :         }
     834           46 :     }
     835              : };
     836              : 
     837              : //------------------------------------------------
     838              : 
     839           92 : serializer::
     840              : ~serializer()
     841              : {
     842           92 :     delete impl_;
     843           92 : }
     844              : 
     845            1 : serializer::
     846            1 : serializer(serializer&& other) noexcept
     847            1 :     : impl_(other.impl_)
     848              : {
     849            1 :     other.impl_ = nullptr;
     850            1 : }
     851              : 
     852              : serializer&
     853            2 : serializer::
     854              : operator=(serializer&& other) noexcept
     855              : {
     856            2 :     if(this != &other)
     857              :     {
     858            2 :         delete impl_;
     859            2 :         impl_ = other.impl_;
     860            2 :         other.impl_ = nullptr;
     861              :     }
     862            2 :     return *this;
     863              : }
     864              : 
     865           87 : serializer::
     866              : serializer(
     867           87 :     std::shared_ptr<serializer_config_impl const> cfg)
     868           87 :     : impl_(new impl(std::move(cfg)))
     869              : {
     870           87 : }
     871              : 
     872            0 : serializer::
     873              : serializer(
     874              :     std::shared_ptr<serializer_config_impl const> cfg,
     875            0 :     message_base const& m)
     876            0 :     : impl_(new impl(std::move(cfg), m))
     877              : {
     878            0 : }
     879              : 
     880              : void
     881            6 : serializer::
     882              : reset() noexcept
     883              : {
     884            6 :     BOOST_ASSERT(impl_);
     885            6 :     impl_->reset();
     886            6 : }
     887              : 
     888              : void
     889            0 : serializer::
     890              : set_message(message_base const& m) noexcept
     891              : {
     892            0 :     BOOST_ASSERT(impl_);
     893            0 :     impl_->msg_ = &m;
     894            0 : }
     895              : 
     896              : void
     897            6 : serializer::
     898              : start(message_base const& m)
     899              : {
     900            6 :     BOOST_ASSERT(impl_);
     901            6 :     impl_->start_empty(m);
     902            6 : }
     903              : 
     904              : void
     905            0 : serializer::
     906              : start()
     907              : {
     908            0 :     if(!impl_ || !impl_->msg_)
     909            0 :         detail::throw_logic_error();
     910            0 :     impl_->start_empty(*impl_->msg_);
     911            0 : }
     912              : 
     913              : void
     914           75 : serializer::
     915              : start_stream(
     916              :     message_base const& m)
     917              : {
     918           75 :     BOOST_ASSERT(impl_);
     919           75 :     impl_->start_stream(m);
     920           75 : }
     921              : 
     922              : void
     923            0 : serializer::
     924              : start_stream()
     925              : {
     926            0 :     if(!impl_ || !impl_->msg_)
     927            0 :         detail::throw_logic_error();
     928            0 :     impl_->start_stream(*impl_->msg_);
     929            0 : }
     930              : 
     931              : auto
     932          170 : serializer::
     933              : prepare() ->
     934              :     system::result<const_buffers_type>
     935              : {
     936          170 :     BOOST_ASSERT(impl_);
     937          170 :     return impl_->prepare();
     938              : }
     939              : 
     940              : void
     941         1848 : serializer::
     942              : consume(std::size_t n)
     943              : {
     944         1848 :     BOOST_ASSERT(impl_);
     945         1848 :     impl_->consume(n);
     946         1847 : }
     947              : 
     948              : bool
     949          236 : serializer::
     950              : is_done() const noexcept
     951              : {
     952          236 :     BOOST_ASSERT(impl_);
     953          236 :     return impl_->is_done();
     954              : }
     955              : 
     956              : //------------------------------------------------
     957              : 
     958              : detail::workspace&
     959           10 : serializer::
     960              : ws()
     961              : {
     962           10 :     BOOST_ASSERT(impl_);
     963           10 :     return impl_->ws();
     964              : }
     965              : 
     966              : void
     967           10 : serializer::
     968              : start_init(message_base const& m)
     969              : {
     970           10 :     BOOST_ASSERT(impl_);
     971           10 :     impl_->start_init(m);
     972           10 : }
     973              : 
     974              : void
     975           10 : serializer::
     976              : start_buffers(
     977              :     message_base const& m,
     978              :     cbs_gen& cbs_gen)
     979              : {
     980           10 :     BOOST_ASSERT(impl_);
     981           10 :     impl_->start_buffers(m, cbs_gen);
     982           10 : }
     983              : 
     984              : //------------------------------------------------
     985              : 
     986              : std::size_t
     987           17 : serializer::
     988              : stream_capacity() const
     989              : {
     990           17 :     BOOST_ASSERT(impl_);
     991           17 :     return impl_->stream_capacity();
     992              : }
     993              : 
     994              : auto
     995           68 : serializer::
     996              : stream_prepare() ->
     997              :     mutable_buffers_type
     998              : {
     999           68 :     BOOST_ASSERT(impl_);
    1000           68 :     return impl_->stream_prepare();
    1001              : }
    1002              : 
    1003              : void
    1004           75 : serializer::
    1005              : stream_commit(std::size_t n)
    1006              : {
    1007           75 :     BOOST_ASSERT(impl_);
    1008           75 :     impl_->stream_commit(n);
    1009           74 : }
    1010              : 
    1011              : void
    1012           40 : serializer::
    1013              : stream_close() noexcept
    1014              : {
    1015           40 :     BOOST_ASSERT(impl_);
    1016           40 :     impl_->stream_close();
    1017           40 : }
    1018              : 
    1019              : } // http
    1020              : } // boost
        

Generated by: LCOV version 2.3