GCC Code Coverage Report


Directory: libs/http_proto/
File: libs/http_proto/src/fields_base.cpp
Date: 2024-03-11 21:11:07
Exec Total Coverage
Lines: 454 476 95.4%
Functions: 33 34 97.1%
Branches: 174 230 75.7%

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