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.1 % 696 676
Test Date: 2025-12-18 06:34:36 Functions: 98.6 % 69 68

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

Generated by: LCOV version 2.1