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 "../rfc/detail/charsets.hpp"
18 +
#include <boost/url/rfc/detail/charsets.hpp>
19 -
#include "../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 "../rfc/detail/port_rule.hpp"
21 +
#include <boost/url/rfc/detail/port_rule.hpp>
22 -
#include "../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  
            }
291  
            }
292  
        }
292  
        }
293  
        // 2) url has no authority and path
293  
        // 2) url has no authority and path
294  
        // starts with "//"
294  
        // starts with "//"
295  
        if (!u.has_authority() &&
295  
        if (!u.has_authority() &&
296  
            u.encoded_path().starts_with("//"))
296  
            u.encoded_path().starts_with("//"))
297  
        {
297  
        {
298  
            u.reserve(n_total + 2);
298  
            u.reserve(n_total + 2);
299  
            dest = u.resize_impl(
299  
            dest = u.resize_impl(
300  
                parts::id_path,
300  
                parts::id_path,
301  
                n.path + 2, op);
301  
                n.path + 2, op);
302  
            std::memmove(dest + 2, dest, n.path);
302  
            std::memmove(dest + 2, dest, n.path);
303  
            *dest++ = '/';
303  
            *dest++ = '/';
304  
            *dest = '.';
304  
            *dest = '.';
305  
        }
305  
        }
306  
    }
306  
    }
307  
    if (has_query)
307  
    if (has_query)
308  
    {
308  
    {
309  
        auto dest = u.resize_impl(
309  
        auto dest = u.resize_impl(
310  
            parts::id_query,
310  
            parts::id_query,
311  
            n.query + 1, op);
311  
            n.query + 1, op);
312  
        *dest++ = '?';
312  
        *dest++ = '?';
313  
        pctx = {query, pctx.next_arg_id()};
313  
        pctx = {query, pctx.next_arg_id()};
314  
        fctx.advance_to(dest);
314  
        fctx.advance_to(dest);
315  
        auto dest1 = pct_vformat(
315  
        auto dest1 = pct_vformat(
316  
            query_chars, pctx, fctx);
316  
            query_chars, pctx, fctx);
317  
        pct_string_view nquery(dest, dest1 - dest);
317  
        pct_string_view nquery(dest, dest1 - dest);
318  
        u.impl_.decoded_[parts::id_query] +=
318  
        u.impl_.decoded_[parts::id_query] +=
319  
            detail::to_size_type(
319  
            detail::to_size_type(
320  
                nquery.decoded_size() + 1);
320  
                nquery.decoded_size() + 1);
321  
        if (!nquery.empty())
321  
        if (!nquery.empty())
322  
        {
322  
        {
323  
            u.impl_.nparam_ = detail::to_size_type(
323  
            u.impl_.nparam_ = detail::to_size_type(
324  
                std::count(
324  
                std::count(
325  
                    nquery.begin(),
325  
                    nquery.begin(),
326  
                    nquery.end(), '&') + 1);
326  
                    nquery.end(), '&') + 1);
327  
        }
327  
        }
328  
    }
328  
    }
329  
    if (has_frag)
329  
    if (has_frag)
330  
    {
330  
    {
331  
        auto dest = u.resize_impl(
331  
        auto dest = u.resize_impl(
332  
            parts::id_frag,
332  
            parts::id_frag,
333  
            n.frag + 1, op);
333  
            n.frag + 1, op);
334  
        *dest++ = '#';
334  
        *dest++ = '#';
335  
        pctx = {frag, pctx.next_arg_id()};
335  
        pctx = {frag, pctx.next_arg_id()};
336  
        fctx.advance_to(dest);
336  
        fctx.advance_to(dest);
337  
        auto dest1 = pct_vformat(
337  
        auto dest1 = pct_vformat(
338  
            fragment_chars, pctx, fctx);
338  
            fragment_chars, pctx, fctx);
339  
        u.impl_.decoded_[parts::id_frag] +=
339  
        u.impl_.decoded_[parts::id_frag] +=
340  
            detail::to_size_type(
340  
            detail::to_size_type(
341  
                make_pct_string_view(
341  
                make_pct_string_view(
342  
                    core::string_view(dest, dest1 - dest))
342  
                    core::string_view(dest, dest1 - dest))
343  
                    ->decoded_size() + 1);
343  
                    ->decoded_size() + 1);
344  
    }
344  
    }
345  
}
345  
}
346  

346  

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

354  

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

361  

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

369  

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

381  

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

398  

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

401  

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

405  

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

420  

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

423  

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

431  

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

438  

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

446  

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

464  

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

486  

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

493  

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

496  

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

500  

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

515  

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

518  

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

527  

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

540  

541  
        value_type t;
541  
        value_type t;
542  

542  

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

550  

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

560  

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

569  

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

573  

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

575  

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

579  

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

592  

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

637  

637  
constexpr host_template_rule_t host_template_rule{};
638  
constexpr host_template_rule_t host_template_rule{};
638  

639  

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

643  

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

651  

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

671  

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

681  

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

707  

707  
        return u;
708  
        return u;
708  
    }
709  
    }
709  
};
710  
};
710  

711  

711  
constexpr authority_template_rule_t authority_template_rule{};
712  
constexpr authority_template_rule_t authority_template_rule{};
712  

713  

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

717  

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

737  

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

753  

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

760  

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

779  

779  
constexpr scheme_template_rule_t scheme_template_rule{};
780  
constexpr scheme_template_rule_t scheme_template_rule{};
780  

781  

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

789  

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

797  

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

812  

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

857  

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

878  

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

890  

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

902  

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

921  

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

940  

940  
        return u;
941  
        return u;
941  
    }
942  
    }
942  
};
943  
};
943  

944  

944  
constexpr pattern_rule_t pattern_rule{};
945  
constexpr pattern_rule_t pattern_rule{};
945  

946  

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

954  

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