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