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: 388 417 93.0 %
Date: 2024-02-07 16:12:48 Functions: 31 35 88.6 %

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

Generated by: LCOV version 1.15