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