LCOV - code coverage report
Current view: top level - libs/url/src - url_base.cpp (source / functions) Hit Total Coverage
Test: coverage_filtered.info Lines: 1329 1334 99.6 %
Date: 2024-03-13 19:32:03 Functions: 74 74 100.0 %

          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        8034 : url_base::
      54             : op_t::
      55        8034 : ~op_t()
      56             : {
      57        8034 :     if(old)
      58        1009 :         u.cleanup(*this);
      59        8034 :     u.check_invariants();
      60        8034 : }
      61             : 
      62        8034 : url_base::
      63             : op_t::
      64             : op_t(
      65             :     url_base& impl_,
      66             :     core::string_view* s0_,
      67        8034 :     core::string_view* s1_) noexcept
      68             :     : u(impl_)
      69             :     , s0(s0_)
      70        8034 :     , s1(s1_)
      71             : {
      72        8034 :     u.check_invariants();
      73        8034 : }
      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         149 : url_base::
     109             : reserve_impl(std::size_t n)
     110             : {
     111         298 :     op_t op(*this);
     112         149 :     reserve_impl(n, op);
     113         148 :     if(s_)
     114         146 :         s_[size()] = '\0';
     115         148 : }
     116             : 
     117             : // make a copy of u
     118             : void
     119        3662 : url_base::
     120             : copy(url_view_base const& u)
     121             : {
     122        3662 :     if (this == &u)
     123         117 :         return;
     124        3665 :     op_t op(*this);
     125        3662 :     if(u.size() == 0)
     126             :     {
     127         117 :         clear();
     128         117 :         return;
     129             :     }
     130        3545 :     reserve_impl(
     131        3545 :         u.size(), op);
     132        3542 :     impl_ = u.impl_;
     133        3542 :     impl_.cs_ = s_;
     134        3542 :     impl_.from_ = {from::url};
     135        3542 :     std::memcpy(s_,
     136        3542 :         u.data(), u.size());
     137        3542 :     s_[size()] = '\0';
     138             : }
     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          72 :     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          28 :         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             : }
     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         224 :     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         222 :     return *this;
     308             : }
     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             : }
     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          94 :     return *this;
     384             : }
     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         104 :     return *this;
     439             : }
     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             : }
     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         100 :     return *this;
     476             : }
     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          86 :     return *this;
     498             : }
     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          74 :     return *this;
     519             : }
     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          78 :     return *this;
     542             : }
     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             : }
     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             :             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             :             auto rv = grammar::parse(
     615           1 :                 s.substr(1, s.size() - 2),
     616           1 :                     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             : }
     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             :             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             :             auto rv = grammar::parse(
     668          11 :                 s.substr(1, s.size() - 2),
     669          11 :                     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             : }
     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             :         auto rv = grammar::parse(
     714           8 :             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             : }
     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             :         auto rv = grammar::parse(
     758           6 :             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             : }
     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          32 :     return *this;
     806             : }
     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             :     auto s = addr.to_buffer(
     817          10 :         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          20 :     return *this;
     831             : }
     832             : 
     833             : url_base&
     834           7 : url_base::
     835             : set_host_ipvfuture(
     836             :     core::string_view s)
     837             : {
     838           8 :     op_t op(*this, &s);
     839             :     // validate
     840           1 :     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          12 :     return *this;
     852             : }
     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           8 :     return *this;
     885             : }
     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           8 :     return *this;
     919             : }
     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          46 :     return *this;
     938             : }
     939             : 
     940             : url_base&
     941          90 : url_base::
     942             : set_port(
     943             :     core::string_view s)
     944             : {
     945         109 :     op_t op(*this, &s);
     946          19 :     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         142 :     return *this;
     958             : }
     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          25 :     return *this;
     968             : }
     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         100 :     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             : }
    1072             : 
    1073             : url_base&
    1074          25 : url_base::
    1075             : set_path(
    1076             :     core::string_view s)
    1077             : {
    1078          50 :     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             : }
    1189             : 
    1190             : url_base&
    1191         163 : url_base::
    1192             : set_encoded_path(
    1193             :     pct_string_view s)
    1194             : {
    1195         326 :     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             : }
    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          94 :     return *this;
    1388             : }
    1389             : 
    1390             : params_ref
    1391          92 : url_base::
    1392             : params() noexcept
    1393             : {
    1394             :     return params_ref(
    1395             :         *this,
    1396             :         encoding_opts{
    1397          92 :             true, false, false});
    1398             : }
    1399             : 
    1400             : params_ref
    1401           1 : url_base::
    1402             : params(encoding_opts opt) noexcept
    1403             : {
    1404           1 :     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( std::initializer_list<param_view> ps ) noexcept
    1417             : {
    1418           1 :     params().assign(ps);
    1419           1 :     return *this;
    1420             : }
    1421             : 
    1422             : url_base&
    1423           1 : url_base::
    1424             : set_encoded_params( std::initializer_list< param_pct_view > ps ) noexcept
    1425             : {
    1426           1 :     encoded_params().assign(ps);
    1427           1 :     return *this;
    1428             : }
    1429             : 
    1430             : url_base&
    1431         222 : url_base::
    1432             : remove_query() noexcept
    1433             : {
    1434         222 :     op_t op(*this);
    1435         222 :     resize_impl(id_query, 0, op);
    1436         222 :     impl_.nparam_ = 0;
    1437         222 :     impl_.decoded_[id_query] = 0;
    1438         222 :     return *this;
    1439             : }
    1440             : 
    1441             : //------------------------------------------------
    1442             : //
    1443             : // Fragment
    1444             : //
    1445             : //------------------------------------------------
    1446             : 
    1447             : url_base&
    1448         215 : url_base::
    1449             : remove_fragment() noexcept
    1450             : {
    1451         215 :     op_t op(*this);
    1452         215 :     resize_impl(id_frag, 0, op);
    1453         215 :     impl_.decoded_[id_frag] = 0;
    1454         215 :     return *this;
    1455             : }
    1456             : 
    1457             : url_base&
    1458           7 : url_base::
    1459             : set_fragment(core::string_view s)
    1460             : {
    1461           7 :     op_t op(*this, &s);
    1462           7 :     encoding_opts opt;
    1463           7 :     auto const n = encoded_size(
    1464             :         s,
    1465             :         detail::fragment_chars,
    1466             :         opt);
    1467           7 :     auto dest = resize_impl(
    1468             :         id_frag, n + 1, op);
    1469           7 :     *dest++ = '#';
    1470           7 :     encode_unsafe(
    1471             :         dest,
    1472             :         n,
    1473             :         s,
    1474             :         detail::fragment_chars,
    1475             :         opt);
    1476           7 :     impl_.decoded_[id_frag] = s.size();
    1477          14 :     return *this;
    1478             : }
    1479             : 
    1480             : url_base&
    1481          57 : url_base::
    1482             : set_encoded_fragment(
    1483             :     pct_string_view s)
    1484             : {
    1485          57 :     op_t op(*this, &detail::ref(s));
    1486             :     auto const n =
    1487          57 :         detail::re_encoded_size_unsafe(
    1488             :             s,
    1489             :             detail::fragment_chars);
    1490          57 :     auto dest = resize_impl(
    1491          57 :         id_frag, n + 1, op);
    1492          57 :     *dest++ = '#';
    1493          57 :     impl_.decoded_[id_frag] =
    1494          57 :         detail::re_encode_unsafe(
    1495             :             dest,
    1496          57 :             dest + n,
    1497             :             s,
    1498             :             detail::fragment_chars);
    1499          57 :     BOOST_ASSERT(
    1500             :         impl_.decoded_[id_frag] ==
    1501             :             s.decoded_size());
    1502         114 :     return *this;
    1503             : }
    1504             : 
    1505             : //------------------------------------------------
    1506             : //
    1507             : // Resolution
    1508             : //
    1509             : //------------------------------------------------
    1510             : 
    1511             : system::result<void>
    1512         462 : url_base::
    1513             : resolve(
    1514             :     url_view_base const& ref)
    1515             : {
    1516         465 :     if (this == &ref &&
    1517           3 :         has_scheme())
    1518             :     {
    1519           2 :         normalize_path();
    1520           2 :         return {};
    1521             :     }
    1522             : 
    1523         460 :     if(! has_scheme())
    1524             :     {
    1525           2 :         BOOST_URL_RETURN_EC(error::not_a_base);
    1526             :     }
    1527             : 
    1528         916 :     op_t op(*this);
    1529             : 
    1530             :     //
    1531             :     // 5.2.2. Transform References
    1532             :     // https://datatracker.ietf.org/doc/html/rfc3986#section-5.2.2
    1533             :     //
    1534             : 
    1535         719 :     if( ref.has_scheme() &&
    1536         261 :         ref.scheme() != scheme())
    1537             :     {
    1538         198 :         reserve_impl(ref.size(), op);
    1539         198 :         copy(ref);
    1540         198 :         normalize_path();
    1541         198 :         return {};
    1542             :     }
    1543         260 :     if(ref.has_authority())
    1544             :     {
    1545          70 :         reserve_impl(
    1546          70 :             impl_.offset(id_user) + ref.size(), op);
    1547             :         set_encoded_authority(
    1548          70 :             ref.encoded_authority());
    1549             :         set_encoded_path(
    1550          70 :             ref.encoded_path());
    1551          70 :         if (ref.encoded_path().empty())
    1552          31 :             set_path_absolute(false);
    1553             :         else
    1554          39 :             normalize_path();
    1555          70 :         if(ref.has_query())
    1556             :             set_encoded_query(
    1557           5 :                 ref.encoded_query());
    1558             :         else
    1559          65 :             remove_query();
    1560          70 :         if(ref.has_fragment())
    1561             :             set_encoded_fragment(
    1562           5 :                 ref.encoded_fragment());
    1563             :         else
    1564          65 :             remove_fragment();
    1565          70 :         return {};
    1566             :     }
    1567         190 :     if(ref.encoded_path().empty())
    1568             :     {
    1569          32 :         reserve_impl(
    1570          32 :             impl_.offset(id_query) +
    1571          32 :             ref.size(), op);
    1572          32 :         normalize_path();
    1573          32 :         if(ref.has_query())
    1574             :         {
    1575             :             set_encoded_query(
    1576          10 :                 ref.encoded_query());
    1577             :         }
    1578          32 :         if(ref.has_fragment())
    1579             :             set_encoded_fragment(
    1580          18 :                 ref.encoded_fragment());
    1581          32 :         return {};
    1582             :     }
    1583         158 :     if(ref.is_path_absolute())
    1584             :     {
    1585          35 :         reserve_impl(
    1586          35 :             impl_.offset(id_path) +
    1587          35 :                 ref.size(), op);
    1588             :         set_encoded_path(
    1589          35 :             ref.encoded_path());
    1590          35 :         normalize_path();
    1591          35 :         if(ref.has_query())
    1592             :             set_encoded_query(
    1593           3 :                 ref.encoded_query());
    1594             :         else
    1595          32 :             remove_query();
    1596          35 :         if(ref.has_fragment())
    1597             :             set_encoded_fragment(
    1598           2 :                 ref.encoded_fragment());
    1599             :         else
    1600          33 :             remove_fragment();
    1601          35 :         return {};
    1602             :     }
    1603             :     // General case: ref is relative path
    1604         123 :     reserve_impl(
    1605         123 :         impl_.offset(id_query) +
    1606         123 :         ref.size(), op);
    1607             :     // 5.2.3. Merge Paths
    1608         123 :     auto es = encoded_segments();
    1609         123 :     if(es.size() > 0)
    1610             :     {
    1611         118 :         es.pop_back();
    1612             :     }
    1613         123 :     es.insert(es.end(),
    1614         123 :         ref.encoded_segments().begin(),
    1615         246 :         ref.encoded_segments().end());
    1616         123 :     normalize_path();
    1617         123 :     if(ref.has_query())
    1618             :         set_encoded_query(
    1619          10 :             ref.encoded_query());
    1620             :     else
    1621         113 :         remove_query();
    1622         123 :     if(ref.has_fragment())
    1623             :         set_encoded_fragment(
    1624          10 :             ref.encoded_fragment());
    1625             :     else
    1626         113 :         remove_fragment();
    1627         123 :     return {};
    1628             : }
    1629             : 
    1630             : //------------------------------------------------
    1631             : //
    1632             : // Normalization
    1633             : //
    1634             : //------------------------------------------------
    1635             : 
    1636             : template <class Charset>
    1637             : void
    1638        1917 : url_base::
    1639             : normalize_octets_impl(
    1640             :     int id,
    1641             :     Charset const& allowed,
    1642             :     op_t& op) noexcept
    1643             : {
    1644        1917 :     char* it = s_ + impl_.offset(id);
    1645        1917 :     char* end = s_ + impl_.offset(id + 1);
    1646        1917 :     char d = 0;
    1647        1917 :     char* dest = it;
    1648       10587 :     while (it < end)
    1649             :     {
    1650        8670 :         if (*it != '%')
    1651             :         {
    1652        8562 :             *dest = *it;
    1653        8562 :             ++it;
    1654        8562 :             ++dest;
    1655        8562 :             continue;
    1656             :         }
    1657         108 :         BOOST_ASSERT(end - it >= 3);
    1658             : 
    1659             :         // decode unreserved octets
    1660         108 :         d = detail::decode_one(it + 1);
    1661         108 :         if (allowed(d))
    1662             :         {
    1663          76 :             *dest = d;
    1664          76 :             it += 3;
    1665          76 :             ++dest;
    1666          76 :             continue;
    1667             :         }
    1668             : 
    1669             :         // uppercase percent-encoding triplets
    1670          32 :         *dest++ = '%';
    1671          32 :         ++it;
    1672          32 :         *dest++ = grammar::to_upper(*it++);
    1673          32 :         *dest++ = grammar::to_upper(*it++);
    1674             :     }
    1675        1917 :     if (it != dest)
    1676             :     {
    1677          24 :         auto diff = it - dest;
    1678          24 :         auto n = impl_.len(id) - diff;
    1679          24 :         shrink_impl(id, n, op);
    1680          24 :         s_[size()] = '\0';
    1681             :     }
    1682        1917 : }
    1683             : 
    1684             : url_base&
    1685          38 : url_base::
    1686             : normalize_scheme()
    1687             : {
    1688          38 :     to_lower_impl(id_scheme);
    1689          38 :     return *this;
    1690             : }
    1691             : 
    1692             : url_base&
    1693         383 : url_base::
    1694             : normalize_authority()
    1695             : {
    1696         383 :     op_t op(*this);
    1697             : 
    1698             :     // normalize host
    1699         383 :     if (host_type() == urls::host_type::name)
    1700             :     {
    1701         247 :         normalize_octets_impl(
    1702             :             id_host,
    1703             :             detail::reg_name_chars, op);
    1704             :     }
    1705         383 :     decoded_to_lower_impl(id_host);
    1706             : 
    1707             :     // normalize password
    1708         383 :     normalize_octets_impl(id_pass, detail::password_chars, op);
    1709             : 
    1710             :     // normalize user
    1711         383 :     normalize_octets_impl(id_user, detail::user_chars, op);
    1712         383 :     return *this;
    1713             : }
    1714             : 
    1715             : url_base&
    1716         832 : url_base::
    1717             : normalize_path()
    1718             : {
    1719         832 :     op_t op(*this);
    1720         832 :     normalize_octets_impl(id_path, detail::segment_chars, op);
    1721         832 :     core::string_view p = impl_.get(id_path);
    1722         832 :     char* p_dest = s_ + impl_.offset(id_path);
    1723         832 :     char* p_end = s_ + impl_.offset(id_path + 1);
    1724         832 :     auto pn = p.size();
    1725         832 :     auto skip_dot = 0;
    1726         832 :     bool encode_colons = false;
    1727         832 :     core::string_view first_seg;
    1728             : 
    1729             : //------------------------------------------------
    1730             : //
    1731             : //  Determine unnecessary initial dot segments to skip and
    1732             : //  if we need to encode colons in the first segment
    1733             : //
    1734         832 :     if (
    1735        1096 :         !has_authority() &&
    1736         264 :         p.starts_with("/./"))
    1737             :     {
    1738             :         // check if removing the "/./" would result in "//"
    1739             :         // ex: "/.//", "/././/", "/././/", ...
    1740          14 :         skip_dot = 2;
    1741          15 :         while (p.substr(skip_dot, 3).starts_with("/./"))
    1742           1 :             skip_dot += 2;
    1743          14 :         if (p.substr(skip_dot).starts_with("//"))
    1744          11 :             skip_dot = 2;
    1745             :         else
    1746           3 :             skip_dot = 0;
    1747             :     }
    1748         818 :     else if (
    1749         848 :         !has_scheme() &&
    1750          30 :         !has_authority())
    1751             :     {
    1752          30 :         if (p.starts_with("./"))
    1753             :         {
    1754             :             // check if removing the "./" would result in "//"
    1755             :             // ex: ".//", "././/", "././/", ...
    1756           7 :             skip_dot = 1;
    1757          10 :             while (p.substr(skip_dot, 3).starts_with("/./"))
    1758           3 :                 skip_dot += 2;
    1759           7 :             if (p.substr(skip_dot).starts_with("//"))
    1760           2 :                 skip_dot = 2;
    1761             :             else
    1762           5 :                 skip_dot = 0;
    1763             : 
    1764           7 :             if ( !skip_dot )
    1765             :             {
    1766             :                 // check if removing "./"s would leave us
    1767             :                 // a first segment with an ambiguous ":"
    1768           5 :                 first_seg = p.substr(2);
    1769           7 :                 while (first_seg.starts_with("./"))
    1770           2 :                     first_seg = first_seg.substr(2);
    1771           5 :                 auto i = first_seg.find('/');
    1772           5 :                 if (i != core::string_view::npos)
    1773           1 :                     first_seg = first_seg.substr(0, i);
    1774           5 :                 encode_colons = first_seg.contains(':');
    1775             :             }
    1776             :         }
    1777             :         else
    1778             :         {
    1779             :             // check if normalize_octets_impl
    1780             :             // didn't already create a ":"
    1781             :             // in the first segment
    1782          23 :             first_seg = p;
    1783          23 :             auto i = first_seg.find('/');
    1784          23 :             if (i != core::string_view::npos)
    1785          17 :                 first_seg = p.substr(0, i);
    1786          23 :             encode_colons = first_seg.contains(':');
    1787             :         }
    1788             :     }
    1789             : 
    1790             : //------------------------------------------------
    1791             : //
    1792             : //  Encode colons in the first segment
    1793             : //
    1794         832 :     if (encode_colons)
    1795             :     {
    1796             :         // prepend with "./"
    1797             :         // (resize_impl never throws)
    1798             :         auto cn =
    1799           5 :             std::count(
    1800             :                 first_seg.begin(),
    1801             :                 first_seg.end(),
    1802           5 :                 ':');
    1803           5 :         resize_impl(
    1804           5 :             id_path, pn + (2 * cn), op);
    1805             :         // move the 2nd, 3rd, ... segments
    1806           5 :         auto begin = s_ + impl_.offset(id_path);
    1807           5 :         auto it = begin;
    1808           5 :         auto end = begin + pn;
    1809          11 :         while (core::string_view(it, 2) == "./")
    1810           6 :             it += 2;
    1811          57 :         while (*it != '/' &&
    1812             :                it != end)
    1813          52 :             ++it;
    1814             :         // we don't need op here because this is
    1815             :         // an internal operation
    1816           5 :         std::memmove(it + (2 * cn), it, end - it);
    1817             : 
    1818             :         // move 1st segment
    1819           5 :         auto src = s_ + impl_.offset(id_path) + pn;
    1820           5 :         auto dest = s_ + impl_.offset(id_query);
    1821           5 :         src -= end - it;
    1822           5 :         dest -= end - it;
    1823           5 :         pn -= end - it;
    1824          59 :         do {
    1825          64 :             --src;
    1826          64 :             --dest;
    1827          64 :             if (*src != ':')
    1828             :             {
    1829          57 :                 *dest = *src;
    1830             :             }
    1831             :             else
    1832             :             {
    1833             :                 // use uppercase as required by
    1834             :                 // syntax-based normalization
    1835           7 :                 *dest-- = 'A';
    1836           7 :                 *dest-- = '3';
    1837           7 :                 *dest = '%';
    1838             :             }
    1839          64 :             --pn;
    1840          64 :         } while (pn);
    1841           5 :         skip_dot = 0;
    1842           5 :         p = impl_.get(id_path);
    1843           5 :         pn = p.size();
    1844           5 :         p_dest = s_ + impl_.offset(id_path);
    1845           5 :         p_end = s_ + impl_.offset(id_path + 1);
    1846             :     }
    1847             : 
    1848             : //------------------------------------------------
    1849             : //
    1850             : //  Remove "." and ".." segments
    1851             : //
    1852         832 :     p.remove_prefix(skip_dot);
    1853         832 :     p_dest += skip_dot;
    1854         832 :     auto n = detail::remove_dot_segments(
    1855             :         p_dest, p_end, p);
    1856             : 
    1857             : //------------------------------------------------
    1858             : //
    1859             : //  Update path parameters
    1860             : //
    1861         832 :     if (n != pn)
    1862             :     {
    1863         134 :         BOOST_ASSERT(n < pn);
    1864         134 :         shrink_impl(id_path, n + skip_dot, op);
    1865         134 :         p = encoded_path();
    1866         134 :         if (p == "/")
    1867           9 :             impl_.nseg_ = 0;
    1868         125 :         else if (!p.empty())
    1869         123 :             impl_.nseg_ = std::count(
    1870         246 :                 p.begin() + 1, p.end(), '/') + 1;
    1871             :         else
    1872           2 :             impl_.nseg_ = 0;
    1873         134 :         impl_.decoded_[id_path] =
    1874         134 :             detail::decode_bytes_unsafe(impl_.get(id_path));
    1875             :     }
    1876        1664 :     return *this;
    1877             : }
    1878             : 
    1879             : url_base&
    1880          36 : url_base::
    1881             : normalize_query()
    1882             : {
    1883          36 :     op_t op(*this);
    1884          36 :     normalize_octets_impl(
    1885             :         id_query, detail::query_chars, op);
    1886          36 :     return *this;
    1887             : }
    1888             : 
    1889             : url_base&
    1890          36 : url_base::
    1891             : normalize_fragment()
    1892             : {
    1893          36 :     op_t op(*this);
    1894          36 :     normalize_octets_impl(
    1895             :         id_frag, detail::fragment_chars, op);
    1896          36 :     return *this;
    1897             : }
    1898             : 
    1899             : url_base&
    1900          36 : url_base::
    1901             : normalize()
    1902             : {
    1903          36 :     normalize_fragment();
    1904          36 :     normalize_query();
    1905          36 :     normalize_path();
    1906          36 :     normalize_authority();
    1907          36 :     normalize_scheme();
    1908          36 :     return *this;
    1909             : }
    1910             : 
    1911             : //------------------------------------------------
    1912             : //
    1913             : // Implementation
    1914             : //
    1915             : //------------------------------------------------
    1916             : 
    1917             : void
    1918       17757 : url_base::
    1919             : check_invariants() const noexcept
    1920             : {
    1921       17757 :     BOOST_ASSERT(pi_);
    1922       17757 :     BOOST_ASSERT(
    1923             :         impl_.len(id_scheme) == 0 ||
    1924             :         impl_.get(id_scheme).ends_with(':'));
    1925       17757 :     BOOST_ASSERT(
    1926             :         impl_.len(id_user) == 0 ||
    1927             :         impl_.get(id_user).starts_with("//"));
    1928       17757 :     BOOST_ASSERT(
    1929             :         impl_.len(id_pass) == 0 ||
    1930             :         impl_.get(id_user).starts_with("//"));
    1931       17757 :     BOOST_ASSERT(
    1932             :         impl_.len(id_pass) == 0 ||
    1933             :         (impl_.len(id_pass) == 1 &&
    1934             :             impl_.get(id_pass) == "@") ||
    1935             :         (impl_.len(id_pass) > 1 &&
    1936             :             impl_.get(id_pass).starts_with(':') &&
    1937             :             impl_.get(id_pass).ends_with('@')));
    1938       17757 :     BOOST_ASSERT(
    1939             :         impl_.len(id_user, id_path) == 0 ||
    1940             :         impl_.get(id_user).starts_with("//"));
    1941       17757 :     BOOST_ASSERT(impl_.decoded_[id_path] >=
    1942             :         ((impl_.len(id_path) + 2) / 3));
    1943       17757 :     BOOST_ASSERT(
    1944             :         impl_.len(id_port) == 0 ||
    1945             :         impl_.get(id_port).starts_with(':'));
    1946       17757 :     BOOST_ASSERT(
    1947             :         impl_.len(id_query) == 0 ||
    1948             :         impl_.get(id_query).starts_with('?'));
    1949       17757 :     BOOST_ASSERT(
    1950             :         (impl_.len(id_query) == 0 && impl_.nparam_ == 0) ||
    1951             :         (impl_.len(id_query) > 0 && impl_.nparam_ > 0));
    1952       17757 :     BOOST_ASSERT(
    1953             :         impl_.len(id_frag) == 0 ||
    1954             :         impl_.get(id_frag).starts_with('#'));
    1955       17757 :     BOOST_ASSERT(c_str()[size()] == '\0');
    1956       17757 : }
    1957             : 
    1958             : char*
    1959        1512 : url_base::
    1960             : resize_impl(
    1961             :     int id,
    1962             :     std::size_t new_size,
    1963             :     op_t& op)
    1964             : {
    1965        1512 :     return resize_impl(
    1966        1512 :         id, id + 1, new_size, op);
    1967             : }
    1968             : 
    1969             : char*
    1970        1781 : url_base::
    1971             : resize_impl(
    1972             :     int first,
    1973             :     int last,
    1974             :     std::size_t new_len,
    1975             :     op_t& op)
    1976             : {
    1977        1781 :     auto const n0 = impl_.len(first, last);
    1978        1781 :     if(new_len == 0 && n0 == 0)
    1979         371 :         return s_ + impl_.offset(first);
    1980        1410 :     if(new_len <= n0)
    1981         501 :         return shrink_impl(
    1982         501 :             first, last, new_len, op);
    1983             : 
    1984             :     // growing
    1985         909 :     std::size_t n = new_len - n0;
    1986         909 :     reserve_impl(size() + n, op);
    1987             :     auto const pos =
    1988         909 :         impl_.offset(last);
    1989             :     // adjust chars
    1990         909 :     op.move(
    1991         909 :         s_ + pos + n,
    1992         909 :         s_ + pos,
    1993         909 :         impl_.offset(id_end) -
    1994             :             pos + 1);
    1995             :     // collapse (first, last)
    1996         909 :     impl_.collapse(first, last,
    1997         909 :         impl_.offset(last) + n);
    1998             :     // shift (last, end) right
    1999         909 :     impl_.adjust_right(last, id_end, n);
    2000         909 :     s_[size()] = '\0';
    2001         909 :     return s_ + impl_.offset(first);
    2002             : }
    2003             : 
    2004             : char*
    2005         158 : url_base::
    2006             : shrink_impl(
    2007             :     int id,
    2008             :     std::size_t new_size,
    2009             :     op_t& op)
    2010             : {
    2011         158 :     return shrink_impl(
    2012         158 :         id, id + 1, new_size, op);
    2013             : }
    2014             : 
    2015             : char*
    2016         659 : url_base::
    2017             : shrink_impl(
    2018             :     int first,
    2019             :     int last,
    2020             :     std::size_t new_len,
    2021             :     op_t& op)
    2022             : {
    2023             :     // shrinking
    2024         659 :     auto const n0 = impl_.len(first, last);
    2025         659 :     BOOST_ASSERT(new_len <= n0);
    2026         659 :     std::size_t n = n0 - new_len;
    2027             :     auto const pos =
    2028         659 :         impl_.offset(last);
    2029             :     // adjust chars
    2030         659 :     op.move(
    2031         659 :         s_ + pos - n,
    2032         659 :         s_ + pos,
    2033         659 :         impl_.offset(
    2034         659 :             id_end) - pos + 1);
    2035             :     // collapse (first, last)
    2036         659 :     impl_.collapse(first,  last,
    2037         659 :         impl_.offset(last) - n);
    2038             :     // shift (last, end) left
    2039         659 :     impl_.adjust_left(last, id_end, n);
    2040         659 :     s_[size()] = '\0';
    2041         659 :     return s_ + impl_.offset(first);
    2042             : }
    2043             : 
    2044             : //------------------------------------------------
    2045             : 
    2046             : void
    2047          61 : url_base::
    2048             : set_scheme_impl(
    2049             :     core::string_view s,
    2050             :     urls::scheme id)
    2051             : {
    2052         122 :     op_t op(*this, &s);
    2053          61 :     check_invariants();
    2054          13 :     grammar::parse(
    2055          13 :         s, detail::scheme_rule()
    2056          61 :             ).value(BOOST_URL_POS);
    2057          48 :     auto const n = s.size();
    2058          48 :     auto const p = impl_.offset(id_path);
    2059             : 
    2060             :     // check for "./" prefix
    2061             :     bool const has_dot =
    2062          75 :         [this, p]
    2063             :     {
    2064          48 :         if(impl_.nseg_ == 0)
    2065          30 :             return false;
    2066          18 :         if(first_segment().size() < 2)
    2067           9 :             return false;
    2068           9 :         auto const src = s_ + p;
    2069           9 :         if(src[0] != '.')
    2070           6 :             return false;
    2071           3 :         if(src[1] != '/')
    2072           0 :             return false;
    2073           3 :         return true;
    2074          48 :     }();
    2075             : 
    2076             :     // Remove "./"
    2077          48 :     if(has_dot)
    2078             :     {
    2079             :         // do this first, for
    2080             :         // strong exception safety
    2081           3 :         reserve_impl(
    2082           3 :             size() + n + 1 - 2, op);
    2083           3 :         op.move(
    2084           3 :             s_ + p,
    2085           3 :             s_ + p + 2,
    2086           3 :             size() + 1 -
    2087             :                 (p + 2));
    2088           3 :         impl_.set_size(
    2089             :             id_path,
    2090           3 :             impl_.len(id_path) - 2);
    2091           3 :         s_[size()] = '\0';
    2092             :     }
    2093             : 
    2094          48 :     auto dest = resize_impl(
    2095             :         id_scheme, n + 1, op);
    2096          48 :     s.copy(dest, n);
    2097          48 :     dest[n] = ':';
    2098          48 :     impl_.scheme_ = id;
    2099          48 :     check_invariants();
    2100          48 : }
    2101             : 
    2102             : char*
    2103         101 : url_base::
    2104             : set_user_impl(
    2105             :     std::size_t n,
    2106             :     op_t& op)
    2107             : {
    2108         101 :     check_invariants();
    2109         101 :     if(impl_.len(id_pass) != 0)
    2110             :     {
    2111             :         // keep "//"
    2112          50 :         auto dest = resize_impl(
    2113             :             id_user, 2 + n, op);
    2114          50 :         check_invariants();
    2115          50 :         return dest + 2;
    2116             :     }
    2117             :     // add authority
    2118             :     bool const make_absolute =
    2119          91 :         !is_path_absolute() &&
    2120          40 :         !impl_.get(id_path).empty();
    2121         102 :     auto dest = resize_impl(
    2122          51 :         id_user, 2 + n + 1 + make_absolute, op);
    2123          51 :     impl_.split(id_user, 2 + n);
    2124          51 :     dest[0] = '/';
    2125          51 :     dest[1] = '/';
    2126          51 :     dest[2 + n] = '@';
    2127          51 :     if (make_absolute)
    2128             :     {
    2129           4 :         impl_.split(id_pass, 1);
    2130           4 :         impl_.split(id_host, 0);
    2131           4 :         impl_.split(id_port, 0);
    2132           4 :         dest[3 + n] = '/';
    2133             :     }
    2134          51 :     check_invariants();
    2135          51 :     return dest + 2;
    2136             : }
    2137             : 
    2138             : char*
    2139          82 : url_base::
    2140             : set_password_impl(
    2141             :     std::size_t n,
    2142             :     op_t& op)
    2143             : {
    2144          82 :     check_invariants();
    2145          82 :     if(impl_.len(id_user) != 0)
    2146             :     {
    2147             :         // already have authority
    2148          66 :         auto const dest = resize_impl(
    2149             :             id_pass, 1 + n + 1, op);
    2150          66 :         dest[0] = ':';
    2151          66 :         dest[n + 1] = '@';
    2152          66 :         check_invariants();
    2153          66 :         return dest + 1;
    2154             :     }
    2155             :     // add authority
    2156             :     bool const make_absolute =
    2157          25 :             !is_path_absolute() &&
    2158           9 :             !impl_.get(id_path).empty();
    2159             :     auto const dest =
    2160          32 :         resize_impl(
    2161             :         id_user, id_host,
    2162          16 :         2 + 1 + n + 1 + make_absolute, op);
    2163          16 :     impl_.split(id_user, 2);
    2164          16 :     dest[0] = '/';
    2165          16 :     dest[1] = '/';
    2166          16 :     dest[2] = ':';
    2167          16 :     dest[2 + n + 1] = '@';
    2168          16 :     if (make_absolute)
    2169             :     {
    2170           2 :         impl_.split(id_pass, 2 + n);
    2171           2 :         impl_.split(id_host, 0);
    2172           2 :         impl_.split(id_port, 0);
    2173           2 :         dest[4 + n] = '/';
    2174             :     }
    2175          16 :     check_invariants();
    2176          16 :     return dest + 3;
    2177             : }
    2178             : 
    2179             : char*
    2180          99 : url_base::
    2181             : set_userinfo_impl(
    2182             :     std::size_t n,
    2183             :     op_t& op)
    2184             : {
    2185             :     // "//" {dest} "@"
    2186          99 :     check_invariants();
    2187             :     bool const make_absolute =
    2188         180 :             !is_path_absolute() &&
    2189          81 :             !impl_.get(id_path).empty();
    2190         198 :     auto dest = resize_impl(
    2191          99 :         id_user, id_host, n + 3 + make_absolute, op);
    2192          99 :     impl_.split(id_user, n + 2);
    2193          99 :     dest[0] = '/';
    2194          99 :     dest[1] = '/';
    2195          99 :     dest[n + 2] = '@';
    2196          99 :     if (make_absolute)
    2197             :     {
    2198           2 :         impl_.split(id_pass, 1);
    2199           2 :         impl_.split(id_host, 0);
    2200           2 :         impl_.split(id_port, 0);
    2201           2 :         dest[3 + n] = '/';
    2202             :     }
    2203          99 :     check_invariants();
    2204          99 :     return dest + 2;
    2205             : }
    2206             : 
    2207             : char*
    2208         206 : url_base::
    2209             : set_host_impl(
    2210             :     std::size_t n,
    2211             :     op_t& op)
    2212             : {
    2213         206 :     check_invariants();
    2214         206 :     if(impl_.len(id_user) == 0)
    2215             :     {
    2216             :         // add authority
    2217             :         bool make_absolute =
    2218         184 :             !is_path_absolute() &&
    2219          90 :             impl_.len(id_path) != 0;
    2220          94 :         auto pn = impl_.len(id_path);
    2221         188 :         auto dest = resize_impl(
    2222          94 :             id_user, n + 2 + make_absolute, op);
    2223          94 :         impl_.split(id_user, 2);
    2224          94 :         impl_.split(id_pass, 0);
    2225          94 :         impl_.split(id_host, n);
    2226          94 :         impl_.split(id_port, 0);
    2227          94 :         impl_.split(id_path, pn + make_absolute);
    2228          94 :         if (make_absolute)
    2229             :         {
    2230           7 :             dest[n + 2] = '/';
    2231           7 :             ++impl_.decoded_[id_path];
    2232             :         }
    2233          94 :         dest[0] = '/';
    2234          94 :         dest[1] = '/';
    2235          94 :         check_invariants();
    2236          94 :         return dest + 2;
    2237             :     }
    2238             :     // already have authority
    2239         112 :     auto const dest = resize_impl(
    2240             :         id_host, n, op);
    2241         112 :     check_invariants();
    2242         112 :     return dest;
    2243             : }
    2244             : 
    2245             : char*
    2246         107 : url_base::
    2247             : set_port_impl(
    2248             :     std::size_t n,
    2249             :     op_t& op)
    2250             : {
    2251         107 :     check_invariants();
    2252         107 :     if(impl_.len(id_user) != 0)
    2253             :     {
    2254             :         // authority exists
    2255          85 :         auto dest = resize_impl(
    2256             :             id_port, n + 1, op);
    2257          85 :         dest[0] = ':';
    2258          85 :         check_invariants();
    2259          85 :         return dest + 1;
    2260             :     }
    2261             :     bool make_absolute =
    2262          38 :         !is_path_absolute() &&
    2263          16 :         impl_.len(id_path) != 0;
    2264          44 :     auto dest = resize_impl(
    2265          22 :         id_user, 3 + n + make_absolute, op);
    2266          22 :     impl_.split(id_user, 2);
    2267          22 :     impl_.split(id_pass, 0);
    2268          22 :     impl_.split(id_host, 0);
    2269          22 :     dest[0] = '/';
    2270          22 :     dest[1] = '/';
    2271          22 :     dest[2] = ':';
    2272          22 :     if (make_absolute)
    2273             :     {
    2274           2 :         impl_.split(id_port, n + 1);
    2275           2 :         dest[n + 3] = '/';
    2276           2 :         ++impl_.decoded_[id_path];
    2277             :     }
    2278          22 :     check_invariants();
    2279          22 :     return dest + 3;
    2280             : }
    2281             : 
    2282             : char*
    2283         188 : url_base::
    2284             : set_path_impl(
    2285             :     std::size_t n,
    2286             :     op_t& op)
    2287             : {
    2288         188 :     check_invariants();
    2289         188 :     auto const dest = resize_impl(
    2290             :         id_path, n, op);
    2291         188 :     return dest;
    2292             : }
    2293             : 
    2294             : 
    2295             : //------------------------------------------------
    2296             : 
    2297             : // return the first segment of the path.
    2298             : // this is needed for some algorithms.
    2299             : core::string_view
    2300          45 : url_base::
    2301             : first_segment() const noexcept
    2302             : {
    2303          45 :     if(impl_.nseg_ == 0)
    2304           7 :         return {};
    2305          38 :     auto const p0 = impl_.cs_ +
    2306          38 :         impl_.offset(id_path) +
    2307          38 :             detail::path_prefix(
    2308          38 :                 impl_.get(id_path));
    2309          38 :     auto const end = impl_.cs_ +
    2310          38 :         impl_.offset(id_query);
    2311          38 :     if(impl_.nseg_ == 1)
    2312          42 :         return core::string_view(
    2313          21 :             p0, end - p0);
    2314          17 :     auto p = p0;
    2315          39 :     while(*p != '/')
    2316          22 :         ++p;
    2317          17 :     BOOST_ASSERT(p < end);
    2318          17 :     return core::string_view(p0, p - p0);
    2319             : }
    2320             : 
    2321             : detail::segments_iter_impl
    2322         597 : url_base::
    2323             : edit_segments(
    2324             :     detail::segments_iter_impl const& it0,
    2325             :     detail::segments_iter_impl const& it1,
    2326             :     detail::any_segments_iter&& src,
    2327             :     // -1 = preserve
    2328             :     //  0 = make relative (can fail)
    2329             :     //  1 = make absolute
    2330             :     int absolute)
    2331             : {
    2332             :     // Iterator doesn't belong to this url
    2333         597 :     BOOST_ASSERT(it0.ref.alias_of(impl_));
    2334             : 
    2335             :     // Iterator doesn't belong to this url
    2336         597 :     BOOST_ASSERT(it1.ref.alias_of(impl_));
    2337             : 
    2338             :     // Iterator is in the wrong order
    2339         597 :     BOOST_ASSERT(it0.index <= it1.index);
    2340             : 
    2341             :     // Iterator is out of range
    2342         597 :     BOOST_ASSERT(it0.index <= impl_.nseg_);
    2343         597 :     BOOST_ASSERT(it0.pos <= impl_.len(id_path));
    2344             : 
    2345             :     // Iterator is out of range
    2346         597 :     BOOST_ASSERT(it1.index <= impl_.nseg_);
    2347         597 :     BOOST_ASSERT(it1.pos <= impl_.len(id_path));
    2348             : 
    2349             : //------------------------------------------------
    2350             : //
    2351             : //  Calculate output prefix
    2352             : //
    2353             : //  0 = ""
    2354             : //  1 = "/"
    2355             : //  2 = "./"
    2356             : //  3 = "/./"
    2357             : //
    2358         597 :     bool const is_abs = is_path_absolute();
    2359         597 :     if(has_authority())
    2360             :     {
    2361             :         // Check if the new
    2362             :         // path would be empty
    2363         213 :         if( src.fast_nseg == 0 &&
    2364         108 :             it0.index == 0 &&
    2365          18 :             it1.index == impl_.nseg_)
    2366             :         {
    2367             :             // VFALCO we don't have
    2368             :             // access to nchar this early
    2369             :             //
    2370             :             //BOOST_ASSERT(nchar == 0);
    2371          15 :             absolute = 0;
    2372             :         }
    2373             :         else
    2374             :         {
    2375             :             // prefix "/" required
    2376         198 :             absolute = 1;
    2377             :         }
    2378             :     }
    2379         384 :     else if(absolute < 0)
    2380             :     {
    2381         384 :         absolute = is_abs; // preserve
    2382             :     }
    2383         597 :     auto const path_pos = impl_.offset(id_path);
    2384             : 
    2385         597 :     std::size_t nchar = 0;
    2386         597 :     std::size_t prefix = 0;
    2387         597 :     bool encode_colons = false;
    2388         597 :     bool cp_src_prefix = false;
    2389         597 :     if(it0.index > 0)
    2390             :     {
    2391             :         // first segment unchanged
    2392         323 :         prefix = src.fast_nseg > 0;
    2393             :     }
    2394         274 :     else if(src.fast_nseg > 0)
    2395             :     {
    2396             :         // first segment from src
    2397         221 :         if(! src.front.empty())
    2398             :         {
    2399         162 :             if( src.front == "." &&
    2400           7 :                     src.fast_nseg > 1)
    2401           4 :                 if (src.s.empty())
    2402             :                 {
    2403             :                     // if front is ".", we need the extra "." in the prefix
    2404             :                     // which will maintain the invariant that segments represent
    2405             :                     // {"."}
    2406           4 :                     prefix = 2 + absolute;
    2407             :                 }
    2408             :                 else
    2409             :                 {
    2410             :                     // if the "." prefix is explicitly required from set_path
    2411             :                     // we do not include an extra "." segment
    2412           0 :                     prefix = absolute;
    2413           0 :                     cp_src_prefix = true;
    2414             :                 }
    2415         151 :             else if(absolute)
    2416          79 :                 prefix = 1;
    2417         140 :             else if(has_scheme() ||
    2418          68 :                     ! src.front.contains(':'))
    2419          67 :                 prefix = 0;
    2420             :             else
    2421             :             {
    2422           5 :                 prefix = 0;
    2423           5 :                 encode_colons = true;
    2424             :             }
    2425             :         }
    2426             :         else
    2427             :         {
    2428          66 :             prefix = 2 + absolute;
    2429             :         }
    2430             :     }
    2431             :     else
    2432             :     {
    2433             :         // first segment from it1
    2434          53 :         auto const p =
    2435          53 :             impl_.cs_ + path_pos + it1.pos;
    2436         106 :         switch(impl_.cs_ +
    2437          53 :             impl_.offset(id_query) - p)
    2438             :         {
    2439          34 :         case 0:
    2440             :             // points to end
    2441          34 :             prefix = absolute;
    2442          34 :             break;
    2443          11 :         default:
    2444          11 :             BOOST_ASSERT(*p == '/');
    2445          11 :             if(p[1] != '/')
    2446             :             {
    2447          11 :                 if(absolute)
    2448           5 :                     prefix = 1;
    2449          11 :                 else if(has_scheme() ||
    2450          11 :                         ! it1.dereference().contains(':'))
    2451           5 :                     prefix = 0;
    2452             :                 else
    2453           1 :                     prefix = 2;
    2454          11 :                 break;
    2455             :             }
    2456             :             // empty
    2457             :             BOOST_FALLTHROUGH;
    2458             :         case 1:
    2459             :             // empty
    2460           8 :             BOOST_ASSERT(*p == '/');
    2461           8 :             prefix = 2 + absolute;
    2462           8 :             break;
    2463             :         }
    2464             :     }
    2465             : 
    2466             : //  append '/' to new segs
    2467             : //  if inserting at front.
    2468         597 :     std::size_t const suffix =
    2469         776 :         it1.index == 0 &&
    2470         660 :         impl_.nseg_ > 0 &&
    2471          63 :         src.fast_nseg > 0;
    2472             : 
    2473             : //------------------------------------------------
    2474             : //
    2475             : //  Measure the number of encoded characters
    2476             : //  of output, and the number of inserted
    2477             : //  segments including internal separators.
    2478             : //
    2479         597 :     src.encode_colons = encode_colons;
    2480         597 :     std::size_t nseg = 0;
    2481         597 :     if(src.measure(nchar))
    2482             :     {
    2483         408 :         src.encode_colons = false;
    2484             :         for(;;)
    2485             :         {
    2486         733 :             ++nseg;
    2487         733 :             if(! src.measure(nchar))
    2488         406 :                 break;
    2489         325 :             ++nchar;
    2490             :         }
    2491             :     }
    2492             : 
    2493         595 :     switch(src.fast_nseg)
    2494             :     {
    2495         189 :     case 0:
    2496         189 :         BOOST_ASSERT(nseg == 0);
    2497         189 :         break;
    2498         219 :     case 1:
    2499         219 :         BOOST_ASSERT(nseg == 1);
    2500         219 :         break;
    2501         187 :     case 2:
    2502         187 :         BOOST_ASSERT(nseg >= 2);
    2503         187 :         break;
    2504             :     }
    2505             : 
    2506             : //------------------------------------------------
    2507             : //
    2508             : //  Calculate [pos0, pos1) to remove
    2509             : //
    2510         595 :     auto pos0 = it0.pos;
    2511         595 :     if(it0.index == 0)
    2512             :     {
    2513             :         // patch pos for prefix
    2514         272 :         pos0 = 0;
    2515             :     }
    2516         595 :     auto pos1 = it1.pos;
    2517         595 :     if(it1.index == 0)
    2518             :     {
    2519             :         // patch pos for prefix
    2520         179 :         pos1 = detail::path_prefix(
    2521             :             impl_.get(id_path));
    2522             :     }
    2523         416 :     else if(
    2524         416 :         it0.index == 0 &&
    2525          93 :         it1.index < impl_.nseg_ &&
    2526             :         nseg == 0)
    2527             :     {
    2528             :         // Remove the slash from segment it1
    2529             :         // if it is becoming the new first
    2530             :         // segment.
    2531          19 :         ++pos1;
    2532             :     }
    2533             :     // calc decoded size of old range
    2534             :     auto const dn0 =
    2535         595 :         detail::decode_bytes_unsafe(
    2536             :             core::string_view(
    2537         595 :                 impl_.cs_ +
    2538         595 :                     impl_.offset(id_path) +
    2539             :                     pos0,
    2540             :                 pos1 - pos0));
    2541             : 
    2542             : //------------------------------------------------
    2543             : //
    2544             : //  Resize
    2545             : //
    2546        1190 :     op_t op(*this, &src.s);
    2547             :     char* dest;
    2548             :     char const* end;
    2549             :     {
    2550         595 :         auto const nremove = pos1 - pos0;
    2551             :         // check overflow
    2552        1190 :         if( nchar <= max_size() && (
    2553         595 :             prefix + suffix <=
    2554         595 :                 max_size() - nchar))
    2555             :         {
    2556         595 :             nchar = prefix + nchar + suffix;
    2557         939 :             if( nchar <= nremove ||
    2558         344 :                 nchar - nremove <=
    2559         344 :                     max_size() - size())
    2560         595 :                 goto ok;
    2561             :         }
    2562             :         // too large
    2563           0 :         detail::throw_length_error();
    2564         595 :     ok:
    2565             :         auto const new_size =
    2566         595 :             size() + nchar - nremove;
    2567         595 :         reserve_impl(new_size, op);
    2568         595 :         dest = s_ + path_pos + pos0;
    2569         595 :         op.move(
    2570         595 :             dest + nchar,
    2571         595 :             s_ + path_pos + pos1,
    2572         595 :             size() - path_pos - pos1);
    2573        1190 :         impl_.set_size(
    2574             :             id_path,
    2575         595 :             impl_.len(id_path) + nchar - nremove);
    2576         595 :         BOOST_ASSERT(size() == new_size);
    2577         595 :         end = dest + nchar;
    2578         595 :         impl_.nseg_ = impl_.nseg_ + nseg - (
    2579         595 :             it1.index - it0.index) - cp_src_prefix;
    2580         595 :         if(s_)
    2581         593 :             s_[size()] = '\0';
    2582             :     }
    2583             : 
    2584             : //------------------------------------------------
    2585             : //
    2586             : //  Output segments and internal separators:
    2587             : //
    2588             : //  prefix [ segment [ '/' segment ] ] suffix
    2589             : //
    2590         595 :     auto const dest0 = dest;
    2591         595 :     switch(prefix)
    2592             :     {
    2593          38 :     case 3:
    2594          38 :         *dest++ = '/';
    2595          38 :         *dest++ = '.';
    2596          38 :         *dest++ = '/';
    2597          38 :         break;
    2598          41 :     case 2:
    2599          41 :         *dest++ = '.';
    2600             :         BOOST_FALLTHROUGH;
    2601         323 :     case 1:
    2602         323 :         *dest++ = '/';
    2603         323 :         break;
    2604         234 :     default:
    2605         234 :         break;
    2606             :     }
    2607         595 :     src.rewind();
    2608         595 :     if(nseg > 0)
    2609             :     {
    2610         406 :         src.encode_colons = encode_colons;
    2611             :         for(;;)
    2612             :         {
    2613         731 :             src.copy(dest, end);
    2614         731 :             if(--nseg == 0)
    2615         406 :                 break;
    2616         325 :             *dest++ = '/';
    2617         325 :             src.encode_colons = false;
    2618             :         }
    2619         406 :         if(suffix)
    2620          63 :             *dest++ = '/';
    2621             :     }
    2622         595 :     BOOST_ASSERT(dest == dest0 + nchar);
    2623             : 
    2624             :     // calc decoded size of new range,
    2625             :     auto const dn =
    2626         595 :         detail::decode_bytes_unsafe(
    2627         595 :             core::string_view(dest0, dest - dest0));
    2628         595 :     impl_.decoded_[id_path] += dn - dn0;
    2629             : 
    2630             :     return detail::segments_iter_impl(
    2631        1190 :         impl_, pos0, it0.index);
    2632             : }
    2633             : 
    2634             : //------------------------------------------------
    2635             : 
    2636             : auto
    2637         138 : url_base::
    2638             : edit_params(
    2639             :     detail::params_iter_impl const& it0,
    2640             :     detail::params_iter_impl const& it1,
    2641             :     detail::any_params_iter&& src) ->
    2642             :         detail::params_iter_impl
    2643             : {
    2644         138 :     auto pos0 = impl_.offset(id_query);
    2645         138 :     auto pos1 = pos0 + it1.pos;
    2646         138 :     pos0 = pos0 + it0.pos;
    2647             : 
    2648             :     // Iterator doesn't belong to this url
    2649         138 :     BOOST_ASSERT(it0.ref.alias_of(impl_));
    2650             : 
    2651             :     // Iterator doesn't belong to this url
    2652         138 :     BOOST_ASSERT(it1.ref.alias_of(impl_));
    2653             : 
    2654             :     // Iterator is in the wrong order
    2655         138 :     BOOST_ASSERT(it0.index <= it1.index);
    2656             : 
    2657             :     // Iterator is out of range
    2658         138 :     BOOST_ASSERT(it0.index <= impl_.nparam_);
    2659         138 :     BOOST_ASSERT(pos0 <= impl_.offset(id_frag));
    2660             : 
    2661             :     // Iterator is out of range
    2662         138 :     BOOST_ASSERT(it1.index <= impl_.nparam_);
    2663         138 :     BOOST_ASSERT(pos1 <= impl_.offset(id_frag));
    2664             : 
    2665             :     // calc decoded size of old range,
    2666             :     // minus one if '?' or '&' prefixed
    2667             :     auto const dn0 =
    2668         138 :         detail::decode_bytes_unsafe(
    2669             :             core::string_view(
    2670         138 :                 impl_.cs_ + pos0,
    2671             :                 pos1 - pos0)) - (
    2672         138 :                     impl_.len(id_query) > 0);
    2673             : 
    2674             : //------------------------------------------------
    2675             : //
    2676             : //  Measure the number of encoded characters
    2677             : //  of output, and the number of inserted
    2678             : //  segments including internal separators.
    2679             : //
    2680             : 
    2681         138 :     std::size_t nchar = 0;
    2682         138 :     std::size_t nparam = 0;
    2683         138 :     if(src.measure(nchar))
    2684             :     {
    2685         111 :         ++nchar; // for '?' or '&'
    2686             :         for(;;)
    2687             :         {
    2688         176 :             ++nparam;
    2689         176 :             if(! src.measure(nchar))
    2690         111 :                 break;
    2691          65 :             ++nchar; // for '&'
    2692             :         }
    2693             :     }
    2694             : 
    2695             : //------------------------------------------------
    2696             : //
    2697             : //  Resize
    2698             : //
    2699         133 :     op_t op(*this, &src.s0, &src.s1);
    2700             :     char* dest;
    2701             :     char const* end;
    2702             :     {
    2703         133 :         auto const nremove = pos1 - pos0;
    2704             :         // check overflow
    2705         228 :         if( nchar > nremove &&
    2706          95 :             nchar - nremove >
    2707          95 :                 max_size() - size())
    2708             :         {
    2709             :             // too large
    2710           0 :             detail::throw_length_error();
    2711             :         }
    2712         133 :         auto const nparam1 =
    2713         133 :             impl_.nparam_ + nparam - (
    2714         133 :                 it1.index - it0.index);
    2715         133 :         reserve_impl(size() + nchar - nremove, op);
    2716         133 :         dest = s_ + pos0;
    2717         133 :         end = dest + nchar;
    2718         133 :         if(impl_.nparam_ > 0)
    2719             :         {
    2720             :             // needed when we move
    2721             :             // the beginning of the query
    2722          99 :             s_[impl_.offset(id_query)] = '&';
    2723             :         }
    2724         133 :         op.move(
    2725         133 :             dest + nchar,
    2726         133 :             impl_.cs_ + pos1,
    2727         133 :             size() - pos1);
    2728         266 :         impl_.set_size(
    2729             :             id_query,
    2730         133 :             impl_.len(id_query) +
    2731             :                 nchar - nremove);
    2732         133 :         impl_.nparam_ = nparam1;
    2733         133 :         if(nparam1 > 0)
    2734             :         {
    2735             :             // needed when we erase
    2736             :             // the beginning of the query
    2737         133 :             s_[impl_.offset(id_query)] = '?';
    2738             :         }
    2739         133 :         if(s_)
    2740         133 :             s_[size()] = '\0';
    2741             :     }
    2742         133 :     auto const dest0 = dest;
    2743             : 
    2744             : //------------------------------------------------
    2745             : //
    2746             : //  Output params and internal separators:
    2747             : //
    2748             : //  [ '?' param ] [ '&' param ]
    2749             : //
    2750         133 :     if(nparam > 0)
    2751             :     {
    2752         111 :         if(it0.index == 0)
    2753          68 :             *dest++ = '?';
    2754             :         else
    2755          43 :             *dest++ = '&';
    2756         111 :         src.rewind();
    2757             :         for(;;)
    2758             :         {
    2759         176 :             src.copy(dest, end);
    2760         176 :             if(--nparam == 0)
    2761         111 :                 break;
    2762          65 :             *dest++ = '&';
    2763             :         }
    2764             :     }
    2765             : 
    2766             :     // calc decoded size of new range,
    2767             :     // minus one if '?' or '&' prefixed
    2768             :     auto const dn =
    2769         133 :         detail::decode_bytes_unsafe(
    2770         133 :             core::string_view(dest0, dest - dest0)) - (
    2771         133 :                 impl_.len(id_query) > 0);
    2772             : 
    2773         133 :     impl_.decoded_[id_query] += (dn - dn0);
    2774             : 
    2775             :     return detail::params_iter_impl(
    2776         133 :         impl_,
    2777         133 :         pos0 - impl_.offset_[id_query],
    2778         266 :         it0.index);
    2779             : }
    2780             : 
    2781             : //------------------------------------------------
    2782             : 
    2783             : void
    2784         383 : url_base::
    2785             : decoded_to_lower_impl(int id) noexcept
    2786             : {
    2787         383 :     char* it = s_ + impl_.offset(id);
    2788         383 :     char const* const end = s_ + impl_.offset(id + 1);
    2789        2213 :     while(it < end)
    2790             :     {
    2791        1830 :         if (*it != '%')
    2792             :         {
    2793        3650 :             *it = grammar::to_lower(
    2794        1825 :                 *it);
    2795        1825 :             ++it;
    2796        1825 :             continue;
    2797             :         }
    2798           5 :         it += 3;
    2799             :     }
    2800         383 : }
    2801             : 
    2802             : void
    2803          38 : url_base::
    2804             : to_lower_impl(int id) noexcept
    2805             : {
    2806          38 :     char* it = s_ + impl_.offset(id);
    2807          38 :     char const* const end = s_ + impl_.offset(id + 1);
    2808         155 :     while(it < end)
    2809             :     {
    2810         234 :         *it = grammar::to_lower(
    2811         117 :             *it);
    2812         117 :         ++it;
    2813             :     }
    2814          38 : }
    2815             : 
    2816             : } // urls
    2817             : } // boost
    2818             : 

Generated by: LCOV version 1.15