GCC Code Coverage Report


Directory: libs/http_proto/
File: src/fields_base.cpp
Date: 2025-06-18 09:40:27
Exec Total Coverage
Lines: 574 611 93.9%
Functions: 41 42 97.6%
Branches: 219 282 77.7%

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