LCOV - code coverage report
Current view: top level - src - fields_base.cpp (source / functions) Coverage Total Hit Missed
Test: coverage_remapped.info Lines: 97.4 % 703 685 18
Test Date: 2026-08-26 15:14:03 Functions: 98.6 % 69 68 1

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

Generated by: LCOV version 2.3