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 "../rfc/detail/charsets.hpp"
19  
#include "../rfc/detail/host_rule.hpp"
19  
#include "../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 "../rfc/detail/port_rule.hpp"
22  
#include "../rfc/detail/scheme_rule.hpp"
22  
#include "../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 -
                pct_string_view(dest, dest1 - dest)
169 +
                detail::to_size_type(
170 -
                    ->decoded_size();
170 +
                    pct_string_view(dest, dest1 - dest)
 
171 +
                        ->decoded_size());
171  
            if (has_pass)
172  
            if (has_pass)
172  
            {
173  
            {
173  
                char* destp = u.set_password_impl(
174  
                char* destp = u.set_password_impl(
174  
                    n.pass, op);
175  
                    n.pass, op);
175  
                pctx = {pass, pctx.next_arg_id()};
176  
                pctx = {pass, pctx.next_arg_id()};
176  
                fctx.advance_to(destp);
177  
                fctx.advance_to(destp);
177  
                dest1 = pct_vformat(
178  
                dest1 = pct_vformat(
178  
                    password_chars, pctx, fctx);
179  
                    password_chars, pctx, fctx);
179  
                u.impl_.decoded_[parts::id_pass] =
180  
                u.impl_.decoded_[parts::id_pass] =
180 -
                    pct_string_view({destp, dest1})
181 +
                    detail::to_size_type(
181 -
                        ->decoded_size() + 1;
182 +
                        pct_string_view({destp, dest1})
 
183 +
                            ->decoded_size() + 1);
182  
            }
184  
            }
183  
        }
185  
        }
184  
        auto dest = u.set_host_impl(
186  
        auto dest = u.set_host_impl(
185  
            n.host, op);
187  
            n.host, op);
186  
        if (host.starts_with('['))
188  
        if (host.starts_with('['))
187  
        {
189  
        {
188  
            BOOST_ASSERT(host.ends_with(']'));
190  
            BOOST_ASSERT(host.ends_with(']'));
189  
            pctx = {host.substr(1, host.size() - 2), pctx.next_arg_id()};
191  
            pctx = {host.substr(1, host.size() - 2), pctx.next_arg_id()};
190  
            *dest++ = '[';
192  
            *dest++ = '[';
191  
            fctx.advance_to(dest);
193  
            fctx.advance_to(dest);
192  
            char* dest1 =
194  
            char* dest1 =
193  
                pct_vformat(lhost_chars, pctx, fctx);
195  
                pct_vformat(lhost_chars, pctx, fctx);
194  
            *dest1++ = ']';
196  
            *dest1++ = ']';
195  
            u.impl_.decoded_[parts::id_host] =
197  
            u.impl_.decoded_[parts::id_host] =
196 -
                pct_string_view(dest - 1, dest1 - dest)
198 +
                detail::to_size_type(
197 -
                    ->decoded_size();
199 +
                    pct_string_view(dest - 1, dest1 - dest)
 
200 +
                        ->decoded_size());
198  
        }
201  
        }
199  
        else
202  
        else
200  
        {
203  
        {
201  
            pctx = {host, pctx.next_arg_id()};
204  
            pctx = {host, pctx.next_arg_id()};
202  
            fctx.advance_to(dest);
205  
            fctx.advance_to(dest);
203  
            char const* dest1 =
206  
            char const* dest1 =
204  
                pct_vformat(host_chars, pctx, fctx);
207  
                pct_vformat(host_chars, pctx, fctx);
205  
            u.impl_.decoded_[parts::id_host] =
208  
            u.impl_.decoded_[parts::id_host] =
206 -
                pct_string_view(dest, dest1 - dest)
209 +
                detail::to_size_type(
207 -
                    ->decoded_size();
210 +
                    pct_string_view(dest, dest1 - dest)
 
211 +
                        ->decoded_size());
208  
        }
212  
        }
209  
        auto uh = u.encoded_host();
213  
        auto uh = u.encoded_host();
210  
        auto h = grammar::parse(uh, host_rule).value();
214  
        auto h = grammar::parse(uh, host_rule).value();
211  
        std::memcpy(
215  
        std::memcpy(
212  
            u.impl_.ip_addr_,
216  
            u.impl_.ip_addr_,
213  
            h.addr,
217  
            h.addr,
214  
            sizeof(u.impl_.ip_addr_));
218  
            sizeof(u.impl_.ip_addr_));
215  
        u.impl_.host_type_ = h.host_type;
