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

Generated by: LCOV version 1.15