src/serializer.cpp

98.9% Lines (344/348) 96.6% List of functions (28/29) 95.0% Branches (322/339)
serializer.cpp
f(x) Functions (29)
Function Calls Lines Branches Blocks
boost::burl::(anonymous namespace)::suffix(boost::capy::const_buffer, unsigned long) :46 1369x 100.0% – 100.0% boost::burl::serializer::serializer(boost::burl::serializer::config const&) :56 189x 100.0% – 100.0% boost::burl::serializer::serializer(boost::burl::serializer&&) :68 1x 100.0% – 100.0% boost::burl::serializer::operator=(boost::burl::serializer&&) :72 0 0.0% – 0.0% boost::burl::serializer::~serializer() :75 190x 100.0% – 100.0% boost::burl::serializer::start(boost::burl::message_head_base*, bool) :79 200x 100.0% 50.0% 83.0% boost::burl::serializer::select_framing_() :107 200x 100.0% 89.5% 94.0% boost::burl::serializer::select_framing_()::{lambda()#1}::operator()() const :127 200x 100.0% 100.0% 100.0% boost::burl::serializer::revise_framing_(unsigned long) :145 180x 100.0% 100.0% 100.0% boost::burl::serializer::split_() :164 235x 100.0% 100.0% 100.0% boost::burl::serializer::sealed_() const :175 5020x 100.0% – 100.0% boost::burl::serializer::set_encoder(std::unique_ptr<boost::burl::detail::encoder, std::default_delete<boost::burl::detail::encoder> >) :182 35x 100.0% 50.0% 100.0% boost::burl::serializer::prepare(std::span<boost::capy::mutable_buffer, 18446744073709551615ul>) :194 27x 100.0% 88.9% 100.0% boost::burl::serializer::commit(unsigned long) :215 66x 100.0% 50.0% 71.0% boost::burl::serializer::chunked_() const :224 4411x 100.0% – 100.0% boost::burl::serializer::to_eof_() const :231 548x 100.0% – 100.0% boost::burl::serializer::should_coalesce_(unsigned long) const :238 165x 100.0% 100.0% 100.0% boost::burl::serializer::buffered_() :250 2762x 100.0% 100.0% 100.0% boost::burl::serializer::epilogue_buf_() const :257 465x 100.0% 100.0% 100.0% boost::burl::serializer::trailer_buf_() const :268 465x 100.0% 100.0% 100.0% boost::burl::serializer::settled_() const :277 460x 100.0% 100.0% 100.0% boost::burl::serializer::open_chunk_(unsigned long) :299 127x 100.0% 70.0% 79.0% boost::burl::serializer::encode_(boost::burl::serializer::source&, std::error_code&) :333 200x 100.0% – 97.0% boost::burl::serializer::encode_(boost::burl::serializer::source&, std::error_code&)::{lambda(boost::capy::const_buffer, bool)#1}::operator()(boost::capy::const_buffer, bool) const :336 74x 100.0% 100.0% 100.0% boost::burl::serializer::ingest_(boost::burl::serializer::source&, bool) :394 684x 96.5% 99.0% 97.0% boost::burl::serializer::gather_(std::span<boost::capy::const_buffer, 18446744073709551615ul>, boost::burl::serializer::source&, bool, bool) :487 1086x 100.0% – 100.0% boost::burl::serializer::gather_(std::span<boost::capy::const_buffer, 18446744073709551615ul>, boost::burl::serializer::source&, bool, bool)::{lambda(boost::capy::const_buffer)#1}::operator()(boost::capy::const_buffer) const :497 3033x 100.0% 100.0% 100.0% boost::burl::serializer::frame_(std::span<boost::capy::const_buffer, 18446744073709551615ul>, boost::burl::serializer::source&, bool) :559 1276x 96.9% 89.6% 93.0% boost::burl::serializer::consume(unsigned long) :616 1270x 100.0% 90.0% 97.0%
Line Branch TLA Hits Source Code
1 //
2 // Copyright (c) 2026 Mohammad Nejati
3 //
4 // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 //
7 // Official repository: https://github.com/cppalliance/burl
8 //
9
10 #include <boost/burl/error.hpp>
11 #include <boost/burl/serializer.hpp>
12
13 #include "detail/content_coding.hpp"
14 #include "detail/encoder.hpp"
15 #include "detail/util.hpp"
16
17 #include <boost/assert.hpp>
18 #include <boost/capy/buffers/buffer_copy.hpp>
19 #include <boost/capy/buffers/make_buffer.hpp>
20 #include <boost/capy/cond.hpp>
21 #include <boost/url/grammar/ci_string.hpp>
22
23 #include <algorithm>
24 #include <utility>
25
26 namespace boost
27 {
28 namespace burl
29 {
30
31 // assert relying facts
32 static_assert(
33 fields_base::max_buffer_size <=
34 (std::numeric_limits<std::uint32_t>::max)());
35
36 using http::payload;
37
38 using detail::clamp;
39 using detail::content_coding;
40 using detail::make_encoder;
41
42 namespace
43 {
44
45 capy::const_buffer
46 1369x suffix(capy::const_buffer buf, std::size_t n) noexcept
47 {
48 return {
49 1369x static_cast<char const*>(buf.data()) + n,
50 1369x buf.size() - n
51 1369x };
52 }
53
54 } // namespace
55
56 189x serializer::
57 189x serializer(config const& cfg)
58 189x : buf_(new char[margin + cfg.stage_buffer + cfg.enc_buffer])
59 189x , min_prepare_(cfg.min_prepare)
60 189x , min_direct_(cfg.min_direct)
61 189x , enc_threshold_(cfg.enc_threshold)
62 189x , stage_{ buf_.get() + margin, cfg.stage_buffer }
63 189x , enc_out_{ buf_.get() + margin, cfg.enc_buffer }
64 189x , enc_cfg_(cfg.encoder)
65 {
66 189x }
67
68 1x serializer::
69 serializer(serializer&& other) noexcept = default;
70
71 serializer&
72 ✗ serializer::
73 operator=(serializer&& other) noexcept = default;
74
75 190x serializer::
76 ~serializer() = default;
77
78 void
79 200x serializer::
80 start(
81 message_head_base* msg,
82 bool head)
83 {
84
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 200 times.
200x BOOST_ASSERT(msg != nullptr);
85
86 200x stage_.clear();
87 200x enc_out_.clear();
88
89 200x msg_ = msg;
90 200x enc_ = nullptr;
91 200x trailer_ = nullptr;
92
93 200x header_offset_ = 0;
94 200x tail_offset_ = 0;
95 200x owed_ = 0;
96 200x input_framed_ = 0;
97 200x input_digested_ = 0;
98 200x prefix_rem_ = 0;
99 200x payload_ = payload::none;
100 200x state_ = state::started;
101 200x head_ = head;
102 200x crlf_owed_ = false;
103 200x enc_started_ = false;
104 200x }
105
106 void
107 200x serializer::
108 select_framing_()
109 {
110
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 200 times.
200x BOOST_ASSERT(state_ == state::started);
111
112
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 193 times.
200x payload_ = head_ ? payload::none : msg_->payload();
113
114
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 13 times.
21x if( payload_ == payload::to_eof &&
115
4/4
✓ Branch 0 taken 21 times.
✓ Branch 1 taken 179 times.
✓ Branch 3 taken 7 times.
✓ Branch 4 taken 193 times.
221x msg_->version() == http::version::http_1_1 &&
116
2/2
✓ Branch 1 taken 7 times.
✓ Branch 2 taken 1 time.
8x !msg_->contains(http::field::transfer_encoding))
117 {
118 7x msg_->set_chunked(true);
119 7x payload_ = payload::chunked;
120 }
121
122
5/6
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 197 times.
✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 3 times.
✓ Branch 6 taken 197 times.
200x if(enc_cfg_ && payload_ != payload::none)
123
1/1
✓ Branch 3 taken 3 times.
3x enc_ = make_encoder(content_coding(*msg_), *enc_cfg_);
124
125 200x split_();
126
127 600x owed_ = [&]() -> std::uint64_t
128 {
129
3/3
✓ Branch 0 taken 148 times.
✓ Branch 1 taken 14 times.
✓ Branch 2 taken 38 times.
200x switch(payload_)
130 {
131 148x case payload::none:
132 case payload::chunked:
133 148x return 0;
134 14x case payload::to_eof:
135 14x return std::uint64_t(-1);
136 38x default:
137 38x return msg_->content_length().value_or(0);
138 }
139
1/1
✓ Branch 1 taken 200 times.
200x }();
140
141 200x state_ = state::streaming;
142 200x }
143
144 void
145 180x serializer::
146 revise_framing_(std::uint64_t remaining) noexcept
147 {
148
6/6
✓ Branch 1 taken 85 times.
✓ Branch 2 taken 95 times.
✓ Branch 4 taken 71 times.
✓ Branch 5 taken 14 times.
✓ Branch 6 taken 71 times.
✓ Branch 7 taken 109 times.
180x if(!chunked_() && !to_eof_())
149 71x return;
150
151
4/4
✓ Branch 0 taken 31 times.
✓ Branch 1 taken 78 times.
✓ Branch 2 taken 23 times.
✓ Branch 3 taken 8 times.
109x if(header_offset_ == 0 && !trailer_)
152 {
153 23x msg_->set_content_length(remaining);
154 23x payload_ = payload::size;
155 23x owed_ = remaining;
156 }
157
2/2
✓ Branch 1 taken 14 times.
✓ Branch 2 taken 72 times.
86x else if(to_eof_())
158 {
159 14x owed_ = remaining;
160 }
161 }
162
163 void
164 235x serializer::
165 split_() noexcept
166 {
167
2/2
✓ Branch 1 taken 40 times.
✓ Branch 2 taken 195 times.
235x if((enc_ != nullptr) != (stage_.ptr != enc_out_.ptr))
168 {
169 40x std::swap(stage_.cap, enc_out_.cap);
170
2/2
✓ Branch 1 taken 38 times.
✓ Branch 2 taken 2 times.
40x stage_.ptr = enc_out_.ptr + (enc_ ? enc_out_.cap : 0);
171 }
172 235x }
173
174 bool
175 5020x serializer::
176 sealed_() const noexcept
177 {
178 5020x return state_ == state::sealed;
179 }
180
181 void
182 35x serializer::
183 set_encoder(
184 std::unique_ptr<detail::encoder> enc)
185 {
186
1/2
✓ Branch 0 taken 35 times.
✗ Branch 1 not taken.
35x if(state_ == state::started)
187 35x select_framing_();
188
189 35x enc_ = std::move(enc);
190 35x split_();
191 35x }
192
193 std::span<capy::mutable_buffer>
194 27x serializer::
195 prepare(std::span<capy::mutable_buffer> dest)
196 {
197
2/3
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
27x switch(state_)
198 {
199 13x case state::started:
200 13x select_framing_();
201 BOOST_FALLTHROUGH;
202 27x case state::streaming:
203
6/6
✓ Branch 1 taken 26 times.
✓ Branch 2 taken 1 time.
✓ Branch 4 taken 25 times.
✓ Branch 5 taken 1 time.
✓ Branch 6 taken 25 times.
✓ Branch 7 taken 2 times.
27x if(!dest.empty() && !stage_.full())
204 {
205 25x dest[0] = stage_.prepare();
206 25x return dest.first(1);
207 }
208 BOOST_FALLTHROUGH;
209 default:
210 2x return {};
211 };
212 }
213
214 void
215 66x serializer::
216 commit(std::size_t n) noexcept
217 {
218
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 66 times.
66x BOOST_ASSERT(msg_ != nullptr);
219
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 66 times.
66x BOOST_ASSERT(!sealed_());
220 66x stage_.commit(n);
221 66x }
222
223 bool
224 4411x serializer::
225 chunked_() const noexcept
226 {
227 4411x return payload_ == payload::chunked;
228 }
229
230 bool
231 548x serializer::
232 to_eof_() const noexcept
233 {
234 548x return payload_ == payload::to_eof;
235 }
236
237 bool
238 165x serializer::
239 should_coalesce_(std::size_t avail) const noexcept
240 {
241
2/2
✓ Branch 1 taken 12 times.
✓ Branch 2 taken 153 times.
165x if(avail > stage_.capacity())
242 12x return false;
243
2/2
✓ Branch 1 taken 7 times.
✓ Branch 2 taken 146 times.
153x if(enc_)
244
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 3 times.
11x return !enc_started_ &&
245
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
11x avail + stage_.len < enc_threshold_;
246 146x return avail < min_direct_;
247 }
248
249 detail::flat_buffer&
250 2762x serializer::
251 buffered_() noexcept
252 {
253
2/2
✓ Branch 1 taken 2308 times.
✓ Branch 2 taken 454 times.
2762x return enc_out_.empty() ? stage_ : enc_out_;
254 }
255
256 capy::const_buffer
257 465x serializer::
258 epilogue_buf_() const noexcept
259 {
260 static constexpr char s[] =
261 "\r\n" "0\r\n" "\r\n";
262
2/2
✓ Branch 0 taken 459 times.
✓ Branch 1 taken 6 times.
465x std::size_t const skip = crlf_owed_ ? 0 : 2;
263
2/2
✓ Branch 0 taken 76 times.
✓ Branch 1 taken 389 times.
465x std::size_t const drop = trailer_ ? 2 : 0;
264 465x return { s + skip, sizeof(s) - 1 - skip - drop };
265 }
266
267 capy::const_buffer
268 465x serializer::
269 trailer_buf_() const noexcept
270 {
271
2/2
✓ Branch 0 taken 76 times.
✓ Branch 1 taken 389 times.
465x if(trailer_)
272 76x return capy::make_buffer(trailer_->buffer());
273 389x return {};
274 }
275
276 bool
277 460x serializer::
278 settled_() const noexcept
279 {
280
2/2
✓ Branch 2 taken 71 times.
✓ Branch 3 taken 389 times.
460x if(msg_->buffer().size() != header_offset_)
281 71x return false;
282
283
2/2
✓ Branch 0 taken 70 times.
✓ Branch 1 taken 319 times.
389x if(input_framed_ != 0)
284 70x return false;
285
286
6/6
✓ Branch 1 taken 310 times.
✓ Branch 2 taken 9 times.
✓ Branch 4 taken 80 times.
✓ Branch 5 taken 230 times.
✓ Branch 6 taken 89 times.
✓ Branch 7 taken 230 times.
319x if(!stage_.empty() || !enc_out_.empty())
287 89x return false;
288
289
2/2
✓ Branch 1 taken 119 times.
✓ Branch 2 taken 111 times.
349x if( chunked_() &&
290 119x tail_offset_ !=
291
2/2
✓ Branch 2 taken 49 times.
✓ Branch 3 taken 70 times.
238x epilogue_buf_().size() +
292
2/2
✓ Branch 2 taken 49 times.
✓ Branch 3 taken 181 times.
349x trailer_buf_().size())
293 49x return false;
294
295 181x return owed_ == 0;
296 }
297
298 void
299 127x serializer::
300 open_chunk_(std::uint64_t s) noexcept
301 {
302
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 127 times.
127x BOOST_ASSERT(prefix_rem_ == 0);
303
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 127 times.
127x BOOST_ASSERT(owed_ == 0);
304
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 127 times.
127x BOOST_ASSERT(s != 0);
305
306 127x auto const p0 = buffered_().ptr + buffered_().pos;
307 127x auto p = p0;
308
309 127x *--p = '\n';
310 127x *--p = '\r';
311
312 static constexpr char hex[] = "0123456789abcdef";
313 127x auto v = s;
314 do
315 {
316 205x *--p = hex[v & 0xF];
317 205x v >>= 4;
318
2/2
✓ Branch 0 taken 78 times.
✓ Branch 1 taken 127 times.
205x } while(v != 0);
319
320 // prev chunk's CRLF
321
2/2
✓ Branch 0 taken 50 times.
✓ Branch 1 taken 77 times.
127x if(crlf_owed_)
322 {
323 50x *--p = '\n';
324 50x *--p = '\r';
325 }
326 127x crlf_owed_ = true;
327
328 127x prefix_rem_ = static_cast<std::uint8_t>(p0 - p);
329 127x owed_ = s;
330 127x }
331
332 void
333 200x serializer::
334 encode_(source& src, std::error_code& ec)
335 {
336 74x auto process = [&](
337 capy::const_buffer in, bool more)
338 {
339
1/1
✓ Branch 2 taken 74 times.
148x auto const r = enc_->process(
340 74x enc_out_.prepare(), in, more);
341 74x enc_started_ = true;
342 74x enc_out_.commit(r.produced);
343
2/2
✓ Branch 2 taken 28 times.
✓ Branch 3 taken 46 times.
74x if(r.ec == capy::cond::eof)
344 {
345 28x revise_framing_(enc_out_.size());
346 28x enc_ = nullptr;
347 }
348
2/2
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 43 times.
46x else if(r.ec)
349 {
350 3x ec = r.ec;
351 }
352 74x return r.consumed;
353 200x };
354
355 // staged input drains first
356
2/2
✓ Branch 1 taken 9 times.
✓ Branch 2 taken 197 times.
206x while(!stage_.empty())
357 {
358
2/2
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 8 times.
9x if(enc_out_.capacity() == 0)
359 173x return;
360
4/4
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 5 times.
✓ Branch 3 taken 1 time.
✓ Branch 4 taken 2 times.
8x auto const more = !sealed_() || src.remain != 0;
361
1/1
✓ Branch 2 taken 8 times.
8x auto const n = process(stage_.data(), more);
362 8x stage_.consume(n);
363
6/6
✓ Branch 1 taken 7 times.
✓ Branch 2 taken 1 time.
✓ Branch 4 taken 1 time.
✓ Branch 5 taken 6 times.
✓ Branch 6 taken 2 times.
✓ Branch 7 taken 6 times.
8x if(ec || !enc_)
364 2x return;
365 }
366
367 // the supplied buffers
368
2/2
✓ Branch 2 taken 192 times.
✓ Branch 3 taken 39 times.
428x for(auto cur = src.next(); cur.size() != 0;)
369 {
370
2/2
✓ Branch 1 taken 139 times.
✓ Branch 2 taken 53 times.
192x if(enc_out_.capacity() == 0)
371 158x return;
372
4/4
✓ Branch 1 taken 36 times.
✓ Branch 2 taken 17 times.
✓ Branch 3 taken 1 time.
✓ Branch 4 taken 35 times.
53x auto const more = !sealed_() || src.remain != 0;
373
1/1
✓ Branch 1 taken 53 times.
53x auto const n = process(cur, more);
374 53x cur += n;
375 53x input_digested_ += n;
376
6/6
✓ Branch 1 taken 52 times.
✓ Branch 2 taken 1 time.
✓ Branch 4 taken 18 times.
✓ Branch 5 taken 34 times.
✓ Branch 6 taken 19 times.
✓ Branch 7 taken 34 times.
53x if(ec || !enc_)
377 19x return;
378
2/2
✓ Branch 1 taken 23 times.
✓ Branch 2 taken 11 times.
34x if(cur.size() == 0)
379 11x cur = src.next();
380 }
381
382 // finish the stream
383
2/2
✓ Branch 1 taken 15 times.
✓ Branch 2 taken 27 times.
42x while(sealed_())
384 {
385
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 13 times.
15x if(enc_out_.capacity() == 0)
386 2x return;
387
1/1
✓ Branch 2 taken 13 times.
13x process({}, false);
388
6/6
✓ Branch 1 taken 12 times.
✓ Branch 2 taken 1 time.
✓ Branch 4 taken 9 times.
✓ Branch 5 taken 3 times.
✓ Branch 6 taken 10 times.
✓ Branch 7 taken 3 times.
13x if(ec || !enc_)
389 10x return;
390 }
391 }
392
393 system::result<bool, std::error_code>
394 684x serializer::
395 ingest_(
396 source& src,
397 bool more)
398 {
399
4/4
✓ Branch 0 taken 409 times.
✓ Branch 1 taken 275 times.
✓ Branch 3 taken 187 times.
✓ Branch 4 taken 222 times.
684x auto const sealing = !more && !sealed_();
400 1368x auto const have = std::uint64_t(src.remain) +
401 684x stage_.size() + enc_out_.size();
402
403
2/2
✓ Branch 1 taken 321 times.
✓ Branch 2 taken 363 times.
684x if(chunked_())
404 {
405
8/8
✓ Branch 1 taken 45 times.
✓ Branch 2 taken 276 times.
✓ Branch 4 taken 40 times.
✓ Branch 5 taken 5 times.
✓ Branch 6 taken 1 time.
✓ Branch 7 taken 39 times.
✓ Branch 8 taken 1 time.
✓ Branch 9 taken 320 times.
321x if(sealed_() && !enc_ && src.remain != 0)
406 2x return make_error_code(
407 1x std::errc::invalid_argument);
408 }
409
2/2
✓ Branch 1 taken 223 times.
✓ Branch 2 taken 140 times.
363x else if(!enc_)
410 {
411
4/4
✓ Branch 0 taken 216 times.
✓ Branch 1 taken 7 times.
✓ Branch 2 taken 11 times.
✓ Branch 3 taken 212 times.
439x if( have > owed_ ||
412
6/6
✓ Branch 1 taken 108 times.
✓ Branch 2 taken 108 times.
✓ Branch 3 taken 70 times.
✓ Branch 4 taken 38 times.
✓ Branch 5 taken 4 times.
✓ Branch 6 taken 66 times.
216x (!to_eof_() && sealing && have < owed_))
413 {
414
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 11 times.
11x if(to_eof_())
415 ✗ return make_error_code(
416 ✗ std::errc::invalid_argument);
417 11x return error::body_size_mismatch;
418 }
419 }
420
421
2/2
✓ Branch 0 taken 183 times.
✓ Branch 1 taken 489 times.
672x if(sealing)
422 183x state_ = state::sealed;
423
424
2/2
✓ Branch 1 taken 404 times.
✓ Branch 2 taken 268 times.
672x if(sealed_())
425 {
426
8/8
✓ Branch 1 taken 168 times.
✓ Branch 2 taken 236 times.
✓ Branch 3 taken 29 times.
✓ Branch 4 taken 139 times.
✓ Branch 5 taken 23 times.
✓ Branch 6 taken 6 times.
✓ Branch 7 taken 6 times.
✓ Branch 8 taken 398 times.
427x if( enc_ && !enc_started_ && header_offset_ == 0 &&
427
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 17 times.
23x have < enc_threshold_)
428 {
429 6x msg_->erase(http::field::content_encoding);
430 6x enc_ = nullptr;
431 }
432
6/6
✓ Branch 1 taken 242 times.
✓ Branch 2 taken 162 times.
✓ Branch 3 taken 152 times.
✓ Branch 4 taken 90 times.
✓ Branch 5 taken 152 times.
✓ Branch 6 taken 252 times.
404x if(!enc_ && sealing)
433 152x revise_framing_(have);
434 }
435
436 // small inputs are coalesced into the stage
437
8/8
✓ Branch 1 taken 268 times.
✓ Branch 2 taken 404 times.
✓ Branch 3 taken 165 times.
✓ Branch 4 taken 103 times.
✓ Branch 5 taken 49 times.
✓ Branch 6 taken 116 times.
✓ Branch 7 taken 49 times.
✓ Branch 8 taken 623 times.
837x if( !sealed_() && src.remain != 0 &&
438 165x should_coalesce_(src.remain))
439 {
440 49x input_digested_ = src.remain;
441
2/2
✓ Branch 2 taken 49 times.
✓ Branch 3 taken 49 times.
98x for(auto b = src.next(); b.size() != 0;
442 49x b = src.next())
443 49x stage_.commit(capy::buffer_copy(
444 98x stage_.prepare(), b));
445
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 47 times.
49x if(enc_)
446 2x return false;
447 }
448
2/2
✓ Branch 1 taken 200 times.
✓ Branch 2 taken 423 times.
623x else if(enc_)
449 {
450 200x std::error_code ec;
451
1/1
✓ Branch 1 taken 200 times.
200x encode_(src, ec);
452
2/2
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 197 times.
200x if(ec)
453 {
454 3x enc_ = nullptr;
455 3x state_ = state::failed;
456 3x return ec;
457 }
458
459 197x auto const finished = (enc_ == nullptr);
460 197x auto const encoded = enc_out_.size();
461
6/6
✓ Branch 1 taken 147 times.
✓ Branch 2 taken 50 times.
✓ Branch 4 taken 138 times.
✓ Branch 5 taken 9 times.
✓ Branch 6 taken 138 times.
✓ Branch 7 taken 59 times.
197x if(!chunked_() && !to_eof_())
462 {
463
6/6
✓ Branch 0 taken 136 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 10 times.
✓ Branch 3 taken 126 times.
✓ Branch 4 taken 1 time.
✓ Branch 5 taken 9 times.
138x if(encoded > owed_ || (finished && encoded != owed_))
464 {
465 3x state_ = state::failed;
466 3x return error::body_size_mismatch;
467 }
468 }
469
4/4
✓ Branch 0 taken 169 times.
✓ Branch 1 taken 25 times.
✓ Branch 3 taken 144 times.
✓ Branch 4 taken 25 times.
194x auto const flush = finished || enc_out_.capacity() == 0;
470
8/8
✓ Branch 0 taken 169 times.
✓ Branch 1 taken 25 times.
✓ Branch 2 taken 167 times.
✓ Branch 3 taken 2 times.
✓ Branch 5 taken 30 times.
✓ Branch 6 taken 137 times.
✓ Branch 7 taken 30 times.
✓ Branch 8 taken 164 times.
194x if(flush && encoded != 0 && chunked_())
471 30x open_chunk_(encoded);
472 194x return flush;
473 }
474
475
4/4
✓ Branch 1 taken 129 times.
✓ Branch 2 taken 141 times.
✓ Branch 3 taken 123 times.
✓ Branch 4 taken 6 times.
399x auto const flush = src.remain != 0 || sealed_() ||
476
4/4
✓ Branch 0 taken 270 times.
✓ Branch 1 taken 200 times.
✓ Branch 3 taken 1 time.
✓ Branch 4 taken 122 times.
869x should_drain() || stage_.pos != 0;
477
6/6
✓ Branch 0 taken 348 times.
✓ Branch 1 taken 122 times.
✓ Branch 3 taken 152 times.
✓ Branch 4 taken 196 times.
✓ Branch 5 taken 152 times.
✓ Branch 6 taken 318 times.
470x if(flush && chunked_())
478 {
479 152x auto const s = buffered_().size() + src.remain;
480
2/2
✓ Branch 0 taken 97 times.
✓ Branch 1 taken 55 times.
152x if(s != 0)
481 97x open_chunk_(s);
482 }
483 470x return flush;
484 }
485
486 std::span<capy::const_buffer const>
487 1086x serializer::
488 gather_(
489 std::span<capy::const_buffer> dest,
490 source& src,
491 bool flush_body,
492 bool flush_header) noexcept
493 {
494 1086x std::size_t n = 0;
495
496 // returns false when dest is exhausted
497 3033x auto const push = [&](capy::const_buffer b)
498 {
499
2/2
✓ Branch 1 taken 1094 times.
✓ Branch 2 taken 1939 times.
3033x if(b.size() == 0)
500 1094x return true;
501
2/2
✓ Branch 1 taken 15 times.
✓ Branch 2 taken 1924 times.
1939x if(n == dest.size())
502 15x return false;
503 1924x dest[n++] = b;
504 1924x return true;
505 1086x };
506
507 // [header]
508
2/2
✓ Branch 0 taken 1033 times.
✓ Branch 1 taken 53 times.
1086x if(flush_header)
509 1033x push(suffix(capy::make_buffer(
510 1033x msg_->buffer()), header_offset_));
511
512 // [CRLF + chunk_header][buffered]
513 1086x auto const& buffered = buffered_();
514 1086x auto buffered_take = std::uint64_t(buffered.size());
515
2/2
✓ Branch 1 taken 714 times.
✓ Branch 2 taken 372 times.
1086x if(chunked_())
516 714x buffered_take = (std::min)(buffered_take, owed_);
517
2/2
✓ Branch 0 taken 38 times.
✓ Branch 1 taken 334 times.
372x else if(!flush_body)
518 38x buffered_take = 0;
519 1086x push({ buffered.ptr + buffered.pos - prefix_rem_,
520 1086x prefix_rem_ + std::size_t(buffered_take) });
521
522 // [supplied body]
523 1086x auto const quota = owed_ - buffered_take;
524 1086x auto const supplied = src.remain;
525 1086x std::size_t placed = 0;
526
2/2
✓ Branch 1 taken 914 times.
✓ Branch 2 taken 172 times.
1086x if(!enc_)
527 {
528
2/2
✓ Branch 0 taken 659 times.
✓ Branch 1 taken 825 times.
1484x while(placed != quota)
529 {
530 659x auto const b = src.next();
531
2/2
✓ Branch 1 taken 81 times.
✓ Branch 2 taken 578 times.
659x if(b.size() == 0)
532 89x break;
533 578x auto const k = clamp(quota - placed, b.size());
534
2/2
✓ Branch 3 taken 8 times.
✓ Branch 4 taken 570 times.
578x if(!push({ b.data(), k }))
535 8x break;
536 570x placed += k;
537 }
538 }
539 1086x input_framed_ = placed;
540
541 // [epilogue][trailer]
542
4/4
✓ Branch 2 taken 329 times.
✓ Branch 3 taken 131 times.
✓ Branch 5 taken 173 times.
✓ Branch 6 taken 156 times.
1546x if( sealed_() && !enc_ && chunked_() &&
543
8/8
✓ Branch 0 taken 460 times.
✓ Branch 1 taken 626 times.
✓ Branch 3 taken 170 times.
✓ Branch 4 taken 3 times.
✓ Branch 5 taken 168 times.
✓ Branch 6 taken 2 times.
✓ Branch 7 taken 168 times.
✓ Branch 8 taken 918 times.
1546x buffered.size() + supplied == owed_ &&
544 placed == supplied)
545 {
546 168x auto const eb = epilogue_buf_();
547 168x auto const tb = trailer_buf_();
548 168x auto const k = clamp(tail_offset_, eb.size());
549 168x push(suffix(eb, k));
550 168x push(suffix(tb, tail_offset_ - k));
551 }
552
553 1086x return { dest.data(), n };
554 }
555
556 system::result<
557 std::span<capy::const_buffer const>,
558 std::error_code>
559 1276x serializer::
560 frame_(
561 std::span<capy::const_buffer> dest,
562 source& src,
563 bool more)
564 {
565
4/4
✓ Branch 0 taken 152 times.
✓ Branch 1 taken 952 times.
✓ Branch 2 taken 168 times.
✓ Branch 3 taken 4 times.
1276x switch(state_)
566 {
567 152x case state::started:
568 152x select_framing_();
569 BOOST_FALLTHROUGH;
570 1104x case state::streaming:
571 case state::sealed:
572 {
573
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1104 times.
1104x BOOST_ASSERT(input_digested_ == 0);
574
575 1104x auto const drain_call = (src.remain == 0);
576
577 1104x bool flush_body = true;
578
6/6
✓ Branch 0 taken 734 times.
✓ Branch 1 taken 370 times.
✓ Branch 3 taken 314 times.
✓ Branch 4 taken 420 times.
✓ Branch 5 taken 684 times.
✓ Branch 6 taken 420 times.
1104x if(owed_ == 0 || !chunked_())
579 {
580
1/1
✓ Branch 1 taken 684 times.
684x auto const r = ingest_(src, more);
581
2/2
✓ Branch 1 taken 18 times.
✓ Branch 2 taken 666 times.
684x if(r.has_error())
582 18x return r.error();
583
1/1
✓ Branch 1 taken 666 times.
666x flush_body = *r;
584 }
585
586 1086x auto const out = gather_(
587
4/4
✓ Branch 0 taken 149 times.
✓ Branch 1 taken 937 times.
✓ Branch 2 taken 96 times.
✓ Branch 3 taken 53 times.
1086x dest, src, flush_body, flush_body || drain_call);
588
589
6/6
✓ Branch 1 taken 142 times.
✓ Branch 2 taken 944 times.
✓ Branch 4 taken 139 times.
✓ Branch 5 taken 3 times.
✓ Branch 6 taken 139 times.
✓ Branch 7 taken 947 times.
1086x if(out.empty() && !dest.empty())
590 {
591
4/4
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 130 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 6 times.
139x if(!more && owed_ != 0)
592 {
593
4/6
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 1 time.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 3 times.
✗ Branch 7 not taken.
3x if(to_eof_() || chunked_())
594 6x return make_error_code(
595 3x std::errc::invalid_argument);
596 ✗ return error::body_size_mismatch;
597 }
598
5/6
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 130 times.
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 6 times.
✓ Branch 7 taken 130 times.
136x if(sealed_() && settled_())
599 6x state_ = state::done;
600 }
601
602 1083x return out;
603 }
604 168x case state::done:
605
5/6
✓ Branch 0 taken 165 times.
✓ Branch 1 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 165 times.
✓ Branch 5 taken 3 times.
✓ Branch 6 taken 165 times.
168x if(src.remain != 0 || !stage_.empty())
606 6x return make_error_code(
607 3x std::errc::invalid_argument);
608 165x return {};
609 4x default:
610 8x return make_error_code(
611 4x std::errc::state_not_recoverable);
612 };
613 }
614
615 std::size_t
616 1270x serializer::
617 consume(std::size_t n) noexcept
618 {
619 // [header]
620 {
621 1270x auto const k = clamp(
622 1270x n, msg_->buffer().size() - header_offset_);
623 1270x header_offset_ += static_cast<std::uint32_t>(k);
624 1270x n -= k;
625 }
626
627 // [CRLF + chunk_header]
628 {
629 1270x auto const k = clamp(n, prefix_rem_);
630 1270x prefix_rem_ -= static_cast<std::uint8_t>(k);
631 1270x n -= k;
632 }
633
634 // [buffered]
635 {
636 1270x auto& buffered = buffered_();
637 1270x auto const rem = clamp(owed_, buffered.size());
638 1270x auto const k = clamp(n, rem);
639 1270x buffered.consume(k);
640 1270x owed_ -= k;
641 1270x n -= k;
642 }
643
644 // [supplied body]
645 std::size_t input_taken;
646 {
647 1270x auto const k = clamp(n, input_framed_);
648 1270x input_framed_ -= k;
649 1270x owed_ -= k;
650 1270x input_taken = k;
651 1270x n -= k;
652 }
653
654
2/2
✓ Branch 1 taken 454 times.
✓ Branch 2 taken 816 times.
1270x if(sealed_())
655 {
656 // [epilogue][trailer]
657
2/2
✓ Branch 1 taken 178 times.
✓ Branch 2 taken 276 times.
454x if(chunked_())
658 {
659 178x auto const total = epilogue_buf_().size() +
660 178x trailer_buf_().size();
661 178x auto const k = clamp(n, total - tail_offset_);
662 178x tail_offset_ += static_cast<std::uint32_t>(k);
663 178x n -= k;
664 }
665
666
2/2
✓ Branch 1 taken 169 times.
✓ Branch 2 taken 285 times.
454x if(settled_())
667 169x state_ = state::done;
668 }
669
670
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1270 times.
1270x BOOST_ASSERT(n == 0);
671
672
2/2
✓ Branch 0 taken 91 times.
✓ Branch 1 taken 1179 times.
1270x if(input_digested_ != 0)
673 91x return std::exchange(input_digested_, 0);
674
675 1179x return input_taken;
676 }
677
678 } // namespace burl
679 } // namespace boost
680