219  
        u.impl_.host_type_ = h.host_type;
216  
        if (has_port)
220  
        if (has_port)
217  
        {
221  
        {
218  
            dest = u.set_port_impl(n.port, op);
222  
            dest = u.set_port_impl(n.port, op);
219  
            pctx = {port, pctx.next_arg_id()};
223  
            pctx = {port, pctx.next_arg_id()};
220  
            fctx.advance_to(dest);
224  
            fctx.advance_to(dest);
221  
            char const* dest1 = pct_vformat(
225  
            char const* dest1 = pct_vformat(
222  
                grammar::digit_chars, pctx, fctx);
226  
                grammar::digit_chars, pctx, fctx);
223  
            u.impl_.decoded_[parts::id_port] =
227  
            u.impl_.decoded_[parts::id_port] =
224 -
                pct_string_view(dest, dest1 - dest)
228 +
                detail::to_size_type(
225 -
                    ->decoded_size() + 1;
229 +
                    pct_string_view(dest, dest1 - dest)
 
230 +
                        ->decoded_size() + 1);
226  
            core::string_view up = {dest - 1, dest1};
231  
            core::string_view up = {dest - 1, dest1};
227  
            auto p = grammar::parse(up, detail::port_part_rule).value();
232  
            auto p = grammar::parse(up, detail::port_part_rule).value();
228  
            if (p.has_port)
233  
            if (p.has_port)
229  
                u.impl_.port_number_ = p.port_number;
234  
                u.impl_.port_number_ = p.port_number;
230  
        }
235  
        }
231  
    }
236  
    }
232  
    if (!path.empty())
237  
    if (!path.empty())
233  
    {
238  
    {
234  
        auto dest = u.resize_impl(
239  
        auto dest = u.resize_impl(
235  
            parts::id_path,
240  
            parts::id_path,
236  
            n.path, op);
241  
            n.path, op);
237  
        pctx = {path, pctx.next_arg_id()};
242  
        pctx = {path, pctx.next_arg_id()};
238  
        fctx.advance_to(dest);
243  
        fctx.advance_to(dest);
239  
        auto dest1 = pct_vformat(
244  
        auto dest1 = pct_vformat(
240  
            path_chars, pctx, fctx);
245  
            path_chars, pctx, fctx);
241  
        pct_string_view npath(dest, dest1 - dest);
246  
        pct_string_view npath(dest, dest1 - dest);
242  
        u.impl_.decoded_[parts::id_path] +=
247  
        u.impl_.decoded_[parts::id_path] +=
243 -
            npath.decoded_size();
248 +
            detail::to_size_type(
 
249 +
                npath.decoded_size());
244  
        if (!npath.empty())
250  
        if (!npath.empty())
245  
        {
251  
        {
246 -
            u.impl_.nseg_ = std::count(
252 +
            u.impl_.nseg_ = detail::to_size_type(
247 -
                npath.begin() + 1,
253 +
                std::count(
248 -
                npath.end(), '/') + 1;
254 +
                    npath.begin() + 1,
 
255 +
                    npath.end(), '/') + 1);
249  
        }
256  
        }
250  
        // handle edge cases
257  
        // handle edge cases
251  
        // 1) path is first component and the
258  
        // 1) path is first component and the
252  
        // first segment contains an unencoded ':'
259  
        // first segment contains an unencoded ':'
253  
        // This is impossible because the template
260  
        // This is impossible because the template
254  
        // "{}" would be a host.
261  
        // "{}" would be a host.
255  
        if (u.scheme().empty() &&
262  
        if (u.scheme().empty() &&
256  
            !u.has_authority())
263  
            !u.has_authority())
257  
        {
264  
        {
258  
            auto fseg = u.encoded_segments().front();
265  
            auto fseg = u.encoded_segments().front();
259  
            std::size_t nc = std::count(
266  
            std::size_t nc = std::count(
260  
                fseg.begin(), fseg.end(), ':');
267  
                fseg.begin(), fseg.end(), ':');
261  
            if (nc)
268  
            if (nc)
262  
            {
269  
            {
263  
                std::size_t diff = nc * 2;
270  
                std::size_t diff = nc * 2;
264  
                u.reserve(n_total + diff);
271  
                u.reserve(n_total + diff);
265  
                dest = u.resize_impl(
272  
                dest = u.resize_impl(
266  
                    parts::id_path,
273  
                    parts::id_path,
267  
                    n.path + diff, op);
274  
                    n.path + diff, op);
268  
                char* dest0 = dest + diff;
275  
                char* dest0 = dest + diff;
269  
                std::memmove(dest0, dest, n.path);
276  
                std::memmove(dest0, dest, n.path);
270  
                while (dest0 != dest)
277  
                while (dest0 != dest)
271  
                {
278  
                {
272  
                    if (*dest0 != ':')
279  
                    if (*dest0 != ':')
273  
                    {
280  
                    {
274  
                        *dest++ = *dest0++;
281  
                        *dest++ = *dest0++;
275  
                    }
282  
                    }
276  
                    else
283  
                    else
277  
                    {
284  
                    {
278  
                        *dest++ = '%';
285  
                        *dest++ = '%';
279  
                        *dest++ = '3';
286  
                        *dest++ = '3';
280  
                        *dest++ = 'A';
287  
                        *dest++ = 'A';
281  
                        dest0++;
288  
                        dest0++;
282  
                    }
289  
                    }
283  
                }
290  
                }
284  
            }
291  
            }
285  
        }
292  
        }
286  
        // 2) url has no authority and path
293  
        // 2) url has no authority and path
287  
        // starts with "//"
294  
        // starts with "//"
288  
        if (!u.has_authority() &&
295  
        if (!u.has_authority() &&
289  
            u.encoded_path().starts_with("//"))
296  
            u.encoded_path().starts_with("//"))
290  
        {
297  
        {
291  
            u.reserve(n_total + 2);
298  
            u.reserve(n_total + 2);
292  
            dest = u.resize_impl(
299  
            dest = u.resize_impl(
293  
                parts::id_path,
300  
                parts::id_path,
294  
                n.path + 2, op);
301  
                n.path + 2, op);
295  
            std::memmove(dest + 2, dest, n.path);
302  
            std::memmove(dest + 2, dest, n.path);
296  
            *dest++ = '/';
303  
            *dest++ = '/';
297  
            *dest = '.';
304  
            *dest = '.';
298  
        }
305  
        }
299  
    }
306  
    }
