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