GCC Code Coverage Report


Directory: libs/http_proto/
File: libs/http_proto/src/fields_base.cpp
Date: 2024-08-25 18:42:42
Exec Total Coverage
Lines: 474 497 95.4%
Functions: 35 36 97.2%
Branches: 186 240 77.5%

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