300  
    if (has_query)
307  
    if (has_query)
301  
    {
308  
    {
302  
        auto dest = u.resize_impl(
309  
        auto dest = u.resize_impl(
303  
            parts::id_query,
310  
            parts::id_query,
304  
            n.query + 1, op);
311  
            n.query + 1, op);
305  
        *dest++ = '?';
312  
        *dest++ = '?';
306  
        pctx = {query, pctx.next_arg_id()};
313  
        pctx = {query, pctx.next_arg_id()};
307  
        fctx.advance_to(dest);
314  
        fctx.advance_to(dest);
308  
        auto dest1 = pct_vformat(
315  
        auto dest1 = pct_vformat(
309  
            query_chars, pctx, fctx);
316  
            query_chars, pctx, fctx);
310  
        pct_string_view nquery(dest, dest1 - dest);
317  
        pct_string_view nquery(dest, dest1 - dest);
311  
        u.impl_.decoded_[parts::id_query] +=
318  
        u.impl_.decoded_[parts::id_query] +=
312 -
            nquery.decoded_size() + 1;
319 +
            detail::to_size_type(
 
320 +
                nquery.decoded_size() + 1);
313  
        if (!nquery.empty())
321  
        if (!nquery.empty())
314  
        {
322  
        {
315 -
            u.impl_.nparam_ = std::count(
323 +
            u.impl_.nparam_ = detail::to_size_type(
316 -
                nquery.begin(),
324 +
                std::count(
317 -
                nquery.end(), '&') + 1;
325 +
                    nquery.begin(),
 
326 +
                    nquery.end(), '&') + 1);
318  
        }
327  
        }
319  
    }
328  
    }
320  
    if (has_frag)
329  
    if (has_frag)
321  
    {
330  
    {
322  
        auto dest = u.resize_impl(
331  
        auto dest = u.resize_impl(
323  
            parts::id_frag,
332  
            parts::id_frag,
324  
            n.frag + 1, op);
333  
            n.frag + 1, op);
325  
        *dest++ = '#';
334  
        *dest++ = '#';
326  
        pctx = {frag, pctx.next_arg_id()};
335  
        pctx = {frag, pctx.next_arg_id()};
327  
        fctx.advance_to(dest);
336  
        fctx.advance_to(dest);
328  
        auto dest1 = pct_vformat(
337  
        auto dest1 = pct_vformat(
329  
            fragment_chars, pctx, fctx);
338  
            fragment_chars, pctx, fctx);
330  
        u.impl_.decoded_[parts::id_frag] +=
339  
        u.impl_.decoded_[parts::id_frag] +=
331 -
            make_pct_string_view(
340 +
            detail::to_size_type(
332 -
                core::string_view(dest, dest1 - dest))
341 +
                make_pct_string_view(
333 -
                ->decoded_size() + 1;
342 +
                    core::string_view(dest, dest1 - dest))
 
343 +
                    ->decoded_size() + 1);
334  
    }
344  
    }
