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
31  
pattern::
31  
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  
    };
48  
    sizes n;
48  
    sizes n;
49  

49  

50  
    format_parse_context pctx(nullptr, nullptr, 0);
50  
    format_parse_context pctx(nullptr, nullptr, 0);
51  
    measure_context mctx(args);
51  
    measure_context mctx(args);
52  
    if (!scheme.empty())
52  
    if (!scheme.empty())
53  
    {
53  
    {
54  
        pctx = {scheme, pctx.next_arg_id()};
54  
        pctx = {scheme, pctx.next_arg_id()};
55  
        n.scheme = pct_vmeasure(
55  
        n.scheme = pct_vmeasure(
56  
            grammar::alpha_chars, pctx, mctx);
56  
            grammar::alpha_chars, pctx, mctx);
57  
        mctx.advance_to(0);
57  
        mctx.advance_to(0);
58  
    }
58  
    }
59  
    if (has_authority)
59  
    if (has_authority)
60  
    {
60  
    {
61  
        if (has_user)
61  
        if (has_user)
62  
        {
62  
        {
63  
            pctx = {user, pctx.next_arg_id()};
63  
            pctx = {user, pctx.next_arg_id()};
64  
            n.user = pct_vmeasure(
64  
            n.user = pct_vmeasure(
65  
                user_chars, pctx, mctx);
65  
                user_chars, pctx, mctx);
66  
            mctx.advance_to(0);
66  
            mctx.advance_to(0);
67  
            if (has_pass)
67  
            if (has_pass)
68  
            {
68  
            {
69  
                pctx = {pass, pctx.next_arg_id()};
69  
                pctx = {pass, pctx.next_arg_id()};
70  
                n.pass = pct_vmeasure(
70  
                n.pass = pct_vmeasure(
71  
                    password_chars, pctx, mctx);
71  
                    password_chars, pctx, mctx);
72  
                mctx.advance_to(0);
72  
                mctx.advance_to(0);
73  
            }
73  
            }
74  
        }
74  
        }
75  
        if (host.starts_with('['))
75  
        if (host.starts_with('['))
76  
        {
76  
        {
77  
            BOOST_ASSERT(host.ends_with(']'));
77  
            BOOST_ASSERT(host.ends_with(']'));
78  
            pctx = {host.substr(1, host.size() - 2), pctx.next_arg_id()};
78  
            pctx = {host.substr(1, host.size() - 2), pctx.next_arg_id()};
79  
            n.host = pct_vmeasure(
79  
            n.host = pct_vmeasure(
80  
                lhost_chars, pctx, mctx) + 2;
80  
                lhost_chars, pctx, mctx) + 2;
81  
            mctx.advance_to(0);
81  
            mctx.advance_to(0);
82  
        }
82  
        }
83  
        else
83  
        else
84  
        {
84  
        {
85  
            pctx = {host, pctx.next_arg_id()};
85  
            pctx = {host, pctx.next_arg_id()};
86  
            n.host = pct_vmeasure(
86  
            n.host = pct_vmeasure(
87  
                host_chars, pctx, mctx);
87  
                host_chars, pctx, mctx);
88  
            mctx.advance_to(0);
88  
            mctx.advance_to(0);
89  
        }
89  
        }
90  
        if (has_port)
90  
        if (has_port)
91  
        {
91  
        {
92  
            pctx = {port, pctx.next_arg_id()};
92  
            pctx = {port, pctx.next_arg_id()};
93  
            n.port = pct_vmeasure(
93  
            n.port = pct_vmeasure(
94  
                grammar::digit_chars, pctx, mctx);
94  
                grammar::digit_chars, pctx, mctx);
95  
            mctx.advance_to(0);
95  
            mctx.advance_to(0);
96  
        }
96  
        }
97  
    }
97  
    }
98  
    if (!path.empty())
98  
    if (!path.empty())
99  
    {
99  
    {
100  
        pctx = {path, pctx.next_arg_id()};
100  
        pctx = {path, pctx.next_arg_id()};
101  
        n.path = pct_vmeasure(
101  
        n.path = pct_vmeasure(
102  
            path_chars, pctx, mctx);
102  
            path_chars, pctx, mctx);
103  
        mctx.advance_to(0);
103  
        mctx.advance_to(0);
104  
    }
104  
    }
105  
    if (has_query)
105  
    if (has_query)
106  
    {
106  
    {
107  
        pctx = {query, pctx.next_arg_id()};
107  
        pctx = {query, pctx.next_arg_id()};
108  
        n.query = pct_vmeasure(
108  
        n.query = pct_vmeasure(
109  
            query_chars, pctx, mctx);
109  
            query_chars, pctx, mctx);
110  
        mctx.advance_to(0);
110  
        mctx.advance_to(0);
111  
    }
111  
    }
112  
    if (has_frag)
112  
    if (has_frag)
113  
    {
113  
    {
114  
        pctx = {frag, pctx.next_arg_id()};
114  
        pctx = {frag, pctx.next_arg_id()};
115  
        n.frag = pct_vmeasure(
115  
        n.frag = pct_vmeasure(
116  
            fragment_chars, pctx, mctx);
116  
            fragment_chars, pctx, mctx);
117  
        mctx.advance_to(0);
117  
        mctx.advance_to(0);
118  
    }
118  
    }
119  
    std::size_t const n_total =
119  
    std::size_t const n_total =
120  
        n.scheme +
120  
        n.scheme +
121  
        (n.scheme != 0) * 1 + // ":"
121  
        (n.scheme != 0) * 1 + // ":"
122  
        has_authority * 2 +   // "//"
122  
        has_authority * 2 +   // "//"
123  
        n.user +
123  
        n.user +
124  
        has_pass * 1 +        // ":"
124  
        has_pass * 1 +        // ":"
125  
        n.pass +
125  
        n.pass +
126  
        has_user * 1 +        // "@"
126  
        has_user * 1 +        // "@"
127  
        n.host +
127  
        n.host +
128  
        has_port * 1 +        // ":"
128  
        has_port * 1 +        // ":"
129  
        n.port +
129  
        n.port +
130  
        n.path +
130  
        n.path +
131  
        has_query * 1 +       // "?"
131  
        has_query * 1 +       // "?"
132  
        n.query +
132  
        n.query +
133  
        has_frag * 1 +        // "#"
133  
        has_frag * 1 +        // "#"
134  
        n.frag;
134  
        n.frag;
135  
    u.reserve(n_total);
135  
    u.reserve(n_total);
136  

136  

137  
    // Apply
137  
    // Apply
138  
    pctx = {nullptr, nullptr, 0};
138  
    pctx = {nullptr, nullptr, 0};
139  
    format_context fctx(nullptr, args);
139  
    format_context fctx(nullptr, args);
140  
    url_base::op_t op(u);
140  
    url_base::op_t op(u);
141  
    using parts = parts_base;
141  
    using parts = parts_base;
142  
    if (!scheme.empty())
142  
    if (!scheme.empty())
