Line data Source code
1 : //
2 : // Copyright (c) 2021 Vinnie Falco (vinnie.falco@gmail.com)
3 : // Copyright (c) 2025 Mohammad Nejati
4 : //
5 : // Distributed under the Boost Software License, Version 1.0. (See accompanying
6 : // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7 : //
8 : // Official repository: https://github.com/cppalliance/http_proto
9 : //
10 :
11 : #include <boost/http_proto/detail/config.hpp>
12 : #include <boost/http_proto/detail/except.hpp>
13 : #include <boost/http_proto/detail/header.hpp>
14 : #include <boost/http_proto/error.hpp>
15 : #include <boost/http_proto/field.hpp>
16 : #include <boost/http_proto/fields_base.hpp>
17 : #include <boost/http_proto/header_limits.hpp>
18 : #include <boost/http_proto/rfc/token_rule.hpp>
19 :
20 : #include "src/detail/move_chars.hpp"
21 : #include "src/rfc/detail/rules.hpp"
22 :
23 : #include <boost/assert.hpp>
24 : #include <boost/assert/source_location.hpp>
25 : #include <boost/core/detail/string_view.hpp>
26 : #include <boost/system/result.hpp>
27 : #include <boost/url/grammar/ci_string.hpp>
28 : #include <boost/url/grammar/error.hpp>
29 : #include <boost/url/grammar/parse.hpp>
30 : #include <boost/url/grammar/token_rule.hpp>
31 :
32 : namespace boost {
33 : namespace http_proto {
34 :
35 : namespace {
36 :
37 : std::size_t
38 96 : align_down(
39 : void * ptr,
40 : std::size_t size,
41 : std::size_t alignment)
42 : {
43 96 : auto addr = reinterpret_cast<std::uintptr_t>(ptr);
44 96 : auto aligned_end = (addr + size) & ~(alignment - 1);
45 :
46 96 : if(aligned_end > addr)
47 96 : return aligned_end - addr;
48 :
49 0 : return 0;
50 : }
51 :
52 : void
53 250 : verify_field_name(
54 : core::string_view name,
55 : system::error_code& ec)
56 : {
57 250 : auto rv = grammar::parse(
58 : name, detail::field_name_rule);
59 250 : if(rv.has_error())
60 : {
61 18 : ec = BOOST_HTTP_PROTO_ERR(
62 : error::bad_field_name);
63 : }
64 250 : }
65 :
66 : system::result<detail::field_value_rule_t::value_type>
67 382 : verify_field_value(
68 : core::string_view value)
69 : {
70 382 : auto it = value.begin();
71 382 : auto end = value.end();
72 : auto rv =
73 382 : grammar::parse(it, end, detail::field_value_rule);
74 382 : if( rv.has_error() )
75 : {
76 7 : if( rv.error() == condition::need_more_input )
77 7 : return error::bad_field_value;
78 0 : return rv.error();
79 : }
80 :
81 375 : if( rv->has_crlf )
82 16 : return error::bad_field_smuggle;
83 :
84 359 : if( it != end )
85 7 : return error::bad_field_value;
86 :
87 352 : return rv;
88 : }
89 :
90 : } // namespace
91 :
92 : class fields_base::
93 : op_t
94 : {
95 : fields_base& self_;
96 : core::string_view* s0_;
97 : core::string_view* s1_;
98 : char* buf_ = nullptr;
99 : char const* cbuf_ = nullptr;
100 : std::size_t cap_ = 0;
101 :
102 : public:
103 : explicit
104 1035 : op_t(
105 : fields_base& self,
106 : core::string_view* s0 = nullptr,
107 : core::string_view* s1 = nullptr) noexcept
108 1035 : : self_(self)
109 1035 : , s0_(s0)
110 1035 : , s1_(s1)
111 : {
112 1035 : }
113 :
114 1035 : ~op_t()
115 : {
116 1035 : if(buf_)
117 162 : delete[] buf_;
118 1035 : }
119 :
120 : char const*
121 12 : buf() const noexcept
122 : {
123 12 : return buf_;
124 : }
125 :
126 : char const*
127 449 : cbuf() const noexcept
128 : {
129 449 : return cbuf_;
130 : }
131 :
132 : char*
133 12 : end() const noexcept
134 : {
135 12 : return buf_ + cap_;
136 : }
137 :
138 : table
139 6 : tab() const noexcept
140 : {
141 6 : return table(end());
142 : }
143 :
144 : bool
145 : reserve(std::size_t n);
146 :
147 : bool
148 : grow(
149 : std::size_t extra_char,
150 : std::size_t extra_field);
151 :
152 : void
153 : move_chars(
154 : char* dest,
155 : char const* src,
156 : std::size_t n) const noexcept;
157 : };
158 :
159 : bool
160 1011 : fields_base::
161 : op_t::
162 : reserve(
163 : std::size_t n)
164 : {
165 : // TODO: consider using a growth factor
166 1011 : if(n > self_.max_cap_)
167 : {
168 : // max capacity exceeded
169 19 : detail::throw_length_error();
170 : }
171 992 : if(n <= self_.h_.cap)
172 150 : return false;
173 842 : auto buf = new char[n];
174 842 : buf_ = self_.h_.buf;
175 842 : cbuf_ = self_.h_.cbuf;
176 842 : cap_ = self_.h_.cap;
177 842 : self_.h_.buf = buf;
178 842 : self_.h_.cbuf = buf;
179 842 : self_.h_.cap = n;
180 842 : return true;
181 : }
182 :
183 : bool
184 914 : fields_base::
185 : op_t::
186 : grow(
187 : std::size_t extra_char,
188 : std::size_t extra_field)
189 : {
190 914 : if(extra_field > detail::header::max_offset - self_.h_.count)
191 0 : detail::throw_length_error();
192 :
193 914 : if(extra_char > detail::header::max_offset - self_.h_.size)
194 2 : detail::throw_length_error();
195 :
196 912 : return reserve(
197 : detail::header::bytes_needed(
198 912 : self_.h_.size + extra_char,
199 1818 : self_.h_.count + extra_field));
200 : }
201 :
202 : void
203 121 : fields_base::
204 : op_t::
205 : move_chars(
206 : char* dest,
207 : char const* src,
208 : std::size_t n) const noexcept
209 : {
210 121 : detail::move_chars(
211 121 : dest, src, n, s0_, s1_);
212 121 : }
213 :
214 : //------------------------------------------------
215 :
216 73 : fields_base::
217 : prefix_op_t::
218 : prefix_op_t(
219 : fields_base& self,
220 : std::size_t new_prefix,
221 : core::string_view* s0,
222 73 : core::string_view* s1)
223 73 : : self_(self)
224 73 : , new_prefix_(static_cast<
225 73 : offset_type>(new_prefix))
226 : {
227 73 : if(self.h_.size - self.h_.prefix + new_prefix
228 : > detail::header::max_offset)
229 2 : detail::throw_length_error();
230 :
231 : // memmove happens in the destructor
232 : // to avoid overlaping with start line.
233 142 : if(new_prefix_ < self_.h_.prefix
234 71 : && !self.h_.is_default())
235 10 : return;
236 :
237 61 : auto new_size = static_cast<offset_type>(
238 61 : self.h_.size - self.h_.prefix + new_prefix_);
239 :
240 : auto bytes_needed =
241 61 : detail::header::bytes_needed(
242 : new_size,
243 61 : self.h_.count);
244 :
245 61 : if(bytes_needed > self.h_.cap)
246 : {
247 : // static storage will always throw which is
248 : // intended since they cannot reallocate.
249 28 : if(self.max_cap_ < bytes_needed)
250 1 : detail::throw_length_error();
251 : // TODO: consider using a growth factor
252 27 : char* p = new char[bytes_needed];
253 27 : std::memcpy(
254 27 : p + new_prefix_,
255 27 : self.h_.cbuf + self.h_.prefix,
256 27 : self.h_.size - self.h_.prefix);
257 27 : self.h_.copy_table(p + bytes_needed);
258 :
259 : // old buffer gets released in the destructor
260 : // to avoid invalidating any string_views
261 : // that may still reference it.
262 27 : buf_ = self.h_.buf;
263 27 : self.h_.buf = p;
264 27 : self.h_.cap = bytes_needed;
265 : }
266 : else
267 : {
268 : // memmove to the right and update any
269 : // string_views that reference that region.
270 33 : detail::move_chars(
271 33 : self.h_.buf + new_prefix_,
272 33 : self.h_.cbuf + self.h_.prefix,
273 33 : self.h_.size - self.h_.prefix,
274 : s0,
275 : s1);
276 : }
277 :
278 60 : self.h_.cbuf = self.h_.buf;
279 60 : self.h_.size = new_size;
280 60 : self.h_.prefix = new_prefix_;
281 : }
282 :
283 70 : fields_base::
284 : prefix_op_t::
285 : ~prefix_op_t()
286 : {
287 70 : if(new_prefix_ < self_.h_.prefix)
288 : {
289 10 : std::memmove(
290 10 : self_.h_.buf + new_prefix_,
291 10 : self_.h_.cbuf + self_.h_.prefix,
292 10 : self_.h_.size - self_.h_.prefix);
293 :
294 10 : self_.h_.size =
295 10 : self_.h_.size - self_.h_.prefix + new_prefix_;
296 10 : self_.h_.prefix = new_prefix_;
297 : }
298 60 : else if(buf_)
299 : {
300 5 : delete[] buf_;
301 : }
302 70 : }
303 :
304 : //------------------------------------------------
305 :
306 193 : fields_base::
307 : fields_base(
308 0 : detail::kind k) noexcept
309 0 : : fields_view_base(&h_)
310 193 : , h_(k)
311 : {
312 193 : }
313 :
314 49 : fields_base::
315 : fields_base(
316 : detail::kind k,
317 : char* storage,
318 0 : std::size_t cap) noexcept
319 : : fields_base(
320 49 : *detail::header::get_default(k), storage, cap)
321 : {
322 49 : }
323 :
324 : // copy s and parse it
325 569 : fields_base::
326 : fields_base(
327 : detail::kind k,
328 0 : core::string_view s)
329 0 : : fields_view_base(&h_)
330 569 : , h_(detail::empty{k})
331 : {
332 569 : auto n = detail::header::count_crlf(s);
333 569 : if(h_.kind == detail::kind::fields)
334 : {
335 265 : if(n < 1)
336 1 : detail::throw_invalid_argument();
337 264 : n -= 1;
338 : }
339 : else
340 : {
341 304 : if(n < 2)
342 2 : detail::throw_invalid_argument();
343 302 : n -= 2;
344 : }
345 566 : op_t op(*this);
346 566 : op.grow(s.size(), n);
347 566 : s.copy(h_.buf, s.size());
348 566 : system::error_code ec;
349 : // VFALCO This is using defaults?
350 566 : header_limits lim;
351 566 : h_.parse(s.size(), lim, ec);
352 566 : if(ec.failed())
353 0 : detail::throw_system_error(ec);
354 566 : }
355 :
356 : // copy s and parse it
357 37 : fields_base::
358 : fields_base(
359 : detail::kind k,
360 : char* storage,
361 : std::size_t cap,
362 0 : core::string_view s)
363 0 : : fields_view_base(&h_)
364 37 : , h_(detail::empty{k})
365 37 : , external_storage_(true)
366 : {
367 37 : h_.cbuf = storage;
368 37 : h_.buf = storage;
369 37 : h_.cap = align_down(
370 : storage,
371 : cap,
372 : alignof(detail::header::entry));
373 37 : max_cap_ = h_.cap;
374 :
375 37 : auto n = detail::header::count_crlf(s);
376 37 : if(h_.kind == detail::kind::fields)
377 : {
378 12 : if(n < 1)
379 0 : detail::throw_invalid_argument();
380 12 : n -= 1;
381 : }
382 : else
383 : {
384 25 : if(n < 2)
385 0 : detail::throw_invalid_argument();
386 25 : n -= 2;
387 : }
388 :
389 37 : if(detail::header::bytes_needed(
390 : s.size(), n)
391 37 : >= h_.cap)
392 0 : detail::throw_length_error();
393 :
394 37 : s.copy(h_.buf, s.size());
395 37 : system::error_code ec;
396 : // VFALCO This is using defaults?
397 37 : header_limits lim;
398 37 : h_.parse(s.size(), lim, ec);
399 37 : if(ec.failed())
400 0 : detail::throw_system_error(ec);
401 37 : }
402 :
403 : // construct a complete copy of h
404 26 : fields_base::
405 : fields_base(
406 14 : detail::header const& h)
407 14 : : fields_view_base(&h_)
408 26 : , h_(h.kind)
409 : {
410 26 : if(h.is_default())
411 8 : return;
412 :
413 : // allocate and copy the buffer
414 18 : op_t op(*this);
415 18 : op.grow(h.size, h.count);
416 18 : h.assign_to(h_);
417 18 : std::memcpy(
418 18 : h_.buf, h.cbuf, h.size);
419 18 : h.copy_table(h_.buf + h_.cap);
420 18 : }
421 :
422 : // construct a complete copy of h
423 59 : fields_base::
424 : fields_base(
425 : detail::header const& h,
426 : char* storage,
427 0 : std::size_t cap)
428 0 : : fields_view_base(&h_)
429 59 : , h_(h.kind)
430 59 : , external_storage_(true)
431 : {
432 59 : h_.cbuf = storage;
433 59 : h_.buf = storage;
434 59 : h_.cap = align_down(
435 : storage,
436 : cap,
437 : alignof(detail::header::entry));
438 59 : max_cap_ = h_.cap;
439 :
440 118 : if(detail::header::bytes_needed(
441 59 : h.size, h.count)
442 59 : >= h_.cap)
443 0 : detail::throw_length_error();
444 :
445 59 : h.assign_to(h_);
446 59 : std::memcpy(
447 59 : h_.buf, h.cbuf, h.size);
448 59 : h.copy_table(h_.buf + h_.cap);
449 59 : }
450 :
451 : //------------------------------------------------
452 :
453 881 : fields_base::
454 881 : ~fields_base()
455 : {
456 881 : if(h_.buf && !external_storage_)
457 702 : delete[] h_.buf;
458 881 : }
459 :
460 : //------------------------------------------------
461 : //
462 : // Capacity
463 : //
464 : //------------------------------------------------
465 :
466 : void
467 14 : fields_base::
468 : clear() noexcept
469 : {
470 14 : if(! h_.buf)
471 5 : return;
472 : using H =
473 : detail::header;
474 : auto const& h =
475 9 : *H::get_default(
476 9 : h_.kind);
477 9 : h.assign_to(h_);
478 9 : std::memcpy(
479 9 : h_.buf,
480 9 : h.cbuf,
481 9 : h_.size);
482 : }
483 :
484 : void
485 99 : fields_base::
486 : reserve_bytes(
487 : std::size_t n)
488 : {
489 99 : op_t op(*this);
490 99 : if(! op.reserve(n))
491 51 : return;
492 70 : std::memcpy(
493 35 : h_.buf, op.cbuf(), h_.size);
494 35 : auto const nt =
495 35 : sizeof(entry) * h_.count;
496 35 : if(nt > 0)
497 6 : std::memcpy(
498 6 : h_.buf + h_.cap - nt,
499 6 : op.end() - nt,
500 : nt);
501 99 : }
502 :
503 : void
504 8 : fields_base::
505 : shrink_to_fit()
506 : {
507 16 : if(detail::header::bytes_needed(
508 8 : h_.size, h_.count) >=
509 8 : h_.cap)
510 4 : return;
511 :
512 5 : if(external_storage_)
513 1 : return;
514 :
515 4 : fields_base tmp(h_);
516 4 : tmp.h_.swap(h_);
517 4 : }
518 :
519 :
520 : void
521 30 : fields_base::
522 : set_max_capacity_in_bytes(std::size_t n)
523 : {
524 30 : if(n < h_.cap)
525 6 : detail::throw_invalid_argument();
526 24 : max_cap_ = n;
527 24 : }
528 :
529 : //------------------------------------------------
530 : //
531 : // Modifiers
532 : //
533 : //------------------------------------------------
534 :
535 : auto
536 31 : fields_base::
537 : erase(
538 : iterator it) noexcept -> iterator
539 : {
540 31 : auto const id = it->id.value_or(
541 : detail::header::unknown_field);
542 31 : raw_erase(it.i_);
543 31 : h_.on_erase(id);
544 31 : return it;
545 : }
546 :
547 : std::size_t
548 30 : fields_base::
549 : erase(
550 : field id) noexcept
551 : {
552 30 : auto const i0 = h_.find(id);
553 30 : if(i0 == h_.count)
554 3 : return 0;
555 27 : return erase_all(i0, id);
556 : }
557 :
558 : std::size_t
559 18 : fields_base::
560 : erase(
561 : core::string_view name) noexcept
562 : {
563 18 : auto const i0 = h_.find(name);
564 18 : if(i0 == h_.count)
565 3 : return 0;
566 15 : auto const ft = h_.tab();
567 15 : auto const id = ft[i0].id;
568 15 : if(id == detail::header::unknown_field)
569 6 : return erase_all(i0, name);
570 9 : return erase_all(i0, id);
571 : }
572 :
573 : //------------------------------------------------
574 :
575 : void
576 32 : fields_base::
577 : set(
578 : iterator it,
579 : core::string_view value,
580 : system::error_code& ec)
581 : {
582 32 : auto rv = verify_field_value(value);
583 32 : if(rv.has_error())
584 : {
585 4 : ec = rv.error();
586 4 : return;
587 : }
588 :
589 28 : value = rv->value;
590 28 : bool has_obs_fold = rv->has_obs_fold;
591 :
592 28 : auto const i = it.i_;
593 28 : auto tab = h_.tab();
594 28 : auto const& e0 = tab[i];
595 28 : auto const pos0 = offset(i);
596 28 : auto const pos1 = offset(i + 1);
597 : std::ptrdiff_t dn =
598 28 : value.size() -
599 28 : it->value.size();
600 28 : if( value.empty() &&
601 28 : ! it->value.empty())
602 0 : --dn; // remove SP
603 28 : else if(
604 28 : it->value.empty() &&
605 0 : ! value.empty())
606 0 : ++dn; // add SP
607 :
608 28 : op_t op(*this, &value);
609 34 : if( dn > 0 &&
610 12 : op.grow(value.size() -
611 34 : it->value.size(), 0))
612 : {
613 : // reallocated
614 6 : auto dest = h_.buf +
615 6 : pos0 + e0.nn + 1;
616 12 : std::memcpy(
617 6 : h_.buf,
618 6 : op.buf(),
619 6 : dest - h_.buf);
620 6 : if(! value.empty())
621 : {
622 6 : *dest++ = ' ';
623 6 : value.copy(
624 : dest,
625 : value.size());
626 6 : if( has_obs_fold )
627 3 : detail::remove_obs_fold(
628 3 : dest, dest + value.size());
629 6 : dest += value.size();
630 : }
631 6 : *dest++ = '\r';
632 6 : *dest++ = '\n';
633 12 : std::memcpy(
634 6 : h_.buf + pos1 + dn,
635 12 : op.buf() + pos1,
636 6 : h_.size - pos1);
637 12 : std::memcpy(
638 6 : h_.buf + h_.cap -
639 6 : sizeof(entry) * h_.count,
640 6 : &op.tab()[h_.count - 1],
641 6 : sizeof(entry) * h_.count);
642 : }
643 : else
644 : {
645 : // copy the value first
646 44 : auto dest = h_.buf + pos0 +
647 22 : it->name.size() + 1;
648 22 : if(! value.empty())
649 : {
650 22 : *dest++ = ' ';
651 22 : value.copy(
652 : dest,
653 : value.size());
654 22 : if( has_obs_fold )
655 0 : detail::remove_obs_fold(
656 0 : dest, dest + value.size());
657 22 : dest += value.size();
658 : }
659 22 : op.move_chars(
660 22 : h_.buf + pos1 + dn,
661 22 : h_.buf + pos1,
662 22 : h_.size - pos1);
663 22 : *dest++ = '\r';
664 22 : *dest++ = '\n';
665 : }
666 : {
667 : // update tab
668 28 : auto ft = h_.tab();
669 40 : for(std::size_t j = h_.count - 1;
670 40 : j > i; --j)
671 12 : ft[j] = ft[j] + dn;
672 28 : auto& e = ft[i];
673 56 : e.vp = e.np + e.nn +
674 28 : 1 + ! value.empty();
675 28 : e.vn = static_cast<
676 28 : offset_type>(value.size());
677 28 : h_.size = static_cast<
678 28 : offset_type>(h_.size + dn);
679 : }
680 28 : auto const id = it->id.value_or(
681 : detail::header::unknown_field);
682 28 : if(h_.is_special(id))
683 : {
684 : // replace first char of name
685 : // with null to hide metadata
686 13 : char saved = h_.buf[pos0];
687 13 : auto& e = h_.tab()[i];
688 13 : e.id = detail::header::unknown_field;
689 13 : h_.buf[pos0] = '\0';
690 13 : h_.on_erase(id);
691 13 : h_.buf[pos0] = saved; // restore
692 13 : e.id = id;
693 13 : h_.on_insert(id, it->value);
694 : }
695 28 : }
696 :
697 : // erase existing fields with id
698 : // and then add the field with value
699 : void
700 109 : fields_base::
701 : set(
702 : field id,
703 : core::string_view value,
704 : system::error_code& ec)
705 : {
706 109 : auto rv = verify_field_value(value);
707 109 : if(rv.has_error())
708 : {
709 4 : ec = rv.error();
710 4 : return;
711 : }
712 :
713 105 : auto const i0 = h_.find(id);
714 105 : if(i0 != h_.count)
715 : {
716 : // field exists
717 21 : auto const ft = h_.tab();
718 : {
719 : // provide strong guarantee
720 : auto const n0 =
721 21 : h_.size - length(i0);
722 : auto const n =
723 21 : ft[i0].nn + 2 +
724 21 : rv->value.size() + 2;
725 : // VFALCO missing overflow check
726 21 : reserve_bytes(n0 + n);
727 : }
728 21 : erase_all(i0, id);
729 : }
730 :
731 105 : insert_unchecked(
732 : id,
733 : to_string(id),
734 105 : rv->value,
735 105 : h_.count,
736 105 : rv->has_obs_fold);
737 : }
738 :
739 : // erase existing fields with name
740 : // and then add the field with value
741 : void
742 33 : fields_base::
743 : set(
744 : core::string_view name,
745 : core::string_view value,
746 : system::error_code& ec)
747 : {
748 33 : verify_field_name(name , ec);
749 33 : if(ec.failed())
750 8 : return;
751 :
752 29 : auto rv = verify_field_value(value);
753 29 : if(rv.has_error())
754 : {
755 4 : ec = rv.error();
756 4 : return;
757 : }
758 :
759 25 : auto const i0 = h_.find(name);
760 25 : if(i0 != h_.count)
761 : {
762 : // field exists
763 18 : auto const ft = h_.tab();
764 18 : auto const id = ft[i0].id;
765 : {
766 : // provide strong guarantee
767 : auto const n0 =
768 18 : h_.size - length(i0);
769 : auto const n =
770 18 : ft[i0].nn + 2 +
771 18 : rv->value.size() + 2;
772 : // VFALCO missing overflow check
773 18 : reserve_bytes(n0 + n);
774 : }
775 : // VFALCO simple algorithm but
776 : // costs one extra memmove
777 18 : if(id != detail::header::unknown_field)
778 15 : erase_all(i0, id);
779 : else
780 3 : erase_all(i0, name);
781 : }
782 25 : insert_unchecked(
783 : string_to_field(name),
784 : name,
785 25 : rv->value,
786 25 : h_.count,
787 25 : rv->has_obs_fold);
788 : }
789 :
790 : //------------------------------------------------
791 : //
792 : // (implementation)
793 : //
794 : //------------------------------------------------
795 :
796 : // copy start line and fields
797 : void
798 31 : fields_base::
799 : copy_impl(
800 : detail::header const& h)
801 : {
802 31 : BOOST_ASSERT(
803 : h.kind == ph_->kind);
804 :
805 : auto const n =
806 31 : detail::header::bytes_needed(
807 31 : h.size, h.count);
808 31 : if(n <= h_.cap && (!h.is_default() || external_storage_))
809 : {
810 : // no realloc
811 21 : h.assign_to(h_);
812 21 : h.copy_table(
813 21 : h_.buf + h_.cap);
814 21 : std::memcpy(
815 21 : h_.buf,
816 21 : h.cbuf,
817 21 : h.size);
818 21 : return;
819 : }
820 :
821 : // static storages cannot reallocate
822 10 : if(external_storage_)
823 0 : detail::throw_length_error();
824 :
825 10 : fields_base tmp(h);
826 10 : tmp.h_.swap(h_);
827 10 : }
828 :
829 : void
830 217 : fields_base::
831 : insert_impl(
832 : optional<field> id,
833 : core::string_view name,
834 : core::string_view value,
835 : std::size_t before,
836 : system::error_code& ec)
837 : {
838 217 : verify_field_name(name, ec);
839 217 : if(ec.failed())
840 23 : return;
841 :
842 212 : auto rv = verify_field_value(value);
843 212 : if(rv.has_error())
844 : {
845 18 : ec = rv.error();
846 18 : return;
847 : }
848 :
849 194 : insert_unchecked(
850 : id,
851 : name,
852 194 : rv->value,
853 : before,
854 194 : rv->has_obs_fold);
855 : }
856 :
857 : void
858 324 : fields_base::
859 : insert_unchecked(
860 : optional<field> id,
861 : core::string_view name,
862 : core::string_view value,
863 : std::size_t before,
864 : bool has_obs_fold)
865 : {
866 324 : auto const tab0 = h_.tab_();
867 324 : auto const pos = offset(before);
868 : auto const n =
869 324 : name.size() + // name
870 324 : 1 + // ':'
871 324 : ! value.empty() + // [SP]
872 324 : value.size() + // value
873 324 : 2; // CRLF
874 :
875 324 : op_t op(*this, &name, &value);
876 324 : if(op.grow(n, 1))
877 : {
878 : // reallocated
879 217 : if(pos > 0)
880 197 : std::memcpy(
881 197 : h_.buf,
882 197 : op.cbuf(),
883 : pos);
884 217 : if(before > 0)
885 178 : std::memcpy(
886 89 : h_.tab_() - before,
887 89 : tab0 - before,
888 : before * sizeof(entry));
889 434 : std::memcpy(
890 217 : h_.buf + pos + n,
891 217 : op.cbuf() + pos,
892 217 : h_.size - pos);
893 : }
894 : else
895 : {
896 99 : op.move_chars(
897 99 : h_.buf + pos + n,
898 99 : h_.buf + pos,
899 99 : h_.size - pos);
900 : }
901 :
902 : // serialize
903 : {
904 316 : auto dest = h_.buf + pos;
905 316 : name.copy(dest, name.size());
906 316 : dest += name.size();
907 316 : *dest++ = ':';
908 316 : if(! value.empty())
909 : {
910 304 : *dest++ = ' ';
911 304 : value.copy(
912 : dest, value.size());
913 304 : if( has_obs_fold )
914 18 : detail::remove_obs_fold(
915 18 : dest, dest + value.size());
916 304 : dest += value.size();
917 : }
918 316 : *dest++ = '\r';
919 316 : *dest = '\n';
920 : }
921 :
922 : // update table
923 316 : auto const tab = h_.tab_();
924 : {
925 316 : auto i = h_.count - before;
926 316 : if(i > 0)
927 : {
928 43 : auto p0 = tab0 - h_.count;
929 43 : auto p = tab - h_.count - 1;
930 : do
931 : {
932 80 : *p++ = *p0++ + n;
933 : }
934 80 : while(--i);
935 : }
936 : }
937 316 : auto& e = tab[0 - static_cast<std::ptrdiff_t>(before) - 1];
938 316 : e.np = static_cast<offset_type>(
939 316 : pos - h_.prefix);
940 316 : e.nn = static_cast<
941 316 : offset_type>(name.size());
942 316 : e.vp = static_cast<offset_type>(
943 632 : pos - h_.prefix +
944 316 : name.size() + 1 +
945 316 : ! value.empty());
946 316 : e.vn = static_cast<
947 316 : offset_type>(value.size());
948 316 : e.id = id.value_or(
949 : detail::header::unknown_field);
950 :
951 : // update container
952 316 : h_.count++;
953 316 : h_.size = static_cast<
954 316 : offset_type>(h_.size + n);
955 316 : h_.on_insert(e.id, value);
956 324 : }
957 :
958 : void
959 175 : fields_base::
960 : raw_erase(
961 : std::size_t i) noexcept
962 : {
963 175 : BOOST_ASSERT(i < h_.count);
964 175 : BOOST_ASSERT(h_.buf != nullptr);
965 175 : auto const p0 = offset(i);
966 175 : auto const p1 = offset(i + 1);
967 175 : std::memmove(
968 175 : h_.buf + p0,
969 175 : h_.buf + p1,
970 175 : h_.size - p1);
971 175 : auto const n = p1 - p0;
972 175 : --h_.count;
973 175 : auto ft = h_.tab();
974 277 : for(;i < h_.count; ++i)
975 102 : ft[i] = ft[i + 1] - n;
976 175 : h_.size = static_cast<
977 175 : offset_type>(h_.size - n);
978 175 : }
979 :
980 : // erase n fields matching id
981 : // without updating metadata
982 : void
983 8 : fields_base::
984 : raw_erase_n(
985 : field id,
986 : std::size_t n) noexcept
987 : {
988 : // iterate in reverse
989 8 : auto e = &h_.tab()[h_.count];
990 8 : auto const e0 = &h_.tab()[0];
991 20 : while(n > 0)
992 : {
993 12 : BOOST_ASSERT(e != e0);
994 12 : ++e; // decrement
995 12 : if(e->id == id)
996 : {
997 10 : raw_erase(e0 - e);
998 10 : --n;
999 : }
1000 : }
1001 8 : }
1002 :
1003 : // erase all fields with id
1004 : // and update metadata
1005 : std::size_t
1006 72 : fields_base::
1007 : erase_all(
1008 : std::size_t i0,
1009 : field id) noexcept
1010 : {
1011 72 : BOOST_ASSERT(
1012 : id != detail::header::unknown_field);
1013 72 : std::size_t n = 1;
1014 72 : std::size_t i = h_.count - 1;
1015 72 : auto const ft = h_.tab();
1016 149 : while(i > i0)
1017 : {
1018 77 : if(ft[i].id == id)
1019 : {
1020 44 : raw_erase(i);
1021 44 : ++n;
1022 : }
1023 : // go backwards to
1024 : // reduce memmoves
1025 77 : --i;
1026 : }
1027 72 : raw_erase(i0);
1028 72 : h_.on_erase_all(id);
1029 72 : return n;
1030 : }
1031 :
1032 : // erase all fields with name
1033 : // when id == detail::header::unknown_field
1034 : std::size_t
1035 9 : fields_base::
1036 : erase_all(
1037 : std::size_t i0,
1038 : core::string_view name) noexcept
1039 : {
1040 9 : std::size_t n = 1;
1041 9 : std::size_t i = h_.count - 1;
1042 9 : auto const ft = h_.tab();
1043 9 : auto const* p = h_.cbuf + h_.prefix;
1044 36 : while(i > i0)
1045 : {
1046 : core::string_view s(
1047 27 : p + ft[i].np, ft[i].nn);
1048 27 : if(s == name)
1049 : {
1050 9 : raw_erase(i);
1051 9 : ++n;
1052 : }
1053 : // go backwards to
1054 : // reduce memmoves
1055 27 : --i;
1056 : }
1057 9 : raw_erase(i0);
1058 9 : return n;
1059 : }
1060 :
1061 : // return i-th field absolute offset
1062 : std::size_t
1063 808 : fields_base::
1064 : offset(
1065 : std::size_t i) const noexcept
1066 : {
1067 808 : if(i == 0)
1068 323 : return h_.prefix;
1069 485 : if(i < h_.count)
1070 227 : return h_.prefix + h_.tab()[i].np;
1071 : // make final CRLF the last "field"
1072 258 : return h_.size - 2;
1073 : }
1074 :
1075 : // return i-th field absolute length
1076 : std::size_t
1077 39 : fields_base::
1078 : length(
1079 : std::size_t i) const noexcept
1080 : {
1081 : return
1082 39 : offset(i + 1) -
1083 39 : offset(i);
1084 : }
1085 :
1086 : } // http_proto
1087 : } // boost
|