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

Generated by: LCOV version 2.3