143  
    {
143  
    {
144  
        auto dest = u.resize_impl(
144  
        auto dest = u.resize_impl(
145  
            parts::id_scheme,
145  
            parts::id_scheme,
146  
            n.scheme + 1, op);
146  
            n.scheme + 1, op);
147  
        pctx = {scheme, pctx.next_arg_id()};
147  
        pctx = {scheme, pctx.next_arg_id()};
148  
        fctx.advance_to(dest);
148  
        fctx.advance_to(dest);
149  
        const char* dest1 = pct_vformat(
149  
        const char* dest1 = pct_vformat(
150  
            grammar::alpha_chars, pctx, fctx);
150  
            grammar::alpha_chars, pctx, fctx);
151  
        dest[n.scheme] = ':';
151  
        dest[n.scheme] = ':';
152  
        // validate
152  
        // validate
153  
        if (!grammar::parse({dest, dest1}, scheme_rule()))
153  
        if (!grammar::parse({dest, dest1}, scheme_rule()))
154  
        {
154  
        {
155  
            throw_invalid_argument();
155  
            throw_invalid_argument();
156  
        }
156  
        }
157  
    }
157  
    }
158  
    if (has_authority)
158  
    if (has_authority)
159  
    {
159  
    {
160  
        if (has_user)
160  
        if (has_user)
161  
        {
161  
        {
162  
            auto dest = u.set_user_impl(
162  
            auto dest = u.set_user_impl(
163  
                n.user, op);
163  
                n.user, op);
164  
            pctx = {user, pctx.next_arg_id()};
164  
            pctx = {user, pctx.next_arg_id()};
165  
            fctx.advance_to(dest);
165  
            fctx.advance_to(dest);
166  
            char const* dest1 = pct_vformat(
166  
            char const* dest1 = pct_vformat(
167  
                user_chars, pctx, fctx);
167  
                user_chars, pctx, fctx);
168  
            u.impl_.decoded_[parts::id_user] =
168  
            u.impl_.decoded_[parts::id_user] =
169  
                detail::to_size_type(
169  
                detail::to_size_type(
170  
                    pct_string_view(dest, dest1 - dest)
170  
                    pct_string_view(dest, dest1 - dest)
171  
                        ->decoded_size());
171  
                        ->decoded_size());
172  
            if (has_pass)
172  
            if (has_pass)
173  
            {
173  
            {
174  
                char* destp = u.set_password_impl(
174  
                char* destp = u.set_password_impl(
175  
                    n.pass, op);
175  
                    n.pass, op);
176  
                pctx = {pass, pctx.next_arg_id()};
176  
                pctx = {pass, pctx.next_arg_id()};
177  
                fctx.advance_to(destp);
177  
                fctx.advance_to(destp);
178  
                dest1 = pct_vformat(
178  
                dest1 = pct_vformat(
179  
                    password_chars, pctx, fctx);
179  
                    password_chars, pctx, fctx);
180  
                u.impl_.decoded_[parts::id_pass] =
180  
                u.impl_.decoded_[parts::id_pass] =
181  
                    detail::to_size_type(
181  
                    detail::to_size_type(
182  
                        pct_string_view({destp, dest1})
182  
                        pct_string_view({destp, dest1})
183  
                            ->decoded_size() + 1);
183  
                            ->decoded_size() + 1);
184  
            }
184  
            }
185  
        }
185  
        }
186  
        auto dest = u.set_host_impl(
186  
        auto dest = u.set_host_impl(
187  
            n.host, op);
187  
            n.host, op);
188  
        if (host.starts_with('['))
188  
        if (host.starts_with('['))
189  
        {
189  
        {
190  
            BOOST_ASSERT(host.ends_with(']'));
190  
            BOOST_ASSERT(host.ends_with(']'));
191  
            pctx = {host.substr(1, host.size() - 2), pctx.next_arg_id()};
191  
            pctx = {host.substr(1, host.size() - 2), pctx.next_arg_id()};
192  
            *dest++ = '[';
192  
            *dest++ = '[';
193  
            fctx.advance_to(dest);
193  
            fctx.advance_to(dest);
194  
            char* dest1 =
194  
            char* dest1 =
195  
                pct_vformat(lhost_chars, pctx, fctx);
195  
                pct_vformat(lhost_chars, pctx, fctx);
196  
            *dest1++ = ']';
196  
            *dest1++ = ']';
197  
            u.impl_.decoded_[parts::id_host] =
197  
            u.impl_.decoded_[parts::id_host] =
198  
                detail::to_size_type(
198  
                detail::to_size_type(
199  
                    pct_string_view(dest - 1, dest1 - dest)
199  
                    pct_string_view(dest - 1, dest1 - dest)
200  
                        ->decoded_size());
200  
                        ->decoded_size());
201  
        }
201  
        }
202  
        else
202  
        else
203  
        {
203  
        {
204  
            pctx = {host, pctx.next_arg_id()};
204  
            pctx = {host, pctx.next_arg_id()};
205  
            fctx.advance_to(dest);
205  
            fctx.advance_to(dest);
206  
            char const* dest1 =
206  
            char const* dest1 =
207  
                pct_vformat(host_chars, pctx, fctx);
207  
                pct_vformat(host_chars, pctx, fctx);
208  
            u.impl_.decoded_[parts::id_host] =
208  
            u.impl_.decoded_[parts::id_host] =
209  
                detail::to_size_type(
209  
                detail::to_size_type(
210  
                    pct_string_view(dest, dest1 - dest)
210  
                    pct_string_view(dest, dest1 - dest)
211  
                        ->decoded_size());
211  
                        ->decoded_size());
212  
        }
212  
        }
213  
        auto uh = u.encoded_host();
213  
        auto uh = u.encoded_host();
214  
        auto h = grammar::parse(uh, host_rule).value();
214  
        auto h = grammar::parse(uh, host_rule).value();
215  
        std::memcpy(
215  
        std::memcpy(
216  
            u.impl_.ip_addr_,
216  
            u.impl_.ip_addr_,
217  
            h.addr,
217  
            h.addr,
218  
            sizeof(u.impl_.ip_addr_));
218  
            sizeof(u.impl_.ip_addr_));
219  
        u.impl_.host_type_ = h.host_type;
219  
        u.impl_.host_type_ = h.host_type;
220  
        if (has_port)
220  
        if (has_port)
221  
        {
221  
        {
222  
            dest = u.set_port_impl(n.port, op);
222  
            dest = u.set_port_impl(n.port, op);
223  
            pctx = {port, pctx.next_arg_id()};
223  
            pctx = {port, pctx.next_arg_id()};
224  
            fctx.advance_to(dest);
224  
            fctx.advance_to(dest);
225  
            char const* dest1 = pct_vformat(
225  
            char const* dest1 = pct_vformat(
226  
                grammar::digit_chars, pctx, fctx);
226  
                grammar::digit_chars, pctx, fctx);
227  
            u.impl_.decoded_[parts::id_port] =
227  
            u.impl_.decoded_[parts::id_port] =
228  
                detail::to_size_type(
228  
                detail::to_size_type(
229  
                    pct_string_view(dest, dest1 - dest)
229  
                    pct_string_view(dest, dest1 - dest)
230  
                        ->decoded_size() + 1);
230  
                        ->decoded_size() + 1);
231  
            core::string_view up = {dest - 1, dest1};
231  
            core::string_view up = {dest - 1, dest1};
232  
            auto p = grammar::parse(up, detail::port_part_rule).value();
232  
            auto p = grammar::parse(up, detail::port_part_rule).value();
233  
            if (p.has_port)
233  
            if (p.has_port)
234  
                u.impl_.port_number_ = p.port_number;
234  
                u.impl_.port_number_ = p.port_number;
235  
        }
235  
        }
236  
    }
236  
    }