335  
}
345  
}
336  

346  

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

354  

345  
    constexpr
355  
    constexpr
346  
    pct_encoded_fmt_string_rule_t(
356  
    pct_encoded_fmt_string_rule_t(
347  
        CharSet const& cs) noexcept
357  
        CharSet const& cs) noexcept
348  
        : cs_(cs)
358  
        : cs_(cs)
349  
    {
359  
    {
350  
    }
360  
    }
351  

361  

352  
    template<class CharSet_>
362  
    template<class CharSet_>
353  
    friend
363  
    friend
354  
    constexpr
364  
    constexpr
355  
    auto
365  
    auto
356  
    pct_encoded_fmt_string_rule(
366  
    pct_encoded_fmt_string_rule(
357  
        CharSet_ const& cs) noexcept ->
367  
        CharSet_ const& cs) noexcept ->
358  
    pct_encoded_fmt_string_rule_t<CharSet_>;
368  
    pct_encoded_fmt_string_rule_t<CharSet_>;
359  

369  

360  
    system::result<value_type>
370  
    system::result<value_type>
361  
    parse(
371  
    parse(
362  
        char const*& it,
372  
        char const*& it,
363  
        char const* end) const noexcept
373  
        char const* end) const noexcept
364  
    {
374  
    {
365  
        auto const start = it;
375  
        auto const start = it;
366  
        if(it == end)
376  
        if(it == end)
367  
        {
377  
        {
368  
            // this might be empty
378  
            // this might be empty
369  
            return {};
379  
            return {};
370  
        }
380  
        }
371  

381  

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

398  

389  
        return core::string_view(start, it - start);
399  
        return core::string_view(start, it - start);
390  
    }
400  
    }
391  

401  

392  
private:
402  
private:
393  
    CharSet cs_;
403  
    CharSet cs_;
394  
};
404  
};
395  

405  

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

420  

411  
    return pct_encoded_fmt_string_rule_t<CharSet>(cs);
421  
    return pct_encoded_fmt_string_rule_t<CharSet>(cs);
412  
}
422  
}
413  

423  

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

431  

422  
    constexpr
432  
    constexpr
423  
    fmt_token_rule_t(
433  
    fmt_token_rule_t(
424  
        CharSet const& cs) noexcept
434  
        CharSet const& cs) noexcept
425  
        : cs_(cs)
435  
        : cs_(cs)
426  
    {
436  
    {
427  
    }
437  
    }
428  

438  

429  
    template<class CharSet_>
439  
    template<class CharSet_>
430  
    friend
440  
    friend
431  
    constexpr
441  
    constexpr
432  
    auto
442  
    auto
433  
    fmt_token_rule(
443  
    fmt_token_rule(
434  
        CharSet_ const& cs) noexcept ->
444  
        CharSet_ const& cs) noexcept ->
435  
    fmt_token_rule_t<CharSet_>;
445  
    fmt_token_rule_t<CharSet_>;
436  

446  

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

464  

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

486  

477  
        if(it == start)
487  
        if(it == start)
478  
        {
488  
        {
479  
            // it != end but we consumed nothing
489  
            // it != end but we consumed nothing
480  
            BOOST_URL_RETURN_EC(
490  
            BOOST_URL_RETURN_EC(
481  
                grammar::error::need_more);
491  
                grammar::error::need_more);
482  
        }
492  
        }
483  

493  

484  
        return core::string_view(start, it - start);
494  
        return core::string_view(start, it - start);
485  
    }
495  
    }
486  

496  

487  
private:
497  
private:
488  
    CharSet cs_;
498  
    CharSet cs_;
489  
};
499  
};
490  

500  

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

515  

506  
    return fmt_token_rule_t<CharSet>(cs);
516  
    return fmt_token_rule_t<CharSet>(cs);
507  
}
517  
}
508  

518  

