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 109 : verify_field_name(
42 : core::string_view name)
43 : {
44 : auto rv =
45 109 : grammar::parse(name, detail::field_name_rule);
46 109 : 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 105 : return rv;
55 : }
56 :
57 : static
58 : system::result<typename detail::field_value_rule_t::value_type>
59 145 : verify_field_value(
60 : core::string_view value)
61 : {
62 145 : auto it = value.begin();
63 145 : auto end = value.end();
64 : auto rv =
65 145 : grammar::parse(it, end, detail::field_value_rule);
66 145 : 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 140 : if( rv->has_crlf )
74 7 : return error::bad_field_smuggle;
75 :
76 133 : if( it != end )
77 7 : return error::bad_field_value;
78 :
79 126 : 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 700 : op_t(
95 : fields_base& self,
96 : core::string_view* s0 = nullptr,
97 : core::string_view* s1 = nullptr) noexcept
98 700 : : self_(self)
99 : , s0_(s0)
100 700 : , s1_(s1)
101 : {
102 700 : }
103 :
104 700 : ~op_t()
105 700 : {
106 700 : if(buf_)
107 91 : delete[] buf_;
108 700 : }
109 :
110 : char const*
111 12 : buf() const noexcept
112 : {
113 12 : return buf_;
114 : }
115 :
116 : char const*
117 158 : cbuf() const noexcept
118 : {
119 158 : 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 1324 : fields_base::
171 : op_t::
172 : growth(
173 : std::size_t n0,
174 : std::size_t m) noexcept
175 : {
176 1324 : auto const E = alignof(entry);
177 1324 : auto const m1 =
178 1324 : E * ((m + E - 1) / E);
179 1324 : BOOST_ASSERT(m1 >= m);
180 1324 : if(n0 == 0)
181 : {
182 : // exact
183 1060 : return m1;
184 : }
185 264 : if(m1 > n0)
186 175 : return m1;
187 89 : return n0;
188 : }
189 :
190 : bool
191 687 : fields_base::
192 : op_t::
193 : reserve(
194 : std::size_t bytes)
195 : {
196 687 : if(bytes > max_capacity_in_bytes())
197 : {
198 : // max capacity exceeded
199 1 : detail::throw_length_error();
200 : }
201 686 : auto n = growth(
202 686 : self_.h_.cap, bytes);
203 686 : if(n <= self_.h_.cap)
204 62 : return false;
205 624 : auto buf = new char[n];
206 624 : buf_ = self_.h_.buf;
207 624 : cbuf_ = self_.h_.cbuf;
208 624 : cap_ = self_.h_.cap;
209 624 : self_.h_.buf = buf;
210 624 : self_.h_.cbuf = buf;
211 624 : self_.h_.cap = n;
212 624 : return true;
213 : }
214 :
215 : bool
216 640 : 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 640 : BOOST_ASSERT(
226 : extra_field <= max_offset &&
227 : extra_field <= static_cast<
228 : std::size_t>(
229 : max_offset - self_.h_.count));
230 640 : if( extra_char > max_offset ||
231 638 : extra_char > static_cast<std::size_t>(
232 638 : max_offset - self_.h_.size))
233 2 : detail::throw_length_error();
234 1276 : auto n1 = growth(
235 638 : self_.h_.cap,
236 : detail::header::bytes_needed(
237 638 : self_.h_.size + extra_char,
238 638 : self_.h_.count + extra_field));
239 638 : 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 39 : fields_base::
265 : op_t::
266 : move_chars(
267 : char* dest,
268 : char const* src,
269 : std::size_t n) const noexcept
270 : {
271 39 : detail::move_chars(
272 39 : dest, src, n, s0_, s1_);
273 39 : }
274 :
275 : //------------------------------------------------
276 :
277 69 : fields_base::
278 : fields_base(
279 0 : detail::kind k) noexcept
280 0 : : fields_view_base(&h_)
281 69 : , h_(k)
282 : {
283 69 : }
284 :
285 : // copy s and parse it
286 513 : fields_base::
287 : fields_base(
288 : detail::kind k,
289 0 : core::string_view s)
290 0 : : fields_view_base(&h_)
291 513 : , h_(detail::empty{k})
292 : {
293 513 : auto n = detail::header::count_crlf(s);
294 513 : 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 274 : if(n < 2)
303 0 : detail::throw_invalid_argument();
304 274 : n -= 2;
305 : }
306 1026 : op_t op(*this);
307 513 : op.grow(s.size(), n);
308 513 : s.copy(h_.buf, s.size());
309 513 : system::error_code ec;
310 : // VFALCO This is using defaults?
311 513 : header_limits lim;
312 513 : h_.parse(s.size(), lim, ec);
313 513 : if(ec.failed())
314 0 : detail::throw_system_error(ec);
315 513 : }
316 :
317 : // construct a complete copy of h
318 18 : fields_base::
319 : fields_base(
320 12 : detail::header const& h)
321 12 : : fields_view_base(&h_)
322 18 : , h_(h.kind)
323 : {
324 18 : if(h.is_default())
325 : {
326 6 : BOOST_ASSERT(h.cap == 0);
327 6 : BOOST_ASSERT(h.buf == nullptr);
328 6 : h_ = h;
329 6 : return;
330 : }
331 :
332 : // allocate and copy the buffer
333 24 : op_t op(*this);
334 12 : op.grow(h.size, h.count);
335 12 : h.assign_to(h_);
336 12 : std::memcpy(
337 12 : h_.buf, h.cbuf, h.size);
338 12 : h.copy_table(h_.buf + h_.cap);
339 : }
340 :
341 : //------------------------------------------------
342 :
343 600 : fields_base::
344 612 : ~fields_base()
345 : {
346 600 : if(h_.buf)
347 537 : delete[] h_.buf;
348 600 : }
349 :
350 : //------------------------------------------------
351 : //
352 : // Capacity
353 : //
354 : //------------------------------------------------
355 :
356 : void
357 8 : fields_base::
358 : clear() noexcept
359 : {
360 8 : if(! h_.buf)
361 4 : return;
362 : using H =
363 : detail::header;
364 : auto const& h =
365 4 : *H::get_default(
366 4 : h_.kind);
367 4 : h.assign_to(h_);
368 4 : std::memcpy(
369 4 : h_.buf,
370 4 : h.cbuf,
371 4 : 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 19 : fields_base::
523 : set(
524 : iterator it,
525 : core::string_view value)
526 : {
527 19 : auto rv = verify_field_value(value);
528 19 : if( rv.has_error() )
529 2 : return rv.error();
530 :
531 17 : value = rv->value;
532 17 : bool has_obs_fold = rv->has_obs_fold;
533 :
534 17 : auto const i = it.i_;
535 17 : auto tab = h_.tab();
536 17 : auto const& e0 = tab[i];
537 17 : auto const pos0 = offset(i);
538 17 : auto const pos1 = offset(i + 1);
539 : std::ptrdiff_t dn =
540 17 : value.size() -
541 17 : it->value.size();
542 17 : if( value.empty() &&
543 17 : ! it->value.empty())
544 0 : --dn; // remove SP
545 17 : else if(
546 17 : it->value.empty() &&
547 0 : ! value.empty())
548 0 : ++dn; // add SP
549 :
550 34 : op_t op(*this, &value);
551 23 : if( dn > 0 &&
552 12 : op.grow(value.size() -
553 23 : 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 22 : auto dest = h_.buf + pos0 +
589 11 : it->name.size() + 1;
590 11 : if(! value.empty())
591 : {
592 11 : *dest++ = ' ';
593 11 : value.copy(
594 : dest,
595 : value.size());
596 11 : if( has_obs_fold )
597 0 : detail::remove_obs_fold(
598 0 : dest, dest + value.size());
599 11 : dest += value.size();
600 : }
601 11 : op.move_chars(
602 11 : h_.buf + pos1 + dn,
603 11 : h_.buf + pos1,
604 11 : h_.size - pos1);
605 11 : *dest++ = '\r';
606 11 : *dest++ = '\n';
607 : }
608 : {
609 : // update tab
610 17 : auto ft = h_.tab();
611 19 : for(std::size_t j = h_.count - 1;
612 19 : j > i; --j)
613 2 : ft[j] = ft[j] + dn;
614 17 : auto& e = ft[i];
615 34 : e.vp = e.np + e.nn +
616 17 : 1 + ! value.empty();
617 17 : e.vn = static_cast<
618 17 : offset_type>(value.size());
619 17 : h_.size = static_cast<
620 17 : offset_type>(h_.size + dn);
621 : }
622 17 : auto const id = it->id;
623 17 : if(h_.is_special(id))
624 : {
625 : // replace first char of name
626 : // with null to hide metadata
627 5 : char saved = h_.buf[pos0];
628 5 : auto& e = h_.tab()[i];
629 5 : e.id = field::unknown;
630 5 : h_.buf[pos0] = '\0';
631 5 : h_.on_erase(id);
632 5 : h_.buf[pos0] = saved; // restore
633 5 : e.id = id;
634 5 : h_.on_insert(id, it->value);
635 : }
636 17 : 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 23 : fields_base::
684 : set(
685 : core::string_view name,
686 : core::string_view value)
687 : {
688 : {
689 23 : auto rv = verify_field_name(name);
690 23 : if( rv.has_error() )
691 2 : return rv.error();
692 : }
693 :
694 21 : auto rv = verify_field_value(value);
695 21 : if( rv.has_error() )
696 2 : return rv.error();
697 :
698 19 : value = rv->value;
699 19 : bool has_obs_fold = rv->has_obs_fold;
700 :
701 19 : auto const i0 = h_.find(name);
702 19 : 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 19 : insert_impl_unchecked(
722 : string_to_field(name),
723 19 : name, value, h_.count, has_obs_fold);
724 18 : return {};
725 : }
726 :
727 : //------------------------------------------------
728 : //
729 : // (implementation)
730 : //
731 : //------------------------------------------------
732 :
733 : // copy start line and fields
734 : void
735 9 : fields_base::
736 : copy_impl(
737 : detail::header const& h)
738 : {
739 9 : BOOST_ASSERT(
740 : h.kind == ph_->kind);
741 9 : if(! h.is_default())
742 : {
743 : auto const n =
744 6 : detail::header::bytes_needed(
745 6 : h.size, h.count);
746 6 : 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 16 : fields_base tmp(h);
760 8 : tmp.h_.swap(h_);
761 : }
762 :
763 : void
764 109 : 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 109 : auto const tab0 = h_.tab_();
773 109 : auto const pos = offset(before);
774 : auto const n =
775 109 : name.size() + // name
776 109 : 1 + // ':'
777 109 : ! value.empty() + // [SP]
778 109 : value.size() + // value
779 109 : 2; // CRLF
780 :
781 218 : op_t op(*this, &name, &value);
782 109 : if(op.grow(n, 1))
783 : {
784 : // reallocated
785 79 : if(pos > 0)
786 65 : std::memcpy(
787 65 : h_.buf,
788 65 : op.cbuf(),
789 : pos);
790 79 : if(before > 0)
791 48 : std::memcpy(
792 24 : h_.tab_() - before,
793 24 : tab0 - before,
794 : before * sizeof(entry));
795 158 : std::memcpy(
796 79 : h_.buf + pos + n,
797 79 : op.cbuf() + pos,
798 79 : 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 107 : auto dest = h_.buf + pos;
811 107 : name.copy(dest, name.size());
812 107 : dest += name.size();
813 107 : *dest++ = ':';
814 107 : if(! value.empty())
815 : {
816 95 : *dest++ = ' ';
817 95 : value.copy(
818 : dest, value.size());
819 95 : if( has_obs_fold )
820 15 : detail::remove_obs_fold(
821 15 : dest, dest + value.size());
822 95 : dest += value.size();
823 : }
824 107 : *dest++ = '\r';
825 107 : *dest = '\n';
826 : }
827 :
828 : // update table
829 107 : auto const tab = h_.tab_();
830 : {
831 107 : auto i = h_.count - before;
832 107 : 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 107 : auto& e = tab[0 - static_cast<std::ptrdiff_t>(before) - 1];
844 107 : e.np = static_cast<offset_type>(
845 107 : pos - h_.prefix);
846 107 : e.nn = static_cast<
847 107 : offset_type>(name.size());
848 107 : e.vp = static_cast<offset_type>(
849 214 : pos - h_.prefix +
850 107 : name.size() + 1 +
851 107 : ! value.empty());
852 107 : e.vn = static_cast<
853 107 : offset_type>(value.size());
854 107 : e.id = id;
855 :
856 : // update container
857 107 : h_.count++;
858 107 : h_.size = static_cast<
859 107 : offset_type>(h_.size + n);
860 107 : if( id != field::unknown)
861 77 : h_.on_insert(id, value);
862 107 : }
863 :
864 : system::result<void>
865 86 : 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 86 : auto rv = verify_field_name(name);
874 86 : if( rv.has_error() )
875 4 : return rv.error();
876 : }
877 :
878 82 : auto rv = verify_field_value(value);
879 82 : if( rv.has_error() )
880 13 : return rv.error();
881 :
882 69 : insert_impl_unchecked(
883 69 : id, name, rv->value, before, rv->has_obs_fold);
884 68 : 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 150 : fields_base::
903 : raw_erase(
904 : std::size_t i) noexcept
905 : {
906 150 : BOOST_ASSERT(i < h_.count);
907 150 : BOOST_ASSERT(h_.buf != nullptr);
908 150 : auto const p0 = offset(i);
909 150 : auto const p1 = offset(i + 1);
910 150 : std::memmove(
911 150 : h_.buf + p0,
912 150 : h_.buf + p1,
913 150 : h_.size - p1);
914 150 : auto const n = p1 - p0;
915 150 : --h_.count;
916 150 : auto ft = h_.tab();
917 228 : for(;i < h_.count; ++i)
918 78 : ft[i] = ft[i + 1] - n;
919 150 : h_.size = static_cast<
920 150 : offset_type>(h_.size - n);
921 150 : }
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 503 : fields_base::
957 : offset(
958 : std::size_t i) const noexcept
959 : {
960 503 : if(i == 0)
961 173 : return h_.prefix;
962 330 : if(i < h_.count)
963 366 : return h_.prefix +
964 183 : 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 147 : 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 0 : fields_base::
987 : raw_erase_n(
988 : field id,
989 : std::size_t n) noexcept
990 : {
991 : // iterate in reverse
992 0 : auto e = &h_.tab()[h_.count];
993 0 : auto const e0 = &h_.tab()[0];
994 0 : while(n > 0)
995 : {
996 0 : BOOST_ASSERT(e != e0);
997 0 : ++e; // decrement
998 0 : if(e->id == id)
999 : {
1000 0 : raw_erase(e0 - e);
1001 0 : --n;
1002 : }
1003 : }
1004 0 : }
1005 :
1006 : } // http_proto
1007 : } // boost
|