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