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

Generated by: LCOV version 1.15