GCC Code Coverage Report


Directory: libs/http_proto/
File: src/fields_base.cpp
Date: 2025-09-21 18:08:15
Exec Total Coverage
Lines: 517 541 95.6%
Functions: 41 41 100.0%
Branches: 194 252 77.0%

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