LCOV - code coverage report
Current view: top level - libs/http_proto/src/fields_base.cpp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 95.6 % 541 517
Test Date: 2025-09-21 18:08:14 Functions: 89.6 % 48 43

            Line data    Source code
       1              : //
       2              : // Copyright (c) 2021 Vinnie Falco (vinnie.falco@gmail.com)
       3              : // Copyright (c) 2025 Mohammad Nejati
       4              : //
       5              : // Distributed under the Boost Software License, Version 1.0. (See accompanying
       6              : // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
       7              : //
       8              : // Official repository: https://github.com/cppalliance/http_proto
       9              : //
      10              : 
      11              : #include <boost/http_proto/detail/config.hpp>
      12              : #include <boost/http_proto/detail/except.hpp>
      13              : #include <boost/http_proto/detail/header.hpp>
      14              : #include <boost/http_proto/error.hpp>
      15              : #include <boost/http_proto/field.hpp>
      16              : #include <boost/http_proto/fields_base.hpp>
      17              : #include <boost/http_proto/header_limits.hpp>
      18              : #include <boost/http_proto/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_proto {
      34              : 
      35              : namespace {
      36              : 
      37              : std::size_t
      38           96 : align_down(
      39              :     void * ptr,
      40              :     std::size_t size,
      41              :     std::size_t alignment)
      42              : {
      43           96 :     auto addr = reinterpret_cast<std::uintptr_t>(ptr);
      44           96 :     auto aligned_end = (addr + size) & ~(alignment - 1);
      45              : 
      46           96 :     if(aligned_end > addr)
      47           96 :         return aligned_end - addr;
      48              : 
      49            0 :     return 0;
      50              : }
      51              : 
      52              : void
      53          250 : verify_field_name(
      54              :     core::string_view name,
      55              :     system::error_code& ec)
      56              : {
      57          250 :     auto rv = grammar::parse(
      58              :         name, detail::field_name_rule);
      59          250 :     if(rv.has_error())
      60              :     {
      61           18 :         ec = BOOST_HTTP_PROTO_ERR(
      62              :             error::bad_field_name);
      63              :     }
      64          250 : }
      65              : 
      66              : system::result<detail::field_value_rule_t::value_type>
      67          382 : verify_field_value(
      68              :     core::string_view value)
      69              : {
      70          382 :     auto it = value.begin();
      71          382 :     auto end = value.end();
      72              :     auto rv =
      73          382 :         grammar::parse(it, end, detail::field_value_rule);
      74          382 :     if( rv.has_error() )
      75              :     {
      76            7 :         if( rv.error() == condition::need_more_input )
      77            7 :             return error::bad_field_value;
      78            0 :         return rv.error();
      79              :     }
      80              : 
      81          375 :     if( rv->has_crlf )
      82           16 :         return error::bad_field_smuggle;
      83              : 
      84          359 :     if( it != end )
      85            7 :         return error::bad_field_value;
      86              : 
      87          352 :     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         1035 :     op_t(
     105              :         fields_base& self,
     106              :         core::string_view* s0 = nullptr,
     107              :         core::string_view* s1 = nullptr) noexcept
     108         1035 :         : self_(self)
     109         1035 :         , s0_(s0)
     110         1035 :         , s1_(s1)
     111              :     {
     112         1035 :     }
     113              : 
     114         1035 :     ~op_t()
     115              :     {
     116         1035 :         if(buf_)
     117          162 :             delete[] buf_;
     118         1035 :     }
     119              : 
     120              :     char const*
     121           12 :     buf() const noexcept
     122              :     {
     123           12 :         return buf_;
     124              :     }
     125              : 
     126              :     char const*
     127          449 :     cbuf() const noexcept
     128              :     {
     129          449 :         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         1011 : fields_base::
     161              : op_t::
     162              : reserve(
     163              :     std::size_t n)
     164              : {
     165              :     // TODO: consider using a growth factor
     166         1011 :     if(n > self_.max_cap_)
     167              :     {
     168              :         // max capacity exceeded
     169           19 :         detail::throw_length_error();
     170              :     }
     171          992 :     if(n <= self_.h_.cap)
     172          150 :         return false;
     173          842 :     auto buf = new char[n];
     174          842 :     buf_ = self_.h_.buf;
     175          842 :     cbuf_ = self_.h_.cbuf;
     176          842 :     cap_ = self_.h_.cap;
     177          842 :     self_.h_.buf = buf;
     178          842 :     self_.h_.cbuf = buf;
     179          842 :     self_.h_.cap = n;
     180          842 :     return true;
     181              : }
     182              : 
     183              : bool
     184          914 : fields_base::
     185              : op_t::
     186              : grow(
     187              :     std::size_t extra_char,
     188              :     std::size_t extra_field)
     189              : {
     190          914 :     if(extra_field > detail::header::max_offset - self_.h_.count)
     191            0 :         detail::throw_length_error();
     192              : 
     193          914 :     if(extra_char > detail::header::max_offset - self_.h_.size)
     194            2 :         detail::throw_length_error();
     195              : 
     196          912 :     return reserve(
     197              :         detail::header::bytes_needed(
     198          912 :             self_.h_.size + extra_char,
     199         1818 :             self_.h_.count + extra_field));
     200              : }
     201              : 
     202              : void
     203          121 : fields_base::
     204              : op_t::
     205              : move_chars(
     206              :     char* dest,
     207              :     char const* src,
     208              :     std::size_t n) const noexcept
     209              : {
     210          121 :     detail::move_chars(
     211          121 :         dest, src, n, s0_, s1_);
     212          121 : }
     213              : 
     214              : //------------------------------------------------
     215              : 
     216           73 : 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           73 :     core::string_view* s1)
     223           73 :     : self_(self)
     224           73 :     , new_prefix_(static_cast<
     225           73 :         offset_type>(new_prefix))
     226              : {
     227           73 :     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          142 :     if(new_prefix_ < self_.h_.prefix
     234           71 :         && !self.h_.is_default())
     235           10 :         return;
     236              : 
     237           61 :     auto new_size = static_cast<offset_type>(
     238           61 :         self.h_.size - self.h_.prefix + new_prefix_);
     239              : 
     240              :     auto bytes_needed =
     241           61 :         detail::header::bytes_needed(
     242              :             new_size,
     243           61 :             self.h_.count);
     244              : 
     245           61 :     if(bytes_needed > self.h_.cap)
     246              :     {
     247              :         // static storage will always throw which is
     248              :         // intended since they cannot reallocate.
     249           28 :         if(self.max_cap_ < bytes_needed)
     250            1 :             detail::throw_length_error();
     251              :         // TODO: consider using a growth factor
     252           27 :         char* p = new char[bytes_needed];
     253           27 :         std::memcpy(
     254           27 :             p + new_prefix_,
     255           27 :             self.h_.cbuf + self.h_.prefix,
     256           27 :             self.h_.size - self.h_.prefix);
     257           27 :         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           27 :         buf_        = self.h_.buf;
     263           27 :         self.h_.buf = p;
     264           27 :         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           33 :         detail::move_chars(
     271           33 :             self.h_.buf + new_prefix_,
     272           33 :             self.h_.cbuf + self.h_.prefix,
     273           33 :             self.h_.size - self.h_.prefix,
     274              :             s0,
     275              :             s1);
     276              :     }
     277              : 
     278           60 :     self.h_.cbuf   = self.h_.buf;
     279           60 :     self.h_.size   = new_size;
     280           60 :     self.h_.prefix = new_prefix_;
     281              : }
     282              : 
     283           70 : fields_base::
     284              : prefix_op_t::
     285              : ~prefix_op_t()
     286              : {
     287           70 :     if(new_prefix_ < self_.h_.prefix)
     288              :     {
     289           10 :         std::memmove(
     290           10 :             self_.h_.buf + new_prefix_,
     291           10 :             self_.h_.cbuf + self_.h_.prefix,
     292           10 :             self_.h_.size - self_.h_.prefix);
     293              : 
     294           10 :         self_.h_.size =
     295           10 :             self_.h_.size - self_.h_.prefix + new_prefix_;
     296           10 :         self_.h_.prefix = new_prefix_;
     297              :     }
     298           60 :     else if(buf_)
     299              :     {
     300            5 :         delete[] buf_;
     301              :     }
     302           70 : }
     303              : 
     304              : //------------------------------------------------
     305              : 
     306          193 : fields_base::
     307              : fields_base(
     308            0 :     detail::kind k) noexcept
     309            0 :     : fields_view_base(&h_)
     310          193 :     , h_(k)
     311              : {
     312          193 : }
     313              : 
     314           49 : fields_base::
     315              : fields_base(
     316              :     detail::kind k,
     317              :     char* storage,
     318            0 :     std::size_t cap) noexcept
     319              :     : fields_base(
     320           49 :         *detail::header::get_default(k), storage, cap)
     321              : {
     322           49 : }
     323              : 
     324              : // copy s and parse it
     325          569 : fields_base::
     326              : fields_base(
     327              :     detail::kind k,
     328            0 :     core::string_view s)
     329            0 :     : fields_view_base(&h_)
     330          569 :     , h_(detail::empty{k})
     331              : {
     332          569 :     auto n = detail::header::count_crlf(s);
     333          569 :     if(h_.kind == detail::kind::fields)
     334              :     {
     335          265 :         if(n < 1)
     336            1 :             detail::throw_invalid_argument();
     337          264 :         n -= 1;
     338              :     }
     339              :     else
     340              :     {
     341          304 :         if(n < 2)
     342            2 :             detail::throw_invalid_argument();
     343          302 :         n -= 2;
     344              :     }
     345          566 :     op_t op(*this);
     346          566 :     op.grow(s.size(), n);
     347          566 :     s.copy(h_.buf, s.size());
     348          566 :     system::error_code ec;
     349              :     // VFALCO This is using defaults?
     350          566 :     header_limits lim;
     351          566 :     h_.parse(s.size(), lim, ec);
     352          566 :     if(ec.failed())
     353            0 :         detail::throw_system_error(ec);
     354          566 : }
     355              : 
     356              : // copy s and parse it
     357           37 : fields_base::
     358              : fields_base(
     359              :     detail::kind k,
     360              :     char* storage,
     361              :     std::size_t cap,
     362            0 :     core::string_view s)
     363            0 :     : fields_view_base(&h_)
     364           37 :     , h_(detail::empty{k})
     365           37 :     , external_storage_(true)
     366              : {
     367           37 :     h_.cbuf = storage;
     368           37 :     h_.buf = storage;
     369           37 :     h_.cap = align_down(
     370              :         storage,
     371              :         cap,
     372              :         alignof(detail::header::entry));
     373           37 :     max_cap_ = h_.cap;
     374              : 
     375           37 :     auto n = detail::header::count_crlf(s);
     376           37 :     if(h_.kind == detail::kind::fields)
     377              :     {
     378           12 :         if(n < 1)
     379            0 :             detail::throw_invalid_argument();
     380           12 :         n -= 1;
     381              :     }
     382              :     else
     383              :     {
     384           25 :         if(n < 2)
     385            0 :             detail::throw_invalid_argument();
     386           25 :         n -= 2;
     387              :     }
     388              : 
     389           37 :     if(detail::header::bytes_needed(
     390              :         s.size(), n)
     391           37 :             >= h_.cap)
     392            0 :         detail::throw_length_error();
     393              : 
     394           37 :     s.copy(h_.buf, s.size());
     395           37 :     system::error_code ec;
     396              :     // VFALCO This is using defaults?
     397           37 :     header_limits lim;
     398           37 :     h_.parse(s.size(), lim, ec);
     399           37 :     if(ec.failed())
     400            0 :         detail::throw_system_error(ec);
     401           37 : }
     402              : 
     403              : // construct a complete copy of h
     404           26 : fields_base::
     405              : fields_base(
     406           14 :     detail::header const& h)
     407           14 :     : fields_view_base(&h_)
     408           26 :     , h_(h.kind)
     409              : {
     410           26 :     if(h.is_default())
     411            8 :         return;
     412              : 
     413              :     // allocate and copy the buffer
     414           18 :     op_t op(*this);
     415           18 :     op.grow(h.size, h.count);
     416           18 :     h.assign_to(h_);
     417           18 :     std::memcpy(
     418           18 :         h_.buf, h.cbuf, h.size);
     419           18 :     h.copy_table(h_.buf + h_.cap);
     420           18 : }
     421              : 
     422              : // construct a complete copy of h
     423           59 : fields_base::
     424              : fields_base(
     425              :     detail::header const& h,
     426              :     char* storage,
     427            0 :     std::size_t cap)
     428            0 :     : fields_view_base(&h_)
     429           59 :     , h_(h.kind)
     430           59 :     , external_storage_(true)
     431              : {
     432           59 :     h_.cbuf = storage;
     433           59 :     h_.buf = storage;
     434           59 :     h_.cap = align_down(
     435              :         storage,
     436              :         cap,
     437              :         alignof(detail::header::entry));
     438           59 :     max_cap_ = h_.cap;
     439              : 
     440          118 :     if(detail::header::bytes_needed(
     441           59 :         h.size, h.count)
     442           59 :             >= h_.cap)
     443            0 :         detail::throw_length_error();
     444              : 
     445           59 :     h.assign_to(h_);
     446           59 :     std::memcpy(
     447           59 :         h_.buf, h.cbuf, h.size);
     448           59 :     h.copy_table(h_.buf + h_.cap);
     449           59 : }
     450              : 
     451              : //------------------------------------------------
     452              : 
     453          881 : fields_base::
     454          881 : ~fields_base()
     455              : {
     456          881 :     if(h_.buf && !external_storage_)
     457          702 :         delete[] h_.buf;
     458          881 : }
     459              : 
     460              : //------------------------------------------------
     461              : //
     462              : // Capacity
     463              : //
     464              : //------------------------------------------------
     465              : 
     466              : void
     467           14 : fields_base::
     468              : clear() noexcept
     469              : {
     470           14 :     if(! h_.buf)
     471            5 :         return;
     472              :     using H =
     473              :         detail::header;
     474              :     auto const& h =
     475            9 :         *H::get_default(
     476            9 :             h_.kind);
     477            9 :     h.assign_to(h_);
     478            9 :     std::memcpy(
     479            9 :         h_.buf,
     480            9 :         h.cbuf,
     481            9 :         h_.size);
     482              : }
     483              : 
     484              : void
     485           99 : fields_base::
     486              : reserve_bytes(
     487              :     std::size_t n)
     488              : {
     489           99 :     op_t op(*this);
     490           99 :     if(! op.reserve(n))
     491           51 :         return;
     492           70 :     std::memcpy(
     493           35 :         h_.buf, op.cbuf(), h_.size);
     494           35 :     auto const nt =
     495           35 :         sizeof(entry) * h_.count;
     496           35 :     if(nt > 0)
     497            6 :         std::memcpy(
     498            6 :             h_.buf + h_.cap - nt,
     499            6 :             op.end() - nt,
     500              :             nt);
     501           99 : }
     502              : 
     503              : void
     504            8 : fields_base::
     505              : shrink_to_fit()
     506              : {
     507           16 :     if(detail::header::bytes_needed(
     508            8 :         h_.size, h_.count) >=
     509            8 :             h_.cap)
     510            4 :         return;
     511              : 
     512            5 :     if(external_storage_)
     513            1 :         return;
     514              : 
     515            4 :     fields_base tmp(h_);
     516            4 :     tmp.h_.swap(h_);
     517            4 : }
     518              : 
     519              : 
     520              : void
     521           30 : fields_base::
     522              : set_max_capacity_in_bytes(std::size_t n)
     523              : {
     524           30 :     if(n < h_.cap)
     525            6 :         detail::throw_invalid_argument();
     526           24 :     max_cap_ = n;
     527           24 : }
     528              : 
     529              : //------------------------------------------------
     530              : //
     531              : // Modifiers
     532              : //
     533              : //------------------------------------------------
     534              : 
     535              : auto
     536           31 : fields_base::
     537              : erase(
     538              :     iterator it) noexcept -> iterator
     539              : {
     540           31 :     auto const id = it->id.value_or(
     541              :         detail::header::unknown_field);
     542           31 :     raw_erase(it.i_);
     543           31 :     h_.on_erase(id);
     544           31 :     return it;
     545              : }
     546              : 
     547              : std::size_t
     548           30 : fields_base::
     549              : erase(
     550              :     field id) noexcept
     551              : {
     552           30 :     auto const i0 = h_.find(id);
     553           30 :     if(i0 == h_.count)
     554            3 :         return 0;
     555           27 :     return erase_all(i0, id);
     556              : }
     557              : 
     558              : std::size_t
     559           18 : fields_base::
     560              : erase(
     561              :     core::string_view name) noexcept
     562              : {
     563           18 :     auto const i0 = h_.find(name);
     564           18 :     if(i0 == h_.count)
     565            3 :         return 0;
     566           15 :     auto const ft = h_.tab();
     567           15 :     auto const id = ft[i0].id;
     568           15 :     if(id == detail::header::unknown_field)
     569            6 :         return erase_all(i0, name);
     570            9 :     return erase_all(i0, id);
     571              : }
     572              : 
     573              : //------------------------------------------------
     574              : 
     575              : void
     576           32 : fields_base::
     577              : set(
     578              :     iterator it,
     579              :     core::string_view value,
     580              :     system::error_code& ec)
     581              : {
     582           32 :     auto rv = verify_field_value(value);
     583           32 :     if(rv.has_error())
     584              :     {
     585            4 :         ec = rv.error();
     586            4 :         return;
     587              :     }
     588              : 
     589           28 :     value = rv->value;
     590           28 :     bool has_obs_fold = rv->has_obs_fold;
     591              : 
     592           28 :     auto const i = it.i_;
     593           28 :     auto tab = h_.tab();
     594           28 :     auto const& e0 = tab[i];
     595           28 :     auto const pos0 = offset(i);
     596           28 :     auto const pos1 = offset(i + 1);
     597              :     std::ptrdiff_t dn =
     598           28 :         value.size() -
     599           28 :         it->value.size();
     600           28 :     if( value.empty() &&
     601           28 :         ! it->value.empty())
     602            0 :         --dn; // remove SP
     603           28 :     else if(
     604           28 :         it->value.empty() &&
     605            0 :         ! value.empty())
     606            0 :         ++dn; // add SP
     607              : 
     608           28 :     op_t op(*this, &value);
     609           34 :     if( dn > 0 &&
     610           12 :         op.grow(value.size() -
     611           34 :             it->value.size(), 0))
     612              :     {
     613              :         // reallocated
     614            6 :         auto dest = h_.buf +
     615            6 :             pos0 + e0.nn + 1;
     616           12 :         std::memcpy(
     617            6 :             h_.buf,
     618            6 :             op.buf(),
     619            6 :             dest - h_.buf);
     620            6 :         if(! value.empty())
     621              :         {
     622            6 :             *dest++ = ' ';
     623            6 :             value.copy(
     624              :                 dest,
     625              :                 value.size());
     626            6 :             if( has_obs_fold )
     627            3 :                 detail::remove_obs_fold(
     628            3 :                     dest, dest + value.size());
     629            6 :             dest += value.size();
     630              :         }
     631            6 :         *dest++ = '\r';
     632            6 :         *dest++ = '\n';
     633           12 :         std::memcpy(
     634            6 :             h_.buf + pos1 + dn,
     635           12 :             op.buf() + pos1,
     636            6 :             h_.size - pos1);
     637           12 :         std::memcpy(
     638            6 :             h_.buf + h_.cap -
     639            6 :                 sizeof(entry) * h_.count,
     640            6 :             &op.tab()[h_.count - 1],
     641            6 :             sizeof(entry) * h_.count);
     642              :     }
     643              :     else
     644              :     {
     645              :         // copy the value first
     646           44 :         auto dest = h_.buf + pos0 +
     647           22 :             it->name.size() + 1;
     648           22 :         if(! value.empty())
     649              :         {
     650           22 :             *dest++ = ' ';
     651           22 :             value.copy(
     652              :                 dest,
     653              :                 value.size());
     654           22 :             if( has_obs_fold )
     655            0 :                 detail::remove_obs_fold(
     656            0 :                     dest, dest + value.size());
     657           22 :             dest += value.size();
     658              :         }
     659           22 :         op.move_chars(
     660           22 :             h_.buf + pos1 + dn,
     661           22 :             h_.buf + pos1,
     662           22 :             h_.size - pos1);
     663           22 :         *dest++ = '\r';
     664           22 :         *dest++ = '\n';
     665              :     }
     666              :     {
     667              :         // update tab
     668           28 :         auto ft = h_.tab();
     669           40 :         for(std::size_t j = h_.count - 1;
     670           40 :                 j > i; --j)
     671           12 :             ft[j] = ft[j] + dn;
     672           28 :         auto& e = ft[i];
     673           56 :         e.vp = e.np + e.nn +
     674           28 :             1 + ! value.empty();
     675           28 :         e.vn = static_cast<
     676           28 :             offset_type>(value.size());
     677           28 :         h_.size = static_cast<
     678           28 :             offset_type>(h_.size + dn);
     679              :     }
     680           28 :     auto const id = it->id.value_or(
     681              :         detail::header::unknown_field);
     682           28 :     if(h_.is_special(id))
     683              :     {
     684              :         // replace first char of name
     685              :         // with null to hide metadata
     686           13 :         char saved = h_.buf[pos0];
     687           13 :         auto& e = h_.tab()[i];
     688           13 :         e.id = detail::header::unknown_field;
     689           13 :         h_.buf[pos0] = '\0';
     690           13 :         h_.on_erase(id);
     691           13 :         h_.buf[pos0] = saved; // restore
     692           13 :         e.id = id;
     693           13 :         h_.on_insert(id, it->value);
     694              :     }
     695           28 : }
     696              : 
     697              : // erase existing fields with id
     698              : // and then add the field with value
     699              : void
     700          109 : fields_base::
     701              : set(
     702              :     field id,
     703              :     core::string_view value,
     704              :     system::error_code& ec)
     705              : {
     706          109 :     auto rv = verify_field_value(value);
     707          109 :     if(rv.has_error())
     708              :     {
     709            4 :         ec = rv.error();
     710            4 :         return;
     711              :     }
     712              : 
     713          105 :     auto const i0 = h_.find(id);
     714          105 :     if(i0 != h_.count)
     715              :     {
     716              :         // field exists
     717           21 :         auto const ft = h_.tab();
     718              :         {
     719              :             // provide strong guarantee
     720              :             auto const n0 =
     721           21 :                 h_.size - length(i0);
     722              :             auto const n =
     723           21 :                 ft[i0].nn + 2 +
     724           21 :                     rv->value.size() + 2;
     725              :             // VFALCO missing overflow check
     726           21 :             reserve_bytes(n0 + n);
     727              :         }
     728           21 :         erase_all(i0, id);
     729              :     }
     730              : 
     731          105 :     insert_unchecked(
     732              :         id,
     733              :         to_string(id),
     734          105 :         rv->value,
     735          105 :         h_.count,
     736          105 :         rv->has_obs_fold);
     737              : }
     738              : 
     739              : // erase existing fields with name
     740              : // and then add the field with value
     741              : void
     742           33 : fields_base::
     743              : set(
     744              :     core::string_view name,
     745              :     core::string_view value,
     746              :     system::error_code& ec)
     747              : {
     748           33 :     verify_field_name(name , ec);
     749           33 :     if(ec.failed())
     750            8 :         return;
     751              : 
     752           29 :     auto rv = verify_field_value(value);
     753           29 :     if(rv.has_error())
     754              :     {
     755            4 :         ec = rv.error();
     756            4 :         return;
     757              :     }
     758              : 
     759           25 :     auto const i0 = h_.find(name);
     760           25 :     if(i0 != h_.count)
     761              :     {
     762              :         // field exists
     763           18 :         auto const ft = h_.tab();
     764           18 :         auto const id = ft[i0].id;
     765              :         {
     766              :             // provide strong guarantee
     767              :             auto const n0 =
     768           18 :                 h_.size - length(i0);
     769              :             auto const n =
     770           18 :                 ft[i0].nn + 2 +
     771           18 :                     rv->value.size() + 2;
     772              :             // VFALCO missing overflow check
     773           18 :             reserve_bytes(n0 + n);
     774              :         }
     775              :         // VFALCO simple algorithm but
     776              :         // costs one extra memmove
     777           18 :         if(id != detail::header::unknown_field)
     778           15 :             erase_all(i0, id);
     779              :         else
     780            3 :             erase_all(i0, name);
     781              :     }
     782           25 :     insert_unchecked(
     783              :         string_to_field(name),
     784              :         name,
     785           25 :         rv->value,
     786           25 :         h_.count,
     787           25 :         rv->has_obs_fold);
     788              : }
     789              : 
     790              : //------------------------------------------------
     791              : //
     792              : // (implementation)
     793              : //
     794              : //------------------------------------------------
     795              : 
     796              : // copy start line and fields
     797              : void
     798           31 : fields_base::
     799              : copy_impl(
     800              :     detail::header const& h)
     801              : {
     802           31 :     BOOST_ASSERT(
     803              :         h.kind == ph_->kind);
     804              : 
     805              :     auto const n =
     806           31 :         detail::header::bytes_needed(
     807           31 :             h.size, h.count);
     808           31 :     if(n <= h_.cap && (!h.is_default() || external_storage_))
     809              :     {
     810              :         // no realloc
     811           21 :         h.assign_to(h_);
     812           21 :         h.copy_table(
     813           21 :             h_.buf + h_.cap);
     814           21 :         std::memcpy(
     815           21 :             h_.buf,
     816           21 :             h.cbuf,
     817           21 :             h.size);
     818           21 :         return;
     819              :     }
     820              : 
     821              :     // static storages cannot reallocate
     822           10 :     if(external_storage_)
     823            0 :         detail::throw_length_error();
     824              : 
     825           10 :     fields_base tmp(h);
     826           10 :     tmp.h_.swap(h_);
     827           10 : }
     828              : 
     829              : void
     830          217 : fields_base::
     831              : insert_impl(
     832              :     optional<field> id,
     833              :     core::string_view name,
     834              :     core::string_view value,
     835              :     std::size_t before,
     836              :     system::error_code& ec)
     837              : {
     838          217 :     verify_field_name(name, ec);
     839          217 :     if(ec.failed())
     840           23 :         return;
     841              : 
     842          212 :     auto rv = verify_field_value(value);
     843          212 :     if(rv.has_error())
     844              :     {
     845           18 :         ec = rv.error();
     846           18 :         return;
     847              :     }
     848              : 
     849          194 :     insert_unchecked(
     850              :         id,
     851              :         name,
     852          194 :         rv->value,
     853              :         before,
     854          194 :         rv->has_obs_fold);
     855              : }
     856              : 
     857              : void
     858          324 : fields_base::
     859              : insert_unchecked(
     860              :     optional<field> id,
     861              :     core::string_view name,
     862              :     core::string_view value,
     863              :     std::size_t before,
     864              :     bool has_obs_fold)
     865              : {
     866          324 :     auto const tab0 = h_.tab_();
     867          324 :     auto const pos = offset(before);
     868              :     auto const n =
     869          324 :         name.size() +       // name
     870          324 :         1 +                 // ':'
     871          324 :         ! value.empty() +   // [SP]
     872          324 :         value.size() +      // value
     873          324 :         2;                  // CRLF
     874              : 
     875          324 :     op_t op(*this, &name, &value);
     876          324 :     if(op.grow(n, 1))
     877              :     {
     878              :         // reallocated
     879          217 :         if(pos > 0)
     880          197 :             std::memcpy(
     881          197 :                 h_.buf,
     882          197 :                 op.cbuf(),
     883              :                 pos);
     884          217 :         if(before > 0)
     885          178 :             std::memcpy(
     886           89 :                 h_.tab_() - before,
     887           89 :                 tab0 - before,
     888              :                 before * sizeof(entry));
     889          434 :         std::memcpy(
     890          217 :             h_.buf + pos + n,
     891          217 :             op.cbuf() + pos,
     892          217 :             h_.size - pos);
     893              :     }
     894              :     else
     895              :     {
     896           99 :         op.move_chars(
     897           99 :             h_.buf + pos + n,
     898           99 :             h_.buf + pos,
     899           99 :             h_.size - pos);
     900              :     }
     901              : 
     902              :     // serialize
     903              :     {
     904          316 :         auto dest = h_.buf + pos;
     905          316 :         name.copy(dest, name.size());
     906          316 :         dest += name.size();
     907          316 :         *dest++ = ':';
     908          316 :         if(! value.empty())
     909              :         {
     910          304 :             *dest++ = ' ';
     911          304 :             value.copy(
     912              :                 dest, value.size());
     913          304 :             if( has_obs_fold )
     914           18 :                 detail::remove_obs_fold(
     915           18 :                     dest, dest + value.size());
     916          304 :             dest += value.size();
     917              :         }
     918          316 :         *dest++ = '\r';
     919          316 :         *dest = '\n';
     920              :     }
     921              : 
     922              :     // update table
     923          316 :     auto const tab = h_.tab_();
     924              :     {
     925          316 :         auto i = h_.count - before;
     926          316 :         if(i > 0)
     927              :         {
     928           43 :             auto p0 = tab0 - h_.count;
     929           43 :             auto p = tab - h_.count - 1;
     930              :             do
     931              :             {
     932           80 :                 *p++ = *p0++ + n;
     933              :             }
     934           80 :             while(--i);
     935              :         }
     936              :     }
     937          316 :     auto& e = tab[0 - static_cast<std::ptrdiff_t>(before) - 1];
     938          316 :     e.np = static_cast<offset_type>(
     939          316 :         pos - h_.prefix);
     940          316 :     e.nn = static_cast<
     941          316 :         offset_type>(name.size());
     942          316 :     e.vp = static_cast<offset_type>(
     943          632 :         pos - h_.prefix +
     944          316 :             name.size() + 1 +
     945          316 :             ! value.empty());
     946          316 :     e.vn = static_cast<
     947          316 :         offset_type>(value.size());
     948          316 :     e.id = id.value_or(
     949              :         detail::header::unknown_field);
     950              : 
     951              :     // update container
     952          316 :     h_.count++;
     953          316 :     h_.size = static_cast<
     954          316 :         offset_type>(h_.size + n);
     955          316 :     h_.on_insert(e.id, value);
     956          324 : }
     957              : 
     958              : void
     959          175 : fields_base::
     960              : raw_erase(
     961              :     std::size_t i) noexcept
     962              : {
     963          175 :     BOOST_ASSERT(i < h_.count);
     964          175 :     BOOST_ASSERT(h_.buf != nullptr);
     965          175 :     auto const p0 = offset(i);
     966          175 :     auto const p1 = offset(i + 1);
     967          175 :     std::memmove(
     968          175 :         h_.buf + p0,
     969          175 :         h_.buf + p1,
     970          175 :         h_.size - p1);
     971          175 :     auto const n = p1 - p0;
     972          175 :     --h_.count;
     973          175 :     auto ft = h_.tab();
     974          277 :     for(;i < h_.count; ++i)
     975          102 :         ft[i] = ft[i + 1] - n;
     976          175 :     h_.size = static_cast<
     977          175 :         offset_type>(h_.size - n);
     978          175 : }
     979              : 
     980              : // erase n fields matching id
     981              : // without updating metadata
     982              : void
     983            8 : fields_base::
     984              : raw_erase_n(
     985              :     field id,
     986              :     std::size_t n) noexcept
     987              : {
     988              :     // iterate in reverse
     989            8 :     auto e = &h_.tab()[h_.count];
     990            8 :     auto const e0 = &h_.tab()[0];
     991           20 :     while(n > 0)
     992              :     {
     993           12 :         BOOST_ASSERT(e != e0);
     994           12 :         ++e; // decrement
     995           12 :         if(e->id == id)
     996              :         {
     997           10 :             raw_erase(e0 - e);
     998           10 :             --n;
     999              :         }
    1000              :     }
    1001            8 : }
    1002              : 
    1003              : // erase all fields with id
    1004              : // and update metadata
    1005              : std::size_t
    1006           72 : fields_base::
    1007              : erase_all(
    1008              :     std::size_t i0,
    1009              :     field id) noexcept
    1010              : {
    1011           72 :     BOOST_ASSERT(
    1012              :         id != detail::header::unknown_field);
    1013           72 :     std::size_t n = 1;
    1014           72 :     std::size_t i = h_.count - 1;
    1015           72 :     auto const ft = h_.tab();
    1016          149 :     while(i > i0)
    1017              :     {
    1018           77 :         if(ft[i].id == id)
    1019              :         {
    1020           44 :             raw_erase(i);
    1021           44 :             ++n;
    1022              :         }
    1023              :         // go backwards to
    1024              :         // reduce memmoves
    1025           77 :         --i;
    1026              :     }
    1027           72 :     raw_erase(i0);
    1028           72 :     h_.on_erase_all(id);
    1029           72 :     return n;
    1030              : }
    1031              : 
    1032              : // erase all fields with name
    1033              : // when id == detail::header::unknown_field
    1034              : std::size_t
    1035            9 : fields_base::
    1036              : erase_all(
    1037              :     std::size_t i0,
    1038              :     core::string_view name) noexcept
    1039              : {
    1040            9 :     std::size_t n = 1;
    1041            9 :     std::size_t i = h_.count - 1;
    1042            9 :     auto const ft = h_.tab();
    1043            9 :     auto const* p = h_.cbuf + h_.prefix;
    1044           36 :     while(i > i0)
    1045              :     {
    1046              :         core::string_view s(
    1047           27 :             p + ft[i].np, ft[i].nn);
    1048           27 :         if(s == name)
    1049              :         {
    1050            9 :             raw_erase(i);
    1051            9 :             ++n;
    1052              :         }
    1053              :         // go backwards to
    1054              :         // reduce memmoves
    1055           27 :         --i;
    1056              :     }
    1057            9 :     raw_erase(i0);
    1058            9 :     return n;
    1059              : }
    1060              : 
    1061              : // return i-th field absolute offset
    1062              : std::size_t
    1063          808 : fields_base::
    1064              : offset(
    1065              :     std::size_t i) const noexcept
    1066              : {
    1067          808 :     if(i == 0)
    1068          323 :         return h_.prefix;
    1069          485 :     if(i < h_.count)
    1070          227 :         return h_.prefix + h_.tab()[i].np;
    1071              :     // make final CRLF the last "field"
    1072          258 :     return h_.size - 2;
    1073              : }
    1074              : 
    1075              : // return i-th field absolute length
    1076              : std::size_t
    1077           39 : fields_base::
    1078              : length(
    1079              :     std::size_t i) const noexcept
    1080              : {
    1081              :     return
    1082           39 :         offset(i + 1) -
    1083           39 :         offset(i);
    1084              : }
    1085              : 
    1086              : } // http_proto
    1087              : } // boost
        

Generated by: LCOV version 2.1