GCC Code Coverage Report


Directory: ./
File: libs/http_proto/src/fields_base.cpp
Date: 2025-12-18 06:34:37
Exec Total Coverage
Lines: 676 696 97.1%
Functions: 68 69 98.6%
Branches: 260 294 88.4%

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