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 33184x 100.0% 100.0% 100.0% boost::burl::(anonymous namespace)::ci_equal_rev(char const*, char const*, unsigned long) :42 16370x 100.0% 100.0% 100.0% boost::burl::(anonymous namespace)::grow_size(unsigned long, unsigned long) :75 847x 75.0% 50.0% 83.0% boost::burl::fields_base::fields_base() :86 407x 100.0% – 100.0% boost::burl::fields_base::alloc::alloc(unsigned long) :98 1006x 100.0% – 100.0% boost::burl::fields_base::alloc::~alloc() :106 1006x 75.0% 50.0% 67.0% boost::burl::fields_base::alloc::size_for(unsigned long) :114 1015x 100.0% – 100.0% boost::burl::fields_base::release_() :128 1883x 100.0% 100.0% 100.0% boost::burl::fields_base::adopt_(boost::burl::fields_base::alloc&) :136 1006x 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 73x 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 197x 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 66995x 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 133990x 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 66995x 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 67012x 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 132196x 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 66766x 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 255x 100.0% 75.0% 89.0% boost::burl::fields_base::notify_(unsigned short) :410 67018x 100.0% 100.0% 100.0% boost::burl::fields_base::resolve_(std::basic_string_view<char, std::char_traits<char> >) :434 83777x 100.0% 100.0% 100.0% boost::burl::fields_base::find_(unsigned short, boost::http::field) const :444 1241x 100.0% 100.0% 100.0% boost::burl::fields_base::find_(unsigned short, std::basic_string_view<char, std::char_traits<char> >) const :458 432x 100.0% 100.0% 100.0% boost::burl::fields_base::find_last_(unsigned short, boost::http::field) const :475 429x 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 14x 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 206x 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 66737x 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 132x 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 29x 100.0% 90.0% 96.0% boost::burl::fields_base::erase_all_(unsigned short, unsigned short) :765 29x 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 55x 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 328x 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 20x 100.0% 100.0% 100.0% boost::burl::fields_base::erase(boost::http::field) :838 205x 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 33184x to_lower(char c) noexcept
35 {
36
4/4
✓ Branch 0 taken 1064 times.
✓ Branch 1 taken 32120 times.
✓ Branch 2 taken 553 times.
✓ Branch 3 taken 511 times.
33184x if(c >= 'A' && c <= 'Z')
37 553x return static_cast<char>(c - 'A' + 'a');
38 32631x return c;
39 }
40
41 bool
42 16370x ci_equal_rev(
43 char const* p1,
44 char const* p2,
45 std::size_t n) noexcept
46 {
47 16370x p1 += n;
48 16370x p2 += n;
49 char a, b;
50 // fast loop
51
2/2
✓ Branch 0 taken 21301 times.
✓ Branch 1 taken 297 times.
21598x while(n != 0)
52 {
53 21301x --n;
54 21301x a = *--p1;
55 21301x b = *--p2;
56
2/2
✓ Branch 0 taken 16073 times.
✓ Branch 1 taken 5228 times.
21301x if(a != b)
57 16073x goto slow;
58 }
59 297x return true;
60 for(;;)
61 {
62 519x a = *--p1;
63 519x b = *--p2;
64 16592x slow:
65
2/2
✓ Branch 2 taken 15840 times.
✓ Branch 3 taken 752 times.
16592x if(to_lower(a) != to_lower(b))
66 15840x 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 847x grow_size(
76 std::size_t need,
77 std::size_t cap) noexcept
78 {
79
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 847 times.
847x if(cap > std::size_t(-1) - cap)
80 ✗ return need;
81 847x return (std::max)(need, 2 * cap);
82 }
83
84 } // namespace
85
86 407x fields_base::
87 407x fields_base() noexcept
88 407x : fields_base(buf, 0, 2, 0)
89 {
90 407x }
91
92 struct fields_base::alloc
93 {
94 std::size_t n;
95 char* p;
96
97 explicit
98 1006x alloc(std::size_t n_)
99 1006x : n(size_for(n_))
100 1006x , p(static_cast<char*>(::operator new(n)))
101 {
102 1006x }
103
104 alloc(alloc const&) = delete;
105
106 1006x ~alloc()
107 {
108
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1006 times.
1006x if(p)
109 ✗ ::operator delete(p);
110 1006x }
111
112 static
113 std::size_t
114 1015x size_for(std::size_t n) noexcept
115 {
116 1015x constexpr auto align = alignof(entry);
117 1015x return (n + (align - 1)) & ~(align - 1);
118 }
119 };
120
121 //------------------------------------------------
122 //
123 // Owning storage
124 //
125 //------------------------------------------------
126
127 void
128 1883x fields_base::
129 release_() noexcept
130 {
131
2/2
✓ Branch 1 taken 1006 times.
✓ Branch 2 taken 877 times.
1883x if(owns_())
132 1006x ::operator delete(base_());
133 1883x }
134
135 void
136 1006x fields_base::
137 adopt_(alloc& a) noexcept
138 {
139 1006x release_();
140 1006x end_ = a.p + a.n;
141 1006x buf_ = a.p + prefix_;
142 1006x a.p = nullptr;
143 1006x }
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 73x fields_base::
163 realloc_(std::size_t total)
164 {
165
2/2
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 72 times.
73x if(static_())
166 1x detail::throw_length_error("buffer limit exceeded");
167 72x auto const tn = table_space_(count_);
168
1/1
✓ Branch 1 taken 72 times.
72x alloc a(total);
169 72x std::memcpy(a.p, base_(), std::size_t(prefix_) + size_);
170 72x std::memcpy(a.p + a.n - tn, end_ - tn, tn);
171 72x adopt_(a);
172 72x }
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 197x fields_base::swap_(fields_base& other) noexcept
185 {
186 197x std::swap(buf_, other.buf_);
187 197x std::swap(end_, other.end_);
188 197x std::swap(size_, other.size_);
189 197x std::swap(count_, other.count_);
190 197x std::swap(prefix_, other.prefix_);
191 197x }
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 66995x fields_base::
257 fill_(
258 char* g,
259 piece const& p0,
260 piece const& p1) noexcept
261 {
262 133990x auto const put = [](char* d, std::string_view s) noexcept
263 {
264
2/2
✓ Branch 1 taken 68259 times.
✓ Branch 2 taken 65731 times.
133990x if(!s.empty())
265 68259x std::memmove(d, s.data(), s.size());
266 133990x };
267
268 66995x 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 457 times.
✓ Branch 3 taken 66538 times.
✓ Branch 5 taken 3 times.
✓ Branch 6 taken 454 times.
66995x return d < s.data() + s.size() && s.data() < d + n;
274 };
275
276
2/2
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 66992 times.
66995x 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 66992x put(g + p0.at, p0.src);
284 66992x put(g + p1.at, p1.src);
285 }
286 66995x }
287
288 char*
289 67012x 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 67012x auto* const base = base_();
299 67012x auto const head = std::size_t(prefix_) + size_;
300
301
2/4
✓ Branch 0 taken 67012 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 67012 times.
✗ Branch 3 not taken.
67012x 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 67012 times.
67012x if(new_n > max_buffer_size - (head - old_n))
305 ✗ detail::throw_length_error("buffer limit exceeded");
306
307 auto const need =
308 67012x head - old_n + new_n + table_space_(count_ + added);
309
310
2/2
✓ Branch 1 taken 864 times.
✓ Branch 2 taken 66148 times.
67012x if(need > capacity_in_bytes())
311 {
312
2/2
✓ Branch 1 taken 17 times.
✓ Branch 2 taken 847 times.
864x if(static_())
313 17x detail::throw_length_error("buffer limit exceeded");
314
315 847x auto const tn = table_space_(count_);
316
1/1
✓ Branch 3 taken 847 times.
847x alloc a(grow_size(need, capacity_in_bytes()));
317
318 847x std::memcpy(a.p, base, pos);
319 847x std::memcpy(
320 847x a.p + pos + new_n,
321 847x base + pos + old_n,
322 847x head - pos - old_n);
323 847x std::memcpy(a.p + a.n - tn, end_ - tn, tn);
324
325 847x auto* const g = a.p + pos;
326 847x fill_(g, p0, p1);
327 847x adopt_(a);
328 847x return g;
329 847x }
330
331 66148x auto* const g = base + pos;
332 66148x auto const tail = head - pos - old_n;
333
334
2/2
✓ Branch 0 taken 23 times.
✓ Branch 1 taken 66125 times.
66148x 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 66098 times.
✓ Branch 1 taken 27 times.
66125x else if(new_n > old_n)
343 {
344 66098x auto const delta = new_n - old_n;
345 66098x auto* const tail_end = g + old_n + tail;
346
347 66098x std::memmove(g + new_n, g + old_n, tail);
348
349 // a source which was in the tail moved with it
350
351 132196x auto const slide = [&](piece p) noexcept
352 {
353 132196x if(!p.src.empty() &&
354
6/6
✓ Branch 0 taken 66622 times.
✓ Branch 1 taken 65574 times.
✓ Branch 3 taken 545 times.
✓ Branch 4 taken 66077 times.
✓ Branch 5 taken 3 times.
✓ Branch 6 taken 132193 times.
132741x p.src.data() >= g + old_n &&
355
2/2
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 542 times.
545x p.src.data() < tail_end)
356 {
357 3x p.src = { p.src.data() + delta, p.src.size() };
358 }
359 132196x return p;
360 66098x };
361
362 66098x fill_(g, slide(p0), slide(p1));
363 }
364 else
365 {
366 27x fill_(g, p0, p1);
367 }
368
369 66148x return g;
370 }
371
372 char*
373 66766x 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 133518x auto* const g = splice_(
383 66766x prefix_ + pos, old_n, new_n, added, p0, p1);
384 66752x buf_ = g - pos;
385 66752x size_ = static_cast<std::uint32_t>(
386 66752x size_ - old_n + new_n);
387 66752x return g;
388 }
389
390 char*
391 255x 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 255 times.
255x BOOST_ASSERT(pos + old_n <= prefix_);
400 255x auto const n = std::size_t(prefix_) - old_n + new_n;
401
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 246 times.
255x if(n > max_start_line_size)
402 9x detail::throw_length_error("start line too large");
403 246x auto* const g = splice_(pos, old_n, new_n, 0, p0, p1);
404 243x prefix_ = static_cast<std::uint16_t>(n);
405 243x buf_ = (g - pos) + prefix_;
406 243x return g;
407 }
408
409 void
410 67018x fields_base::notify_(std::uint16_t id) noexcept
411 {
412 67018x auto const f = static_cast<http::field>(id);
413
2/2
✓ Branch 0 taken 376 times.
✓ Branch 1 taken 66642 times.
67018x switch(f)
414 {
415 376x 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 376x on_special_(f);
421 376x break;
422 66642x default:
423 66642x break;
424 }
425 67018x }
426
427 //------------------------------------------------
428 //
429 // Lookup
430 //
431 //------------------------------------------------
432
433 std::uint16_t
434 83777x fields_base::
435 resolve_(std::string_view name) noexcept
436 {
437 83777x auto const f = http::string_to_field(name);
438
2/2
✓ Branch 1 taken 446 times.
✓ Branch 2 taken 83331 times.
83777x if(f)
439 446x return static_cast<std::uint16_t>(*f);
440 83331x return 0;
441 }
442
443 std::uint16_t
444 1241x fields_base::
445 find_(
446 std::uint16_t from,
447 http::field id) const noexcept
448 {
449 1241x auto const v = static_cast<std::uint16_t>(id);
450 1241x auto* const et = tab_();
451
2/2
✓ Branch 0 taken 988 times.
✓ Branch 1 taken 870 times.
1858x for(auto i = from; i < count_; ++i)
452
2/2
✓ Branch 1 taken 371 times.
✓ Branch 2 taken 617 times.
988x if(ent_(et, i).id == v)
453 371x return i;
454 870x return count_;
455 }
456
457 std::uint16_t
458 432x fields_base::
459 find_(
460 std::uint16_t from,
461 std::string_view name) const noexcept
462 {
463 432x auto const n = name.size();
464 432x auto* const et = tab_();
465
2/2
✓ Branch 0 taken 25381 times.
✓ Branch 1 taken 25 times.
25406x for(auto i = from; i < count_; ++i)
466 {
467 25381x auto const& e = ent_(et, i);
468
6/6
✓ Branch 0 taken 11692 times.
✓ Branch 1 taken 13689 times.
✓ Branch 4 taken 407 times.
✓ Branch 5 taken 11285 times.
✓ Branch 6 taken 407 times.
✓ Branch 7 taken 24974 times.
25381x if(e.nn == n && ci_equal_rev(buf_ + e.of, name.data(), n))
469 407x return i;
470 }
471 25x return count_;
472 }
473
474 std::uint16_t
475 429x 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 429 times.
429x BOOST_ASSERT(before <= count_);
481 429x auto const v = static_cast<std::uint16_t>(id);
482 429x auto i = before;
483
2/2
✓ Branch 0 taken 207 times.
✓ Branch 1 taken 261 times.
468x while(i != 0)
484 {
485 207x --i;
486
2/2
✓ Branch 1 taken 168 times.
✓ Branch 2 taken 39 times.
207x if(ent_(i).id == v)
487 168x return i;
488 }
489 261x 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 14x fields_base::
533 count(http::field id) const noexcept
534 {
535
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 13 times.
14x if(count_ == 0)
536 1x return 0;
537 13x auto const v = static_cast<std::uint16_t>(id);
538 13x auto const* e = &ent_(0);
539 13x std::size_t n = 0;
540
2/2
✓ Branch 0 taken 32 times.
✓ Branch 1 taken 13 times.
45x for(std::uint16_t i = 0; i != count_; ++i, --e)
541
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 17 times.
32x if(e->id == v)
542 15x ++n;
543 13x 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 206x fields_base::
563 subrange::iterator::operator++() noexcept -> iterator&
564 {
565
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 206 times.
206x BOOST_ASSERT(f_ != nullptr);
566
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 206 times.
206x BOOST_ASSERT(i_ < f_->count_);
567 206x auto const& c = *f_;
568 206x auto const* e = &c.ent_(i_);
569 206x auto const id = e->id;
570
2/2
✓ Branch 0 taken 203 times.
✓ Branch 1 taken 3 times.
206x if(id != 0)
571 {
572 // known field; ids alone identify the name
573
2/2
✓ Branch 0 taken 29 times.
✓ Branch 1 taken 188 times.
217x 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 203x 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 66737x 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 66737 times.
66737x BOOST_ASSERT(i <= count_);
634
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 66737 times.
66737x BOOST_ASSERT(size_ >= 2);
635
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 66735 times.
66737x 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 66733 times.
66735x 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 66730 times.
66733x if(value.size() > max_value_size)
640 3x detail::throw_length_error("field value too large");
641 66730x auto const nn = static_cast<std::uint16_t>(name.size());
642 66730x auto const vn = static_cast<std::uint16_t>(value.size());
643 66730x auto const len = nn + vn + std::uint32_t(4);
644
2/2
✓ Branch 0 taken 66725 times.
✓ Branch 1 taken 5 times.
66730x auto const pos = (i == count_) ? size_ - 2 : ent_(i).of;
645 133446x char* p = splice_fields_(
646 66730x pos, 0, len, 1, { 0, name }, { std::size_t(nn) + 2, value });
647 66716x p[nn] = ':';
648 66716x p[nn + 1] = ' ';
649 66716x p[len - 2] = '\r';
650 66716x p[len - 1] = '\n';
651 66716x auto* et = tab_();
652
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 66716 times.
66728x 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 66716x ent_(et, i) = { pos, id, nn, 2, vn };
659 66716x ++count_;
660 66716x notify_(id);
661 66716x 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 132x fields_base::
695 erase_at_(std::uint16_t i) noexcept
696 {
697
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 132 times.
132x BOOST_ASSERT(i < count_);
698 132x auto const id = ent_(i).id;
699 132x auto const of = ent_(i).of;
700 132x auto const len = line_len_(i);
701 132x std::memmove(buf_ + of, buf_ + of + len, size_ - of - len);
702 132x auto* et = tab_();
703
2/2
✓ Branch 0 taken 4969 times.
✓ Branch 1 taken 132 times.
5101x for(auto j = i + 1; j < count_; ++j)
704 {
705 4969x auto e = ent_(et, j);
706 4969x e.of -= len;
707 4969x ent_(et, j - 1) = e;
708 }
709 132x size_ -= len;
710 132x --count_;
711 132x notify_(id);
712 132x }
713
714 template<class Match>
715 std::uint16_t
716 134x 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 134 times.
134x 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 134x auto* et = tab_();
727 134x auto cw = ent_(et, i).of;
728 134x auto ew = i;
729
2/2
✓ Branch 0 taken 254 times.
✓ Branch 1 taken 134 times.
388x while(i != count_)
730 {
731 254x auto e = ent_(et, i);
732
2/2
✓ Branch 1 taken 139 times.
✓ Branch 2 taken 115 times.
254x if(match(e))
733 {
734 139x ++i;
735 139x continue;
736 }
737 115x std::uint32_t const rp = e.of;
738 115x std::uint32_t rn = 0;
739 9910x for(;;)
740 {
741 10025x auto const len = line_len_(i);
742 10025x e.of = cw + rn;
743 10025x ent_(et, ew) = e;
744 10025x rn += len;
745 10025x ++ew;
746
2/2
✓ Branch 0 taken 112 times.
✓ Branch 1 taken 9913 times.
10025x if(++i == count_)
747 112x 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 115x std::memmove(buf_ + cw, buf_ + rp, rn);
753 115x cw += rn;
754 }
755 134x buf_[cw] = '\r';
756 134x buf_[cw + 1] = '\n';
757 134x auto const n = static_cast<std::uint16_t>(count_ - ew);
758 134x size_ = cw + 2;
759 134x count_ = ew;
760 134x notify_(id);
761 134x return n;
762 }
763
764 std::uint16_t
765 29x fields_base::
766 erase_all_(
767 std::uint16_t i,
768 std::uint16_t id) noexcept
769 {
770 29x return erase_all_(i, id,
771 58x [id](entry const& e) noexcept
772 {
773 55x return e.id == id;
774 29x });
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 328x fields_base::
813 set(
814 http::field id,
815 std::string_view value)
816 {
817 328x auto const i = find_(0, id);
818
2/2
✓ Branch 0 taken 301 times.
✓ Branch 1 taken 27 times.
328x if(i == count_)
819 301x return append(id, value);
820 27x replace_value_(i, value);
821 27x erase_dups_(i);
822 }
823
824 void
825 20x fields_base::
826 set(
827 std::string_view name,
828 std::string_view value)
829 {
830 20x auto const i = find_(0, name);
831
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 5 times.
20x if(i == count_)
832 15x return append(name, value);
833 5x replace_value_(i, value);
834 4x erase_dups_(i);
835 }
836
837 std::size_t
838 205x fields_base::
839 erase(http::field id) noexcept
840 {
841 205x auto const i = find_(0, id);
842
2/2
✓ Branch 0 taken 178 times.
✓ Branch 1 taken 27 times.
205x if(i == count_)
843 178x return 0;
844 27x 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