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