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