src/fields_base.cpp

99.3% Lines (454/457) 100.0% List of functions (51/51) 91.3% Branches (209/229)
fields_base.cpp
f(x) Functions (51)
Function Calls Lines Branches Blocks
boost::burl::(anonymous namespace)::to_lower(char) :34 33182x 100.0% 100.0% 100.0% boost::burl::(anonymous namespace)::ci_equal_rev(char const*, char const*, unsigned long) :42 16369x 100.0% 100.0% 100.0% boost::burl::(anonymous namespace)::grow_size(unsigned long, unsigned long) :75 757x 75.0% 50.0% 83.0% boost::burl::fields_base::fields_base() :86 389x 100.0% 100.0% boost::burl::fields_base::alloc::alloc(unsigned long) :98 903x 100.0% 100.0% boost::burl::fields_base::alloc::~alloc() :106 903x 75.0% 50.0% 67.0% boost::burl::fields_base::alloc::size_for(unsigned long) :114 912x 100.0% 100.0% boost::burl::fields_base::release_() :128 1675x 100.0% 100.0% 100.0% boost::burl::fields_base::adopt_(boost::burl::fields_base::alloc&) :136 903x 100.0% 100.0% boost::burl::fields_base::init_static_(char*, unsigned long) :146 30x 100.0% 100.0% 100.0% boost::burl::fields_base::realloc_(unsigned long) :162 60x 100.0% 100.0% 100.0% boost::burl::fields_base::operator=(boost::burl::fields_base const&) :175 99x 100.0% 100.0% 100.0% boost::burl::fields_base::swap_(boost::burl::fields_base&) :184 193x 100.0% 100.0% boost::burl::fields_base::assign_(boost::burl::fields_base const&, unsigned short) :194 113x 100.0% 86.7% 93.0% boost::burl::fields_base::reserve_(unsigned long, unsigned long) :223 59x 100.0% 90.0% 100.0% boost::burl::fields_base::shrink_to_fit_() :245 10x 100.0% 100.0% 100.0% boost::burl::fields_base::fill_(char*, boost::burl::fields_base::piece const&, boost::burl::fields_base::piece const&) :256 66899x 100.0% 100.0% boost::burl::fields_base::fill_(char*, boost::burl::fields_base::piece const&, boost::burl::fields_base::piece const&)::{lambda(char*, std::basic_string_view<char, std::char_traits<char> >)#1}::operator()(char*, std::basic_string_view<char, std::char_traits<char> >) const :262 133798x 100.0% 100.0% 100.0% boost::burl::fields_base::fill_(char*, boost::burl::fields_base::piece const&, boost::burl::fields_base::piece const&)::{lambda(char const*, unsigned long, std::basic_string_view<char, std::char_traits<char> >)#1}::operator()(char const*, unsigned long, std::basic_string_view<char, std::char_traits<char> >) const :268 66899x 100.0% 100.0% 100.0% boost::burl::fields_base::splice_(unsigned int, unsigned int, unsigned long, unsigned short, boost::burl::fields_base::piece, boost::burl::fields_base::piece) :289 66916x 96.9% 80.0% 90.0% boost::burl::fields_base::splice_(unsigned int, unsigned int, unsigned long, unsigned short, boost::burl::fields_base::piece, boost::burl::fields_base::piece)::{lambda(boost::burl::fields_base::piece)#1}::operator()(boost::burl::fields_base::piece) const :351 132184x 100.0% 100.0% 100.0% boost::burl::fields_base::splice_fields_(unsigned int, unsigned int, unsigned long, unsigned short, boost::burl::fields_base::piece, boost::burl::fields_base::piece) :373 66673x 100.0% 100.0% boost::burl::fields_base::splice_prefix_(unsigned int, unsigned int, unsigned long, boost::burl::fields_base::piece, boost::burl::fields_base::piece) :391 252x 100.0% 75.0% 89.0% boost::burl::fields_base::notify_(unsigned short) :410 66920x 100.0% 100.0% 100.0% boost::burl::fields_base::resolve_(std::basic_string_view<char, std::char_traits<char> >) :434 83750x 100.0% 100.0% 100.0% boost::burl::fields_base::find_(unsigned short, boost::http::field) const :444 901x 100.0% 100.0% 100.0% boost::burl::fields_base::find_(unsigned short, std::basic_string_view<char, std::char_traits<char> >) const :458 421x 100.0% 100.0% 100.0% boost::burl::fields_base::find_last_(unsigned short, boost::http::field) const :475 294x 100.0% 83.3% 90.0% boost::burl::fields_base::find_last_(unsigned short, std::basic_string_view<char, std::char_traits<char> >) const :493 5x 100.0% 80.0% 94.0% boost::burl::fields_base::at(boost::http::field) const :512 68x 100.0% 100.0% 100.0% boost::burl::fields_base::at(std::basic_string_view<char, std::char_traits<char> >) const :522 80x 100.0% 100.0% 100.0% boost::burl::fields_base::count(boost::http::field) const :532 13x 100.0% 100.0% 100.0% boost::burl::fields_base::count(std::basic_string_view<char, std::char_traits<char> >) const :547 8x 100.0% 100.0% 100.0% boost::burl::fields_base::subrange::iterator::operator++() :562 187x 100.0% 83.3% 91.0% boost::burl::fields_base::append(std::initializer_list<boost::burl::fields_base::field_view>) :601 37x 100.0% 100.0% 100.0% boost::burl::fields_base::insert_(unsigned short, unsigned short, std::basic_string_view<char, std::char_traits<char> >, std::basic_string_view<char, std::char_traits<char> >) :626 66644x 100.0% 85.7% 94.0% boost::burl::fields_base::replace_value_(unsigned short, std::basic_string_view<char, std::char_traits<char> >) :665 38x 100.0% 83.3% 95.0% boost::burl::fields_base::erase_at_(unsigned short) :694 109x 100.0% 75.0% 92.0% unsigned short boost::burl::fields_base::erase_all_<boost::burl::fields_base::erase_all_(unsigned short, std::basic_string_view<char, std::char_traits<char> >)::{lambda(boost::burl::fields_base::entry const&)#1}>(unsigned short, unsigned short, boost::burl::fields_base::erase_all_(unsigned short, std::basic_string_view<char, std::char_traits<char> >)::{lambda(boost::burl::fields_base::entry const&)#1} const&) :716 105x 100.0% 90.0% 96.0% unsigned short boost::burl::fields_base::erase_all_<boost::burl::fields_base::erase_all_(unsigned short, unsigned short)::{lambda(boost::burl::fields_base::entry const&)#1}>(unsigned short, unsigned short, boost::burl::fields_base::erase_all_(unsigned short, unsigned short)::{lambda(boost::burl::fields_base::entry const&)#1} const&) :716 47x 100.0% 90.0% 96.0% boost::burl::fields_base::erase_all_(unsigned short, unsigned short) :765 47x 100.0% 100.0% boost::burl::fields_base::erase_all_(unsigned short, unsigned short)::{lambda(boost::burl::fields_base::entry const&)#1}::operator()(boost::burl::fields_base::entry const&) const :771 81x 100.0% 100.0% boost::burl::fields_base::erase_all_(unsigned short, std::basic_string_view<char, std::char_traits<char> >) :778 105x 100.0% 100.0% boost::burl::fields_base::erase_all_(unsigned short, std::basic_string_view<char, std::char_traits<char> >)::{lambda(boost::burl::fields_base::entry const&)#1}::operator()(boost::burl::fields_base::entry const&) const :784 10112x 100.0% 100.0% 100.0% boost::burl::fields_base::erase_dups_(unsigned short) :792 31x 100.0% 100.0% 100.0% boost::burl::fields_base::set(boost::http::field, std::basic_string_view<char, std::char_traits<char> >) :812 301x 100.0% 100.0% 100.0% boost::burl::fields_base::set(std::basic_string_view<char, std::char_traits<char> >, std::basic_string_view<char, std::char_traits<char> >) :825 9x 100.0% 100.0% 100.0% boost::burl::fields_base::erase(boost::http::field) :838 167x 100.0% 100.0% 100.0% boost::burl::fields_base::erase(std::basic_string_view<char, std::char_traits<char> >) :848 105x 100.0% 100.0% 100.0% boost::burl::fields_base::clear() :858 6x 100.0% 100.0% 100.0% boost::burl::operator<<(std::ostream&, boost::burl::fields_base const&) :871 6x 100.0% 100.0% 100.0%
Line Branch TLA Hits Source Code
1 //
2 // Copyright (c) 2021 Vinnie Falco ([email protected])
3 // Copyright (c) 2026 Mohammad Nejati
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/burl
9 //
10
11 #include <boost/burl/fields_base.hpp>
12
13 #include "detail/except.hpp"
14
15 #include <boost/throw_exception.hpp>
16
17 #include <algorithm>
18 #include <cstring>
19 #include <new>
20 #include <ostream>
21 #include <utility>
22
23 namespace boost
24 {
25 namespace burl
26 {
27
28 namespace
29 {
30
31 char buf[] = "\r\n";
32
33 constexpr char
34 33182x to_lower(char c) noexcept
35 {
36
4/4
✓ Branch 0 taken 1062 times.
✓ Branch 1 taken 32120 times.
✓ Branch 2 taken 553 times.
✓ Branch 3 taken 509 times.
33182x if(c >= 'A' && c <= 'Z')
37 553x return static_cast<char>(c - 'A' + 'a');
38 32629x return c;
39 }
40
41 bool
42 16369x ci_equal_rev(
43 char const* p1,
44 char const* p2,
45 std::size_t n) noexcept
46 {
47 16369x p1 += n;
48 16369x p2 += n;
49 char a, b;
50 // fast loop
51
2/2
✓ Branch 0 taken 21300 times.
✓ Branch 1 taken 297 times.
21597x while(n != 0)
52 {
53 21300x --n;
54 21300x a = *--p1;
55 21300x b = *--p2;
56
2/2
✓ Branch 0 taken 16072 times.
✓ Branch 1 taken 5228 times.
21300x if(a != b)
57 16072x goto slow;
58 }
59 297x return true;
60 for(;;)
61 {
62 519x a = *--p1;
63 519x b = *--p2;
64 16591x slow:
65
2/2
✓ Branch 2 taken 15839 times.
✓ Branch 3 taken 752 times.
16591x if(to_lower(a) != to_lower(b))
66 15839x return false;
67
2/2
✓ Branch 0 taken 233 times.
✓ Branch 1 taken 519 times.
752x if(n == 0)
68 233x break;
69 519x --n;
70 }
71 233x return true;
72 }
73
74 std::size_t
75 757x grow_size(
76 std::size_t need,
77 std::size_t cap) noexcept
78 {
79
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 757 times.
757x if(cap > std::size_t(-1) - cap)
80 return need;
81 757x return (std::max)(need, 2 * cap);
82 }
83
84 } // namespace
85
86 389x fields_base::
87 389x fields_base() noexcept
88 389x : fields_base(buf, 0, 2, 0)
89 {
90 389x }
91
92 struct fields_base::alloc
93 {
94 std::size_t n;
95 char* p;
96
97 explicit
98 903x alloc(std::size_t n_)
99 903x : n(size_for(n_))
100 903x , p(static_cast<char*>(::operator new(n)))
101 {
102 903x }
103
104 alloc(alloc const&) = delete;
105
106 903x ~alloc()
107 {
108
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 903 times.
903x if(p)
109 ::operator delete(p);
110 903x }
111
112 static
113 std::size_t
114 912x size_for(std::size_t n) noexcept
115 {
116 912x constexpr auto align = alignof(entry);
117 912x return (n + (align - 1)) & ~(align - 1);
118 }
119 };
120
121 //------------------------------------------------
122 //
123 // Owning storage
124 //
125 //------------------------------------------------
126
127 void
128 1675x fields_base::
129 release_() noexcept
130 {
131
2/2
✓ Branch 1 taken 903 times.
✓ Branch 2 taken 772 times.
1675x if(owns_())
132 903x ::operator delete(base_());
133 1675x }
134
135 void
136 903x fields_base::
137 adopt_(alloc& a) noexcept
138 {
139 903x release_();
140 903x end_ = a.p + a.n;
141 903x buf_ = a.p + prefix_;
142 903x a.p = nullptr;
143 903x }
144
145 void
146 30x fields_base::
147 init_static_(
148 char* storage,
149 std::size_t n)
150 {
151 30x auto const a = reinterpret_cast<std::uintptr_t>(storage);
152 30x auto const e = (a + n) & ~std::uintptr_t(alignof(entry) - 1);
153 30x auto const need = std::size_t(prefix_) + size_;
154
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 27 times.
30x if(e < a + need)
155 3x detail::throw_length_error("buffer too small");
156 27x std::memcpy(storage, base_(), need);
157 27x buf_ = storage + prefix_;
158 27x end_ = storage + (e - a);
159 27x }
160
161 void
162 60x fields_base::
163 realloc_(std::size_t total)
164 {
165
2/2
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 59 times.
60x if(static_())
166 1x detail::throw_length_error("buffer limit exceeded");
167 59x auto const tn = table_space_(count_);
168
1/1
✓ Branch 1 taken 59 times.
59x alloc a(total);
169 59x std::memcpy(a.p, base_(), std::size_t(prefix_) + size_);
170 59x std::memcpy(a.p + a.n - tn, end_ - tn, tn);
171 59x adopt_(a);
172 59x }
173
174 fields_base&
175 99x fields_base::
176 operator=(fields_base const& other)
177 {
178
2/2
✓ Branch 0 taken 95 times.
✓ Branch 1 taken 4 times.
99x if(this != &other)
179 95x assign_(other, other.prefix_);
180 93x return *this;
181 }
182
183 void
184 193x fields_base::swap_(fields_base& other) noexcept
185 {
186 193x std::swap(buf_, other.buf_);
187 193x std::swap(end_, other.end_);
188 193x std::swap(size_, other.size_);
189 193x std::swap(count_, other.count_);
190 193x std::swap(prefix_, other.prefix_);
191 193x }
192
193 void
194 113x fields_base::
195 assign_(
196 fields_base const& other,
197 std::uint16_t prefix)
198 {
199
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 113 times.
113x BOOST_ASSERT(this != &other);
200
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 113 times.
113x BOOST_ASSERT(prefix <= other.prefix_);
201
6/6
✓ Branch 1 taken 89 times.
✓ Branch 2 taken 24 times.
✓ Branch 4 taken 3 times.
✓ Branch 5 taken 86 times.
✓ Branch 6 taken 3 times.
✓ Branch 7 taken 110 times.
113x if(default_() && other.default_())
202 3x return;
203 110x auto const tn = table_space_(other.count_);
204 110x auto const need = std::size_t(prefix) + other.size_ + tn;
205
2/2
✓ Branch 1 taken 95 times.
✓ Branch 2 taken 15 times.
110x if(need > capacity_in_bytes())
206 {
207
2/2
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 87 times.
95x if(static_())
208 8x detail::throw_length_error("buffer limit exceeded");
209
1/1
✓ Branch 1 taken 87 times.
87x alloc a(need);
210 87x adopt_(a);
211 87x }
212 102x auto* const base = base_();
213 102x buf_ = base + prefix;
214 102x size_ = other.size_;
215 102x count_ = other.count_;
216 102x prefix_ = prefix;
217 102x std::memcpy(
218 102x base, other.buf_ - prefix, std::size_t(prefix) + other.size_);
219 102x std::memcpy(end_ - tn, other.end_ - tn, tn);
220 }
221
222 void
223 59x fields_base::
224 reserve_(
225 std::size_t bytes,
226 std::size_t count)
227 {
228
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 57 times.
59x if(bytes > max_buffer_size)
229 2x detail::throw_length_error("buffer limit exceeded");
230
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 55 times.
57x if(count > max_field_count)
231 2x detail::throw_length_error("field count too large");
232
233 55x bytes = (std::max)(bytes, std::size_t(size_));
234 55x count = (std::max)(count, std::size_t(count_));
235
236
3/4
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 51 times.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
55x if(bytes <= size_ && count <= count_)
237 4x return;
238
239 51x auto const total = table_space_(count) + bytes + prefix_;
240
2/2
✓ Branch 1 taken 49 times.
✓ Branch 2 taken 2 times.
51x if(total > capacity_in_bytes())
241 49x realloc_(total);
242 }
243
244 void
245 10x fields_base::
246 shrink_to_fit_()
247 {
248
2/2
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 9 times.
10x if(! owns_())
249 1x return;
250 9x auto const total = table_space_(count_) + size_ + prefix_;
251
2/2
✓ Branch 2 taken 7 times.
✓ Branch 3 taken 2 times.
9x if(alloc::size_for(total) < capacity_in_bytes())
252 7x realloc_(total);
253 }
254
255 void
256 66899x fields_base::
257 fill_(
258 char* g,
259 piece const& p0,
260 piece const& p1) noexcept
261 {
262 133798x auto const put = [](char* d, std::string_view s) noexcept
263 {
264
2/2
✓ Branch 1 taken 68069 times.
✓ Branch 2 taken 65729 times.
133798x if(!s.empty())
265 68069x std::memmove(d, s.data(), s.size());
266 133798x };
267
268 66899x auto const overlaps = [](
269 char const* d,
270 std::size_t n,
271 std::string_view s) noexcept
272 {
273
4/4
✓ Branch 2 taken 449 times.
✓ Branch 3 taken 66450 times.
✓ Branch 5 taken 3 times.
✓ Branch 6 taken 446 times.
66899x return d < s.data() + s.size() && s.data() < d + n;
274 };
275
276
2/2
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 66896 times.
66899x if(overlaps(g + p0.at, p0.src.size(), p1.src))
277 {
278 3x put(g + p1.at, p1.src);
279 3x put(g + p0.at, p0.src);
280 }
281 else
282 {
283 66896x put(g + p0.at, p0.src);
284 66896x put(g + p1.at, p1.src);
285 }
286 66899x }
287
288 char*
289 66916x fields_base::
290 splice_(
291 std::uint32_t pos,
292 std::uint32_t old_n,
293 std::size_t new_n,
294 std::uint16_t added,
295 piece p0,
296 piece p1)
297 {
298 66916x auto* const base = base_();
299 66916x auto const head = std::size_t(prefix_) + size_;
300
301
2/4
✓ Branch 0 taken 66916 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 66916 times.
✗ Branch 3 not taken.
66916x BOOST_ASSERT(pos <= head && old_n <= head - pos);
302
303 // checked before the sums below, so they cannot wrap
304
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 66916 times.
66916x if(new_n > max_buffer_size - (head - old_n))
305 detail::throw_length_error("buffer limit exceeded");
306
307 auto const need =
308 66916x head - old_n + new_n + table_space_(count_ + added);
309
310
2/2
✓ Branch 1 taken 774 times.
✓ Branch 2 taken 66142 times.
66916x if(need > capacity_in_bytes())
311 {
312
2/2
✓ Branch 1 taken 17 times.
✓ Branch 2 taken 757 times.
774x if(static_())
313 17x detail::throw_length_error("buffer limit exceeded");
314
315 757x auto const tn = table_space_(count_);
316
1/1
✓ Branch 3 taken 757 times.
757x alloc a(grow_size(need, capacity_in_bytes()));
317
318 757x std::memcpy(a.p, base, pos);
319 757x std::memcpy(
320 757x a.p + pos + new_n,
321 757x base + pos + old_n,
322 757x head - pos - old_n);
323 757x std::memcpy(a.p + a.n - tn, end_ - tn, tn);
324
325 757x auto* const g = a.p + pos;
326 757x fill_(g, p0, p1);
327 757x adopt_(a);
328 757x return g;
329 757x }
330
331 66142x auto* const g = base + pos;
332 66142x auto const tail = head - pos - old_n;
333
334
2/2
✓ Branch 0 taken 23 times.
✓ Branch 1 taken 66119 times.
66142x if(new_n < old_n)
335 {
336 // fill first: the sources may view the bytes the
337 // tail is about to slide over
338
339 23x fill_(g, p0, p1);
340 23x std::memmove(g + new_n, g + old_n, tail);
341 }
342
2/2
✓ Branch 0 taken 66092 times.
✓ Branch 1 taken 27 times.
66119x else if(new_n > old_n)
343 {
344 66092x auto const delta = new_n - old_n;
345 66092x auto* const tail_end = g + old_n + tail;
346
347 66092x std::memmove(g + new_n, g + old_n, tail);
348
349 // a source which was in the tail moved with it
350
351 132184x auto const slide = [&](piece p) noexcept
352 {
353 132184x if(!p.src.empty() &&
354
6/6
✓ Branch 0 taken 66611 times.
✓ Branch 1 taken 65573 times.
✓ Branch 3 taken 541 times.
✓ Branch 4 taken 66070 times.
✓ Branch 5 taken 3 times.
✓ Branch 6 taken 132181 times.
132725x p.src.data() >= g + old_n &&
355
2/2
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 538 times.
541x p.src.data() < tail_end)
356 {
357 3x p.src = { p.src.data() + delta, p.src.size() };
358 }
359 132184x return p;
360 66092x };
361
362 66092x fill_(g, slide(p0), slide(p1));
363 }
364 else
365 {
366 27x fill_(g, p0, p1);
367 }
368
369 66142x return g;
370 }
371
372 char*
373 66673x fields_base::
374 splice_fields_(
375 std::uint32_t pos,
376 std::uint32_t old_n,
377 std::size_t new_n,
378 std::uint16_t added,
379 piece p0,
380 piece p1)
381 {
382 133332x auto* const g = splice_(
383 66673x prefix_ + pos, old_n, new_n, added, p0, p1);
384 66659x buf_ = g - pos;
385 66659x size_ = static_cast<std::uint32_t>(
386 66659x size_ - old_n + new_n);
387 66659x return g;
388 }
389
390 char*
391 252x fields_base::
392 splice_prefix_(
393 std::uint32_t pos,
394 std::uint32_t old_n,
395 std::size_t new_n,
396 piece p0,
397 piece p1)
398 {
399
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 252 times.
252x BOOST_ASSERT(pos + old_n <= prefix_);
400 252x auto const n = std::size_t(prefix_) - old_n + new_n;
401
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 243 times.
252x if(n > max_start_line_size)
402 9x detail::throw_length_error("start line too large");
403 243x auto* const g = splice_(pos, old_n, new_n, 0, p0, p1);
404 240x prefix_ = static_cast<std::uint16_t>(n);
405 240x buf_ = (g - pos) + prefix_;
406 240x return g;
407 }
408
409 void
410 66920x fields_base::notify_(std::uint16_t id) noexcept
411 {
412 66920x auto const f = static_cast<http::field>(id);
413
2/2
✓ Branch 0 taken 296 times.
✓ Branch 1 taken 66624 times.
66920x switch(f)
414 {
415 296x case http::field::connection:
416 case http::field::content_length:
417 case http::field::expect:
418 case http::field::transfer_encoding:
419 case http::field::upgrade:
420 296x on_special_(f);
421 296x break;
422 66624x default:
423 66624x break;
424 }
425 66920x }
426
427 //------------------------------------------------
428 //
429 // Lookup
430 //
431 //------------------------------------------------
432
433 std::uint16_t
434 83750x fields_base::
435 resolve_(std::string_view name) noexcept
436 {
437 83750x auto const f = http::string_to_field(name);
438
2/2
✓ Branch 1 taken 430 times.
✓ Branch 2 taken 83320 times.
83750x if(f)
439 430x return static_cast<std::uint16_t>(*f);
440 83320x return 0;
441 }
442
443 std::uint16_t
444 901x fields_base::
445 find_(
446 std::uint16_t from,
447 http::field id) const noexcept
448 {
449 901x auto const v = static_cast<std::uint16_t>(id);
450 901x auto* const et = tab_();
451
2/2
✓ Branch 0 taken 751 times.
✓ Branch 1 taken 540 times.
1291x for(auto i = from; i < count_; ++i)
452
2/2
✓ Branch 1 taken 361 times.
✓ Branch 2 taken 390 times.
751x if(ent_(et, i).id == v)
453 361x return i;
454 540x return count_;
455 }
456
457 std::uint16_t
458 421x fields_base::
459 find_(
460 std::uint16_t from,
461 std::string_view name) const noexcept
462 {
463 421x auto const n = name.size();
464 421x auto* const et = tab_();
465
2/2
✓ Branch 0 taken 25380 times.
✓ Branch 1 taken 14 times.
25394x for(auto i = from; i < count_; ++i)
466 {
467 25380x auto const& e = ent_(et, i);
468
6/6
✓ Branch 0 taken 11691 times.
✓ Branch 1 taken 13689 times.
✓ Branch 4 taken 407 times.
✓ Branch 5 taken 11284 times.
✓ Branch 6 taken 407 times.
✓ Branch 7 taken 24973 times.
25380x if(e.nn == n && ci_equal_rev(buf_ + e.of, name.data(), n))
469 407x return i;
470 }
471 14x return count_;
472 }
473
474 std::uint16_t
475 294x fields_base::
476 find_last_(
477 std::uint16_t before,
478 http::field id) const noexcept
479 {
480
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 294 times.
294x BOOST_ASSERT(before <= count_);
481 294x auto const v = static_cast<std::uint16_t>(id);
482 294x auto i = before;
483
2/2
✓ Branch 0 taken 127 times.
✓ Branch 1 taken 205 times.
332x while(i != 0)
484 {
485 127x --i;
486
2/2
✓ Branch 1 taken 89 times.
✓ Branch 2 taken 38 times.
127x if(ent_(i).id == v)
487 89x return i;
488 }
489 205x return count_;
490 }
491
492 std::uint16_t
493 5x fields_base::
494 find_last_(
495 std::uint16_t before,
496 std::string_view name) const noexcept
497 {
498
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5x BOOST_ASSERT(before <= count_);
499 5x auto const n = name.size();
500 5x auto i = before;
501
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 3 times.
11x while(i != 0)
502 {
503 8x --i;
504 8x auto const& e = ent_(i);
505
5/6
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 6 times.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 2 times.
✓ Branch 7 taken 6 times.
8x if(e.nn == n && ci_equal_rev(buf_ + e.of, name.data(), n))
506 2x return i;
507 }
508 3x return count_;
509 }
510
511 std::string_view
512 68x fields_base::
513 at(http::field id) const
514 {
515 68x auto const i = find_(0, id);
516
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 67 times.
68x if(i == count_)
517 1x detail::throw_out_of_range("field not found");
518 67x return ref_(i).value;
519 }
520
521 std::string_view
522 80x fields_base::
523 at(std::string_view name) const
524 {
525 80x auto const i = find_(0, name);
526
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 79 times.
80x if(i == count_)
527 1x detail::throw_out_of_range("field not found");
528 79x return ref_(i).value;
529 }
530
531 std::size_t
532 13x fields_base::
533 count(http::field id) const noexcept
534 {
535
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 12 times.
13x if(count_ == 0)
536 1x return 0;
537 12x auto const v = static_cast<std::uint16_t>(id);
538 12x auto const* e = &ent_(0);
539 12x std::size_t n = 0;
540
2/2
✓ Branch 0 taken 31 times.
✓ Branch 1 taken 12 times.
43x for(std::uint16_t i = 0; i != count_; ++i, --e)
541
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 17 times.
31x if(e->id == v)
542 14x ++n;
543 12x return n;
544 }
545
546 std::size_t
547 8x fields_base::
548 count(std::string_view name) const noexcept
549 {
550
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 7 times.
8x if(count_ == 0)
551 1x return 0;
552 7x auto const nn = name.size();
553 7x auto const* e = &ent_(0);
554 7x std::size_t n = 0;
555
2/2
✓ Branch 0 taken 29 times.
✓ Branch 1 taken 7 times.
36x for(std::uint16_t i = 0; i != count_; ++i, --e)
556
6/6
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 15 times.
✓ Branch 4 taken 11 times.
✓ Branch 5 taken 3 times.
✓ Branch 6 taken 11 times.
✓ Branch 7 taken 18 times.
29x if(e->nn == nn && ci_equal_rev(buf_ + e->of, name.data(), nn))
557 11x ++n;
558 7x return n;
559 }
560
561 auto
562 187x fields_base::
563 subrange::iterator::operator++() noexcept -> iterator&
564 {
565
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 187 times.
187x BOOST_ASSERT(f_ != nullptr);
566
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 187 times.
187x BOOST_ASSERT(i_ < f_->count_);
567 187x auto const& c = *f_;
568 187x auto const* e = &c.ent_(i_);
569 187x auto const id = e->id;
570
2/2
✓ Branch 0 taken 184 times.
✓ Branch 1 taken 3 times.
187x if(id != 0)
571 {
572 // known field; ids alone identify the name
573
2/2
✓ Branch 0 taken 29 times.
✓ Branch 1 taken 169 times.
198x while(++i_ != c.count_)
574 {
575 29x --e;
576
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 14 times.
29x if(e->id == id)
577 15x break;
578 }
579 184x return *this;
580 }
581 3x auto const nn = e->nn;
582 3x auto const* p = c.buf_ + e->of;
583
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 1 time.
6x while(++i_ != c.count_)
584 {
585 5x --e;
586
3/4
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
5x if(e->id != 0 || e->nn != nn)
587 2x continue;
588
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 1 time.
3x if(ci_equal_rev(c.buf_ + e->of, p, nn))
589 2x break;
590 }
591 3x return *this;
592 }
593
594 //------------------------------------------------
595 //
596 // Modifiers
597 //
598 //------------------------------------------------
599
600 void
601 37x fields_base::
602 append(std::initializer_list<field_view> init)
603 {
604 // validate and total up front, so that
605 // nothing can throw once we start inserting
606 37x auto bytes = std::size_t(size_);
607
2/2
✓ Branch 2 taken 86 times.
✓ Branch 3 taken 34 times.
120x for(auto const& e : init)
608 {
609
2/2
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 85 times.
86x if(e.name.size() > max_name_size)
610 1x detail::throw_length_error("field name too large");
611
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 83 times.
85x if(e.value.size() > max_value_size)
612 2x detail::throw_length_error("field value too large");
613 83x bytes += e.name.size() + e.value.size() + 4;
614 }
615 34x reserve_(bytes, count_ + init.size());
616
2/2
✓ Branch 2 taken 79 times.
✓ Branch 3 taken 33 times.
112x for(auto const& e : init)
617 {
618 79x auto const id = (e.id == unknown_field)
619
2/2
✓ Branch 0 taken 44 times.
✓ Branch 1 taken 35 times.
79x ? resolve_(e.name)
620 79x : static_cast<std::uint16_t>(e.id);
621 79x insert_(count_, id, e.name, e.value);
622 }
623 33x }
624
625 std::uint16_t
626 66644x fields_base::
627 insert_(
628 std::uint16_t i,
629 std::uint16_t id,
630 std::string_view name,
631 std::string_view value)
632 {
633
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 66644 times.
66644x BOOST_ASSERT(i <= count_);
634
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 66644 times.
66644x BOOST_ASSERT(size_ >= 2);
635
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 66642 times.
66644x if(count_ == max_field_count)
636 2x detail::throw_length_error("field count limit exceeded");
637
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 66640 times.
66642x if(name.size() > max_name_size)
638 2x detail::throw_length_error("field name too large");
639
2/2
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 66637 times.
66640x if(value.size() > max_value_size)
640 3x detail::throw_length_error("field value too large");
641 66637x auto const nn = static_cast<std::uint16_t>(name.size());
642 66637x auto const vn = static_cast<std::uint16_t>(value.size());
643 66637x auto const len = nn + vn + std::uint32_t(4);
644
2/2
✓ Branch 0 taken 66632 times.
✓ Branch 1 taken 5 times.
66637x auto const pos = (i == count_) ? size_ - 2 : ent_(i).of;
645 133260x char* p = splice_fields_(
646 66637x pos, 0, len, 1, { 0, name }, { std::size_t(nn) + 2, value });
647 66623x p[nn] = ':';
648 66623x p[nn + 1] = ' ';
649 66623x p[len - 2] = '\r';
650 66623x p[len - 1] = '\n';
651 66623x auto* et = tab_();
652
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 66623 times.
66635x for(auto j = count_; j > i; --j)
653 {
654 12x auto e = ent_(et, j - 1);
655 12x e.of += len;
656 12x ent_(et, j) = e;
657 }
658 66623x ent_(et, i) = { pos, id, nn, 2, vn };
659 66623x ++count_;
660 66623x notify_(id);
661 66623x return i;
662 }
663
664 void
665 38x fields_base::
666 replace_value_(
667 std::uint16_t i,
668 std::string_view value)
669 {
670
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 38 times.
38x BOOST_ASSERT(i < count_);
671
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 36 times.
38x if(value.size() > max_value_size)
672 2x detail::throw_length_error("field value too large");
673 36x auto const vn2 = static_cast<std::uint16_t>(value.size());
674 36x auto const of = ent_(i).of;
675 36x auto const vp = of + ent_(i).nn + ent_(i).ws;
676 36x auto const old_n = line_len_(i) - (vp - of);
677 36x auto const new_n = std::size_t(vn2) + 2;
678 36x auto* const p = splice_fields_(
679 vp, old_n, new_n, 0, { 0, value }, {});
680 36x p[vn2] = '\r';
681 36x p[vn2 + 1] = '\n';
682 36x auto const d = std::ptrdiff_t(new_n) - std::ptrdiff_t(old_n);
683 36x auto* et = tab_();
684
2/2
✓ Branch 0 taken 32 times.
✓ Branch 1 taken 36 times.
68x for(auto j = i + 1; j < count_; ++j)
685 {
686 32x auto& e = ent_(et, j);
687 32x e.of = static_cast<std::uint32_t>(e.of + d);
688 }
689 36x ent_(i).vn = vn2;
690 36x notify_(ent_(i).id);
691 36x }
692
693 void
694 109x fields_base::
695 erase_at_(std::uint16_t i) noexcept
696 {
697
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 109 times.
109x BOOST_ASSERT(i < count_);
698 109x auto const id = ent_(i).id;
699 109x auto const of = ent_(i).of;
700 109x auto const len = line_len_(i);
701 109x std::memmove(buf_ + of, buf_ + of + len, size_ - of - len);
702 109x auto* et = tab_();
703
2/2
✓ Branch 0 taken 4961 times.
✓ Branch 1 taken 109 times.
5070x for(auto j = i + 1; j < count_; ++j)
704 {
705 4961x auto e = ent_(et, j);
706 4961x e.of -= len;
707 4961x ent_(et, j - 1) = e;
708 }
709 109x size_ -= len;
710 109x --count_;
711 109x notify_(id);
712 109x }
713
714 template<class Match>
715 std::uint16_t
716 152x fields_base::
717 erase_all_(
718 std::uint16_t i,
719 std::uint16_t id,
720 Match const& match) noexcept
721 {
722
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 152 times.
152x BOOST_ASSERT(i < count_);
723 // single pass, relocating runs of surviving
724 // lines as whole blocks, so each character
725 // moves at most once
726 152x auto* et = tab_();
727 152x auto cw = ent_(et, i).of;
728 152x auto ew = i;
729
2/2
✓ Branch 0 taken 280 times.
✓ Branch 1 taken 152 times.
432x while(i != count_)
730 {
731 280x auto e = ent_(et, i);
732
2/2
✓ Branch 1 taken 157 times.
✓ Branch 2 taken 123 times.
280x if(match(e))
733 {
734 157x ++i;
735 157x continue;
736 }
737 123x std::uint32_t const rp = e.of;
738 123x std::uint32_t rn = 0;
739 9910x for(;;)
740 {
741 10033x auto const len = line_len_(i);
742 10033x e.of = cw + rn;
743 10033x ent_(et, ew) = e;
744 10033x rn += len;
745 10033x ++ew;
746
2/2
✓ Branch 0 taken 120 times.
✓ Branch 1 taken 9913 times.
10033x if(++i == count_)
747 120x break;
748 9913x e = ent_(et, i);
749
2/2
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 9910 times.
9913x if(match(e))
750 3x break;
751 }
752 123x std::memmove(buf_ + cw, buf_ + rp, rn);
753 123x cw += rn;
754 }
755 152x buf_[cw] = '\r';
756 152x buf_[cw + 1] = '\n';
757 152x auto const n = static_cast<std::uint16_t>(count_ - ew);
758 152x size_ = cw + 2;
759 152x count_ = ew;
760 152x notify_(id);
761 152x return n;
762 }
763
764 std::uint16_t
765 47x fields_base::
766 erase_all_(
767 std::uint16_t i,
768 std::uint16_t id) noexcept
769 {
770 47x return erase_all_(i, id,
771 94x [id](entry const& e) noexcept
772 {
773 81x return e.id == id;
774 47x });
775 }
776
777 std::uint16_t
778 105x fields_base::
779 erase_all_(
780 std::uint16_t i,
781 std::string_view name) noexcept
782 {
783 105x return erase_all_(i, ent_(i).id,
784 210x [this, name](entry const& e) noexcept
785 {
786
4/4
✓ Branch 1 taken 4659 times.
✓ Branch 2 taken 5453 times.
✓ Branch 3 taken 108 times.
✓ Branch 4 taken 4551 times.
14771x return e.nn == name.size() &&
787 14771x ci_equal_rev(buf_ + e.of, name.data(), name.size());
788 105x });
789 }
790
791 void
792 31x fields_base::
793 erase_dups_(std::uint16_t i) noexcept
794 {
795 31x auto const& e = ent_(i);
796
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 3 times.
31x if(e.id != 0)
797 {
798 28x auto const j = find_(i + 1, static_cast<http::field>(e.id));
799
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 26 times.
28x if(j != count_)
800 2x erase_all_(j, e.id);
801 }
802 else
803 {
804 3x std::string_view const name(buf_ + e.of, e.nn);
805 3x auto const j = find_(i + 1, name);
806
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 2 times.
3x if(j != count_)
807 1x erase_all_(j, name);
808 }
809 31x }
810
811 void
812 301x fields_base::
813 set(
814 http::field id,
815 std::string_view value)
816 {
817 301x auto const i = find_(0, id);
818
2/2
✓ Branch 0 taken 274 times.
✓ Branch 1 taken 27 times.
301x if(i == count_)
819 274x return append(id, value);
820 27x replace_value_(i, value);
821 27x erase_dups_(i);
822 }
823
824 void
825 9x fields_base::
826 set(
827 std::string_view name,
828 std::string_view value)
829 {
830 9x auto const i = find_(0, name);
831
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 5 times.
9x if(i == count_)
832 4x return append(name, value);
833 5x replace_value_(i, value);
834 4x erase_dups_(i);
835 }
836
837 std::size_t
838 167x fields_base::
839 erase(http::field id) noexcept
840 {
841 167x auto const i = find_(0, id);
842
2/2
✓ Branch 0 taken 122 times.
✓ Branch 1 taken 45 times.
167x if(i == count_)
843 122x return 0;
844 45x return erase_all_(i, static_cast<std::uint16_t>(id));
845 }
846
847 std::size_t
848 105x fields_base::
849 erase(std::string_view name) noexcept
850 {
851 105x auto const i = find_(0, name);
852
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 104 times.
105x if(i == count_)
853 1x return 0;
854 104x return erase_all_(i, name);
855 }
856
857 void
858 6x fields_base::
859 clear() noexcept
860 {
861
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 5 times.
6x if(count_ == 0)
862 1x return;
863 5x buf_[0] = '\r';
864 5x buf_[1] = '\n';
865 5x size_ = 2;
866 5x count_ = 0;
867 5x on_clear_();
868 }
869
870 std::ostream&
871 6x operator<<(
872 std::ostream& os,
873 fields_base const& f)
874 {
875
2/2
✓ Branch 5 taken 7 times.
✓ Branch 6 taken 6 times.
13x for(auto fv : f)
876
4/4
✓ Branch 1 taken 7 times.
✓ Branch 4 taken 7 times.
✓ Branch 7 taken 7 times.
✓ Branch 10 taken 7 times.
7x os << fv.name << ": " << fv.value << '\n';
877 6x return os;
878 }
879
880 } // namespace burl
881 } // namespace boost
882