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