src/fields_base.cpp

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