509  
struct userinfo_template_rule_t
519  
struct userinfo_template_rule_t
510  
{
520  
{
511  
    struct value_type
521  
    struct value_type
512  
    {
522  
    {
513  
        core::string_view user;
523  
        core::string_view user;
514  
        core::string_view password;
524  
        core::string_view password;
515  
        bool has_password = false;
525  
        bool has_password = false;
516  
    };
526  
    };
517  

527  

518  
    auto
528  
    auto
519  
    parse(
529  
    parse(
520  
        char const*& it,
530  
        char const*& it,
521  
        char const* end
531  
        char const* end
522  
            ) const noexcept ->
532  
            ) const noexcept ->
523  
        system::result<value_type>
533  
        system::result<value_type>
524  
    {
534  
    {
525  
        static constexpr auto uchars =
535  
        static constexpr auto uchars =
526  
            unreserved_chars +
536  
            unreserved_chars +
527  
            sub_delim_chars;
537  
            sub_delim_chars;
528  
        static constexpr auto pwchars =
538  
        static constexpr auto pwchars =
529  
            uchars + ':';
539  
            uchars + ':';
530  

540  

531  
        value_type t;
541  
        value_type t;
532  

542  

533  
        // user
543  
        // user
534  
        static constexpr auto user_fmt_rule =
544  
        static constexpr auto user_fmt_rule =
535  
            pct_encoded_fmt_string_rule(uchars);
545  
            pct_encoded_fmt_string_rule(uchars);
536  
        auto rv = grammar::parse(
546  
        auto rv = grammar::parse(
537  
            it, end, user_fmt_rule);
547  
            it, end, user_fmt_rule);
538  
        BOOST_ASSERT(rv);
548  
        BOOST_ASSERT(rv);
539  
        t.user = *rv;
549  
        t.user = *rv;
540  

550  

541  
        // ':'
551  
        // ':'
542  
        if( it == end ||
552  
        if( it == end ||
543  
            *it != ':')
553  
            *it != ':')
544  
        {
554  
        {
545  
            t.has_password = false;
555  
            t.has_password = false;
546  
            t.password = {};
556  
            t.password = {};
547  
            return t;
557  
            return t;
548  
        }
558  
        }
549  
        ++it;
559  
        ++it;
550  

560  

551  
        // pass
561  
        // pass
552  
        static constexpr auto pass_fmt_rule =
562  
        static constexpr auto pass_fmt_rule =
553  
            pct_encoded_fmt_string_rule(grammar::ref(pwchars));
563  
            pct_encoded_fmt_string_rule(grammar::ref(pwchars));
554  
        rv = grammar::parse(
564  
        rv = grammar::parse(
555  
            it, end, pass_fmt_rule);
565  
            it, end, pass_fmt_rule);
556  
        BOOST_ASSERT(rv);
566  
        BOOST_ASSERT(rv);
557  
        t.has_password = true;
567  
        t.has_password = true;
558  
        t.password = *rv;
568  
        t.password = *rv;
559  

569  

560  
        return t;
570  
        return t;
561  
    }
571  
    }
562  
};
572  
};
563  

573  

564  
constexpr userinfo_template_rule_t userinfo_template_rule{};
574  
constexpr userinfo_template_rule_t userinfo_template_rule{};
565  

575  

