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

Generated by: LCOV version 1.15