GCC Code Coverage Report


Directory: libs/http_proto/
File: libs/http_proto/src/fields_base.cpp
Date: 2024-03-18 23:12:48
Exec Total Coverage
Lines: 464 486 95.5%
Functions: 35 36 97.2%
Branches: 180 234 76.9%

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