LCOV - code coverage report
Current view: top level - url/impl - url_base.hpp (source / functions) Coverage Total Hit Missed
Test: coverage_remapped.info Lines: 99.4 % 1538 1529 9
Test Date: 2026-10-16 04:17:11 Functions: 100.0 % 80 80

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

Generated by: LCOV version 2.3