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: 93.9 % 611 574
Test Date: 2025-06-18 09:40:26 Functions: 84.3 % 51 43

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

Generated by: LCOV version 2.1