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: 444 476 93.3 %
Date: 2024-03-04 19:17:05 Functions: 34 38 89.5 %

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

Generated by: LCOV version 1.15