LCOV - code coverage report
Current view: top level - libs/http_proto/src/fields_base.cpp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 95.4 % 497 474
Test Date: 2024-05-23 18:56:44 Functions: 88.1 % 42 37

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

Generated by: LCOV version 2.1