GCC Code Coverage Report


Directory: libs/http_proto/
File: src/fields_base.cpp
Date: 2025-10-12 23:51:57
Exec Total Coverage
Lines: 679 697 97.4%
Functions: 69 70 98.6%
Branches: 256 324 79.0%

Line Branch Exec Source
1 //
2 // Copyright (c) 2021 Vinnie Falco (vinnie.falco@gmail.com)
3 // Copyright (c) 2025 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/http_proto
9 //
10
11 #include <boost/http_proto/detail/config.hpp>
12 #include <boost/http_proto/detail/except.hpp>
13 #include <boost/http_proto/detail/header.hpp>
14 #include <boost/http_proto/error.hpp>
15 #include <boost/http_proto/field.hpp>
16 #include <boost/http_proto/fields_base.hpp>
17 #include <boost/http_proto/header_limits.hpp>
18 #include <boost/http_proto/rfc/token_rule.hpp>
19
20 #include "src/detail/move_chars.hpp"
21 #include "src/rfc/detail/rules.hpp"
22
23 #include <boost/assert.hpp>
24 #include <boost/assert/source_location.hpp>
25 #include <boost/core/detail/string_view.hpp>
26 #include <boost/system/result.hpp>
27 #include <boost/url/grammar/ci_string.hpp>
28 #include <boost/url/grammar/error.hpp>
29 #include <boost/url/grammar/parse.hpp>
30 #include <boost/url/grammar/token_rule.hpp>
31
32 namespace boost {
33 namespace http_proto {
34
35 namespace {
36
37 std::size_t
38 1066 align_down(
39 void * ptr,
40 std::size_t size,
41 std::size_t alignment)
42 {
43 1066 auto addr = reinterpret_cast<std::uintptr_t>(ptr);
44 1066 auto aligned_end = (addr + size) & ~(alignment - 1);
45
46
1/2
✓ Branch 0 taken 1066 times.
✗ Branch 1 not taken.
1066 if(aligned_end > addr)
47 1066 return aligned_end - addr;
48
49 return 0;
50 }
51
52 void
53 235 verify_field_name(
54 core::string_view name,
55 system::error_code& ec)
56 {
57 235 auto rv = grammar::parse(
58 name, detail::field_name_rule);
59
2/2
✓ Branch 1 taken 9 times.
✓ Branch 2 taken 226 times.
235 if(rv.has_error())
60 {
61 18 ec = BOOST_HTTP_PROTO_ERR(
62 error::bad_field_name);
63 }
64 235 }
65
66 system::result<detail::field_value_rule_t::value_type>
67 364 verify_field_value(
68 core::string_view value)
69 {
70 364 auto it = value.begin();
71 364 auto end = value.end();
72 auto rv =
73 364 grammar::parse(it, end, detail::field_value_rule);
74
2/2
✓ Branch 1 taken 7 times.
✓ Branch 2 taken 357 times.
364 if( rv.has_error() )
75 {
76
1/2
✓ Branch 3 taken 7 times.
✗ Branch 4 not taken.
7 if( rv.error() == condition::need_more_input )
77 7 return error::bad_field_value;
78 return rv.error();
79 }
80
81
2/2
✓ Branch 1 taken 16 times.
✓ Branch 2 taken 341 times.
357 if( rv->has_crlf )
82 16 return error::bad_field_smuggle;
83
84
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 334 times.
341 if( it != end )
85 7 return error::bad_field_value;
86
87 334 return rv;
88 }
89
90 } // namespace
91
92 class fields_base::
93 op_t
94 {
95 fields_base& self_;
96 core::string_view* s0_;
97 core::string_view* s1_;
98 char* buf_ = nullptr;
99 char const* cbuf_ = nullptr;
100 std::size_t cap_ = 0;
101
102 public:
103 explicit
104 988 op_t(
105 fields_base& self,
106 core::string_view* s0 = nullptr,
107 core::string_view* s1 = nullptr) noexcept
108 988 : self_(self)
109 988 , s0_(s0)
110 988 , s1_(s1)
111 {
112 988 }
113
114 988 ~op_t()
115 {
116
2/2
✓ Branch 0 taken 162 times.
✓ Branch 1 taken 826 times.
988 if(buf_)
117
1/2
✓ Branch 0 taken 162 times.
✗ Branch 1 not taken.
162 delete[] buf_;
118 988 }
119
120 char const*
121 12 buf() const noexcept
122 {
123 12 return buf_;
124 }
125
126 char const*
127 449 cbuf() const noexcept
128 {
129 449 return cbuf_;
130 }
131
132 char*
133 12 end() const noexcept
134 {
135 12 return buf_ + cap_;
136 }
137
138 table
139 6 tab() const noexcept
140 {
141 6 return table(end());
142 }
143
144 bool
145 reserve(std::size_t n);
146
147 bool
148 grow(
149 std::size_t extra_char,
150 std::size_t extra_field);
151
152 void
153 move_chars(
154 char* dest,
155 char const* src,
156 std::size_t n) const noexcept;
157 };
158
159 bool
160 968 fields_base::
161 op_t::
162 reserve(
163 std::size_t n)
164 {
165 // TODO: consider using a growth factor
166
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 950 times.
968 if(n > self_.max_cap_)
167 {
168 // max capacity exceeded
169 18 detail::throw_length_error();
170 }
171
2/2
✓ Branch 0 taken 133 times.
✓ Branch 1 taken 817 times.
950 if(n <= self_.h_.cap)
172 133 return false;
173 817 auto buf = new char[n];
174 817 buf_ = self_.h_.buf;
175 817 cbuf_ = self_.h_.cbuf;
176 817 cap_ = self_.h_.cap;
177 817 self_.h_.buf = buf;
178 817 self_.h_.cbuf = buf;
179 817 self_.h_.cap = n;
180 817 return true;
181 }
182
183 bool
184 875 fields_base::
185 op_t::
186 grow(
187 std::size_t extra_char,
188 std::size_t extra_field)
189 {
190
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 875 times.
875 if(extra_field > detail::header::max_offset - self_.h_.count)
191 detail::throw_length_error();
192
193
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 873 times.
875 if(extra_char > detail::header::max_offset - self_.h_.size)
194 2 detail::throw_length_error();
195
196 873 return reserve(
197 detail::header::bytes_needed(
198 873 self_.h_.size + extra_char,
199 1741 self_.h_.count + extra_field));
200 }
201
202 void
203 103 fields_base::
204 op_t::
205 move_chars(
206 char* dest,
207 char const* src,
208 std::size_t n) const noexcept
209 {
210 103 detail::move_chars(
211 103 dest, src, n, s0_, s1_);
212 103 }
213
214 //------------------------------------------------
215
216 39 fields_base::
217 prefix_op_t::
218 prefix_op_t(
219 fields_base& self,
220 std::size_t new_prefix,
221 core::string_view* s0,
222 39 core::string_view* s1)
223 39 : self_(self)
224 39 , new_prefix_(static_cast<
225 39 offset_type>(new_prefix))
226 {
227
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 37 times.
39 if(self.h_.size - self.h_.prefix + new_prefix
228 > detail::header::max_offset)
229 2 detail::throw_length_error();
230
231 // memmove happens in the destructor
232 // to avoid overlaping with start line.
233 74 if(new_prefix_ < self_.h_.prefix
234
5/6
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 32 times.
✓ Branch 3 taken 5 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 5 times.
✓ Branch 6 taken 32 times.
37 && !self.h_.is_default())
235 5 return;
236
237 32 auto new_size = static_cast<offset_type>(
238 32 self.h_.size - self.h_.prefix + new_prefix_);
239
240 auto bytes_needed =
241 32 detail::header::bytes_needed(
242 new_size,
243 32 self.h_.count);
244
245
2/2
✓ Branch 0 taken 27 times.
✓ Branch 1 taken 5 times.
32 if(bytes_needed > self.h_.cap)
246 {
247 // static storage will always throw which is
248 // intended since they cannot reallocate.
249
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 26 times.
27 if(self.max_cap_ < bytes_needed)
250 1 detail::throw_length_error();
251 // TODO: consider using a growth factor
252 26 char* p = new char[bytes_needed];
253 26 std::memcpy(
254 26 p + new_prefix_,
255 26 self.h_.cbuf + self.h_.prefix,
256 26 self.h_.size - self.h_.prefix);
257 26 self.h_.copy_table(p + bytes_needed);
258
259 // old buffer gets released in the destructor
260 // to avoid invalidating any string_views
261 // that may still reference it.
262 26 buf_ = self.h_.buf;
263 26 self.h_.buf = p;
264 26 self.h_.cap = bytes_needed;
265 }
266 else
267 {
268 // memmove to the right and update any
269 // string_views that reference that region.
270 5 detail::move_chars(
271 5 self.h_.buf + new_prefix_,
272 5 self.h_.cbuf + self.h_.prefix,
273 5 self.h_.size - self.h_.prefix,
274 s0,
275 s1);
276 }
277
278 31 self.h_.cbuf = self.h_.buf;
279 31 self.h_.size = new_size;
280 31 self.h_.prefix = new_prefix_;
281 }
282
283 36 fields_base::
284 prefix_op_t::
285 ~prefix_op_t()
286 {
287
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 31 times.
36 if(new_prefix_ < self_.h_.prefix)
288 {
289 5 std::memmove(
290 5 self_.h_.buf + new_prefix_,
291 5 self_.h_.cbuf + self_.h_.prefix,
292 5 self_.h_.size - self_.h_.prefix);
293
294 5 self_.h_.size =
295 5 self_.h_.size - self_.h_.prefix + new_prefix_;
296 5 self_.h_.prefix = new_prefix_;
297 }
298
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 26 times.
31 else if(buf_)
299 {
300
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 delete[] buf_;
301 }
302 36 }
303
304 //------------------------------------------------
305
306 198 fields_base::
307 fields_base(
308 198 detail::kind k) noexcept
309 198 : h_(k)
310 {
311 198 }
312
313 1066 fields_base::
314 fields_base(
315 detail::kind k,
316 void* storage,
317 1066 std::size_t cap) noexcept
318 : fields_base(
319 1066 *detail::header::get_default(k), storage, cap)
320 {
321 1066 }
322
323 // copy s and parse it
324 546 fields_base::
325 fields_base(
326 detail::kind k,
327 546 core::string_view s)
328 546 : h_(detail::empty{k})
329 {
330 546 auto n = detail::header::count_crlf(s);
331
2/2
✓ Branch 0 taken 235 times.
✓ Branch 1 taken 311 times.
546 if(h_.kind == detail::kind::fields)
332 {
333
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 234 times.
235 if(n < 1)
334 1 detail::throw_invalid_argument();
335 234 n -= 1;
336 }
337 else
338 {
339
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 309 times.
311 if(n < 2)
340 2 detail::throw_invalid_argument();
341 309 n -= 2;
342 }
343 543 op_t op(*this);
344
1/2
✓ Branch 2 taken 543 times.
✗ Branch 3 not taken.
543 op.grow(s.size(), n);
345
1/2
✓ Branch 2 taken 543 times.
✗ Branch 3 not taken.
543 s.copy(h_.buf, s.size());
346 543 system::error_code ec;
347 // VFALCO This is using defaults?
348 543 header_limits lim;
349 543 h_.parse(s.size(), lim, ec);
350
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 543 times.
543 if(ec.failed())
351 detail::throw_system_error(ec);
352 543 }
353
354 // construct a complete copy of h
355 25 fields_base::
356 fields_base(
357 25 detail::header const& h)
358 25 : h_(h.kind)
359 {
360
2/2
✓ Branch 1 taken 9 times.
✓ Branch 2 taken 16 times.
25 if(h.is_default())
361 9 return;
362
363 // allocate and copy the buffer
364 16 op_t op(*this);
365
1/2
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
16 op.grow(h.size, h.count);
366 16 h.assign_to(h_);
367 16 std::memcpy(
368 16 h_.buf, h.cbuf, h.size);
369 16 h.copy_table(h_.buf + h_.cap);
370 16 }
371
372 // construct a complete copy of h
373 1066 fields_base::
374 fields_base(
375 detail::header const& h,
376 void* storage,
377 1066 std::size_t cap)
378 1066 : h_(h.kind)
379 1066 , external_storage_(true)
380 {
381 1066 h_.cbuf = static_cast<char*>(storage);
382 1066 h_.buf = static_cast<char*>(storage);
383 1066 h_.cap = align_down(
384 storage,
385 cap,
386 alignof(detail::header::entry));
387 1066 max_cap_ = h_.cap;
388
389 2132 if(detail::header::bytes_needed(
390 1066 h.size, h.count)
391
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1066 times.
1066 >= h_.cap)
392 detail::throw_length_error();
393
394 1066 h.assign_to(h_);
395 1066 std::memcpy(
396 1066 h_.buf, h.cbuf, h.size);
397 1066 h.copy_table(h_.buf + h_.cap);
398 1066 }
399
400 //------------------------------------------------
401
402 13 fields_base::
403 13 fields_base(fields_base const& other)
404 13 : fields_base(other.h_)
405 {
406 13 }
407
408 1832 fields_base::
409 ~fields_base()
410 {
411
4/4
✓ Branch 0 taken 1720 times.
✓ Branch 1 taken 112 times.
✓ Branch 2 taken 676 times.
✓ Branch 3 taken 1044 times.
1832 if(h_.buf && !external_storage_)
412
1/2
✓ Branch 0 taken 676 times.
✗ Branch 1 not taken.
676 delete[] h_.buf;
413 1832 }
414
415 //------------------------------------------------
416 //
417 // Capacity
418 //
419 //------------------------------------------------
420
421 void
422 10 fields_base::
423 clear() noexcept
424 {
425
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 5 times.
10 if(! h_.buf)
426 5 return;
427 using H =
428 detail::header;
429 auto const& h =
430 5 *H::get_default(
431 5 h_.kind);
432 5 h.assign_to(h_);
433 5 std::memcpy(
434 5 h_.buf,
435 5 h.cbuf,
436 5 h_.size);
437 }
438
439 void
440 95 fields_base::
441 reserve_bytes(
442 std::size_t n)
443 {
444 95 op_t op(*this);
445
4/4
✓ Branch 1 taken 82 times.
✓ Branch 2 taken 13 times.
✓ Branch 3 taken 48 times.
✓ Branch 4 taken 34 times.
95 if(! op.reserve(n))
446 48 return;
447 68 std::memcpy(
448 34 h_.buf, op.cbuf(), h_.size);
449 34 auto const nt =
450 34 sizeof(entry) * h_.count;
451
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 28 times.
34 if(nt > 0)
452 6 std::memcpy(
453 6 h_.buf + h_.cap - nt,
454 6 op.end() - nt,
455 nt);
456
2/2
✓ Branch 1 taken 34 times.
✓ Branch 2 taken 48 times.
95 }
457
458 void
459 7 fields_base::
460 shrink_to_fit()
461 {
462 14 if(detail::header::bytes_needed(
463 7 h_.size, h_.count) >=
464
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 4 times.
7 h_.cap)
465 3 return;
466
467
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if(external_storage_)
468 return;
469
470
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 fields_base tmp(h_);
471 4 tmp.h_.swap(h_);
472 4 }
473
474
475 void
476 30 fields_base::
477 set_max_capacity_in_bytes(std::size_t n)
478 {
479
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 24 times.
30 if(n < h_.cap)
480 6 detail::throw_invalid_argument();
481 24 max_cap_ = n;
482 24 }
483
484 //--------------------------------------------
485 //
486 // Observers
487 //
488 //--------------------------------------------
489
490
491 fields_base::
492 value_type::
493 value_type(
494 reference const& other)
495 : id(other.id)
496 , name(other.name)
497 , value(other.value)
498 {
499 }
500
501 //------------------------------------------------
502
503 auto
504 1814 fields_base::
505 iterator::
506 operator*() const noexcept ->
507 reference
508 {
509
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1814 times.
1814 BOOST_ASSERT(i_ < ph_->count);
510 auto tab =
511 1814 ph_->tab();
512 auto const& e =
513 1814 tab[i_];
514 1814 auto const* p =
515 1814 ph_->cbuf + ph_->prefix;
516 return {
517 1814 (e.id == detail::header::unknown_field)
518
2/2
✓ Branch 0 taken 789 times.
✓ Branch 1 taken 1025 times.
1814 ? optional<field>{} : e.id,
519 core::string_view(
520 1814 p + e.np, e.nn),
521 core::string_view(
522 1814 p + e.vp, e.vn) };
523 }
524
525 //------------------------------------------------
526
527 auto
528 24 fields_base::
529 reverse_iterator::
530 operator*() const noexcept ->
531 reference
532 {
533
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 24 times.
24 BOOST_ASSERT(i_ > 0);
534 auto tab =
535 24 ph_->tab();
536 auto const& e =
537 24 tab[i_-1];
538 24 auto const* p =
539 24 ph_->cbuf + ph_->prefix;
540 return {
541 24 (e.id == detail::header::unknown_field)
542
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 9 times.
24 ? optional<field>{} : e.id,
543 core::string_view(
544 24 p + e.np, e.nn),
545 core::string_view(
546 24 p + e.vp, e.vn) };
547 }
548
549 //------------------------------------------------
550
551 21 fields_base::
552 subrange::
553 iterator::
554 iterator(
555 detail::header const* ph,
556 21 std::size_t i) noexcept
557 21 : ph_(ph)
558 21 , i_(i)
559 {
560
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 21 times.
21 BOOST_ASSERT(i <= ph_->count);
561 21 }
562
563 21 fields_base::
564 subrange::
565 iterator::
566 iterator(
567 21 detail::header const* ph) noexcept
568 21 : ph_(ph)
569 21 , i_(ph->count)
570 {
571 21 }
572
573 auto
574 11 fields_base::
575 subrange::
576 iterator::
577 operator*() const noexcept ->
578 reference const
579 {
580 auto tab =
581 11 ph_->tab();
582 auto const& e =
583 11 tab[i_];
584 11 auto const p =
585 11 ph_->cbuf + ph_->prefix;
586 22 return core::string_view(
587 11 p + e.vp, e.vn);
588 }
589
590 auto
591 27 fields_base::
592 subrange::
593 iterator::
594 operator++() noexcept ->
595 iterator&
596 {
597
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 27 times.
27 BOOST_ASSERT(i_ < ph_->count);
598 27 auto const* e = &ph_->tab()[i_];
599 27 auto const id = e->id;
600
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 7 times.
27 if(id != detail::header::unknown_field)
601 {
602 20 ++i_;
603 20 --e;
604
2/2
✓ Branch 0 taken 26 times.
✓ Branch 1 taken 12 times.
38 while(i_ != ph_->count)
605 {
606
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 18 times.
26 if(e->id == id)
607 8 break;
608 18 ++i_;
609 18 --e;
610 }
611 20 return *this;
612 }
613 7 auto const p =
614 7 ph_->cbuf + ph_->prefix;
615 auto name = core::string_view(
616 7 p + e->np, e->nn);
617 7 ++i_;
618 7 --e;
619
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 4 times.
24 while(i_ != ph_->count)
620 {
621
2/2
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 17 times.
20 if(grammar::ci_is_equal(
622 name, core::string_view(
623 20 p + e->np, e->nn)))
624 3 break;
625 17 ++i_;
626 17 --e;
627 }
628 7 return *this;
629 }
630
631 //------------------------------------------------
632 //
633 // fields_base
634 //
635 //------------------------------------------------
636
637 core::string_view
638 2 fields_base::
639 at(
640 field id) const
641 {
642 2 auto const it = find(id);
643
2/2
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 1 times.
2 if(it == end())
644
1/2
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
2 BOOST_THROW_EXCEPTION(
645 std::out_of_range{ "field not found" });
646 1 return it->value;
647 }
648
649 core::string_view
650 2 fields_base::
651 at(
652 core::string_view name) const
653 {
654 2 auto const it = find(name);
655
2/2
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 1 times.
2 if(it == end())
656
1/2
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
2 BOOST_THROW_EXCEPTION(
657 std::out_of_range{ "field not found" });
658 1 return it->value;
659 }
660
661 bool
662 4 fields_base::
663 exists(
664 field id) const noexcept
665 {
666 4 return find(id) != end();
667 }
668
669 bool
670 7 fields_base::
671 exists(
672 core::string_view name) const noexcept
673 {
674 7 return find(name) != end();
675 }
676
677 std::size_t
678 12 fields_base::
679 count(field id) const noexcept
680 {
681 12 std::size_t n = 0;
682
2/2
✓ Branch 5 taken 45 times.
✓ Branch 6 taken 12 times.
57 for(auto v : *this)
683
2/2
✓ Branch 1 taken 11 times.
✓ Branch 2 taken 34 times.
45 if(v.id == id)
684 11 ++n;
685 12 return n;
686 }
687
688 std::size_t
689 14 fields_base::
690 count(
691 core::string_view name) const noexcept
692 {
693 14 std::size_t n = 0;
694
2/2
✓ Branch 5 taken 62 times.
✓ Branch 6 taken 14 times.
76 for(auto v : *this)
695
2/2
✓ Branch 1 taken 19 times.
✓ Branch 2 taken 43 times.
62 if(grammar::ci_is_equal(
696 v.name, name))
697 19 ++n;
698 14 return n;
699 }
700
701 auto
702 94 fields_base::
703 find(field id) const noexcept ->
704 iterator
705 {
706 94 auto it = begin();
707 94 auto const last = end();
708
2/2
✓ Branch 1 taken 199 times.
✓ Branch 2 taken 9 times.
208 while(it != last)
709 {
710
2/2
✓ Branch 3 taken 85 times.
✓ Branch 4 taken 114 times.
199 if(it->id == id)
711 85 break;
712 114 ++it;
713 }
714 94 return it;
715 }
716
717 auto
718 93 fields_base::
719 find(
720 core::string_view name) const noexcept ->
721 iterator
722 {
723 93 auto it = begin();
724 93 auto const last = end();
725
2/2
✓ Branch 1 taken 200 times.
✓ Branch 2 taken 6 times.
206 while(it != last)
726 {
727 200 if(grammar::ci_is_equal(
728
2/2
✓ Branch 2 taken 87 times.
✓ Branch 3 taken 113 times.
400 it->name, name))
729 87 break;
730 113 ++it;
731 }
732 93 return it;
733 }
734
735 auto
736 2 fields_base::
737 find(
738 iterator from,
739 field id) const noexcept ->
740 iterator
741 {
742 2 auto const last = end();
743
2/2
✓ Branch 1 taken 10 times.
✓ Branch 2 taken 1 times.
11 while(from != last)
744 {
745
2/2
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 9 times.
10 if(from->id == id)
746 1 break;
747 9 ++from;
748 }
749 2 return from;
750 }
751
752 auto
753 2 fields_base::
754 find(
755 iterator from,
756 core::string_view name) const noexcept ->
757 iterator
758 {
759 2 auto const last = end();
760
2/2
✓ Branch 1 taken 11 times.
✓ Branch 2 taken 1 times.
12 while(from != last)
761 {
762 11 if(grammar::ci_is_equal(
763
2/2
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 10 times.
22 name, from->name))
764 1 break;
765 10 ++from;
766 }
767 2 return from;
768 }
769
770 auto
771 3 fields_base::
772 find_last(
773 iterator it,
774 field id) const noexcept ->
775 iterator
776 {
777 3 auto const it0 = begin();
778 for(;;)
779 {
780
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 9 times.
10 if(it == it0)
781 1 return end();
782 9 --it;
783
2/2
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 7 times.
9 if(it->id == id)
784 2 return it;
785 }
786 }
787
788 auto
789 3 fields_base::
790 find_last(
791 iterator it,
792 core::string_view name) const noexcept ->
793 iterator
794 {
795 3 auto const it0 = begin();
796 for(;;)
797 {
798
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 13 times.
14 if(it == it0)
799 1 return end();
800 13 --it;
801 13 if(grammar::ci_is_equal(
802
2/2
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 11 times.
26 it->name, name))
803 2 return it;
804 }
805 }
806
807 core::string_view
808 2 fields_base::
809 value_or(
810 field id,
811 core::string_view s) const noexcept
812 {
813 2 auto it = find(id);
814
2/2
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 1 times.
2 if(it != end())
815 1 return it->value;
816 1 return s;
817 }
818
819 core::string_view
820 2 fields_base::
821 value_or(
822 core::string_view name,
823 core::string_view s) const noexcept
824 {
825 2 auto it = find(name);
826
2/2
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 1 times.
2 if(it != end())
827 1 return it->value;
828 1 return s;
829 }
830
831 //------------------------------------------------
832
833 auto
834 16 fields_base::
835 find_all(
836 field id) const noexcept ->
837 subrange
838 {
839 16 return subrange(
840 32 &h_, find(id).i_);
841 }
842
843 auto
844 5 fields_base::
845 find_all(
846 core::string_view name) const noexcept ->
847 subrange
848 {
849 5 return subrange(
850 10 &h_, find(name).i_);
851 }
852
853 //------------------------------------------------
854
855 std::ostream&
856 1 operator<<(
857 std::ostream& os,
858 const fields_base& f)
859 {
860 1 return os << f.buffer();
861 }
862
863 //------------------------------------------------
864 //
865 // Modifiers
866 //
867 //------------------------------------------------
868
869 auto
870 30 fields_base::
871 erase(
872 iterator it) noexcept -> iterator
873 {
874 30 auto const id = it->id.value_or(
875 detail::header::unknown_field);
876 30 raw_erase(it.i_);
877 30 h_.on_erase(id);
878 30 return it;
879 }
880
881 std::size_t
882 30 fields_base::
883 erase(
884 field id) noexcept
885 {
886 30 auto const i0 = h_.find(id);
887
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 27 times.
30 if(i0 == h_.count)
888 3 return 0;
889 27 return erase_all(i0, id);
890 }
891
892 std::size_t
893 18 fields_base::
894 erase(
895 core::string_view name) noexcept
896 {
897 18 auto const i0 = h_.find(name);
898
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 15 times.
18 if(i0 == h_.count)
899 3 return 0;
900 15 auto const ft = h_.tab();
901 15 auto const id = ft[i0].id;
902
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 9 times.
15 if(id == detail::header::unknown_field)
903 6 return erase_all(i0, name);
904 9 return erase_all(i0, id);
905 }
906
907 //------------------------------------------------
908
909 void
910 28 fields_base::
911 set(
912 iterator it,
913 core::string_view value,
914 system::error_code& ec)
915 {
916
1/2
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
28 auto rv = verify_field_value(value);
917
2/2
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 24 times.
28 if(rv.has_error())
918 {
919 4 ec = rv.error();
920 4 return;
921 }
922
923 24 value = rv->value;
924 24 bool has_obs_fold = rv->has_obs_fold;
925
926 24 auto const i = it.i_;
927 24 auto tab = h_.tab();
928 24 auto const& e0 = tab[i];
929 24 auto const pos0 = offset(i);
930 24 auto const pos1 = offset(i + 1);
931 std::ptrdiff_t dn =
932 24 value.size() -
933 24 it->value.size();
934
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 24 times.
24 if( value.empty() &&
935
1/4
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 24 times.
24 ! it->value.empty())
936 --dn; // remove SP
937 24 else if(
938
2/4
✗ Branch 3 not taken.
✓ Branch 4 taken 24 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 24 times.
24 it->value.empty() &&
939 ! value.empty())
940 ++dn; // add SP
941
942 24 op_t op(*this, &value);
943
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 18 times.
30 if( dn > 0 &&
944
2/4
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
12 op.grow(value.size() -
945
2/2
✓ Branch 3 taken 6 times.
✓ Branch 4 taken 18 times.
30 it->value.size(), 0))
946 {
947 // reallocated
948 6 auto dest = h_.buf +
949 6 pos0 + e0.nn + 1;
950 12 std::memcpy(
951 6 h_.buf,
952 6 op.buf(),
953 6 dest - h_.buf);
954
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 if(! value.empty())
955 {
956 6 *dest++ = ' ';
957
1/2
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
6 value.copy(
958 dest,
959 value.size());
960
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
6 if( has_obs_fold )
961 3 detail::remove_obs_fold(
962 3 dest, dest + value.size());
963 6 dest += value.size();
964 }
965 6 *dest++ = '\r';
966 6 *dest++ = '\n';
967 12 std::memcpy(
968 6 h_.buf + pos1 + dn,
969 12 op.buf() + pos1,
970 6 h_.size - pos1);
971 12 std::memcpy(
972 6 h_.buf + h_.cap -
973 6 sizeof(entry) * h_.count,
974 6 &op.tab()[h_.count - 1],
975 6 sizeof(entry) * h_.count);
976 }
977 else
978 {
979 // copy the value first
980 36 auto dest = h_.buf + pos0 +
981 18 it->name.size() + 1;
982
1/2
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
18 if(! value.empty())
983 {
984 18 *dest++ = ' ';
985
1/2
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 value.copy(
986 dest,
987 value.size());
988
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
18 if( has_obs_fold )
989 detail::remove_obs_fold(
990 dest, dest + value.size());
991 18 dest += value.size();
992 }
993 18 op.move_chars(
994 18 h_.buf + pos1 + dn,
995 18 h_.buf + pos1,
996 18 h_.size - pos1);
997 18 *dest++ = '\r';
998 18 *dest++ = '\n';
999 }
1000 {
1001 // update tab
1002 24 auto ft = h_.tab();
1003 31 for(std::size_t j = h_.count - 1;
1004
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 24 times.
31 j > i; --j)
1005 7 ft[j] = ft[j] + dn;
1006 24 auto& e = ft[i];
1007 48 e.vp = e.np + e.nn +
1008 24 1 + ! value.empty();
1009 24 e.vn = static_cast<
1010 24 offset_type>(value.size());
1011 24 h_.size = static_cast<
1012 24 offset_type>(h_.size + dn);
1013 }
1014
1/2
✓ Branch 3 taken 24 times.
✗ Branch 4 not taken.
24 auto const id = it->id.value_or(
1015 detail::header::unknown_field);
1016
2/2
✓ Branch 1 taken 9 times.
✓ Branch 2 taken 15 times.
24 if(h_.is_special(id))
1017 {
1018 // replace first char of name
1019 // with null to hide metadata
1020 9 char saved = h_.buf[pos0];
1021 9 auto& e = h_.tab()[i];
1022 9 e.id = detail::header::unknown_field;
1023 9 h_.buf[pos0] = '\0';
1024
1/2
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
9 h_.on_erase(id);
1025 9 h_.buf[pos0] = saved; // restore
1026 9 e.id = id;
1027
1/2
✓ Branch 3 taken 9 times.
✗ Branch 4 not taken.
9 h_.on_insert(id, it->value);
1028 }
1029 24 }
1030
1031 // erase existing fields with id
1032 // and then add the field with value
1033 void
1034 110 fields_base::
1035 set(
1036 field id,
1037 core::string_view value,
1038 system::error_code& ec)
1039 {
1040
1/2
✓ Branch 1 taken 110 times.
✗ Branch 2 not taken.
110 auto rv = verify_field_value(value);
1041
2/2
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 106 times.
110 if(rv.has_error())
1042 {
1043 4 ec = rv.error();
1044 4 return;
1045 }
1046
1047 106 auto const i0 = h_.find(id);
1048
2/2
✓ Branch 0 taken 21 times.
✓ Branch 1 taken 85 times.
106 if(i0 != h_.count)
1049 {
1050 // field exists
1051 21 auto const ft = h_.tab();
1052 {
1053 // provide strong guarantee
1054 auto const n0 =
1055 21 h_.size - length(i0);
1056 auto const n =
1057 21 ft[i0].nn + 2 +
1058 21 rv->value.size() + 2;
1059 // VFALCO missing overflow check
1060
1/2
✓ Branch 1 taken 21 times.
✗ Branch 2 not taken.
21 reserve_bytes(n0 + n);
1061 }
1062 21 erase_all(i0, id);
1063 }
1064
1065
3/6
✓ Branch 1 taken 106 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 106 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 106 times.
✗ Branch 8 not taken.
106 insert_unchecked(
1066 id,
1067 to_string(id),
1068 106 rv->value,
1069 106 h_.count,
1070 106 rv->has_obs_fold);
1071 }
1072
1073 // erase existing fields with name
1074 // and then add the field with value
1075 void
1076 32 fields_base::
1077 set(
1078 core::string_view name,
1079 core::string_view value,
1080 system::error_code& ec)
1081 {
1082
1/2
✓ Branch 1 taken 32 times.
✗ Branch 2 not taken.
32 verify_field_name(name , ec);
1083
2/2
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 28 times.
32 if(ec.failed())
1084 8 return;
1085
1086
1/2
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
28 auto rv = verify_field_value(value);
1087
2/2
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 24 times.
28 if(rv.has_error())
1088 {
1089 4 ec = rv.error();
1090 4 return;
1091 }
1092
1093 24 auto const i0 = h_.find(name);
1094
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 6 times.
24 if(i0 != h_.count)
1095 {
1096 // field exists
1097 18 auto const ft = h_.tab();
1098 18 auto const id = ft[i0].id;
1099 {
1100 // provide strong guarantee
1101 auto const n0 =
1102 18 h_.size - length(i0);
1103 auto const n =
1104 18 ft[i0].nn + 2 +
1105 18 rv->value.size() + 2;
1106 // VFALCO missing overflow check
1107
1/2
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
18 reserve_bytes(n0 + n);
1108 }
1109 // VFALCO simple algorithm but
1110 // costs one extra memmove
1111
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 3 times.
18 if(id != detail::header::unknown_field)
1112 15 erase_all(i0, id);
1113 else
1114 3 erase_all(i0, name);
1115 }
1116
2/2
✓ Branch 2 taken 23 times.
✓ Branch 3 taken 1 times.
24 insert_unchecked(
1117 string_to_field(name),
1118 name,
1119 24 rv->value,
1120 24 h_.count,
1121 24 rv->has_obs_fold);
1122 }
1123
1124 auto
1125 26 fields_base::
1126 insert(
1127 iterator before,
1128 field id,
1129 core::string_view value)
1130 -> iterator
1131 {
1132 26 system::error_code ec;
1133
1/2
✓ Branch 1 taken 26 times.
✗ Branch 2 not taken.
26 auto const it = insert(before, id, value, ec);
1134
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 25 times.
26 if(ec.failed())
1135 1 detail::throw_system_error(ec);
1136 25 return it;
1137 }
1138
1139 auto
1140 33 fields_base::
1141 insert(
1142 iterator before,
1143 field id,
1144 core::string_view value,
1145 system::error_code& ec)
1146 -> iterator
1147 {
1148
2/4
✓ Branch 2 taken 33 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 33 times.
✗ Branch 6 not taken.
33 insert_impl(
1149 id,
1150 to_string(id),
1151 value,
1152 before.i_, ec);
1153 33 return before;
1154 }
1155
1156 auto
1157 13 fields_base::
1158 insert(
1159 iterator before,
1160 core::string_view name,
1161 core::string_view value)
1162 -> iterator
1163 {
1164 13 system::error_code ec;
1165
1/2
✓ Branch 1 taken 13 times.
✗ Branch 2 not taken.
13 insert(before, name, value, ec);
1166
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 12 times.
13 if(ec.failed())
1167 1 detail::throw_system_error(ec);
1168 12 return before;
1169 }
1170
1171 auto
1172 16 fields_base::
1173 insert(
1174 iterator before,
1175 core::string_view name,
1176 core::string_view value,
1177 system::error_code& ec)
1178 -> iterator
1179 {
1180 16 insert_impl(
1181 string_to_field(name),
1182 name,
1183 value,
1184 before.i_,
1185 ec);
1186 16 return before;
1187 }
1188
1189 void
1190 23 fields_base::
1191 set(
1192 iterator it,
1193 core::string_view value)
1194 {
1195 23 system::error_code ec;
1196
1/2
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
23 set(it, value, ec);
1197
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 21 times.
23 if(ec.failed())
1198 2 detail::throw_system_error(ec);
1199 21 }
1200
1201 //------------------------------------------------
1202 //
1203 // (implementation)
1204 //
1205 //------------------------------------------------
1206
1207 // copy start line and fields
1208 void
1209 16 fields_base::
1210 copy_impl(
1211 detail::header const& h)
1212 {
1213
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
16 BOOST_ASSERT(
1214 h.kind == h_.kind);
1215
1216 auto const n =
1217 16 detail::header::bytes_needed(
1218 16 h.size, h.count);
1219
7/8
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 5 times.
✓ Branch 3 taken 3 times.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 3 times.
✓ Branch 7 taken 8 times.
✓ Branch 8 taken 8 times.
16 if(n <= h_.cap && (!h.is_default() || external_storage_))
1220 {
1221 // no realloc
1222 8 h.assign_to(h_);
1223 8 h.copy_table(
1224 8 h_.buf + h_.cap);
1225 8 std::memcpy(
1226 8 h_.buf,
1227 8 h.cbuf,
1228 8 h.size);
1229 8 return;
1230 }
1231
1232 // static storages cannot reallocate
1233
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if(external_storage_)
1234 detail::throw_length_error();
1235
1236
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 fields_base tmp(h);
1237 8 tmp.h_.swap(h_);
1238 8 }
1239
1240 void
1241 203 fields_base::
1242 insert_impl(
1243 optional<field> id,
1244 core::string_view name,
1245 core::string_view value,
1246 std::size_t before,
1247 system::error_code& ec)
1248 {
1249
1/2
✓ Branch 1 taken 203 times.
✗ Branch 2 not taken.
203 verify_field_name(name, ec);
1250
2/2
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 198 times.
203 if(ec.failed())
1251 23 return;
1252
1253
1/2
✓ Branch 1 taken 198 times.
✗ Branch 2 not taken.
198 auto rv = verify_field_value(value);
1254
2/2
✓ Branch 1 taken 18 times.
✓ Branch 2 taken 180 times.
198 if(rv.has_error())
1255 {
1256 18 ec = rv.error();
1257 18 return;
1258 }
1259
1260
2/2
✓ Branch 1 taken 174 times.
✓ Branch 2 taken 6 times.
180 insert_unchecked(
1261 id,
1262 name,
1263 180 rv->value,
1264 before,
1265 180 rv->has_obs_fold);
1266 }
1267
1268 void
1269 310 fields_base::
1270 insert_unchecked(
1271 optional<field> id,
1272 core::string_view name,
1273 core::string_view value,
1274 std::size_t before,
1275 bool has_obs_fold)
1276 {
1277 310 auto const tab0 = h_.tab_();
1278 310 auto const pos = offset(before);
1279 auto const n =
1280 310 name.size() + // name
1281 310 1 + // ':'
1282 310 ! value.empty() + // [SP]
1283 310 value.size() + // value
1284 310 2; // CRLF
1285
1286 310 op_t op(*this, &name, &value);
1287
4/4
✓ Branch 1 taken 303 times.
✓ Branch 2 taken 7 times.
✓ Branch 3 taken 218 times.
✓ Branch 4 taken 85 times.
310 if(op.grow(n, 1))
1288 {
1289 // reallocated
1290
2/2
✓ Branch 0 taken 197 times.
✓ Branch 1 taken 21 times.
218 if(pos > 0)
1291 197 std::memcpy(
1292 197 h_.buf,
1293 197 op.cbuf(),
1294 pos);
1295
2/2
✓ Branch 0 taken 89 times.
✓ Branch 1 taken 129 times.
218 if(before > 0)
1296 178 std::memcpy(
1297 89 h_.tab_() - before,
1298 89 tab0 - before,
1299 before * sizeof(entry));
1300 436 std::memcpy(
1301 218 h_.buf + pos + n,
1302 218 op.cbuf() + pos,
1303 218 h_.size - pos);
1304 }
1305 else
1306 {
1307 85 op.move_chars(
1308 85 h_.buf + pos + n,
1309 85 h_.buf + pos,
1310 85 h_.size - pos);
1311 }
1312
1313 // serialize
1314 {
1315 303 auto dest = h_.buf + pos;
1316
1/2
✓ Branch 2 taken 303 times.
✗ Branch 3 not taken.
303 name.copy(dest, name.size());
1317 303 dest += name.size();
1318 303 *dest++ = ':';
1319
2/2
✓ Branch 1 taken 291 times.
✓ Branch 2 taken 12 times.
303 if(! value.empty())
1320 {
1321 291 *dest++ = ' ';
1322
1/2
✓ Branch 2 taken 291 times.
✗ Branch 3 not taken.
291 value.copy(
1323 dest, value.size());
1324
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 273 times.
291 if( has_obs_fold )
1325 18 detail::remove_obs_fold(
1326 18 dest, dest + value.size());
1327 291 dest += value.size();
1328 }
1329 303 *dest++ = '\r';
1330 303 *dest = '\n';
1331 }
1332
1333 // update table
1334 303 auto const tab = h_.tab_();
1335 {
1336 303 auto i = h_.count - before;
1337
2/2
✓ Branch 0 taken 43 times.
✓ Branch 1 taken 260 times.
303 if(i > 0)
1338 {
1339 43 auto p0 = tab0 - h_.count;
1340 43 auto p = tab - h_.count - 1;
1341 do
1342 {
1343 80 *p++ = *p0++ + n;
1344 }
1345
2/2
✓ Branch 0 taken 37 times.
✓ Branch 1 taken 43 times.
80 while(--i);
1346 }
1347 }
1348 303 auto& e = tab[0 - static_cast<std::ptrdiff_t>(before) - 1];
1349 303 e.np = static_cast<offset_type>(
1350 303 pos - h_.prefix);
1351 303 e.nn = static_cast<
1352 303 offset_type>(name.size());
1353 303 e.vp = static_cast<offset_type>(
1354 606 pos - h_.prefix +
1355 303 name.size() + 1 +
1356 303 ! value.empty());
1357 303 e.vn = static_cast<
1358 303 offset_type>(value.size());
1359
1/2
✓ Branch 1 taken 303 times.
✗ Branch 2 not taken.
303 e.id = id.value_or(
1360 detail::header::unknown_field);
1361
1362 // update container
1363 303 h_.count++;
1364 303 h_.size = static_cast<
1365 303 offset_type>(h_.size + n);
1366
1/2
✓ Branch 1 taken 303 times.
✗ Branch 2 not taken.
303 h_.on_insert(e.id, value);
1367 310 }
1368
1369 void
1370 169 fields_base::
1371 raw_erase(
1372 std::size_t i) noexcept
1373 {
1374
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 169 times.
169 BOOST_ASSERT(i < h_.count);
1375
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 169 times.
169 BOOST_ASSERT(h_.buf != nullptr);
1376 169 auto const p0 = offset(i);
1377 169 auto const p1 = offset(i + 1);
1378 169 std::memmove(
1379 169 h_.buf + p0,
1380 169 h_.buf + p1,
1381 169 h_.size - p1);
1382 169 auto const n = p1 - p0;
1383 169 --h_.count;
1384 169 auto ft = h_.tab();
1385
2/2
✓ Branch 0 taken 101 times.
✓ Branch 1 taken 169 times.
270 for(;i < h_.count; ++i)
1386 101 ft[i] = ft[i + 1] - n;
1387 169 h_.size = static_cast<
1388 169 offset_type>(h_.size - n);
1389 169 }
1390
1391 // erase n fields matching id
1392 // without updating metadata
1393 void
1394 4 fields_base::
1395 raw_erase_n(
1396 field id,
1397 std::size_t n) noexcept
1398 {
1399 // iterate in reverse
1400 4 auto e = &h_.tab()[h_.count];
1401 4 auto const e0 = &h_.tab()[0];
1402
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 4 times.
10 while(n > 0)
1403 {
1404
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 BOOST_ASSERT(e != e0);
1405 6 ++e; // decrement
1406
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 1 times.
6 if(e->id == id)
1407 {
1408 5 raw_erase(e0 - e);
1409 5 --n;
1410 }
1411 }
1412 4 }
1413
1414 // erase all fields with id
1415 // and update metadata
1416 std::size_t
1417 72 fields_base::
1418 erase_all(
1419 std::size_t i0,
1420 field id) noexcept
1421 {
1422
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 72 times.
72 BOOST_ASSERT(
1423 id != detail::header::unknown_field);
1424 72 std::size_t n = 1;
1425 72 std::size_t i = h_.count - 1;
1426 72 auto const ft = h_.tab();
1427
2/2
✓ Branch 0 taken 77 times.
✓ Branch 1 taken 72 times.
149 while(i > i0)
1428 {
1429
2/2
✓ Branch 1 taken 44 times.
✓ Branch 2 taken 33 times.
77 if(ft[i].id == id)
1430 {
1431 44 raw_erase(i);
1432 44 ++n;
1433 }
1434 // go backwards to
1435 // reduce memmoves
1436 77 --i;
1437 }
1438 72 raw_erase(i0);
1439 72 h_.on_erase_all(id);
1440 72 return n;
1441 }
1442
1443 // erase all fields with name
1444 // when id == detail::header::unknown_field
1445 std::size_t
1446 9 fields_base::
1447 erase_all(
1448 std::size_t i0,
1449 core::string_view name) noexcept
1450 {
1451 9 std::size_t n = 1;
1452 9 std::size_t i = h_.count - 1;
1453 9 auto const ft = h_.tab();
1454 9 auto const* p = h_.cbuf + h_.prefix;
1455
2/2
✓ Branch 0 taken 27 times.
✓ Branch 1 taken 9 times.
36 while(i > i0)
1456 {
1457 core::string_view s(
1458 27 p + ft[i].np, ft[i].nn);
1459
2/2
✓ Branch 1 taken 9 times.
✓ Branch 2 taken 18 times.
27 if(s == name)
1460 {
1461 9 raw_erase(i);
1462 9 ++n;
1463 }
1464 // go backwards to
1465 // reduce memmoves
1466 27 --i;
1467 }
1468 9 raw_erase(i0);
1469 9 return n;
1470 }
1471
1472 // return i-th field absolute offset
1473 std::size_t
1474 774 fields_base::
1475 offset(
1476 std::size_t i) const noexcept
1477 {
1478
2/2
✓ Branch 0 taken 310 times.
✓ Branch 1 taken 464 times.
774 if(i == 0)
1479 310 return h_.prefix;
1480
2/2
✓ Branch 0 taken 219 times.
✓ Branch 1 taken 245 times.
464 if(i < h_.count)
1481 219 return h_.prefix + h_.tab()[i].np;
1482 // make final CRLF the last "field"
1483 245 return h_.size - 2;
1484 }
1485
1486 // return i-th field absolute length
1487 std::size_t
1488 39 fields_base::
1489 length(
1490 std::size_t i) const noexcept
1491 {
1492 return
1493 39 offset(i + 1) -
1494 39 offset(i);
1495 }
1496
1497 } // http_proto
1498 } // boost
1499