GCC Code Coverage Report


Directory: libs/http_proto/include/boost/http_proto/
File: boost/http_proto/impl/fields_base.ipp
Date: 2023-02-27 20:43:06
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.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 #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 core::string_view* s0_;
31 core::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 core::string_view* s0 = nullptr,
41 core::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 62 times.
✓ Branch 1 taken 539 times.
601 if(buf_)
51
1/2
✓ Branch 0 taken 62 times.
✗ Branch 1 not taken.
62 delete[] buf_;
52 601 }
53
54 char const*
55 6 buf() const noexcept
56 {
57 6 return buf_;
58 }
59
60 char const*
61 112 cbuf() const noexcept
62 {
63 112 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 117 times.
✓ Branch 1 taken 72 times.
189 if(m1 > n0)
130 117 return m1;
131 72 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 49 times.
✓ Branch 1 taken 535 times.
584 if(n <= self_.h_.cap)
148 49 return false;
149 535 auto buf = new char[n];
150 535 buf_ = self_.h_.buf;
151 535 cbuf_ = self_.h_.cbuf;
152 535 cap_ = self_.h_.cap;
153 535 self_.h_.buf = buf;
154 535 self_.h_.cbuf = buf;
155 535 self_.h_.cap = n;
156 535 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: "X:\r\n"
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 38 fields_base::
209 op_t::
210 move_chars(
211 char* dest,
212 char const* src,
213 std::size_t n) const noexcept
214 {
215 38 detail::move_chars(
216 38 dest, src, n, s0_, s1_);
217 38 }
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 core::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 system::error_code ec;
254 // VFALCO This is using defaults?
255 906 header_limits lim;
256 906 h_.parse(s.size(), lim, ec);
257
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 453 times.
906 if(ec.failed())
258 detail::throw_system_error(ec);
259 }
260
261 // construct a complete copy of h
262 36 fields_base::
263 fields_base(
264 24 detail::header const& h)
265 24 : fields_view_base(&h_)
266 36 , h_(h.kind)
267 {
268
2/2
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 12 times.
36 if(h.is_default())
269 {
270
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
12 BOOST_ASSERT(h.cap == 0);
271
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
12 BOOST_ASSERT(h.buf == nullptr);
272 12 h_ = h;
273 12 return;
274 }
275
276 // allocate and copy the buffer
277 48 op_t op(*this);
278
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
24 op.grow(h.size, h.count);
279 24 h.assign_to(h_);
280 24 std::memcpy(
281 24 h_.buf, h.cbuf, h.size);
282 24 h.copy_table(h_.buf + h_.cap);
283 }
284
285 //------------------------------------------------
286
287 1080 fields_base::
288 1104 ~fields_base()
289 {
290
2/2
✓ Branch 0 taken 477 times.
✓ Branch 1 taken 63 times.
1080 if(h_.buf)
291
1/2
✓ Branch 0 taken 477 times.
✗ Branch 1 not taken.
954 delete[] h_.buf;
292 1080 }
293
294 //------------------------------------------------
295 //
296 // Capacity
297 //
298 //------------------------------------------------
299
300 void
301 8 fields_base::
302 clear() noexcept
303 {
304
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 4 times.
8 if(! h_.buf)
305 4 return;
306 using H =
307 detail::header;
308 auto const& h =
309 4 *H::get_default(
310 4 h_.kind);
311 4 h.assign_to(h_);
312 4 std::memcpy(
313 4 h_.buf,
314 4 h.cbuf,
315 4 h_.size);
316 }
317
318 void
319 40 fields_base::
320 reserve_bytes(
321 std::size_t n)
322 {
323 41 op_t op(*this);
324
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))
325 25 return;
326 28 std::memcpy(
327 14 h_.buf, op.cbuf(), h_.size);
328 14 auto const nt =
329 14 sizeof(entry) * h_.count;
330
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 8 times.
14 if(nt > 0)
331 6 std::memcpy(
332 6 h_.buf + h_.cap - nt,
333 6 op.end() - nt,
334 nt);
335 }
336
337 void
338 7 fields_base::
339 shrink_to_fit() noexcept
340 {
341 14 if(detail::header::bytes_needed(
342 7 h_.size, h_.count) >=
343
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 4 times.
7 h_.cap)
344 3 return;
345 8 fields_base tmp(h_);
346 4 tmp.h_.swap(h_);
347 }
348
349 //------------------------------------------------
350 //
351 // Modifiers
352 //
353 //------------------------------------------------
354
355 std::size_t
356 24 fields_base::
357 erase(
358 field id) noexcept
359 {
360
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 24 times.
24 BOOST_ASSERT(
361 id != field::unknown);
362 #if 1
363 24 auto const end_ = end();
364 24 auto it = find_last(end_, id);
365
2/2
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 21 times.
24 if(it == end_)
366 3 return 0;
367 21 std::size_t n = 1;
368 21 auto const begin_ = begin();
369 21 raw_erase(it.i_);
370
2/2
✓ Branch 1 taken 36 times.
✓ Branch 2 taken 21 times.
57 while(it != begin_)
371 {
372 36 --it;
373
2/2
✓ Branch 2 taken 25 times.
✓ Branch 3 taken 11 times.
36 if(it->id == id)
374 {
375 25 raw_erase(it.i_);
376 25 ++n;
377 }
378 }
379 21 h_.on_erase_all(id);
380 21 return n;
381 #else
382 std::size_t n = 0;
383 auto it0 = find(id);
384 auto const end_ = end();
385 if(it0 != end_)
386 {
387 auto it1 = it0;
388 std::size_t total = 0;
389 std::size_t size = 0;
390 // [it0, it1) run of id
391 for(;;)
392 {
393 size += length(it1.i_);
394 ++it1;
395 if(it1 == end_)
396 goto finish;
397 if(it1->id != id)
398 break;
399 }
400 std::memmove(
401 h_.buf + offset(it0.i_),
402 h_.buf + offset(it1.i_),
403 h_.size - offset(it2.i_));
404
405 finish:
406 h_.size -= size;
407 h_.count -= n;
408 }
409 return n;
410 #endif
411 }
412
413 std::size_t
414 18 fields_base::
415 erase(
416 core::string_view name) noexcept
417 {
418 18 auto it0 = find(name);
419 18 auto const end_ = end();
420
2/2
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 15 times.
18 if(it0 == end_)
421 3 return 0;
422 15 auto it = end_;
423 15 std::size_t n = 1;
424 15 auto const id = it0->id;
425
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 9 times.
15 if(id == field::unknown)
426 {
427 // fix self-intersection
428 6 name = it0->name;
429
430 for(;;)
431 {
432 24 --it;
433
2/2
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 18 times.
24 if(it == it0)
434 6 break;
435 18 if(grammar::ci_is_equal(
436
2/2
✓ Branch 2 taken 9 times.
✓ Branch 3 taken 9 times.
36 it->name, name))
437 {
438 9 raw_erase(it.i_);
439 9 ++n;
440 }
441 }
442 6 raw_erase(it.i_);
443 }
444 else
445 {
446 for(;;)
447 {
448 21 --it;
449
2/2
✓ Branch 1 taken 9 times.
✓ Branch 2 taken 12 times.
21 if(it == it0)
450 9 break;
451
2/2
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 6 times.
12 if(it->id == id)
452 {
453 6 raw_erase(it.i_);
454 6 ++n;
455 }
456 }
457 9 raw_erase(it.i_);
458 9 h_.on_erase_all(id);
459 }
460 15 return n;
461 }
462
463 //------------------------------------------------
464
465 void
466 17 fields_base::
467 set(
468 iterator it,
469 core::string_view value)
470 {
471 17 auto const i = it.i_;
472 17 auto const& e0 = h_.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 off_t>(value.size());
549 17 h_.size = static_cast<
550 17 off_t>(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 79 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 79 auto const tab0 = h_.tab_();
677 79 auto const pos = offset(before);
678 auto const n =
679 79 name.size() + // name
680 79 1 + // ':'
681 79 ! value.empty() + // [SP]
682 79 value.size() + // value
683 79 2; // CRLF
684
685 158 op_t op(*this, &name, &value);
686
4/4
✓ Branch 1 taken 77 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 53 times.
✓ Branch 4 taken 24 times.
79 if(op.grow(n, 1))
687 {
688 // reallocated
689
2/2
✓ Branch 0 taken 45 times.
✓ Branch 1 taken 8 times.
53 if(pos > 0)
690 45 std::memcpy(
691 45 h_.buf,
692 45 op.cbuf(),
693 pos);
694
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 35 times.
53 if(before > 0)
695 36 std::memcpy(
696 18 h_.tab_() - before,
697 18 tab0 - before,
698 before * sizeof(entry));
699 106 std::memcpy(
700 53 h_.buf + pos + n,
701 53 op.cbuf() + pos,
702 53 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 77 auto dest = h_.buf + pos;
715
1/2
✓ Branch 2 taken 77 times.
✗ Branch 3 not taken.
77 name.copy(dest, name.size());
716 77 dest += name.size();
717 77 *dest++ = ':';
718
2/2
✓ Branch 1 taken 74 times.
✓ Branch 2 taken 3 times.
77 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 77 *dest++ = '\r';
726 77 *dest = '\n';
727 }
728
729 // update table
730 77 auto const tab = h_.tab_();
731 {
732 77 auto i = h_.count - before;
733
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 59 times.
77 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 77 auto& e = tab[0 - before - 1];
745 77 e.np = static_cast<off_t>(
746 77 pos - h_.prefix);
747 77 e.nn = static_cast<
748 77 off_t>(name.size());
749 77 e.vp = static_cast<off_t>(
750 154 pos - h_.prefix +
751 77 name.size() + 1 +
752 77 ! value.empty());
753 77 e.vn = static_cast<
754 77 off_t>(value.size());
755 77 e.id = id;
756
757 // update container
758 77 h_.count++;
759 77 h_.size = static_cast<
760 77 off_t>(h_.size + n);
761
2/2
✓ Branch 0 taken 68 times.
✓ Branch 1 taken 9 times.
77 if( id != field::unknown)
762
1/2
✓ Branch 1 taken 68 times.
✗ Branch 2 not taken.
68 h_.on_insert(id, value);
763 77 }
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 off_t>(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 437 fields_base::
835 offset(
836 std::size_t i) const noexcept
837 {
838
2/2
✓ Branch 0 taken 140 times.
✓ Branch 1 taken 297 times.
437 if(i == 0)
839 140 return h_.prefix;
840
2/2
✓ Branch 0 taken 174 times.
✓ Branch 1 taken 123 times.
297 if(i < h_.count)
841 348 return h_.prefix +
842 174 h_.tab_()[0-(i + 1)].np;
843 // make final CRLF the last "field"
844 //BOOST_ASSERT(i == h_.count);
845 123 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
887 #endif
888