src/detail/serializer.cpp

100.0% Lines (116/116) 100.0% List of functions (20/20) 96.4% Branches (54/56)
serializer.cpp
f(x) Functions (20)
Function Calls Lines Branches Blocks
boost::burl::detail::serializer::serializer(boost::burl::detail::serializer::config const&, boost::capy::any_write_stream*) :27 102x 100.0% 100.0% boost::burl::detail::serializer::serializer(boost::burl::detail::serializer&&) :41 4x 100.0% 100.0% boost::burl::detail::serializer::operator=(boost::burl::detail::serializer&&) :65 3x 100.0% 100.0% 100.0% boost::burl::detail::serializer::~serializer() :74 106x 100.0% 75.0% 100.0% boost::burl::detail::serializer::reset(boost::capy::any_write_stream*, boost::http::message_base*, boost::burl::detail::serializer::encoder*, bool) :82 113x 100.0% 100.0% 100.0% boost::burl::detail::serializer::write_eof() :111 70x 100.0% 100.0% boost::burl::detail::serializer::prepare(std::span<boost::capy::mutable_buffer, 18446744073709551615ul>) :118 25x 100.0% 100.0% 100.0% boost::burl::detail::serializer::commit(unsigned long) :128 14x 100.0% 100.0% 44.0% boost::burl::detail::serializer::commit_eof(unsigned long) :140 75x 100.0% 100.0% 44.0% boost::burl::detail::serializer::capacity() const :151 198x 100.0% 100.0% boost::burl::detail::serializer::do_prepare() :158 46x 100.0% 100.0% boost::burl::detail::serializer::do_commit(unsigned long) :165 113x 100.0% 100.0% boost::burl::detail::serializer::should_coalesce(unsigned long) const :172 71x 100.0% 100.0% 100.0% boost::burl::detail::serializer::finalize(unsigned long) :183 96x 100.0% 100.0% 100.0% boost::burl::detail::serializer::decide_framing(unsigned long) :204 87x 100.0% 90.0% 93.0% boost::burl::detail::serializer::process(std::span<boost::capy::const_buffer const, 18446744073709551615ul>, bool) :216 172x 100.0% 100.0% 100.0% boost::burl::detail::serializer::encode(std::span<boost::capy::const_buffer const, 18446744073709551615ul>, bool) :227 71x 100.0% 100.0% 44.0% boost::burl::detail::serializer::flush(std::span<boost::capy::const_buffer const, 18446744073709551615ul>, bool) :284 141x 100.0% 100.0% 44.0% boost::burl::detail::serializer::flush(std::span<boost::capy::const_buffer const, 18446744073709551615ul>, bool)::{lambda(boost::capy::const_buffer)#1}::operator()(boost::capy::const_buffer) const :299 237x 100.0% 100.0% boost::burl::detail::serializer::flush(std::span<boost::capy::const_buffer const, 18446744073709551615ul>, bool)::{lambda()#1}::operator()() const :334 88x 100.0% 100.0% 100.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 "serializer.hpp"
11
12 #include <boost/assert.hpp>
13 #include <boost/capy/buffers/make_buffer.hpp>
14 #include <boost/capy/cond.hpp>
15 #include <boost/capy/write_at_least.hpp>
16
17 #include <cstring>
18 #include <utility>
19
20 namespace boost
21 {
22 namespace burl
23 {
24 namespace detail
25 {
26
27 102x serializer::
28 102x serializer(config const& cfg, capy::any_write_stream* stream)
29 102x : stream_(stream)
30 102x , min_prepare_(cfg.min_prepare)
31 102x , direct_thr_(cfg.direct_thr)
32 102x , enc_thr_(cfg.enc_thr)
33 102x , out_(new unsigned char[
34 204x cfg.out_buffer + cfg.enc_buffer + margin] + margin)
35 102x , out_cap_(cfg.enc_buffer)
36 102x , in_(out_)
37 102x , in_cap_(cfg.out_buffer)
38 {
39 102x }
40
41 4x serializer::
42 4x serializer(serializer&& other) noexcept
43 4x : stream_(other.stream_)
44 4x , msg_(other.msg_)
45 4x , enc_(other.enc_)
46 4x , min_prepare_(other.min_prepare_)
47 4x , direct_thr_(other.direct_thr_)
48 4x , enc_thr_(other.enc_thr_)
49 4x , out_(other.out_)
50 4x , out_cap_(other.out_cap_)
51 4x , out_len_(other.out_len_)
52 4x , in_(other.in_)
53 4x , in_cap_(other.in_cap_)
54 4x , in_len_(other.in_len_)
55 4x , total_body_(other.total_body_)
56 4x , head_(other.head_)
57 4x , enc_started_(other.enc_started_)
58 4x , hdr_sent_(other.hdr_sent_)
59 4x , done_(other.done_)
60 {
61 4x other.out_ = nullptr;
62 4x }
63
64 serializer&
65 3x serializer::
66 operator=(serializer&& other) noexcept
67 {
68
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 2 times.
3x if(this == &other)
69 1x return *this;
70 2x this->~serializer();
71 2x return *new(this) serializer(std::move(other));
72 }
73
74 106x serializer::
75 ~serializer()
76 {
77
2/2
✓ Branch 0 taken 102 times.
✓ Branch 1 taken 4 times.
106x if(out_)
78
1/2
✓ Branch 0 taken 102 times.
✗ Branch 1 not taken.
102x delete[](out_ - margin);
79 106x }
80
81 void
82 113x serializer::
83 reset(
84 capy::any_write_stream* stream,
85 http::message_base* msg,
86 encoder* enc,
87 bool head) noexcept
88 {
89
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 104 times.
113x if(head)
90 9x enc = nullptr;
91
92
2/2
✓ Branch 0 taken 32 times.
✓ Branch 1 taken 81 times.
113x if(!enc == (in_ != out_))
93 {
94 32x std::swap(in_cap_, out_cap_);
95
2/2
✓ Branch 0 taken 31 times.
✓ Branch 1 taken 1 time.
32x in_ = enc ? out_ + out_cap_ : out_;
96 }
97
98 113x stream_ = stream;
99 113x msg_ = msg;
100 113x enc_ = enc;
101 113x in_len_ = 0;
102 113x out_len_ = 0;
103 113x total_body_ = 0;
104 113x head_ = head;
105 113x enc_started_ = false;
106 113x hdr_sent_ = false;
107 113x done_ = false;
108 113x }
109
110 capy::io_task<>
111 70x serializer::
112 write_eof()
113 {
114 70x return commit_eof(0);
115 }
116
117 std::span<capy::mutable_buffer>
118 25x serializer::
119 prepare(std::span<capy::mutable_buffer> dest)
120 {
121
6/6
✓ Branch 1 taken 24 times.
✓ Branch 2 taken 1 time.
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 22 times.
✓ Branch 6 taken 3 times.
✓ Branch 7 taken 22 times.
25x if(dest.empty() || capacity() == 0)
122 3x return dest.first(0);
123 22x dest[0] = do_prepare();
124 22x return dest.first(1);
125 }
126
127 capy::io_task<>
128
1/1
✓ Branch 1 taken 14 times.
14x serializer::
129 commit(std::size_t n)
130 {
131 BOOST_ASSERT(n <= capacity());
132 do_commit(n);
133 if(capacity() >= min_prepare_)
134 co_return {};
135 auto [ec, _] = co_await process({}, false);
136 co_return { ec };
137 28x }
138
139 capy::io_task<>
140
1/1
✓ Branch 1 taken 75 times.
75x serializer::
141 commit_eof(std::size_t n)
142 {
143 BOOST_ASSERT(n <= capacity());
144 do_commit(n);
145 finalize(0);
146 auto [ec, _] = co_await process({}, true);
147 co_return { ec };
148 150x }
149
150 std::size_t
151 198x serializer::
152 capacity() const noexcept
153 {
154 198x return in_cap_ - in_len_;
155 }
156
157 capy::mutable_buffer
158 46x serializer::
159 do_prepare() noexcept
160 {
161 46x return { in_ + in_len_, in_cap_ - in_len_ };
162 }
163
164 void
165 113x serializer::
166 do_commit(std::size_t n) noexcept
167 {
168 113x in_len_ += n;
169 113x }
170
171 bool
172 71x serializer::
173 should_coalesce(std::size_t avail) const noexcept
174 {
175
2/2
✓ Branch 1 taken 26 times.
✓ Branch 2 taken 45 times.
71x if(avail > capacity())
176 26x return false;
177
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 35 times.
45x if(enc_)
178
4/4
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 5 times.
✓ Branch 3 taken 1 time.
10x return !enc_started_ && avail + in_len_ < enc_thr_;
179 35x return avail < direct_thr_;
180 }
181
182 void
183 96x serializer::
184 finalize(std::size_t remaining) noexcept
185 {
186
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 88 times.
96x if(head_)
187 8x return;
188
189
2/2
✓ Branch 0 taken 27 times.
✓ Branch 1 taken 61 times.
88x if(enc_)
190 {
191
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 17 times.
27x if(enc_started_)
192 10x return;
193
194
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 6 times.
17x if(remaining + in_len_ >= enc_thr_)
195 11x return;
196
197 6x msg_->erase(http::field::content_encoding);
198 6x enc_ = nullptr;
199 }
200 67x decide_framing(remaining);
201 }
202
203 void
204 87x serializer::
205 decide_framing(std::size_t remaining) noexcept
206 {
207
6/6
✓ Branch 1 taken 37 times.
✓ Branch 2 taken 50 times.
✓ Branch 3 taken 19 times.
✓ Branch 4 taken 18 times.
✓ Branch 5 taken 69 times.
✓ Branch 6 taken 18 times.
87x if(!msg_->chunked() || hdr_sent_)
208 69x return;
209
210
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
18x BOOST_ASSERT(total_body_ == 0);
211 18x msg_->erase(http::field::transfer_encoding);
212
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 10 times.
18x msg_->set_content_length((enc_ ? out_len_ : in_len_) + remaining);
213 }
214
215 capy::io_task<std::size_t>
216 172x serializer::
217 process(
218 std::span<capy::const_buffer const> tail,
219 bool eof)
220 {
221
2/2
✓ Branch 0 taken 71 times.
✓ Branch 1 taken 101 times.
172x if(enc_)
222 71x return encode(tail, eof);
223 101x return flush(tail, eof);
224 }
225
226 capy::io_task<std::size_t>
227
1/1
✓ Branch 1 taken 71 times.
71x serializer::
228 encode(
229 std::span<capy::const_buffer const> tail,
230 bool eof)
231 {
232 std::size_t n = 0;
233 std::size_t consumed = 0;
234 capy::const_buffer in = { in_, in_len_ };
235 for(;;)
236 {
237 if(in.size() == 0)
238 {
239 if(n != tail.size())
240 in = tail[n++];
241 else if(!eof)
242 co_return { {}, consumed };
243 }
244
245 if(out_len_ == out_cap_)
246 {
247 if(auto [ec, _] = co_await flush({}, false); ec)
248 co_return { ec, consumed };
249 }
250
251 auto r = enc_->process(
252 { out_ + out_len_, out_cap_ - out_len_ },
253 in,
254 eof && n == tail.size());
255
256 enc_started_ = true;
257 out_len_ += r.produced;
258 in += r.consumed;
259 if(in_len_ != 0)
260 in_len_ -= r.consumed;
261 else
262 consumed += r.consumed;
263
264 if(r.ec)
265 {
266 if(r.ec == capy::cond::eof)
267 {
268 decide_framing(0);
269 auto [ec, _] = co_await flush({}, true);
270 co_return { ec, consumed };
271 }
272 else
273 {
274 co_return { r.ec, consumed };
275 }
276 }
277
278 if(!eof && consumed != 0)
279 co_return { {}, consumed };
280 }
281 142x }
282
283 capy::io_task<std::size_t>
284
1/1
✓ Branch 1 taken 141 times.
141x serializer::
285 flush(
286 std::span<capy::const_buffer const> tail,
287 bool eof)
288 {
289 auto const buf = enc_ ? out_ : in_;
290 auto& len = enc_ ? out_len_ : in_len_;
291 auto const tail_len = capy::buffer_size(tail);
292 auto const chunked = msg_->chunked() && !head_;
293 BOOST_ASSERT(eof || len + tail_len != 0);
294 BOOST_ASSERT(tail.size() <= capy::detail::max_iovec_);
295
296 capy::const_buffer vec[capy::detail::max_iovec_ + 3];
297 std::size_t n = 0;
298 std::size_t sum = 0;
299 237x auto const append = [&](capy::const_buffer b)
300 {
301 237x vec[n++] = b;
302 237x sum += b.size();
303 237x };
304
305 if(!hdr_sent_)
306 append(capy::make_buffer(msg_->buffer()));
307
308 if(chunked)
309 {
310 static constexpr char hex[] = "0123456789abcdef";
311 auto s = len + tail_len;
312 auto p = buf;
313
314 *--p = '\n';
315 *--p = '\r';
316
317 do
318 {
319 *--p = hex[s & 0xF];
320 s >>= 4;
321 } while(s != 0);
322
323 // prev chunk's CRLF
324 if(total_body_)
325 {
326 *--p = '\n';
327 *--p = '\r';
328 }
329
330 append({ p, static_cast<std::size_t>(buf - p) + len });
331 }
332 else
333 {
334 88x auto const decl = [&]()-> std::uint64_t
335 {
336
6/6
✓ Branch 0 taken 79 times.
✓ Branch 1 taken 9 times.
✓ Branch 3 taken 34 times.
✓ Branch 4 taken 45 times.
✓ Branch 5 taken 43 times.
✓ Branch 6 taken 45 times.
88x if(head_ || msg_->payload() != http::payload::size)
337 43x return 0;
338 45x return msg_->payload_size();
339 }();
340 auto const prod = total_body_ + len + tail_len;
341
342 if(prod > decl || (eof && prod != decl))
343 co_return { error::body_size_mismatch, 0 };
344
345 if(len != 0)
346 append({ buf, len });
347 }
348
349 auto const owned = sum;
350
351 for(auto& b : tail)
352 append(b);
353
354 if(chunked && eof)
355 append({ "\r\n0\r\n\r\n", len || tail_len ? 7u : 2u });
356
357 auto const need = chunked || eof ? sum : owned + !!tail_len;
358 auto [ec, written] = co_await capy::write_at_least(
359 *stream_, std::span{ vec, n }, need);
360 if(ec)
361 co_return { ec, 0 };
362
363 auto const consumed = (std::min)(written - owned, tail_len);
364 total_body_ += len + consumed;
365 len = 0;
366 hdr_sent_ = true;
367 done_ = eof;
368 co_return { {}, consumed };
369 282x }
370
371 } // namespace detail
372 } // namespace burl
373 } // namespace boost
374