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