LCOV - code coverage report
Current view: top level - libs/http_proto/src/serializer.cpp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 91.6 % 367 336
Test Date: 2024-09-18 08:42:22 Functions: 92.9 % 28 26

            Line data    Source code
       1              : //
       2              : // Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
       3              : // Copyright (c) 2024 Christian Mazakas
       4              : //
       5              : // Distributed under the Boost Software License, Version 1.0. (See accompanying
       6              : // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
       7              : //
       8              : // Official repository: https://github.com/cppalliance/http_proto
       9              : //
      10              : 
      11              : #include <boost/http_proto/detail/except.hpp>
      12              : #include <boost/http_proto/message_view_base.hpp>
      13              : #include <boost/http_proto/serializer.hpp>
      14              : #include <boost/http_proto/service/zlib_service.hpp>
      15              : #include <boost/buffers/algorithm.hpp>
      16              : #include <boost/buffers/buffer_copy.hpp>
      17              : #include <boost/buffers/buffer_size.hpp>
      18              : #include <boost/core/ignore_unused.hpp>
      19              : #include <stddef.h>
      20              : 
      21              : #include "detail/filter.hpp"
      22              : namespace boost {
      23              : namespace http_proto {
      24              : 
      25              : namespace {
      26              : class deflator_filter
      27              :     : public http_proto::detail::filter
      28              : {
      29              :     using stream_t = zlib::service::stream;
      30              :     stream_t& deflator_;
      31              : 
      32              : public:
      33           48 :     deflator_filter(
      34              :         context& ctx,
      35              :         http_proto::detail::workspace& ws,
      36              :         bool use_gzip)
      37          192 :         : deflator_{ ctx.get_service<zlib::service>()
      38           48 :             .make_deflator(ws, -1, use_gzip ? 31 : 15, 8) }
      39              :     {
      40           48 :     }
      41              : 
      42              :     virtual filter::results
      43        23756 :     on_process(
      44              :         buffers::mutable_buffer out,
      45              :         buffers::const_buffer in,
      46              :         bool more) override
      47              :     {
      48        23756 :         auto flush =
      49        23756 :             more ? stream_t::flush::none : stream_t::flush::finish;
      50        23756 :         filter::results results;
      51              : 
      52              :         for(;;)
      53              :         {
      54        36236 :             auto r = deflator_.write(out, in, flush);
      55              : 
      56        36236 :             results.out_bytes += r.out_bytes;
      57        36236 :             results.in_bytes  += r.in_bytes;
      58        36236 :             results.ec         = r.ec;
      59        36236 :             results.finished   = r.finished;
      60              : 
      61        36236 :             if(r.ec || r.finished)
      62        23756 :                 return results;
      63              : 
      64        23820 :             out = buffers::sans_prefix(out, r.out_bytes);
      65        23820 :             in  = buffers::sans_prefix(in, r.in_bytes);
      66              : 
      67        23820 :             if(in.size() == 0)
      68              :             {
      69        22444 :                 if(r.out_bytes == 0)
      70              :                 {
      71              :                     // TODO: is this necessary?
      72        11104 :                     flush = stream_t::flush::sync;
      73        11104 :                     continue;
      74              :                 }
      75        11340 :                 return results;
      76              :             }
      77        12480 :         }
      78              :     }
      79              : };
      80              : } // namespace
      81              : 
      82              : void
      83            0 : consume_buffers(
      84              :     buffers::const_buffer*& p,
      85              :     std::size_t& n,
      86              :     std::size_t bytes)
      87              : {
      88            0 :     while(n > 0)
      89              :     {
      90            0 :         if(bytes < p->size())
      91              :         {
      92            0 :             *p += bytes;
      93            0 :             return;
      94              :         }
      95            0 :         bytes -= p->size();
      96            0 :         ++p;
      97            0 :         --n;
      98              :     }
      99              : 
     100              :     // Precondition violation
     101            0 :     if(bytes > 0)
     102            0 :         detail::throw_invalid_argument();
     103              : }
     104              : 
     105              : template<class MutableBuffers>
     106              : void
     107         6312 : write_chunk_header(
     108              :     MutableBuffers const& dest0,
     109              :     std::size_t size) noexcept
     110              : {
     111              :     static constexpr char hexdig[] =
     112              :         "0123456789ABCDEF";
     113              :     char buf[18];
     114         6312 :     auto p = buf + 16;
     115       107304 :     for(std::size_t i = 16; i--;)
     116              :     {
     117       100992 :         *--p = hexdig[size & 0xf];
     118       100992 :         size >>= 4;
     119              :     }
     120         6312 :     buf[16] = '\r';
     121         6312 :     buf[17] = '\n';
     122         6312 :     auto n = buffers::buffer_copy(
     123              :         dest0,
     124        12624 :         buffers::const_buffer(
     125              :             buf, sizeof(buf)));
     126              :     ignore_unused(n);
     127         6312 :     BOOST_ASSERT(n == 18);
     128         6312 :     BOOST_ASSERT(
     129              :         buffers::buffer_size(dest0) == n);
     130         6312 : }
     131              : 
     132              : template<class DynamicBuffer>
     133              : void
     134              : write_chunk_close(DynamicBuffer& db)
     135              : {
     136              :     db.commit(
     137              :         buffers::buffer_copy(
     138              :             db.prepare(2),
     139              :             buffers::const_buffer("\r\n", 2)));
     140              : }
     141              : 
     142              : template<class DynamicBuffer>
     143              : void
     144              : write_last_chunk(DynamicBuffer& db)
     145              : {
     146              :     db.commit(
     147              :         buffers::buffer_copy(
     148              :             db.prepare(5),
     149              :             buffers::const_buffer("0\r\n\r\n", 5)));
     150              : }
     151              : 
     152              : //------------------------------------------------
     153              : 
     154           43 : serializer::
     155              : ~serializer()
     156              : {
     157           43 : }
     158              : 
     159            0 : serializer::
     160              : serializer(
     161              :     serializer&&) noexcept = default;
     162              : 
     163            9 : serializer::
     164              : serializer(
     165            9 :     context& ctx)
     166            9 :     : serializer(ctx, 65536)
     167              : {
     168            9 : }
     169              : 
     170           43 : serializer::
     171              : serializer(
     172              :     context& ctx,
     173           43 :     std::size_t buffer_size)
     174           43 :     : ws_(buffer_size)
     175           43 :     , ctx_(ctx)
     176              : {
     177           43 : }
     178              : 
     179              : void
     180           56 : serializer::
     181              : reset() noexcept
     182              : {
     183           56 :     chunk_header_ = {};
     184           56 :     chunk_close_ = {};
     185           56 :     last_chunk_ = {};
     186           56 :     filter_ = nullptr;
     187           56 :     more_ = false;
     188           56 :     is_done_ = false;
     189           56 :     is_chunked_ = false;
     190           56 :     is_expect_continue_ = false;
     191           56 :     is_compressed_ = false;
     192           56 :     filter_done_ = false;
     193           56 :     in_ = nullptr;
     194           56 :     out_ = nullptr;
     195           56 :     ws_.clear();
     196           56 : }
     197              : 
     198              : //------------------------------------------------
     199              : 
     200              : auto
     201        12604 : serializer::
     202              : prepare() ->
     203              :     system::result<
     204              :         const_buffers_type>
     205              : {
     206              :     // Precondition violation
     207        12604 :     if( is_done_ )
     208            1 :         detail::throw_logic_error();
     209              : 
     210              :     // Expect: 100-continue
     211        12603 :     if( is_expect_continue_ )
     212              :     {
     213            4 :         if( !is_header_done_ )
     214            2 :             return const_buffers_type(hp_, 1);
     215            2 :         is_expect_continue_ = false;
     216            2 :         BOOST_HTTP_PROTO_RETURN_EC(
     217              :             error::expect_100_continue);
     218              :     }
     219              : 
     220        12599 :     if( st_ == style::empty )
     221            9 :         return const_buffers_type(
     222            6 :             prepped_.data(), prepped_.size());
     223              : 
     224        12596 :     if( st_ == style::buffers && !filter_ )
     225            9 :         return const_buffers_type(
     226            6 :             prepped_.data(), prepped_.size());
     227              : 
     228              :     // callers must consume() everything before invoking
     229              :     // prepare() again
     230        12652 :     if( !is_header_done_ &&
     231           59 :         buffers::buffer_size(prepped_) != prepped_[0].size() )
     232            0 :         detail::throw_logic_error();
     233              : 
     234        25127 :     if( is_header_done_ &&
     235        12534 :         buffers::buffer_size(prepped_) > 0 )
     236            0 :         detail::throw_logic_error();
     237              : 
     238        12593 :     auto& input = *in_;
     239        12593 :     auto& output = *out_;
     240        12593 :     if( st_ == style::source && more_ )
     241              :     {
     242         5490 :         auto results = src_->read(
     243         5490 :             input.prepare(input.capacity()));
     244         5490 :         more_ = !results.finished;
     245         5490 :         input.commit(results.bytes);
     246              :     }
     247              : 
     248        30717 :     if( st_ == style::stream &&
     249        18103 :         more_ &&
     250         5510 :         in_->size() == 0 )
     251            1 :         BOOST_HTTP_PROTO_RETURN_EC(error::need_data);
     252              : 
     253              :     bool has_avail_out =
     254        25145 :         ((!filter_ && (more_ || input.size() > 0)) ||
     255        12553 :         (filter_ && !filter_done_));
     256              : 
     257        25312 :     auto get_input = [&]() -> buffers::const_buffer
     258              :     {
     259        25312 :         if( st_ == style::buffers )
     260              :         {
     261         3360 :             if( buffers::buffer_size(buf_) == 0 )
     262           56 :                 return {};
     263              : 
     264         3304 :             auto buf = *(buf_.data());
     265         3304 :             BOOST_ASSERT(buf.size() > 0);
     266         3304 :             return buf;
     267              :         }
     268              :         else
     269              :         {
     270        21952 :             if( input.size() == 0 )
     271        10992 :                 return {};
     272              : 
     273        10960 :             auto cbs = input.data();
     274        10960 :             auto buf = *cbs.begin();
     275        10960 :             if( buf.size() == 0 )
     276              :             {
     277            0 :                 auto p = cbs.begin();
     278            0 :                 ++p;
     279            0 :                 buf = *p;
     280              :             }
     281        10960 :             if( buf.size() == 0 )
     282            0 :                 detail::throw_logic_error();
     283        10960 :             return buf;
     284              :         }
     285        12592 :     };
     286              : 
     287        25312 :     auto get_output = [&]() -> buffers::mutable_buffer
     288              :     {
     289        25312 :         auto mbs = output.prepare(output.capacity());
     290        25312 :         auto buf = *mbs.begin();
     291        25312 :         if( buf.size() == 0 )
     292              :         {
     293         1556 :             auto p = mbs.begin();
     294         1556 :             ++p;
     295         1556 :             buf = *p;
     296              :         }
     297        25312 :         return buf;
     298        12592 :     };
     299              : 
     300        23756 :     auto consume = [&](std::size_t n)
     301              :     {
     302        23756 :         if( st_ == style::buffers )
     303              :         {
     304         1804 :             buf_.consume(n);
     305         1804 :             if( buffers::buffer_size(buf_) == 0 )
     306           60 :                 more_ = false;
     307              :         }
     308              :         else
     309        21952 :             input.consume(n);
     310        36348 :     };
     311              : 
     312        12592 :     std::size_t num_written = 0;
     313        12592 :     if( !filter_ )
     314           44 :         num_written += input.size();
     315              :     else
     316              :     {
     317              :         for(;;)
     318              :         {
     319        25312 :             auto in = get_input();
     320        25312 :             auto out = get_output();
     321        25312 :             if( out.size() == 0 )
     322              :             {
     323         1556 :                 if( output.size() == 0 )
     324            0 :                     detail::throw_logic_error();
     325        12548 :                 break;
     326              :             }
     327              : 
     328        23756 :             auto rs = filter_->process(
     329        23756 :                 out, in, more_);
     330              : 
     331        23756 :             if( rs.finished )
     332           96 :                 filter_done_ = true;
     333              : 
     334        23756 :             consume(rs.in_bytes);
     335              : 
     336        23756 :             if( rs.out_bytes == 0 )
     337        10992 :                 break;
     338              : 
     339        12764 :             num_written += rs.out_bytes;
     340        12764 :             output.commit(rs.out_bytes);
     341        12764 :         }
     342              :     }
     343              : 
     344              :     // end:
     345        12592 :     std::size_t n = 0;
     346        12592 :     if( !is_header_done_ )
     347              :     {
     348           58 :         BOOST_ASSERT(hp_ == &prepped_[0]);
     349           58 :         ++n;
     350              :     }
     351              :     else
     352        12534 :         prepped_.reset(prepped_.capacity());
     353              : 
     354        12592 :     if( !is_chunked_ )
     355              :     {
     356        18834 :         for(buffers::const_buffer const& b : output.data())
     357        12556 :             prepped_[n++] = b;
     358              :     }
     359              :     else
     360              :     {
     361         6314 :         if( has_avail_out )
     362              :         {
     363         6311 :             write_chunk_header(
     364         6311 :                 chunk_header_, num_written);
     365         6311 :             prepped_[n++] = chunk_header_;
     366              : 
     367        18933 :             for(buffers::const_buffer const& b : output.data())
     368        12622 :                 prepped_[n++] = b;
     369              : 
     370         6311 :             prepped_[n++] = chunk_close_;
     371              :         }
     372              : 
     373         6314 :         if( (filter_ && filter_done_) ||
     374         6290 :             (!filter_ && !more_) )
     375           29 :             prepped_[n++] = last_chunk_;
     376              :     }
     377              : 
     378              :     auto cbs = const_buffers_type(
     379        12592 :         prepped_.data(), prepped_.size());
     380              : 
     381        12592 :     BOOST_ASSERT(buffers::buffer_size(cbs) > 0);
     382        12592 :     return cbs;
     383              : }
     384              : 
     385              : void
     386        14345 : serializer::
     387              : consume(
     388              :     std::size_t n)
     389              : {
     390              :     // Precondition violation
     391        14345 :     if( is_done_ )
     392            1 :         detail::throw_logic_error();
     393              : 
     394        14344 :     if( is_expect_continue_ )
     395              :     {
     396              :         // Cannot consume more than
     397              :         // the header on 100-continue
     398            3 :         if( n > hp_->size() )
     399            1 :             detail::throw_invalid_argument();
     400              :     }
     401              : 
     402        14343 :     if( !is_header_done_ )
     403              :     {
     404              :         // consume header
     405           76 :         if( n < hp_->size() )
     406              :         {
     407           11 :             prepped_.consume(n);
     408           11 :             return;
     409              :         }
     410           65 :         n -= hp_->size();
     411           65 :         prepped_.consume(hp_->size());
     412           65 :         is_header_done_ = true;
     413              :     }
     414              : 
     415        14332 :     prepped_.consume(n);
     416        14332 :     auto is_empty = (buffers::buffer_size(prepped_) == 0);
     417              : 
     418        14332 :     if( st_ == style::buffers && !filter_ && is_empty )
     419            3 :         more_ = false;
     420              : 
     421        14332 :     if( st_ == style::empty &&
     422            4 :         is_empty &&
     423            4 :         !is_expect_continue_ )
     424            3 :         more_ = false;
     425              : 
     426        14332 :     if( is_empty )
     427              :     {
     428        12600 :         if( out_ && out_->size() )
     429              :         {
     430        12587 :             BOOST_ASSERT(st_ != style::empty);
     431        12587 :             out_->consume(out_->size());
     432              :         }
     433        12600 :         is_done_ = filter_ ? filter_done_ : !more_;
     434              :     }
     435              : }
     436              : 
     437              : void
     438           24 : serializer::
     439              : use_deflate_encoding()
     440              : {
     441              :     // can only apply one encoding
     442           24 :     if(filter_)
     443            0 :         detail::throw_logic_error();
     444              : 
     445           24 :     is_compressed_ = true;
     446           24 :     filter_ = &ws_.emplace<deflator_filter>(ctx_, ws_, false);
     447           24 : }
     448              : 
     449              : void
     450           24 : serializer::
     451              : use_gzip_encoding()
     452              : {
     453              :     // can only apply one encoding
     454           24 :     if( filter_ )
     455            0 :         detail::throw_logic_error();
     456              : 
     457           24 :     is_compressed_ = true;
     458           24 :     filter_ = &ws_.emplace<deflator_filter>(ctx_, ws_, true);
     459           24 : }
     460              : 
     461              : //------------------------------------------------
     462              : 
     463              : void
     464            7 : serializer::
     465              : copy(
     466              :     buffers::const_buffer* dest,
     467              :     buffers::const_buffer const* src,
     468              :     std::size_t n) noexcept
     469              : {
     470           14 :     while(n--)
     471            7 :         *dest++ = *src++;
     472            7 : }
     473              : 
     474              : void
     475           73 : serializer::
     476              : start_init(
     477              :     message_view_base const& m)
     478              : {
     479              :     // VFALCO what do we do with
     480              :     // metadata error code failures?
     481              :     // m.ph_->md.maybe_throw();
     482              : 
     483           73 :     auto const& md = m.metadata();
     484              : 
     485           73 :     is_done_ = false;
     486           73 :     is_header_done_ = false;
     487           73 :     is_expect_continue_ = md.expect.is_100_continue;
     488              : 
     489              :     // Transfer-Encoding
     490              :     {
     491           73 :         auto const& te = md.transfer_encoding;
     492           73 :         is_chunked_ = te.is_chunked;
     493              :     }
     494              : 
     495           73 :     if( is_chunked_)
     496              :     {
     497           31 :         auto* p = ws_.reserve_front(chunked_overhead_);
     498           31 :         chunk_header_ =
     499           31 :             buffers::mutable_buffer(p, chunk_header_len_);
     500           31 :         chunk_close_ =
     501           62 :             buffers::mutable_buffer(
     502           31 :                 p + chunk_header_len_, crlf_len_);
     503           31 :         last_chunk_ =
     504           62 :             buffers::mutable_buffer(
     505           31 :                 p + chunk_header_len_ + crlf_len_,
     506              :                 last_chunk_len_);
     507              : 
     508           31 :         buffers::buffer_copy(
     509           31 :             chunk_close_, buffers::const_buffer("\r\n", 2));
     510           31 :         buffers::buffer_copy(
     511           31 :             last_chunk_,
     512           62 :             buffers::const_buffer("0\r\n\r\n", 5));
     513              :     }
     514           73 : }
     515              : 
     516              : void
     517            4 : serializer::
     518              : start_empty(
     519              :     message_view_base const& m)
     520              : {
     521            4 :     start_init(m);
     522              : 
     523            4 :     st_ = style::empty;
     524            4 :     more_ = true;
     525              : 
     526            4 :     if(! is_chunked_)
     527              :     {
     528            3 :         prepped_ = make_array(
     529              :             1); // header
     530              :     }
     531              :     else
     532              :     {
     533            1 :         prepped_ = make_array(
     534              :             1 + // header
     535              :             1); // final chunk
     536              : 
     537              :         // Buffer is too small
     538            1 :         if(ws_.size() < 5)
     539            0 :             detail::throw_length_error();
     540              : 
     541              :         buffers::mutable_buffer dest(
     542            1 :             ws_.data(), 5);
     543            1 :         buffers::buffer_copy(
     544              :             dest,
     545            1 :             buffers::const_buffer(
     546              :                 "0\r\n\r\n", 5));
     547            1 :         prepped_[1] = dest;
     548              :     }
     549              : 
     550            4 :     hp_ = &prepped_[0];
     551            4 :     *hp_ = { m.ph_->cbuf, m.ph_->size };
     552            4 : }
     553              : 
     554              : void
     555           23 : serializer::
     556              : start_buffers(
     557              :     message_view_base const& m)
     558              : {
     559           23 :     st_ = style::buffers;
     560           23 :     tmp1_ = {};
     561              : 
     562           23 :     if( !filter_ && !is_chunked_ )
     563              :     {
     564            6 :         prepped_ = make_array(
     565              :             1 +           // header
     566            6 :             buf_.size()); // user input
     567              : 
     568            6 :         hp_ = &prepped_[0];
     569            6 :         *hp_ = { m.ph_->cbuf, m.ph_->size };
     570              : 
     571            6 :         copy(&prepped_[1], buf_.data(), buf_.size());
     572              : 
     573            6 :         more_ = (buffers::buffer_size(buf_) > 0);
     574            6 :         return;
     575              :     }
     576              : 
     577           17 :     if( !filter_ && is_chunked_ )
     578              :     {
     579            1 :         if( buffers::buffer_size(buf_) == 0 )
     580              :         {
     581            0 :             prepped_ = make_array(
     582              :                 1 +           // header
     583              :                 1);           // last chunk
     584              : 
     585            0 :             hp_ = &prepped_[0];
     586            0 :             *hp_ = { m.ph_->cbuf, m.ph_->size };
     587            0 :             prepped_[1] = last_chunk_;
     588            0 :             more_ = false;
     589            0 :             return;
     590              :         }
     591              : 
     592            2 :         write_chunk_header(
     593            1 :             chunk_header_, buffers::buffer_size(buf_));
     594              : 
     595            1 :         prepped_ = make_array(
     596              :             1 +           // header
     597              :             1 +           // chunk header
     598            1 :             buf_.size() + // user input
     599              :             1 +           // chunk close
     600              :             1);           // last chunk
     601              : 
     602            1 :         hp_ = &prepped_[0];
     603            1 :         *hp_ = { m.ph_->cbuf, m.ph_->size };
     604            1 :         prepped_[1] = chunk_header_;
     605            1 :         copy(&prepped_[2], buf_.data(), buf_.size());
     606              : 
     607            1 :         prepped_[prepped_.size() - 2] = chunk_close_;
     608            1 :         prepped_[prepped_.size() - 1] = last_chunk_;
     609            1 :         more_ = true;
     610            1 :         return;
     611              :     }
     612              : 
     613           16 :     if( is_chunked_ )
     614              :     {
     615            8 :         prepped_ = make_array(
     616              :             1 + // header
     617              :             1 + // chunk header
     618              :             2 + // tmp
     619              :             1 + // chunk close
     620              :             1); // last chunk
     621              :     }
     622              :     else
     623            8 :         prepped_ = make_array(
     624              :             1 + // header
     625              :             2); // tmp
     626              : 
     627           16 :     hp_ = &prepped_[0];
     628           16 :     *hp_ = { m.ph_->cbuf, m.ph_->size };
     629           16 :     tmp0_ = { ws_.data(), ws_.size() };
     630           16 :     out_ = &tmp0_;
     631           16 :     in_ = out_;
     632           16 :     more_ = true;
     633              : }
     634              : 
     635              : void
     636           24 : serializer::
     637              : start_source(
     638              :     message_view_base const& m,
     639              :     source* src)
     640              : {
     641           24 :     st_ = style::source;
     642           24 :     src_ = src;
     643              : 
     644           24 :     if( is_chunked_ )
     645              :     {
     646           10 :         prepped_ = make_array(
     647              :             1 + // header
     648              :             1 + // chunk header
     649              :             2 + // tmp
     650              :             1 + // chunk close
     651              :             1); // last chunk
     652              :     }
     653              :     else
     654           14 :         prepped_ = make_array(
     655              :             1 + // header
     656              :             2); // tmp
     657              : 
     658           24 :     if( !filter_ )
     659              :     {
     660            8 :         tmp0_ = { ws_.data(), ws_.size() };
     661            8 :         if( tmp0_.capacity() < 1 )
     662            0 :             detail::throw_length_error();
     663              : 
     664            8 :         in_ = &tmp0_;
     665            8 :         out_ = &tmp0_;
     666              :     }
     667              :     else
     668              :     {
     669           16 :         auto n = ws_.size() / 2;
     670           16 :         auto* p = ws_.reserve_front(n);
     671           16 :         tmp1_ = buffers::circular_buffer(p, n);
     672              : 
     673           16 :         tmp0_ = { ws_.data(), ws_.size() };
     674           16 :         if( tmp0_.capacity() < 1 )
     675            0 :             detail::throw_length_error();
     676              : 
     677           16 :         in_ = &tmp1_;
     678           16 :         out_ = &tmp0_;
     679              :     }
     680              : 
     681           24 :     hp_ = &prepped_[0];
     682           24 :     *hp_ = { m.ph_->cbuf, m.ph_->size };
     683           24 :     more_ = true;
     684           24 : }
     685              : 
     686              : auto
     687           22 : serializer::
     688              : start_stream(
     689              :     message_view_base const& m) ->
     690              :         stream
     691              : {
     692           22 :     start_init(m);
     693              : 
     694           22 :     st_ = style::stream;
     695           22 :     if( is_chunked_ )
     696              :     {
     697           11 :         prepped_ = make_array(
     698              :             1 + // header
     699              :             1 + // chunk header
     700              :             2 + // tmp
     701              :             1 + // chunk close
     702              :             1); // last chunk
     703              :     }
     704              :     else
     705           11 :         prepped_ = make_array(
     706              :             1 + // header
     707              :             2); // tmp
     708              : 
     709           22 :     if( !filter_ )
     710              :     {
     711            6 :         tmp0_ = { ws_.data(), ws_.size() };
     712            6 :         if( tmp0_.capacity() < 1 )
     713            0 :             detail::throw_length_error();
     714              : 
     715            6 :         in_ = &tmp0_;
     716            6 :         out_ = &tmp0_;
     717              :     }
     718              :     else
     719              :     {
     720           16 :         auto n = ws_.size() / 2;
     721           16 :         auto* p = ws_.reserve_front(n);
     722           16 :         tmp1_ = buffers::circular_buffer(p, n);
     723              : 
     724           16 :         tmp0_ = { ws_.data(), ws_.size() };
     725           16 :         if( tmp0_.capacity() < 1 )
     726            0 :             detail::throw_length_error();
     727              : 
     728           16 :         in_ = &tmp1_;
     729           16 :         out_ = &tmp0_;
     730              :     }
     731              : 
     732           22 :     hp_ = &prepped_[0];
     733           22 :     *hp_ = { m.ph_->cbuf, m.ph_->size };
     734           22 :     more_ = true;
     735           22 :     return stream{*this};
     736              : }
     737              : 
     738              : //------------------------------------------------
     739              : 
     740              : std::size_t
     741          139 : serializer::
     742              : stream::
     743              : capacity() const noexcept
     744              : {
     745          139 :     return sr_->in_->capacity();
     746              : }
     747              : 
     748              : std::size_t
     749           72 : serializer::
     750              : stream::
     751              : size() const noexcept
     752              : {
     753           72 :     return sr_->in_->size();
     754              : }
     755              : 
     756              : bool
     757           63 : serializer::
     758              : stream::
     759              : is_full() const noexcept
     760              : {
     761           63 :     return capacity() == 0;
     762              : }
     763              : 
     764              : auto
     765         5512 : serializer::
     766              : stream::
     767              : prepare() const ->
     768              :     buffers_type
     769              : {
     770         5512 :     return sr_->in_->prepare(sr_->in_->capacity());
     771              : }
     772              : 
     773              : void
     774         5512 : serializer::
     775              : stream::
     776              : commit(std::size_t n) const
     777              : {
     778              :     // the stream must make a non-zero amount of bytes
     779              :     // available to the serializer
     780         5512 :     if( n == 0 )
     781            1 :         detail::throw_logic_error();
     782              : 
     783         5511 :     sr_->in_->commit(n);
     784         5511 : }
     785              : 
     786              : void
     787           25 : serializer::
     788              : stream::
     789              : close() const
     790              : {
     791              :     // Precondition violation
     792           25 :     if(! sr_->more_ )
     793            4 :         detail::throw_logic_error();
     794           21 :     sr_->more_ = false;
     795           21 : }
     796              : 
     797              : //------------------------------------------------
     798              : 
     799              : } // http_proto
     800              : } // boost
        

Generated by: LCOV version 2.1