LCOV - code coverage report
Current view: top level - src - serializer.cpp (source / functions) Coverage Total Hit Missed
Test: coverage_remapped.info Lines: 62.8 % 393 247 146
Test Date: 2026-08-26 15:14:03 Functions: 80.8 % 52 42 10

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

Generated by: LCOV version 2.3