GCC Code Coverage Report


Directory: libs/http_proto/
File: libs/http_proto/src/fields_base.cpp
Date: 2024-03-22 19:51:41
Exec Total Coverage
Lines: 468 491 95.3%
Functions: 35 36 97.2%
Branches: 182 236 77.1%

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