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: 97.4 % 697 679
Test Date: 2025-10-12 23:51:56 Functions: 98.6 % 70 69

            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         1066 : align_down(
      39              :     void * ptr,
      40              :     std::size_t size,
      41              :     std::size_t alignment)
      42              : {
      43         1066 :     auto addr = reinterpret_cast<std::uintptr_t>(ptr);
      44         1066 :     auto aligned_end = (addr + size) & ~(alignment - 1);
      45              : 
      46         1066 :     if(aligned_end > addr)
      47         1066 :         return aligned_end - addr;
      48              : 
      49            0 :     return 0;
      50              : }
      51              : 
      52              : void
      53          235 : verify_field_name(
      54              :     core::string_view name,
      55              :     system::error_code& ec)
      56              : {
      57          235 :     auto rv = grammar::parse(
      58              :         name, detail::field_name_rule);
      59          235 :     if(rv.has_error())
      60              :     {
      61           18 :         ec = BOOST_HTTP_PROTO_ERR(
      62              :             error::bad_field_name);
      63              :     }
      64          235 : }
      65              : 
      66              : system::result<detail::field_value_rule_t::value_type>
      67          364 : verify_field_value(
      68              :     core::string_view value)
      69              : {
      70          364 :     auto it = value.begin();
      71          364 :     auto end = value.end();
      72              :     auto rv =
      73          364 :         grammar::parse(it, end, detail::field_value_rule);
      74          364 :     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          357 :     if( rv->has_crlf )
      82           16 :         return error::bad_field_smuggle;
      83              : 
      84          341 :     if( it != end )
      85            7 :         return error::bad_field_value;
      86              : 
      87          334 :     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          988 :     op_t(
     105              :         fields_base& self,
     106              :         core::string_view* s0 = nullptr,
     107              :         core::string_view* s1 = nullptr) noexcept
     108          988 :         : self_(self)
     109          988 :         , s0_(s0)
     110          988 :         , s1_(s1)
     111              :     {
     112          988 :     }
     113              : 
     114          988 :     ~op_t()
     115              :     {
     116          988 :         if(buf_)
     117          162 :             delete[] buf_;
     118          988 :     }
     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          968 : fields_base::
     161              : op_t::
     162              : reserve(
     163              :     std::size_t n)
     164              : {
     165              :     // TODO: consider using a growth factor
     166          968 :     if(n > self_.max_cap_)
     167              :     {
     168              :         // max capacity exceeded
     169           18 :         detail::throw_length_error();
     170              :     }
     171          950 :     if(n <= self_.h_.cap)
     172          133 :         return false;
     173          817 :     auto buf = new char[n];
     174          817 :     buf_ = self_.h_.buf;
     175          817 :     cbuf_ = self_.h_.cbuf;
     176          817 :     cap_ = self_.h_.cap;
     177          817 :     self_.h_.buf = buf;
     178          817 :     self_.h_.cbuf = buf;
     179          817 :     self_.h_.cap = n;
     180          817 :     return true;
     181              : }
     182              : 
     183              : bool
     184          875 : fields_base::
     185              : op_t::
     186              : grow(
     187              :     std::size_t extra_char,
     188              :     std::size_t extra_field)
     189              : {
     190          875 :     if(extra_field > detail::header::max_offset - self_.h_.count)
     191            0 :         detail::throw_length_error();
     192              : 
     193          875 :     if(extra_char > detail::header::max_offset - self_.h_.size)
     194            2 :         detail::throw_length_error();
     195              : 
     196          873 :     return reserve(
     197              :         detail::header::bytes_needed(
     198          873 :             self_.h_.size + extra_char,
     199         1741 :             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           39 : 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           39 :     core::string_view* s1)
     223           39 :     : self_(self)
     224           39 :     , new_prefix_(static_cast<
     225           39 :         offset_type>(new_prefix))
     226              : {
     227           39 :     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           74 :     if(new_prefix_ < self_.h_.prefix
     234           37 :         && !self.h_.is_default())
     235            5 :         return;
     236              : 
     237           32 :     auto new_size = static_cast<offset_type>(
     238           32 :         self.h_.size - self.h_.prefix + new_prefix_);
     239              : 
     240              :     auto bytes_needed =
     241           32 :         detail::header::bytes_needed(
     242              :             new_size,
     243           32 :             self.h_.count);
     244              : 
     245           32 :     if(bytes_needed > self.h_.cap)
     246              :     {
     247              :         // static storage will always throw which is
     248              :         // intended since they cannot reallocate.
     249           27 :         if(self.max_cap_ < bytes_needed)
     250            1 :             detail::throw_length_error();
     251              :         // TODO: consider using a growth factor
     252           26 :         char* p = new char[bytes_needed];
     253           26 :         std::memcpy(
     254           26 :             p + new_prefix_,
     255           26 :             self.h_.cbuf + self.h_.prefix,
     256           26 :             self.h_.size - self.h_.prefix);
     257           26 :         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           26 :         buf_        = self.h_.buf;
     263           26 :         self.h_.buf = p;
     264           26 :         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            5 :         detail::move_chars(
     271            5 :             self.h_.buf + new_prefix_,
     272            5 :             self.h_.cbuf + self.h_.prefix,
     273            5 :             self.h_.size - self.h_.prefix,
     274              :             s0,
     275              :             s1);
     276              :     }
     277              : 
     278           31 :     self.h_.cbuf   = self.h_.buf;
     279           31 :     self.h_.size   = new_size;
     280           31 :     self.h_.prefix = new_prefix_;
     281              : }
     282              : 
     283           36 : fields_base::
     284              : prefix_op_t::
     285              : ~prefix_op_t()
     286              : {
     287           36 :     if(new_prefix_ < self_.h_.prefix)
     288              :     {
     289            5 :         std::memmove(
     290            5 :             self_.h_.buf + new_prefix_,
     291            5 :             self_.h_.cbuf + self_.h_.prefix,
     292            5 :             self_.h_.size - self_.h_.prefix);
     293              : 
     294            5 :         self_.h_.size =
     295            5 :             self_.h_.size - self_.h_.prefix + new_prefix_;
     296            5 :         self_.h_.prefix = new_prefix_;
     297              :     }
     298           31 :     else if(buf_)
     299              :     {
     300            5 :         delete[] buf_;
     301              :     }
     302           36 : }
     303              : 
     304              : //------------------------------------------------
     305              : 
     306          198 : fields_base::
     307              : fields_base(
     308          198 :     detail::kind k) noexcept
     309          198 :     : h_(k)
     310              : {
     311          198 : }
     312              : 
     313         1066 : fields_base::
     314              : fields_base(
     315              :     detail::kind k,
     316              :     void* storage,
     317         1066 :     std::size_t cap) noexcept
     318              :     : fields_base(
     319         1066 :         *detail::header::get_default(k), storage, cap)
     320              : {
     321         1066 : }
     322              : 
     323              : // copy s and parse it
     324          546 : fields_base::
     325              : fields_base(
     326              :     detail::kind k,
     327          546 :     core::string_view s)
     328          546 :     : h_(detail::empty{k})
     329              : {
     330          546 :     auto n = detail::header::count_crlf(s);
     331          546 :     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          311 :         if(n < 2)
     340            2 :             detail::throw_invalid_argument();
     341          309 :         n -= 2;
     342              :     }
     343          543 :     op_t op(*this);
     344          543 :     op.grow(s.size(), n);
     345          543 :     s.copy(h_.buf, s.size());
     346          543 :     system::error_code ec;
     347              :     // VFALCO This is using defaults?
     348          543 :     header_limits lim;
     349          543 :     h_.parse(s.size(), lim, ec);
     350          543 :     if(ec.failed())
     351            0 :         detail::throw_system_error(ec);
     352          543 : }
     353              : 
     354              : // construct a complete copy of h
     355           25 : fields_base::
     356              : fields_base(
     357           25 :     detail::header const& h)
     358           25 :     : h_(h.kind)
     359              : {
     360           25 :     if(h.is_default())
     361            9 :         return;
     362              : 
     363              :     // allocate and copy the buffer
     364           16 :     op_t op(*this);
     365           16 :     op.grow(h.size, h.count);
     366           16 :     h.assign_to(h_);
     367           16 :     std::memcpy(
     368           16 :         h_.buf, h.cbuf, h.size);
     369           16 :     h.copy_table(h_.buf + h_.cap);
     370           16 : }
     371              : 
     372              : // construct a complete copy of h
     373         1066 : fields_base::
     374              : fields_base(
     375              :     detail::header const& h,
     376              :     void* storage,
     377         1066 :     std::size_t cap)
     378         1066 :     : h_(h.kind)
     379         1066 :     , external_storage_(true)
     380              : {
     381         1066 :     h_.cbuf = static_cast<char*>(storage);
     382         1066 :     h_.buf = static_cast<char*>(storage);
     383         1066 :     h_.cap = align_down(
     384              :         storage,
     385              :         cap,
     386              :         alignof(detail::header::entry));
     387         1066 :     max_cap_ = h_.cap;
     388              : 
     389         2132 :     if(detail::header::bytes_needed(
     390         1066 :         h.size, h.count)
     391         1066 :             >= h_.cap)
     392            0 :         detail::throw_length_error();
     393              : 
     394         1066 :     h.assign_to(h_);
     395         1066 :     std::memcpy(
     396         1066 :         h_.buf, h.cbuf, h.size);
     397         1066 :     h.copy_table(h_.buf + h_.cap);
     398         1066 : }
     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         1832 : fields_base::
     409              : ~fields_base()
     410              : {
     411         1832 :     if(h_.buf && !external_storage_)
     412          676 :         delete[] h_.buf;
     413         1832 : }
     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            0 :         return;
     469              : 
     470            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            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         1814 : fields_base::
     505              : iterator::
     506              : operator*() const noexcept ->
     507              :     reference
     508              : {
     509         1814 :     BOOST_ASSERT(i_ < ph_->count);
     510              :     auto tab =
     511         1814 :         ph_->tab();
     512              :     auto const& e =
     513         1814 :         tab[i_];
     514         1814 :     auto const* p =
     515         1814 :         ph_->cbuf + ph_->prefix;
     516              :     return {
     517         1814 :         (e.id == detail::header::unknown_field)
     518         1814 :             ? optional<field>{} : e.id,
     519              :         core::string_view(
     520         1814 :             p + e.np, e.nn),
     521              :         core::string_view(
     522         1814 :             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            4 : fields_base::
     663              : exists(
     664              :     field id) const noexcept
     665              : {
     666            4 :     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           94 : fields_base::
     703              : find(field id) const noexcept ->
     704              :     iterator
     705              : {
     706           94 :     auto it = begin();
     707           94 :     auto const last = end();
     708          208 :     while(it != last)
     709              :     {
     710          199 :         if(it->id == id)
     711           85 :             break;
     712          114 :         ++it;
     713              :     }
     714           94 :     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            2 : fields_base::
     809              : value_or(
     810              :     field id,
     811              :     core::string_view s) const noexcept
     812              : {
     813            2 :     auto it = find(id);
     814            2 :     if(it != end())
     815            1 :         return it->value;
     816            1 :     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              : //------------------------------------------------
     854              : 
     855              : std::ostream&
     856            1 : operator<<(
     857              :     std::ostream& os,
     858              :     const fields_base& f)
     859              : {
     860            1 :     return os << f.buffer();
     861              : }
     862              : 
     863              : //------------------------------------------------
     864              : //
     865              : // Modifiers
     866              : //
     867              : //------------------------------------------------
     868              : 
     869              : auto
     870           30 : fields_base::
     871              : erase(
     872              :     iterator it) noexcept -> iterator
     873              : {
     874           30 :     auto const id = it->id.value_or(
     875              :         detail::header::unknown_field);
     876           30 :     raw_erase(it.i_);
     877           30 :     h_.on_erase(id);
     878           30 :     return it;
     879              : }
     880              : 
     881              : std::size_t
     882           30 : fields_base::
     883              : erase(
     884              :     field id) noexcept
     885              : {
     886           30 :     auto const i0 = h_.find(id);
     887           30 :     if(i0 == h_.count)
     888            3 :         return 0;
     889           27 :     return erase_all(i0, id);
     890              : }
     891              : 
     892              : std::size_t
     893           18 : fields_base::
     894              : erase(
     895              :     core::string_view name) noexcept
     896              : {
     897           18 :     auto const i0 = h_.find(name);
     898           18 :     if(i0 == h_.count)
     899            3 :         return 0;
     900           15 :     auto const ft = h_.tab();
     901           15 :     auto const id = ft[i0].id;
     902           15 :     if(id == detail::header::unknown_field)
     903            6 :         return erase_all(i0, name);
     904            9 :     return erase_all(i0, id);
     905              : }
     906              : 
     907              : //------------------------------------------------
     908              : 
     909              : void
     910           28 : fields_base::
     911              : set(
     912              :     iterator it,
     913              :     core::string_view value,
     914              :     system::error_code& ec)
     915              : {
     916           28 :     auto rv = verify_field_value(value);
     917           28 :     if(rv.has_error())
     918              :     {
     919            4 :         ec = rv.error();
     920            4 :         return;
     921              :     }
     922              : 
     923           24 :     value = rv->value;
     924           24 :     bool has_obs_fold = rv->has_obs_fold;
     925              : 
     926           24 :     auto const i = it.i_;
     927           24 :     auto tab = h_.tab();
     928           24 :     auto const& e0 = tab[i];
     929           24 :     auto const pos0 = offset(i);
     930           24 :     auto const pos1 = offset(i + 1);
     931              :     std::ptrdiff_t dn =
     932           24 :         value.size() -
     933           24 :         it->value.size();
     934           24 :     if( value.empty() &&
     935           24 :         ! it->value.empty())
     936            0 :         --dn; // remove SP
     937           24 :     else if(
     938           24 :         it->value.empty() &&
     939            0 :         ! value.empty())
     940            0 :         ++dn; // add SP
     941              : 
     942           24 :     op_t op(*this, &value);
     943           30 :     if( dn > 0 &&
     944           12 :         op.grow(value.size() -
     945           30 :             it->value.size(), 0))
     946              :     {
     947              :         // reallocated
     948            6 :         auto dest = h_.buf +
     949            6 :             pos0 + e0.nn + 1;
     950           12 :         std::memcpy(
     951            6 :             h_.buf,
     952            6 :             op.buf(),
     953            6 :             dest - h_.buf);
     954            6 :         if(! value.empty())
     955              :         {
     956            6 :             *dest++ = ' ';
     957            6 :             value.copy(
     958              :                 dest,
     959              :                 value.size());
     960            6 :             if( has_obs_fold )
     961            3 :                 detail::remove_obs_fold(
     962            3 :                     dest, dest + value.size());
     963            6 :             dest += value.size();
     964              :         }
     965            6 :         *dest++ = '\r';
     966            6 :         *dest++ = '\n';
     967           12 :         std::memcpy(
     968            6 :             h_.buf + pos1 + dn,
     969           12 :             op.buf() + pos1,
     970            6 :             h_.size - pos1);
     971           12 :         std::memcpy(
     972            6 :             h_.buf + h_.cap -
     973            6 :                 sizeof(entry) * h_.count,
     974            6 :             &op.tab()[h_.count - 1],
     975            6 :             sizeof(entry) * h_.count);
     976              :     }
     977              :     else
     978              :     {
     979              :         // copy the value first
     980           36 :         auto dest = h_.buf + pos0 +
     981           18 :             it->name.size() + 1;
     982           18 :         if(! value.empty())
     983              :         {
     984           18 :             *dest++ = ' ';
     985           18 :             value.copy(
     986              :                 dest,
     987              :                 value.size());
     988           18 :             if( has_obs_fold )
     989            0 :                 detail::remove_obs_fold(
     990            0 :                     dest, dest + value.size());
     991           18 :             dest += value.size();
     992              :         }
     993           18 :         op.move_chars(
     994           18 :             h_.buf + pos1 + dn,
     995           18 :             h_.buf + pos1,
     996           18 :             h_.size - pos1);
     997           18 :         *dest++ = '\r';
     998           18 :         *dest++ = '\n';
     999              :     }
    1000              :     {
    1001              :         // update tab
    1002           24 :         auto ft = h_.tab();
    1003           31 :         for(std::size_t j = h_.count - 1;
    1004           31 :                 j > i; --j)
    1005            7 :             ft[j] = ft[j] + dn;
    1006           24 :         auto& e = ft[i];
    1007           48 :         e.vp = e.np + e.nn +
    1008           24 :             1 + ! value.empty();
    1009           24 :         e.vn = static_cast<
    1010           24 :             offset_type>(value.size());
    1011           24 :         h_.size = static_cast<
    1012           24 :             offset_type>(h_.size + dn);
    1013              :     }
    1014           24 :     auto const id = it->id.value_or(
    1015              :         detail::header::unknown_field);
    1016           24 :     if(h_.is_special(id))
    1017              :     {
    1018              :         // replace first char of name
    1019              :         // with null to hide metadata
    1020            9 :         char saved = h_.buf[pos0];
    1021            9 :         auto& e = h_.tab()[i];
    1022            9 :         e.id = detail::header::unknown_field;
    1023            9 :         h_.buf[pos0] = '\0';
    1024            9 :         h_.on_erase(id);
    1025            9 :         h_.buf[pos0] = saved; // restore
    1026            9 :         e.id = id;
    1027            9 :         h_.on_insert(id, it->value);
    1028              :     }
    1029           24 : }
    1030              : 
    1031              : // erase existing fields with id
    1032              : // and then add the field with value
    1033              : void
    1034          110 : fields_base::
    1035              : set(
    1036              :     field id,
    1037              :     core::string_view value,
    1038              :     system::error_code& ec)
    1039              : {
    1040          110 :     auto rv = verify_field_value(value);
    1041          110 :     if(rv.has_error())
    1042              :     {
    1043            4 :         ec = rv.error();
    1044            4 :         return;
    1045              :     }
    1046              : 
    1047          106 :     auto const i0 = h_.find(id);
    1048          106 :     if(i0 != h_.count)
    1049              :     {
    1050              :         // field exists
    1051           21 :         auto const ft = h_.tab();
    1052              :         {
    1053              :             // provide strong guarantee
    1054              :             auto const n0 =
    1055           21 :                 h_.size - length(i0);
    1056              :             auto const n =
    1057           21 :                 ft[i0].nn + 2 +
    1058           21 :                     rv->value.size() + 2;
    1059              :             // VFALCO missing overflow check
    1060           21 :             reserve_bytes(n0 + n);
    1061              :         }
    1062           21 :         erase_all(i0, id);
    1063              :     }
    1064              : 
    1065          106 :     insert_unchecked(
    1066              :         id,
    1067              :         to_string(id),
    1068          106 :         rv->value,
    1069          106 :         h_.count,
    1070          106 :         rv->has_obs_fold);
    1071              : }
    1072              : 
    1073              : // erase existing fields with name
    1074              : // and then add the field with value
    1075              : void
    1076           32 : fields_base::
    1077              : set(
    1078              :     core::string_view name,
    1079              :     core::string_view value,
    1080              :     system::error_code& ec)
    1081              : {
    1082           32 :     verify_field_name(name , ec);
    1083           32 :     if(ec.failed())
    1084            8 :         return;
    1085              : 
    1086           28 :     auto rv = verify_field_value(value);
    1087           28 :     if(rv.has_error())
    1088              :     {
    1089            4 :         ec = rv.error();
    1090            4 :         return;
    1091              :     }
    1092              : 
    1093           24 :     auto const i0 = h_.find(name);
    1094           24 :     if(i0 != h_.count)
    1095              :     {
    1096              :         // field exists
    1097           18 :         auto const ft = h_.tab();
    1098           18 :         auto const id = ft[i0].id;
    1099              :         {
    1100              :             // provide strong guarantee
    1101              :             auto const n0 =
    1102           18 :                 h_.size - length(i0);
    1103              :             auto const n =
    1104           18 :                 ft[i0].nn + 2 +
    1105           18 :                     rv->value.size() + 2;
    1106              :             // VFALCO missing overflow check
    1107           18 :             reserve_bytes(n0 + n);
    1108              :         }
    1109              :         // VFALCO simple algorithm but
    1110              :         // costs one extra memmove
    1111           18 :         if(id != detail::header::unknown_field)
    1112           15 :             erase_all(i0, id);
    1113              :         else
    1114            3 :             erase_all(i0, name);
    1115              :     }
    1116           24 :     insert_unchecked(
    1117              :         string_to_field(name),
    1118              :         name,
    1119           24 :         rv->value,
    1120           24 :         h_.count,
    1121           24 :         rv->has_obs_fold);
    1122              : }
    1123              : 
    1124              : auto
    1125           26 : fields_base::
    1126              : insert(
    1127              :     iterator before,
    1128              :     field id,
    1129              :     core::string_view value)
    1130              :     -> iterator
    1131              : {
    1132           26 :     system::error_code ec;
    1133           26 :     auto const it = insert(before, id, value, ec);
    1134           26 :     if(ec.failed())
    1135            1 :         detail::throw_system_error(ec);
    1136           25 :     return it;
    1137              : }
    1138              : 
    1139              : auto
    1140           33 : fields_base::
    1141              : insert(
    1142              :     iterator before,
    1143              :     field id,
    1144              :     core::string_view value,
    1145              :     system::error_code& ec)
    1146              :     -> iterator
    1147              : {
    1148           33 :     insert_impl(
    1149              :         id,
    1150              :         to_string(id),
    1151              :         value,
    1152              :         before.i_, ec);
    1153           33 :     return before;
    1154              : }
    1155              : 
    1156              : auto
    1157           13 : fields_base::
    1158              : insert(
    1159              :     iterator before,
    1160              :     core::string_view name,
    1161              :     core::string_view value)
    1162              :     -> iterator
    1163              : {
    1164           13 :     system::error_code ec;
    1165           13 :     insert(before, name, value, ec);
    1166           13 :     if(ec.failed())
    1167            1 :         detail::throw_system_error(ec);
    1168           12 :     return before;
    1169              : }
    1170              : 
    1171              : auto
    1172           16 : fields_base::
    1173              : insert(
    1174              :     iterator before,
    1175              :     core::string_view name,
    1176              :     core::string_view value,
    1177              :     system::error_code& ec)
    1178              :     -> iterator
    1179              : {
    1180           16 :     insert_impl(
    1181              :         string_to_field(name),
    1182              :         name,
    1183              :         value,
    1184              :         before.i_,
    1185              :         ec);
    1186           16 :     return before;
    1187              : }
    1188              : 
    1189              : void
    1190           23 : fields_base::
    1191              : set(
    1192              :     iterator it,
    1193              :     core::string_view value)
    1194              : {
    1195           23 :     system::error_code ec;
    1196           23 :     set(it, value, ec);
    1197           23 :     if(ec.failed())
    1198            2 :         detail::throw_system_error(ec);
    1199           21 : }
    1200              : 
    1201              : //------------------------------------------------
    1202              : //
    1203              : // (implementation)
    1204              : //
    1205              : //------------------------------------------------
    1206              : 
    1207              : // copy start line and fields
    1208              : void
    1209           16 : fields_base::
    1210              : copy_impl(
    1211              :     detail::header const& h)
    1212              : {
    1213           16 :     BOOST_ASSERT(
    1214              :         h.kind == h_.kind);
    1215              : 
    1216              :     auto const n =
    1217           16 :         detail::header::bytes_needed(
    1218           16 :             h.size, h.count);
    1219           16 :     if(n <= h_.cap && (!h.is_default() || external_storage_))
    1220              :     {
    1221              :         // no realloc
    1222            8 :         h.assign_to(h_);
    1223            8 :         h.copy_table(
    1224            8 :             h_.buf + h_.cap);
    1225            8 :         std::memcpy(
    1226            8 :             h_.buf,
    1227            8 :             h.cbuf,
    1228            8 :             h.size);
    1229            8 :         return;
    1230              :     }
    1231              : 
    1232              :     // static storages cannot reallocate
    1233            8 :     if(external_storage_)
    1234            0 :         detail::throw_length_error();
    1235              : 
    1236            8 :     fields_base tmp(h);
    1237            8 :     tmp.h_.swap(h_);
    1238            8 : }
    1239              : 
    1240              : void
    1241          203 : fields_base::
    1242              : insert_impl(
    1243              :     optional<field> id,
    1244              :     core::string_view name,
    1245              :     core::string_view value,
    1246              :     std::size_t before,
    1247              :     system::error_code& ec)
    1248              : {
    1249          203 :     verify_field_name(name, ec);
    1250          203 :     if(ec.failed())
    1251           23 :         return;
    1252              : 
    1253          198 :     auto rv = verify_field_value(value);
    1254          198 :     if(rv.has_error())
    1255              :     {
    1256           18 :         ec = rv.error();
    1257           18 :         return;
    1258              :     }
    1259              : 
    1260          180 :     insert_unchecked(
    1261              :         id,
    1262              :         name,
    1263          180 :         rv->value,
    1264              :         before,
    1265          180 :         rv->has_obs_fold);
    1266              : }
    1267              : 
    1268              : void
    1269          310 : fields_base::
    1270              : insert_unchecked(
    1271              :     optional<field> id,
    1272              :     core::string_view name,
    1273              :     core::string_view value,
    1274              :     std::size_t before,
    1275              :     bool has_obs_fold)
    1276              : {
    1277          310 :     auto const tab0 = h_.tab_();
    1278          310 :     auto const pos = offset(before);
    1279              :     auto const n =
    1280          310 :         name.size() +       // name
    1281          310 :         1 +                 // ':'
    1282          310 :         ! value.empty() +   // [SP]
    1283          310 :         value.size() +      // value
    1284          310 :         2;                  // CRLF
    1285              : 
    1286          310 :     op_t op(*this, &name, &value);
    1287          310 :     if(op.grow(n, 1))
    1288              :     {
    1289              :         // reallocated
    1290          218 :         if(pos > 0)
    1291          197 :             std::memcpy(
    1292          197 :                 h_.buf,
    1293          197 :                 op.cbuf(),
    1294              :                 pos);
    1295          218 :         if(before > 0)
    1296          178 :             std::memcpy(
    1297           89 :                 h_.tab_() - before,
    1298           89 :                 tab0 - before,
    1299              :                 before * sizeof(entry));
    1300          436 :         std::memcpy(
    1301          218 :             h_.buf + pos + n,
    1302          218 :             op.cbuf() + pos,
    1303          218 :             h_.size - pos);
    1304              :     }
    1305              :     else
    1306              :     {
    1307           85 :         op.move_chars(
    1308           85 :             h_.buf + pos + n,
    1309           85 :             h_.buf + pos,
    1310           85 :             h_.size - pos);
    1311              :     }
    1312              : 
    1313              :     // serialize
    1314              :     {
    1315          303 :         auto dest = h_.buf + pos;
    1316          303 :         name.copy(dest, name.size());
    1317          303 :         dest += name.size();
    1318          303 :         *dest++ = ':';
    1319          303 :         if(! value.empty())
    1320              :         {
    1321          291 :             *dest++ = ' ';
    1322          291 :             value.copy(
    1323              :                 dest, value.size());
    1324          291 :             if( has_obs_fold )
    1325           18 :                 detail::remove_obs_fold(
    1326           18 :                     dest, dest + value.size());
    1327          291 :             dest += value.size();
    1328              :         }
    1329          303 :         *dest++ = '\r';
    1330          303 :         *dest = '\n';
    1331              :     }
    1332              : 
    1333              :     // update table
    1334          303 :     auto const tab = h_.tab_();
    1335              :     {
    1336          303 :         auto i = h_.count - before;
    1337          303 :         if(i > 0)
    1338              :         {
    1339           43 :             auto p0 = tab0 - h_.count;
    1340           43 :             auto p = tab - h_.count - 1;
    1341              :             do
    1342              :             {
    1343           80 :                 *p++ = *p0++ + n;
    1344              :             }
    1345           80 :             while(--i);
    1346              :         }
    1347              :     }
    1348          303 :     auto& e = tab[0 - static_cast<std::ptrdiff_t>(before) - 1];
    1349          303 :     e.np = static_cast<offset_type>(
    1350          303 :         pos - h_.prefix);
    1351          303 :     e.nn = static_cast<
    1352          303 :         offset_type>(name.size());
    1353          303 :     e.vp = static_cast<offset_type>(
    1354          606 :         pos - h_.prefix +
    1355          303 :             name.size() + 1 +
    1356          303 :             ! value.empty());
    1357          303 :     e.vn = static_cast<
    1358          303 :         offset_type>(value.size());
    1359          303 :     e.id = id.value_or(
    1360              :         detail::header::unknown_field);
    1361              : 
    1362              :     // update container
    1363          303 :     h_.count++;
    1364          303 :     h_.size = static_cast<
    1365          303 :         offset_type>(h_.size + n);
    1366          303 :     h_.on_insert(e.id, value);
    1367          310 : }
    1368              : 
    1369              : void
    1370          169 : fields_base::
    1371              : raw_erase(
    1372              :     std::size_t i) noexcept
    1373              : {
    1374          169 :     BOOST_ASSERT(i < h_.count);
    1375          169 :     BOOST_ASSERT(h_.buf != nullptr);
    1376          169 :     auto const p0 = offset(i);
    1377          169 :     auto const p1 = offset(i + 1);
    1378          169 :     std::memmove(
    1379          169 :         h_.buf + p0,
    1380          169 :         h_.buf + p1,
    1381          169 :         h_.size - p1);
    1382          169 :     auto const n = p1 - p0;
    1383          169 :     --h_.count;
    1384          169 :     auto ft = h_.tab();
    1385          270 :     for(;i < h_.count; ++i)
    1386          101 :         ft[i] = ft[i + 1] - n;
    1387          169 :     h_.size = static_cast<
    1388          169 :         offset_type>(h_.size - n);
    1389          169 : }
    1390              : 
    1391              : // erase n fields matching id
    1392              : // without updating metadata
    1393              : void
    1394            4 : fields_base::
    1395              : raw_erase_n(
    1396              :     field id,
    1397              :     std::size_t n) noexcept
    1398              : {
    1399              :     // iterate in reverse
    1400            4 :     auto e = &h_.tab()[h_.count];
    1401            4 :     auto const e0 = &h_.tab()[0];
    1402           10 :     while(n > 0)
    1403              :     {
    1404            6 :         BOOST_ASSERT(e != e0);
    1405            6 :         ++e; // decrement
    1406            6 :         if(e->id == id)
    1407              :         {
    1408            5 :             raw_erase(e0 - e);
    1409            5 :             --n;
    1410              :         }
    1411              :     }
    1412            4 : }
    1413              : 
    1414              : // erase all fields with id
    1415              : // and update metadata
    1416              : std::size_t
    1417           72 : fields_base::
    1418              : erase_all(
    1419              :     std::size_t i0,
    1420              :     field id) noexcept
    1421              : {
    1422           72 :     BOOST_ASSERT(
    1423              :         id != detail::header::unknown_field);
    1424           72 :     std::size_t n = 1;
    1425           72 :     std::size_t i = h_.count - 1;
    1426           72 :     auto const ft = h_.tab();
    1427          149 :     while(i > i0)
    1428              :     {
    1429           77 :         if(ft[i].id == id)
    1430              :         {
    1431           44 :             raw_erase(i);
    1432           44 :             ++n;
    1433              :         }
    1434              :         // go backwards to
    1435              :         // reduce memmoves
    1436           77 :         --i;
    1437              :     }
    1438           72 :     raw_erase(i0);
    1439           72 :     h_.on_erase_all(id);
    1440           72 :     return n;
    1441              : }
    1442              : 
    1443              : // erase all fields with name
    1444              : // when id == detail::header::unknown_field
    1445              : std::size_t
    1446            9 : fields_base::
    1447              : erase_all(
    1448              :     std::size_t i0,
    1449              :     core::string_view name) noexcept
    1450              : {
    1451            9 :     std::size_t n = 1;
    1452            9 :     std::size_t i = h_.count - 1;
    1453            9 :     auto const ft = h_.tab();
    1454            9 :     auto const* p = h_.cbuf + h_.prefix;
    1455           36 :     while(i > i0)
    1456              :     {
    1457              :         core::string_view s(
    1458           27 :             p + ft[i].np, ft[i].nn);
    1459           27 :         if(s == name)
    1460              :         {
    1461            9 :             raw_erase(i);
    1462            9 :             ++n;
    1463              :         }
    1464              :         // go backwards to
    1465              :         // reduce memmoves
    1466           27 :         --i;
    1467              :     }
    1468            9 :     raw_erase(i0);
    1469            9 :     return n;
    1470              : }
    1471              : 
    1472              : // return i-th field absolute offset
    1473              : std::size_t
    1474          774 : fields_base::
    1475              : offset(
    1476              :     std::size_t i) const noexcept
    1477              : {
    1478          774 :     if(i == 0)
    1479          310 :         return h_.prefix;
    1480          464 :     if(i < h_.count)
    1481          219 :         return h_.prefix + h_.tab()[i].np;
    1482              :     // make final CRLF the last "field"
    1483          245 :     return h_.size - 2;
    1484              : }
    1485              : 
    1486              : // return i-th field absolute length
    1487              : std::size_t
    1488           39 : fields_base::
    1489              : length(
    1490              :     std::size_t i) const noexcept
    1491              : {
    1492              :     return
    1493           39 :         offset(i + 1) -
    1494           39 :         offset(i);
    1495              : }
    1496              : 
    1497              : } // http_proto
    1498              : } // boost
        

Generated by: LCOV version 2.1