src/fields_base.cpp

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