100.00% Lines (360/360) 100.00% Functions (9/9)
TLA Baseline Branch
Line Hits Code Line Hits Code
1   // 1   //
2   // Copyright (c) 2022 Alan de Freitas (alandefreitas@gmail.com) 2   // Copyright (c) 2022 Alan de Freitas (alandefreitas@gmail.com)
3   // 3   //
4   // Distributed under the Boost Software License, Version 1.0. (See accompanying 4   // Distributed under the Boost Software License, Version 1.0. (See accompanying
5   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6   // 6   //
7   // Official repository: https://github.com/boostorg/url 7   // Official repository: https://github.com/boostorg/url
8   // 8   //
9   9  
10   10  
11   #include <boost/url/detail/config.hpp> 11   #include <boost/url/detail/config.hpp>
12   #include "pattern.hpp" 12   #include "pattern.hpp"
13   #include "pct_format.hpp" 13   #include "pct_format.hpp"
14   #include "boost/url/detail/replacement_field_rule.hpp" 14   #include "boost/url/detail/replacement_field_rule.hpp"
15   #include <boost/url/grammar/alpha_chars.hpp> 15   #include <boost/url/grammar/alpha_chars.hpp>
16   #include <boost/url/grammar/optional_rule.hpp> 16   #include <boost/url/grammar/optional_rule.hpp>
17   #include <boost/url/grammar/token_rule.hpp> 17   #include <boost/url/grammar/token_rule.hpp>
18   #include <boost/url/rfc/detail/charsets.hpp> 18   #include <boost/url/rfc/detail/charsets.hpp>
19   #include <boost/url/rfc/detail/host_rule.hpp> 19   #include <boost/url/rfc/detail/host_rule.hpp>
20   #include <boost/url/rfc/detail/path_rules.hpp> 20   #include <boost/url/rfc/detail/path_rules.hpp>
21   #include <boost/url/rfc/detail/port_rule.hpp> 21   #include <boost/url/rfc/detail/port_rule.hpp>
22   #include <boost/url/rfc/detail/scheme_rule.hpp> 22   #include <boost/url/rfc/detail/scheme_rule.hpp>
23   23  
24   namespace boost { 24   namespace boost {
25   namespace urls { 25   namespace urls {
26   namespace detail { 26   namespace detail {
27   27  
28   static constexpr auto lhost_chars = host_chars + ':'; 28   static constexpr auto lhost_chars = host_chars + ':';
29   29  
30   void 30   void
HITCBC 31   161 pattern:: 31   161 pattern::
32   apply( 32   apply(
33   url_base& u, 33   url_base& u,
34   format_args const& args) const 34   format_args const& args) const
35   { 35   {
36   // measure total 36   // measure total
37   struct sizes 37   struct sizes
38   { 38   {
39   std::size_t scheme = 0; 39   std::size_t scheme = 0;
40   std::size_t user = 0; 40   std::size_t user = 0;
41   std::size_t pass = 0; 41   std::size_t pass = 0;
42   std::size_t host = 0; 42   std::size_t host = 0;
43   std::size_t port = 0; 43   std::size_t port = 0;
44   std::size_t path = 0; 44   std::size_t path = 0;
45   std::size_t query = 0; 45   std::size_t query = 0;
46   std::size_t frag = 0; 46   std::size_t frag = 0;
47   }; 47   };
HITCBC 48   161 sizes n; 48   161 sizes n;
49   49  
HITCBC 50   161 format_parse_context pctx(nullptr, nullptr, 0); 50   161 format_parse_context pctx(nullptr, nullptr, 0);
HITCBC 51   161 measure_context mctx(args); 51   161 measure_context mctx(args);
HITCBC 52   161 if (!scheme.empty()) 52   161 if (!scheme.empty())
53   { 53   {
HITCBC 54   70 pctx = {scheme, pctx.next_arg_id()}; 54   70 pctx = {scheme, pctx.next_arg_id()};
HITCBC 55   70 n.scheme = pct_vmeasure( 55   70 n.scheme = pct_vmeasure(
56   grammar::alpha_chars, pctx, mctx); 56   grammar::alpha_chars, pctx, mctx);
HITCBC 57   70 mctx.advance_to(0); 57   70 mctx.advance_to(0);
58   } 58   }
HITCBC 59   161 if (has_authority) 59   161 if (has_authority)
60   { 60   {
HITCBC 61   62 if (has_user) 61   62 if (has_user)
62   { 62   {
HITCBC 63   10 pctx = {user, pctx.next_arg_id()}; 63   10 pctx = {user, pctx.next_arg_id()};
HITCBC 64   10 n.user = pct_vmeasure( 64   10 n.user = pct_vmeasure(
65   user_chars, pctx, mctx); 65   user_chars, pctx, mctx);
HITCBC 66   10 mctx.advance_to(0); 66   10 mctx.advance_to(0);
HITCBC 67   10 if (has_pass) 67   10 if (has_pass)
68   { 68   {
HITCBC 69   7 pctx = {pass, pctx.next_arg_id()}; 69   7 pctx = {pass, pctx.next_arg_id()};
HITCBC 70   7 n.pass = pct_vmeasure( 70   7 n.pass = pct_vmeasure(
71   password_chars, pctx, mctx); 71   password_chars, pctx, mctx);
HITCBC 72   7 mctx.advance_to(0); 72   7 mctx.advance_to(0);
73   } 73   }
74   } 74   }
HITCBC 75   62 if (host.starts_with('[')) 75   62 if (host.starts_with('['))
76   { 76   {
HITCBC 77   2 BOOST_ASSERT(host.ends_with(']')); 77   2 BOOST_ASSERT(host.ends_with(']'));
HITCBC 78   2 pctx = {host.substr(1, host.size() - 2), pctx.next_arg_id()}; 78   2 pctx = {host.substr(1, host.size() - 2), pctx.next_arg_id()};
HITCBC 79   2 n.host = pct_vmeasure( 79   2 n.host = pct_vmeasure(
HITCBC 80   2 lhost_chars, pctx, mctx) + 2; 80   2 lhost_chars, pctx, mctx) + 2;
HITCBC 81   2 mctx.advance_to(0); 81   2 mctx.advance_to(0);
82   } 82   }
83   else 83   else
84   { 84   {
HITCBC 85   60 pctx = {host, pctx.next_arg_id()}; 85   60 pctx = {host, pctx.next_arg_id()};
HITCBC 86   60 n.host = pct_vmeasure( 86   60 n.host = pct_vmeasure(
87   host_chars, pctx, mctx); 87   host_chars, pctx, mctx);
HITCBC 88   60 mctx.advance_to(0); 88   60 mctx.advance_to(0);
89   } 89   }
HITCBC 90   62 if (has_port) 90   62 if (has_port)
91   { 91   {
HITCBC 92   22 pctx = {port, pctx.next_arg_id()}; 92   22 pctx = {port, pctx.next_arg_id()};
HITCBC 93   22 n.port = pct_vmeasure( 93   22 n.port = pct_vmeasure(
94   grammar::digit_chars, pctx, mctx); 94   grammar::digit_chars, pctx, mctx);
HITCBC 95   22 mctx.advance_to(0); 95   22 mctx.advance_to(0);
96   } 96   }
97   } 97   }
HITCBC 98   161 if (!path.empty()) 98   161 if (!path.empty())
99   { 99   {
HITCBC 100   123 pctx = {path, pctx.next_arg_id()}; 100   123 pctx = {path, pctx.next_arg_id()};
HITCBC 101   123 n.path = pct_vmeasure( 101   123 n.path = pct_vmeasure(
102   path_chars, pctx, mctx); 102   path_chars, pctx, mctx);
HITCBC 103   121 mctx.advance_to(0); 103   121 mctx.advance_to(0);
104   } 104   }
HITCBC 105   159 if (has_query) 105   159 if (has_query)
106   { 106   {
HITCBC 107   13 pctx = {query, pctx.next_arg_id()}; 107   13 pctx = {query, pctx.next_arg_id()};
HITCBC 108   13 n.query = pct_vmeasure( 108   13 n.query = pct_vmeasure(
109   query_chars, pctx, mctx); 109   query_chars, pctx, mctx);
HITCBC 110   13 mctx.advance_to(0); 110   13 mctx.advance_to(0);
111   } 111   }
HITCBC 112   159 if (has_frag) 112   159 if (has_frag)
113   { 113   {
HITCBC 114   7 pctx = {frag, pctx.next_arg_id()}; 114   7 pctx = {frag, pctx.next_arg_id()};
HITCBC 115   7 n.frag = pct_vmeasure( 115   7 n.frag = pct_vmeasure(
116   fragment_chars, pctx, mctx); 116   fragment_chars, pctx, mctx);
HITCBC 117   7 mctx.advance_to(0); 117   7 mctx.advance_to(0);
118   } 118   }
HITCBC 119   159 std::size_t const n_total = 119   159 std::size_t const n_total =
HITCBC 120   159 n.scheme + 120   159 n.scheme +
HITCBC 121   159 (n.scheme != 0) * 1 + // ":" 121   159 (n.scheme != 0) * 1 + // ":"
HITCBC 122   159 has_authority * 2 + // "//" 122   159 has_authority * 2 + // "//"
HITCBC 123   159 n.user + 123   159 n.user +
HITCBC 124   159 has_pass * 1 + // ":" 124   159 has_pass * 1 + // ":"
HITCBC 125   159 n.pass + 125   159 n.pass +
HITCBC 126   159 has_user * 1 + // "@" 126   159 has_user * 1 + // "@"
HITCBC 127   159 n.host + 127   159 n.host +
HITCBC 128   159 has_port * 1 + // ":" 128   159 has_port * 1 + // ":"
HITCBC 129   159 n.port + 129   159 n.port +
HITCBC 130   159 n.path + 130   159 n.path +
HITCBC 131   159 has_query * 1 + // "?" 131   159 has_query * 1 + // "?"
HITCBC 132   159 n.query + 132   159 n.query +
HITCBC 133   159 has_frag * 1 + // "#" 133   159 has_frag * 1 + // "#"
HITCBC 134   159 n.frag; 134   159 n.frag;
HITCBC 135   159 u.reserve(n_total); 135   159 u.reserve(n_total);
136   136  
137   // Apply 137   // Apply
HITCBC 138   158 pctx = {nullptr, nullptr, 0}; 138   158 pctx = {nullptr, nullptr, 0};
HITCBC 139   158 format_context fctx(nullptr, args); 139   158 format_context fctx(nullptr, args);
HITCBC 140   158 url_base::op_t op(u); 140   158 url_base::op_t op(u);
141   using parts = parts_base; 141   using parts = parts_base;
HITCBC 142   158 if (!scheme.empty()) 142   158 if (!scheme.empty())
143   { 143   {
HITCBC 144   138 auto dest = u.resize_impl( 144   138 auto dest = u.resize_impl(
145   parts::id_scheme, 145   parts::id_scheme,
HITCBC 146   69 n.scheme + 1, op); 146   69 n.scheme + 1, op);
HITCBC 147   69 pctx = {scheme, pctx.next_arg_id()}; 147   69 pctx = {scheme, pctx.next_arg_id()};
HITCBC 148   69 fctx.advance_to(dest); 148   69 fctx.advance_to(dest);
HITCBC 149   69 const char* dest1 = pct_vformat( 149   69 const char* dest1 = pct_vformat(
150   grammar::alpha_chars, pctx, fctx); 150   grammar::alpha_chars, pctx, fctx);
HITCBC 151   69 dest[n.scheme] = ':'; 151   69 dest[n.scheme] = ':';
152   // validate 152   // validate
HITCBC 153   69 if (!grammar::parse({dest, dest1}, scheme_rule())) 153   69 if (!grammar::parse({dest, dest1}, scheme_rule()))
154   { 154   {
HITCBC 155   1 throw_invalid_argument(); 155   1 throw_invalid_argument();
156   } 156   }
157   } 157   }
HITCBC 158   157 if (has_authority) 158   157 if (has_authority)
159   { 159   {
HITCBC 160   60 if (has_user) 160   60 if (has_user)
161   { 161   {
HITCBC 162   10 auto dest = u.set_user_impl( 162   10 auto dest = u.set_user_impl(
163   n.user, op); 163   n.user, op);
HITCBC 164   10 pctx = {user, pctx.next_arg_id()}; 164   10 pctx = {user, pctx.next_arg_id()};
HITCBC 165   10 fctx.advance_to(dest); 165   10 fctx.advance_to(dest);
HITCBC 166   10 char const* dest1 = pct_vformat( 166   10 char const* dest1 = pct_vformat(
167   user_chars, pctx, fctx); 167   user_chars, pctx, fctx);
HITCBC 168   10 u.impl_.decoded_[parts::id_user] = 168   10 u.impl_.decoded_[parts::id_user] =
HITCBC 169   10 detail::to_size_type( 169   10 detail::to_size_type(
HITCBC 170   10 pct_string_view(dest, dest1 - dest) 170   10 pct_string_view(dest, dest1 - dest)
171   ->decoded_size()); 171   ->decoded_size());
HITCBC 172   10 if (has_pass) 172   10 if (has_pass)
173   { 173   {
HITCBC 174   7 char* destp = u.set_password_impl( 174   7 char* destp = u.set_password_impl(
175   n.pass, op); 175   n.pass, op);
HITCBC 176   7 pctx = {pass, pctx.next_arg_id()}; 176   7 pctx = {pass, pctx.next_arg_id()};
HITCBC 177   7 fctx.advance_to(destp); 177   7 fctx.advance_to(destp);
HITCBC 178   7 dest1 = pct_vformat( 178   7 dest1 = pct_vformat(
179   password_chars, pctx, fctx); 179   password_chars, pctx, fctx);
HITCBC 180   7 u.impl_.decoded_[parts::id_pass] = 180   7 u.impl_.decoded_[parts::id_pass] =
HITCBC 181   7 detail::to_size_type( 181   7 detail::to_size_type(
HITCBC 182   14 pct_string_view({destp, dest1}) 182   14 pct_string_view({destp, dest1})
183   ->decoded_size()); 183   ->decoded_size());
184   } 184   }
185   } 185   }
HITCBC 186   60 auto dest = u.set_host_impl( 186   60 auto dest = u.set_host_impl(
187   n.host, op); 187   n.host, op);
HITCBC 188   60 if (host.starts_with('[')) 188   60 if (host.starts_with('['))
189   { 189   {
HITCBC 190   2 BOOST_ASSERT(host.ends_with(']')); 190   2 BOOST_ASSERT(host.ends_with(']'));
HITCBC 191   2 pctx = {host.substr(1, host.size() - 2), pctx.next_arg_id()}; 191   2 pctx = {host.substr(1, host.size() - 2), pctx.next_arg_id()};
HITCBC 192   2 *dest++ = '['; 192   2 *dest++ = '[';
HITCBC 193   2 fctx.advance_to(dest); 193   2 fctx.advance_to(dest);
194   char* dest1 = 194   char* dest1 =
HITCBC 195   2 pct_vformat(lhost_chars, pctx, fctx); 195   2 pct_vformat(lhost_chars, pctx, fctx);
HITCBC 196   2 *dest1++ = ']'; 196   2 *dest1++ = ']';
HITCBC 197   2 u.impl_.decoded_[parts::id_host] = 197   2 u.impl_.decoded_[parts::id_host] =
HITCBC 198   2 detail::to_size_type( 198   2 detail::to_size_type(
HITCBC 199   4 pct_string_view(dest - 1, dest1 - dest + 1) 199   4 pct_string_view(dest - 1, dest1 - dest + 1)
200   ->decoded_size()); 200   ->decoded_size());
201   } 201   }
202   else 202   else
203   { 203   {
HITCBC 204   58 pctx = {host, pctx.next_arg_id()}; 204   58 pctx = {host, pctx.next_arg_id()};
HITCBC 205   58 fctx.advance_to(dest); 205   58 fctx.advance_to(dest);
206   char const* dest1 = 206   char const* dest1 =
HITCBC 207   58 pct_vformat(host_chars, pctx, fctx); 207   58 pct_vformat(host_chars, pctx, fctx);
HITCBC 208   58 u.impl_.decoded_[parts::id_host] = 208   58 u.impl_.decoded_[parts::id_host] =
HITCBC 209   58 detail::to_size_type( 209   58 detail::to_size_type(
HITCBC 210   116 pct_string_view(dest, dest1 - dest) 210   116 pct_string_view(dest, dest1 - dest)
211   ->decoded_size()); 211   ->decoded_size());
212   } 212   }
HITCBC 213   60 auto uh = u.encoded_host(); 213   60 auto uh = u.encoded_host();
HITCBC 214   60 auto h = grammar::parse(uh, host_rule).value(); 214   60 auto h = grammar::parse(uh, host_rule).value();
HITCBC 215   60 std::memcpy( 215   60 std::memcpy(
HITCBC 216   60 u.impl_.ip_addr_, 216   60 u.impl_.ip_addr_,
217   h.addr, 217   h.addr,
218   sizeof(u.impl_.ip_addr_)); 218   sizeof(u.impl_.ip_addr_));
HITCBC 219   60 u.impl_.host_type_ = h.host_type; 219   60 u.impl_.host_type_ = h.host_type;
HITCBC 220   60 if (has_port) 220   60 if (has_port)
221   { 221   {
HITCBC 222   22 dest = u.set_port_impl(n.port, op); 222   22 dest = u.set_port_impl(n.port, op);
HITCBC 223   22 pctx = {port, pctx.next_arg_id()}; 223   22 pctx = {port, pctx.next_arg_id()};
HITCBC 224   22 fctx.advance_to(dest); 224   22 fctx.advance_to(dest);
HITCBC 225   22 char const* dest1 = pct_vformat( 225   22 char const* dest1 = pct_vformat(
226   grammar::digit_chars, pctx, fctx); 226   grammar::digit_chars, pctx, fctx);
HITCBC 227   22 core::string_view up = {dest - 1, dest1}; 227   22 core::string_view up = {dest - 1, dest1};
HITCBC 228   22 auto p = grammar::parse(up, detail::port_part_rule).value(); 228   22 auto p = grammar::parse(up, detail::port_part_rule).value();
HITCBC 229   22 if (p.has_port) 229   22 if (p.has_port)
HITCBC 230   22 u.impl_.port_number_ = p.port_number; 230   22 u.impl_.port_number_ = p.port_number;
231   } 231   }
232   } 232   }
HITCBC 233   157 if (!path.empty()) 233   157 if (!path.empty())
234   { 234   {
HITCBC 235   121 auto dest = u.resize_impl( 235   121 auto dest = u.resize_impl(
236   parts::id_path, 236   parts::id_path,
237   n.path, op); 237   n.path, op);
HITCBC 238   121 pctx = {path, pctx.next_arg_id()}; 238   121 pctx = {path, pctx.next_arg_id()};
HITCBC 239   121 fctx.advance_to(dest); 239   121 fctx.advance_to(dest);
HITCBC 240   121 auto dest1 = pct_vformat( 240   121 auto dest1 = pct_vformat(
241   path_chars, pctx, fctx); 241   path_chars, pctx, fctx);
HITCBC 242   121 pct_string_view npath(dest, dest1 - dest); 242   121 pct_string_view npath(dest, dest1 - dest);
HITCBC 243   121 u.impl_.decoded_[parts::id_path] += 243   121 u.impl_.decoded_[parts::id_path] +=
HITCBC 244   121 detail::to_size_type( 244   121 detail::to_size_type(
245   npath.decoded_size()); 245   npath.decoded_size());
HITCBC 246   121 if (!npath.empty()) 246   121 if (!npath.empty())
247   { 247   {
HITCBC 248   242 u.impl_.nseg_ = detail::to_size_type( 248   242 u.impl_.nseg_ = detail::to_size_type(
HITCBC 249   121 std::count( 249   121 std::count(
HITCBC 250   121 npath.begin() + 1, 250   121 npath.begin() + 1,
HITCBC 251   242 npath.end(), '/') + 1); 251   242 npath.end(), '/') + 1);
252   } 252   }
253   // handle edge cases 253   // handle edge cases
254   // 1) path is first component and the 254   // 1) path is first component and the
255   // first segment contains an unencoded ':' 255   // first segment contains an unencoded ':'
256   // This is impossible because the template 256   // This is impossible because the template
257   // "{}" would be a host. 257   // "{}" would be a host.
HITCBC 258   204 if (u.scheme().empty() && 258   204 if (u.scheme().empty() &&
HITCBC 259   83 !u.has_authority()) 259   83 !u.has_authority())
260   { 260   {
HITCBC 261   83 auto fseg = u.encoded_segments().front(); 261   83 auto fseg = u.encoded_segments().front();
HITCBC 262   83 std::size_t nc = std::count( 262   83 std::size_t nc = std::count(
HITCBC 263   83 fseg.begin(), fseg.end(), ':'); 263   83 fseg.begin(), fseg.end(), ':');
HITCBC 264   83 if (nc) 264   83 if (nc)
265   { 265   {
HITCBC 266   6 std::size_t diff = nc * 2; 266   6 std::size_t diff = nc * 2;
HITCBC 267   6 u.reserve(n_total + diff); 267   6 u.reserve(n_total + diff);
HITCBC 268   12 dest = u.resize_impl( 268   12 dest = u.resize_impl(
269   parts::id_path, 269   parts::id_path,
HITCBC 270   6 n.path + diff, op); 270   6 n.path + diff, op);
HITCBC 271   6 char* dest0 = dest + diff; 271   6 char* dest0 = dest + diff;
HITCBC 272   6 std::memmove(dest0, dest, n.path); 272   6 std::memmove(dest0, dest, n.path);
HITCBC 273   38 while (dest0 != dest) 273   38 while (dest0 != dest)
274   { 274   {
HITCBC 275   32 if (*dest0 != ':') 275   32 if (*dest0 != ':')
276   { 276   {
HITCBC 277   22 *dest++ = *dest0++; 277   22 *dest++ = *dest0++;
278   } 278   }
279   else 279   else
280   { 280   {
HITCBC 281   10 *dest++ = '%'; 281   10 *dest++ = '%';
HITCBC 282   10 *dest++ = '3'; 282   10 *dest++ = '3';
HITCBC 283   10 *dest++ = 'A'; 283   10 *dest++ = 'A';
HITCBC 284   10 dest0++; 284   10 dest0++;
285   } 285   }
286   } 286   }
HITCBC 287   6 n.path += diff; 287   6 n.path += diff;
288   } 288   }
289   } 289   }
290   // 2) url has no authority and path 290   // 2) url has no authority and path
291   // starts with "//" 291   // starts with "//"
HITCBC 292   213 if (!u.has_authority() && 292   213 if (!u.has_authority() &&
HITCBC 293   213 u.encoded_path().starts_with("//")) 293   213 u.encoded_path().starts_with("//"))
294   { 294   {
HITCBC 295   2 u.reserve(n_total + 2); 295   2 u.reserve(n_total + 2);
HITCBC 296   4 dest = u.resize_impl( 296   4 dest = u.resize_impl(
297   parts::id_path, 297   parts::id_path,
HITCBC 298   2 n.path + 2, op); 298   2 n.path + 2, op);
HITCBC 299   2 std::memmove(dest + 2, dest, n.path); 299   2 std::memmove(dest + 2, dest, n.path);
HITCBC 300   2 *dest++ = '/'; 300   2 *dest++ = '/';
HITCBC 301   2 *dest = '.'; 301   2 *dest = '.';
302   } 302   }
303   } 303   }
HITCBC 304   157 if (has_query) 304   157 if (has_query)
305   { 305   {
HITCBC 306   26 auto dest = u.resize_impl( 306   26 auto dest = u.resize_impl(
307   parts::id_query, 307   parts::id_query,
HITCBC 308   13 n.query + 1, op); 308   13 n.query + 1, op);
HITCBC 309   13 *dest++ = '?'; 309   13 *dest++ = '?';
HITCBC 310   13 pctx = {query, pctx.next_arg_id()}; 310   13 pctx = {query, pctx.next_arg_id()};
HITCBC 311   13 fctx.advance_to(dest); 311   13 fctx.advance_to(dest);
HITCBC 312   13 auto dest1 = pct_vformat( 312   13 auto dest1 = pct_vformat(
313   query_chars, pctx, fctx); 313   query_chars, pctx, fctx);
HITCBC 314   13 pct_string_view nquery(dest, dest1 - dest); 314   13 pct_string_view nquery(dest, dest1 - dest);
HITCBC 315   13 u.impl_.decoded_[parts::id_query] += 315   13 u.impl_.decoded_[parts::id_query] +=
HITCBC 316   13 detail::to_size_type( 316   13 detail::to_size_type(
HITCBC 317   13 nquery.decoded_size() + 1); 317   13 nquery.decoded_size() + 1);
HITCBC 318   13 if (!nquery.empty()) 318   13 if (!nquery.empty())
319   { 319   {
HITCBC 320   26 u.impl_.nparam_ = detail::to_size_type( 320   26 u.impl_.nparam_ = detail::to_size_type(
HITCBC 321   13 std::count( 321   13 std::count(
322   nquery.begin(), 322   nquery.begin(),
HITCBC 323   26 nquery.end(), '&') + 1); 323   26 nquery.end(), '&') + 1);
324   } 324   }
325   } 325   }
HITCBC 326   157 if (has_frag) 326   157 if (has_frag)
327   { 327   {
HITCBC 328   14 auto dest = u.resize_impl( 328   14 auto dest = u.resize_impl(
329   parts::id_frag, 329   parts::id_frag,
HITCBC 330   7 n.frag + 1, op); 330   7 n.frag + 1, op);
HITCBC 331   7 *dest++ = '#'; 331   7 *dest++ = '#';
HITCBC 332   7 pctx = {frag, pctx.next_arg_id()}; 332   7 pctx = {frag, pctx.next_arg_id()};
HITCBC 333   7 fctx.advance_to(dest); 333   7 fctx.advance_to(dest);
HITCBC 334   7 auto dest1 = pct_vformat( 334   7 auto dest1 = pct_vformat(
335   fragment_chars, pctx, fctx); 335   fragment_chars, pctx, fctx);
HITCBC 336   7 u.impl_.decoded_[parts::id_frag] += 336   7 u.impl_.decoded_[parts::id_frag] +=
HITCBC 337   7 detail::to_size_type( 337   7 detail::to_size_type(
HITCBC 338   14 make_pct_string_view( 338   14 make_pct_string_view(
HITCBC 339   7 core::string_view(dest, dest1 - dest)) 339   7 core::string_view(dest, dest1 - dest))
HITCBC 340   7 ->decoded_size() + 1); 340   7 ->decoded_size() + 1);
341   } 341   }
HITCBC 342   158 } 342   158 }
343   343  
344   // This rule represents a pct-encoded string 344   // This rule represents a pct-encoded string
345   // that contains an arbitrary number of 345   // that contains an arbitrary number of
346   // replacement ids in it 346   // replacement ids in it
347   template<class CharSet> 347   template<class CharSet>
348   struct pct_encoded_fmt_string_rule_t 348   struct pct_encoded_fmt_string_rule_t
349   { 349   {
350   using value_type = pct_string_view; 350   using value_type = pct_string_view;
351   351  
352   constexpr 352   constexpr
353   pct_encoded_fmt_string_rule_t( 353   pct_encoded_fmt_string_rule_t(
354   CharSet const& cs) noexcept 354   CharSet const& cs) noexcept
355   : cs_(cs) 355   : cs_(cs)
356   { 356   {
357   } 357   }
358   358  
359   template<class CharSet_> 359   template<class CharSet_>
360   friend 360   friend
361   constexpr 361   constexpr
362   auto 362   auto
363   pct_encoded_fmt_string_rule( 363   pct_encoded_fmt_string_rule(
364   CharSet_ const& cs) noexcept -> 364   CharSet_ const& cs) noexcept ->
365   pct_encoded_fmt_string_rule_t<CharSet_>; 365   pct_encoded_fmt_string_rule_t<CharSet_>;
366   366  
367   system::result<value_type> 367   system::result<value_type>
HITCBC 368   301 parse( 368   301 parse(
369   char const*& it, 369   char const*& it,
370   char const* end) const noexcept 370   char const* end) const noexcept
371   { 371   {
HITCBC 372   301 auto const start = it; 372   301 auto const start = it;
HITCBC 373   301 if(it == end) 373   301 if(it == end)
374   { 374   {
375   // this might be empty 375   // this might be empty
HITCBC 376   1 return {}; 376   1 return {};
377   } 377   }
378   378  
379   // consume some with literal rule 379   // consume some with literal rule
380   // this might be an empty literal 380   // this might be an empty literal
HITCBC 381   300 auto literal_rule = pct_encoded_rule(cs_); 381   300 auto literal_rule = pct_encoded_rule(cs_);
HITCBC 382   300 auto rv = literal_rule.parse(it, end); 382   300 auto rv = literal_rule.parse(it, end);
HITCBC 383   580 while (rv) 383   580 while (rv)
384   { 384   {
HITCBC 385   580 auto it0 = it; 385   580 auto it0 = it;
386   // consume some with replacement id 386   // consume some with replacement id
387   // rule 387   // rule
HITCBC 388   580 if (!replacement_field_rule.parse(it, end)) 388   580 if (!replacement_field_rule.parse(it, end))
389   { 389   {
HITCBC 390   300 it = it0; 390   300 it = it0;
HITCBC 391   300 break; 391   300 break;
392   } 392   }
HITCBC 393   280 rv = literal_rule.parse(it, end); 393   280 rv = literal_rule.parse(it, end);
394   } 394   }
395   395  
HITCBC 396   300 return core::string_view(start, it - start); 396   300 return core::string_view(start, it - start);
397   } 397   }
398   398  
399   private: 399   private:
400   CharSet cs_; 400   CharSet cs_;
401   }; 401   };
402   402  
403   template<class CharSet> 403   template<class CharSet>
404   constexpr 404   constexpr
405   auto 405   auto
406   pct_encoded_fmt_string_rule( 406   pct_encoded_fmt_string_rule(
407   CharSet const& cs) noexcept -> 407   CharSet const& cs) noexcept ->
408   pct_encoded_fmt_string_rule_t<CharSet> 408   pct_encoded_fmt_string_rule_t<CharSet>
409   { 409   {
410   // If an error occurs here it means that 410   // If an error occurs here it means that
411   // the value of your type does not meet 411   // the value of your type does not meet
412   // the requirements. Please check the 412   // the requirements. Please check the
413   // documentation! 413   // documentation!
414   static_assert( 414   static_assert(
415   grammar::is_charset<CharSet>::value, 415   grammar::is_charset<CharSet>::value,
416   "CharSet requirements not met"); 416   "CharSet requirements not met");
417   417  
418   return pct_encoded_fmt_string_rule_t<CharSet>(cs); 418   return pct_encoded_fmt_string_rule_t<CharSet>(cs);
419   } 419   }
420   420  
421   // This rule represents a regular string with 421   // This rule represents a regular string with
422   // only chars from the specified charset and 422   // only chars from the specified charset and
423   // an arbitrary number of replacement ids in it 423   // an arbitrary number of replacement ids in it
424   template<class CharSet> 424   template<class CharSet>
425   struct fmt_token_rule_t 425   struct fmt_token_rule_t
426   { 426   {
427   using value_type = pct_string_view; 427   using value_type = pct_string_view;
428   428  
429   constexpr 429   constexpr
430   fmt_token_rule_t( 430   fmt_token_rule_t(
431   CharSet const& cs) noexcept 431   CharSet const& cs) noexcept
432   : cs_(cs) 432   : cs_(cs)
433   { 433   {
434   } 434   }
435   435  
436   template<class CharSet_> 436   template<class CharSet_>
437   friend 437   friend
438   constexpr 438   constexpr
439   auto 439   auto
440   fmt_token_rule( 440   fmt_token_rule(
441   CharSet_ const& cs) noexcept -> 441   CharSet_ const& cs) noexcept ->
442   fmt_token_rule_t<CharSet_>; 442   fmt_token_rule_t<CharSet_>;
443   443  
444   system::result<value_type> 444   system::result<value_type>
HITCBC 445   22 parse( 445   22 parse(
446   char const*& it, 446   char const*& it,
447   char const* end) const noexcept 447   char const* end) const noexcept
448   { 448   {
HITCBC 449   22 auto const start = it; 449   22 auto const start = it;
HITCBC 450   22 BOOST_ASSERT(it != end); 450   22 BOOST_ASSERT(it != end);
451   /* 451   /*
452   // This should never happen because 452   // This should never happen because
453   // all tokens are optional and will 453   // all tokens are optional and will
454   // already return `none`: 454   // already return `none`:
455   if(it == end) 455   if(it == end)
456   { 456   {
457   BOOST_URL_RETURN_EC( 457   BOOST_URL_RETURN_EC(
458   grammar::error::need_more); 458   grammar::error::need_more);
459   } 459   }
460   */ 460   */
461   461  
462   // consume some with literal rule 462   // consume some with literal rule
463   // this might be an empty literal 463   // this might be an empty literal
464   auto partial_token_rule = 464   auto partial_token_rule =
HITCBC 465   22 grammar::optional_rule( 465   22 grammar::optional_rule(
HITCBC 466   22 grammar::token_rule(cs_)); 466   22 grammar::token_rule(cs_));
HITCBC 467   22 auto rv = partial_token_rule.parse(it, end); 467   22 auto rv = partial_token_rule.parse(it, end);
HITCBC 468   41 while (rv) 468   41 while (rv)
469   { 469   {
HITCBC 470   41 auto it0 = it; 470   41 auto it0 = it;
471   // consume some with replacement id 471   // consume some with replacement id
HITCBC 472   41 if (!replacement_field_rule.parse(it, end)) 472   41 if (!replacement_field_rule.parse(it, end))
473   { 473   {
474   // no replacement and no more cs 474   // no replacement and no more cs
475   // before: nothing else to consume 475   // before: nothing else to consume
HITCBC 476   22 it = it0; 476   22 it = it0;
HITCBC 477   22 break; 477   22 break;
478   } 478   }
479   // after {...}, consume any more chars 479   // after {...}, consume any more chars
480   // in the charset 480   // in the charset
HITCBC 481   19 rv = partial_token_rule.parse(it, end); 481   19 rv = partial_token_rule.parse(it, end);
482   } 482   }
483   483  
HITCBC 484   22 if(it == start) 484   22 if(it == start)
485   { 485   {
486   // it != end but we consumed nothing 486   // it != end but we consumed nothing
HITCBC 487   1 BOOST_URL_RETURN_EC( 487   1 BOOST_URL_RETURN_EC(
488   grammar::error::need_more); 488   grammar::error::need_more);
489   } 489   }
490   490  
HITCBC 491   21 return core::string_view(start, it - start); 491   21 return core::string_view(start, it - start);
492   } 492   }
493   493  
494   private: 494   private:
495   CharSet cs_; 495   CharSet cs_;
496   }; 496   };
497   497  
498   template<class CharSet> 498   template<class CharSet>
499   constexpr 499   constexpr
500   auto 500   auto
501   fmt_token_rule( 501   fmt_token_rule(
502   CharSet const& cs) noexcept -> 502   CharSet const& cs) noexcept ->
503   fmt_token_rule_t<CharSet> 503   fmt_token_rule_t<CharSet>
504   { 504   {
505   // If an error occurs here it means that 505   // If an error occurs here it means that
506   // the value of your type does not meet 506   // the value of your type does not meet
507   // the requirements. Please check the 507   // the requirements. Please check the
508   // documentation! 508   // documentation!
509   static_assert( 509   static_assert(
510   grammar::is_charset<CharSet>::value, 510   grammar::is_charset<CharSet>::value,
511   "CharSet requirements not met"); 511   "CharSet requirements not met");
512   512  
513   return fmt_token_rule_t<CharSet>(cs); 513   return fmt_token_rule_t<CharSet>(cs);
514   } 514   }
515   515  
516   struct userinfo_template_rule_t 516   struct userinfo_template_rule_t
517   { 517   {
518   struct value_type 518   struct value_type
519   { 519   {
520   core::string_view user; 520   core::string_view user;
521   core::string_view password; 521   core::string_view password;
522   bool has_password = false; 522   bool has_password = false;
523   }; 523   };
524   524  
525   auto 525   auto
HITCBC 526   63 parse( 526   63 parse(
527   char const*& it, 527   char const*& it,
528   char const* end 528   char const* end
529   ) const noexcept -> 529   ) const noexcept ->
530   system::result<value_type> 530   system::result<value_type>
531   { 531   {
532   static constexpr auto uchars = 532   static constexpr auto uchars =
533   unreserved_chars + 533   unreserved_chars +
534   sub_delim_chars; 534   sub_delim_chars;
535   static constexpr auto pwchars = 535   static constexpr auto pwchars =
536   uchars + ':'; 536   uchars + ':';
537   537  
HITCBC 538   63 value_type t; 538   63 value_type t;
539   539  
540   // user 540   // user
541   static constexpr auto user_fmt_rule = 541   static constexpr auto user_fmt_rule =
542   pct_encoded_fmt_string_rule(uchars); 542   pct_encoded_fmt_string_rule(uchars);
HITCBC 543   63 auto rv = grammar::parse( 543   63 auto rv = grammar::parse(
544   it, end, user_fmt_rule); 544   it, end, user_fmt_rule);
HITCBC 545   63 BOOST_ASSERT(rv); 545   63 BOOST_ASSERT(rv);
HITCBC 546   63 t.user = *rv; 546   63 t.user = *rv;
547   547  
548   // ':' 548   // ':'
HITCBC 549   63 if( it == end || 549   63 if( it == end ||
HITCBC 550   46 *it != ':') 550   46 *it != ':')
551   { 551   {
HITCBC 552   38 t.has_password = false; 552   38 t.has_password = false;
HITCBC 553   38 t.password = {}; 553   38 t.password = {};
HITCBC 554   38 return t; 554   38 return t;
555   } 555   }
HITCBC 556   25 ++it; 556   25 ++it;
557   557  
558   // pass 558   // pass
559   static constexpr auto pass_fmt_rule = 559   static constexpr auto pass_fmt_rule =
560   pct_encoded_fmt_string_rule(grammar::ref(pwchars)); 560   pct_encoded_fmt_string_rule(grammar::ref(pwchars));
HITCBC 561   25 rv = grammar::parse( 561   25 rv = grammar::parse(
562   it, end, pass_fmt_rule); 562   it, end, pass_fmt_rule);
HITCBC 563   25 BOOST_ASSERT(rv); 563   25 BOOST_ASSERT(rv);
HITCBC 564   25 t.has_password = true; 564   25 t.has_password = true;
HITCBC 565   25 t.password = *rv; 565   25 t.password = *rv;
566   566  
HITCBC 567   25 return t; 567   25 return t;
568   } 568   }
569   }; 569   };
570   570  
571   constexpr userinfo_template_rule_t userinfo_template_rule{}; 571   constexpr userinfo_template_rule_t userinfo_template_rule{};
572   572  
573   struct host_template_rule_t 573   struct host_template_rule_t
574   { 574   {
575   using value_type = core::string_view; 575   using value_type = core::string_view;
576   576  
577   auto 577   auto
HITCBC 578   64 parse( 578   64 parse(
579   char const*& it, 579   char const*& it,
580   char const* end 580   char const* end
581   ) const noexcept -> 581   ) const noexcept ->
582   system::result<value_type> 582   system::result<value_type>
583   { 583   {
HITCBC 584   64 if(it == end) 584   64 if(it == end)
585   { 585   {
586   // empty host 586   // empty host
HITCBC 587   1 return {}; 587   1 return {};
588   } 588   }
589   589  
590   // the host type will be ultimately 590   // the host type will be ultimately
591   // validated when applying the replacement 591   // validated when applying the replacement
592   // strings. Any chars allowed in hosts 592   // strings. Any chars allowed in hosts
593   // are allowed here. 593   // are allowed here.
HITCBC 594   63 if (*it != '[') 594   63 if (*it != '[')
595   { 595   {
596   // IPv4address and reg-name have the 596   // IPv4address and reg-name have the
597   // same char sets. 597   // same char sets.
HITCBC 598   60 constexpr auto any_host_template_rule = 598   60 constexpr auto any_host_template_rule =
599   pct_encoded_fmt_string_rule(host_chars); 599   pct_encoded_fmt_string_rule(host_chars);
HITCBC 600   60 auto rv = grammar::parse( 600   60 auto rv = grammar::parse(
601   it, end, any_host_template_rule); 601   it, end, any_host_template_rule);
602   // any_host_template_rule can always 602   // any_host_template_rule can always
603   // be empty, so it's never invalid 603   // be empty, so it's never invalid
HITCBC 604   60 BOOST_ASSERT(rv); 604   60 BOOST_ASSERT(rv);
HITCBC 605   60 return detail::to_sv(*rv); 605   60 return detail::to_sv(*rv);
606   } 606   }
607   // IP-literals need to be enclosed in 607   // IP-literals need to be enclosed in
608   // "[]" if using ':' in the template 608   // "[]" if using ':' in the template
609   // string, because the ':' would be 609   // string, because the ':' would be
610   // ambiguous with the port in fmt string. 610   // ambiguous with the port in fmt string.
611   // The "[]:" can be used in replacement 611   // The "[]:" can be used in replacement
612   // strings without the "[]" though. 612   // strings without the "[]" though.
HITCBC 613   3 constexpr auto ip_literal_template_rule = 613   3 constexpr auto ip_literal_template_rule =
614   pct_encoded_fmt_string_rule(lhost_chars); 614   pct_encoded_fmt_string_rule(lhost_chars);
HITCBC 615   3 auto it0 = it; 615   3 auto it0 = it;
616   auto rv = grammar::parse( 616   auto rv = grammar::parse(
617   it, end, 617   it, end,
HITCBC 618   3 grammar::optional_rule( 618   3 grammar::optional_rule(
HITCBC 619   3 grammar::tuple_rule( 619   3 grammar::tuple_rule(
HITCBC 620   3 grammar::squelch( 620   3 grammar::squelch(
HITCBC 621   3 grammar::delim_rule('[')), 621   3 grammar::delim_rule('[')),
622   ip_literal_template_rule, 622   ip_literal_template_rule,
HITCBC 623   3 grammar::squelch( 623   3 grammar::squelch(
HITCBC 624   6 grammar::delim_rule(']'))))); 624   6 grammar::delim_rule(']')))));
625   // ip_literal_template_rule can always 625   // ip_literal_template_rule can always
626   // be empty, so it's never invalid, but 626   // be empty, so it's never invalid, but
627   // the rule might fail to match the 627   // the rule might fail to match the
628   // closing "]" 628   // closing "]"
HITCBC 629   3 BOOST_ASSERT(rv); 629   3 BOOST_ASSERT(rv);
630   (void)rv; 630   (void)rv;
HITCBC 631   3 return core::string_view{it0, it}; 631   3 return core::string_view{it0, it};
632   } 632   }
633   }; 633   };
634   634  
635   constexpr host_template_rule_t host_template_rule{}; 635   constexpr host_template_rule_t host_template_rule{};
636   636  
637   struct authority_template_rule_t 637   struct authority_template_rule_t
638   { 638   {
639   using value_type = pattern; 639   using value_type = pattern;
640   640  
641   system::result<value_type> 641   system::result<value_type>
HITCBC 642   64 parse( 642   64 parse(
643   char const*& it, 643   char const*& it,
644   char const* end 644   char const* end
645   ) const noexcept 645   ) const noexcept
646   { 646   {
HITCBC 647   64 pattern u; 647   64 pattern u;
648   648  
649   // [ userinfo "@" ] 649   // [ userinfo "@" ]
650   { 650   {
651   auto rv = grammar::parse( 651   auto rv = grammar::parse(
652   it, end, 652   it, end,
HITCBC 653   64 grammar::optional_rule( 653   64 grammar::optional_rule(
HITCBC 654   64 grammar::tuple_rule( 654   64 grammar::tuple_rule(
655   userinfo_template_rule, 655   userinfo_template_rule,
HITCBC 656   64 grammar::squelch( 656   64 grammar::squelch(
HITCBC 657   128 grammar::delim_rule('@'))))); 657   128 grammar::delim_rule('@')))));
HITCBC 658   64 BOOST_ASSERT(rv); 658   64 BOOST_ASSERT(rv);
HITCBC 659   64 if(rv->has_value()) 659   64 if(rv->has_value())
660   { 660   {
HITCBC 661   11 auto& r = **rv; 661   11 auto& r = **rv;
HITCBC 662   11 u.has_user = true; 662   11 u.has_user = true;
HITCBC 663   11 u.user = r.user; 663   11 u.user = r.user;
HITCBC 664   11 u.has_pass = r.has_password; 664   11 u.has_pass = r.has_password;
HITCBC 665   11 u.pass = r.password; 665   11 u.pass = r.password;
666   } 666   }
667   } 667   }
668   668  
669   // host 669   // host
670   { 670   {
HITCBC 671   64 auto rv = grammar::parse( 671   64 auto rv = grammar::parse(
672   it, end, 672   it, end,
673   host_template_rule); 673   host_template_rule);
674   // host is allowed to be empty 674   // host is allowed to be empty
HITCBC 675   64 BOOST_ASSERT(rv); 675   64 BOOST_ASSERT(rv);
HITCBC 676   64 u.host = *rv; 676   64 u.host = *rv;
677   } 677   }
678   678  
679   // [ ":" port ] 679   // [ ":" port ]
680   { 680   {
681   constexpr auto port_template_rule = 681   constexpr auto port_template_rule =
682   grammar::optional_rule( 682   grammar::optional_rule(
683   fmt_token_rule(grammar::digit_chars)); 683   fmt_token_rule(grammar::digit_chars));
HITCBC 684   64 auto it0 = it; 684   64 auto it0 = it;
685   auto rv = grammar::parse( 685   auto rv = grammar::parse(
686   it, end, 686   it, end,
HITCBC 687   64 grammar::tuple_rule( 687   64 grammar::tuple_rule(
HITCBC 688   64 grammar::squelch( 688   64 grammar::squelch(
HITCBC 689   64 grammar::delim_rule(':')), 689   64 grammar::delim_rule(':')),
HITCBC 690   64 port_template_rule)); 690   64 port_template_rule));
HITCBC 691   64 if (!rv) 691   64 if (!rv)
692   { 692   {
HITCBC 693   41 it = it0; 693   41 it = it0;
694   } 694   }
695   else 695   else
696   { 696   {
HITCBC 697   23 u.has_port = true; 697   23 u.has_port = true;
HITCBC 698   23 if (rv->has_value()) 698   23 if (rv->has_value())
699   { 699   {
HITCBC 700   21 u.port = **rv; 700   21 u.port = **rv;
701   } 701   }
702   } 702   }
703   } 703   }
704   704  
HITCBC 705   64 return u; 705   64 return u;
706   } 706   }
707   }; 707   };
708   708  
709   constexpr authority_template_rule_t authority_template_rule{}; 709   constexpr authority_template_rule_t authority_template_rule{};
710   710  
711   struct scheme_template_rule_t 711   struct scheme_template_rule_t
712   { 712   {
713   using value_type = core::string_view; 713   using value_type = core::string_view;
714   714  
715   system::result<value_type> 715   system::result<value_type>
HITCBC 716   168 parse( 716   168 parse(
717   char const*& it, 717   char const*& it,
718   char const* end) const noexcept 718   char const* end) const noexcept
719   { 719   {
HITCBC 720   168 auto const start = it; 720   168 auto const start = it;
HITCBC 721   168 if(it == end) 721   168 if(it == end)
722   { 722   {
723   // scheme can't be empty 723   // scheme can't be empty
HITCBC 724   1 BOOST_URL_RETURN_EC( 724   1 BOOST_URL_RETURN_EC(
725   grammar::error::mismatch); 725   grammar::error::mismatch);
726   } 726   }
HITCBC 727   305 if(!grammar::alpha_chars(*it) && 727   305 if(!grammar::alpha_chars(*it) &&
HITCBC 728   138 *it != '{') 728   138 *it != '{')
729   { 729   {
730   // expected alpha 730   // expected alpha
HITCBC 731   22 BOOST_URL_RETURN_EC( 731   22 BOOST_URL_RETURN_EC(
732   grammar::error::mismatch); 732   grammar::error::mismatch);
733   } 733   }
734   734  
735   // it starts with replacement id or alpha char 735   // it starts with replacement id or alpha char
HITCBC 736   145 if (!grammar::alpha_chars(*it)) 736   145 if (!grammar::alpha_chars(*it))
737   { 737   {
HITCBC 738   116 if (!replacement_field_rule.parse(it, end)) 738   116 if (!replacement_field_rule.parse(it, end))
739   { 739   {
740   // replacement_field_rule is invalid 740   // replacement_field_rule is invalid
HITCBC 741   2 BOOST_URL_RETURN_EC( 741   2 BOOST_URL_RETURN_EC(
742   grammar::error::mismatch); 742   grammar::error::mismatch);
743   } 743   }
744   } 744   }
745   else 745   else
746   { 746   {
747   // skip first 747   // skip first
HITCBC 748   29 ++it; 748   29 ++it;
749   } 749   }
750   750  
751   static 751   static
752   constexpr 752   constexpr
753   grammar::lut_chars scheme_chars( 753   grammar::lut_chars scheme_chars(
754   "0123456789" "+-." 754   "0123456789" "+-."
755   "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 755   "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
756   "abcdefghijklmnopqrstuvwxyz"); 756   "abcdefghijklmnopqrstuvwxyz");
757   757  
758   // non-scheme chars might be a new 758   // non-scheme chars might be a new
759   // replacement-id or just an invalid char 759   // replacement-id or just an invalid char
HITCBC 760   143 it = grammar::find_if_not( 760   143 it = grammar::find_if_not(
761   it, end, scheme_chars); 761   it, end, scheme_chars);
HITCBC 762   146 while (it != end) 762   146 while (it != end)
763   { 763   {
HITCBC 764   92 auto it0 = it; 764   92 auto it0 = it;
HITCBC 765   92 if (!replacement_field_rule.parse(it, end)) 765   92 if (!replacement_field_rule.parse(it, end))
766   { 766   {
HITCBC 767   89 it = it0; 767   89 it = it0;
HITCBC 768   89 break; 768   89 break;
769   } 769   }
HITCBC 770   3 it = grammar::find_if_not( 770   3 it = grammar::find_if_not(
771   it, end, scheme_chars); 771   it, end, scheme_chars);
772   } 772   }
HITCBC 773   143 return core::string_view(start, it - start); 773   143 return core::string_view(start, it - start);
774   } 774   }
775   }; 775   };
776   776  
777   constexpr scheme_template_rule_t scheme_template_rule{}; 777   constexpr scheme_template_rule_t scheme_template_rule{};
778   778  
779   // This rule should consider all url types at the 779   // This rule should consider all url types at the
780   // same time according to the format string 780   // same time according to the format string
781   // - relative urls with no scheme/authority 781   // - relative urls with no scheme/authority
782   // - absolute urls have no fragment 782   // - absolute urls have no fragment
783   struct pattern_rule_t 783   struct pattern_rule_t
784   { 784   {
785   using value_type = pattern; 785   using value_type = pattern;
786   786  
787   system::result<value_type> 787   system::result<value_type>
HITCBC 788   168 parse( 788   168 parse(
789   char const*& it, 789   char const*& it,
790   char const* const end 790   char const* const end
791   ) const noexcept 791   ) const noexcept
792   { 792   {
HITCBC 793   168 pattern u; 793   168 pattern u;
794   794  
795   // optional scheme 795   // optional scheme
796   { 796   {
HITCBC 797   168 auto it0 = it; 797   168 auto it0 = it;
HITCBC 798   168 auto rv = grammar::parse( 798   168 auto rv = grammar::parse(
799   it, end, 799   it, end,
HITCBC 800   168 grammar::tuple_rule( 800   168 grammar::tuple_rule(
801   scheme_template_rule, 801   scheme_template_rule,
HITCBC 802   168 grammar::squelch( 802   168 grammar::squelch(
HITCBC 803   168 grammar::delim_rule(':')))); 803   168 grammar::delim_rule(':'))));
HITCBC 804   168 if(rv) 804   168 if(rv)
HITCBC 805   75 u.scheme = *rv; 805   75 u.scheme = *rv;
806   else 806   else
HITCBC 807   93 it = it0; 807   93 it = it0;
808   } 808   }
809   809  
810   // hier_part (authority + path) 810   // hier_part (authority + path)
811   // if there are less than 2 chars left, 811   // if there are less than 2 chars left,
812   // we are parsing the path 812   // we are parsing the path
HITCBC 813   168 if (it == end) 813   168 if (it == end)
814   { 814   {
815   // this is over, so we can consider 815   // this is over, so we can consider
816   // that a "path-empty" 816   // that a "path-empty"
HITCBC 817   4 return u; 817   4 return u;
818   } 818   }
HITCBC 819   164 if(end - it == 1) 819   164 if(end - it == 1)
820   { 820   {
821   // only one char left 821   // only one char left
822   // it can be a single separator "/", 822   // it can be a single separator "/",
823   // representing an empty absolute path, 823   // representing an empty absolute path,
824   // or a single-char segment 824   // or a single-char segment
HITCBC 825   5 if(*it == '/') 825   5 if(*it == '/')
826   { 826   {
827   // path-absolute 827   // path-absolute
HITCBC 828   2 u.path = {it, 1}; 828   2 u.path = {it, 1};
HITCBC 829   2 ++it; 829   2 ++it;
HITCBC 830   2 return u; 830   2 return u;
831   } 831   }
832   // this can be a: 832   // this can be a:
833   // - path-noscheme if there's no scheme, or 833   // - path-noscheme if there's no scheme, or
834   // - path-rootless with a single char, or 834   // - path-rootless with a single char, or
835   // - path-empty (and consume nothing) 835   // - path-empty (and consume nothing)
HITCBC 836   4 if (!u.scheme.empty() || 836   4 if (!u.scheme.empty() ||
HITCBC 837   1 *it != ':') 837   1 *it != ':')
838   { 838   {
839   // path-rootless with a single char 839   // path-rootless with a single char
840   // this needs to be a segment because 840   // this needs to be a segment because
841   // the authority needs two slashes 841   // the authority needs two slashes
842   // "//" 842   // "//"
843   // path-noscheme also matches here 843   // path-noscheme also matches here
844   // because we already validated the 844   // because we already validated the
845   // first char 845   // first char
HITCBC 846   3 auto rv = grammar::parse( 846   3 auto rv = grammar::parse(
847   it, end, urls::detail::segment_rule); 847   it, end, urls::detail::segment_rule);
HITCBC 848   3 if(! rv) 848   3 if(! rv)
HITCBC 849   1 return rv.error(); 849   1 return rv.error();
HITCBC 850   2 u.path = *rv; 850   2 u.path = *rv;
851   } 851   }
HITCBC 852   2 return u; 852   2 return u;
853   } 853   }
854   854  
855   // authority 855   // authority
HITCBC 856   159 if( it[0] == '/' && 856   159 if( it[0] == '/' &&
HITCBC 857   79 it[1] == '/') 857   79 it[1] == '/')
858   { 858   {
859   // "//" always indicates authority 859   // "//" always indicates authority
HITCBC 860   64 it += 2; 860   64 it += 2;
HITCBC 861   64 auto rv = grammar::parse( 861   64 auto rv = grammar::parse(
862   it, end, 862   it, end,
863   authority_template_rule); 863   authority_template_rule);
864   // authority is allowed to be empty 864   // authority is allowed to be empty
HITCBC 865   64 BOOST_ASSERT(rv); 865   64 BOOST_ASSERT(rv);
HITCBC 866   64 u.has_authority = true; 866   64 u.has_authority = true;
HITCBC 867   64 u.has_user = rv->has_user; 867   64 u.has_user = rv->has_user;
HITCBC 868   64 u.user = rv->user; 868   64 u.user = rv->user;
HITCBC 869   64 u.has_pass = rv->has_pass; 869   64 u.has_pass = rv->has_pass;
HITCBC 870   64 u.pass = rv->pass; 870   64 u.pass = rv->pass;
HITCBC 871   64 u.host = rv->host; 871   64 u.host = rv->host;
HITCBC 872   64 u.has_port = rv->has_port; 872   64 u.has_port = rv->has_port;
HITCBC 873   64 u.port = rv->port; 873   64 u.port = rv->port;
874   } 874   }
875   875  
876   // the authority requires an absolute path 876   // the authority requires an absolute path
877   // or an empty path 877   // or an empty path
HITCBC 878   159 if (it == end || 878   159 if (it == end ||
HITCBC 879   132 (u.has_authority && 879   132 (u.has_authority &&
HITCBC 880   37 (*it != '/' && 880   37 (*it != '/' &&
HITCBC 881   8 *it != '?' && 881   8 *it != '?' &&
HITCBC 882   2 *it != '#'))) 882   2 *it != '#')))
883   { 883   {
884   // path-empty 884   // path-empty
HITCBC 885   29 return u; 885   29 return u;
886   } 886   }
887   887  
888   // path-abempty 888   // path-abempty
889   // consume the whole path at once because 889   // consume the whole path at once because
890   // we're going to count number of segments 890   // we're going to count number of segments
891   // later after the replacements happen 891   // later after the replacements happen
892   static constexpr auto segment_fmt_rule = 892   static constexpr auto segment_fmt_rule =
893   pct_encoded_fmt_string_rule(path_chars); 893   pct_encoded_fmt_string_rule(path_chars);
HITCBC 894   130 auto rp = grammar::parse( 894   130 auto rp = grammar::parse(
895   it, end, segment_fmt_rule); 895   it, end, segment_fmt_rule);
896   // path-abempty is allowed to be empty 896   // path-abempty is allowed to be empty
HITCBC 897   130 BOOST_ASSERT(rp); 897   130 BOOST_ASSERT(rp);
HITCBC 898   130 u.path = *rp; 898   130 u.path = *rp;
899   899  
900   // [ "?" query ] 900   // [ "?" query ]
901   { 901   {
902   static constexpr auto query_fmt_rule = 902   static constexpr auto query_fmt_rule =
903   pct_encoded_fmt_string_rule(query_chars); 903   pct_encoded_fmt_string_rule(query_chars);
HITCBC 904   130 auto rv = grammar::parse( 904   130 auto rv = grammar::parse(
905   it, end, 905   it, end,
HITCBC 906   130 grammar::tuple_rule( 906   130 grammar::tuple_rule(
HITCBC 907   130 grammar::squelch( 907   130 grammar::squelch(
HITCBC 908   130 grammar::delim_rule('?')), 908   130 grammar::delim_rule('?')),
909   query_fmt_rule)); 909   query_fmt_rule));
910   // query is allowed to be empty but 910   // query is allowed to be empty but
911   // delim rule is not 911   // delim rule is not
HITCBC 912   130 if (rv) 912   130 if (rv)
913   { 913   {
HITCBC 914   13 u.has_query = true; 914   13 u.has_query = true;
HITCBC 915   13 u.query = *rv; 915   13 u.query = *rv;
916   } 916   }
917   } 917   }
918   918  
919   // [ "#" fragment ] 919   // [ "#" fragment ]
920   { 920   {
921   static constexpr auto frag_fmt_rule = 921   static constexpr auto frag_fmt_rule =
922   pct_encoded_fmt_string_rule(fragment_chars); 922   pct_encoded_fmt_string_rule(fragment_chars);
HITCBC 923   130 auto rv = grammar::parse( 923   130 auto rv = grammar::parse(
924   it, end, 924   it, end,
HITCBC 925   130 grammar::tuple_rule( 925   130 grammar::tuple_rule(
HITCBC 926   130 grammar::squelch( 926   130 grammar::squelch(
HITCBC 927   130 grammar::delim_rule('#')), 927   130 grammar::delim_rule('#')),
928   frag_fmt_rule)); 928   frag_fmt_rule));
929   // frag is allowed to be empty but 929   // frag is allowed to be empty but
930   // delim rule is not 930   // delim rule is not
HITCBC 931   130 if (rv) 931   130 if (rv)
932   { 932   {
HITCBC 933   7 u.has_frag = true; 933   7 u.has_frag = true;
HITCBC 934   7 u.frag = *rv; 934   7 u.frag = *rv;
935   } 935   }
936   } 936   }
937   937  
HITCBC 938   130 return u; 938   130 return u;
939   } 939   }
940   }; 940   };
941   941  
942   constexpr pattern_rule_t pattern_rule{}; 942   constexpr pattern_rule_t pattern_rule{};
943   943  
944   system::result<pattern> 944   system::result<pattern>
HITCBC 945   168 parse_pattern( 945   168 parse_pattern(
946   core::string_view s) 946   core::string_view s)
947   { 947   {
HITCBC 948   168 return grammar::parse( 948   168 return grammar::parse(
HITCBC 949   168 s, pattern_rule); 949   168 s, pattern_rule);
950   } 950   }
951   951  
952   } // detail 952   } // detail
953   } // urls 953   } // urls
954   } // boost 954   } // boost