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

Generated by: LCOV version 2.3