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 : #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 233 : if( rv.has_error() )
49 : {
50 6 : auto ec = rv.error();
51 6 : if( ec == urls::grammar::error::leftover )
52 3 : return error::bad_field_name;
53 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 273 : if( rv.has_error() )
69 : {
70 5 : if( rv.error() == condition::need_more_input )
71 5 : return error::bad_field_value;
72 0 : return rv.error();
73 : }
74 :
75 268 : if( rv->has_crlf )
76 7 : return error::bad_field_smuggle;
77 :
78 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 901 : if(buf_)
109 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 1631 : BOOST_ASSERT(m1 >= m);
181 1631 : if(n0 == 0)
182 : {
183 : // exact
184 1149 : return m1;
185 : }
186 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 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 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 783 : BOOST_ASSERT(
227 : extra_field <= max_offset &&
228 : extra_field <= static_cast<
229 : std::size_t>(
230 : max_offset - self_.h_.count));
231 783 : if( extra_char > max_offset ||
232 781 : extra_char > static_cast<std::size_t>(
233 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 0 : 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 0 : std::memcpy(
252 0 : self_.h_.buf,
253 0 : cbuf_,
254 : n);
255 : // copy first i entries
256 0 : if(i > 0)
257 0 : std::memcpy(
258 0 : self_.h_.tab_() - i,
259 : reinterpret_cast<entry*>(
260 0 : buf_ + cap_) - i,
261 : i * sizeof(entry));
262 0 : }
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 114 : fields_base::
279 : fields_base(
280 0 : detail::kind k) noexcept
281 114 : : fields_base(k, 0)
282 : {
283 114 : }
284 :
285 126 : fields_base::
286 : fields_base(
287 : detail::kind k,
288 0 : std::size_t size)
289 0 : : fields_view_base(&h_)
290 126 : , h_(k)
291 : {
292 126 : if( size > 0 )
293 : {
294 9 : h_.max_cap = detail::align_up(
295 : size, alignof(detail::header::entry));
296 9 : reserve_bytes(size);
297 : }
298 126 : }
299 :
300 24 : fields_base::
301 : fields_base(
302 : detail::kind k,
303 : std::size_t size,
304 0 : std::size_t max_size)
305 0 : : fields_view_base(&h_)
306 24 : , h_(k)
307 : {
308 24 : if( size > max_size )
309 6 : detail::throw_length_error();
310 :
311 18 : h_.max_cap = detail::align_up(
312 : max_size, alignof(detail::header::entry));
313 18 : if( size > 0 )
314 : {
315 15 : reserve_bytes(size);
316 : }
317 18 : }
318 :
319 : // copy s and parse it
320 529 : fields_base::
321 : fields_base(
322 : detail::kind k,
323 0 : core::string_view s)
324 0 : : fields_view_base(&h_)
325 529 : , h_(detail::empty{k})
326 : {
327 529 : auto n = detail::header::count_crlf(s);
328 529 : if(h_.kind == detail::kind::fields)
329 : {
330 241 : if(n < 1)
331 1 : detail::throw_invalid_argument();
332 240 : n -= 1;
333 : }
334 : else
335 : {
336 288 : if(n < 2)
337 2 : detail::throw_invalid_argument();
338 286 : n -= 2;
339 : }
340 1052 : op_t op(*this);
341 526 : op.grow(s.size(), n);
342 526 : s.copy(h_.buf, s.size());
343 526 : system::error_code ec;
344 : // VFALCO This is using defaults?
345 526 : header_limits lim;
346 526 : h_.parse(s.size(), lim, ec);
347 526 : if(ec.failed())
348 0 : detail::throw_system_error(ec);
349 526 : }
350 :
351 : // construct a complete copy of h
352 26 : fields_base::
353 : fields_base(
354 14 : detail::header const& h)
355 14 : : fields_view_base(&h_)
356 26 : , h_(h.kind)
357 : {
358 26 : if(h.is_default())
359 : {
360 8 : BOOST_ASSERT(h.cap == 0);
361 8 : BOOST_ASSERT(h.buf == nullptr);
362 8 : h_ = h;
363 8 : return;
364 : }
365 :
366 : // allocate and copy the buffer
367 36 : op_t op(*this);
368 18 : op.grow(h.size, h.count);
369 18 : h.assign_to(h_);
370 18 : std::memcpy(
371 18 : h_.buf, h.cbuf, h.size);
372 18 : h.copy_table(h_.buf + h_.cap);
373 : }
374 :
375 : //------------------------------------------------
376 :
377 696 : fields_base::
378 710 : ~fields_base()
379 : {
380 696 : if(h_.buf)
381 608 : delete[] h_.buf;
382 696 : }
383 :
384 : //------------------------------------------------
385 : //
386 : // Capacity
387 : //
388 : //------------------------------------------------
389 :
390 : void
391 10 : fields_base::
392 : clear() noexcept
393 : {
394 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 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 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 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 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 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 57 : while(it != begin_)
461 : {
462 36 : --it;
463 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 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 15 : if(id == field::unknown)
516 : {
517 : // fix self-intersection
518 6 : name = it0->name;
519 :
520 : for(;;)
521 : {
522 24 : --it;
523 24 : if(it == it0)
524 6 : break;
525 18 : if(grammar::ci_is_equal(
526 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 21 : if(it == it0)
540 9 : break;
541 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 23 : auto rv = verify_field_value(value);
562 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 21 : if( value.empty() &&
577 21 : ! it->value.empty())
578 0 : --dn; // remove SP
579 21 : else if(
580 21 : it->value.empty() &&
581 0 : ! value.empty())
582 0 : ++dn; // add SP
583 :
584 42 : op_t op(*this, &value);
585 27 : if( dn > 0 &&
586 12 : op.grow(value.size() -
587 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 6 : if(! value.empty())
597 : {
598 6 : *dest++ = ' ';
599 6 : value.copy(
600 : dest,
601 : value.size());
602 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 15 : if(! value.empty())
625 : {
626 15 : *dest++ = ' ';
627 15 : value.copy(
628 : dest,
629 : value.size());
630 15 : if( has_obs_fold )
631 0 : detail::remove_obs_fold(
632 0 : 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 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 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 9 : h_.on_erase(id);
666 9 : h_.buf[pos0] = saved; // restore
667 9 : e.id = id;
668 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 23 : BOOST_ASSERT(
682 : id != field::unknown);
683 :
684 23 : auto rv = verify_field_value(value);
685 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 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 15 : reserve_bytes(n0 + n);
705 : }
706 15 : erase_all_impl(i0, id);
707 : }
708 :
709 21 : insert_impl_unchecked(
710 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 24 : auto rv = verify_field_name(name);
724 24 : if( rv.has_error() )
725 2 : return rv.error();
726 : }
727 :
728 22 : auto rv = verify_field_value(value);
729 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 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 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 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 17 : BOOST_ASSERT(
774 : h.kind == ph_->kind);
775 17 : if(! h.is_default())
776 : {
777 : auto const n =
778 14 : detail::header::bytes_needed(
779 14 : h.size, h.count);
780 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 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 233 : if(op.grow(n, 1))
817 : {
818 : // reallocated
819 110 : if(pos > 0)
820 92 : std::memcpy(
821 92 : h_.buf,
822 92 : op.cbuf(),
823 : pos);
824 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 228 : name.copy(dest, name.size());
846 228 : dest += name.size();
847 228 : *dest++ = ':';
848 228 : if(! value.empty())
849 : {
850 216 : *dest++ = ' ';
851 216 : value.copy(
852 : dest, value.size());
853 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 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 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 228 : if( id != field::unknown)
895 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 209 : auto rv = verify_field_name(name);
908 209 : if( rv.has_error() )
909 4 : return rv.error();
910 : }
911 :
912 205 : auto rv = verify_field_value(value);
913 205 : if( rv.has_error() )
914 13 : return rv.error();
915 :
916 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 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 155 : BOOST_ASSERT(i < h_.count);
941 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 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 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 58 : while(i > i0)
973 : {
974 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 645 : if(i == 0)
995 259 : return h_.prefix;
996 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 10 : while(n > 0)
1029 : {
1030 6 : BOOST_ASSERT(e != e0);
1031 6 : ++e; // decrement
1032 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
|