566  
struct host_template_rule_t
576  
struct host_template_rule_t
567  
{
577  
{
568  
    using value_type = core::string_view;
578  
    using value_type = core::string_view;
569  

579  

570  
    auto
580  
    auto
571  
    parse(
581  
    parse(
572  
        char const*& it,
582  
        char const*& it,
573  
        char const* end
583  
        char const* end
574  
            ) const noexcept ->
584  
            ) const noexcept ->
575  
        system::result<value_type>
585  
        system::result<value_type>
576  
    {
586  
    {
577  
        if(it == end)
587  
        if(it == end)
578  
        {
588  
        {
579  
            // empty host
589  
            // empty host
580  
            return {};
590  
            return {};
581  
        }
591  
        }
582  

592  

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

636  

627  
constexpr host_template_rule_t host_template_rule{};
637  
constexpr host_template_rule_t host_template_rule{};
628  

638  

629  
struct authority_template_rule_t
639  
struct authority_template_rule_t
630  
{
640  
{
631  
    using value_type = pattern;
641  
    using value_type = pattern;
632  

642  

633  
    system::result<value_type>
643  
    system::result<value_type>
634  
    parse(
644  
    parse(
635  
        char const*& it,
645  
        char const*& it,
636  
        char const* end
646  
        char const* end
637  
    ) const noexcept
647  
    ) const noexcept
638  
    {
648  
    {
639  
        pattern u;
649  
        pattern u;
640  

650  

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

670  

661  
        // host
671  
        // host
662  
        {
672  
        {
663  
            auto rv = grammar::parse(
673  
            auto rv = grammar::parse(
664  
                it, end,
674  
                it, end,
665  
                host_template_rule);
675  
                host_template_rule);
666  
            // host is allowed to be empty
676  
            // host is allowed to be empty
667  
            BOOST_ASSERT(rv);
677  
            BOOST_ASSERT(rv);
668  
            u.host = *rv;
678  
            u.host = *rv;
669  
        }
679  
        }
670  

680  

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

706  

697  
        return u;
707  
        return u;
698  
    }
708  
    }
699  
};
709  
};
700  

710  

701  
constexpr authority_template_rule_t authority_template_rule{};
711  
constexpr authority_template_rule_t authority_template_rule{};
702  

712  

703  
struct scheme_template_rule_t
713  
struct scheme_template_rule_t
704  
{
714  
{
705  
    using value_type = core::string_view;
715  
    using value_type = core::string_view;
706  

716  

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

736  

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

752  

743  
        static
753  
        static
744  
        constexpr
754  
        constexpr
745  
        grammar::lut_chars scheme_chars(
755  
        grammar::lut_chars scheme_chars(
746  
            "0123456789" "+-."
756  
            "0123456789" "+-."
747  
            "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
757  
            "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
748  
            "abcdefghijklmnopqrstuvwxyz");
758  
            "abcdefghijklmnopqrstuvwxyz");
749  

759  

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

778  

769  
constexpr scheme_template_rule_t scheme_template_rule{};
779  
constexpr scheme_template_rule_t scheme_template_rule{};
770  

780  

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

788  

779  
    system::result<value_type>
789  
    system::result<value_type>
780  
    parse(
790  
    parse(
781  
        char const*& it,
791  
        char const*& it,
782  
        char const* const end
792  
        char const* const end
783  
    ) const noexcept
793  
    ) const noexcept
784  
    {
794  
    {
785  
        pattern u;
795  
        pattern u;
786  

796  

787  
        // optional scheme
797  
        // optional scheme
788  
        {
798  
        {
789  
            auto it0 = it;
799  
            auto it0 = it;
790  
            auto rv = grammar::parse(
800  
            auto rv = grammar::parse(
791  
                it, end,
801  
                it, end,
792  
                grammar::tuple_rule(
802  
                grammar::tuple_rule(
793  
                    scheme_template_rule,
803  
                    scheme_template_rule,
794  
                    grammar::squelch(
804  
                    grammar::squelch(
795  
                        grammar::delim_rule(':'))));
805  
                        grammar::delim_rule(':'))));
796  
            if(rv)
806  
            if(rv)
797  
                u.scheme = *rv;
807  
                u.scheme = *rv;
798  
            else
808  
            else
799  
                it = it0;
809  
                it = it0;
800  
        }
810  
        }
801  

811  

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

856  

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

877  

868  
        // the authority requires an absolute path
878  
        // the authority requires an absolute path
869  
        // or an empty path
879  
        // or an empty path
870  
        if (it == end ||
880  
        if (it == end ||
871  
            (u.has_authority &&
881  
            (u.has_authority &&
872  
             (*it != '/' &&
882  
             (*it != '/' &&
873  
              *it != '?' &&
883  
              *it != '?' &&
874  
              *it != '#')))
884  
              *it != '#')))
875  
        {
885  
        {
876  
            // path-empty
886  
            // path-empty
877  
            return u;
887  
            return u;
878  
        }
888  
        }
879  

889  

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

901  

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

920  

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

939  

930  
        return u;
940  
        return u;
931  
    }
941  
    }
932  
};
942  
};
933  

943  

934  
constexpr pattern_rule_t pattern_rule{};
944  
constexpr pattern_rule_t pattern_rule{};
935  

945  

936  
system::result<pattern>
946  
system::result<pattern>
937  
parse_pattern(
947  
parse_pattern(
938  
    core::string_view s)
948  
    core::string_view s)
939  
{
949  
{
940  
    return grammar::parse(
950  
    return grammar::parse(
941  
        s, pattern_rule);
951  
        s, pattern_rule);
942  
}
952  
}
943  

953  

944  
} // detail
954  
} // detail
945  
} // urls
955  
} // urls
946 -

 
947  
} // boost
956  
} // boost