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

Generated by: LCOV version 1.15