LCOV - code coverage report
Current view: top level - libs/url/src/url_base.cpp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 99.6 % 1372 1367
Test Date: 2024-08-19 20:08:54 Functions: 100.0 % 74 74

            Line data    Source code
       1              : //
       2              : // Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
       3              : // Copyright (c) 2022 Alan de Freitas (alandefreitas@gmail.com)
       4              : //
       5              : // Distributed under the Boost Software License, Version 1.0. (See accompanying
       6              : // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
       7              : //
       8              : // Official repository: https://github.com/boostorg/url
       9              : //
      10              : 
      11              : 
      12              : #include <boost/url/detail/config.hpp>
      13              : #include <boost/url/url_base.hpp>
      14              : #include <boost/url/encode.hpp>
      15              : #include <boost/url/error.hpp>
      16              : #include <boost/url/host_type.hpp>
      17              : #include <boost/url/scheme.hpp>
      18              : #include <boost/url/url_view.hpp>
      19              : #include <boost/url/detail/any_params_iter.hpp>
      20              : #include <boost/url/detail/any_segments_iter.hpp>
      21              : #include "detail/decode.hpp"
      22              : #include <boost/url/detail/encode.hpp>
      23              : #include <boost/url/detail/except.hpp>
      24              : #include "detail/normalize.hpp"
      25              : #include "detail/path.hpp"
      26              : #include "detail/print.hpp"
      27              : #include <boost/url/grammar/ci_string.hpp>
      28              : #include <boost/url/rfc/authority_rule.hpp>
      29              : #include <boost/url/rfc/query_rule.hpp>
      30              : #include "rfc/detail/charsets.hpp"
      31              : #include "rfc/detail/host_rule.hpp"
      32              : #include "rfc/detail/ipvfuture_rule.hpp"
      33              : #include "boost/url/rfc/detail/path_rules.hpp"
      34              : #include "rfc/detail/port_rule.hpp"
      35              : #include "rfc/detail/scheme_rule.hpp"
      36              : #include "rfc/detail/userinfo_rule.hpp"
      37              : #include <boost/url/grammar/parse.hpp>
      38              : #include "detail/move_chars.hpp"
      39              : #include <cstring>
      40              : #include <iostream>
      41              : #include <stdexcept>
      42              : #include <utility>
      43              : 
      44              : namespace boost {
      45              : namespace urls {
      46              : 
      47              : //------------------------------------------------
      48              : 
      49              : // these objects help handle the cases
      50              : // where the user passes in strings that
      51              : // come from inside the url buffer.
      52              : 
      53         8039 : url_base::
      54              : op_t::
      55              : ~op_t()
      56              : {
      57         8039 :     if(old)
      58         1009 :         u.cleanup(*this);
      59         8039 :     u.check_invariants();
      60         8039 : }
      61              : 
      62         8039 : url_base::
      63              : op_t::
      64              : op_t(
      65              :     url_base& impl_,
      66              :     core::string_view* s0_,
      67         8039 :     core::string_view* s1_) noexcept
      68         8039 :     : u(impl_)
      69         8039 :     , s0(s0_)
      70         8039 :     , s1(s1_)
      71              : {
      72         8039 :     u.check_invariants();
      73         8039 : }
      74              : 
      75              : void
      76         2326 : url_base::
      77              : op_t::
      78              : move(
      79              :     char* dest,
      80              :     char const* src,
      81              :     std::size_t n) noexcept
      82              : {
      83         2326 :     if(! n)
      84          402 :         return;
      85         1924 :     if(s0)
      86              :     {
      87         1226 :         if(s1)
      88           62 :             return detail::move_chars(
      89           62 :              dest, src, n, *s0, *s1);
      90         1164 :         return detail::move_chars(
      91         1164 :             dest, src, n, *s0);
      92              :     }
      93          698 :     detail::move_chars(
      94              :         dest, src, n);
      95              : }
      96              : 
      97              : //------------------------------------------------
      98              : 
      99              : // construct reference
     100         1500 : url_base::
     101              : url_base(
     102         1500 :     detail::url_impl const& impl) noexcept
     103         1500 :     : url_view_base(impl)
     104              : {
     105         1500 : }
     106              : 
     107              : void
     108          150 : url_base::
     109              : reserve_impl(std::size_t n)
     110              : {
     111          150 :     op_t op(*this);
     112          150 :     reserve_impl(n, op);
     113          149 :     if(s_)
     114          147 :         s_[size()] = '\0';
     115          150 : }
     116              : 
     117              : // make a copy of u
     118              : void
     119         3666 : url_base::
     120              : copy(url_view_base const& u)
     121              : {
     122         3666 :     if (this == &u)
     123          117 :         return;
     124         3666 :     op_t op(*this);
     125         3666 :     if(u.size() == 0)
     126              :     {
     127          117 :         clear();
     128          117 :         return;
     129              :     }
     130         3549 :     reserve_impl(
     131              :         u.size(), op);
     132         3546 :     impl_ = u.impl_;
     133         3546 :     impl_.cs_ = s_;
     134         3546 :     impl_.from_ = {from::url};
     135         3546 :     std::memcpy(s_,
     136         3546 :         u.data(), u.size());
     137         3546 :     s_[size()] = '\0';
     138         3666 : }
     139              : 
     140              : //------------------------------------------------
     141              : //
     142              : // Scheme
     143              : //
     144              : //------------------------------------------------
     145              : 
     146              : url_base&
     147           52 : url_base::
     148              : set_scheme(core::string_view s)
     149              : {
     150           52 :     set_scheme_impl(
     151              :         s, string_to_scheme(s));
     152           39 :     return *this;
     153              : }
     154              : 
     155              : url_base&
     156           11 : url_base::
     157              : set_scheme_id(urls::scheme id)
     158              : {
     159           11 :     if(id == urls::scheme::unknown)
     160            1 :         detail::throw_invalid_argument();
     161           10 :     if(id == urls::scheme::none)
     162            1 :         return remove_scheme();
     163            9 :     set_scheme_impl(to_string(id), id);
     164            9 :     return *this;
     165              : }
     166              : 
     167              : url_base&
     168           36 : url_base::
     169              : remove_scheme()
     170              : {
     171           36 :     op_t op(*this);
     172           36 :     auto const sn = impl_.len(id_scheme);
     173           36 :     if(sn == 0)
     174            9 :         return *this;
     175           27 :     auto const po = impl_.offset(id_path);
     176           27 :     auto fseg = first_segment();
     177              :     bool const encode_colon =
     178           27 :         !has_authority() &&
     179           20 :         impl_.nseg_ > 0 &&
     180           58 :         s_[po] != '/' &&
     181           11 :         fseg.contains(':');
     182           27 :     if(!encode_colon)
     183              :     {
     184              :         // just remove the scheme
     185           18 :         resize_impl(id_scheme, 0, op);
     186           18 :         impl_.scheme_ = urls::scheme::none;
     187           18 :         check_invariants();
     188           18 :         return *this;
     189              :     }
     190              :     // encode any ":" in the first path segment
     191            9 :     BOOST_ASSERT(sn >= 2);
     192            9 :     auto pn = impl_.len(id_path);
     193            9 :     std::size_t cn = 0;
     194           46 :     for (char c: fseg)
     195           37 :         cn += c == ':';
     196              :     std::size_t new_size =
     197            9 :         size() - sn + 2 * cn;
     198            9 :     bool need_resize = new_size > size();
     199            9 :     if (need_resize)
     200              :     {
     201            1 :         resize_impl(
     202            1 :             id_path, pn + 2 * cn, op);
     203              :     }
     204              :     // move [id_scheme, id_path) left
     205            9 :     op.move(
     206              :         s_,
     207            9 :         s_ + sn,
     208              :         po - sn);
     209              :     // move [id_path, id_query) left
     210            9 :     auto qo = impl_.offset(id_query);
     211            9 :     op.move(
     212            9 :         s_ + po - sn,
     213            9 :         s_ + po,
     214              :         qo - po);
     215              :     // move [id_query, id_end) left
     216            9 :     op.move(
     217            9 :         s_ + qo - sn + 2 * cn,
     218            9 :         s_ + qo,
     219            9 :         impl_.offset(id_end) - qo);
     220              : 
     221              :     // adjust part offsets.
     222              :     // (po and qo are invalidated)
     223            9 :     if (need_resize)
     224              :     {
     225            1 :         impl_.adjust_left(id_user, id_end, sn);
     226              :     }
     227              :     else
     228              :     {
     229            8 :         impl_.adjust_left(id_user, id_path, sn);
     230            8 :         impl_.adjust_left(id_query, id_end, sn - 2 * cn);
     231              :     }
     232            9 :     if (encode_colon)
     233              :     {
     234              :         // move the 2nd, 3rd, ... segments
     235            9 :         auto begin = s_ + impl_.offset(id_path);
     236            9 :         auto it = begin;
     237            9 :         auto end = begin + pn;
     238           46 :         while (*it != '/' &&
     239              :                it != end)
     240           37 :             ++it;
     241              :         // we don't need op here because this is
     242              :         // an internal operation
     243            9 :         std::memmove(it + (2 * cn), it, end - it);
     244              : 
     245              :         // move 1st segment
     246            9 :         auto src = s_ + impl_.offset(id_path) + pn;
     247            9 :         auto dest = s_ + impl_.offset(id_query);
     248            9 :         src -= end - it;
     249            9 :         dest -= end - it;
     250            9 :         pn -= end - it;
     251              :         do {
     252           37 :             --src;
     253           37 :             --dest;
     254           37 :             if (*src != ':')
     255              :             {
     256           25 :                 *dest = *src;
     257              :             }
     258              :             else
     259              :             {
     260              :                 // use uppercase as required by
     261              :                 // syntax-based normalization
     262           12 :                 *dest-- = 'A';
     263           12 :                 *dest-- = '3';
     264           12 :                 *dest = '%';
     265              :             }
     266           37 :             --pn;
     267           37 :         } while (pn);
     268              :     }
     269            9 :     s_[size()] = '\0';
     270            9 :     impl_.scheme_ = urls::scheme::none;
     271            9 :     return *this;
     272           36 : }
     273              : 
     274              : //------------------------------------------------
     275              : //
     276              : // Authority
     277              : //
     278              : //------------------------------------------------
     279              : 
     280              : url_base&
     281          112 : url_base::
     282              : set_encoded_authority(
     283              :     pct_string_view s)
     284              : {
     285          112 :     op_t op(*this, &detail::ref(s));
     286          113 :     authority_view a = grammar::parse(
     287              :         s, authority_rule
     288          113 :             ).value(BOOST_URL_POS);
     289          111 :     auto n = s.size() + 2;
     290              :     auto const need_slash =
     291          133 :         ! is_path_absolute() &&
     292           22 :         impl_.len(id_path) > 0;
     293          111 :     if(need_slash)
     294            2 :         ++n;
     295          111 :     auto dest = resize_impl(
     296              :         id_user, id_path, n, op);
     297          111 :     dest[0] = '/';
     298          111 :     dest[1] = '/';
     299          111 :     std::memcpy(dest + 2,
     300          111 :         s.data(), s.size());
     301          111 :     if(need_slash)
     302            2 :         dest[n - 1] = '/';
     303          111 :     impl_.apply_authority(a);
     304          111 :     if(need_slash)
     305            2 :         impl_.adjust_right(
     306              :                 id_query, id_end, 1);
     307          111 :     return *this;
     308          112 : }
     309              : 
     310              : url_base&
     311           57 : url_base::
     312              : remove_authority()
     313              : {
     314           57 :     if(! has_authority())
     315           30 :         return *this;
     316              : 
     317           27 :     op_t op(*this);
     318           27 :     auto path = impl_.get(id_path);
     319           27 :     bool const need_dot = path.starts_with("//");
     320           27 :     if(need_dot)
     321              :     {
     322              :         // prepend "/.", can't throw
     323            4 :         auto p = resize_impl(
     324              :             id_user, id_path, 2, op);
     325            4 :         p[0] = '/';
     326            4 :         p[1] = '.';
     327            4 :         impl_.split(id_user, 0);
     328            4 :         impl_.split(id_pass, 0);
     329            4 :         impl_.split(id_host, 0);
     330            4 :         impl_.split(id_port, 0);
     331              :     }
     332              :     else
     333              :     {
     334           23 :         resize_impl(
     335              :             id_user, id_path, 0, op);
     336              :     }
     337           27 :     impl_.host_type_ =
     338              :         urls::host_type::none;
     339           27 :     return *this;
     340           27 : }
     341              : 
     342              : //------------------------------------------------
     343              : //
     344              : // Userinfo
     345              : //
     346              : //------------------------------------------------
     347              : 
     348              : url_base&
     349           47 : url_base::
     350              : set_userinfo(
     351              :     core::string_view s)
     352              : {
     353           47 :     op_t op(*this, &s);
     354           47 :     encoding_opts opt;
     355           47 :     auto const n = encoded_size(
     356              :         s, detail::userinfo_chars, opt);
     357           47 :     auto dest = set_userinfo_impl(n, op);
     358           47 :     encode(
     359              :         dest,
     360              :         n,
     361              :         s,
     362              :         detail::userinfo_chars,
     363              :         opt);
     364           47 :     auto const pos = impl_.get(
     365              :         id_user, id_host
     366           47 :             ).find_first_of(':');
     367           47 :     if(pos != core::string_view::npos)
     368              :     {
     369            9 :         impl_.split(id_user, pos);
     370              :         // find ':' in plain string
     371              :         auto const pos2 =
     372            9 :             s.find_first_of(':');
     373            9 :         impl_.decoded_[id_user] =
     374            9 :             pos2 - 1;
     375            9 :         impl_.decoded_[id_pass] =
     376            9 :             s.size() - pos2;
     377              :     }
     378              :     else
     379              :     {
     380           38 :         impl_.decoded_[id_user] = s.size();
     381           38 :         impl_.decoded_[id_pass] = 0;
     382              :     }
     383           47 :     return *this;
     384           47 : }
     385              : 
     386              : url_base&
     387           52 : url_base::
     388              : set_encoded_userinfo(
     389              :     pct_string_view s)
     390              : {
     391           52 :     op_t op(*this, &detail::ref(s));
     392           52 :     auto const pos = s.find_first_of(':');
     393           52 :     if(pos != core::string_view::npos)
     394              :     {
     395              :         // user:pass
     396            7 :         auto const s0 = s.substr(0, pos);
     397            7 :         auto const s1 = s.substr(pos + 1);
     398              :         auto const n0 =
     399            7 :             detail::re_encoded_size_unsafe(
     400              :                 s0,
     401              :                 detail::user_chars);
     402              :         auto const n1 =
     403            7 :             detail::re_encoded_size_unsafe(s1,
     404              :                 detail::password_chars);
     405              :         auto dest =
     406            7 :             set_userinfo_impl(n0 + n1 + 1, op);
     407            7 :         impl_.decoded_[id_user] =
     408            7 :             detail::re_encode_unsafe(
     409              :                 dest,
     410            7 :                 dest + n0,
     411              :                 s0,
     412              :                 detail::user_chars);
     413            7 :         *dest++ = ':';
     414            7 :         impl_.decoded_[id_pass] =
     415            7 :             detail::re_encode_unsafe(
     416              :                 dest,
     417            7 :                 dest + n1,
     418              :                 s1,
     419              :                 detail::password_chars);
     420            7 :         impl_.split(id_user, 2 + n0);
     421              :     }
     422              :     else
     423              :     {
     424              :         // user
     425              :         auto const n =
     426           45 :             detail::re_encoded_size_unsafe(
     427              :                 s, detail::user_chars);
     428           45 :         auto dest = set_userinfo_impl(n, op);
     429           45 :         impl_.decoded_[id_user] =
     430           45 :             detail::re_encode_unsafe(
     431              :                 dest,
     432           45 :                 dest + n,
     433              :                 s,
     434              :                 detail::user_chars);
     435           45 :         impl_.split(id_user, 2 + n);
     436           45 :         impl_.decoded_[id_pass] = 0;
     437              :     }
     438           52 :     return *this;
     439           52 : }
     440              : 
     441              : url_base&
     442           22 : url_base::
     443              : remove_userinfo() noexcept
     444              : {
     445           22 :     if(impl_.len(id_pass) == 0)
     446            6 :         return *this; // no userinfo
     447              : 
     448           16 :     op_t op(*this);
     449              :     // keep authority '//'
     450           16 :     resize_impl(
     451              :         id_user, id_host, 2, op);
     452           16 :     impl_.decoded_[id_user] = 0;
     453           16 :     impl_.decoded_[id_pass] = 0;
     454           16 :     return *this;
     455           16 : }
     456              : 
     457              : //------------------------------------------------
     458              : 
     459              : url_base&
     460           50 : url_base::
     461              : set_user(core::string_view s)
     462              : {
     463           50 :     op_t op(*this, &s);
     464           50 :     encoding_opts opt;
     465           50 :     auto const n = encoded_size(
     466              :         s, detail::user_chars, opt);
     467           50 :     auto dest = set_user_impl(n, op);
     468           50 :     encode_unsafe(
     469              :         dest,
     470              :         n,
     471              :         s,
     472              :         detail::user_chars,
     473              :         opt);
     474           50 :     impl_.decoded_[id_user] = s.size();
     475           50 :     return *this;
     476           50 : }
     477              : 
     478              : url_base&
     479           43 : url_base::
     480              : set_encoded_user(
     481              :     pct_string_view s)
     482              : {
     483           43 :     op_t op(*this, &detail::ref(s));
     484              :     auto const n =
     485           43 :         detail::re_encoded_size_unsafe(
     486              :             s, detail::user_chars);
     487           43 :     auto dest = set_user_impl(n, op);
     488           43 :     impl_.decoded_[id_user] =
     489           43 :         detail::re_encode_unsafe(
     490              :             dest,
     491           43 :             dest + n,
     492              :             s,
     493              :             detail::user_chars);
     494           43 :     BOOST_ASSERT(
     495              :         impl_.decoded_[id_user] ==
     496              :             s.decoded_size());
     497           43 :     return *this;
     498           43 : }
     499              : 
     500              : //------------------------------------------------
     501              : 
     502              : url_base&
     503           37 : url_base::
     504              : set_password(core::string_view s)
     505              : {
     506           37 :     op_t op(*this, &s);
     507           37 :     encoding_opts opt;
     508           37 :     auto const n = encoded_size(
     509              :         s, detail::password_chars, opt);
     510           37 :     auto dest = set_password_impl(n, op);
     511           37 :     encode_unsafe(
     512              :         dest,
     513              :         n,
     514              :         s,
     515              :         detail::password_chars,
     516              :         opt);
     517           37 :     impl_.decoded_[id_pass] = s.size();
     518           37 :     return *this;
     519           37 : }
     520              : 
     521              : url_base&
     522           39 : url_base::
     523              : set_encoded_password(
     524              :     pct_string_view s)
     525              : {
     526           39 :     op_t op(*this, &detail::ref(s));
     527              :     auto const n =
     528           39 :         detail::re_encoded_size_unsafe(
     529              :             s,
     530              :             detail::password_chars);
     531           39 :     auto dest = set_password_impl(n, op);
     532           39 :     impl_.decoded_[id_pass] =
     533           39 :         detail::re_encode_unsafe(
     534              :             dest,
     535           39 :             dest + n,
     536              :             s,
     537              :             detail::password_chars);
     538           39 :     BOOST_ASSERT(
     539              :         impl_.decoded_[id_pass] ==
     540              :             s.decoded_size());
     541           39 :     return *this;
     542           39 : }
     543              : 
     544              : url_base&
     545           19 : url_base::
     546              : remove_password() noexcept
     547              : {
     548           19 :     auto const n = impl_.len(id_pass);
     549           19 :     if(n < 2)
     550           12 :         return *this; // no password
     551              : 
     552            7 :     op_t op(*this);
     553              :     // clear password, retain '@'
     554              :     auto dest =
     555            7 :         resize_impl(id_pass, 1, op);
     556            7 :     dest[0] = '@';
     557            7 :     impl_.decoded_[id_pass] = 0;
     558            7 :     return *this;
     559            7 : }
     560              : 
     561              : //------------------------------------------------
     562              : //
     563              : // Host
     564              : //
     565              : //------------------------------------------------
     566              : /*
     567              : host_type       host_type()                 // ipv4, ipv6, ipvfuture, name
     568              : 
     569              : std::string     host()                      // return encoded_host().decode()
     570              : pct_string_view encoded_host()              // return host part, as-is
     571              : std::string     host_address()              // return encoded_host_address().decode()
     572              : pct_string_view encoded_host_address()      // ipv4, ipv6, ipvfut, or encoded name, no brackets
     573              : 
     574              : ipv4_address    host_ipv4_address()         // return ipv4_address or {}
     575              : ipv6_address    host_ipv6_address()         // return ipv6_address or {}
     576              : core::string_view     host_ipvfuture()            // return ipvfuture or {}
     577              : std::string     host_name()                 // return decoded name or ""
     578              : pct_string_view encoded_host_name()         // return encoded host name or ""
     579              : 
     580              : --------------------------------------------------
     581              : 
     582              : set_host( core::string_view )                     // set host part from plain text
     583              : set_encoded_host( pct_string_view )         // set host part from encoded text
     584              : set_host_address( core::string_view )             // set host from ipv4, ipv6, ipvfut, or plain reg-name string
     585              : set_encoded_host_address( pct_string_view ) // set host from ipv4, ipv6, ipvfut, or encoded reg-name string
     586              : 
     587              : set_host_ipv4( ipv4_address )               // set ipv4
     588              : set_host_ipv6( ipv6_address )               // set ipv6
     589              : set_host_ipvfuture( core::string_view )           // set ipvfuture
     590              : set_host_name( core::string_view )                // set name from plain
     591              : set_encoded_host_name( pct_string_view )    // set name from encoded
     592              : */
     593              : 
     594              : // set host part from plain text
     595              : url_base&
     596           14 : url_base::
     597              : set_host(
     598              :     core::string_view s)
     599              : {
     600           14 :     if( s.size() > 2 &&
     601           16 :         s.front() == '[' &&
     602            2 :         s.back() == ']')
     603              :     {
     604              :         // IP-literal
     605              :         {
     606              :             // IPv6-address
     607            2 :             auto rv = parse_ipv6_address(
     608            2 :                 s.substr(1, s.size() - 2));
     609            2 :             if(rv)
     610            1 :                 return set_host_ipv6(*rv);
     611              :         }
     612              :         {
     613              :             // IPvFuture
     614            1 :             auto rv = grammar::parse(
     615            1 :                 s.substr(1, s.size() - 2),
     616              :                     detail::ipvfuture_rule);
     617            1 :             if(rv)
     618            1 :                 return set_host_ipvfuture(rv->str);
     619              :         }
     620              :     }
     621           12 :     else if(s.size() >= 7) // "0.0.0.0"
     622              :     {
     623              :         // IPv4-address
     624           10 :         auto rv = parse_ipv4_address(s);
     625           10 :         if(rv)
     626            4 :             return set_host_ipv4(*rv);
     627              :     }
     628              : 
     629              :     // reg-name
     630            8 :     op_t op(*this, &s);
     631            8 :     encoding_opts opt;
     632            8 :     auto const n = encoded_size(
     633              :         s, detail::host_chars, opt);
     634            8 :     auto dest = set_host_impl(n, op);
     635            8 :     encode(
     636              :         dest,
     637            8 :         impl_.get(id_path).data() - dest,
     638              :         s,
     639              :         detail::host_chars,
     640              :         opt);
     641            8 :     impl_.decoded_[id_host] = s.size();
     642            8 :     impl_.host_type_ =
     643              :         urls::host_type::name;
     644            8 :     return *this;
     645            8 : }
     646              : 
     647              : // set host part from encoded text
     648              : url_base&
     649          115 : url_base::
     650              : set_encoded_host(
     651              :     pct_string_view s)
     652              : {
     653          115 :     if( s.size() > 2 &&
     654          131 :         s.front() == '[' &&
     655           16 :         s.back() == ']')
     656              :     {
     657              :         // IP-literal
     658              :         {
     659              :             // IPv6-address
     660           16 :             auto rv = parse_ipv6_address(
     661           16 :                 s.substr(1, s.size() - 2));
     662           16 :             if(rv)
     663            5 :                 return set_host_ipv6(*rv);
     664              :         }
     665              :         {
     666              :             // IPvFuture
     667           11 :             auto rv = grammar::parse(
     668           11 :                 s.substr(1, s.size() - 2),
     669              :                     detail::ipvfuture_rule);
     670           11 :             if(rv)
     671            1 :                 return set_host_ipvfuture(rv->str);
     672              :         }
     673              :     }
     674           99 :     else if(s.size() >= 7) // "0.0.0.0"
     675              :     {
     676              :         // IPv4-address
     677           55 :         auto rv = parse_ipv4_address(s);
     678           55 :         if(rv)
     679            5 :             return set_host_ipv4(*rv);
     680              :     }
     681              : 
     682              :     // reg-name
     683          104 :     op_t op(*this, &detail::ref(s));
     684          104 :     auto const n = detail::re_encoded_size_unsafe(
     685              :         s, detail::host_chars);
     686          104 :     auto dest = set_host_impl(n, op);
     687          104 :     impl_.decoded_[id_host] =
     688          208 :         detail::re_encode_unsafe(
     689              :             dest,
     690          104 :             impl_.get(id_path).data(),
     691              :             s,
     692              :             detail::host_chars);
     693          104 :     BOOST_ASSERT(impl_.decoded_[id_host] ==
     694              :         s.decoded_size());
     695          104 :     impl_.host_type_ =
     696              :         urls::host_type::name;
     697          104 :     return *this;
     698          104 : }
     699              : 
     700              : url_base&
     701            9 : url_base::
     702              : set_host_address(
     703              :     core::string_view s)
     704              : {
     705              :     {
     706              :         // IPv6-address
     707            9 :         auto rv = parse_ipv6_address(s);
     708            9 :         if(rv)
     709            1 :             return set_host_ipv6(*rv);
     710              :     }
     711              :     {
     712              :         // IPvFuture
     713            8 :         auto rv = grammar::parse(
     714              :             s, detail::ipvfuture_rule);
     715            8 :         if(rv)
     716            1 :             return set_host_ipvfuture(rv->str);
     717              :     }
     718            7 :     if(s.size() >= 7) // "0.0.0.0"
     719              :     {
     720              :         // IPv4-address
     721            5 :         auto rv = parse_ipv4_address(s);
     722            5 :         if(rv)
     723            2 :             return set_host_ipv4(*rv);
     724              :     }
     725              : 
     726              :     // reg-name
     727            5 :     op_t op(*this, &s);
     728            5 :     encoding_opts opt;
     729            5 :     auto const n = encoded_size(
     730              :         s, detail::host_chars, opt);
     731            5 :     auto dest = set_host_impl(n, op);
     732            5 :     encode(
     733              :         dest,
     734            5 :         impl_.get(id_path).data() - dest,
     735              :         s,
     736              :         detail::host_chars,
     737              :         opt);
     738            5 :     impl_.decoded_[id_host] = s.size();
     739            5 :     impl_.host_type_ =
     740              :         urls::host_type::name;
     741            5 :     return *this;
     742            5 : }
     743              : 
     744              : url_base&
     745            7 : url_base::
     746              : set_encoded_host_address(
     747              :     pct_string_view s)
     748              : {
     749              :     {
     750              :         // IPv6-address
     751            7 :         auto rv = parse_ipv6_address(s);
     752            7 :         if(rv)
     753            1 :             return set_host_ipv6(*rv);
     754              :     }
     755              :     {
     756              :         // IPvFuture
     757            6 :         auto rv = grammar::parse(
     758              :             s, detail::ipvfuture_rule);
     759            6 :         if(rv)
     760            1 :             return set_host_ipvfuture(rv->str);
     761              :     }
     762            5 :     if(s.size() >= 7) // "0.0.0.0"
     763              :     {
     764              :         // IPv4-address
     765            3 :         auto rv = parse_ipv4_address(s);
     766            3 :         if(rv)
     767            1 :             return set_host_ipv4(*rv);
     768              :     }
     769              : 
     770              :     // reg-name
     771            4 :     op_t op(*this, &detail::ref(s));
     772            4 :     auto const n = detail::re_encoded_size_unsafe(
     773              :         s, detail::host_chars);
     774            4 :     auto dest = set_host_impl(n, op);
     775            4 :     impl_.decoded_[id_host] =
     776            8 :         detail::re_encode_unsafe(
     777              :             dest,
     778            4 :             impl_.get(id_path).data(),
     779              :             s,
     780              :             detail::host_chars);
     781            4 :     BOOST_ASSERT(impl_.decoded_[id_host] ==
     782              :         s.decoded_size());
     783            4 :     impl_.host_type_ =
     784              :         urls::host_type::name;
     785            4 :     return *this;
     786            4 : }
     787              : 
     788              : url_base&
     789           16 : url_base::
     790              : set_host_ipv4(
     791              :     ipv4_address const& addr)
     792              : {
     793           16 :     op_t op(*this);
     794              :     char buf[urls::ipv4_address::max_str_len];
     795           16 :     auto s = addr.to_buffer(buf, sizeof(buf));
     796           16 :     auto dest = set_host_impl(s.size(), op);
     797           16 :     std::memcpy(dest, s.data(), s.size());
     798           16 :     impl_.decoded_[id_host] = impl_.len(id_host);
     799           16 :     impl_.host_type_ = urls::host_type::ipv4;
     800           16 :     auto bytes = addr.to_bytes();
     801           16 :     std::memcpy(
     802           16 :         impl_.ip_addr_,
     803           16 :         bytes.data(),
     804              :         bytes.size());
     805           16 :     return *this;
     806           16 : }
     807              : 
     808              : url_base&
     809           10 : url_base::
     810              : set_host_ipv6(
     811              :     ipv6_address const& addr)
     812              : {
     813           10 :     op_t op(*this);
     814              :     char buf[2 +
     815              :         urls::ipv6_address::max_str_len];
     816           10 :     auto s = addr.to_buffer(
     817              :         buf + 1, sizeof(buf) - 2);
     818           10 :     buf[0] = '[';
     819           10 :     buf[s.size() + 1] = ']';
     820           10 :     auto const n = s.size() + 2;
     821           10 :     auto dest = set_host_impl(n, op);
     822           10 :     std::memcpy(dest, buf, n);
     823           10 :     impl_.decoded_[id_host] = n;
     824           10 :     impl_.host_type_ = urls::host_type::ipv6;
     825           10 :     auto bytes = addr.to_bytes();
     826           10 :     std::memcpy(
     827           10 :         impl_.ip_addr_,
     828           10 :         bytes.data(),
     829              :         bytes.size());
     830           10 :     return *this;
     831           10 : }
     832              : 
     833              : url_base&
     834            7 : url_base::
     835              : set_host_ipvfuture(
     836              :     core::string_view s)
     837              : {
     838            7 :     op_t op(*this, &s);
     839              :     // validate
     840            8 :     grammar::parse(s,
     841              :         detail::ipvfuture_rule
     842            7 :             ).value(BOOST_URL_POS);
     843            6 :     auto dest = set_host_impl(
     844            6 :         s.size() + 2, op);
     845            6 :     *dest++ = '[';
     846            6 :     dest += s.copy(dest, s.size());
     847            6 :     *dest = ']';
     848            6 :     impl_.host_type_ =
     849              :         urls::host_type::ipvfuture;
     850            6 :     impl_.decoded_[id_host] = s.size() + 2;
     851            6 :     return *this;
     852            7 : }
     853              : 
     854              : url_base&
     855            4 : url_base::
     856              : set_host_name(
     857              :     core::string_view s)
     858              : {
     859            4 :     bool is_ipv4 = false;
     860            4 :     if(s.size() >= 7) // "0.0.0.0"
     861              :     {
     862              :         // IPv4-address
     863            3 :         if(parse_ipv4_address(s).has_value())
     864            1 :             is_ipv4 = true;
     865              :     }
     866            4 :     auto allowed = detail::host_chars;
     867            4 :     if(is_ipv4)
     868            1 :         allowed = allowed - '.';
     869              : 
     870            4 :     op_t op(*this, &s);
     871            4 :     encoding_opts opt;
     872            4 :     auto const n = encoded_size(
     873              :         s, allowed, opt);
     874            4 :     auto dest = set_host_impl(n, op);
     875            4 :     encode_unsafe(
     876              :         dest,
     877              :         n,
     878              :         s,
     879              :         allowed,
     880              :         opt);
     881            4 :     impl_.host_type_ =
     882              :         urls::host_type::name;
     883            4 :     impl_.decoded_[id_host] = s.size();
     884            4 :     return *this;
     885            4 : }
     886              : 
     887              : url_base&
     888            4 : url_base::
     889              : set_encoded_host_name(
     890              :     pct_string_view s)
     891              : {
     892            4 :     bool is_ipv4 = false;
     893            4 :     if(s.size() >= 7) // "0.0.0.0"
     894              :     {
     895              :         // IPv4-address
     896            3 :         if(parse_ipv4_address(s).has_value())
     897            1 :             is_ipv4 = true;
     898              :     }
     899            4 :     auto allowed = detail::host_chars;
     900            4 :     if(is_ipv4)
     901            1 :         allowed = allowed - '.';
     902              : 
     903            4 :     op_t op(*this, &detail::ref(s));
     904            4 :     auto const n = detail::re_encoded_size_unsafe(
     905              :         s, allowed);
     906            4 :     auto dest = set_host_impl(n, op);
     907            4 :     impl_.decoded_[id_host] =
     908            4 :         detail::re_encode_unsafe(
     909              :             dest,
     910            4 :             dest + n,
     911              :             s,
     912              :             allowed);
     913            4 :     BOOST_ASSERT(
     914              :         impl_.decoded_[id_host] ==
     915              :             s.decoded_size());
     916            4 :     impl_.host_type_ =
     917              :         urls::host_type::name;
     918            4 :     return *this;
     919            4 : }
     920              : 
     921              : //------------------------------------------------
     922              : 
     923              : url_base&
     924           23 : url_base::
     925              : set_port_number(
     926              :     std::uint16_t n)
     927              : {
     928           23 :     op_t op(*this);
     929              :     auto s =
     930           23 :         detail::make_printed(n);
     931           23 :     auto dest = set_port_impl(
     932           23 :         s.string().size(), op);
     933           23 :     std::memcpy(
     934           23 :         dest, s.string().data(),
     935           23 :             s.string().size());
     936           23 :     impl_.port_number_ = n;
     937           23 :     return *this;
     938           23 : }
     939              : 
     940              : url_base&
     941           90 : url_base::
     942              : set_port(
     943              :     core::string_view s)
     944              : {
     945           90 :     op_t op(*this, &s);
     946          109 :     auto t = grammar::parse(s,
     947           19 :         detail::port_rule{}
     948           90 :             ).value(BOOST_URL_POS);
     949              :     auto dest =
     950           71 :         set_port_impl(t.str.size(), op);
     951           71 :     std::memcpy(dest,
     952           71 :         t.str.data(), t.str.size());
     953           71 :     if(t.has_number)
     954           35 :         impl_.port_number_ = t.number;
     955              :     else
     956           36 :         impl_.port_number_ = 0;
     957           71 :     return *this;
     958           90 : }
     959              : 
     960              : url_base&
     961           25 : url_base::
     962              : remove_port() noexcept
     963              : {
     964           25 :     op_t op(*this);
     965           25 :     resize_impl(id_port, 0, op);
     966           25 :     impl_.port_number_ = 0;
     967           50 :     return *this;
     968           25 : }
     969              : 
     970              : //------------------------------------------------
     971              : //
     972              : // Compound Fields
     973              : //
     974              : //------------------------------------------------
     975              : 
     976              : url_base&
     977           14 : url_base::
     978              : remove_origin()
     979              : {
     980              :     // these two calls perform 2 memmoves instead of 1
     981           14 :     remove_authority();
     982           14 :     remove_scheme();
     983           14 :     return *this;
     984              : }
     985              : 
     986              : //------------------------------------------------
     987              : //
     988              : // Path
     989              : //
     990              : //------------------------------------------------
     991              : 
     992              : bool
     993           50 : url_base::
     994              : set_path_absolute(
     995              :     bool absolute)
     996              : {
     997           50 :     op_t op(*this);
     998              : 
     999              :     // check if path empty
    1000           50 :     if(impl_.len(id_path) == 0)
    1001              :     {
    1002           38 :         if(! absolute)
    1003              :         {
    1004              :             // already not absolute
    1005           32 :             return true;
    1006              :         }
    1007              : 
    1008              :         // add '/'
    1009            6 :         auto dest = resize_impl(
    1010              :             id_path, 1, op);
    1011            6 :         *dest = '/';
    1012            6 :         ++impl_.decoded_[id_path];
    1013            6 :         return true;
    1014              :     }
    1015              : 
    1016              :     // check if path absolute
    1017           12 :     if(s_[impl_.offset(id_path)] == '/')
    1018              :     {
    1019            9 :         if(absolute)
    1020              :         {
    1021              :             // already absolute
    1022            2 :             return true;
    1023              :         }
    1024              : 
    1025           11 :         if( has_authority() &&
    1026            4 :             impl_.len(id_path) > 1)
    1027              :         {
    1028              :             // can't do it, paths are always
    1029              :             // absolute when authority present!
    1030            2 :             return false;
    1031              :         }
    1032              : 
    1033            5 :         auto p = encoded_path();
    1034            5 :         auto pos = p.find_first_of(":/", 1);
    1035            6 :         if (pos != core::string_view::npos &&
    1036            1 :             p[pos] == ':')
    1037              :         {
    1038              :             // prepend with .
    1039            1 :             auto n = impl_.len(id_path);
    1040            1 :             resize_impl(id_path, n + 1, op);
    1041            1 :             std::memmove(
    1042            2 :                 s_ + impl_.offset(id_path) + 1,
    1043            1 :                 s_ + impl_.offset(id_path), n);
    1044            1 :             *(s_ + impl_.offset(id_path)) = '.';
    1045            1 :             ++impl_.decoded_[id_path];
    1046            1 :             return true;
    1047              :         }
    1048              : 
    1049              :         // remove '/'
    1050            4 :         auto n = impl_.len(id_port);
    1051            4 :         impl_.split(id_port, n + 1);
    1052            4 :         resize_impl(id_port, n, op);
    1053            4 :         --impl_.decoded_[id_path];
    1054            4 :         return true;
    1055              :     }
    1056              : 
    1057            3 :     if(! absolute)
    1058              :     {
    1059              :         // already not absolute
    1060            1 :         return true;
    1061              :     }
    1062              : 
    1063              :     // add '/'
    1064            2 :     auto n = impl_.len(id_port);
    1065            2 :     auto dest = resize_impl(
    1066            2 :         id_port, n + 1, op) + n;
    1067            2 :     impl_.split(id_port, n);
    1068            2 :     *dest = '/';
    1069            2 :     ++impl_.decoded_[id_path];
    1070            2 :     return true;
    1071           50 : }
    1072              : 
    1073              : url_base&
    1074           25 : url_base::
    1075              : set_path(
    1076              :     core::string_view s)
    1077              : {
    1078           25 :     op_t op(*this, &s);
    1079           25 :     encoding_opts opt;
    1080              : 
    1081              : //------------------------------------------------
    1082              : //
    1083              : //  Calculate encoded size
    1084              : //
    1085              : // - "/"s are not encoded
    1086              : // - "%2F"s are not encoded
    1087              : //
    1088              : // - reserved path chars are re-encoded
    1089              : // - colons in first segment might need to be re-encoded
    1090              : // - the path might need to receive a prefix
    1091           25 :     auto const n = encoded_size(
    1092              :         s, detail::path_chars, opt);
    1093           25 :     std::size_t n_reencode_colons = 0;
    1094           25 :     core::string_view first_seg;
    1095           25 :     if (!has_scheme() &&
    1096           40 :         !has_authority() &&
    1097           15 :         !s.starts_with('/'))
    1098              :     {
    1099              :         // the first segment with unencoded colons would look
    1100              :         // like the scheme
    1101            6 :         first_seg = detail::to_sv(s);
    1102            6 :         std::size_t p = s.find('/');
    1103            6 :         if (p != core::string_view::npos)
    1104            2 :             first_seg = s.substr(0, p);
    1105            6 :         n_reencode_colons = std::count(
    1106           12 :             first_seg.begin(), first_seg.end(), ':');
    1107              :     }
    1108              :     // the authority can only be followed by an empty or relative path
    1109              :     // if we have an authority and the path is a non-empty relative path, we
    1110              :     // add the "/" prefix to make it valid.
    1111              :     bool make_absolute =
    1112           25 :         has_authority() &&
    1113           30 :         !s.starts_with('/') &&
    1114            5 :         !s.empty();
    1115              :     // a path starting with "//" might look like the authority.
    1116              :     // we add a "/." prefix to prevent that
    1117              :     bool add_dot_segment =
    1118           47 :         !make_absolute &&
    1119           22 :         s.starts_with("//");
    1120              : 
    1121              : //------------------------------------------------
    1122              : //
    1123              : //  Re-encode data
    1124              : //
    1125           50 :     auto dest = set_path_impl(
    1126           25 :         n + make_absolute + 2 * n_reencode_colons + 2 * add_dot_segment, op);
    1127           25 :     impl_.decoded_[id_path] = 0;
    1128           25 :     if (!dest)
    1129              :     {
    1130            3 :         impl_.nseg_ = 0;
    1131            3 :         return *this;
    1132              :     }
    1133           22 :     if (make_absolute)
    1134              :     {
    1135            3 :         *dest++ = '/';
    1136            3 :         impl_.decoded_[id_path] += 1;
    1137              :     }
    1138           19 :     else if (add_dot_segment)
    1139              :     {
    1140            1 :         *dest++ = '/';
    1141            1 :         *dest++ = '.';
    1142            1 :         impl_.decoded_[id_path] += 2;
    1143              :     }
    1144           44 :     dest += encode_unsafe(
    1145              :         dest,
    1146           22 :         impl_.get(id_query).data() - dest,
    1147              :         first_seg,
    1148           22 :         detail::segment_chars - ':',
    1149              :         opt);
    1150           22 :     dest += encode_unsafe(
    1151              :         dest,
    1152           22 :         impl_.get(id_query).data() - dest,
    1153              :         s.substr(first_seg.size()),
    1154              :         detail::path_chars,
    1155              :         opt);
    1156           22 :     impl_.decoded_[id_path] += s.size();
    1157           22 :     BOOST_ASSERT(!dest || dest == impl_.get(id_query).data());
    1158           22 :     BOOST_ASSERT(
    1159              :         impl_.decoded_[id_path] ==
    1160              :         s.size() + make_absolute + 2 * add_dot_segment);
    1161              : 
    1162              : //------------------------------------------------
    1163              : //
    1164              : //  Update path parameters
    1165              : //
    1166              :     // get the encoded_path with the replacements we applied
    1167           22 :     if (s == "/")
    1168              :     {
    1169              :         // "/" maps to sequence {}
    1170            3 :         impl_.nseg_ = 0;
    1171              :     }
    1172           19 :     else if (!s.empty())
    1173              :     {
    1174           15 :         if (s.starts_with("/./"))
    1175            1 :             s = s.substr(2);
    1176              :         // count segments as number of '/'s + 1
    1177           15 :         impl_.nseg_ = std::count(
    1178           30 :             s.begin() + 1, s.end(), '/') + 1;
    1179              :     }
    1180              :     else
    1181              :     {
    1182              :         // an empty relative path maps to sequence {}
    1183            4 :         impl_.nseg_ = 0;
    1184              :     }
    1185              : 
    1186           22 :     check_invariants();
    1187           22 :     return *this;
    1188           25 : }
    1189              : 
    1190              : url_base&
    1191          163 : url_base::
    1192              : set_encoded_path(
    1193              :     pct_string_view s)
    1194              : {
    1195          163 :     op_t op(*this, &detail::ref(s));
    1196              : 
    1197              : //------------------------------------------------
    1198              : //
    1199              : //  Calculate re-encoded output size
    1200              : //
    1201              : // - reserved path chars are re-encoded
    1202              : // - colons in first segment might need to be re-encoded
    1203              : // - the path might need to receive a prefix
    1204          163 :     auto const n = detail::re_encoded_size_unsafe(
    1205              :         s, detail::path_chars);
    1206          163 :     std::size_t n_reencode_colons = 0;
    1207          163 :     core::string_view first_seg;
    1208          163 :     if (!has_scheme() &&
    1209          180 :         !has_authority() &&
    1210           17 :         !s.starts_with('/'))
    1211              :     {
    1212              :         // the first segment with unencoded colons would look
    1213              :         // like the scheme
    1214           10 :         first_seg = detail::to_sv(s);
    1215           10 :         std::size_t p = s.find('/');
    1216           10 :         if (p != core::string_view::npos)
    1217            5 :             first_seg = s.substr(0, p);
    1218           10 :         n_reencode_colons = std::count(
    1219           20 :             first_seg.begin(), first_seg.end(), ':');
    1220              :     }
    1221              :     // the authority can only be followed by an empty or relative path
    1222              :     // if we have an authority and the path is a non-empty relative path, we
    1223              :     // add the "/" prefix to make it valid.
    1224              :     bool make_absolute =
    1225          163 :         has_authority() &&
    1226          211 :         !s.starts_with('/') &&
    1227           48 :         !s.empty();
    1228              :     // a path starting with "//" might look like the authority
    1229              :     // we add a "/." prefix to prevent that
    1230              :     bool add_dot_segment =
    1231          313 :         !make_absolute &&
    1232          200 :         !has_authority() &&
    1233           37 :         s.starts_with("//");
    1234              : 
    1235              : //------------------------------------------------
    1236              : //
    1237              : //  Re-encode data
    1238              : //
    1239          326 :     auto dest = set_path_impl(
    1240          163 :         n + make_absolute + 2 * n_reencode_colons + 2 * add_dot_segment, op);
    1241          163 :     impl_.decoded_[id_path] = 0;
    1242          163 :     if (!dest)
    1243              :     {
    1244            1 :         impl_.nseg_ = 0;
    1245            1 :         return *this;
    1246              :     }
    1247          162 :     if (make_absolute)
    1248              :     {
    1249           13 :         *dest++ = '/';
    1250           13 :         impl_.decoded_[id_path] += 1;
    1251              :     }
    1252          149 :     else if (add_dot_segment)
    1253              :     {
    1254            3 :         *dest++ = '/';
    1255            3 :         *dest++ = '.';
    1256            3 :         impl_.decoded_[id_path] += 2;
    1257              :     }
    1258          162 :     impl_.decoded_[id_path] +=
    1259          162 :         detail::re_encode_unsafe(
    1260              :             dest,
    1261          162 :             impl_.get(id_query).data(),
    1262              :             first_seg,
    1263          162 :             detail::segment_chars - ':');
    1264          162 :     impl_.decoded_[id_path] +=
    1265          324 :         detail::re_encode_unsafe(
    1266              :             dest,
    1267          162 :             impl_.get(id_query).data(),
    1268              :             s.substr(first_seg.size()),
    1269              :             detail::path_chars);
    1270          162 :     BOOST_ASSERT(dest == impl_.get(id_query).data());
    1271          162 :     BOOST_ASSERT(
    1272              :         impl_.decoded_[id_path] ==
    1273              :         s.decoded_size() + make_absolute + 2 * add_dot_segment);
    1274              : 
    1275              : //------------------------------------------------
    1276              : //
    1277              : //  Update path parameters
    1278              : //
    1279              :     // get the encoded_path with the replacements we applied
    1280          162 :     if (s == "/")
    1281              :     {
    1282              :         // "/" maps to sequence {}
    1283           16 :         impl_.nseg_ = 0;
    1284              :     }
    1285          146 :     else if (!s.empty())
    1286              :     {
    1287          109 :         if (s.starts_with("/./"))
    1288            7 :             s = s.substr(2);
    1289              :         // count segments as number of '/'s + 1
    1290          109 :         impl_.nseg_ = std::count(
    1291          218 :             s.begin() + 1, s.end(), '/') + 1;
    1292              :     }
    1293              :     else
    1294              :     {
    1295              :         // an empty relative path maps to sequence {}
    1296           37 :         impl_.nseg_ = 0;
    1297              :     }
    1298              : 
    1299          162 :     check_invariants();
    1300          162 :     return *this;
    1301          163 : }
    1302              : 
    1303              : segments_ref
    1304          266 : url_base::
    1305              : segments() noexcept
    1306              : {
    1307          266 :     return {*this};
    1308              : }
    1309              : 
    1310              : segments_encoded_ref
    1311          462 : url_base::
    1312              : encoded_segments() noexcept
    1313              : {
    1314          462 :     return {*this};
    1315              : }
    1316              : 
    1317              : //------------------------------------------------
    1318              : //
    1319              : // Query
    1320              : //
    1321              : //------------------------------------------------
    1322              : 
    1323              : url_base&
    1324           10 : url_base::
    1325              : set_query(
    1326              :     core::string_view s)
    1327              : {
    1328           10 :     edit_params(
    1329           10 :         detail::params_iter_impl(impl_),
    1330           10 :         detail::params_iter_impl(impl_, 0),
    1331           20 :         detail::query_iter(s, true));
    1332           10 :     return *this;
    1333              : }
    1334              : 
    1335              : url_base&
    1336           47 : url_base::
    1337              : set_encoded_query(
    1338              :     pct_string_view s)
    1339              : {
    1340           47 :     op_t op(*this);
    1341           47 :     std::size_t n = 0;      // encoded size
    1342           47 :     std::size_t nparam = 1; // param count
    1343           47 :     auto const end = s.end();
    1344           47 :     auto p = s.begin();
    1345              : 
    1346              :     // measure
    1347          195 :     while(p != end)
    1348              :     {
    1349          148 :         if(*p == '&')
    1350              :         {
    1351            3 :             ++p;
    1352            3 :             ++n;
    1353            3 :             ++nparam;
    1354              :         }
    1355          145 :         else if(*p != '%')
    1356              :         {
    1357          142 :             if(detail::query_chars(*p))
    1358          139 :                 n += 1; // allowed
    1359              :             else
    1360            3 :                 n += 3; // escaped
    1361          142 :             ++p;
    1362              :         }
    1363              :         else
    1364              :         {
    1365              :             // escape
    1366            3 :             n += 3;
    1367            3 :             p += 3;
    1368              :         }
    1369              :     }
    1370              : 
    1371              :     // resize
    1372           47 :     auto dest = resize_impl(
    1373           47 :         id_query, n + 1, op);
    1374           47 :     *dest++ = '?';
    1375              : 
    1376              :     // encode
    1377           47 :     impl_.decoded_[id_query] =
    1378           47 :         detail::re_encode_unsafe(
    1379              :             dest,
    1380           47 :             dest + n,
    1381              :             s,
    1382              :             detail::query_chars);
    1383           47 :     BOOST_ASSERT(
    1384              :         impl_.decoded_[id_query] ==
    1385              :             s.decoded_size());
    1386           47 :     impl_.nparam_ = nparam;
    1387           47 :     return *this;
    1388           47 : }
    1389              : 
    1390              : params_ref
    1391           91 : url_base::
    1392              : params() noexcept
    1393              : {
    1394              :     return params_ref(
    1395              :         *this,
    1396              :         encoding_opts{
    1397           91 :             true, false, false});
    1398              : }
    1399              : 
    1400              : params_ref
    1401            2 : url_base::
    1402              : params(encoding_opts opt) noexcept
    1403              : {
    1404            2 :     return params_ref(*this, opt);
    1405              : }
    1406              : 
    1407              : params_encoded_ref
    1408           77 : url_base::
    1409              : encoded_params() noexcept
    1410              : {
    1411           77 :     return {*this};
    1412              : }
    1413              : 
    1414              : url_base&
    1415            1 : url_base::
    1416              : set_params(
    1417              :     std::initializer_list<param_view> ps,
    1418              :     encoding_opts opts) noexcept
    1419              : {
    1420            1 :     params(opts).assign(ps);
    1421            1 :     return *this;
    1422              : }
    1423              : 
    1424              : url_base&
    1425            1 : url_base::
    1426              : set_encoded_params( std::initializer_list< param_pct_view > ps ) noexcept
    1427              : {
    1428            1 :     encoded_params().assign(ps);
    1429            1 :     return *this;
    1430              : }
    1431              : 
    1432              : url_base&
    1433          222 : url_base::
    1434              : remove_query() noexcept
    1435              : {
    1436          222 :     op_t op(*this);
    1437          222 :     resize_impl(id_query, 0, op);
    1438          222 :     impl_.nparam_ = 0;
    1439          222 :     impl_.decoded_[id_query] = 0;
    1440          444 :     return *this;
    1441          222 : }
    1442              : 
    1443              : //------------------------------------------------
    1444              : //
    1445              : // Fragment
    1446              : //
    1447              : //------------------------------------------------
    1448              : 
    1449              : url_base&
    1450          215 : url_base::
    1451              : remove_fragment() noexcept
    1452              : {
    1453          215 :     op_t op(*this);
    1454          215 :     resize_impl(id_frag, 0, op);
    1455          215 :     impl_.decoded_[id_frag] = 0;
    1456          430 :     return *this;
    1457          215 : }
    1458              : 
    1459              : url_base&
    1460            7 : url_base::
    1461              : set_fragment(core::string_view s)
    1462              : {
    1463            7 :     op_t op(*this, &s);
    1464            7 :     encoding_opts opt;
    1465            7 :     auto const n = encoded_size(
    1466              :         s,
    1467              :         detail::fragment_chars,
    1468              :         opt);
    1469            7 :     auto dest = resize_impl(
    1470              :         id_frag, n + 1, op);
    1471            7 :     *dest++ = '#';
    1472            7 :     encode_unsafe(
    1473              :         dest,
    1474              :         n,
    1475              :         s,
    1476              :         detail::fragment_chars,
    1477              :         opt);
    1478            7 :     impl_.decoded_[id_frag] = s.size();
    1479            7 :     return *this;
    1480            7 : }
    1481              : 
    1482              : url_base&
    1483           57 : url_base::
    1484              : set_encoded_fragment(
    1485              :     pct_string_view s)
    1486              : {
    1487           57 :     op_t op(*this, &detail::ref(s));
    1488              :     auto const n =
    1489           57 :         detail::re_encoded_size_unsafe(
    1490              :             s,
    1491              :             detail::fragment_chars);
    1492           57 :     auto dest = resize_impl(
    1493           57 :         id_frag, n + 1, op);
    1494           57 :     *dest++ = '#';
    1495           57 :     impl_.decoded_[id_frag] =
    1496           57 :         detail::re_encode_unsafe(
    1497              :             dest,
    1498           57 :             dest + n,
    1499              :             s,
    1500              :             detail::fragment_chars);
    1501           57 :     BOOST_ASSERT(
    1502              :         impl_.decoded_[id_frag] ==
    1503              :             s.decoded_size());
    1504           57 :     return *this;
    1505           57 : }
    1506              : 
    1507              : //------------------------------------------------
    1508              : //
    1509              : // Resolution
    1510              : //
    1511              : //------------------------------------------------
    1512              : 
    1513              : system::result<void>
    1514          462 : url_base::
    1515              : resolve(
    1516              :     url_view_base const& ref)
    1517              : {
    1518          465 :     if (this == &ref &&
    1519            3 :         has_scheme())
    1520              :     {
    1521            2 :         normalize_path();
    1522            2 :         return {};
    1523              :     }
    1524              : 
    1525          460 :     if(! has_scheme())
    1526              :     {
    1527            2 :         BOOST_URL_RETURN_EC(error::not_a_base);
    1528              :     }
    1529              : 
    1530          458 :     op_t op(*this);
    1531              : 
    1532              :     //
    1533              :     // 5.2.2. Transform References
    1534              :     // https://datatracker.ietf.org/doc/html/rfc3986#section-5.2.2
    1535              :     //
    1536              : 
    1537          719 :     if( ref.has_scheme() &&
    1538          261 :         ref.scheme() != scheme())
    1539              :     {
    1540          198 :         reserve_impl(ref.size(), op);
    1541          198 :         copy(ref);
    1542          198 :         normalize_path();
    1543          198 :         return {};
    1544              :     }
    1545          260 :     if(ref.has_authority())
    1546              :     {
    1547           70 :         reserve_impl(
    1548           70 :             impl_.offset(id_user) + ref.size(), op);
    1549           70 :         set_encoded_authority(
    1550              :             ref.encoded_authority());
    1551           70 :         set_encoded_path(
    1552              :             ref.encoded_path());
    1553           70 :         if (ref.encoded_path().empty())
    1554           31 :             set_path_absolute(false);
    1555              :         else
    1556           39 :             normalize_path();
    1557           70 :         if(ref.has_query())
    1558            5 :             set_encoded_query(
    1559              :                 ref.encoded_query());
    1560              :         else
    1561           65 :             remove_query();
    1562           70 :         if(ref.has_fragment())
    1563            5 :             set_encoded_fragment(
    1564              :                 ref.encoded_fragment());
    1565              :         else
    1566           65 :             remove_fragment();
    1567           70 :         return {};
    1568              :     }
    1569          190 :     if(ref.encoded_path().empty())
    1570              :     {
    1571           32 :         reserve_impl(
    1572           32 :             impl_.offset(id_query) +
    1573           32 :             ref.size(), op);
    1574           32 :         normalize_path();
    1575           32 :         if(ref.has_query())
    1576              :         {
    1577           10 :             set_encoded_query(
    1578              :                 ref.encoded_query());
    1579              :         }
    1580           32 :         if(ref.has_fragment())
    1581           18 :             set_encoded_fragment(
    1582              :                 ref.encoded_fragment());
    1583           32 :         return {};
    1584              :     }
    1585          158 :     if(ref.is_path_absolute())
    1586              :     {
    1587           35 :         reserve_impl(
    1588           35 :             impl_.offset(id_path) +
    1589           35 :                 ref.size(), op);
    1590           35 :         set_encoded_path(
    1591              :             ref.encoded_path());
    1592           35 :         normalize_path();
    1593           35 :         if(ref.has_query())
    1594            3 :             set_encoded_query(
    1595              :                 ref.encoded_query());
    1596              :         else
    1597           32 :             remove_query();
    1598           35 :         if(ref.has_fragment())
    1599            2 :             set_encoded_fragment(
    1600              :                 ref.encoded_fragment());
    1601              :         else
    1602           33 :             remove_fragment();
    1603           35 :         return {};
    1604              :     }
    1605              :     // General case: ref is relative path
    1606          123 :     reserve_impl(
    1607          123 :         impl_.offset(id_query) +
    1608          123 :         ref.size(), op);
    1609              :     // 5.2.3. Merge Paths
    1610          123 :     auto es = encoded_segments();
    1611          123 :     if(es.size() > 0)
    1612              :     {
    1613          118 :         es.pop_back();
    1614              :     }
    1615          246 :     es.insert(es.end(),
    1616          123 :         ref.encoded_segments().begin(),
    1617          123 :         ref.encoded_segments().end());
    1618          123 :     normalize_path();
    1619          123 :     if(ref.has_query())
    1620           10 :         set_encoded_query(
    1621              :             ref.encoded_query());
    1622              :     else
    1623          113 :         remove_query();
    1624          123 :     if(ref.has_fragment())
    1625           10 :         set_encoded_fragment(
    1626              :             ref.encoded_fragment());
    1627              :     else
    1628          113 :         remove_fragment();
    1629          123 :     return {};
    1630          458 : }
    1631              : 
    1632              : //------------------------------------------------
    1633              : //
    1634              : // Normalization
    1635              : //
    1636              : //------------------------------------------------
    1637              : 
    1638              : template <class Charset>
    1639              : void
    1640         1917 : url_base::
    1641              : normalize_octets_impl(
    1642              :     int id,
    1643              :     Charset const& allowed,
    1644              :     op_t& op) noexcept
    1645              : {
    1646         1917 :     char* it = s_ + impl_.offset(id);
    1647         1917 :     char* end = s_ + impl_.offset(id + 1);
    1648         1917 :     char d = 0;
    1649         1917 :     char* dest = it;
    1650        10587 :     while (it < end)
    1651              :     {
    1652         8670 :         if (*it != '%')
    1653              :         {
    1654         8562 :             *dest = *it;
    1655         8562 :             ++it;
    1656         8562 :             ++dest;
    1657         8562 :             continue;
    1658              :         }
    1659          108 :         BOOST_ASSERT(end - it >= 3);
    1660              : 
    1661              :         // decode unreserved octets
    1662          108 :         d = detail::decode_one(it + 1);
    1663          108 :         if (allowed(d))
    1664              :         {
    1665           76 :             *dest = d;
    1666           76 :             it += 3;
    1667           76 :             ++dest;
    1668           76 :             continue;
    1669              :         }
    1670              : 
    1671              :         // uppercase percent-encoding triplets
    1672           32 :         *dest++ = '%';
    1673           32 :         ++it;
    1674           32 :         *dest++ = grammar::to_upper(*it++);
    1675           32 :         *dest++ = grammar::to_upper(*it++);
    1676              :     }
    1677         1917 :     if (it != dest)
    1678              :     {
    1679           24 :         auto diff = it - dest;
    1680           24 :         auto n = impl_.len(id) - diff;
    1681           24 :         shrink_impl(id, n, op);
    1682           24 :         s_[size()] = '\0';
    1683              :     }
    1684         1917 : }
    1685              : 
    1686              : url_base&
    1687           38 : url_base::
    1688              : normalize_scheme()
    1689              : {
    1690           38 :     to_lower_impl(id_scheme);
    1691           38 :     return *this;
    1692              : }
    1693              : 
    1694              : url_base&
    1695          383 : url_base::
    1696              : normalize_authority()
    1697              : {
    1698          383 :     op_t op(*this);
    1699              : 
    1700              :     // normalize host
    1701          383 :     if (host_type() == urls::host_type::name)
    1702              :     {
    1703          247 :         normalize_octets_impl(
    1704              :             id_host,
    1705              :             detail::reg_name_chars, op);
    1706              :     }
    1707          383 :     decoded_to_lower_impl(id_host);
    1708              : 
    1709              :     // normalize password
    1710          383 :     normalize_octets_impl(id_pass, detail::password_chars, op);
    1711              : 
    1712              :     // normalize user
    1713          383 :     normalize_octets_impl(id_user, detail::user_chars, op);
    1714          766 :     return *this;
    1715          383 : }
    1716              : 
    1717              : url_base&
    1718          832 : url_base::
    1719              : normalize_path()
    1720              : {
    1721          832 :     op_t op(*this);
    1722          832 :     normalize_octets_impl(id_path, detail::segment_chars, op);
    1723          832 :     core::string_view p = impl_.get(id_path);
    1724          832 :     char* p_dest = s_ + impl_.offset(id_path);
    1725          832 :     char* p_end = s_ + impl_.offset(id_path + 1);
    1726          832 :     auto pn = p.size();
    1727          832 :     auto skip_dot = 0;
    1728          832 :     bool encode_colons = false;
    1729          832 :     core::string_view first_seg;
    1730              : 
    1731              : //------------------------------------------------
    1732              : //
    1733              : //  Determine unnecessary initial dot segments to skip and
    1734              : //  if we need to encode colons in the first segment
    1735              : //
    1736          832 :     if (
    1737         1096 :         !has_authority() &&
    1738          264 :         p.starts_with("/./"))
    1739              :     {
    1740              :         // check if removing the "/./" would result in "//"
    1741              :         // ex: "/.//", "/././/", "/././/", ...
    1742           14 :         skip_dot = 2;
    1743           15 :         while (p.substr(skip_dot, 3).starts_with("/./"))
    1744            1 :             skip_dot += 2;
    1745           14 :         if (p.substr(skip_dot).starts_with("//"))
    1746           11 :             skip_dot = 2;
    1747              :         else
    1748            3 :             skip_dot = 0;
    1749              :     }
    1750          818 :     else if (
    1751          848 :         !has_scheme() &&
    1752           30 :         !has_authority())
    1753              :     {
    1754           30 :         if (p.starts_with("./"))
    1755              :         {
    1756              :             // check if removing the "./" would result in "//"
    1757              :             // ex: ".//", "././/", "././/", ...
    1758            7 :             skip_dot = 1;
    1759           10 :             while (p.substr(skip_dot, 3).starts_with("/./"))
    1760            3 :                 skip_dot += 2;
    1761            7 :             if (p.substr(skip_dot).starts_with("//"))
    1762            2 :                 skip_dot = 2;
    1763              :             else
    1764            5 :                 skip_dot = 0;
    1765              : 
    1766            7 :             if ( !skip_dot )
    1767              :             {
    1768              :                 // check if removing "./"s would leave us
    1769              :                 // a first segment with an ambiguous ":"
    1770            5 :                 first_seg = p.substr(2);
    1771            7 :                 while (first_seg.starts_with("./"))
    1772            2 :                     first_seg = first_seg.substr(2);
    1773            5 :                 auto i = first_seg.find('/');
    1774            5 :                 if (i != core::string_view::npos)
    1775            1 :                     first_seg = first_seg.substr(0, i);
    1776            5 :                 encode_colons = first_seg.contains(':');
    1777              :             }
    1778              :         }
    1779              :         else
    1780              :         {
    1781              :             // check if normalize_octets_impl
    1782              :             // didn't already create a ":"
    1783              :             // in the first segment
    1784           23 :             first_seg = p;
    1785           23 :             auto i = first_seg.find('/');
    1786           23 :             if (i != core::string_view::npos)
    1787           17 :                 first_seg = p.substr(0, i);
    1788           23 :             encode_colons = first_seg.contains(':');
    1789              :         }
    1790              :     }
    1791              : 
    1792              : //------------------------------------------------
    1793              : //
    1794              : //  Encode colons in the first segment
    1795              : //
    1796          832 :     if (encode_colons)
    1797              :     {
    1798              :         // prepend with "./"
    1799              :         // (resize_impl never throws)
    1800              :         auto cn =
    1801            5 :             std::count(
    1802              :                 first_seg.begin(),
    1803              :                 first_seg.end(),
    1804            5 :                 ':');
    1805            5 :         resize_impl(
    1806            5 :             id_path, pn + (2 * cn), op);
    1807              :         // move the 2nd, 3rd, ... segments
    1808            5 :         auto begin = s_ + impl_.offset(id_path);
    1809            5 :         auto it = begin;
    1810            5 :         auto end = begin + pn;
    1811           11 :         while (core::string_view(it, 2) == "./")
    1812            6 :             it += 2;
    1813           57 :         while (*it != '/' &&
    1814              :                it != end)
    1815           52 :             ++it;
    1816              :         // we don't need op here because this is
    1817              :         // an internal operation
    1818            5 :         std::memmove(it + (2 * cn), it, end - it);
    1819              : 
    1820              :         // move 1st segment
    1821            5 :         auto src = s_ + impl_.offset(id_path) + pn;
    1822            5 :         auto dest = s_ + impl_.offset(id_query);
    1823            5 :         src -= end - it;
    1824            5 :         dest -= end - it;
    1825            5 :         pn -= end - it;
    1826              :         do {
    1827           64 :             --src;
    1828           64 :             --dest;
    1829           64 :             if (*src != ':')
    1830              :             {
    1831           57 :                 *dest = *src;
    1832              :             }
    1833              :             else
    1834              :             {
    1835              :                 // use uppercase as required by
    1836              :                 // syntax-based normalization
    1837            7 :                 *dest-- = 'A';
    1838            7 :                 *dest-- = '3';
    1839            7 :                 *dest = '%';
    1840              :             }
    1841           64 :             --pn;
    1842           64 :         } while (pn);
    1843            5 :         skip_dot = 0;
    1844            5 :         p = impl_.get(id_path);
    1845            5 :         pn = p.size();
    1846            5 :         p_dest = s_ + impl_.offset(id_path);
    1847            5 :         p_end = s_ + impl_.offset(id_path + 1);
    1848              :     }
    1849              : 
    1850              : //------------------------------------------------
    1851              : //
    1852              : //  Remove "." and ".." segments
    1853              : //
    1854          832 :     p.remove_prefix(skip_dot);
    1855          832 :     p_dest += skip_dot;
    1856          832 :     auto n = detail::remove_dot_segments(
    1857              :         p_dest, p_end, p);
    1858              : 
    1859              : //------------------------------------------------
    1860              : //
    1861              : //  Update path parameters
    1862              : //
    1863          832 :     if (n != pn)
    1864              :     {
    1865          134 :         BOOST_ASSERT(n < pn);
    1866          134 :         shrink_impl(id_path, n + skip_dot, op);
    1867          134 :         p = encoded_path();
    1868          134 :         if (p == "/")
    1869            9 :             impl_.nseg_ = 0;
    1870          125 :         else if (!p.empty())
    1871          123 :             impl_.nseg_ = std::count(
    1872          246 :                 p.begin() + 1, p.end(), '/') + 1;
    1873              :         else
    1874            2 :             impl_.nseg_ = 0;
    1875          134 :         impl_.decoded_[id_path] =
    1876          134 :             detail::decode_bytes_unsafe(impl_.get(id_path));
    1877              :     }
    1878          832 :     return *this;
    1879          832 : }
    1880              : 
    1881              : url_base&
    1882           36 : url_base::
    1883              : normalize_query()
    1884              : {
    1885           36 :     op_t op(*this);
    1886           36 :     normalize_octets_impl(
    1887              :         id_query, detail::query_chars, op);
    1888           72 :     return *this;
    1889           36 : }
    1890              : 
    1891              : url_base&
    1892           36 : url_base::
    1893              : normalize_fragment()
    1894              : {
    1895           36 :     op_t op(*this);
    1896           36 :     normalize_octets_impl(
    1897              :         id_frag, detail::fragment_chars, op);
    1898           72 :     return *this;
    1899           36 : }
    1900              : 
    1901              : url_base&
    1902           36 : url_base::
    1903              : normalize()
    1904              : {
    1905           36 :     normalize_fragment();
    1906           36 :     normalize_query();
    1907           36 :     normalize_path();
    1908           36 :     normalize_authority();
    1909           36 :     normalize_scheme();
    1910           36 :     return *this;
    1911              : }
    1912              : 
    1913              : //------------------------------------------------
    1914              : //
    1915              : // Implementation
    1916              : //
    1917              : //------------------------------------------------
    1918              : 
    1919              : void
    1920        17767 : url_base::
    1921              : check_invariants() const noexcept
    1922              : {
    1923        17767 :     BOOST_ASSERT(pi_);
    1924        17767 :     BOOST_ASSERT(
    1925              :         impl_.len(id_scheme) == 0 ||
    1926              :         impl_.get(id_scheme).ends_with(':'));
    1927        17767 :     BOOST_ASSERT(
    1928              :         impl_.len(id_user) == 0 ||
    1929              :         impl_.get(id_user).starts_with("//"));
    1930        17767 :     BOOST_ASSERT(
    1931              :         impl_.len(id_pass) == 0 ||
    1932              :         impl_.get(id_user).starts_with("//"));
    1933        17767 :     BOOST_ASSERT(
    1934              :         impl_.len(id_pass) == 0 ||
    1935              :         (impl_.len(id_pass) == 1 &&
    1936              :             impl_.get(id_pass) == "@") ||
    1937              :         (impl_.len(id_pass) > 1 &&
    1938              :             impl_.get(id_pass).starts_with(':') &&
    1939              :             impl_.get(id_pass).ends_with('@')));
    1940        17767 :     BOOST_ASSERT(
    1941              :         impl_.len(id_user, id_path) == 0 ||
    1942              :         impl_.get(id_user).starts_with("//"));
    1943        17767 :     BOOST_ASSERT(impl_.decoded_[id_path] >=
    1944              :         ((impl_.len(id_path) + 2) / 3));
    1945        17767 :     BOOST_ASSERT(
    1946              :         impl_.len(id_port) == 0 ||
    1947              :         impl_.get(id_port).starts_with(':'));
    1948        17767 :     BOOST_ASSERT(
    1949              :         impl_.len(id_query) == 0 ||
    1950              :         impl_.get(id_query).starts_with('?'));
    1951        17767 :     BOOST_ASSERT(
    1952              :         (impl_.len(id_query) == 0 && impl_.nparam_ == 0) ||
    1953              :         (impl_.len(id_query) > 0 && impl_.nparam_ > 0));
    1954        17767 :     BOOST_ASSERT(
    1955              :         impl_.len(id_frag) == 0 ||
    1956              :         impl_.get(id_frag).starts_with('#'));
    1957        17767 :     BOOST_ASSERT(c_str()[size()] == '\0');
    1958        17767 : }
    1959              : 
    1960              : char*
    1961         1512 : url_base::
    1962              : resize_impl(
    1963              :     int id,
    1964              :     std::size_t new_size,
    1965              :     op_t& op)
    1966              : {
    1967         1512 :     return resize_impl(
    1968         1512 :         id, id + 1, new_size, op);
    1969              : }
    1970              : 
    1971              : char*
    1972         1781 : url_base::
    1973              : resize_impl(
    1974              :     int first,
    1975              :     int last,
    1976              :     std::size_t new_len,
    1977              :     op_t& op)
    1978              : {
    1979         1781 :     auto const n0 = impl_.len(first, last);
    1980         1781 :     if(new_len == 0 && n0 == 0)
    1981          371 :         return s_ + impl_.offset(first);
    1982         1410 :     if(new_len <= n0)
    1983          501 :         return shrink_impl(
    1984          501 :             first, last, new_len, op);
    1985              : 
    1986              :     // growing
    1987          909 :     std::size_t n = new_len - n0;
    1988          909 :     reserve_impl(size() + n, op);
    1989              :     auto const pos =
    1990          909 :         impl_.offset(last);
    1991              :     // adjust chars
    1992          909 :     op.move(
    1993          909 :         s_ + pos + n,
    1994          909 :         s_ + pos,
    1995          909 :         impl_.offset(id_end) -
    1996              :             pos + 1);
    1997              :     // collapse (first, last)
    1998          909 :     impl_.collapse(first, last,
    1999          909 :         impl_.offset(last) + n);
    2000              :     // shift (last, end) right
    2001          909 :     impl_.adjust_right(last, id_end, n);
    2002          909 :     s_[size()] = '\0';
    2003          909 :     return s_ + impl_.offset(first);
    2004              : }
    2005              : 
    2006              : char*
    2007          158 : url_base::
    2008              : shrink_impl(
    2009              :     int id,
    2010              :     std::size_t new_size,
    2011              :     op_t& op)
    2012              : {
    2013          158 :     return shrink_impl(
    2014          158 :         id, id + 1, new_size, op);
    2015              : }
    2016              : 
    2017              : char*
    2018          659 : url_base::
    2019              : shrink_impl(
    2020              :     int first,
    2021              :     int last,
    2022              :     std::size_t new_len,
    2023              :     op_t& op)
    2024              : {
    2025              :     // shrinking
    2026          659 :     auto const n0 = impl_.len(first, last);
    2027          659 :     BOOST_ASSERT(new_len <= n0);
    2028          659 :     std::size_t n = n0 - new_len;
    2029              :     auto const pos =
    2030          659 :         impl_.offset(last);
    2031              :     // adjust chars
    2032          659 :     op.move(
    2033          659 :         s_ + pos - n,
    2034          659 :         s_ + pos,
    2035          659 :         impl_.offset(
    2036          659 :             id_end) - pos + 1);
    2037              :     // collapse (first, last)
    2038          659 :     impl_.collapse(first,  last,
    2039          659 :         impl_.offset(last) - n);
    2040              :     // shift (last, end) left
    2041          659 :     impl_.adjust_left(last, id_end, n);
    2042          659 :     s_[size()] = '\0';
    2043          659 :     return s_ + impl_.offset(first);
    2044              : }
    2045              : 
    2046              : //------------------------------------------------
    2047              : 
    2048              : void
    2049           61 : url_base::
    2050              : set_scheme_impl(
    2051              :     core::string_view s,
    2052              :     urls::scheme id)
    2053              : {
    2054           61 :     op_t op(*this, &s);
    2055           61 :     check_invariants();
    2056           74 :     grammar::parse(
    2057           13 :         s, detail::scheme_rule()
    2058           61 :             ).value(BOOST_URL_POS);
    2059           48 :     auto const n = s.size();
    2060           48 :     auto const p = impl_.offset(id_path);
    2061              : 
    2062              :     // check for "./" prefix
    2063              :     bool const has_dot =
    2064           75 :         [this, p]
    2065              :     {
    2066           48 :         if(impl_.nseg_ == 0)
    2067           30 :             return false;
    2068           18 :         if(first_segment().size() < 2)
    2069            9 :             return false;
    2070            9 :         auto const src = s_ + p;
    2071            9 :         if(src[0] != '.')
    2072            6 :             return false;
    2073            3 :         if(src[1] != '/')
    2074            0 :             return false;
    2075            3 :         return true;
    2076           48 :     }();
    2077              : 
    2078              :     // Remove "./"
    2079           48 :     if(has_dot)
    2080              :     {
    2081              :         // do this first, for
    2082              :         // strong exception safety
    2083            6 :         reserve_impl(
    2084            3 :             size() + n + 1 - 2, op);
    2085            3 :         op.move(
    2086            3 :             s_ + p,
    2087            3 :             s_ + p + 2,
    2088            3 :             size() + 1 -
    2089              :                 (p + 2));
    2090            3 :         impl_.set_size(
    2091              :             id_path,
    2092            3 :             impl_.len(id_path) - 2);
    2093            3 :         s_[size()] = '\0';
    2094              :     }
    2095              : 
    2096           48 :     auto dest = resize_impl(
    2097              :         id_scheme, n + 1, op);
    2098           48 :     s.copy(dest, n);
    2099           48 :     dest[n] = ':';
    2100           48 :     impl_.scheme_ = id;
    2101           48 :     check_invariants();
    2102           61 : }
    2103              : 
    2104              : char*
    2105          101 : url_base::
    2106              : set_user_impl(
    2107              :     std::size_t n,
    2108              :     op_t& op)
    2109              : {
    2110          101 :     check_invariants();
    2111          101 :     if(impl_.len(id_pass) != 0)
    2112              :     {
    2113              :         // keep "//"
    2114           50 :         auto dest = resize_impl(
    2115              :             id_user, 2 + n, op);
    2116           50 :         check_invariants();
    2117           50 :         return dest + 2;
    2118              :     }
    2119              :     // add authority
    2120              :     bool const make_absolute =
    2121           91 :         !is_path_absolute() &&
    2122           40 :         !impl_.get(id_path).empty();
    2123          102 :     auto dest = resize_impl(
    2124           51 :         id_user, 2 + n + 1 + make_absolute, op);
    2125           51 :     impl_.split(id_user, 2 + n);
    2126           51 :     dest[0] = '/';
    2127           51 :     dest[1] = '/';
    2128           51 :     dest[2 + n] = '@';
    2129           51 :     if (make_absolute)
    2130              :     {
    2131            4 :         impl_.split(id_pass, 1);
    2132            4 :         impl_.split(id_host, 0);
    2133            4 :         impl_.split(id_port, 0);
    2134            4 :         dest[3 + n] = '/';
    2135              :     }
    2136           51 :     check_invariants();
    2137           51 :     return dest + 2;
    2138              : }
    2139              : 
    2140              : char*
    2141           82 : url_base::
    2142              : set_password_impl(
    2143              :     std::size_t n,
    2144              :     op_t& op)
    2145              : {
    2146           82 :     check_invariants();
    2147           82 :     if(impl_.len(id_user) != 0)
    2148              :     {
    2149              :         // already have authority
    2150           66 :         auto const dest = resize_impl(
    2151              :             id_pass, 1 + n + 1, op);
    2152           66 :         dest[0] = ':';
    2153           66 :         dest[n + 1] = '@';
    2154           66 :         check_invariants();
    2155           66 :         return dest + 1;
    2156              :     }
    2157              :     // add authority
    2158              :     bool const make_absolute =
    2159           25 :             !is_path_absolute() &&
    2160            9 :             !impl_.get(id_path).empty();
    2161              :     auto const dest =
    2162           32 :         resize_impl(
    2163              :         id_user, id_host,
    2164           16 :         2 + 1 + n + 1 + make_absolute, op);
    2165           16 :     impl_.split(id_user, 2);
    2166           16 :     dest[0] = '/';
    2167           16 :     dest[1] = '/';
    2168           16 :     dest[2] = ':';
    2169           16 :     dest[2 + n + 1] = '@';
    2170           16 :     if (make_absolute)
    2171              :     {
    2172            2 :         impl_.split(id_pass, 2 + n);
    2173            2 :         impl_.split(id_host, 0);
    2174            2 :         impl_.split(id_port, 0);
    2175            2 :         dest[4 + n] = '/';
    2176              :     }
    2177           16 :     check_invariants();
    2178           16 :     return dest + 3;
    2179              : }
    2180              : 
    2181              : char*
    2182           99 : url_base::
    2183              : set_userinfo_impl(
    2184              :     std::size_t n,
    2185              :     op_t& op)
    2186              : {
    2187              :     // "//" {dest} "@"
    2188           99 :     check_invariants();
    2189              :     bool const make_absolute =
    2190          180 :             !is_path_absolute() &&
    2191           81 :             !impl_.get(id_path).empty();
    2192          198 :     auto dest = resize_impl(
    2193           99 :         id_user, id_host, n + 3 + make_absolute, op);
    2194           99 :     impl_.split(id_user, n + 2);
    2195           99 :     dest[0] = '/';
    2196           99 :     dest[1] = '/';
    2197           99 :     dest[n + 2] = '@';
    2198           99 :     if (make_absolute)
    2199              :     {
    2200            2 :         impl_.split(id_pass, 1);
    2201            2 :         impl_.split(id_host, 0);
    2202            2 :         impl_.split(id_port, 0);
    2203            2 :         dest[3 + n] = '/';
    2204              :     }
    2205           99 :     check_invariants();
    2206           99 :     return dest + 2;
    2207              : }
    2208              : 
    2209              : char*
    2210          206 : url_base::
    2211              : set_host_impl(
    2212              :     std::size_t n,
    2213              :     op_t& op)
    2214              : {
    2215          206 :     check_invariants();
    2216          206 :     if(impl_.len(id_user) == 0)
    2217              :     {
    2218              :         // add authority
    2219              :         bool make_absolute =
    2220          184 :             !is_path_absolute() &&
    2221           90 :             impl_.len(id_path) != 0;
    2222           94 :         auto pn = impl_.len(id_path);
    2223          188 :         auto dest = resize_impl(
    2224           94 :             id_user, n + 2 + make_absolute, op);
    2225           94 :         impl_.split(id_user, 2);
    2226           94 :         impl_.split(id_pass, 0);
    2227           94 :         impl_.split(id_host, n);
    2228           94 :         impl_.split(id_port, 0);
    2229           94 :         impl_.split(id_path, pn + make_absolute);
    2230           94 :         if (make_absolute)
    2231              :         {
    2232            7 :             dest[n + 2] = '/';
    2233            7 :             ++impl_.decoded_[id_path];
    2234              :         }
    2235           94 :         dest[0] = '/';
    2236           94 :         dest[1] = '/';
    2237           94 :         check_invariants();
    2238           94 :         return dest + 2;
    2239              :     }
    2240              :     // already have authority
    2241          112 :     auto const dest = resize_impl(
    2242              :         id_host, n, op);
    2243          112 :     check_invariants();
    2244          112 :     return dest;
    2245              : }
    2246              : 
    2247              : char*
    2248          107 : url_base::
    2249              : set_port_impl(
    2250              :     std::size_t n,
    2251              :     op_t& op)
    2252              : {
    2253          107 :     check_invariants();
    2254          107 :     if(impl_.len(id_user) != 0)
    2255              :     {
    2256              :         // authority exists
    2257           85 :         auto dest = resize_impl(
    2258              :             id_port, n + 1, op);
    2259           85 :         dest[0] = ':';
    2260           85 :         check_invariants();
    2261           85 :         return dest + 1;
    2262              :     }
    2263              :     bool make_absolute =
    2264           38 :         !is_path_absolute() &&
    2265           16 :         impl_.len(id_path) != 0;
    2266           44 :     auto dest = resize_impl(
    2267           22 :         id_user, 3 + n + make_absolute, op);
    2268           22 :     impl_.split(id_user, 2);
    2269           22 :     impl_.split(id_pass, 0);
    2270           22 :     impl_.split(id_host, 0);
    2271           22 :     dest[0] = '/';
    2272           22 :     dest[1] = '/';
    2273           22 :     dest[2] = ':';
    2274           22 :     if (make_absolute)
    2275              :     {
    2276            2 :         impl_.split(id_port, n + 1);
    2277            2 :         dest[n + 3] = '/';
    2278            2 :         ++impl_.decoded_[id_path];
    2279              :     }
    2280           22 :     check_invariants();
    2281           22 :     return dest + 3;
    2282              : }
    2283              : 
    2284              : char*
    2285          188 : url_base::
    2286              : set_path_impl(
    2287              :     std::size_t n,
    2288              :     op_t& op)
    2289              : {
    2290          188 :     check_invariants();
    2291          188 :     auto const dest = resize_impl(
    2292              :         id_path, n, op);
    2293          188 :     return dest;
    2294              : }
    2295              : 
    2296              : 
    2297              : //------------------------------------------------
    2298              : 
    2299              : // return the first segment of the path.
    2300              : // this is needed for some algorithms.
    2301              : core::string_view
    2302           45 : url_base::
    2303              : first_segment() const noexcept
    2304              : {
    2305           45 :     if(impl_.nseg_ == 0)
    2306            7 :         return {};
    2307           38 :     auto const p0 = impl_.cs_ +
    2308           38 :         impl_.offset(id_path) +
    2309           38 :             detail::path_prefix(
    2310           38 :                 impl_.get(id_path));
    2311           38 :     auto const end = impl_.cs_ +
    2312           38 :         impl_.offset(id_query);
    2313           38 :     if(impl_.nseg_ == 1)
    2314           42 :         return core::string_view(
    2315           21 :             p0, end - p0);
    2316           17 :     auto p = p0;
    2317           39 :     while(*p != '/')
    2318           22 :         ++p;
    2319           17 :     BOOST_ASSERT(p < end);
    2320           17 :     return core::string_view(p0, p - p0);
    2321              : }
    2322              : 
    2323              : detail::segments_iter_impl
    2324          597 : url_base::
    2325              : edit_segments(
    2326              :     detail::segments_iter_impl const& it0,
    2327              :     detail::segments_iter_impl const& it1,
    2328              :     detail::any_segments_iter&& src,
    2329              :     // -1 = preserve
    2330              :     //  0 = make relative (can fail)
    2331              :     //  1 = make absolute
    2332              :     int absolute)
    2333              : {
    2334              :     // Iterator doesn't belong to this url
    2335          597 :     BOOST_ASSERT(it0.ref.alias_of(impl_));
    2336              : 
    2337              :     // Iterator doesn't belong to this url
    2338          597 :     BOOST_ASSERT(it1.ref.alias_of(impl_));
    2339              : 
    2340              :     // Iterator is in the wrong order
    2341          597 :     BOOST_ASSERT(it0.index <= it1.index);
    2342              : 
    2343              :     // Iterator is out of range
    2344          597 :     BOOST_ASSERT(it0.index <= impl_.nseg_);
    2345          597 :     BOOST_ASSERT(it0.pos <= impl_.len(id_path));
    2346              : 
    2347              :     // Iterator is out of range
    2348          597 :     BOOST_ASSERT(it1.index <= impl_.nseg_);
    2349          597 :     BOOST_ASSERT(it1.pos <= impl_.len(id_path));
    2350              : 
    2351              : //------------------------------------------------
    2352              : //
    2353              : //  Calculate output prefix
    2354              : //
    2355              : //  0 = ""
    2356              : //  1 = "/"
    2357              : //  2 = "./"
    2358              : //  3 = "/./"
    2359              : //
    2360          597 :     bool const is_abs = is_path_absolute();
    2361          597 :     if(has_authority())
    2362              :     {
    2363              :         // Check if the new
    2364              :         // path would be empty
    2365          213 :         if( src.fast_nseg == 0 &&
    2366          108 :             it0.index == 0 &&
    2367           18 :             it1.index == impl_.nseg_)
    2368              :         {
    2369              :             // VFALCO we don't have
    2370              :             // access to nchar this early
    2371              :             //
    2372              :             //BOOST_ASSERT(nchar == 0);
    2373           15 :             absolute = 0;
    2374              :         }
    2375              :         else
    2376              :         {
    2377              :             // prefix "/" required
    2378          198 :             absolute = 1;
    2379              :         }
    2380              :     }
    2381          384 :     else if(absolute < 0)
    2382              :     {
    2383          384 :         absolute = is_abs; // preserve
    2384              :     }
    2385          597 :     auto const path_pos = impl_.offset(id_path);
    2386              : 
    2387          597 :     std::size_t nchar = 0;
    2388          597 :     std::size_t prefix = 0;
    2389          597 :     bool encode_colons = false;
    2390          597 :     bool cp_src_prefix = false;
    2391          597 :     if(it0.index > 0)
    2392              :     {
    2393              :         // first segment unchanged
    2394          323 :         prefix = src.fast_nseg > 0;
    2395              :     }
    2396          274 :     else if(src.fast_nseg > 0)
    2397              :     {
    2398              :         // first segment from src
    2399          221 :         if(! src.front.empty())
    2400              :         {
    2401          162 :             if( src.front == "." &&
    2402            7 :                     src.fast_nseg > 1)
    2403            4 :                 if (src.s.empty())
    2404              :                 {
    2405              :                     // if front is ".", we need the extra "." in the prefix
    2406              :                     // which will maintain the invariant that segments represent
    2407              :                     // {"."}
    2408            4 :                     prefix = 2 + absolute;
    2409              :                 }
    2410              :                 else
    2411              :                 {
    2412              :                     // if the "." prefix is explicitly required from set_path
    2413              :                     // we do not include an extra "." segment
    2414            0 :                     prefix = absolute;
    2415            0 :                     cp_src_prefix = true;
    2416              :                 }
    2417          151 :             else if(absolute)
    2418           79 :                 prefix = 1;
    2419          140 :             else if(has_scheme() ||
    2420           68 :                     ! src.front.contains(':'))
    2421           67 :                 prefix = 0;
    2422              :             else
    2423              :             {
    2424            5 :                 prefix = 0;
    2425            5 :                 encode_colons = true;
    2426              :             }
    2427              :         }
    2428              :         else
    2429              :         {
    2430           66 :             prefix = 2 + absolute;
    2431              :         }
    2432              :     }
    2433              :     else
    2434              :     {
    2435              :         // first segment from it1
    2436           53 :         auto const p =
    2437           53 :             impl_.cs_ + path_pos + it1.pos;
    2438          106 :         switch(impl_.cs_ +
    2439           53 :             impl_.offset(id_query) - p)
    2440              :         {
    2441           34 :         case 0:
    2442              :             // points to end
    2443           34 :             prefix = absolute;
    2444           34 :             break;
    2445           11 :         default:
    2446           11 :             BOOST_ASSERT(*p == '/');
    2447           11 :             if(p[1] != '/')
    2448              :             {
    2449           11 :                 if(absolute)
    2450            5 :                     prefix = 1;
    2451           11 :                 else if(has_scheme() ||
    2452           11 :                         ! it1.dereference().contains(':'))
    2453            5 :                     prefix = 0;
    2454              :                 else
    2455            1 :                     prefix = 2;
    2456           11 :                 break;
    2457              :             }
    2458              :             // empty
    2459              :             BOOST_FALLTHROUGH;
    2460              :         case 1:
    2461              :             // empty
    2462            8 :             BOOST_ASSERT(*p == '/');
    2463            8 :             prefix = 2 + absolute;
    2464            8 :             break;
    2465              :         }
    2466              :     }
    2467              : 
    2468              : //  append '/' to new segs
    2469              : //  if inserting at front.
    2470          597 :     std::size_t const suffix =
    2471          776 :         it1.index == 0 &&
    2472          660 :         impl_.nseg_ > 0 &&
    2473           63 :         src.fast_nseg > 0;
    2474              : 
    2475              : //------------------------------------------------
    2476              : //
    2477              : //  Measure the number of encoded characters
    2478              : //  of output, and the number of inserted
    2479              : //  segments including internal separators.
    2480              : //
    2481          597 :     src.encode_colons = encode_colons;
    2482          597 :     std::size_t nseg = 0;
    2483          597 :     if(src.measure(nchar))
    2484              :     {
    2485          408 :         src.encode_colons = false;
    2486              :         for(;;)
    2487              :         {
    2488          733 :             ++nseg;
    2489          733 :             if(! src.measure(nchar))
    2490          406 :                 break;
    2491          325 :             ++nchar;
    2492              :         }
    2493              :     }
    2494              : 
    2495          595 :     switch(src.fast_nseg)
    2496              :     {
    2497          189 :     case 0:
    2498          189 :         BOOST_ASSERT(nseg == 0);
    2499          189 :         break;
    2500          219 :     case 1:
    2501          219 :         BOOST_ASSERT(nseg == 1);
    2502          219 :         break;
    2503          187 :     case 2:
    2504          187 :         BOOST_ASSERT(nseg >= 2);
    2505          187 :         break;
    2506              :     }
    2507              : 
    2508              : //------------------------------------------------
    2509              : //
    2510              : //  Calculate [pos0, pos1) to remove
    2511              : //
    2512          595 :     auto pos0 = it0.pos;
    2513          595 :     if(it0.index == 0)
    2514              :     {
    2515              :         // patch pos for prefix
    2516          272 :         pos0 = 0;
    2517              :     }
    2518          595 :     auto pos1 = it1.pos;
    2519          595 :     if(it1.index == 0)
    2520              :     {
    2521              :         // patch pos for prefix
    2522          179 :         pos1 = detail::path_prefix(
    2523              :             impl_.get(id_path));
    2524              :     }
    2525          416 :     else if(
    2526          416 :         it0.index == 0 &&
    2527           93 :         it1.index < impl_.nseg_ &&
    2528              :         nseg == 0)
    2529              :     {
    2530              :         // Remove the slash from segment it1
    2531              :         // if it is becoming the new first
    2532              :         // segment.
    2533           19 :         ++pos1;
    2534              :     }
    2535              :     // calc decoded size of old range
    2536              :     auto const dn0 =
    2537          595 :         detail::decode_bytes_unsafe(
    2538              :             core::string_view(
    2539          595 :                 impl_.cs_ +
    2540          595 :                     impl_.offset(id_path) +
    2541              :                     pos0,
    2542              :                 pos1 - pos0));
    2543              : 
    2544              : //------------------------------------------------
    2545              : //
    2546              : //  Resize
    2547              : //
    2548         1190 :     op_t op(*this, &src.s);
    2549              :     char* dest;
    2550              :     char const* end;
    2551              :     {
    2552          595 :         auto const nremove = pos1 - pos0;
    2553              :         // check overflow
    2554         1190 :         if( nchar <= max_size() && (
    2555          595 :             prefix + suffix <=
    2556          595 :                 max_size() - nchar))
    2557              :         {
    2558          595 :             nchar = prefix + nchar + suffix;
    2559          939 :             if( nchar <= nremove ||
    2560          344 :                 nchar - nremove <=
    2561          344 :                     max_size() - size())
    2562          595 :                 goto ok;
    2563              :         }
    2564              :         // too large
    2565            0 :         detail::throw_length_error();
    2566          595 :     ok:
    2567              :         auto const new_size =
    2568          595 :             size() + nchar - nremove;
    2569          595 :         reserve_impl(new_size, op);
    2570          595 :         dest = s_ + path_pos + pos0;
    2571          595 :         op.move(
    2572          595 :             dest + nchar,
    2573          595 :             s_ + path_pos + pos1,
    2574          595 :             size() - path_pos - pos1);
    2575         1190 :         impl_.set_size(
    2576              :             id_path,
    2577          595 :             impl_.len(id_path) + nchar - nremove);
    2578          595 :         BOOST_ASSERT(size() == new_size);
    2579          595 :         end = dest + nchar;
    2580          595 :         impl_.nseg_ = impl_.nseg_ + nseg - (
    2581          595 :             it1.index - it0.index) - cp_src_prefix;
    2582          595 :         if(s_)
    2583          593 :             s_[size()] = '\0';
    2584              :     }
    2585              : 
    2586              : //------------------------------------------------
    2587              : //
    2588              : //  Output segments and internal separators:
    2589              : //
    2590              : //  prefix [ segment [ '/' segment ] ] suffix
    2591              : //
    2592          595 :     auto const dest0 = dest;
    2593          595 :     switch(prefix)
    2594              :     {
    2595           38 :     case 3:
    2596           38 :         *dest++ = '/';
    2597           38 :         *dest++ = '.';
    2598           38 :         *dest++ = '/';
    2599           38 :         break;
    2600           41 :     case 2:
    2601           41 :         *dest++ = '.';
    2602              :         BOOST_FALLTHROUGH;
    2603          323 :     case 1:
    2604          323 :         *dest++ = '/';
    2605          323 :         break;
    2606          234 :     default:
    2607          234 :         break;
    2608              :     }
    2609          595 :     src.rewind();
    2610          595 :     if(nseg > 0)
    2611              :     {
    2612          406 :         src.encode_colons = encode_colons;
    2613              :         for(;;)
    2614              :         {
    2615          731 :             src.copy(dest, end);
    2616          731 :             if(--nseg == 0)
    2617          406 :                 break;
    2618          325 :             *dest++ = '/';
    2619          325 :             src.encode_colons = false;
    2620              :         }
    2621          406 :         if(suffix)
    2622           63 :             *dest++ = '/';
    2623              :     }
    2624          595 :     BOOST_ASSERT(dest == dest0 + nchar);
    2625              : 
    2626              :     // calc decoded size of new range,
    2627              :     auto const dn =
    2628          595 :         detail::decode_bytes_unsafe(
    2629          595 :             core::string_view(dest0, dest - dest0));
    2630          595 :     impl_.decoded_[id_path] += dn - dn0;
    2631              : 
    2632              :     return detail::segments_iter_impl(
    2633         1190 :         impl_, pos0, it0.index);
    2634              : }
    2635              : 
    2636              : //------------------------------------------------
    2637              : 
    2638              : auto
    2639          138 : url_base::
    2640              : edit_params(
    2641              :     detail::params_iter_impl const& it0,
    2642              :     detail::params_iter_impl const& it1,
    2643              :     detail::any_params_iter&& src) ->
    2644              :         detail::params_iter_impl
    2645              : {
    2646          138 :     auto pos0 = impl_.offset(id_query);
    2647          138 :     auto pos1 = pos0 + it1.pos;
    2648          138 :     pos0 = pos0 + it0.pos;
    2649              : 
    2650              :     // Iterator doesn't belong to this url
    2651          138 :     BOOST_ASSERT(it0.ref.alias_of(impl_));
    2652              : 
    2653              :     // Iterator doesn't belong to this url
    2654          138 :     BOOST_ASSERT(it1.ref.alias_of(impl_));
    2655              : 
    2656              :     // Iterator is in the wrong order
    2657          138 :     BOOST_ASSERT(it0.index <= it1.index);
    2658              : 
    2659              :     // Iterator is out of range
    2660          138 :     BOOST_ASSERT(it0.index <= impl_.nparam_);
    2661          138 :     BOOST_ASSERT(pos0 <= impl_.offset(id_frag));
    2662              : 
    2663              :     // Iterator is out of range
    2664          138 :     BOOST_ASSERT(it1.index <= impl_.nparam_);
    2665          138 :     BOOST_ASSERT(pos1 <= impl_.offset(id_frag));
    2666              : 
    2667              :     // calc decoded size of old range,
    2668              :     // minus one if '?' or '&' prefixed
    2669              :     auto const dn0 =
    2670          138 :         detail::decode_bytes_unsafe(
    2671              :             core::string_view(
    2672          138 :                 impl_.cs_ + pos0,
    2673              :                 pos1 - pos0)) - (
    2674          138 :                     impl_.len(id_query) > 0);
    2675              : 
    2676              : //------------------------------------------------
    2677              : //
    2678              : //  Measure the number of encoded characters
    2679              : //  of output, and the number of inserted
    2680              : //  segments including internal separators.
    2681              : //
    2682              : 
    2683          138 :     std::size_t nchar = 0;
    2684          138 :     std::size_t nparam = 0;
    2685          138 :     if(src.measure(nchar))
    2686              :     {
    2687          111 :         ++nchar; // for '?' or '&'
    2688              :         for(;;)
    2689              :         {
    2690          176 :             ++nparam;
    2691          176 :             if(! src.measure(nchar))
    2692          111 :                 break;
    2693           65 :             ++nchar; // for '&'
    2694              :         }
    2695              :     }
    2696              : 
    2697              : //------------------------------------------------
    2698              : //
    2699              : //  Resize
    2700              : //
    2701          133 :     op_t op(*this, &src.s0, &src.s1);
    2702              :     char* dest;
    2703              :     char const* end;
    2704              :     {
    2705          133 :         auto const nremove = pos1 - pos0;
    2706              :         // check overflow
    2707          228 :         if( nchar > nremove &&
    2708           95 :             nchar - nremove >
    2709           95 :                 max_size() - size())
    2710              :         {
    2711              :             // too large
    2712            0 :             detail::throw_length_error();
    2713              :         }
    2714          133 :         auto const nparam1 =
    2715          133 :             impl_.nparam_ + nparam - (
    2716          133 :                 it1.index - it0.index);
    2717          133 :         reserve_impl(size() + nchar - nremove, op);
    2718          133 :         dest = s_ + pos0;
    2719          133 :         end = dest + nchar;
    2720          133 :         if(impl_.nparam_ > 0)
    2721              :         {
    2722              :             // needed when we move
    2723              :             // the beginning of the query
    2724           99 :             s_[impl_.offset(id_query)] = '&';
    2725              :         }
    2726          133 :         op.move(
    2727          133 :             dest + nchar,
    2728          133 :             impl_.cs_ + pos1,
    2729          133 :             size() - pos1);
    2730          266 :         impl_.set_size(
    2731              :             id_query,
    2732          133 :             impl_.len(id_query) +
    2733              :                 nchar - nremove);
    2734          133 :         impl_.nparam_ = nparam1;
    2735          133 :         if(nparam1 > 0)
    2736              :         {
    2737              :             // needed when we erase
    2738              :             // the beginning of the query
    2739          133 :             s_[impl_.offset(id_query)] = '?';
    2740              :         }
    2741          133 :         if(s_)
    2742          133 :             s_[size()] = '\0';
    2743              :     }
    2744          133 :     auto const dest0 = dest;
    2745              : 
    2746              : //------------------------------------------------
    2747              : //
    2748              : //  Output params and internal separators:
    2749              : //
    2750              : //  [ '?' param ] [ '&' param ]
    2751              : //
    2752          133 :     if(nparam > 0)
    2753              :     {
    2754          111 :         if(it0.index == 0)
    2755           68 :             *dest++ = '?';
    2756              :         else
    2757           43 :             *dest++ = '&';
    2758          111 :         src.rewind();
    2759              :         for(;;)
    2760              :         {
    2761          176 :             src.copy(dest, end);
    2762          176 :             if(--nparam == 0)
    2763          111 :                 break;
    2764           65 :             *dest++ = '&';
    2765              :         }
    2766              :     }
    2767              : 
    2768              :     // calc decoded size of new range,
    2769              :     // minus one if '?' or '&' prefixed
    2770              :     auto const dn =
    2771          133 :         detail::decode_bytes_unsafe(
    2772          133 :             core::string_view(dest0, dest - dest0)) - (
    2773          133 :                 impl_.len(id_query) > 0);
    2774              : 
    2775          133 :     impl_.decoded_[id_query] += (dn - dn0);
    2776              : 
    2777              :     return detail::params_iter_impl(
    2778          133 :         impl_,
    2779          133 :         pos0 - impl_.offset_[id_query],
    2780          266 :         it0.index);
    2781          133 : }
    2782              : 
    2783              : //------------------------------------------------
    2784              : 
    2785              : void
    2786          383 : url_base::
    2787              : decoded_to_lower_impl(int id) noexcept
    2788              : {
    2789          383 :     char* it = s_ + impl_.offset(id);
    2790          383 :     char const* const end = s_ + impl_.offset(id + 1);
    2791         2213 :     while(it < end)
    2792              :     {
    2793         1830 :         if (*it != '%')
    2794              :         {
    2795         3650 :             *it = grammar::to_lower(
    2796         1825 :                 *it);
    2797         1825 :             ++it;
    2798         1825 :             continue;
    2799              :         }
    2800            5 :         it += 3;
    2801              :     }
    2802          383 : }
    2803              : 
    2804              : void
    2805           38 : url_base::
    2806              : to_lower_impl(int id) noexcept
    2807              : {
    2808           38 :     char* it = s_ + impl_.offset(id);
    2809           38 :     char const* const end = s_ + impl_.offset(id + 1);
    2810          155 :     while(it < end)
    2811              :     {
    2812          234 :         *it = grammar::to_lower(
    2813          117 :             *it);
    2814          117 :         ++it;
    2815              :     }
    2816           38 : }
    2817              : 
    2818              : } // urls
    2819              : } // boost
    2820              : 
        

Generated by: LCOV version 2.1