LCOV - code coverage report
Current view: top level - libs/http_proto/src - fields_base.cpp (source / functions) Hit Total Coverage
Test: coverage_filtered.info Lines: 470 493 95.3 %
Date: 2024-04-22 17:18:35 Functions: 37 42 88.1 %

          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             :         , s0_(s0)
     102         907 :         , s1_(s1)
     103             :     {
     104         907 :     }
     105             : 
     106         907 :     ~op_t()
     107         907 :     {
     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        1064 :     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          36 :     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             : }
     377             : 
     378             : //------------------------------------------------
     379             : 
     380         702 : fields_base::
     381         716 : ~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         135 :     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             : }
     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           8 :     fields_base tmp(h_);
     439           4 :     tmp.h_.swap(h_);
     440             : }
     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          42 :     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             : }
     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          20 :     fields_base tmp(h);
     797          10 :     tmp.h_.swap(h_);
     798             : }
     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         466 :     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          54 :             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         228 : }
     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 1.15