Line data Source code
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 122 : if( rv.has_error() )
47 : {
48 6 : auto ec = rv.error();
49 6 : if( ec == urls::grammar::error::leftover )
50 3 : return error::bad_field_name;
51 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 162 : if( rv.has_error() )
67 : {
68 5 : if( rv.error() == condition::need_more_input )
69 5 : return error::bad_field_value;
70 0 : return rv.error();
71 : }
72 :
73 157 : if( rv->has_crlf )
74 7 : return error::bad_field_smuggle;
75 :
76 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 726 : op_t(
95 : fields_base& self,
96 : core::string_view* s0 = nullptr,
97 : core::string_view* s1 = nullptr) noexcept
98 726 : : self_(self)
99 : , s0_(s0)
100 726 : , s1_(s1)
101 : {
102 726 : }
103 :
104 726 : ~op_t()
105 726 : {
106 726 : if(buf_)
107 98 : delete[] buf_;
108 726 : }
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 1368 : fields_base::
171 : op_t::
172 : growth(
173 : std::size_t n0,
174 : std::size_t m) noexcept
175 : {
176 1368 : auto const E = alignof(entry);
177 1368 : auto const m1 =
178 1368 : E * ((m + E - 1) / E);
179 1368 : BOOST_ASSERT(m1 >= m);
180 1368 : if(n0 == 0)
181 : {
182 : // exact
183 1090 : return m1;
184 : }
185 278 : if(m1 > n0)
186 189 : return m1;
187 89 : return n0;
188 : }
189 :
190 : bool
191 709 : fields_base::
192 : op_t::
193 : reserve(
194 : std::size_t bytes)
195 : {
196 709 : if(bytes > max_capacity_in_bytes())
197 : {
198 : // max capacity exceeded
199 1 : detail::throw_length_error();
200 : }
201 708 : auto n = growth(
202 708 : self_.h_.cap, bytes);
203 708 : if(n <= self_.h_.cap)
204 62 : return false;
205 646 : auto buf = new char[n];
206 646 : buf_ = self_.h_.buf;
207 646 : cbuf_ = self_.h_.cbuf;
208 646 : cap_ = self_.h_.cap;
209 646 : self_.h_.buf = buf;
210 646 : self_.h_.cbuf = buf;
211 646 : self_.h_.cap = n;
212 646 : return true;
213 : }
214 :
215 : bool
216 662 : 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 662 : BOOST_ASSERT(
226 : extra_field <= max_offset &&
227 : extra_field <= static_cast<
228 : std::size_t>(
229 : max_offset - self_.h_.count));
230 662 : if( extra_char > max_offset ||
231 660 : extra_char > static_cast<std::size_t>(
232 660 : max_offset - self_.h_.size))
233 2 : detail::throw_length_error();
234 1320 : auto n1 = growth(
235 660 : self_.h_.cap,
236 : detail::header::bytes_needed(
237 660 : self_.h_.size + extra_char,
238 660 : self_.h_.count + extra_field));
239 660 : return reserve(n1);
240 : }
241 :
242 : void
243 0 : 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 0 : std::memcpy(
251 0 : self_.h_.buf,
252 0 : cbuf_,
253 : n);
254 : // copy first i entries
255 0 : if(i > 0)
256 0 : std::memcpy(
257 0 : self_.h_.tab_() - i,
258 : reinterpret_cast<entry*>(
259 0 : buf_ + cap_) - i,
260 : i * sizeof(entry));
261 0 : }
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 107 : fields_base::
278 : fields_base(
279 0 : detail::kind k) noexcept
280 0 : : fields_view_base(&h_)
281 107 : , h_(k)
282 : {
283 107 : }
284 :
285 : // copy s and parse it
286 516 : fields_base::
287 : fields_base(
288 : detail::kind k,
289 0 : core::string_view s)
290 0 : : fields_view_base(&h_)
291 516 : , h_(detail::empty{k})
292 : {
293 516 : auto n = detail::header::count_crlf(s);
294 516 : if(h_.kind == detail::kind::fields)
295 : {
296 239 : if(n < 1)
297 0 : detail::throw_invalid_argument();
298 239 : n -= 1;
299 : }
300 : else
301 : {
302 277 : if(n < 2)
303 0 : detail::throw_invalid_argument();
304 277 : n -= 2;
305 : }
306 1032 : op_t op(*this);
307 516 : op.grow(s.size(), n);
308 516 : s.copy(h_.buf, s.size());
309 516 : system::error_code ec;
310 : // VFALCO This is using defaults?
311 516 : header_limits lim;
312 516 : h_.parse(s.size(), lim, ec);
313 516 : if(ec.failed())
314 0 : detail::throw_system_error(ec);
315 516 : }
316 :
317 : // construct a complete copy of h
318 26 : fields_base::
319 : fields_base(
320 14 : detail::header const& h)
321 14 : : fields_view_base(&h_)
322 26 : , h_(h.kind)
323 : {
324 26 : if(h.is_default())
325 : {
326 8 : BOOST_ASSERT(h.cap == 0);
327 8 : BOOST_ASSERT(h.buf == nullptr);
328 8 : h_ = h;
329 8 : return;
330 : }
331 :
332 : // allocate and copy the buffer
333 36 : op_t op(*this);
334 18 : op.grow(h.size, h.count);
335 18 : h.assign_to(h_);
336 18 : std::memcpy(
337 18 : h_.buf, h.cbuf, h.size);
338 18 : h.copy_table(h_.buf + h_.cap);
339 : }
340 :
341 : //------------------------------------------------
342 :
343 649 : fields_base::
344 663 : ~fields_base()
345 : {
346 649 : if(h_.buf)
347 567 : delete[] h_.buf;
348 649 : }
349 :
350 : //------------------------------------------------
351 : //
352 : // Capacity
353 : //
354 : //------------------------------------------------
355 :
356 : void
357 10 : fields_base::
358 : clear() noexcept
359 : {
360 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 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 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 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 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 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 57 : while(it != begin_)
427 : {
428 36 : --it;
429 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 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 15 : if(id == field::unknown)
482 : {
483 : // fix self-intersection
484 6 : name = it0->name;
485 :
486 : for(;;)
487 : {
488 24 : --it;
489 24 : if(it == it0)
490 6 : break;
491 18 : if(grammar::ci_is_equal(
492 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 21 : if(it == it0)
506 9 : break;
507 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 23 : auto rv = verify_field_value(value);
528 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 21 : if( value.empty() &&
543 21 : ! it->value.empty())
544 0 : --dn; // remove SP
545 21 : else if(
546 21 : it->value.empty() &&
547 0 : ! value.empty())
548 0 : ++dn; // add SP
549 :
550 42 : op_t op(*this, &value);
551 27 : if( dn > 0 &&
552 12 : op.grow(value.size() -
553 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 6 : if(! value.empty())
563 : {
564 6 : *dest++ = ' ';
565 6 : value.copy(
566 : dest,
567 : value.size());
568 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 15 : if(! value.empty())
591 : {
592 15 : *dest++ = ' ';
593 15 : value.copy(
594 : dest,
595 : value.size());
596 15 : if( has_obs_fold )
597 0 : detail::remove_obs_fold(
598 0 : 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 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 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 9 : h_.on_erase(id);
632 9 : h_.buf[pos0] = saved; // restore
633 9 : e.id = id;
634 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 23 : BOOST_ASSERT(
648 : id != field::unknown);
649 :
650 23 : auto rv = verify_field_value(value);
651 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 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 15 : reserve_bytes(n0 + n);
671 : }
672 15 : erase_all_impl(i0, id);
673 : }
674 :
675 21 : insert_impl_unchecked(
676 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 24 : auto rv = verify_field_name(name);
690 24 : if( rv.has_error() )
691 2 : return rv.error();
692 : }
693 :
694 22 : auto rv = verify_field_value(value);
695 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 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 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 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 11 : BOOST_ASSERT(
740 : h.kind == ph_->kind);
741 11 : if(! h.is_default())
742 : {
743 : auto const n =
744 8 : detail::header::bytes_needed(
745 8 : h.size, h.count);
746 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 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 122 : if(op.grow(n, 1))
783 : {
784 : // reallocated
785 92 : if(pos > 0)
786 78 : std::memcpy(
787 78 : h_.buf,
788 78 : op.cbuf(),
789 : pos);
790 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 120 : name.copy(dest, name.size());
812 120 : dest += name.size();
813 120 : *dest++ = ':';
814 120 : if(! value.empty())
815 : {
816 108 : *dest++ = ' ';
817 108 : value.copy(
818 : dest, value.size());
819 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 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 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 120 : if( id != field::unknown)
861 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 98 : auto rv = verify_field_name(name);
874 98 : if( rv.has_error() )
875 4 : return rv.error();
876 : }
877 :
878 94 : auto rv = verify_field_value(value);
879 94 : if( rv.has_error() )
880 13 : return rv.error();
881 :
882 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 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 155 : BOOST_ASSERT(i < h_.count);
907 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 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 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 58 : while(i > i0)
939 : {
940 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 534 : if(i == 0)
961 184 : return h_.prefix;
962 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 10 : while(n > 0)
995 : {
996 6 : BOOST_ASSERT(e != e0);
997 6 : ++e; // decrement
998 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
|