237  
    if (!path.empty())
237  
    if (!path.empty())
238  
    {
238  
    {
239  
        auto dest = u.resize_impl(
239  
        auto dest = u.resize_impl(
240  
            parts::id_path,
240  
            parts::id_path,
241  
            n.path, op);
241  
            n.path, op);
242  
        pctx = {path, pctx.next_arg_id()};
242  
        pctx = {path, pctx.next_arg_id()};
243  
        fctx.advance_to(dest);
243  
        fctx.advance_to(dest);
244  
        auto dest1 = pct_vformat(
244  
        auto dest1 = pct_vformat(
245  
            path_chars, pctx, fctx);
245  
            path_chars, pctx, fctx);
246  
        pct_string_view npath(dest, dest1 - dest);
246  
        pct_string_view npath(dest, dest1 - dest);
247  
        u.impl_.decoded_[parts::id_path] +=
247  
        u.impl_.decoded_[parts::id_path] +=
248  
            detail::to_size_type(
248  
            detail::to_size_type(
249  
                npath.decoded_size());
249  
                npath.decoded_size());
250  
        if (!npath.empty())
250  
        if (!npath.empty())
251  
        {
251  
        {
252  
            u.impl_.nseg_ = detail::to_size_type(
252  
            u.impl_.nseg_ = detail::to_size_type(
253  
                std::count(
253  
                std::count(
254  
                    npath.begin() + 1,
254  
                    npath.begin() + 1,
255  
                    npath.end(), '/') + 1);
255  
                    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.
262  
        if (u.scheme().empty() &&
262  
        if (u.scheme().empty() &&
263  
            !u.has_authority())
263  
            !u.has_authority())
264  
        {
264  
        {
265  
            auto fseg = u.encoded_segments().front();
265  
            auto fseg = u.encoded_segments().front();
266  
            std::size_t nc = std::count(
266  
            std::size_t nc = std::count(
267  
                fseg.begin(), fseg.end(), ':');
267  
                fseg.begin(), fseg.end(), ':');
268  
            if (nc)
268  
            if (nc)
269  
            {
269  
            {
270  
                std::size_t diff = nc * 2;
270  
                std::size_t diff = nc * 2;
271  
                u.reserve(n_total + diff);
271  
                u.reserve(n_total + diff);
272  
                dest = u.resize_impl(
272  
                dest = u.resize_impl(
273  
                    parts::id_path,
273  
                    parts::id_path,
274  
                    n.path + diff, op);
274  
                    n.path + diff, op);
275  
                char* dest0 = dest + diff;
275  
                char* dest0 = dest + diff;
276  
                std::memmove(dest0, dest, n.path);
276  
                std::memmove(dest0, dest, n.path);
277  
                while (dest0 != dest)
277  
                while (dest0 != dest)
278  
                {
278  
                {
279  
                    if (*dest0 != ':')
279  
                    if (*dest0 != ':')
280  
                    {
280  
                    {
281  
                        *dest++ = *dest0++;
281  
                        *dest++ = *dest0++;
282  
                    }
282  
                    }
283  
                    else
283  
                    else
284  
                    {
284  
                    {
285  
                        *dest++ = '%';
285  
                        *dest++ = '%';
286  
                        *dest++ = '3';
286  
                        *dest++ = '3';
287  
                        *dest++ = 'A';
287  
                        *dest++ = 'A';
288  
                        dest0++;
288  
                        dest0++;
289  
                    }
289  
                    }
290  
                }
290  
                }
 
291 +
                n.path += diff;
291  
            }
292  
            }
292  
        }
293  
        }
293  
        // 2) url has no authority and path
294  
        // 2) url has no authority and path
294  
        // starts with "//"
295  
        // starts with "//"
295  
        if (!u.has_authority() &&
296  
        if (!u.has_authority() &&
296  
            u.encoded_path().starts_with("//"))
297  
            u.encoded_path().starts_with("//"))
297  
        {
298  
        {
298  
            u.reserve(n_total + 2);
299  
            u.reserve(n_total + 2);
299  
            dest = u.resize_impl(
300  
            dest = u.resize_impl(
300  
                parts::id_path,
301  
                parts::id_path,
301  
                n.path + 2, op);
302  
                n.path + 2, op);
302  
            std::memmove(dest + 2, dest, n.path);
303  
            std::memmove(dest + 2, dest, n.path);
303  
            *dest++ = '/';
304  
            *dest++ = '/';
304  
            *dest = '.';
305  
            *dest = '.';
305  
        }
306  
        }
306  
    }
307  
    }
307  
    if (has_query)
308  
    if (has_query)
308  
    {
309  
    {
309  
        auto dest = u.resize_impl(
310  
        auto dest = u.resize_impl(
310  
            parts::id_query,
311  
            parts::id_query,
311  
            n.query + 1, op);
312  
            n.query + 1, op);
312  
        *dest++ = '?';
313  
        *dest++ = '?';
313  
        pctx = {query, pctx.next_arg_id()};
314  
        pctx = {query, pctx.next_arg_id()};
314  
        fctx.advance_to(dest);
315  
        fctx.advance_to(dest);
315  
        auto dest1 = pct_vformat(
316  
        auto dest1 = pct_vformat(
316  
            query_chars, pctx, fctx);
317  
            query_chars, pctx, fctx);
317  
        pct_string_view nquery(dest, dest1 - dest);
318  
        pct_string_view nquery(dest, dest1 - dest);
318  
        u.impl_.decoded_[parts::id_query] +=
319  
        u.impl_.decoded_[parts::id_query] +=
319  
            detail::to_size_type(
320  
            detail::to_size_type(
320  
                nquery.decoded_size() + 1);
321  
                nquery.decoded_size() + 1);
321  
        if (!nquery.empty())
322  
        if (!nquery.empty())
322  
        {
323  
        {
323  
            u.impl_.nparam_ = detail::to_size_type(
324  
            u.impl_.nparam_ = detail::to_size_type(
324  
                std::count(
325  
                std::count(
325  
                    nquery.begin(),
326  
                    nquery.begin(),
326  
                    nquery.end(), '&') + 1);
327  
                    nquery.end(), '&') + 1);
327  
        }
328  
        }
328  
    }
329  
    }
329  
    if (has_frag)
330  
    if (has_frag)
330  
    {
331  
    {
331  
        auto dest = u.resize_impl(
332  
        auto dest = u.resize_impl(
332  
            parts::id_frag,
333  
            parts::id_frag,
333  
            n.frag + 1, op);
334  
            n.frag + 1, op);
334  
        *dest++ = '#';
335  
        *dest++ = '#';
335  
        pctx = {frag, pctx.next_arg_id()};
336  
        pctx = {frag, pctx.next_arg_id()};
336  
        fctx.advance_to(dest);
337  
        fctx.advance_to(dest);
337  
        auto dest1 = pct_vformat(
338  
        auto dest1 = pct_vformat(
338  
            fragment_chars, pctx, fctx);
339  
            fragment_chars, pctx, fctx);
339  
        u.impl_.decoded_[parts::id_frag] +=
340  
        u.impl_.decoded_[parts::id_frag] +=
340  
            detail::to_size_type(
341  
            detail::to_size_type(
341  
                make_pct_string_view(
342  
                make_pct_string_view(
342  
                    core::string_view(dest, dest1 - dest))
343  
                    core::string_view(dest, dest1 - dest))
343  
                    ->decoded_size() + 1);
344  
                    ->decoded_size() + 1);
344  
    }
345  
    }
345  
}
346  
}
346  

347  

347  
// This rule represents a pct-encoded string
348  
// This rule represents a pct-encoded string
348  
// that contains an arbitrary number of
349  
// that contains an arbitrary number of
349  
// replacement ids in it
350  
// replacement ids in it
350  
template<class CharSet>
351  
template<class CharSet>
351  
struct pct_encoded_fmt_string_rule_t
352  
struct pct_encoded_fmt_string_rule_t
352  
{
353  
{
353  
    using value_type = pct_string_view;
354  
    using value_type = pct_string_view;
354  

355  

355  
    constexpr
356  
    constexpr
356  
    pct_encoded_fmt_string_rule_t(
357  
    pct_encoded_fmt_string_rule_t(
357  
        CharSet const& cs) noexcept
358  
        CharSet const& cs) noexcept
358  
        : cs_(cs)
359  
        : cs_(cs)
359  
    {
360  
    {
360  
    }
361  
    }
361  

362  

362  
    template<class CharSet_>
363  
    template<class CharSet_>
363  
    friend
364  
    friend
364  
    constexpr
365  
    constexpr
365  
    auto
366  
    auto
366  
    pct_encoded_fmt_string_rule(
367  
    pct_encoded_fmt_string_rule(
367  
        CharSet_ const& cs) noexcept ->
368  
        CharSet_ const& cs) noexcept ->
368  
    pct_encoded_fmt_string_rule_t<CharSet_>;
369  
    pct_encoded_fmt_string_rule_t<CharSet_>;
369  

370  

370  
    system::result<value_type>
371  
    system::result<value_type>
371  
    parse(
372  
    parse(
372  
        char const*& it,
373  
        char const*& it,
373  
        char const* end) const noexcept
374  
        char const* end) const noexcept
374  
    {
375  
    {
375  
        auto const start = it;
376  
        auto const start = it;
376  
        if(it == end)
377  
        if(it == end)
377  
        {
378  
        {
378  
            // this might be empty
379  
            // this might be empty
379  
            return {};
380  
            return {};
380  
        }
381  
        }
381  

382  

382  
        // consume some with literal rule
383  
        // consume some with literal rule
383  
        // this might be an empty literal
384  
        // this might be an empty literal
384  
        auto literal_rule = pct_encoded_rule(cs_);
385  
        auto literal_rule = pct_encoded_rule(cs_);
385  
        auto rv = literal_rule.parse(it, end);
386  
        auto rv = literal_rule.parse(it, end);
386  
        while (rv)
387  
        while (rv)
387  
        {
388  
        {
388  
            auto it0 = it;
389  
            auto it0 = it;
389  
            // consume some with replacement id
390  
            // consume some with replacement id
390  
            // rule
391  
            // rule
391  
            if (!replacement_field_rule.parse(it, end))
392  
            if (!replacement_field_rule.parse(it, end))
392  
            {
393  
            {
393  
                it = it0;
394  
                it = it0;
394  
                break;
395  
                break;
395  
            }
396  
            }
396  
            rv = literal_rule.parse(it, end);
397  
            rv = literal_rule.parse(it, end);
397  
        }
398  
        }
398  

399  

399  
        return core::string_view(start, it - start);
400  
        return core::string_view(start, it - start);
400  
    }
401  
    }
401  

402  

402  
private:
403  
private:
403  
    CharSet cs_;
404  
    CharSet cs_;
404  
};
405  
};
405  

406  

406  
template<class CharSet>
407  
template<class CharSet>
407  
constexpr
408  
constexpr
408  
auto
409  
auto
409  
pct_encoded_fmt_string_rule(
410  
pct_encoded_fmt_string_rule(
410  
    CharSet const& cs) noexcept ->
411  
    CharSet const& cs) noexcept ->
411  
    pct_encoded_fmt_string_rule_t<CharSet>
412  
    pct_encoded_fmt_string_rule_t<CharSet>
412  
{
413  
{
413  
    // If an error occurs here it means that
414  
    // If an error occurs here it means that
414  
    // the value of your type does not meet
415  
    // the value of your type does not meet
415  
    // the requirements. Please check the
416  
    // the requirements. Please check the
416  
    // documentation!
417  
    // documentation!
417  
    static_assert(
418  
    static_assert(
418  
        grammar::is_charset<CharSet>::value,
419  
        grammar::is_charset<CharSet>::value,
419  
        "CharSet requirements not met");
420  
        "CharSet requirements not met");
420  

421  

421  
    return pct_encoded_fmt_string_rule_t<CharSet>(cs);
422  
    return pct_encoded_fmt_string_rule_t<CharSet>(cs);
422  
}
423  
}
423  

424  

424  
// This rule represents a regular string with
425  
// This rule represents a regular string with
425  
// only chars from the specified charset and
426  
// only chars from the specified charset and
426  
// an arbitrary number of replacement ids in it
427  
// an arbitrary number of replacement ids in it
427  
template<class CharSet>
428  
template<class CharSet>
428  
struct fmt_token_rule_t
429  
struct fmt_token_rule_t
429  
{
430  
{
430  
    using value_type = pct_string_view;
431  
    using value_type = pct_string_view;
431  

432  

432  
    constexpr
433  
    constexpr
433  
    fmt_token_rule_t(
434  
    fmt_token_rule_t(
434  
        CharSet const& cs) noexcept
435  
        CharSet const& cs) noexcept
435  
        : cs_(cs)
436  
        : cs_(cs)
436  
    {
437  
    {
437  
    }
438  
    }
438  

439  

439  
    template<class CharSet_>
440  
    template<class CharSet_>
440  
    friend
441  
    friend
441  
    constexpr
442  
    constexpr
442  
    auto
443  
    auto
443  
    fmt_token_rule(
444  
    fmt_token_rule(
444  
        CharSet_ const& cs) noexcept ->
445  
        CharSet_ const& cs) noexcept ->
445  
    fmt_token_rule_t<CharSet_>;
446  
    fmt_token_rule_t<CharSet_>;
446  

447  

447  
    system::result<value_type>
448  
    system::result<value_type>
448  
    parse(
449  
    parse(
449  
        char const*& it,
450  
        char const*& it,
450  
        char const* end) const noexcept
451  
        char const* end) const noexcept
451  
    {
452  
    {
452  
        auto const start = it;
453  
        auto const start = it;
453  
        BOOST_ASSERT(it != end);
454  
        BOOST_ASSERT(it != end);
454  
        /*
455  
        /*
455  
        // This should never happen because
456  
        // This should never happen because
456  
        // all tokens are optional and will
457  
        // all tokens are optional and will
457  
        // already return `none`:
458  
        // already return `none`:
458  
        if(it == end)
459  
        if(it == end)
459  
        {
460  
        {
460  
            BOOST_URL_RETURN_EC(
461  
            BOOST_URL_RETURN_EC(
461  
                grammar::error::need_more);
462  
                grammar::error::need_more);
462  
        }
463  
        }
463  
        */
464  
        */
464  

465  

465  
        // consume some with literal rule
466  
        // consume some with literal rule
466  
        // this might be an empty literal
467  
        // this might be an empty literal
467  
        auto partial_token_rule =
468  
        auto partial_token_rule =
468  
            grammar::optional_rule(
469  
            grammar::optional_rule(
469  
                grammar::token_rule(cs_));
470  
                grammar::token_rule(cs_));
470  
        auto rv = partial_token_rule.parse(it, end);
471  
        auto rv = partial_token_rule.parse(it, end);
471  
        while (rv)
472  
        while (rv)
472  
        {
473  
        {
473  
            auto it0 = it;
474  
            auto it0 = it;
474  
            // consume some with replacement id
475  
            // consume some with replacement id
475  
            if (!replacement_field_rule.parse(it, end))
476  
            if (!replacement_field_rule.parse(it, end))
476  
            {
477  
            {
477  
                // no replacement and no more cs
478  
                // no replacement and no more cs
478  
                // before: nothing else to consume
479  
                // before: nothing else to consume
479  
                it = it0;
480  
                it = it0;
480  
                break;
481  
                break;
481  
            }
482  
            }
482  
            // after {...}, consume any more chars
483  
            // after {...}, consume any more chars
483  
            // in the charset
484  
            // in the charset
484  
            rv = partial_token_rule.parse(it, end);
485  
            rv = partial_token_rule.parse(it, end);
485  
        }
486  
        }
486  

487  

487  
        if(it == start)
488  
        if(it == start)
488  
        {
489  
        {
489  
            // it != end but we consumed nothing
490  
            // it != end but we consumed nothing
490  
            BOOST_URL_RETURN_EC(
491  
            BOOST_URL_RETURN_EC(
491  
                grammar::error::need_more);
492  
                grammar::error::need_more);
492  
        }
493  
        }
493  

494  

494  
        return core::string_view(start, it - start);
495  
        return core::string_view(start, it - start);
495  
    }
496  
    }
496  

497  

497  
private:
498  
private:
498  
    CharSet cs_;
499  
    CharSet cs_;
499  
};
500  
};
500  

501  

501  
template<class CharSet>
502  
template<class CharSet>
502  
constexpr
503  
constexpr
503  
auto
504  
auto
504  
fmt_token_rule(
505  
fmt_token_rule(
505  
    CharSet const& cs) noexcept ->
506  
    CharSet const& cs) noexcept ->
506  
    fmt_token_rule_t<CharSet>
507  
    fmt_token_rule_t<CharSet>
507  
{
508  
{
508  
    // If an error occurs here it means that
509  
    // If an error occurs here it means that
509  
    // the value of your type does not meet
510  
    // the value of your type does not meet
510  
    // the requirements. Please check the
511  
    // the requirements. Please check the
511  
    // documentation!
512  
    // documentation!
512  
    static_assert(
513  
    static_assert(
513  
        grammar::is_charset<CharSet>::value,
514  
        grammar::is_charset<CharSet>::value,
514  
        "CharSet requirements not met");
515  
        "CharSet requirements not met");
515  

516  

516  
    return fmt_token_rule_t<CharSet>(cs);
517  
    return fmt_token_rule_t<CharSet>(cs);
517  
}
518  
}
518  

519  

519  
struct userinfo_template_rule_t
520  
struct userinfo_template_rule_t
520  
{
521  
{
521  
    struct value_type
522  
    struct value_type
522  
    {
523  
    {
523  
        core::string_view user;
524  
        core::string_view user;
524  
        core::string_view password;
525  
        core::string_view password;
525  
        bool has_password = false;
526  
        bool has_password = false;
526  
    };
527  
    };
527  

528  

528  
    auto
529  
    auto
529  
    parse(
530  
    parse(
530  
        char const*& it,
531  
        char const*& it,
531  
        char const* end
532  
        char const* end
532  
            ) const noexcept ->
533  
            ) const noexcept ->
533  
        system::result<value_type>
534  
        system::result<value_type>
534  
    {
535  
    {
535  
        static constexpr auto uchars =
536  
        static constexpr auto uchars =
536  
            unreserved_chars +
537  
            unreserved_chars +
537  
            sub_delim_chars;
538  
            sub_delim_chars;
538  
        static constexpr auto pwchars =
539  
        static constexpr auto pwchars =
539  
            uchars + ':';
540  
            uchars + ':';
540  

541  

541  
        value_type t;
542  
        value_type t;
542  

543  

543  
        // user
544  
        // user
544  
        static constexpr auto user_fmt_rule =
545  
        static constexpr auto user_fmt_rule =
545  
            pct_encoded_fmt_string_rule(uchars);
546  
            pct_encoded_fmt_string_rule(uchars);
546  
        auto rv = grammar::parse(
547  
        auto rv = grammar::parse(
547  
            it, end, user_fmt_rule);
548  
            it, end, user_fmt_rule);
548  
        BOOST_ASSERT(rv);
549  
        BOOST_ASSERT(rv);
549  
        t.user = *rv;
550  
        t.user = *rv;
550  

551  

551  
        // ':'
552  
        // ':'
552  
        if( it == end ||
553  
        if( it == end ||
553  
            *it != ':')
554  
            *it != ':')
554  
        {
555  
        {
555  
            t.has_password = false;
556  
            t.has_password = false;
556  
            t.password = {};
557  
            t.password = {};
557  
            return t;
558  
            return t;
558  
        }
559  
        }
559  
        ++it;
560  
        ++it;
560  

561  

561  
        // pass
562  
        // pass
562  
        static constexpr auto pass_fmt_rule =
563  
        static constexpr auto pass_fmt_rule =
563  
            pct_encoded_fmt_string_rule(grammar::ref(pwchars));
564  
            pct_encoded_fmt_string_rule(grammar::ref(pwchars));
564  
        rv = grammar::parse(
565  
        rv = grammar::parse(
565  
            it, end, pass_fmt_rule);
566  
            it, end, pass_fmt_rule);
566  
        BOOST_ASSERT(rv);
567  
        BOOST_ASSERT(rv);
567  
        t.has_password = true;
568  
        t.has_password = true;
568  
        t.password = *rv;
569  
        t.password = *rv;
569  

570  

570  
        return t;
571  
        return t;
571  
    }
572  
    }
572  
};
573  
};
573  

574  

574  
constexpr userinfo_template_rule_t userinfo_template_rule{};
575  
constexpr userinfo_template_rule_t userinfo_template_rule{};
575  

576  

576  
struct host_template_rule_t
577  
struct host_template_rule_t
577  
{
578  
{
578  
    using value_type = core::string_view;
579  
    using value_type = core::string_view;
579  

580  

580  
    auto
581  
    auto
581  
    parse(
582  
    parse(
582  
        char const*& it,
583  
        char const*& it,
583  
        char const* end
584  
        char const* end
584  
            ) const noexcept ->
585  
            ) const noexcept ->
585  
        system::result<value_type>
586  
        system::result<value_type>
586  
    {
587  
    {
587  
        if(it == end)
588  
        if(it == end)
588  
        {
589  
        {
589  
            // empty host
590  
            // empty host
590  
            return {};
591  
            return {};
591  
        }
592  
        }
592  

593  

593  
        // the host type will be ultimately
594  
        // the host type will be ultimately
594  
        // validated when applying the replacement
595  
        // validated when applying the replacement
595  
        // strings. Any chars allowed in hosts
596  
        // strings. Any chars allowed in hosts
596  
        // are allowed here.
597  
        // are allowed here.
597  
        if (*it != '[')
598  
        if (*it != '[')
598  
        {
599  
        {
599  
            // IPv4address and reg-name have the
600  
            // IPv4address and reg-name have the
600  
            // same char sets.
601  
            // same char sets.
601  
            constexpr auto any_host_template_rule =
602  
            constexpr auto any_host_template_rule =
602  
                pct_encoded_fmt_string_rule(host_chars);
603  
                pct_encoded_fmt_string_rule(host_chars);
603  
            auto rv = grammar::parse(
604  
            auto rv = grammar::parse(
604  
                it, end, any_host_template_rule);
605  
                it, end, any_host_template_rule);
605  
            // any_host_template_rule can always
606  
            // any_host_template_rule can always
606  
            // be empty, so it's never invalid
607  
            // be empty, so it's never invalid
607  
            BOOST_ASSERT(rv);
608  
            BOOST_ASSERT(rv);
608  
            return detail::to_sv(*rv);
609  
            return detail::to_sv(*rv);
609  
        }
610  
        }
610  
        // IP-literals need to be enclosed in
611  
        // IP-literals need to be enclosed in
611  
        // "[]" if using ':' in the template
612  
        // "[]" if using ':' in the template
612  
        // string, because the ':' would be
613  
        // string, because the ':' would be
613  
        // ambiguous with the port in fmt string.
614  
        // ambiguous with the port in fmt string.
614  
        // The "[]:" can be used in replacement
615  
        // The "[]:" can be used in replacement
615  
        // strings without the "[]" though.
616  
        // strings without the "[]" though.
616  
        constexpr auto ip_literal_template_rule =
617  
        constexpr auto ip_literal_template_rule =
617  
            pct_encoded_fmt_string_rule(lhost_chars);
618  
            pct_encoded_fmt_string_rule(lhost_chars);
618  
        auto it0 = it;
619  
        auto it0 = it;
619  
        auto rv = grammar::parse(
620  
        auto rv = grammar::parse(
620  
            it, end,
621  
            it, end,
621  
            grammar::optional_rule(
622  
            grammar::optional_rule(
622  
                grammar::tuple_rule(
623  
                grammar::tuple_rule(
623  
                    grammar::squelch(
624  
                    grammar::squelch(
624  
                        grammar::delim_rule('[')),
625  
                        grammar::delim_rule('[')),
625  
                    ip_literal_template_rule,
626  
                    ip_literal_template_rule,
626  
                    grammar::squelch(
627  
                    grammar::squelch(
627  
                        grammar::delim_rule(']')))));
628  
                        grammar::delim_rule(']')))));
628  
        // ip_literal_template_rule can always
629  
        // ip_literal_template_rule can always
629  
        // be empty, so it's never invalid, but
630  
        // be empty, so it's never invalid, but
630  
        // the rule might fail to match the
631  
        // the rule might fail to match the
631  
        // closing "]"
632  
        // closing "]"
632  
        BOOST_ASSERT(rv);
633  
        BOOST_ASSERT(rv);
633  
        (void)rv;
634  
        (void)rv;
634  
        return core::string_view{it0, it};
635  
        return core::string_view{it0, it};
635  
    }
636  
    }
636  
};
637  
};
637  

638  

638  
constexpr host_template_rule_t host_template_rule{};
639  
constexpr host_template_rule_t host_template_rule{};
639  

640  

640  
struct authority_template_rule_t
641  
struct authority_template_rule_t
641  
{
642  
{
642  
    using value_type = pattern;
643  
    using value_type = pattern;
643  

644  

644  
    system::result<value_type>
645  
    system::result<value_type>
645  
    parse(
646  
    parse(
646  
        char const*& it,
647  
        char const*& it,
647  
        char const* end
648  
        char const* end
648  
    ) const noexcept
649  
    ) const noexcept
649  
    {
650  
    {
650  
        pattern u;
651  
        pattern u;
651  

652  

652  
        // [ userinfo "@" ]
653  
        // [ userinfo "@" ]
653  
        {
654  
        {
654  
            auto rv = grammar::parse(
655  
            auto rv = grammar::parse(
655  
                it, end,
656  
                it, end,
656  
                grammar::optional_rule(
657  
                grammar::optional_rule(
657  
                    grammar::tuple_rule(
658  
                    grammar::tuple_rule(
658  
                        userinfo_template_rule,
659  
                        userinfo_template_rule,
659  
                        grammar::squelch(
660  
                        grammar::squelch(
660  
                            grammar::delim_rule('@')))));
661  
                            grammar::delim_rule('@')))));
661  
            BOOST_ASSERT(rv);
662  
            BOOST_ASSERT(rv);
662  
            if(rv->has_value())
663  
            if(rv->has_value())
663  
            {
664  
            {
664  
                auto& r = **rv;
665  
                auto& r = **rv;
665  
                u.has_user = true;
666  
                u.has_user = true;
666  
                u.user = r.user;
667  
                u.user = r.user;
667  
                u.has_pass = r.has_password;
668  
                u.has_pass = r.has_password;
668  
                u.pass = r.password;
669  
                u.pass = r.password;
669  
            }
670  
            }
670  
        }
671  
        }
671  

672  

672  
        // host
673  
        // host
673  
        {
674  
        {
674  
            auto rv = grammar::parse(
675  
            auto rv = grammar::parse(
675  
                it, end,
676  
                it, end,
676  
                host_template_rule);
677  
                host_template_rule);
677  
            // host is allowed to be empty
678  
            // host is allowed to be empty
678  
            BOOST_ASSERT(rv);
679  
            BOOST_ASSERT(rv);
679  
            u.host = *rv;
680  
            u.host = *rv;
680  
        }
681  
        }
681  

682  

682  
        // [ ":" port ]
683  
        // [ ":" port ]
683  
        {
684  
        {
684  
            constexpr auto port_template_rule =
685  
            constexpr auto port_template_rule =
685  
                grammar::optional_rule(
686  
                grammar::optional_rule(
686  
                    fmt_token_rule(grammar::digit_chars));
687  
                    fmt_token_rule(grammar::digit_chars));
687  
            auto it0 = it;
688  
            auto it0 = it;
688  
            auto rv = grammar::parse(
689  
            auto rv = grammar::parse(
689  
                it, end,
690  
                it, end,
690  
                grammar::tuple_rule(
691  
                grammar::tuple_rule(
691  
                    grammar::squelch(
692  
                    grammar::squelch(
692  
                        grammar::delim_rule(':')),
693  
                        grammar::delim_rule(':')),
693  
                    port_template_rule));
694  
                    port_template_rule));
694  
            if (!rv)
695  
            if (!rv)
695  
            {
696  
            {
696  
                it = it0;
697  
                it = it0;
697  
            }
698  
            }
698  
            else
699  
            else
699  
            {
700  
            {
700  
                u.has_port = true;
701  
                u.has_port = true;
701  
                if (rv->has_value())
702  
                if (rv->has_value())
702  
                {
703  
                {
703  
                    u.port = **rv;
704  
                    u.port = **rv;
704  
                }
705  
                }
705  
            }
706  
            }
706  
        }
707  
        }
707  

708  

708  
        return u;
709  
        return u;
709  
    }
710  
    }
710  
};
711  
};
711  

712  

712  
constexpr authority_template_rule_t authority_template_rule{};
713  
constexpr authority_template_rule_t authority_template_rule{};
713  

714  

714  
struct scheme_template_rule_t
715  
struct scheme_template_rule_t
715  
{
716  
{
716  
    using value_type = core::string_view;
717  
    using value_type = core::string_view;
717  

718  

718  
    system::result<value_type>
719  
    system::result<value_type>
719  
    parse(
720  
    parse(
720  
        char const*& it,
721  
        char const*& it,
721  
        char const* end) const noexcept
722  
        char const* end) const noexcept
722  
    {
723  
    {
723  
        auto const start = it;
724  
        auto const start = it;
724  
        if(it == end)
725  
        if(it == end)
725  
        {
726  
        {
726  
            // scheme can't be empty
727  
            // scheme can't be empty
727  
            BOOST_URL_RETURN_EC(
728  
            BOOST_URL_RETURN_EC(
728  
                grammar::error::mismatch);
729  
                grammar::error::mismatch);
729  
        }
730  
        }
730  
        if(!grammar::alpha_chars(*it) &&
731  
        if(!grammar::alpha_chars(*it) &&
731  
            *it != '{')
732  
            *it != '{')
732  
        {
733  
        {
733  
            // expected alpha
734  
            // expected alpha
734  
            BOOST_URL_RETURN_EC(
735  
            BOOST_URL_RETURN_EC(
735  
                grammar::error::mismatch);
736  
                grammar::error::mismatch);
736  
        }
737  
        }
737  

738  

738  
        // it starts with replacement id or alpha char
739  
        // it starts with replacement id or alpha char
739  
        if (!grammar::alpha_chars(*it))
740  
        if (!grammar::alpha_chars(*it))
740  
        {
741  
        {
741  
            if (!replacement_field_rule.parse(it, end))
742  
            if (!replacement_field_rule.parse(it, end))
742  
            {
743  
            {
743  
                // replacement_field_rule is invalid
744  
                // replacement_field_rule is invalid
744  
                BOOST_URL_RETURN_EC(
745  
                BOOST_URL_RETURN_EC(
745  
                    grammar::error::mismatch);
746  
                    grammar::error::mismatch);
746  
            }
747  
            }
747  
        }
748  
        }
748  
        else
749  
        else
749  
        {
750  
        {
750  
            // skip first
751  
            // skip first
751  
            ++it;
752  
            ++it;
752  
        }
753  
        }
753  

754  

754  
        static
755  
        static
755  
        constexpr
756  
        constexpr
756  
        grammar::lut_chars scheme_chars(
757  
        grammar::lut_chars scheme_chars(
757  
            "0123456789" "+-."
758  
            "0123456789" "+-."
758  
            "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
759  
            "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
759  
            "abcdefghijklmnopqrstuvwxyz");
760  
            "abcdefghijklmnopqrstuvwxyz");
760  

761  

761  
        // non-scheme chars might be a new
762  
        // non-scheme chars might be a new
762  
        // replacement-id or just an invalid char
763  
        // replacement-id or just an invalid char
763  
        it = grammar::find_if_not(
764  
        it = grammar::find_if_not(
764  
            it, end, scheme_chars);
765  
            it, end, scheme_chars);
765  
        while (it != end)
766  
        while (it != end)
766  
        {
767  
        {
767  
            auto it0 = it;
768  
            auto it0 = it;
768  
            if (!replacement_field_rule.parse(it, end))
769  
            if (!replacement_field_rule.parse(it, end))
769  
            {
770  
            {
770  
                it = it0;
771  
                it = it0;
771  
                break;
772  
                break;
772  
            }
773  
            }
773  
            it = grammar::find_if_not(
774  
            it = grammar::find_if_not(
774  
                it, end, scheme_chars);
775  
                it, end, scheme_chars);
775  
        }
776  
        }
776  
        return core::string_view(start, it - start);
777  
        return core::string_view(start, it - start);
777  
    }
778  
    }
778  
};
779  
};
779  

780  

780  
constexpr scheme_template_rule_t scheme_template_rule{};
781  
constexpr scheme_template_rule_t scheme_template_rule{};
781  

782  

782  
// This rule should consider all url types at the
783  
// This rule should consider all url types at the
783  
// same time according to the format string
784  
// same time according to the format string
784  
// - relative urls with no scheme/authority
785  
// - relative urls with no scheme/authority
785  
// - absolute urls have no fragment
786  
// - absolute urls have no fragment
786  
struct pattern_rule_t
787  
struct pattern_rule_t
787  
{
788  
{
788  
    using value_type = pattern;
789  
    using value_type = pattern;
789  

790  

790  
    system::result<value_type>
791  
    system::result<value_type>
791  
    parse(
792  
    parse(
792  
        char const*& it,
793  
        char const*& it,
793  
        char const* const end
794  
        char const* const end
794  
    ) const noexcept
795  
    ) const noexcept
795  
    {
796  
    {
796  
        pattern u;
797  
        pattern u;
797  

798  

798  
        // optional scheme
799  
        // optional scheme
799  
        {
800  
        {
800  
            auto it0 = it;
801  
            auto it0 = it;
801  
            auto rv = grammar::parse(
802  
            auto rv = grammar::parse(
802  
                it, end,
803  
                it, end,
803  
                grammar::tuple_rule(
804  
                grammar::tuple_rule(
804  
                    scheme_template_rule,
805  
                    scheme_template_rule,
805  
                    grammar::squelch(
806  
                    grammar::squelch(
806  
                        grammar::delim_rule(':'))));
807  
                        grammar::delim_rule(':'))));
807  
            if(rv)
808  
            if(rv)
808  
                u.scheme = *rv;
809  
                u.scheme = *rv;
809  
            else
810  
            else
810  
                it = it0;
811  
                it = it0;
811  
        }
812  
        }
812  

813  

813  
        // hier_part (authority + path)
814  
        // hier_part (authority + path)
814  
        // if there are less than 2 chars left,
815  
        // if there are less than 2 chars left,
815  
        // we are parsing the path
816  
        // we are parsing the path
816  
        if (it == end)
817  
        if (it == end)
817  
        {
818  
        {
818  
            // this is over, so we can consider
819  
            // this is over, so we can consider
819  
            // that a "path-empty"
820  
            // that a "path-empty"
820  
            return u;
821  
            return u;
821  
        }
822  
        }
822  
        if(end - it == 1)
823  
        if(end - it == 1)
823  
        {
824  
        {
824  
            // only one char left
825  
            // only one char left
825  
            // it can be a single separator "/",
826  
            // it can be a single separator "/",
826  
            // representing an empty absolute path,
827  
            // representing an empty absolute path,
827  
            // or a single-char segment
828  
            // or a single-char segment
828  
            if(*it == '/')
829  
            if(*it == '/')
829  
            {
830  
            {
830  
                // path-absolute
831  
                // path-absolute
831  
                u.path = {it, 1};
832  
                u.path = {it, 1};
832  
                ++it;
833  
                ++it;
833  
                return u;
834  
                return u;
834  
            }
835  
            }
835  
            // this can be a:
836  
            // this can be a:
836  
            // - path-noscheme if there's no scheme, or
837  
            // - path-noscheme if there's no scheme, or
837  
            // - path-rootless with a single char, or
838  
            // - path-rootless with a single char, or
838  
            // - path-empty (and consume nothing)
839  
            // - path-empty (and consume nothing)
839  
            if (!u.scheme.empty() ||
840  
            if (!u.scheme.empty() ||
840  
                *it != ':')
841  
                *it != ':')
841  
            {
842  
            {
842  
                // path-rootless with a single char
843  
                // path-rootless with a single char
843  
                // this needs to be a segment because
844  
                // this needs to be a segment because
844  
                // the authority needs two slashes
845  
                // the authority needs two slashes
845  
                // "//"
846  
                // "//"
846  
                // path-noscheme also matches here
847  
                // path-noscheme also matches here
847  
                // because we already validated the
848  
                // because we already validated the
848  
                // first char
849  
                // first char
849  
                auto rv = grammar::parse(
850  
                auto rv = grammar::parse(
850  
                    it, end, urls::detail::segment_rule);
851  
                    it, end, urls::detail::segment_rule);
851  
                if(! rv)
852  
                if(! rv)
852  
                    return rv.error();
853  
                    return rv.error();
853  
                u.path = *rv;
854  
                u.path = *rv;
854  
            }
855  
            }
855  
            return u;
856  
            return u;
856  
        }
857  
        }
857  

858  

858  
        // authority
859  
        // authority
859  
        if( it[0] == '/' &&
860  
        if( it[0] == '/' &&
860  
            it[1] == '/')
861  
            it[1] == '/')
861  
        {
862  
        {
862  
            // "//" always indicates authority
863  
            // "//" always indicates authority
863  
            it += 2;
864  
            it += 2;
864  
            auto rv = grammar::parse(
865  
            auto rv = grammar::parse(
865  
                it, end,
866  
                it, end,
866  
                authority_template_rule);
867  
                authority_template_rule);
867  
            // authority is allowed to be empty
868  
            // authority is allowed to be empty
868  
            BOOST_ASSERT(rv);
869  
            BOOST_ASSERT(rv);
869  
            u.has_authority = true;
870  
            u.has_authority = true;
870  
            u.has_user = rv->has_user;
871  
            u.has_user = rv->has_user;
871  
            u.user = rv->user;
872  
            u.user = rv->user;
872  
            u.has_pass = rv->has_pass;
873  
            u.has_pass = rv->has_pass;
873  
            u.pass = rv->pass;
874  
            u.pass = rv->pass;
874  
            u.host = rv->host;
875  
            u.host = rv->host;
875  
            u.has_port = rv->has_port;
876  
            u.has_port = rv->has_port;
876  
            u.port = rv->port;
877  
            u.port = rv->port;
877  
        }
878  
        }
878  

879  

879  
        // the authority requires an absolute path
880  
        // the authority requires an absolute path
880  
        // or an empty path
881  
        // or an empty path
881  
        if (it == end ||
882  
        if (it == end ||
882  
            (u.has_authority &&
883  
            (u.has_authority &&
883  
             (*it != '/' &&
884  
             (*it != '/' &&
884  
              *it != '?' &&
885  
              *it != '?' &&
885  
              *it != '#')))
886  
              *it != '#')))
886  
        {
887  
        {
887  
            // path-empty
888  
            // path-empty
888  
            return u;
889  
            return u;
889  
        }
890  
        }
890  

891  

891  
        // path-abempty
892  
        // path-abempty
892  
        // consume the whole path at once because
893  
        // consume the whole path at once because
893  
        // we're going to count number of segments
894  
        // we're going to count number of segments
894  
        // later after the replacements happen
895  
        // later after the replacements happen
895  
        static constexpr auto segment_fmt_rule =
896  
        static constexpr auto segment_fmt_rule =
896  
            pct_encoded_fmt_string_rule(path_chars);
897  
            pct_encoded_fmt_string_rule(path_chars);
897  
        auto rp = grammar::parse(
898  
        auto rp = grammar::parse(
898  
            it, end, segment_fmt_rule);
899  
            it, end, segment_fmt_rule);
899  
        // path-abempty is allowed to be empty
900  
        // path-abempty is allowed to be empty
900  
        BOOST_ASSERT(rp);
901  
        BOOST_ASSERT(rp);
901  
        u.path = *rp;
902  
        u.path = *rp;
902  

903  

903  
        // [ "?" query ]
904  
        // [ "?" query ]
904  
        {
905  
        {
905  
            static constexpr auto query_fmt_rule =
906  
            static constexpr auto query_fmt_rule =
906  
                pct_encoded_fmt_string_rule(query_chars);
907  
                pct_encoded_fmt_string_rule(query_chars);
907  
            auto rv = grammar::parse(
908  
            auto rv = grammar::parse(
908  
                it, end,
909  
                it, end,
909  
                grammar::tuple_rule(
910  
                grammar::tuple_rule(
910  
                    grammar::squelch(
911  
                    grammar::squelch(
911  
                        grammar::delim_rule('?')),
912  
                        grammar::delim_rule('?')),
912  
                    query_fmt_rule));
913  
                    query_fmt_rule));
913  
            // query is allowed to be empty but
914  
            // query is allowed to be empty but
914  
            // delim rule is not
915  
            // delim rule is not
915  
            if (rv)
916  
            if (rv)
916  
            {
917  
            {
917  
                u.has_query = true;
918  
                u.has_query = true;
918  
                u.query = *rv;
919  
                u.query = *rv;
919  
            }
920  
            }
920  
        }
921  
        }
921  

922  

922  
        // [ "#" fragment ]
923  
        // [ "#" fragment ]
923  
        {
924  
        {
924  
            static constexpr auto frag_fmt_rule =
925  
            static constexpr auto frag_fmt_rule =
925  
                pct_encoded_fmt_string_rule(fragment_chars);
926  
                pct_encoded_fmt_string_rule(fragment_chars);
926  
            auto rv = grammar::parse(
927  
            auto rv = grammar::parse(
927  
                it, end,
928  
                it, end,
928  
                grammar::tuple_rule(
929  
                grammar::tuple_rule(
929  
                    grammar::squelch(
930  
                    grammar::squelch(
930  
                        grammar::delim_rule('#')),
931  
                        grammar::delim_rule('#')),
931  
                    frag_fmt_rule));
932  
                    frag_fmt_rule));
932  
            // frag is allowed to be empty but
933  
            // frag is allowed to be empty but
933  
            // delim rule is not
934  
            // delim rule is not
934  
            if (rv)
935  
            if (rv)
935  
            {
936  
            {
936  
                u.has_frag = true;
937  
                u.has_frag = true;
937  
                u.frag = *rv;
938  
                u.frag = *rv;
938  
            }
939  
            }
939  
        }
940  
        }
940  

941  

941  
        return u;
942  
        return u;
942  
    }
943  
    }
943  
};
944  
};
944  

945  

945  
constexpr pattern_rule_t pattern_rule{};
946  
constexpr pattern_rule_t pattern_rule{};
946  

947  

947  
system::result<pattern>
948  
system::result<pattern>
948  
parse_pattern(
949  
parse_pattern(
949  
    core::string_view s)
950  
    core::string_view s)
950  
{
951  
{
951  
    return grammar::parse(
952  
    return grammar::parse(
952  
        s, pattern_rule);
953  
        s, pattern_rule);
953  
}
954  
}
954  

955  

955  
} // detail
956  
} // detail
956  
} // urls
957  
} // urls
957  
} // boost
958  
} // boost