GCC Code Coverage Report


Directory: libs/http_proto/
File: libs/http_proto/src/fields_base.cpp
Date: 2024-01-12 19:51:55
Exec Total Coverage
Lines: 388 417 93.0%
Functions: 29 31 93.5%
Branches: 127 178 71.3%

Line Branch Exec Source
1 //
2 // Copyright (c) 2021 Vinnie Falco (vinnie.falco@gmail.com)
3 //
4 // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 //
7 // Official repository: https://github.com/cppalliance/http_proto
8 //
9
10 #include <boost/http_proto/fields_base.hpp>
11 #include <boost/http_proto/field.hpp>
12 #include <boost/http_proto/header_limits.hpp>
13 #include <boost/http_proto/detail/except.hpp>
14 #include "detail/copied_strings.hpp"
15 #include "detail/move_chars.hpp"
16 #include "detail/number_string.hpp"
17 #include <boost/url/grammar/ci_string.hpp>
18 #include <boost/assert.hpp>
19 #include <boost/assert/source_location.hpp>
20 #include <string>
21
22 namespace boost {
23 namespace http_proto {
24
25 class fields_base::
26 op_t
27 {
28 fields_base& self_;
29 core::string_view* s0_;
30 core::string_view* s1_;
31 char* buf_ = nullptr;
32 char const* cbuf_ = nullptr;
33 std::size_t cap_ = 0;
34
35 public:
36 explicit
37 613 op_t(
38 fields_base& self,
39 core::string_view* s0 = nullptr,
40 core::string_view* s1 = nullptr) noexcept
41 613 : self_(self)
42 , s0_(s0)
43 613 , s1_(s1)
44 {
45 613 }
46
47 613 ~op_t()
48 613 {
49
2/2
✓ Branch 0 taken 68 times.
✓ Branch 1 taken 545 times.
613 if(buf_)
50
1/2
✓ Branch 0 taken 68 times.
✗ Branch 1 not taken.
68 delete[] buf_;
51 613 }
52
53 char const*
54 6 buf() const noexcept
55 {
56 6 return buf_;
57 }
58
59 char const*
60 123 cbuf() const noexcept
61 {
62 123 return cbuf_;
63 }
64
65 char*
66 9 end() const noexcept
67 {
68 9 return buf_ + cap_;
69 }
70
71 table
72 3 tab() const noexcept
73 {
74 3 return table(end());
75 }
76
77 static
78 std::size_t
79 growth(
80 std::size_t n0,
81 std::size_t m) noexcept;
82
83 bool
84 reserve(std::size_t bytes);
85
86 bool
87 grow(
88 std::size_t extra_char,
89 std::size_t extra_field);
90
91 void
92 copy_prefix(
93 std::size_t n,
94 std::size_t i) noexcept;
95
96 void
97 move_chars(
98 char* dest,
99 char const* src,
100 std::size_t n) const noexcept;
101 };
102
103 /* Growth functions for containers
104
105 N1 = g( N0, M );
106
107 g = growth function
108 M = minimum capacity
109 N0 = old size
110 N1 = new size
111 */
112 std::size_t
113 1153 fields_base::
114 op_t::
115 growth(
116 std::size_t n0,
117 std::size_t m) noexcept
118 {
119 1153 auto const E = alignof(entry);
120 1153 auto const m1 =
121 1153 E * ((m + E - 1) / E);
122
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1153 times.
1153 BOOST_ASSERT(m1 >= m);
123
2/2
✓ Branch 0 taken 952 times.
✓ Branch 1 taken 201 times.
1153 if(n0 == 0)
124 {
125 // exact
126 952 return m1;
127 }
128
2/2
✓ Branch 0 taken 129 times.
✓ Branch 1 taken 72 times.
201 if(m1 > n0)
129 129 return m1;
130 72 return n0;
131 }
132
133 bool
134 597 fields_base::
135 op_t::
136 reserve(
137 std::size_t bytes)
138 {
139
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 596 times.
597 if(bytes > max_capacity_in_bytes())
140 {
141 // max capacity exceeded
142 1 detail::throw_length_error();
143 }
144 596 auto n = growth(
145 596 self_.h_.cap, bytes);
146
2/2
✓ Branch 0 taken 49 times.
✓ Branch 1 taken 547 times.
596 if(n <= self_.h_.cap)
147 49 return false;
148 547 auto buf = new char[n];
149 547 buf_ = self_.h_.buf;
150 547 cbuf_ = self_.h_.cbuf;
151 547 cap_ = self_.h_.cap;
152 547 self_.h_.buf = buf;
153 547 self_.h_.cbuf = buf;
154 547 self_.h_.cap = n;
155 547 return true;
156 }
157
158 bool
159 559 fields_base::
160 op_t::
161 grow(
162 std::size_t extra_char,
163 std::size_t extra_field)
164 {
165 // extra_field is naturally limited
166 // by max_offset, since each field
167 // is at least 4 bytes: "X:\r\n"
168
2/4
✓ Branch 0 taken 559 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 559 times.
✗ Branch 3 not taken.
559 BOOST_ASSERT(
169 extra_field <= max_offset &&
170 extra_field <= static_cast<
171 std::size_t>(
172 max_offset - self_.h_.count));
173
2/2
✓ Branch 0 taken 557 times.
✓ Branch 1 taken 2 times.
559 if( extra_char > max_offset ||
174 557 extra_char > static_cast<std::size_t>(
175
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 557 times.
557 max_offset - self_.h_.size))
176 2 detail::throw_length_error();
177 1114 auto n1 = growth(
178 557 self_.h_.cap,
179 detail::header::bytes_needed(
180 557 self_.h_.size + extra_char,
181 557 self_.h_.count + extra_field));
182 557 return reserve(n1);
183 }
184
185 void
186 fields_base::
187 op_t::
188 copy_prefix(
189 std::size_t n,
190 std::size_t i) noexcept
191 {
192 // copy first n chars
193 std::memcpy(
194 self_.h_.buf,
195 cbuf_,
196 n);
197 // copy first i entries
198 if(i > 0)
199 std::memcpy(
200 self_.h_.tab_() - i,
201 reinterpret_cast<entry*>(
202 buf_ + cap_) - i,
203 i * sizeof(entry));
204 }
205
206 void
207 38 fields_base::
208 op_t::
209 move_chars(
210 char* dest,
211 char const* src,
212 std::size_t n) const noexcept
213 {
214 38 detail::move_chars(
215 38 dest, src, n, s0_, s1_);
216 38 }
217
218 //------------------------------------------------
219
220 138 fields_base::
221 fields_base(
222 detail::kind k) noexcept
223 : fields_view_base(&h_)
224 138 , h_(k)
225 {
226 138 }
227
228 // copy s and parse it
229 918 fields_base::
230 fields_base(
231 detail::kind k,
232 core::string_view s)
233 : fields_view_base(&h_)
234 918 , h_(detail::empty{k})
235 {
236 918 auto n = detail::header::count_crlf(s);
237
2/2
✓ Branch 0 taken 201 times.
✓ Branch 1 taken 258 times.
918 if(h_.kind == detail::kind::fields)
238 {
239
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 201 times.
402 if(n < 1)
240 detail::throw_invalid_argument();
241 402 n -= 1;
242 }
243 else
244 {
245
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 258 times.
516 if(n < 2)
246 detail::throw_invalid_argument();
247 516 n -= 2;
248 }
249 1836 op_t op(*this);
250
1/2
✓ Branch 2 taken 459 times.
✗ Branch 3 not taken.
918 op.grow(s.size(), n);
251
1/2
✓ Branch 2 taken 459 times.
✗ Branch 3 not taken.
918 s.copy(h_.buf, s.size());
252 918 system::error_code ec;
253 // VFALCO This is using defaults?
254 918 header_limits lim;
255 918 h_.parse(s.size(), lim, ec);
256
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 459 times.
918 if(ec.failed())
257 detail::throw_system_error(ec);
258 918 }
259
260 // construct a complete copy of h
261 36 fields_base::
262 fields_base(
263 24 detail::header const& h)
264 24 : fields_view_base(&h_)
265 36 , h_(h.kind)
266 {
267
2/2
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 12 times.
36 if(h.is_default())
268 {
269
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
12 BOOST_ASSERT(h.cap == 0);
270
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
12 BOOST_ASSERT(h.buf == nullptr);
271 12 h_ = h;
272 12 return;
273 }
274
275 // allocate and copy the buffer
276 48 op_t op(*this);
277
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
24 op.grow(h.size, h.count);
278 24 h.assign_to(h_);
279 24 std::memcpy(
280 24 h_.buf, h.cbuf, h.size);
281 24 h.copy_table(h_.buf + h_.cap);
282 }
283
284 //------------------------------------------------
285
286 1092 fields_base::
287 1116 ~fields_base()
288 {
289
2/2
✓ Branch 0 taken 483 times.
✓ Branch 1 taken 63 times.
1092 if(h_.buf)
290
1/2
✓ Branch 0 taken 483 times.
✗ Branch 1 not taken.
966 delete[] h_.buf;
291 1092 }
292
293 //------------------------------------------------
294 //
295 // Capacity
296 //
297 //------------------------------------------------
298
299 void
300 8 fields_base::
301 clear() noexcept
302 {
303
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 4 times.
8 if(! h_.buf)
304 4 return;
305 using H =
306 detail::header;
307 auto const& h =
308 4 *H::get_default(
309 4 h_.kind);
310 4 h.assign_to(h_);
311 4 std::memcpy(
312 4 h_.buf,
313 4 h.cbuf,
314 4 h_.size);
315 }
316
317 void
318 40 fields_base::
319 reserve_bytes(
320 std::size_t n)
321 {
322 41 op_t op(*this);
323
4/4
✓ Branch 1 taken 39 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 25 times.
✓ Branch 4 taken 14 times.
40 if(! op.reserve(n))
324 25 return;
325 28 std::memcpy(
326 14 h_.buf, op.cbuf(), h_.size);
327 14 auto const nt =
328 14 sizeof(entry) * h_.count;
329
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 8 times.
14 if(nt > 0)
330 6 std::memcpy(
331 6 h_.buf + h_.cap - nt,
332 6 op.end() - nt,
333 nt);
334 }
335
336 void
337 7 fields_base::
338 shrink_to_fit() noexcept
339 {
340 14 if(detail::header::bytes_needed(
341 7 h_.size, h_.count) >=
342
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 4 times.
7 h_.cap)
343 3 return;
344 8 fields_base tmp(h_);
345 4 tmp.h_.swap(h_);
346 }
347
348 //------------------------------------------------
349 //
350 // Modifiers
351 //
352 //------------------------------------------------
353
354 std::size_t
355 24 fields_base::
356 erase(
357 field id) noexcept
358 {
359
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 24 times.
24 BOOST_ASSERT(
360 id != field::unknown);
361 #if 1
362 24 auto const end_ = end();
363 24 auto it = find_last(end_, id);
364
2/2
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 21 times.
24 if(it == end_)
365 3 return 0;
366 21 std::size_t n = 1;
367 21 auto const begin_ = begin();
368 21 raw_erase(it.i_);
369
2/2
✓ Branch 1 taken 36 times.
✓ Branch 2 taken 21 times.
57 while(it != begin_)
370 {
371 36 --it;
372
2/2
✓ Branch 2 taken 25 times.
✓ Branch 3 taken 11 times.
36 if(it->id == id)
373 {
374 25 raw_erase(it.i_);
375 25 ++n;
376 }
377 }
378 21 h_.on_erase_all(id);
379 21 return n;
380 #else
381 std::size_t n = 0;
382 auto it0 = find(id);
383 auto const end_ = end();
384 if(it0 != end_)
385 {
386 auto it1 = it0;
387 std::size_t total = 0;
388 std::size_t size = 0;
389 // [it0, it1) run of id
390 for(;;)
391 {
392 size += length(it1.i_);
393 ++it1;
394 if(it1 == end_)
395 goto finish;
396 if(it1->id != id)
397 break;
398 }
399 std::memmove(
400 h_.buf + offset(it0.i_),
401 h_.buf + offset(it1.i_),
402 h_.size - offset(it2.i_));
403
404 finish:
405 h_.size -= size;
406 h_.count -= n;
407 }
408 return n;
409 #endif
410 }
411
412 std::size_t
413 18 fields_base::
414 erase(
415 core::string_view name) noexcept
416 {
417 18 auto it0 = find(name);
418 18 auto const end_ = end();
419
2/2
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 15 times.
18 if(it0 == end_)
420 3 return 0;
421 15 auto it = end_;
422 15 std::size_t n = 1;
423 15 auto const id = it0->id;
424
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 9 times.
15 if(id == field::unknown)
425 {
426 // fix self-intersection
427 6 name = it0->name;
428
429 for(;;)
430 {
431 24 --it;
432
2/2
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 18 times.
24 if(it == it0)
433 6 break;
434 18 if(grammar::ci_is_equal(
435
2/2
✓ Branch 2 taken 9 times.
✓ Branch 3 taken 9 times.
36 it->name, name))
436 {
437 9 raw_erase(it.i_);
438 9 ++n;
439 }
440 }
441 6 raw_erase(it.i_);
442 }
443 else
444 {
445 for(;;)
446 {
447 21 --it;
448
2/2
✓ Branch 1 taken 9 times.
✓ Branch 2 taken 12 times.
21 if(it == it0)
449 9 break;
450
2/2
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 6 times.
12 if(it->id == id)
451 {
452 6 raw_erase(it.i_);
453 6 ++n;
454 }
455 }
456 9 raw_erase(it.i_);
457 9 h_.on_erase_all(id);
458 }
459 15 return n;
460 }
461
462 //------------------------------------------------
463
464 void
465 17 fields_base::
466 set(
467 iterator it,
468 core::string_view value)
469 {
470 17 auto const i = it.i_;
471 17 auto tab = h_.tab();
472 17 auto const& e0 = tab[i];
473 17 auto const pos0 = offset(i);
474 17 auto const pos1 = offset(i + 1 );
475 std::ptrdiff_t dn =
476 17 value.size() -
477 17 it->value.size();
478
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 17 times.
17 if( value.empty() &&
479
1/4
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 17 times.
17 ! it->value.empty())
480 --dn; // remove SP
481 17 else if(
482
2/4
✗ Branch 3 not taken.
✓ Branch 4 taken 17 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 17 times.
17 it->value.empty() &&
483 ! value.empty())
484 ++dn; // add SP
485
486 34 op_t op(*this, &value);
487
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 14 times.
20 if( dn > 0 &&
488
2/4
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
6 op.grow(value.size() -
489
2/2
✓ Branch 3 taken 3 times.
✓ Branch 4 taken 14 times.
20 it->value.size(), 0))
490 {
491 // reallocated
492 3 auto dest = h_.buf +
493 3 pos0 + e0.nn + 1;
494 6 std::memcpy(
495 3 h_.buf,
496 3 op.buf(),
497 3 dest - h_.buf);
498
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 if(! value.empty())
499 {
500 3 *dest++ = ' ';
501
1/2
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
3 value.copy(
502 dest,
503 value.size());
504 3 dest += value.size();
505 }
506 3 *dest++ = '\r';
507 3 *dest++ = '\n';
508 6 std::memcpy(
509 3 h_.buf + pos1 + dn,
510 6 op.buf() + pos1,
511 3 h_.size - pos1);
512 6 std::memcpy(
513 3 h_.buf + h_.cap -
514 3 sizeof(entry) * h_.count,
515 3 &op.tab()[h_.count - 1],
516 3 sizeof(entry) * h_.count);
517 }
518 else
519 {
520 // copy the value first
521 28 auto dest = h_.buf + pos0 +
522 14 it->name.size() + 1;
523
1/2
✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
14 if(! value.empty())
524 {
525 14 *dest++ = ' ';
526
1/2
✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
14 value.copy(
527 dest,
528 value.size());
529 14 dest += value.size();
530 }
531 14 op.move_chars(
532 14 h_.buf + pos1 + dn,
533 14 h_.buf + pos1,
534 14 h_.size - pos1);
535 14 *dest++ = '\r';
536 14 *dest++ = '\n';
537 }
538 {
539 // update tab
540 17 auto ft = h_.tab();
541 22 for(std::size_t j = h_.count - 1;
542
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 17 times.
22 j > i; --j)
543 5 ft[j] = ft[j] + dn;
544 17 auto& e = ft[i];
545 34 e.vp = e.np + e.nn +
546 17 1 + ! value.empty();
547 17 e.vn = static_cast<
548 17 offset_type>(value.size());
549 17 h_.size = static_cast<
550 17 offset_type>(h_.size + dn);
551 }
552 17 auto const id = it->id;
553
2/2
✓ Branch 1 taken 7 times.
✓ Branch 2 taken 10 times.
17 if(h_.is_special(id))
554 {
555 // replace first char of name
556 // with null to hide metadata
557 7 char saved = h_.buf[pos0];
558 7 auto& e = h_.tab()[i];
559 7 e.id = field::unknown;
560 7 h_.buf[pos0] = '\0';
561
1/2
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
7 h_.on_erase(id);
562 7 h_.buf[pos0] = saved; // restore
563 7 e.id = id;
564
1/2
✓ Branch 3 taken 7 times.
✗ Branch 4 not taken.
7 h_.on_insert(id, it->value);
565 }
566 17 }
567
568 // erase existing fields with id
569 // and then add the field with value
570 void
571 18 fields_base::
572 set(
573 field id,
574 core::string_view value)
575 {
576
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
18 BOOST_ASSERT(
577 id != field::unknown);
578 18 auto const i0 = h_.find(id);
579
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 6 times.
18 if(i0 != h_.count)
580 {
581 // field exists
582 12 auto const ft = h_.tab();
583 {
584 // provide strong guarantee
585 auto const n0 =
586 12 h_.size - length(i0);
587 auto const n =
588 12 ft[i0].nn + 2 +
589 12 value.size() + 2;
590 // VFALCO missing overflow check
591
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 reserve_bytes(n0 + n);
592 }
593 12 erase_all_impl(i0, id);
594 }
595 18 insert_impl(id, to_string(id),
596 18 value, h_.count);
597 18 }
598
599 // erase existing fields with name
600 // and then add the field with value
601 void
602 13 fields_base::
603 set(
604 core::string_view name,
605 core::string_view value)
606 {
607 13 auto const i0 = h_.find(name);
608
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 4 times.
13 if(i0 != h_.count)
609 {
610 // field exists
611 9 auto const ft = h_.tab();
612 9 auto const id = ft[i0].id;
613 {
614 // provide strong guarantee
615 auto const n0 =
616 9 h_.size - length(i0);
617 auto const n =
618 9 ft[i0].nn + 2 +
619 9 value.size() + 2;
620 // VFALCO missing overflow check
621
1/2
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
9 reserve_bytes(n0 + n);
622 }
623 // VFALCO simple algorithm but
624 // costs one extra memmove
625 9 erase_all_impl(i0, id);
626 }
627 13 insert_impl(
628 string_to_field(name),
629 13 name, value, h_.count);
630 12 }
631
632 //------------------------------------------------
633 //
634 // (implementation)
635 //
636 //------------------------------------------------
637
638 // copy start line and fields
639 void
640 9 fields_base::
641 copy_impl(
642 detail::header const& h)
643 {
644
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
9 BOOST_ASSERT(
645 h.kind == ph_->kind);
646
2/2
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 3 times.
9 if(! h.is_default())
647 {
648 auto const n =
649 6 detail::header::bytes_needed(
650 6 h.size, h.count);
651
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 5 times.
6 if(n <= h_.cap)
652 {
653 // no realloc
654 1 h.assign_to(h_);
655 1 h.copy_table(
656 1 h_.buf + h_.cap);
657 1 std::memcpy(
658 1 h_.buf,
659 1 h.cbuf,
660 1 h.size);
661 1 return;
662 }
663 }
664
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
16 fields_base tmp(h);
665 8 tmp.h_.swap(h_);
666 }
667
668 void
669 85 fields_base::
670 insert_impl(
671 field id,
672 core::string_view name,
673 core::string_view value,
674 std::size_t before)
675 {
676 85 auto const tab0 = h_.tab_();
677 85 auto const pos = offset(before);
678 auto const n =
679 85 name.size() + // name
680 85 1 + // ':'
681 85 ! value.empty() + // [SP]
682 85 value.size() + // value
683 85 2; // CRLF
684
685 170 op_t op(*this, &name, &value);
686
4/4
✓ Branch 1 taken 83 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 59 times.
✓ Branch 4 taken 24 times.
85 if(op.grow(n, 1))
687 {
688 // reallocated
689
2/2
✓ Branch 0 taken 50 times.
✓ Branch 1 taken 9 times.
59 if(pos > 0)
690 50 std::memcpy(
691 50 h_.buf,
692 50 op.cbuf(),
693 pos);
694
2/2
✓ Branch 0 taken 21 times.
✓ Branch 1 taken 38 times.
59 if(before > 0)
695 42 std::memcpy(
696 21 h_.tab_() - before,
697 21 tab0 - before,
698 before * sizeof(entry));
699 118 std::memcpy(
700 59 h_.buf + pos + n,
701 59 op.cbuf() + pos,
702 59 h_.size - pos);
703 }
704 else
705 {
706 24 op.move_chars(
707 24 h_.buf + pos + n,
708 24 h_.buf + pos,
709 24 h_.size - pos);
710 }
711
712 // serialize
713 {
714 83 auto dest = h_.buf + pos;
715
1/2
✓ Branch 2 taken 83 times.
✗ Branch 3 not taken.
83 name.copy(dest, name.size());
716 83 dest += name.size();
717 83 *dest++ = ':';
718
2/2
✓ Branch 1 taken 74 times.
✓ Branch 2 taken 9 times.
83 if(! value.empty())
719 {
720 74 *dest++ = ' ';
721
1/2
✓ Branch 2 taken 74 times.
✗ Branch 3 not taken.
74 value.copy(
722 dest, value.size());
723 74 dest += value.size();
724 }
725 83 *dest++ = '\r';
726 83 *dest = '\n';
727 }
728
729 // update table
730 83 auto const tab = h_.tab_();
731 {
732 83 auto i = h_.count - before;
733
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 65 times.
83 if(i > 0)
734 {
735 18 auto p0 = tab0 - h_.count;
736 18 auto p = tab - h_.count - 1;
737 18 do
738 {
739 36 *p++ = *p0++ + n;
740 }
741
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 18 times.
36 while(--i);
742 }
743 }
744 83 auto& e = tab[0 - static_cast<std::ptrdiff_t>(before) - 1];
745 83 e.np = static_cast<offset_type>(
746 83 pos - h_.prefix);
747 83 e.nn = static_cast<
748 83 offset_type>(name.size());
749 83 e.vp = static_cast<offset_type>(
750 166 pos - h_.prefix +
751 83 name.size() + 1 +
752 83 ! value.empty());
753 83 e.vn = static_cast<
754 83 offset_type>(value.size());
755 83 e.id = id;
756
757 // update container
758 83 h_.count++;
759 83 h_.size = static_cast<
760 83 offset_type>(h_.size + n);
761
2/2
✓ Branch 0 taken 68 times.
✓ Branch 1 taken 15 times.
83 if( id != field::unknown)
762
1/2
✓ Branch 1 taken 68 times.
✗ Branch 2 not taken.
68 h_.on_insert(id, value);
763 83 }
764
765 // erase i and update metadata
766 void
767 31 fields_base::
768 erase_impl(
769 std::size_t i,
770 field id) noexcept
771 {
772 31 raw_erase(i);
773
1/2
✓ Branch 0 taken 31 times.
✗ Branch 1 not taken.
31 if(id != field::unknown)
774 31 h_.on_erase(id);
775 31 }
776
777 //------------------------------------------------
778
779 void
780 141 fields_base::
781 raw_erase(
782 std::size_t i) noexcept
783 {
784
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 141 times.
141 BOOST_ASSERT(i < h_.count);
785
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 141 times.
141 BOOST_ASSERT(h_.buf != nullptr);
786 141 auto const p0 = offset(i);
787 141 auto const p1 = offset(i + 1);
788 141 std::memmove(
789 141 h_.buf + p0,
790 141 h_.buf + p1,
791 141 h_.size - p1);
792 141 auto const n = p1 - p0;
793 141 --h_.count;
794 141 auto ft = h_.tab();
795
2/2
✓ Branch 0 taken 75 times.
✓ Branch 1 taken 141 times.
216 for(;i < h_.count; ++i)
796 75 ft[i] = ft[i + 1] - n;
797 141 h_.size = static_cast<
798 141 offset_type>(h_.size - n);
799 141 }
800
801 //------------------------------------------------
802
803 // erase all fields with id
804 // and update metadata
805 std::size_t
806 21 fields_base::
807 erase_all_impl(
808 std::size_t i0,
809 field id) noexcept
810 {
811
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 21 times.
21 BOOST_ASSERT(
812 id != field::unknown);
813 21 std::size_t n = 1;
814 21 std::size_t i = h_.count - 1;
815 21 auto const ft = h_.tab();
816
2/2
✓ Branch 0 taken 25 times.
✓ Branch 1 taken 21 times.
46 while(i > i0)
817 {
818
2/2
✓ Branch 1 taken 13 times.
✓ Branch 2 taken 12 times.
25 if(ft[i].id == id)
819 {
820 13 raw_erase(i);
821 13 ++n;
822 }
823 // go backwards to
824 // reduce memmoves
825 25 --i;
826 }
827 21 raw_erase(i0);
828 21 h_.on_erase_all(id);
829 21 return n;
830 }
831
832 // return i-th field absolute offset
833 std::size_t
834 443 fields_base::
835 offset(
836 std::size_t i) const noexcept
837 {
838
2/2
✓ Branch 0 taken 143 times.
✓ Branch 1 taken 300 times.
443 if(i == 0)
839 143 return h_.prefix;
840
2/2
✓ Branch 0 taken 174 times.
✓ Branch 1 taken 126 times.
300 if(i < h_.count)
841 348 return h_.prefix +
842 174 h_.tab_()[0-(static_cast<std::ptrdiff_t>(i) + 1)].np;
843 // make final CRLF the last "field"
844 //BOOST_ASSERT(i == h_.count);
845 126 return h_.size - 2;
846 }
847
848 // return i-th field absolute length
849 std::size_t
850 21 fields_base::
851 length(
852 std::size_t i) const noexcept
853 {
854 return
855 21 offset(i + 1) -
856 21 offset(i);
857 }
858
859 //------------------------------------------------
860
861 // erase n fields matching id
862 // without updating metadata
863 void
864 fields_base::
865 raw_erase_n(
866 field id,
867 std::size_t n) noexcept
868 {
869 // iterate in reverse
870 auto e = &h_.tab()[h_.count];
871 auto const e0 = &h_.tab()[0];
872 while(n > 0)
873 {
874 BOOST_ASSERT(e != e0);
875 ++e; // decrement
876 if(e->id == id)
877 {
878 raw_erase(e0 - e);
879 --n;
880 }
881 }
882 }
883
884 } // http_proto
885 } // boost
886