GCC Code Coverage Report


Directory: libs/http_proto/include/boost/http_proto/
File: boost/http_proto/impl/fields_base.ipp
Date: 2023-02-02 18:17:22
Exec Total Coverage
Lines: 385 412 93.4%
Functions: 29 31 93.5%
Branches: 127 178 71.3%

Line Branch Exec Source
1 //
2 // Copyright (c) 2021 Vinnie Falco (vinnie dot falco at gmail dot 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 #ifndef BOOST_HTTP_PROTO_IMPL_FIELDS_BASE_IPP
11 #define BOOST_HTTP_PROTO_IMPL_FIELDS_BASE_IPP
12
13 #include <boost/http_proto/fields.hpp>
14 #include <boost/http_proto/field.hpp>
15 #include <boost/http_proto/detail/copied_strings.hpp>
16 #include <boost/http_proto/detail/except.hpp>
17 #include <boost/http_proto/detail/number_string.hpp>
18 #include <boost/http_proto/detail/move_chars.hpp>
19 #include <boost/assert.hpp>
20 #include <boost/assert/source_location.hpp>
21 #include <string>
22
23 namespace boost {
24 namespace http_proto {
25
26 class fields_base::
27 op_t
28 {
29 fields_base& self_;
30 string_view* s0_;
31 string_view* s1_;
32 char* buf_ = nullptr;
33 char const* cbuf_ = nullptr;
34 std::size_t cap_ = 0;
35
36 public:
37 explicit
38 601 op_t(
39 fields_base& self,
40 string_view* s0 = nullptr,
41 string_view* s1 = nullptr) noexcept
42 601 : self_(self)
43 , s0_(s0)
44 601 , s1_(s1)
45 {
46 601 }
47
48 601 ~op_t()
49 601 {
50
2/2
✓ Branch 0 taken 63 times.
✓ Branch 1 taken 538 times.
601 if(buf_)
51
1/2
✓ Branch 0 taken 63 times.
✗ Branch 1 not taken.
63 delete[] buf_;
52 601 }
53
54 char const*
55 6 buf() const noexcept
56 {
57 6 return buf_;
58 }
59
60 char const*
61 114 cbuf() const noexcept
62 {
63 114 return cbuf_;
64 }
65
66 char*
67 9 end() const noexcept
68 {
69 9 return buf_ + cap_;
70 }
71
72 table
73 3 tab() const noexcept
74 {
75 3 return table(end());
76 }
77
78 static
79 std::size_t
80 growth(
81 std::size_t n0,
82 std::size_t m) noexcept;
83
84 bool
85 reserve(std::size_t bytes);
86
87 bool
88 grow(
89 std::size_t extra_char,
90 std::size_t extra_field);
91
92 void
93 copy_prefix(
94 std::size_t n,
95 std::size_t i) noexcept;
96
97 void
98 move_chars(
99 char* dest,
100 char const* src,
101 std::size_t n) const noexcept;
102 };
103
104 /* Growth functions for containers
105
106 N1 = g( N0, M );
107
108 g = growth function
109 M = minimum capacity
110 N0 = old size
111 N1 = new size
112 */
113 std::size_t
114 1129 fields_base::
115 op_t::
116 growth(
117 std::size_t n0,
118 std::size_t m) noexcept
119 {
120 1129 auto const E = alignof(entry);
121 1129 auto const m1 =
122 1129 E * ((m + E - 1) / E);
123
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1129 times.
1129 BOOST_ASSERT(m1 >= m);
124
2/2
✓ Branch 0 taken 940 times.
✓ Branch 1 taken 189 times.
1129 if(n0 == 0)
125 {
126 // exact
127 940 return m1;
128 }
129
2/2
✓ Branch 0 taken 119 times.
✓ Branch 1 taken 70 times.
189 if(m1 > n0)
130 119 return m1;
131 70 return n0;
132 }
133
134 bool
135 585 fields_base::
136 op_t::
137 reserve(
138 std::size_t bytes)
139 {
140
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 584 times.
585 if(bytes > max_capacity_in_bytes())
141 {
142 // max capacity exceeded
143 1 detail::throw_length_error();
144 }
145 584 auto n = growth(
146 584 self_.h_.cap, bytes);
147
2/2
✓ Branch 0 taken 48 times.
✓ Branch 1 taken 536 times.
584 if(n <= self_.h_.cap)
148 48 return false;
149 536 auto buf = new char[n];
150 536 buf_ = self_.h_.buf;
151 536 cbuf_ = self_.h_.cbuf;
152 536 cap_ = self_.h_.cap;
153 536 self_.h_.buf = buf;
154 536 self_.h_.cbuf = buf;
155 536 self_.h_.cap = n;
156 536 return true;
157 }
158
159 bool
160 547 fields_base::
161 op_t::
162 grow(
163 std::size_t extra_char,
164 std::size_t extra_field)
165 {
166 // extra_field is naturally limited
167 // by max_off_t, since each field
168 // is at least 4 bytes
169
2/4
✓ Branch 0 taken 547 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 547 times.
✗ Branch 3 not taken.
547 BOOST_ASSERT(
170 extra_field <= max_off_t &&
171 extra_field <= static_cast<
172 std::size_t>(
173 max_off_t - self_.h_.count));
174
2/2
✓ Branch 0 taken 545 times.
✓ Branch 1 taken 2 times.
547 if( extra_char > max_off_t ||
175 545 extra_char > static_cast<std::size_t>(
176
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 545 times.
545 max_off_t - self_.h_.size))
177 2 detail::throw_length_error();
178 1090 auto n1 = growth(
179 545 self_.h_.cap,
180 detail::header::bytes_needed(
181 545 self_.h_.size + extra_char,
182 545 self_.h_.count + extra_field));
183 545 return reserve(n1);
184 }
185
186 void
187 fields_base::
188 op_t::
189 copy_prefix(
190 std::size_t n,
191 std::size_t i) noexcept
192 {
193 // copy first n chars
194 std::memcpy(
195 self_.h_.buf,
196 cbuf_,
197 n);
198 // copy first i entries
199 if(i > 0)
200 std::memcpy(
201 self_.h_.tab_() - i,
202 reinterpret_cast<entry*>(
203 buf_ + cap_) - i,
204 i * sizeof(entry));
205 }
206
207 void
208 37 fields_base::
209 op_t::
210 move_chars(
211 char* dest,
212 char const* src,
213 std::size_t n) const noexcept
214 {
215 37 detail::move_chars(
216 37 dest, src, n, s0_, s1_);
217 37 }
218
219 //------------------------------------------------
220
221 138 fields_base::
222 fields_base(
223 detail::kind k) noexcept
224 : fields_view_base(&h_)
225 138 , h_(k)
226 {
227 }
228
229 // copy s and parse it
230 906 fields_base::
231 fields_base(
232 detail::kind k,
233 string_view s)
234 : fields_view_base(&h_)
235 906 , h_(detail::empty{k})
236 {
237 906 auto n = detail::header::count_crlf(s);
238
2/2
✓ Branch 0 taken 197 times.
✓ Branch 1 taken 256 times.
906 if(h_.kind == detail::kind::fields)
239 {
240
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 197 times.
394 if(n < 1)
241 detail::throw_invalid_argument();
242 394 n -= 1;
243 }
244 else
245 {
246
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 256 times.
512 if(n < 2)
247 detail::throw_invalid_argument();
248 512 n -= 2;
249 }
250 1812 op_t op(*this);
251
1/2
✓ Branch 2 taken 453 times.
✗ Branch 3 not taken.
906 op.grow(s.size(), n);
252
1/2
✓ Branch 2 taken 453 times.
✗ Branch 3 not taken.
906 s.copy(h_.buf, s.size());
253 906 error_code ec;
254 906 detail::header::config cfg;
255 906 h_.parse(cfg, s.size(), ec);
256
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 453 times.
906 if(ec.failed())
257 detail::throw_system_error(ec);
258 }
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 1080 fields_base::
287 1104 ~fields_base()
288 {
289
2/2
✓ Branch 0 taken 477 times.
✓ Branch 1 taken 63 times.
1080 if(h_.buf)
290
1/2
✓ Branch 0 taken 477 times.
✗ Branch 1 not taken.
954 delete[] h_.buf;
291 1080 }
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 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 string_view value)
469 {
470 17 auto const i = it.i_;
471 17 auto const& e0 = h_.tab()[i];
472 17 auto const pos0 = offset(i);
473 17 auto const pos1 = offset(i + 1 );
474 std::ptrdiff_t dn =
475 17 value.size() -
476 17 it->value.size();
477
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 17 times.
17 if( value.empty() &&
478
1/4
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 17 times.
17 ! it->value.empty())
479 --dn; // remove SP
480 17 else if(
481
2/4
✗ Branch 3 not taken.
✓ Branch 4 taken 17 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 17 times.
17 it->value.empty() &&
482 ! value.empty())
483 ++dn; // add SP
484
485 34 op_t op(*this, &value);
486
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 14 times.
20 if( dn > 0 &&
487
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() -
488
2/2
✓ Branch 3 taken 3 times.
✓ Branch 4 taken 14 times.
20 it->value.size(), 0))
489 {
490 // reallocated
491 3 auto dest = h_.buf +
492 3 pos0 + e0.nn + 1;
493 6 std::memcpy(
494 3 h_.buf,
495 3 op.buf(),
496 3 dest - h_.buf);
497
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 if(! value.empty())
498 {
499 3 *dest++ = ' ';
500
1/2
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
3 value.copy(
501 dest,
502 value.size());
503 3 dest += value.size();
504 }
505 3 *dest++ = '\r';
506 3 *dest++ = '\n';
507 6 std::memcpy(
508 3 h_.buf + pos1 + dn,
509 6 op.buf() + pos1,
510 3 h_.size - pos1);
511 6 std::memcpy(
512 3 h_.buf + h_.cap -
513 3 sizeof(entry) * h_.count,
514 3 &op.tab()[h_.count - 1],
515 3 sizeof(entry) * h_.count);
516 }
517 else
518 {
519 // copy the value first
520 28 auto dest = h_.buf + pos0 +
521 14 it->name.size() + 1;
522
1/2
✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
14 if(! value.empty())
523 {
524 14 *dest++ = ' ';
525
1/2
✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
14 value.copy(
526 dest,
527 value.size());
528 14 dest += value.size();
529 }
530 14 op.move_chars(
531 14 h_.buf + pos1 + dn,
532 14 h_.buf + pos1,
533 14 h_.size - pos1);
534 14 *dest++ = '\r';
535 14 *dest++ = '\n';
536 }
537 {
538 // update tab
539 17 auto ft = h_.tab();
540 22 for(std::size_t j = h_.count - 1;
541
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 17 times.
22 j > i; --j)
542 5 ft[j] = ft[j] + dn;
543 17 auto& e = ft[i];
544 34 e.vp = e.np + e.nn +
545 17 1 + ! value.empty();
546 17 e.vn = static_cast<
547 17 off_t>(value.size());
548 17 h_.size = static_cast<
549 17 off_t>(h_.size + dn);
550 }
551 17 auto const id = it->id;
552
2/2
✓ Branch 1 taken 7 times.
✓ Branch 2 taken 10 times.
17 if(h_.is_special(id))
553 {
554 // replace first char of name
555 // with null to hide metadata
556 7 char saved = h_.buf[pos0];
557 7 auto& e = h_.tab()[i];
558 7 e.id = field::unknown;
559 7 h_.buf[pos0] = '\0';
560
1/2
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
7 h_.on_erase(id);
561 7 h_.buf[pos0] = saved;
562 7 e.id = id;
563
1/2
✓ Branch 3 taken 7 times.
✗ Branch 4 not taken.
7 h_.on_insert(id, it->value);
564 }
565 17 }
566
567 // erase existing fields with id
568 // and then add the field with value
569 void
570 18 fields_base::
571 set(
572 field id,
573 string_view value)
574 {
575
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
18 BOOST_ASSERT(
576 id != field::unknown);
577 18 auto const i0 = h_.find(id);
578
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 6 times.
18 if(i0 != h_.count)
579 {
580 // field exists
581 12 auto const ft = h_.tab();
582 {
583 // provide strong guarantee
584 auto const n0 =
585 12 h_.size - length(i0);
586 auto const n =
587 12 ft[i0].nn + 2 +
588 12 value.size() + 2;
589 // VFALCO missing overflow check
590
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 reserve_bytes(n0 + n);
591 }
592 12 erase_all_impl(i0, id);
593 }
594 18 insert_impl(id, to_string(id),
595 18 value, h_.count);
596 18 }
597
598 // erase existing fields with name
599 // and then add the field with value
600 void
601 13 fields_base::
602 set(
603 string_view name,
604 string_view value)
605 {
606 13 auto const i0 = h_.find(name);
607
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 4 times.
13 if(i0 != h_.count)
608 {
609 // field exists
610 9 auto const ft = h_.tab();
611 9 auto const id = ft[i0].id;
612 {
613 // provide strong guarantee
614 auto const n0 =
615 9 h_.size - length(i0);
616 auto const n =
617 9 ft[i0].nn + 2 +
618 9 value.size() + 2;
619 // VFALCO missing overflow check
620
1/2
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
9 reserve_bytes(n0 + n);
621 }
622 // VFALCO simple algorithm but
623 // costs one extra memmove
624 9 erase_all_impl(i0, id);
625 }
626 13 insert_impl(
627 string_to_field(name),
628 13 name, value, h_.count);
629 12 }
630
631 //------------------------------------------------
632 //
633 // (implementation)
634 //
635 //------------------------------------------------
636
637 // copy start line and fields
638 void
639 9 fields_base::
640 copy_impl(
641 detail::header const& h)
642 {
643
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
9 BOOST_ASSERT(
644 h.kind == ph_->kind);
645
2/2
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 3 times.
9 if(! h.is_default())
646 {
647 auto const n =
648 6 detail::header::bytes_needed(
649 6 h.size, h.count);
650
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 5 times.
6 if(n <= h_.cap)
651 {
652 // no realloc
653 1 h.assign_to(h_);
654 1 h.copy_table(
655 1 h_.buf + h_.cap);
656 1 std::memcpy(
657 1 h_.buf,
658 1 h.cbuf,
659 1 h.size);
660 1 return;
661 }
662 }
663
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
16 fields_base tmp(h);
664 8 tmp.h_.swap(h_);
665 }
666
667 void
668 79 fields_base::
669 insert_impl(
670 field id,
671 string_view name,
672 string_view value,
673 std::size_t before)
674 {
675 79 auto const tab0 = h_.tab_();
676 79 auto const pos = offset(before);
677 auto const n =
678 79 name.size() + // name
679 79 1 + // ':'
680 79 ! value.empty() + // [SP]
681 79 value.size() + // value
682 79 2; // CRLF
683
684 158 op_t op(*this, &name, &value);
685
4/4
✓ Branch 1 taken 77 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 54 times.
✓ Branch 4 taken 23 times.
79 if(op.grow(n, 1))
686 {
687 // reallocated
688
2/2
✓ Branch 0 taken 46 times.
✓ Branch 1 taken 8 times.
54 if(pos > 0)
689 46 std::memcpy(
690 46 h_.buf,
691 46 op.cbuf(),
692 pos);
693
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 36 times.
54 if(before > 0)
694 36 std::memcpy(
695 18 h_.tab_() - before,
696 18 tab0 - before,
697 before * sizeof(entry));
698 108 std::memcpy(
699 54 h_.buf + pos + n,
700 54 op.cbuf() + pos,
701 54 h_.size - pos);
702 }
703 else
704 {
705 23 op.move_chars(
706 23 h_.buf + pos + n,
707 23 h_.buf + pos,
708 23 h_.size - pos);
709 }
710
711 // serialize
712 {
713 77 auto dest = h_.buf + pos;
714
1/2
✓ Branch 2 taken 77 times.
✗ Branch 3 not taken.
77 name.copy(dest, name.size());
715 77 dest += name.size();
716 77 *dest++ = ':';
717
2/2
✓ Branch 1 taken 74 times.
✓ Branch 2 taken 3 times.
77 if(! value.empty())
718 {
719 74 *dest++ = ' ';
720
1/2
✓ Branch 2 taken 74 times.
✗ Branch 3 not taken.
74 value.copy(
721 dest, value.size());
722 74 dest += value.size();
723 }
724 77 *dest++ = '\r';
725 77 *dest = '\n';
726 }
727
728 // update table
729 77 auto const tab = h_.tab_();
730 {
731 77 auto i = h_.count - before;
732
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 59 times.
77 if(i > 0)
733 {
734 18 auto p0 = tab0 - h_.count;
735 18 auto p = tab - h_.count - 1;
736 18 do
737 {
738 36 *p++ = *p0++ + n;
739 }
740
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 18 times.
36 while(--i);
741 }
742 }
743 77 auto& e = tab[0 - before - 1];
744 77 e.np = static_cast<off_t>(
745 77 pos - h_.prefix);
746 77 e.nn = static_cast<
747 77 off_t>(name.size());
748 77 e.vp = static_cast<off_t>(
749 154 pos - h_.prefix +
750 77 name.size() + 1 +
751 77 ! value.empty());
752 77 e.vn = static_cast<
753 77 off_t>(value.size());
754 77 e.id = id;
755
756 // update container
757 77 h_.count++;
758 77 h_.size = static_cast<
759 77 off_t>(h_.size + n);
760
2/2
✓ Branch 0 taken 68 times.
✓ Branch 1 taken 9 times.
77 if( id != field::unknown)
761
1/2
✓ Branch 1 taken 68 times.
✗ Branch 2 not taken.
68 h_.on_insert(id, value);
762 77 }
763
764 // erase i and update metadata
765 void
766 31 fields_base::
767 erase_impl(
768 std::size_t i,
769 field id) noexcept
770 {
771 31 raw_erase(i);
772
1/2
✓ Branch 0 taken 31 times.
✗ Branch 1 not taken.
31 if(id != field::unknown)
773 31 h_.on_erase(id);
774 31 }
775
776 //------------------------------------------------
777
778 void
779 141 fields_base::
780 raw_erase(
781 std::size_t i) noexcept
782 {
783
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 141 times.
141 BOOST_ASSERT(i < h_.count);
784
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 141 times.
141 BOOST_ASSERT(h_.buf != nullptr);
785 141 auto const p0 = offset(i);
786 141 auto const p1 = offset(i + 1);
787 141 std::memmove(
788 141 h_.buf + p0,
789 141 h_.buf + p1,
790 141 h_.size - p1);
791 141 auto const n = p1 - p0;
792 141 --h_.count;
793 141 auto ft = h_.tab();
794
2/2
✓ Branch 0 taken 75 times.
✓ Branch 1 taken 141 times.
216 for(;i < h_.count; ++i)
795 75 ft[i] = ft[i + 1] - n;
796 141 h_.size = static_cast<
797 141 off_t>(h_.size - n);
798 141 }
799
800 //------------------------------------------------
801
802 // erase all fields with id
803 // and update metadata
804 std::size_t
805 21 fields_base::
806 erase_all_impl(
807 std::size_t i0,
808 field id) noexcept
809 {
810
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 21 times.
21 BOOST_ASSERT(
811 id != field::unknown);
812 21 std::size_t n = 1;
813 21 std::size_t i = h_.count - 1;
814 21 auto const ft = h_.tab();
815
2/2
✓ Branch 0 taken 25 times.
✓ Branch 1 taken 21 times.
46 while(i > i0)
816 {
817
2/2
✓ Branch 1 taken 13 times.
✓ Branch 2 taken 12 times.
25 if(ft[i].id == id)
818 {
819 13 raw_erase(i);
820 13 ++n;
821 }
822 // go backwards to
823 // reduce memmoves
824 25 --i;
825 }
826 21 raw_erase(i0);
827 21 h_.on_erase_all(id);
828 21 return n;
829 }
830
831 // return i-th field absolute offset
832 std::size_t
833 437 fields_base::
834 offset(
835 std::size_t i) const noexcept
836 {
837
2/2
✓ Branch 0 taken 140 times.
✓ Branch 1 taken 297 times.
437 if(i == 0)
838 140 return h_.prefix;
839
2/2
✓ Branch 0 taken 174 times.
✓ Branch 1 taken 123 times.
297 if(i < h_.count)
840 348 return h_.prefix +
841 174 h_.tab_()[0-(i + 1)].np;
842 // make final CRLF the last "field"
843 //BOOST_ASSERT(i == h_.count);
844 123 return h_.size - 2;
845 }
846
847 // return i-th field absolute length
848 std::size_t
849 21 fields_base::
850 length(
851 std::size_t i) const noexcept
852 {
853 return
854 21 offset(i + 1) -
855 21 offset(i);
856 }
857
858 //------------------------------------------------
859
860 // erase n fields matching id
861 // without updating metadata
862 void
863 fields_base::
864 raw_erase_n(
865 field id,
866 std::size_t n) noexcept
867 {
868 // iterate in reverse
869 auto e = &h_.tab()[h_.count];
870 auto const e0 = &h_.tab()[0];
871 while(n > 0)
872 {
873 BOOST_ASSERT(e != e0);
874 ++e; // decrement
875 if(e->id == id)
876 {
877 raw_erase(e0 - e);
878 --n;
879 }
880 }
881 }
882
883 } // http_proto
884 } // boost
885
886 #endif
887