1  
//
1  
//
2  
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
2  
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
3  
// Copyright (c) 2022 Alan de Freitas (alandefreitas@gmail.com)
3  
// Copyright (c) 2022 Alan de Freitas (alandefreitas@gmail.com)
4  
//
4  
//
5  
// Distributed under the Boost Software License, Version 1.0. (See accompanying
5  
// Distributed under the Boost Software License, Version 1.0. (See accompanying
6  
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6  
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7  
//
7  
//
8  
// Official repository: https://github.com/boostorg/url
8  
// Official repository: https://github.com/boostorg/url
9  
//
9  
//
10  

10  

11  
#ifndef BOOST_URL_IMPL_URL_BASE_HPP
11  
#ifndef BOOST_URL_IMPL_URL_BASE_HPP
12  
#define BOOST_URL_IMPL_URL_BASE_HPP
12  
#define BOOST_URL_IMPL_URL_BASE_HPP
13  

13  

14  
#include <boost/url/encode.hpp>
14  
#include <boost/url/encode.hpp>
15  
#include <boost/url/error.hpp>
15  
#include <boost/url/error.hpp>
16  
#include <boost/url/host_type.hpp>
16  
#include <boost/url/host_type.hpp>
17  
#include <boost/url/scheme.hpp>
17  
#include <boost/url/scheme.hpp>
18  
#include <boost/url/url_view.hpp>
18  
#include <boost/url/url_view.hpp>
19  
#include <boost/url/detail/any_params_iter.hpp>
19  
#include <boost/url/detail/any_params_iter.hpp>
20  
#include <boost/url/detail/any_segments_iter.hpp>
20  
#include <boost/url/detail/any_segments_iter.hpp>
21  
#include <boost/url/detail/decode.hpp>
21  
#include <boost/url/detail/decode.hpp>
22  
#include <boost/url/detail/encode.hpp>
22  
#include <boost/url/detail/encode.hpp>
23  
#include <boost/url/detail/except.hpp>
23  
#include <boost/url/detail/except.hpp>
24  
#include <boost/url/detail/normalize.hpp>
24  
#include <boost/url/detail/normalize.hpp>
25  
#include <boost/url/detail/path.hpp>
25  
#include <boost/url/detail/path.hpp>
26  
#include <boost/url/detail/print.hpp>
26  
#include <boost/url/detail/print.hpp>
27  
#include <boost/url/grammar/ci_string.hpp>
27  
#include <boost/url/grammar/ci_string.hpp>
28  
#include <boost/url/rfc/authority_rule.hpp>
28  
#include <boost/url/rfc/authority_rule.hpp>
29  
#include <boost/url/rfc/query_rule.hpp>
29  
#include <boost/url/rfc/query_rule.hpp>
30  
#include <boost/url/rfc/ipv6_address_rule.hpp>
30  
#include <boost/url/rfc/ipv6_address_rule.hpp>
31  
#include <boost/url/rfc/detail/charsets.hpp>
31  
#include <boost/url/rfc/detail/charsets.hpp>
32  
#include <boost/url/rfc/detail/host_rule.hpp>
32  
#include <boost/url/rfc/detail/host_rule.hpp>
33  
#include <boost/url/rfc/detail/ipvfuture_rule.hpp>
33  
#include <boost/url/rfc/detail/ipvfuture_rule.hpp>
34  
#include <boost/url/rfc/detail/path_rules.hpp>
34  
#include <boost/url/rfc/detail/path_rules.hpp>
35  
#include <boost/url/rfc/detail/port_rule.hpp>
35  
#include <boost/url/rfc/detail/port_rule.hpp>
36  
#include <boost/url/rfc/detail/scheme_rule.hpp>
36  
#include <boost/url/rfc/detail/scheme_rule.hpp>
37  
#include <boost/url/rfc/detail/userinfo_rule.hpp>
37  
#include <boost/url/rfc/detail/userinfo_rule.hpp>
38  
#include <boost/url/grammar/parse.hpp>
38  
#include <boost/url/grammar/parse.hpp>
39  
#include <boost/url/detail/move_chars.hpp>
39  
#include <boost/url/detail/move_chars.hpp>
40  
#include <cstring>
40  
#include <cstring>
41  
#include <iostream>
41  
#include <iostream>
42  
#include <stdexcept>
42  
#include <stdexcept>
43  
#include <utility>
43  
#include <utility>
44  

44  

45  
namespace boost {
45  
namespace boost {
46  
namespace urls {
46  
namespace urls {
47  

47  

48  
//------------------------------------------------
48  
//------------------------------------------------
49  

49  

50  
// these objects help handle the cases
50  
// these objects help handle the cases
51  
// where the user passes in strings that
51  
// where the user passes in strings that
52  
// come from inside the url buffer.
52  
// come from inside the url buffer.
53  

53  

54  
inline
54  
inline
55  
url_base::
55  
url_base::
56  
op_t::
56  
op_t::
57  
~op_t()
57  
~op_t()
58  
{
58  
{
59  
    if(old)
59  
    if(old)
60  
        u.cleanup(*this);
60  
        u.cleanup(*this);
61  
    u.check_invariants();
61  
    u.check_invariants();
62  
}
62  
}
63  

63  

64  
inline
64  
inline
65  
url_base::
65  
url_base::
66  
op_t::
66  
op_t::
67  
op_t(
67  
op_t(
68  
    url_base& impl_,
68  
    url_base& impl_,
69  
    core::string_view* s0_,
69  
    core::string_view* s0_,
70  
    core::string_view* s1_) noexcept
70  
    core::string_view* s1_) noexcept
71  
    : u(impl_)
71  
    : u(impl_)
72  
    , s0(s0_)
72  
    , s0(s0_)
73  
    , s1(s1_)
73  
    , s1(s1_)
74  
{
74  
{
75  
    u.check_invariants();
75  
    u.check_invariants();
76  
}
76  
}
77  

77  

78  
inline
78  
inline
79  
void
79  
void
80  
url_base::
80  
url_base::
81  
op_t::
81  
op_t::
82  
move(
82  
move(
83  
    char* dest,
83  
    char* dest,
84  
    char const* src,
84  
    char const* src,
85  
    std::size_t n) noexcept
85  
    std::size_t n) noexcept
86  
{
86  
{
87  
    if(! n)
87  
    if(! n)
88  
        return;
88  
        return;
89  
    if(s0)
89  
    if(s0)
90  
    {
90  
    {
91  
        if(s1)
91  
        if(s1)
92  
            return detail::move_chars(
92  
            return detail::move_chars(
93  
             dest, src, n, *s0, *s1);
93  
             dest, src, n, *s0, *s1);
94  
        return detail::move_chars(
94  
        return detail::move_chars(
95  
            dest, src, n, *s0);
95  
            dest, src, n, *s0);
96  
    }
96  
    }
97  
    detail::move_chars(
97  
    detail::move_chars(
98  
        dest, src, n);
98  
        dest, src, n);
99  
}
99  
}
100  

100  

101  
//------------------------------------------------
101  
//------------------------------------------------
102  

102  

103  
// construct reference
103  
// construct reference
104  
inline
104  
inline
105  
url_base::
105  
url_base::
106  
url_base(
106  
url_base(
107  
    detail::url_impl const& impl) noexcept
107  
    detail::url_impl const& impl) noexcept
108  
    : url_view_base(impl)
108  
    : url_view_base(impl)
109  
{
109  
{
110  
}
110  
}
111  

111  

112  
inline
112  
inline
113  
void
113  
void
114  
url_base::
114  
url_base::
115  
reserve_impl(std::size_t n)
115  
reserve_impl(std::size_t n)
116  
{
116  
{
117  
    op_t op(*this);
117  
    op_t op(*this);
118  
    reserve_impl(n, op);
118  
    reserve_impl(n, op);
119  
    if(s_)
119  
    if(s_)
120  
        s_[size()] = '\0';
120  
        s_[size()] = '\0';
121  
}
121  
}
122  

122  

123  
// make a copy of u
123  
// make a copy of u
124  
inline
124  
inline
125  
void
125  
void
126  
url_base::
126  
url_base::
127  
copy(url_view_base const& u)
127  
copy(url_view_base const& u)
128  
{
128  
{
129  
    if (this == &u)
129  
    if (this == &u)
130  
        return;
130  
        return;
131  
    op_t op(*this);
131  
    op_t op(*this);
132  
    if(u.size() == 0)
132  
    if(u.size() == 0)
133  
    {
133  
    {
134  
        clear();
134  
        clear();
135  
        return;
135  
        return;
136  
    }
136  
    }
137  
    reserve_impl(
137  
    reserve_impl(
138  
        u.size(), op);
138  
        u.size(), op);
139  
    impl_ = u.impl();
139  
    impl_ = u.impl();
140  
    impl_.cs_ = s_;
140  
    impl_.cs_ = s_;
141  
    impl_.from_ = {from::url};
141  
    impl_.from_ = {from::url};
142  
    std::memcpy(s_,
142  
    std::memcpy(s_,
143  
        u.data(), u.size());
143  
        u.data(), u.size());
144  
    s_[size()] = '\0';
144  
    s_[size()] = '\0';
145  
}
145  
}
146  

146  

147  
//------------------------------------------------
147  
//------------------------------------------------
148  
//
148  
//
149  
// Scheme
149  
// Scheme
150  
//
150  
//
151  
//------------------------------------------------
151  
//------------------------------------------------
152  

152  

153  
inline
153  
inline
154  
url_base&
154  
url_base&
155  
url_base::
155  
url_base::
156  
set_scheme(core::string_view s)
156  
set_scheme(core::string_view s)
157  
{
157  
{
158  
    set_scheme_impl(
158  
    set_scheme_impl(
159  
        s, string_to_scheme(s));
159  
        s, string_to_scheme(s));
160  
    return *this;
160  
    return *this;
161  
}
161  
}
162  

162  

163  
inline
163  
inline
164  
url_base&
164  
url_base&
165  
url_base::
165  
url_base::
166  
set_scheme_id(urls::scheme id)
166  
set_scheme_id(urls::scheme id)
167  
{
167  
{
168  
    if(id == urls::scheme::unknown)
168  
    if(id == urls::scheme::unknown)
169  
        detail::throw_invalid_argument();
169  
        detail::throw_invalid_argument();
170  
    if(id == urls::scheme::none)
170  
    if(id == urls::scheme::none)
171  
        return remove_scheme();
171  
        return remove_scheme();
172  
    set_scheme_impl(to_string(id), id);
172  
    set_scheme_impl(to_string(id), id);
173  
    return *this;
173  
    return *this;
174  
}
174  
}
175  

175  

176  
inline
176  
inline
177  
url_base&
177  
url_base&
178  
url_base::
178  
url_base::
179  
remove_scheme()
179  
remove_scheme()
180  
{
180  
{
181  
    op_t op(*this);
181  
    op_t op(*this);
182  
    auto const sn = impl_.len(id_scheme);
182  
    auto const sn = impl_.len(id_scheme);
183  
    if(sn == 0)
183  
    if(sn == 0)
184  
        return *this;
184  
        return *this;
185  
    auto const po = impl_.offset(id_path);
185  
    auto const po = impl_.offset(id_path);
186  
    auto fseg = first_segment();
186  
    auto fseg = first_segment();
187  
    bool const encode_colon =
187  
    bool const encode_colon =
188  
        !has_authority() &&
188  
        !has_authority() &&
189  
        impl_.nseg_ > 0 &&
189  
        impl_.nseg_ > 0 &&
190  
        s_[po] != '/' &&
190  
        s_[po] != '/' &&
191  
        fseg.contains(':');
191  
        fseg.contains(':');
192  
    if(!encode_colon)
192  
    if(!encode_colon)
193  
    {
193  
    {
194  
        // just remove the scheme
194  
        // just remove the scheme
195  
        resize_impl(id_scheme, 0, op);
195  
        resize_impl(id_scheme, 0, op);
196  
        impl_.scheme_ = urls::scheme::none;
196  
        impl_.scheme_ = urls::scheme::none;
197  
        check_invariants();
197  
        check_invariants();
198  
        return *this;
198  
        return *this;
199  
    }
199  
    }
200  
    // encode any ":" in the first path segment
200  
    // encode any ":" in the first path segment
201  
    BOOST_ASSERT(sn >= 2);
201  
    BOOST_ASSERT(sn >= 2);
202  
    auto pn = impl_.len(id_path);
202  
    auto pn = impl_.len(id_path);
203  
    std::size_t cn = 0;
203  
    std::size_t cn = 0;
204  
    for (char c: fseg)
204  
    for (char c: fseg)
205  
        cn += c == ':';
205  
        cn += c == ':';
206  
    std::size_t new_size =
206  
    std::size_t new_size =
207  
        size() - sn + 2 * cn;
207  
        size() - sn + 2 * cn;
208  
    bool need_resize = new_size > size();
208  
    bool need_resize = new_size > size();
209  
    if (need_resize)
209  
    if (need_resize)
210  
    {
210  
    {
211  
        resize_impl(
211  
        resize_impl(
212  
            id_path, pn + 2 * cn, op);
212  
            id_path, pn + 2 * cn, op);
213  
    }
213  
    }
214  
    // move [id_scheme, id_path) left
214  
    // move [id_scheme, id_path) left
215  
    op.move(
215  
    op.move(
216  
        s_,
216  
        s_,
217  
        s_ + sn,
217  
        s_ + sn,
218  
        po - sn);
218  
        po - sn);
219  
    // move [id_path, id_query) left
219  
    // move [id_path, id_query) left
220  
    auto qo = impl_.offset(id_query);
220  
    auto qo = impl_.offset(id_query);
221  
    op.move(
221  
    op.move(
222  
        s_ + po - sn,
222  
        s_ + po - sn,
223  
        s_ + po,
223  
        s_ + po,
224  
        qo - po);
224  
        qo - po);
225  
    // move [id_query, id_end) left
225  
    // move [id_query, id_end) left
226  
    op.move(
226  
    op.move(
227  
        s_ + qo - sn + 2 * cn,
227  
        s_ + qo - sn + 2 * cn,
228  
        s_ + qo,
228  
        s_ + qo,
229  
        impl_.offset(id_end) - qo);
229  
        impl_.offset(id_end) - qo);
230  

230  

231  
    // adjust part offsets.
231  
    // adjust part offsets.
232  
    // (po and qo are invalidated)
232  
    // (po and qo are invalidated)
233  
    if (need_resize)
233  
    if (need_resize)
234  
    {
234  
    {
235  
        impl_.adjust_left(id_user, id_end, sn);
235  
        impl_.adjust_left(id_user, id_end, sn);
236  
    }
236  
    }
237  
    else
237  
    else
238  
    {
238  
    {
239  
        impl_.adjust_left(id_user, id_path, sn);
239  
        impl_.adjust_left(id_user, id_path, sn);
240  
        impl_.adjust_left(id_query, id_end, sn - 2 * cn);
240  
        impl_.adjust_left(id_query, id_end, sn - 2 * cn);
241  
    }
241  
    }
242  
    if (encode_colon)
242  
    if (encode_colon)
243  
    {
243  
    {
244  
        // move the 2nd, 3rd, ... segments
244  
        // move the 2nd, 3rd, ... segments
245  
        auto begin = s_ + impl_.offset(id_path);
245  
        auto begin = s_ + impl_.offset(id_path);
246  
        auto it = begin;
246  
        auto it = begin;
247  
        auto end = begin + pn;
247  
        auto end = begin + pn;
248  
        while (it != end &&
248  
        while (it != end &&
249  
               *it != '/')
249  
               *it != '/')
250  
            ++it;
250  
            ++it;
251  
        // we don't need op here because this is
251  
        // we don't need op here because this is
252  
        // an internal operation
252  
        // an internal operation
253  
        std::memmove(it + (2 * cn), it, end - it);
253  
        std::memmove(it + (2 * cn), it, end - it);
254  

254  

255  
        // move 1st segment
255  
        // move 1st segment
256  
        auto src = s_ + impl_.offset(id_path) + pn;
256  
        auto src = s_ + impl_.offset(id_path) + pn;
257  
        auto dest = s_ + impl_.offset(id_query);
257  
        auto dest = s_ + impl_.offset(id_query);
258  
        src -= end - it;
258  
        src -= end - it;
259  
        dest -= end - it;
259  
        dest -= end - it;
260  
        pn -= end - it;
260  
        pn -= end - it;
261  
        do {
261  
        do {
262  
            --src;
262  
            --src;
263  
            --dest;
263  
            --dest;
264  
            if (*src != ':')
264  
            if (*src != ':')
265  
            {
265  
            {
266  
                *dest = *src;
266  
                *dest = *src;
267  
            }
267  
            }
268  
            else
268  
            else
269  
            {
269  
            {
270  
                // use uppercase as required by
270  
                // use uppercase as required by
271  
                // syntax-based normalization
271  
                // syntax-based normalization
272  
                *dest-- = 'A';
272  
                *dest-- = 'A';
273  
                *dest-- = '3';
273  
                *dest-- = '3';
274  
                *dest = '%';
274  
                *dest = '%';
275  
            }
275  
            }
276  
            --pn;
276  
            --pn;
277  
        } while (pn);
277  
        } while (pn);
278  
    }
278  
    }
279  
    s_[size()] = '\0';
279  
    s_[size()] = '\0';
280  
    impl_.scheme_ = urls::scheme::none;
280  
    impl_.scheme_ = urls::scheme::none;
281  
    return *this;
281  
    return *this;
282  
}
282  
}
283  

283  

284  
//------------------------------------------------
284  
//------------------------------------------------
285  
//
285  
//
286  
// Authority
286  
// Authority
287  
//
287  
//
288  
//------------------------------------------------
288  
//------------------------------------------------
289  

289  

290  
inline
290  
inline
291  
url_base&
291  
url_base&
292  
url_base::
292  
url_base::
293  
set_encoded_authority(
293  
set_encoded_authority(
294  
    pct_string_view s)
294  
    pct_string_view s)
295  
{
295  
{
296  
    op_t op(*this, &detail::ref(s));
296  
    op_t op(*this, &detail::ref(s));
297  
    authority_view a = grammar::parse(
297  
    authority_view a = grammar::parse(
298  
        s, authority_rule
298  
        s, authority_rule
299  
            ).value(BOOST_URL_POS);
299  
            ).value(BOOST_URL_POS);
300  
    auto n = s.size() + 2;
300  
    auto n = s.size() + 2;
301  
    auto const need_slash =
301  
    auto const need_slash =
302  
        ! is_path_absolute() &&
302  
        ! is_path_absolute() &&
303  
        impl_.len(id_path) > 0;
303  
        impl_.len(id_path) > 0;
304  
    if(need_slash)
304  
    if(need_slash)
305  
        ++n;
305  
        ++n;
306  
    auto dest = resize_impl(
306  
    auto dest = resize_impl(
307  
        id_user, id_path, n, op);
307  
        id_user, id_path, n, op);
308  
    dest[0] = '/';
308  
    dest[0] = '/';
309  
    dest[1] = '/';
309  
    dest[1] = '/';
310  
    std::memcpy(dest + 2,
310  
    std::memcpy(dest + 2,
311  
        s.data(), s.size());
311  
        s.data(), s.size());
312  
    if(need_slash)
312  
    if(need_slash)
313  
        dest[n - 1] = '/';
313  
        dest[n - 1] = '/';
314  
    impl_.apply_authority(a.u_);
314  
    impl_.apply_authority(a.u_);
315  
    if(need_slash)
315  
    if(need_slash)
316  
        impl_.adjust_right(
316  
        impl_.adjust_right(
317  
                id_query, id_end, 1);
317  
                id_query, id_end, 1);
318  
    return *this;
318  
    return *this;
319  
}
319  
}
320  

320  

321  
inline
321  
inline
322  
url_base&
322  
url_base&
323  
url_base::
323  
url_base::
324  
remove_authority()
324  
remove_authority()
325  
{
325  
{
326  
    if(! has_authority())
326  
    if(! has_authority())
327  
        return *this;
327  
        return *this;
328  

328  

329  
    op_t op(*this);
329  
    op_t op(*this);
330  
    auto path = impl_.get(id_path);
330  
    auto path = impl_.get(id_path);
331  
    bool const need_dot = path.starts_with("//");
331  
    bool const need_dot = path.starts_with("//");
332  
    if(need_dot)
332  
    if(need_dot)
333  
    {
333  
    {
334  
        // prepend "/.", can't throw
334  
        // prepend "/.", can't throw
335  
        auto p = resize_impl(
335  
        auto p = resize_impl(
336  
            id_user, id_path, 2, op);
336  
            id_user, id_path, 2, op);
337  
        p[0] = '/';
337  
        p[0] = '/';
338  
        p[1] = '.';
338  
        p[1] = '.';
339  
        impl_.split(id_user, 0);
339  
        impl_.split(id_user, 0);
340  
        impl_.split(id_pass, 0);
340  
        impl_.split(id_pass, 0);
341  
        impl_.split(id_host, 0);
341  
        impl_.split(id_host, 0);
342  
        impl_.split(id_port, 0);
342  
        impl_.split(id_port, 0);
343  
    }
343  
    }
344  
    else
344  
    else
345  
    {
345  
    {
346  
        resize_impl(
346  
        resize_impl(
347  
            id_user, id_path, 0, op);
347  
            id_user, id_path, 0, op);
348  
    }
348  
    }
349  
    impl_.host_type_ =
349  
    impl_.host_type_ =
350  
        urls::host_type::none;
350  
        urls::host_type::none;
351  
    return *this;
351  
    return *this;
352  
}
352  
}
353  

353  

354  
//------------------------------------------------
354  
//------------------------------------------------
355  
//
355  
//
356  
// Userinfo
356  
// Userinfo
357  
//
357  
//
358  
//------------------------------------------------
358  
//------------------------------------------------
359  

359  

360  
inline
360  
inline
361  
url_base&
361  
url_base&
362  
url_base::
362  
url_base::
363  
set_userinfo(
363  
set_userinfo(
364  
    core::string_view s)
364  
    core::string_view s)
365  
{
365  
{
366  
    op_t op(*this, &s);
366  
    op_t op(*this, &s);
367  
    encoding_opts opt;
367  
    encoding_opts opt;
368  
    auto const n = encoded_size(
368  
    auto const n = encoded_size(
369  
        s, detail::userinfo_chars, opt);
369  
        s, detail::userinfo_chars, opt);
370  
    auto dest = set_userinfo_impl(n, op);
370  
    auto dest = set_userinfo_impl(n, op);
371  
    encode(
371  
    encode(
372  
        dest,
372  
        dest,
373  
        n,
373  
        n,
374  
        s,
374  
        s,
375  
        detail::userinfo_chars,
375  
        detail::userinfo_chars,
376  
        opt);
376  
        opt);
377  
    auto const pos = impl_.get(
377  
    auto const pos = impl_.get(
378  
        id_user, id_host
378  
        id_user, id_host
379  
            ).find_first_of(':');
379  
            ).find_first_of(':');
380  
    if(pos != core::string_view::npos)
380  
    if(pos != core::string_view::npos)
381  
    {
381  
    {
382  
        impl_.split(id_user, pos);
382  
        impl_.split(id_user, pos);
383  
        // find ':' in plain string
383  
        // find ':' in plain string
384  
        auto const pos2 =
384  
        auto const pos2 =
385  
            s.find_first_of(':');
385  
            s.find_first_of(':');
386  
        if(pos2 != core::string_view::npos)
386  
        if(pos2 != core::string_view::npos)
387  
        {
387  
        {
388  
            // pos2 is the ':' index in plain input (user[:pass])
388  
            // pos2 is the ':' index in plain input (user[:pass])
389  
            // decoded user is [0, pos2), decoded pass is (pos2, end].
389  
            // decoded user is [0, pos2), decoded pass is (pos2, end].
390  
            impl_.decoded_[id_user] =
390  
            impl_.decoded_[id_user] =
391  
                detail::to_size_type(pos2);
391  
                detail::to_size_type(pos2);
392  
            impl_.decoded_[id_pass] =
392  
            impl_.decoded_[id_pass] =
393  
                detail::to_size_type(s.size() - pos2 - 1);
393  
                detail::to_size_type(s.size() - pos2 - 1);
394  
        }
394  
        }
395  
        else
395  
        else
396  
        {
396  
        {
397  
            impl_.decoded_[id_user] =
397  
            impl_.decoded_[id_user] =
398  
                detail::to_size_type(s.size());
398  
                detail::to_size_type(s.size());
399  
            impl_.decoded_[id_pass] = 0;
399  
            impl_.decoded_[id_pass] = 0;
400  
        }
400  
        }
401  
    }
401  
    }
402  
    else
402  
    else
403  
    {
403  
    {
404  
        impl_.decoded_[id_user] =
404  
        impl_.decoded_[id_user] =
405  
            detail::to_size_type(s.size());
405  
            detail::to_size_type(s.size());
406  
        impl_.decoded_[id_pass] = 0;
406  
        impl_.decoded_[id_pass] = 0;
407  
    }
407  
    }
408  
    return *this;
408  
    return *this;
409  
}
409  
}
410  

410  

411  
inline
411  
inline
412  
url_base&
412  
url_base&
413  
url_base::
413  
url_base::
414  
set_encoded_userinfo(
414  
set_encoded_userinfo(
415  
    pct_string_view s)
415  
    pct_string_view s)
416  
{
416  
{
417  
    op_t op(*this, &detail::ref(s));
417  
    op_t op(*this, &detail::ref(s));
418  
    auto const pos = s.find_first_of(':');
418  
    auto const pos = s.find_first_of(':');
419  
    if(pos != core::string_view::npos)
419  
    if(pos != core::string_view::npos)
420  
    {
420  
    {
421  
        // user:pass
421  
        // user:pass
422  
        auto const s0 = s.substr(0, pos);
422  
        auto const s0 = s.substr(0, pos);
423  
        auto const s1 = s.substr(pos + 1);
423  
        auto const s1 = s.substr(pos + 1);
424  
        auto const n0 =
424  
        auto const n0 =
425  
            detail::re_encoded_size_unsafe(
425  
            detail::re_encoded_size_unsafe(
426  
                s0,
426  
                s0,
427  
                detail::user_chars);
427  
                detail::user_chars);
428  
        auto const n1 =
428  
        auto const n1 =
429  
            detail::re_encoded_size_unsafe(s1,
429  
            detail::re_encoded_size_unsafe(s1,
430  
                detail::password_chars);
430  
                detail::password_chars);
431  
        auto dest =
431  
        auto dest =
432  
            set_userinfo_impl(n0 + n1 + 1, op);
432  
            set_userinfo_impl(n0 + n1 + 1, op);
433  
        impl_.decoded_[id_user] =
433  
        impl_.decoded_[id_user] =
434  
            detail::to_size_type(detail::re_encode_unsafe(
434  
            detail::to_size_type(detail::re_encode_unsafe(
435  
                dest,
435  
                dest,
436  
                dest + n0,
436  
                dest + n0,
437  
                s0,
437  
                s0,
438  
                detail::user_chars));
438  
                detail::user_chars));
439  
        *dest++ = ':';
439  
        *dest++ = ':';
440  
        impl_.decoded_[id_pass] =
440  
        impl_.decoded_[id_pass] =
441  
            detail::to_size_type(detail::re_encode_unsafe(
441  
            detail::to_size_type(detail::re_encode_unsafe(
442  
                dest,
442  
                dest,
443  
                dest + n1,
443  
                dest + n1,
444  
                s1,
444  
                s1,
445  
                detail::password_chars));
445  
                detail::password_chars));
446  
        impl_.split(id_user, 2 + n0);
446  
        impl_.split(id_user, 2 + n0);
447  
    }
447  
    }
448  
    else
448  
    else
449  
    {
449  
    {
450  
        // user
450  
        // user
451  
        auto const n =
451  
        auto const n =
452  
            detail::re_encoded_size_unsafe(
452  
            detail::re_encoded_size_unsafe(
453  
                s, detail::user_chars);
453  
                s, detail::user_chars);
454  
        auto dest = set_userinfo_impl(n, op);
454  
        auto dest = set_userinfo_impl(n, op);
455  
        impl_.decoded_[id_user] =
455  
        impl_.decoded_[id_user] =
456  
            detail::to_size_type(detail::re_encode_unsafe(
456  
            detail::to_size_type(detail::re_encode_unsafe(
457  
                dest,
457  
                dest,
458  
                dest + n,
458  
                dest + n,
459  
                s,
459  
                s,
460  
                detail::user_chars));
460  
                detail::user_chars));
461  
        impl_.split(id_user, 2 + n);
461  
        impl_.split(id_user, 2 + n);
462  
        impl_.decoded_[id_pass] = 0;
462  
        impl_.decoded_[id_pass] = 0;
463  
    }
463  
    }
464  
    return *this;
464  
    return *this;
465  
}
465  
}
466  

466  

467  
inline
467  
inline
468  
url_base&
468  
url_base&
469  
url_base::
469  
url_base::
470  
remove_userinfo() noexcept
470  
remove_userinfo() noexcept
471  
{
471  
{
472  
    if(impl_.len(id_pass) == 0)
472  
    if(impl_.len(id_pass) == 0)
473  
        return *this; // no userinfo
473  
        return *this; // no userinfo
474  

474  

475  
    op_t op(*this);
475  
    op_t op(*this);
476  
    // keep authority '//'
476  
    // keep authority '//'
477  
    resize_impl(
477  
    resize_impl(
478  
        id_user, id_host, 2, op);
478  
        id_user, id_host, 2, op);
479  
    impl_.decoded_[id_user] = 0;
479  
    impl_.decoded_[id_user] = 0;
480  
    impl_.decoded_[id_pass] = 0;
480  
    impl_.decoded_[id_pass] = 0;
481  
    return *this;
481  
    return *this;
482  
}
482  
}
483  

483  

484  
//------------------------------------------------
484  
//------------------------------------------------
485  

485  

486  
inline
486  
inline
487  
url_base&
487  
url_base&
488  
url_base::
488  
url_base::
489  
set_user(core::string_view s)
489  
set_user(core::string_view s)
490  
{
490  
{
491  
    op_t op(*this, &s);
491  
    op_t op(*this, &s);
492  
    encoding_opts opt;
492  
    encoding_opts opt;
493  
    auto const n = encoded_size(
493  
    auto const n = encoded_size(
494  
        s, detail::user_chars, opt);
494  
        s, detail::user_chars, opt);
495  
    auto dest = set_user_impl(n, op);
495  
    auto dest = set_user_impl(n, op);
496  
    encode_unsafe(
496  
    encode_unsafe(
497  
        dest,
497  
        dest,
498  
        n,
498  
        n,
499  
        s,
499  
        s,
500  
        detail::user_chars,
500  
        detail::user_chars,
501  
        opt);
501  
        opt);
502  
    impl_.decoded_[id_user] =
502  
    impl_.decoded_[id_user] =
503  
        detail::to_size_type(s.size());
503  
        detail::to_size_type(s.size());
504  
    return *this;
504  
    return *this;
505  
}
505  
}
506  

506  

507  
inline
507  
inline
508  
url_base&
508  
url_base&
509  
url_base::
509  
url_base::
510  
set_encoded_user(
510  
set_encoded_user(
511  
    pct_string_view s)
511  
    pct_string_view s)
512  
{
512  
{
513  
    op_t op(*this, &detail::ref(s));
513  
    op_t op(*this, &detail::ref(s));
514  
    auto const n =
514  
    auto const n =
515  
        detail::re_encoded_size_unsafe(
515  
        detail::re_encoded_size_unsafe(
516  
            s, detail::user_chars);
516  
            s, detail::user_chars);
517  
    auto dest = set_user_impl(n, op);
517  
    auto dest = set_user_impl(n, op);
518  
    impl_.decoded_[id_user] =
518  
    impl_.decoded_[id_user] =
519  
        detail::to_size_type(detail::re_encode_unsafe(
519  
        detail::to_size_type(detail::re_encode_unsafe(
520  
            dest,
520  
            dest,
521  
            dest + n,
521  
            dest + n,
522  
            s,
522  
            s,
523  
            detail::user_chars));
523  
            detail::user_chars));
524  
    BOOST_ASSERT(
524  
    BOOST_ASSERT(
525  
        impl_.decoded_[id_user] ==
525  
        impl_.decoded_[id_user] ==
526  
            s.decoded_size());
526  
            s.decoded_size());
527  
    return *this;
527  
    return *this;
528  
}
528  
}
529  

529  

530  
//------------------------------------------------
530  
//------------------------------------------------
531  

531  

532  
inline
532  
inline
533  
url_base&
533  
url_base&
534  
url_base::
534  
url_base::
535  
set_password(core::string_view s)
535  
set_password(core::string_view s)
536  
{
536  
{
537  
    op_t op(*this, &s);
537  
    op_t op(*this, &s);
538  
    encoding_opts opt;
538  
    encoding_opts opt;
539  
    auto const n = encoded_size(
539  
    auto const n = encoded_size(
540  
        s, detail::password_chars, opt);
540  
        s, detail::password_chars, opt);
541  
    auto dest = set_password_impl(n, op);
541  
    auto dest = set_password_impl(n, op);
542  
    encode_unsafe(
542  
    encode_unsafe(
543  
        dest,
543  
        dest,
544  
        n,
544  
        n,
545  
        s,
545  
        s,
546  
        detail::password_chars,
546  
        detail::password_chars,
547  
        opt);
547  
        opt);
548  
    impl_.decoded_[id_pass] =
548  
    impl_.decoded_[id_pass] =
549  
        detail::to_size_type(s.size());
549  
        detail::to_size_type(s.size());
550  
    return *this;
550  
    return *this;
551  
}
551  
}
552  

552  

553  
inline
553  
inline
554  
url_base&
554  
url_base&
555  
url_base::
555  
url_base::
556  
set_encoded_password(
556  
set_encoded_password(
557  
    pct_string_view s)
557  
    pct_string_view s)
558  
{
558  
{
559  
    op_t op(*this, &detail::ref(s));
559  
    op_t op(*this, &detail::ref(s));
560  
    auto const n =
560  
    auto const n =
561  
        detail::re_encoded_size_unsafe(
561  
        detail::re_encoded_size_unsafe(
562  
            s,
562  
            s,
563  
            detail::password_chars);
563  
            detail::password_chars);
564  
    auto dest = set_password_impl(n, op);
564  
    auto dest = set_password_impl(n, op);
565  
    impl_.decoded_[id_pass] =
565  
    impl_.decoded_[id_pass] =
566  
        detail::to_size_type(detail::re_encode_unsafe(
566  
        detail::to_size_type(detail::re_encode_unsafe(
567  
            dest,
567  
            dest,
568  
            dest + n,
568  
            dest + n,
569  
            s,
569  
            s,
570  
            detail::password_chars));
570  
            detail::password_chars));
571  
    BOOST_ASSERT(
571  
    BOOST_ASSERT(
572  
        impl_.decoded_[id_pass] ==
572  
        impl_.decoded_[id_pass] ==
573  
            s.decoded_size());
573  
            s.decoded_size());
574  
    return *this;
574  
    return *this;
575  
}
575  
}
576  

576  

577  
inline
577  
inline
578  
url_base&
578  
url_base&
579  
url_base::
579  
url_base::
580  
remove_password() noexcept
580  
remove_password() noexcept
581  
{
581  
{
582  
    auto const n = impl_.len(id_pass);
582  
    auto const n = impl_.len(id_pass);
583  
    if(n < 2)
583  
    if(n < 2)
584  
        return *this; // no password
584  
        return *this; // no password
585  

585  

586  
    op_t op(*this);
586  
    op_t op(*this);
587  
    // clear password, retain '@'
587  
    // clear password, retain '@'
588  
    auto dest =
588  
    auto dest =
589  
        resize_impl(id_pass, 1, op);
589  
        resize_impl(id_pass, 1, op);
590  
    dest[0] = '@';
590  
    dest[0] = '@';
591  
    impl_.decoded_[id_pass] = 0;
591  
    impl_.decoded_[id_pass] = 0;
592  
    return *this;
592  
    return *this;
593  
}
593  
}
594  

594  

595  
//------------------------------------------------
595  
//------------------------------------------------
596  
//
596  
//
597  
// Host
597  
// Host
598  
//
598  
//
599  
//------------------------------------------------
599  
//------------------------------------------------
600  
/*
600  
/*
601  
host_type       host_type()                 // ipv4, ipv6, ipvfuture, name
601  
host_type       host_type()                 // ipv4, ipv6, ipvfuture, name
602  

602  

603  
std::string     host()                      // return encoded_host().decode()
603  
std::string     host()                      // return encoded_host().decode()
604  
pct_string_view encoded_host()              // return host part, as-is
604  
pct_string_view encoded_host()              // return host part, as-is
605  
std::string     host_address()              // return encoded_host_address().decode()
605  
std::string     host_address()              // return encoded_host_address().decode()
606  
pct_string_view encoded_host_address()      // ipv4, ipv6, ipvfut, or encoded name, no brackets
606  
pct_string_view encoded_host_address()      // ipv4, ipv6, ipvfut, or encoded name, no brackets
607  

607  

608  
ipv4_address    host_ipv4_address()         // return ipv4_address or {}
608  
ipv4_address    host_ipv4_address()         // return ipv4_address or {}
609  
ipv6_address    host_ipv6_address()         // return ipv6_address or {}
609  
ipv6_address    host_ipv6_address()         // return ipv6_address or {}
610  
core::string_view     host_ipvfuture()            // return ipvfuture or {}
610  
core::string_view     host_ipvfuture()            // return ipvfuture or {}
611  
std::string     host_name()                 // return decoded name or ""
611  
std::string     host_name()                 // return decoded name or ""
612  
pct_string_view encoded_host_name()         // return encoded host name or ""
612  
pct_string_view encoded_host_name()         // return encoded host name or ""
613  

613  

614  
--------------------------------------------------
614  
--------------------------------------------------
615  

615  

616  
set_host( core::string_view )                     // set host part from plain text
616  
set_host( core::string_view )                     // set host part from plain text
617  
set_encoded_host( pct_string_view )         // set host part from encoded text
617  
set_encoded_host( pct_string_view )         // set host part from encoded text
618  
set_host_address( core::string_view )             // set host from ipv4, ipv6, ipvfut, or plain reg-name string
618  
set_host_address( core::string_view )             // set host from ipv4, ipv6, ipvfut, or plain reg-name string
619  
set_encoded_host_address( pct_string_view ) // set host from ipv4, ipv6, ipvfut, or encoded reg-name string
619  
set_encoded_host_address( pct_string_view ) // set host from ipv4, ipv6, ipvfut, or encoded reg-name string
620  

620  

621  
set_host_ipv4( ipv4_address )               // set ipv4
621  
set_host_ipv4( ipv4_address )               // set ipv4
622  
set_host_ipv6( ipv6_address )               // set ipv6
622  
set_host_ipv6( ipv6_address )               // set ipv6
623  
set_host_ipvfuture( core::string_view )           // set ipvfuture
623  
set_host_ipvfuture( core::string_view )           // set ipvfuture
624  
set_host_name( core::string_view )                // set name from plain
624  
set_host_name( core::string_view )                // set name from plain
625  
set_encoded_host_name( pct_string_view )    // set name from encoded
625  
set_encoded_host_name( pct_string_view )    // set name from encoded
626  
*/
626  
*/
627  

627  

628  
// set host part from plain text
628  
// set host part from plain text
629  
inline
629  
inline
630  
url_base&
630  
url_base&
631  
url_base::
631  
url_base::
632  
set_host(
632  
set_host(
633  
    core::string_view s)
633  
    core::string_view s)
634  
{
634  
{
635  
    if( s.size() > 2 &&
635  
    if( s.size() > 2 &&
636  
        s.front() == '[' &&
636  
        s.front() == '[' &&
637  
        s.back() == ']')
637  
        s.back() == ']')
638  
    {
638  
    {
639  
        // IP-literal
639  
        // IP-literal
640  
        if (s[1] != 'v')
640  
        if (s[1] != 'v')
641  
        {
641  
        {
642  
            // IPv6-address
642  
            // IPv6-address
643  
            auto innersv = s.substr(1, s.size() - 2);
643  
            auto innersv = s.substr(1, s.size() - 2);
644  
            auto innerit = innersv.begin();
644  
            auto innerit = innersv.begin();
645  
            auto endit = innersv.end();
645  
            auto endit = innersv.end();
646  
            auto rv = grammar::parse(
646  
            auto rv = grammar::parse(
647  
                innerit,
647  
                innerit,
648  
                endit,
648  
                endit,
649  
                ipv6_address_rule);
649  
                ipv6_address_rule);
650  
            if(rv)
650  
            if(rv)
651  
            {
651  
            {
652  
                if (innerit == endit)
652  
                if (innerit == endit)
653  
                {
653  
                {
654  
                    set_host_ipv6_and_encoded_zone_id(*rv, {});
654  
                    set_host_ipv6_and_encoded_zone_id(*rv, {});
655  
                    return *this;
655  
                    return *this;
656  
                }
656  
                }
657  
                // IPv6addrz: https://datatracker.ietf.org/doc/html/rfc6874
657  
                // IPv6addrz: https://datatracker.ietf.org/doc/html/rfc6874
658  
                auto chars_left = endit - innerit;
658  
                auto chars_left = endit - innerit;
659  
                if (chars_left >= 2 &&
659  
                if (chars_left >= 2 &&
660  
                    *innerit++ == '%')
660  
                    *innerit++ == '%')
661  
                {
661  
                {
662  
                    core::string_view zone_id_str = {&*innerit, std::size_t(chars_left - 1)};
662  
                    core::string_view zone_id_str = {&*innerit, std::size_t(chars_left - 1)};
663  
                    set_host_ipv6_and_zone_id(*rv, zone_id_str);
663  
                    set_host_ipv6_and_zone_id(*rv, zone_id_str);
664  
                    return *this;
664  
                    return *this;
665  
                }
665  
                }
666  
            }
666  
            }
667  
        }
667  
        }
668  
        else
668  
        else
669  
        {
669  
        {
670  
            // IPvFuture
670  
            // IPvFuture
671  
            auto rv = grammar::parse(
671  
            auto rv = grammar::parse(
672  
                s.substr(1, s.size() - 2),
672  
                s.substr(1, s.size() - 2),
673  
                detail::ipvfuture_rule);
673  
                detail::ipvfuture_rule);
674  
            if(rv)
674  
            if(rv)
675  
                return set_host_ipvfuture(rv->str);
675  
                return set_host_ipvfuture(rv->str);
676  
        }
676  
        }
677  
    }
677  
    }
678  
    else if(s.size() >= 7) // "0.0.0.0"
678  
    else if(s.size() >= 7) // "0.0.0.0"
679  
    {
679  
    {
680  
        // IPv4-address
680  
        // IPv4-address
681  
        auto rv = parse_ipv4_address(s);
681  
        auto rv = parse_ipv4_address(s);
682  
        if(rv)
682  
        if(rv)
683  
            return set_host_ipv4(*rv);
683  
            return set_host_ipv4(*rv);
684  
    }
684  
    }
685  

685  

686  
    // reg-name
686  
    // reg-name
687  
    op_t op(*this, &s);
687  
    op_t op(*this, &s);
688  
    encoding_opts opt;
688  
    encoding_opts opt;
689  
    auto const n = encoded_size(
689  
    auto const n = encoded_size(
690  
        s, detail::host_chars, opt);
690  
        s, detail::host_chars, opt);
691  
    auto dest = set_host_impl(n, op);
691  
    auto dest = set_host_impl(n, op);
692  
    encode(
692  
    encode(
693  
        dest,
693  
        dest,
694  
        impl_.get(id_path).data() - dest,
694  
        impl_.get(id_path).data() - dest,
695  
        s,
695  
        s,
696  
        detail::host_chars,
696  
        detail::host_chars,
697  
        opt);
697  
        opt);
698  
    impl_.decoded_[id_host] =
698  
    impl_.decoded_[id_host] =
699  
        detail::to_size_type(s.size());
699  
        detail::to_size_type(s.size());
700  
    impl_.host_type_ =
700  
    impl_.host_type_ =
701  
        urls::host_type::name;
701  
        urls::host_type::name;
702  
    return *this;
702  
    return *this;
703  
}
703  
}
704  

704  

705  
// set host part from encoded text
705  
// set host part from encoded text
706  
inline
706  
inline
707  
url_base&
707  
url_base&
708  
url_base::
708  
url_base::
709  
set_encoded_host(
709  
set_encoded_host(
710  
    pct_string_view s)
710  
    pct_string_view s)
711  
{
711  
{
712  
    if( s.size() > 2 &&
712  
    if( s.size() > 2 &&
713  
        s.front() == '[' &&
713  
        s.front() == '[' &&
714  
        s.back() == ']')
714  
        s.back() == ']')
715  
    {
715  
    {
716  
        // IP-literal
716  
        // IP-literal
717  
        if (s[1] != 'v')
717  
        if (s[1] != 'v')
718  
        {
718  
        {
719  
            // IPv6-address
719  
            // IPv6-address
720  
            auto innersv = s.substr(1, s.size() - 2);
720  
            auto innersv = s.substr(1, s.size() - 2);
721  
            auto innerit = innersv.begin();
721  
            auto innerit = innersv.begin();
722  
            auto endit = innersv.end();
722  
            auto endit = innersv.end();
723  
            auto rv = grammar::parse(
723  
            auto rv = grammar::parse(
724  
                innerit,
724  
                innerit,
725  
                endit,
725  
                endit,
726  
                ipv6_address_rule);
726  
                ipv6_address_rule);
727  
            if(rv)
727  
            if(rv)
728  
            {
728  
            {
729  
                if (innerit == endit)
729  
                if (innerit == endit)
730  
                {
730  
                {
731  
                    set_host_ipv6_and_encoded_zone_id(*rv, {});
731  
                    set_host_ipv6_and_encoded_zone_id(*rv, {});
732  
                    return *this;
732  
                    return *this;
733  
                }
733  
                }
734  
                // IPv6addrz: https://datatracker.ietf.org/doc/html/rfc6874
734  
                // IPv6addrz: https://datatracker.ietf.org/doc/html/rfc6874
735  
                auto chars_left = endit - innerit;
735  
                auto chars_left = endit - innerit;
736  
                if (chars_left >= 3 &&
736  
                if (chars_left >= 3 &&
737  
                    *innerit++ == '%' &&
737  
                    *innerit++ == '%' &&
738  
                    *innerit++ == '2' &&
738  
                    *innerit++ == '2' &&
739  
                    *innerit++ == '5')
739  
                    *innerit++ == '5')
740  
                {
740  
                {
741  
                    auto const nz = std::size_t(chars_left - 3);
741  
                    auto const nz = std::size_t(chars_left - 3);
742  
                    core::string_view zone_id_str = {&*innerit, std::size_t(chars_left - 3)};
742  
                    core::string_view zone_id_str = {&*innerit, std::size_t(chars_left - 3)};
743  
                    std::size_t dnz = detail::decode_bytes_unsafe(zone_id_str);
743  
                    std::size_t dnz = detail::decode_bytes_unsafe(zone_id_str);
744  
                    pct_string_view zone_id_pct = make_pct_string_view_unsafe(innerit, nz, dnz);
744  
                    pct_string_view zone_id_pct = make_pct_string_view_unsafe(innerit, nz, dnz);
745  
                    set_host_ipv6_and_encoded_zone_id(*rv, zone_id_pct);
745  
                    set_host_ipv6_and_encoded_zone_id(*rv, zone_id_pct);
746  
                    return *this;
746  
                    return *this;
747  
                }
747  
                }
748  
            }
748  
            }
749  
        }
749  
        }
750  
        else
750  
        else
751  
        {
751  
        {
752  
            // IPvFuture
752  
            // IPvFuture
753  
            auto rv = grammar::parse(
753  
            auto rv = grammar::parse(
754  
                s.substr(1, s.size() - 2),
754  
                s.substr(1, s.size() - 2),
755  
                    detail::ipvfuture_rule);
755  
                    detail::ipvfuture_rule);
756  
            if(rv)
756  
            if(rv)
757  
                return set_host_ipvfuture(rv->str);
757  
                return set_host_ipvfuture(rv->str);
758  
        }
758  
        }
759  
    }
759  
    }
760  
    else if(s.size() >= 7) // "0.0.0.0"
760  
    else if(s.size() >= 7) // "0.0.0.0"
761  
    {
761  
    {
762  
        // IPv4-address
762  
        // IPv4-address
763  
        auto rv = parse_ipv4_address(s);
763  
        auto rv = parse_ipv4_address(s);
764  
        if(rv)
764  
        if(rv)
765  
            return set_host_ipv4(*rv);
765  
            return set_host_ipv4(*rv);
766  
    }
766  
    }
767  

767  

768  
    // reg-name
768  
    // reg-name
769  
    op_t op(*this, &detail::ref(s));
769  
    op_t op(*this, &detail::ref(s));
770  
    auto const n = detail::re_encoded_size_unsafe(
770  
    auto const n = detail::re_encoded_size_unsafe(
771  
        s, detail::host_chars);
771  
        s, detail::host_chars);
772  
    auto dest = set_host_impl(n, op);
772  
    auto dest = set_host_impl(n, op);
773  
    impl_.decoded_[id_host] =
773  
    impl_.decoded_[id_host] =
774  
        detail::to_size_type(detail::re_encode_unsafe(
774  
        detail::to_size_type(detail::re_encode_unsafe(
775  
            dest,
775  
            dest,
776  
            impl_.get(id_path).data(),
776  
            impl_.get(id_path).data(),
777  
            s,
777  
            s,
778  
            detail::host_chars));
778  
            detail::host_chars));
779  
    BOOST_ASSERT(impl_.decoded_[id_host] ==
779  
    BOOST_ASSERT(impl_.decoded_[id_host] ==
780  
        s.decoded_size());
780  
        s.decoded_size());
781  
    impl_.host_type_ =
781  
    impl_.host_type_ =
782  
        urls::host_type::name;
782  
        urls::host_type::name;
783  
    return *this;
783  
    return *this;
784  
}
784  
}
785  

785  

786  
inline
786  
inline
787  
url_base&
787  
url_base&
788  
url_base::
788  
url_base::
789  
set_host_address(
789  
set_host_address(
790  
    core::string_view s)
790  
    core::string_view s)
791  
{
791  
{
792  
    if (!s.empty())
792  
    if (!s.empty())
793  
    {
793  
    {
794  
        // IP-literal
794  
        // IP-literal
795  
        if (s[0] != 'v')
795  
        if (s[0] != 'v')
796  
        {
796  
        {
797  
            // IPv6-address
797  
            // IPv6-address
798  
            auto innerit = s.begin();
798  
            auto innerit = s.begin();
799  
            auto endit = s.end();
799  
            auto endit = s.end();
800  
            auto rv = grammar::parse(
800  
            auto rv = grammar::parse(
801  
                innerit,
801  
                innerit,
802  
                endit,
802  
                endit,
803  
                ipv6_address_rule);
803  
                ipv6_address_rule);
804  
            if(rv)
804  
            if(rv)
805  
            {
805  
            {
806  
                if (innerit == endit)
806  
                if (innerit == endit)
807  
                {
807  
                {
808  
                    set_host_ipv6_and_encoded_zone_id(*rv, {});
808  
                    set_host_ipv6_and_encoded_zone_id(*rv, {});
809  
                    return *this;
809  
                    return *this;
810  
                }
810  
                }
811  
                // IPv6addrz: https://datatracker.ietf.org/doc/html/rfc6874
811  
                // IPv6addrz: https://datatracker.ietf.org/doc/html/rfc6874
812  
                auto chars_left = endit - innerit;
812  
                auto chars_left = endit - innerit;
813  
                if (chars_left >= 2 &&
813  
                if (chars_left >= 2 &&
814  
                    *innerit++ == '%')
814  
                    *innerit++ == '%')
815  
                {
815  
                {
816  
                    core::string_view zone_id_str = {&*innerit, std::size_t(chars_left - 1)};
816  
                    core::string_view zone_id_str = {&*innerit, std::size_t(chars_left - 1)};
817  
                    set_host_ipv6_and_zone_id(*rv, zone_id_str);
817  
                    set_host_ipv6_and_zone_id(*rv, zone_id_str);
818  
                    return *this;
818  
                    return *this;
819  
                }
819  
                }
820  
            }
820  
            }
821  
        }
821  
        }
822  

822  

823  
        // IPvFuture
823  
        // IPvFuture
824  
        auto rv = grammar::parse(s, detail::ipvfuture_rule);
824  
        auto rv = grammar::parse(s, detail::ipvfuture_rule);
825  
        if(rv)
825  
        if(rv)
826  
            return set_host_ipvfuture(rv->str);
826  
            return set_host_ipvfuture(rv->str);
827  

827  

828  
        if(s.size() >= 7) // "0.0.0.0"
828  
        if(s.size() >= 7) // "0.0.0.0"
829  
        {
829  
        {
830  
            // IPv4-address
830  
            // IPv4-address
831  
            auto rv2 = parse_ipv4_address(s);
831  
            auto rv2 = parse_ipv4_address(s);
832  
            if(rv2)
832  
            if(rv2)
833  
                return set_host_ipv4(*rv2);
833  
                return set_host_ipv4(*rv2);
834  
        }
834  
        }
835  
    }
835  
    }
836  

836  

837  
    // reg-name
837  
    // reg-name
838  
    op_t op(*this, &s);
838  
    op_t op(*this, &s);
839  
    encoding_opts opt;
839  
    encoding_opts opt;
840  
    auto const n = encoded_size(
840  
    auto const n = encoded_size(
841  
        s, detail::host_chars, opt);
841  
        s, detail::host_chars, opt);
842  
    auto dest = set_host_impl(n, op);
842  
    auto dest = set_host_impl(n, op);
843  
    encode(
843  
    encode(
844  
        dest,
844  
        dest,
845  
        impl_.get(id_path).data() - dest,
845  
        impl_.get(id_path).data() - dest,
846  
        s,
846  
        s,
847  
        detail::host_chars,
847  
        detail::host_chars,
848  
        opt);
848  
        opt);
849  
    impl_.decoded_[id_host] =
849  
    impl_.decoded_[id_host] =
850  
        detail::to_size_type(s.size());
850  
        detail::to_size_type(s.size());
851  
    impl_.host_type_ =
851  
    impl_.host_type_ =
852  
        urls::host_type::name;
852  
        urls::host_type::name;
853  
    return *this;
853  
    return *this;
854  
}
854  
}
855  

855  

856  
inline
856  
inline
857  
url_base&
857  
url_base&
858  
url_base::
858  
url_base::
859  
set_encoded_host_address(
859  
set_encoded_host_address(
860  
    pct_string_view s)
860  
    pct_string_view s)
861  
{
861  
{
862  
    if( !s.empty() )
862  
    if( !s.empty() )
863  
    {
863  
    {
864  
        // IP-literal
864  
        // IP-literal
865  
        if (s[0] != 'v')
865  
        if (s[0] != 'v')
866  
        {
866  
        {
867  
            // IPv6-address
867  
            // IPv6-address
868  
            auto innerit = s.begin();
868  
            auto innerit = s.begin();
869  
            auto endit = s.end();
869  
            auto endit = s.end();
870  
            auto rv = grammar::parse(
870  
            auto rv = grammar::parse(
871  
                innerit,
871  
                innerit,
872  
                endit,
872  
                endit,
873  
                ipv6_address_rule);
873  
                ipv6_address_rule);
874  
            if(rv)
874  
            if(rv)
875  
            {
875  
            {
876  
                if (innerit == endit)
876  
                if (innerit == endit)
877  
                {
877  
                {
878  
                    set_host_ipv6_and_encoded_zone_id(*rv, {});
878  
                    set_host_ipv6_and_encoded_zone_id(*rv, {});
879  
                    return *this;
879  
                    return *this;
880  
                }
880  
                }
881  
                // IPv6addrz: https://datatracker.ietf.org/doc/html/rfc6874
881  
                // IPv6addrz: https://datatracker.ietf.org/doc/html/rfc6874
882  
                auto chars_left = endit - innerit;
882  
                auto chars_left = endit - innerit;
883  
                if (chars_left >= 3 &&
883  
                if (chars_left >= 3 &&
884  
                    *innerit++ == '%' &&
884  
                    *innerit++ == '%' &&
885  
                    *innerit++ == '2' &&
885  
                    *innerit++ == '2' &&
886  
                    *innerit++ == '5')
886  
                    *innerit++ == '5')
887  
                {
887  
                {
888  
                    auto const nz = std::size_t(chars_left - 3);
888  
                    auto const nz = std::size_t(chars_left - 3);
889  
                    core::string_view zone_id_str = {&*innerit, std::size_t(chars_left - 3)};
889  
                    core::string_view zone_id_str = {&*innerit, std::size_t(chars_left - 3)};
890  
                    std::size_t dnz = detail::decode_bytes_unsafe(zone_id_str);
890  
                    std::size_t dnz = detail::decode_bytes_unsafe(zone_id_str);
891  
                    pct_string_view zone_id_pct = make_pct_string_view_unsafe(innerit, nz, dnz);
891  
                    pct_string_view zone_id_pct = make_pct_string_view_unsafe(innerit, nz, dnz);
892  
                    set_host_ipv6_and_encoded_zone_id(*rv, zone_id_pct);
892  
                    set_host_ipv6_and_encoded_zone_id(*rv, zone_id_pct);
893  
                    return *this;
893  
                    return *this;
894  
                }
894  
                }
895  
            }
895  
            }
896  

896  

897  
            if(s.size() >= 7) // "0.0.0.0"
897  
            if(s.size() >= 7) // "0.0.0.0"
898  
            {
898  
            {
899  
                // IPv4-address
899  
                // IPv4-address
900  
                auto rv2 = parse_ipv4_address(s);
900  
                auto rv2 = parse_ipv4_address(s);
901  
                if(rv2)
901  
                if(rv2)
902  
                   return set_host_ipv4(*rv2);
902  
                   return set_host_ipv4(*rv2);
903  
            }
903  
            }
904  
        }
904  
        }
905  
        else
905  
        else
906  
        {
906  
        {
907  
            // IPvFuture
907  
            // IPvFuture
908  
            auto rv = grammar::parse(
908  
            auto rv = grammar::parse(
909  
                s, detail::ipvfuture_rule);
909  
                s, detail::ipvfuture_rule);
910  
            if(rv)
910  
            if(rv)
911  
                return set_host_ipvfuture(rv->str);
911  
                return set_host_ipvfuture(rv->str);
912  
        }
912  
        }
913  
    }
913  
    }
914  

914  

915  
    // reg-name
915  
    // reg-name
916  
    op_t op(*this, &detail::ref(s));
916  
    op_t op(*this, &detail::ref(s));
917  
    auto const n = detail::re_encoded_size_unsafe(
917  
    auto const n = detail::re_encoded_size_unsafe(
918  
        s, detail::host_chars);
918  
        s, detail::host_chars);
919  
    auto dest = set_host_impl(n, op);
919  
    auto dest = set_host_impl(n, op);
920  
    impl_.decoded_[id_host] =
920  
    impl_.decoded_[id_host] =
921  
        detail::to_size_type(detail::re_encode_unsafe(
921  
        detail::to_size_type(detail::re_encode_unsafe(
922  
            dest,
922  
            dest,
923  
            impl_.get(id_path).data(),
923  
            impl_.get(id_path).data(),
924  
            s,
924  
            s,
925  
            detail::host_chars));
925  
            detail::host_chars));
926  
    BOOST_ASSERT(impl_.decoded_[id_host] ==
926  
    BOOST_ASSERT(impl_.decoded_[id_host] ==
927  
        s.decoded_size());
927  
        s.decoded_size());
928  
    impl_.host_type_ =
928  
    impl_.host_type_ =
929  
        urls::host_type::name;
929  
        urls::host_type::name;
930  
    return *this;
930  
    return *this;
931  
}
931  
}
932  

932  

933  
inline
933  
inline
934  
url_base&
934  
url_base&
935  
url_base::
935  
url_base::
936  
set_host_ipv4(
936  
set_host_ipv4(
937  
    ipv4_address const& addr)
937  
    ipv4_address const& addr)
938  
{
938  
{
939  
    op_t op(*this);
939  
    op_t op(*this);
940  
    char buf[urls::ipv4_address::max_str_len];
940  
    char buf[urls::ipv4_address::max_str_len];
941  
    auto s = addr.to_buffer(buf, sizeof(buf));
941  
    auto s = addr.to_buffer(buf, sizeof(buf));
942  
    auto dest = set_host_impl(s.size(), op);
942  
    auto dest = set_host_impl(s.size(), op);
943  
    std::memcpy(dest, s.data(), s.size());
943  
    std::memcpy(dest, s.data(), s.size());
944  
    impl_.decoded_[id_host] =
944  
    impl_.decoded_[id_host] =
945  
        detail::to_size_type(impl_.len(id_host));
945  
        detail::to_size_type(impl_.len(id_host));
946  
    impl_.host_type_ = urls::host_type::ipv4;
946  
    impl_.host_type_ = urls::host_type::ipv4;
947  
    auto bytes = addr.to_bytes();
947  
    auto bytes = addr.to_bytes();
948  
    std::memcpy(
948  
    std::memcpy(
949  
        impl_.ip_addr_,
949  
        impl_.ip_addr_,
950  
        bytes.data(),
950  
        bytes.data(),
951  
        bytes.size());
951  
        bytes.size());
952  
    return *this;
952  
    return *this;
953  
}
953  
}
954  

954  

955  
inline
955  
inline
956  
url_base&
956  
url_base&
957  
url_base::
957  
url_base::
958  
set_host_ipv6(
958  
set_host_ipv6(
959  
    ipv6_address const& addr)
959  
    ipv6_address const& addr)
960  
{
960  
{
961  
    set_host_ipv6_and_encoded_zone_id(addr, encoded_zone_id());
961  
    set_host_ipv6_and_encoded_zone_id(addr, encoded_zone_id());
962  
    return *this;
962  
    return *this;
963  
}
963  
}
964  

964  

965  
inline
965  
inline
966  
url_base&
966  
url_base&
967  
url_base::
967  
url_base::
968  
set_zone_id(core::string_view s)
968  
set_zone_id(core::string_view s)
969  
{
969  
{
970  
    set_host_ipv6_and_zone_id(host_ipv6_address(), s);
970  
    set_host_ipv6_and_zone_id(host_ipv6_address(), s);
971  
    return *this;
971  
    return *this;
972  
}
972  
}
973  

973  

974  
inline
974  
inline
975  
url_base&
975  
url_base&
976  
url_base::
976  
url_base::
977  
set_encoded_zone_id(pct_string_view s)
977  
set_encoded_zone_id(pct_string_view s)
978  
{
978  
{
979  
    set_host_ipv6_and_encoded_zone_id(host_ipv6_address(), s);
979  
    set_host_ipv6_and_encoded_zone_id(host_ipv6_address(), s);
980  
    return *this;
980  
    return *this;
981  
}
981  
}
982  

982  

983  
inline
983  
inline
984  
void
984  
void
985  
url_base::
985  
url_base::
986  
set_host_ipv6_and_zone_id(
986  
set_host_ipv6_and_zone_id(
987  
    ipv6_address const& addr,
987  
    ipv6_address const& addr,
988  
    core::string_view zone_id)
988  
    core::string_view zone_id)
989  
{
989  
{
990  
    op_t op(*this, &zone_id);
990  
    op_t op(*this, &zone_id);
991  
    char ipv6_str_buf[urls::ipv6_address::max_str_len];
991  
    char ipv6_str_buf[urls::ipv6_address::max_str_len];
992  
    auto ipv6_str = addr.to_buffer(ipv6_str_buf, sizeof(ipv6_str_buf));
992  
    auto ipv6_str = addr.to_buffer(ipv6_str_buf, sizeof(ipv6_str_buf));
993  
    bool const has_zone_id = !zone_id.empty();
993  
    bool const has_zone_id = !zone_id.empty();
994  
    encoding_opts opt;
994  
    encoding_opts opt;
995  
    auto const ipn = ipv6_str.size();
995  
    auto const ipn = ipv6_str.size();
996  
    auto const zn = encoded_size(zone_id, unreserved_chars, opt);
996  
    auto const zn = encoded_size(zone_id, unreserved_chars, opt);
997  
    auto const n = ipn + 2 + has_zone_id * (3 + zn);
997  
    auto const n = ipn + 2 + has_zone_id * (3 + zn);
998  
    auto dest = set_host_impl(n, op);
998  
    auto dest = set_host_impl(n, op);
999  
    *dest++ = '[';
999  
    *dest++ = '[';
1000  
    std::memcpy(dest, ipv6_str.data(), ipn);
1000  
    std::memcpy(dest, ipv6_str.data(), ipn);
1001  
    dest += ipn;
1001  
    dest += ipn;
1002  
    if (has_zone_id)
1002  
    if (has_zone_id)
1003  
    {
1003  
    {
1004  
        *dest++ = '%';
1004  
        *dest++ = '%';
1005  
        *dest++ = '2';
1005  
        *dest++ = '2';
1006  
        *dest++ = '5';
1006  
        *dest++ = '5';
1007  
        encode(dest, zn, zone_id, unreserved_chars, opt);
1007  
        encode(dest, zn, zone_id, unreserved_chars, opt);
1008  
        dest += zn;
1008  
        dest += zn;
1009  
    }
1009  
    }
1010  
    *dest++ = ']';
1010  
    *dest++ = ']';
1011  
    // ipn + |"["| + |"]"| + (has_zone_id ? |"%"| + zn : 0)
1011  
    // ipn + |"["| + |"]"| + (has_zone_id ? |"%"| + zn : 0)
1012  
    impl_.decoded_[id_host] = detail::to_size_type(
1012  
    impl_.decoded_[id_host] = detail::to_size_type(
1013  
        ipn + 2 + has_zone_id * (1 + zone_id.size()));
1013  
        ipn + 2 + has_zone_id * (1 + zone_id.size()));
1014  
    impl_.host_type_ = urls::host_type::ipv6;
1014  
    impl_.host_type_ = urls::host_type::ipv6;
1015  
    auto bytes = addr.to_bytes();
1015  
    auto bytes = addr.to_bytes();
1016  
    std::memcpy(
1016  
    std::memcpy(
1017  
        impl_.ip_addr_,
1017  
        impl_.ip_addr_,
1018  
        bytes.data(),
1018  
        bytes.data(),
1019  
        bytes.size());
1019  
        bytes.size());
1020  
}
1020  
}
1021  

1021  

1022  
inline
1022  
inline
1023  
void
1023  
void
1024  
url_base::
1024  
url_base::
1025  
set_host_ipv6_and_encoded_zone_id(
1025  
set_host_ipv6_and_encoded_zone_id(
1026  
    ipv6_address const& addr,
1026  
    ipv6_address const& addr,
1027  
    pct_string_view zone_id)
1027  
    pct_string_view zone_id)
1028  
{
1028  
{
1029  
    op_t op(*this, &detail::ref(zone_id));
1029  
    op_t op(*this, &detail::ref(zone_id));
1030  
    char ipv6_str_buf[urls::ipv6_address::max_str_len];
1030  
    char ipv6_str_buf[urls::ipv6_address::max_str_len];
1031  
    auto ipv6_str = addr.to_buffer(ipv6_str_buf, sizeof(ipv6_str_buf));
1031  
    auto ipv6_str = addr.to_buffer(ipv6_str_buf, sizeof(ipv6_str_buf));
1032  
    bool const has_zone_id = !zone_id.empty();
1032  
    bool const has_zone_id = !zone_id.empty();
1033  
    auto const ipn = ipv6_str.size();
1033  
    auto const ipn = ipv6_str.size();
1034  
    auto const zn = detail::re_encoded_size_unsafe(zone_id, unreserved_chars);
1034  
    auto const zn = detail::re_encoded_size_unsafe(zone_id, unreserved_chars);
1035  
    auto const n = ipn + 2 + has_zone_id * (3 + zn);
1035  
    auto const n = ipn + 2 + has_zone_id * (3 + zn);
1036  
    auto dest = set_host_impl(n, op);
1036  
    auto dest = set_host_impl(n, op);
1037  
    *dest++ = '[';
1037  
    *dest++ = '[';
1038  
    std::memcpy(dest, ipv6_str.data(), ipn);
1038  
    std::memcpy(dest, ipv6_str.data(), ipn);
1039  
    dest += ipn;
1039  
    dest += ipn;
1040  
    std::size_t dzn = 0;
1040  
    std::size_t dzn = 0;
1041  
    if (has_zone_id)
1041  
    if (has_zone_id)
1042  
    {
1042  
    {
1043  
        *dest++ = '%';
1043  
        *dest++ = '%';
1044  
        *dest++ = '2';
1044  
        *dest++ = '2';
1045  
        *dest++ = '5';
1045  
        *dest++ = '5';
1046  
        dzn = detail::re_encode_unsafe(dest, dest + zn, zone_id, unreserved_chars);
1046  
        dzn = detail::re_encode_unsafe(dest, dest + zn, zone_id, unreserved_chars);
1047  
    }
1047  
    }
1048  
    *dest++ = ']';
1048  
    *dest++ = ']';
1049  
    // ipn + |"["| + |"]"| + (has_zone_id ? |"%"| + zn : 0)
1049  
    // ipn + |"["| + |"]"| + (has_zone_id ? |"%"| + zn : 0)
1050  
    impl_.decoded_[id_host] = detail::to_size_type(
1050  
    impl_.decoded_[id_host] = detail::to_size_type(
1051  
        ipn + 2 + has_zone_id * (1 + dzn));
1051  
        ipn + 2 + has_zone_id * (1 + dzn));
1052  
    impl_.host_type_ = urls::host_type::ipv6;
1052  
    impl_.host_type_ = urls::host_type::ipv6;
1053  
    auto bytes = addr.to_bytes();
1053  
    auto bytes = addr.to_bytes();
1054  
    std::memcpy(
1054  
    std::memcpy(
1055  
        impl_.ip_addr_,
1055  
        impl_.ip_addr_,
1056  
        bytes.data(),
1056  
        bytes.data(),
1057  
        bytes.size());
1057  
        bytes.size());
1058  
}
1058  
}
1059  

1059  

1060  
inline
1060  
inline
1061  
url_base&
1061  
url_base&
1062  
url_base::
1062  
url_base::
1063  
set_host_ipvfuture(
1063  
set_host_ipvfuture(
1064  
    core::string_view s)
1064  
    core::string_view s)
1065  
{
1065  
{
1066  
    op_t op(*this, &s);
1066  
    op_t op(*this, &s);
1067  
    // validate
1067  
    // validate
1068  
    grammar::parse(s,
1068  
    grammar::parse(s,
1069  
        detail::ipvfuture_rule
1069  
        detail::ipvfuture_rule
1070  
            ).value(BOOST_URL_POS);
1070  
            ).value(BOOST_URL_POS);
1071  
    auto dest = set_host_impl(
1071  
    auto dest = set_host_impl(
1072  
        s.size() + 2, op);
1072  
        s.size() + 2, op);
1073  
    *dest++ = '[';
1073  
    *dest++ = '[';
1074  
    dest += s.copy(dest, s.size());
1074  
    dest += s.copy(dest, s.size());
1075  
    *dest = ']';
1075  
    *dest = ']';
1076  
    impl_.host_type_ =
1076  
    impl_.host_type_ =
1077  
        urls::host_type::ipvfuture;
1077  
        urls::host_type::ipvfuture;
1078  
    impl_.decoded_[id_host] =
1078  
    impl_.decoded_[id_host] =
1079  
        detail::to_size_type(s.size() + 2);
1079  
        detail::to_size_type(s.size() + 2);
1080  
    return *this;
1080  
    return *this;
1081  
}
1081  
}
1082  

1082  

1083  
inline
1083  
inline
1084  
url_base&
1084  
url_base&
1085  
url_base::
1085  
url_base::
1086  
set_host_name(
1086  
set_host_name(
1087  
    core::string_view s)
1087  
    core::string_view s)
1088  
{
1088  
{
1089  
    bool is_ipv4 = false;
1089  
    bool is_ipv4 = false;
1090  
    if(s.size() >= 7) // "0.0.0.0"
1090  
    if(s.size() >= 7) // "0.0.0.0"
1091  
    {
1091  
    {
1092  
        // IPv4-address
1092  
        // IPv4-address
1093  
        if(parse_ipv4_address(s).has_value())
1093  
        if(parse_ipv4_address(s).has_value())
1094  
            is_ipv4 = true;
1094  
            is_ipv4 = true;
1095  
    }
1095  
    }
1096  
    auto allowed = detail::host_chars;
1096  
    auto allowed = detail::host_chars;
1097  
    if(is_ipv4)
1097  
    if(is_ipv4)
1098  
        allowed = allowed - '.';
1098  
        allowed = allowed - '.';
1099  

1099  

1100  
    op_t op(*this, &s);
1100  
    op_t op(*this, &s);
1101  
    encoding_opts opt;
1101  
    encoding_opts opt;
1102  
    auto const n = encoded_size(
1102  
    auto const n = encoded_size(
1103  
        s, allowed, opt);
1103  
        s, allowed, opt);
1104  
    auto dest = set_host_impl(n, op);
1104  
    auto dest = set_host_impl(n, op);
1105  
    encode_unsafe(
1105  
    encode_unsafe(
1106  
        dest,
1106  
        dest,
1107  
        n,
1107  
        n,
1108  
        s,
1108  
        s,
1109  
        allowed,
1109  
        allowed,
1110  
        opt);
1110  
        opt);
1111  
    impl_.host_type_ =
1111  
    impl_.host_type_ =
1112  
        urls::host_type::name;
1112  
        urls::host_type::name;
1113  
    impl_.decoded_[id_host] =
1113  
    impl_.decoded_[id_host] =
1114  
        detail::to_size_type(s.size());
1114  
        detail::to_size_type(s.size());
1115  
    return *this;
1115  
    return *this;
1116  
}
1116  
}
1117  

1117  

1118  
inline
1118  
inline
1119  
url_base&
1119  
url_base&
1120  
url_base::
1120  
url_base::
1121  
set_encoded_host_name(
1121  
set_encoded_host_name(
1122  
    pct_string_view s)
1122  
    pct_string_view s)
1123  
{
1123  
{
1124  
    bool is_ipv4 = false;
1124  
    bool is_ipv4 = false;
1125  
    if(s.size() >= 7) // "0.0.0.0"
1125  
    if(s.size() >= 7) // "0.0.0.0"
1126  
    {
1126  
    {
1127  
        // IPv4-address
1127  
        // IPv4-address
1128  
        if(parse_ipv4_address(s).has_value())
1128  
        if(parse_ipv4_address(s).has_value())
1129  
            is_ipv4 = true;
1129  
            is_ipv4 = true;
1130  
    }
1130  
    }
1131  
    auto allowed = detail::host_chars;
1131  
    auto allowed = detail::host_chars;
1132  
    if(is_ipv4)
1132  
    if(is_ipv4)
1133  
        allowed = allowed - '.';
1133  
        allowed = allowed - '.';
1134  

1134  

1135  
    op_t op(*this, &detail::ref(s));
1135  
    op_t op(*this, &detail::ref(s));
1136  
    auto const n = detail::re_encoded_size_unsafe(
1136  
    auto const n = detail::re_encoded_size_unsafe(
1137  
        s, allowed);
1137  
        s, allowed);
1138  
    auto dest = set_host_impl(n, op);
1138  
    auto dest = set_host_impl(n, op);
1139  
    impl_.decoded_[id_host] =
1139  
    impl_.decoded_[id_host] =
1140  
        detail::to_size_type(detail::re_encode_unsafe(
1140  
        detail::to_size_type(detail::re_encode_unsafe(
1141  
            dest,
1141  
            dest,
1142  
            dest + n,
1142  
            dest + n,
1143  
            s,
1143  
            s,
1144  
            allowed));
1144  
            allowed));
1145  
    BOOST_ASSERT(
1145  
    BOOST_ASSERT(
1146  
        impl_.decoded_[id_host] ==
1146  
        impl_.decoded_[id_host] ==
1147  
            s.decoded_size());
1147  
            s.decoded_size());
1148  
    impl_.host_type_ =
1148  
    impl_.host_type_ =
1149  
        urls::host_type::name;
1149  
        urls::host_type::name;
1150  
    return *this;
1150  
    return *this;
1151  
}
1151  
}
1152  

1152  

1153  
//------------------------------------------------
1153  
//------------------------------------------------
1154  

1154  

1155  
inline
1155  
inline
1156  
url_base&
1156  
url_base&
1157  
url_base::
1157  
url_base::
1158  
set_port_number(
1158  
set_port_number(
1159  
    std::uint16_t n)
1159  
    std::uint16_t n)
1160  
{
1160  
{
1161  
    op_t op(*this);
1161  
    op_t op(*this);
1162  
    auto s =
1162  
    auto s =
1163  
        detail::make_printed(n);
1163  
        detail::make_printed(n);
1164  
    auto dest = set_port_impl(
1164  
    auto dest = set_port_impl(
1165  
        s.string().size(), op);
1165  
        s.string().size(), op);
1166  
    std::memcpy(
1166  
    std::memcpy(
1167  
        dest, s.string().data(),
1167  
        dest, s.string().data(),
1168  
            s.string().size());
1168  
            s.string().size());
1169  
    impl_.port_number_ = n;
1169  
    impl_.port_number_ = n;
1170  
    return *this;
1170  
    return *this;
1171  
}
1171  
}
1172  

1172  

1173  
inline
1173  
inline
1174  
url_base&
1174  
url_base&
1175  
url_base::
1175  
url_base::
1176  
set_port(
1176  
set_port(
1177  
    core::string_view s)
1177  
    core::string_view s)
1178  
{
1178  
{
1179  
    op_t op(*this, &s);
1179  
    op_t op(*this, &s);
1180  
    auto t = grammar::parse(s,
1180  
    auto t = grammar::parse(s,
1181  
        detail::port_rule{}
1181  
        detail::port_rule{}
1182  
            ).value(BOOST_URL_POS);
1182  
            ).value(BOOST_URL_POS);
1183  
    auto dest =
1183  
    auto dest =
1184  
        set_port_impl(t.str.size(), op);
1184  
        set_port_impl(t.str.size(), op);
1185  
    std::memcpy(dest,
1185  
    std::memcpy(dest,
1186  
        t.str.data(), t.str.size());
1186  
        t.str.data(), t.str.size());
1187  
    if(t.has_number)
1187  
    if(t.has_number)
1188  
        impl_.port_number_ = t.number;
1188  
        impl_.port_number_ = t.number;
1189  
    else
1189  
    else
1190  
        impl_.port_number_ = 0;
1190  
        impl_.port_number_ = 0;
1191  
    return *this;
1191  
    return *this;
1192  
}
1192  
}
1193  

1193  

1194  
inline
1194  
inline
1195  
url_base&
1195  
url_base&
1196  
url_base::
1196  
url_base::
1197  
remove_port() noexcept
1197  
remove_port() noexcept
1198  
{
1198  
{
1199  
    op_t op(*this);
1199  
    op_t op(*this);
1200  
    resize_impl(id_port, 0, op);
1200  
    resize_impl(id_port, 0, op);
1201  
    impl_.port_number_ = 0;
1201  
    impl_.port_number_ = 0;
1202  
    return *this;
1202  
    return *this;
1203  
}
1203  
}
1204  

1204  

1205  
//------------------------------------------------
1205  
//------------------------------------------------
1206  
//
1206  
//
1207  
// Compound Fields
1207  
// Compound Fields
1208  
//
1208  
//
1209  
//------------------------------------------------
1209  
//------------------------------------------------
1210  

1210  

1211  
inline
1211  
inline
1212  
url_base&
1212  
url_base&
1213  
url_base::
1213  
url_base::
1214  
remove_origin()
1214  
remove_origin()
1215  
{
1215  
{
1216  
    // these two calls perform 2 memmoves instead of 1
1216  
    // these two calls perform 2 memmoves instead of 1
1217  
    remove_authority();
1217  
    remove_authority();
1218  
    remove_scheme();
1218  
    remove_scheme();
1219  
    return *this;
1219  
    return *this;
1220  
}
1220  
}
1221  

1221  

1222  
//------------------------------------------------
1222  
//------------------------------------------------
1223  
//
1223  
//
1224  
// Path
1224  
// Path
1225  
//
1225  
//
1226  
//------------------------------------------------
1226  
//------------------------------------------------
1227  

1227  

1228  
inline
1228  
inline
1229  
bool
1229  
bool
1230  
url_base::
1230  
url_base::
1231  
set_path_absolute(
1231  
set_path_absolute(
1232  
    bool absolute)
1232  
    bool absolute)
1233  
{
1233  
{
1234  
    op_t op(*this);
1234  
    op_t op(*this);
1235  

1235  

1236  
    // check if path empty
1236  
    // check if path empty
1237  
    if(impl_.len(id_path) == 0)
1237  
    if(impl_.len(id_path) == 0)
1238  
    {
1238  
    {
1239  
        if(! absolute)
1239  
        if(! absolute)
1240  
        {
1240  
        {
1241  
            // already not absolute
1241  
            // already not absolute
1242  
            return true;
1242  
            return true;
1243  
        }
1243  
        }
1244  

1244  

1245  
        // add '/'
1245  
        // add '/'
1246  
        auto dest = resize_impl(
1246  
        auto dest = resize_impl(
1247  
            id_path, 1, op);
1247  
            id_path, 1, op);
1248  
        *dest = '/';
1248  
        *dest = '/';
1249  
        ++impl_.decoded_[id_path];
1249  
        ++impl_.decoded_[id_path];
1250  
        return true;
1250  
        return true;
1251  
    }
1251  
    }
1252  

1252  

1253  
    // check if path absolute
1253  
    // check if path absolute
1254  
    if(s_[impl_.offset(id_path)] == '/')
1254  
    if(s_[impl_.offset(id_path)] == '/')
1255  
    {
1255  
    {
1256  
        if(absolute)
1256  
        if(absolute)
1257  
        {
1257  
        {
1258  
            // already absolute
1258  
            // already absolute
1259  
            return true;
1259  
            return true;
1260  
        }
1260  
        }
1261  

1261  

1262  
        if( has_authority() &&
1262  
        if( has_authority() &&
1263  
            impl_.len(id_path) > 1)
1263  
            impl_.len(id_path) > 1)
1264  
        {
1264  
        {
1265  
            // can't do it, paths are always
1265  
            // can't do it, paths are always
1266  
            // absolute when authority present!
1266  
            // absolute when authority present!
1267  
            return false;
1267  
            return false;
1268  
        }
1268  
        }
1269  

1269  

1270  
        auto p = encoded_path();
1270  
        auto p = encoded_path();
1271  
        auto pos = p.find_first_of(":/", 1);
1271  
        auto pos = p.find_first_of(":/", 1);
1272  
        if (pos != core::string_view::npos &&
1272  
        if (pos != core::string_view::npos &&
1273  
            p[pos] == ':')
1273  
            p[pos] == ':')
1274  
        {
1274  
        {
1275  
            // prepend with .
1275  
            // prepend with .
1276  
            auto n = impl_.len(id_path);
1276  
            auto n = impl_.len(id_path);
1277  
            resize_impl(id_path, n + 1, op);
1277  
            resize_impl(id_path, n + 1, op);
1278  
            std::memmove(
1278  
            std::memmove(
1279  
                s_ + impl_.offset(id_path) + 1,
1279  
                s_ + impl_.offset(id_path) + 1,
1280  
                s_ + impl_.offset(id_path), n);
1280  
                s_ + impl_.offset(id_path), n);
1281  
            *(s_ + impl_.offset(id_path)) = '.';
1281  
            *(s_ + impl_.offset(id_path)) = '.';
1282  
            ++impl_.decoded_[id_path];
1282  
            ++impl_.decoded_[id_path];
1283  
            return true;
1283  
            return true;
1284  
        }
1284  
        }
1285  

1285  

1286  
        // remove '/'
1286  
        // remove '/'
1287  
        auto n = impl_.len(id_port);
1287  
        auto n = impl_.len(id_port);
1288  
        impl_.split(id_port, n + 1);
1288  
        impl_.split(id_port, n + 1);
1289  
        resize_impl(id_port, n, op);
1289  
        resize_impl(id_port, n, op);
1290  
        --impl_.decoded_[id_path];
1290  
        --impl_.decoded_[id_path];
1291  
        return true;
1291  
        return true;
1292  
    }
1292  
    }
1293  

1293  

1294  
    if(! absolute)
1294  
    if(! absolute)
1295  
    {
1295  
    {
1296  
        // already not absolute
1296  
        // already not absolute
1297  
        return true;
1297  
        return true;
1298  
    }
1298  
    }
1299  

1299  

1300  
    // add '/'
1300  
    // add '/'
1301  
    auto n = impl_.len(id_port);
1301  
    auto n = impl_.len(id_port);
1302  
    auto dest = resize_impl(
1302  
    auto dest = resize_impl(
1303  
        id_port, n + 1, op) + n;
1303  
        id_port, n + 1, op) + n;
1304  
    impl_.split(id_port, n);
1304  
    impl_.split(id_port, n);
1305  
    *dest = '/';
1305  
    *dest = '/';
1306  
    ++impl_.decoded_[id_path];
1306  
    ++impl_.decoded_[id_path];
1307  
    return true;
1307  
    return true;
1308  
}
1308  
}
1309  

1309  

1310  
inline
1310  
inline
1311  
url_base&
1311  
url_base&
1312  
url_base::
1312  
url_base::
1313  
set_path(
1313  
set_path(
1314  
    core::string_view s)
1314  
    core::string_view s)
1315  
{
1315  
{
1316  
    op_t op(*this, &s);
1316  
    op_t op(*this, &s);
1317  
    encoding_opts opt;
1317  
    encoding_opts opt;
1318  

1318  

1319  
//------------------------------------------------
1319  
//------------------------------------------------
1320  
//
1320  
//
1321  
//  Calculate encoded size
1321  
//  Calculate encoded size
1322  
//
1322  
//
1323  
// - "/"s are not encoded
1323  
// - "/"s are not encoded
1324  
// - "%2F"s are not encoded
1324  
// - "%2F"s are not encoded
1325  
//
1325  
//
1326  
// - reserved path chars are re-encoded
1326  
// - reserved path chars are re-encoded
1327  
// - colons in first segment might need to be re-encoded
1327  
// - colons in first segment might need to be re-encoded
1328  
// - the path might need to receive a prefix
1328  
// - the path might need to receive a prefix
1329  
    auto const n = encoded_size(
1329  
    auto const n = encoded_size(
1330  
        s, detail::path_chars, opt);
1330  
        s, detail::path_chars, opt);
1331  
    std::size_t n_reencode_colons = 0;
1331  
    std::size_t n_reencode_colons = 0;
1332  
    core::string_view first_seg;
1332  
    core::string_view first_seg;
1333  
    if (!has_scheme() &&
1333  
    if (!has_scheme() &&
1334  
        !has_authority() &&
1334  
        !has_authority() &&
1335  
        !s.starts_with('/'))
1335  
        !s.starts_with('/'))
1336  
    {
1336  
    {
1337  
        // the first segment with unencoded colons would look
1337  
        // the first segment with unencoded colons would look
1338  
        // like the scheme
1338  
        // like the scheme
1339  
        first_seg = detail::to_sv(s);
1339  
        first_seg = detail::to_sv(s);
1340  
        std::size_t p = s.find('/');
1340  
        std::size_t p = s.find('/');
1341  
        if (p != core::string_view::npos)
1341  
        if (p != core::string_view::npos)
1342  
            first_seg = s.substr(0, p);
1342  
            first_seg = s.substr(0, p);
1343  
        n_reencode_colons = std::count(
1343  
        n_reencode_colons = std::count(
1344  
            first_seg.begin(), first_seg.end(), ':');
1344  
            first_seg.begin(), first_seg.end(), ':');
1345  
    }
1345  
    }
1346  
    // the authority can only be followed by an empty or relative path
1346  
    // the authority can only be followed by an empty or relative path
1347  
    // if we have an authority and the path is a non-empty relative path, we
1347  
    // if we have an authority and the path is a non-empty relative path, we
1348  
    // add the "/" prefix to make it valid.
1348  
    // add the "/" prefix to make it valid.
1349  
    bool make_absolute =
1349  
    bool make_absolute =
1350  
        has_authority() &&
1350  
        has_authority() &&
1351  
        !s.starts_with('/') &&
1351  
        !s.starts_with('/') &&
1352  
        !s.empty();
1352  
        !s.empty();
1353  
    // a path starting with "//" might look like the authority.
1353  
    // a path starting with "//" might look like the authority.
1354  
    // we add a "/." prefix to prevent that
1354  
    // we add a "/." prefix to prevent that
1355  
    bool add_dot_segment =
1355  
    bool add_dot_segment =
1356  
        !make_absolute &&
1356  
        !make_absolute &&
1357  
        s.starts_with("//");
1357  
        s.starts_with("//");
1358  

1358  

1359  
//------------------------------------------------
1359  
//------------------------------------------------
1360  
//
1360  
//
1361  
//  Re-encode data
1361  
//  Re-encode data
1362  
//
1362  
//
1363  
    auto dest = set_path_impl(
1363  
    auto dest = set_path_impl(
1364  
        n + make_absolute + 2 * n_reencode_colons + 2 * add_dot_segment, op);
1364  
        n + make_absolute + 2 * n_reencode_colons + 2 * add_dot_segment, op);
1365  
    impl_.decoded_[id_path] = 0;
1365  
    impl_.decoded_[id_path] = 0;
1366  
    if (!dest)
1366  
    if (!dest)
1367  
    {
1367  
    {
1368  
        impl_.nseg_ = 0;
1368  
        impl_.nseg_ = 0;
1369  
        return *this;
1369  
        return *this;
1370  
    }
1370  
    }
1371  
    if (make_absolute)
1371  
    if (make_absolute)
1372  
    {
1372  
    {
1373  
        *dest++ = '/';
1373  
        *dest++ = '/';
1374  
        impl_.decoded_[id_path] += 1;
1374  
        impl_.decoded_[id_path] += 1;
1375  
    }
1375  
    }
1376  
    else if (add_dot_segment)
1376  
    else if (add_dot_segment)
1377  
    {
1377  
    {
1378  
        *dest++ = '/';
1378  
        *dest++ = '/';
1379  
        *dest++ = '.';
1379  
        *dest++ = '.';
1380  
        impl_.decoded_[id_path] += 2;
1380  
        impl_.decoded_[id_path] += 2;
1381  
    }
1381  
    }
1382  
    dest += encode_unsafe(
1382  
    dest += encode_unsafe(
1383  
        dest,
1383  
        dest,
1384  
        impl_.get(id_query).data() - dest,
1384  
        impl_.get(id_query).data() - dest,
1385  
        first_seg,
1385  
        first_seg,
1386  
        detail::segment_chars - ':',
1386  
        detail::segment_chars - ':',
1387  
        opt);
1387  
        opt);
1388  
    dest += encode_unsafe(
1388  
    dest += encode_unsafe(
1389  
        dest,
1389  
        dest,
1390  
        impl_.get(id_query).data() - dest,
1390  
        impl_.get(id_query).data() - dest,
1391  
        s.substr(first_seg.size()),
1391  
        s.substr(first_seg.size()),
1392  
        detail::path_chars,
1392  
        detail::path_chars,
1393  
        opt);
1393  
        opt);
1394  
    impl_.decoded_[id_path] +=
1394  
    impl_.decoded_[id_path] +=
1395  
        detail::to_size_type(s.size());
1395  
        detail::to_size_type(s.size());
1396  
    BOOST_ASSERT(!dest || dest == impl_.get(id_query).data());
1396  
    BOOST_ASSERT(!dest || dest == impl_.get(id_query).data());
1397  
    BOOST_ASSERT(
1397  
    BOOST_ASSERT(
1398  
        impl_.decoded_[id_path] ==
1398  
        impl_.decoded_[id_path] ==
1399  
        s.size() + make_absolute + 2 * add_dot_segment);
1399  
        s.size() + make_absolute + 2 * add_dot_segment);
1400  

1400  

1401  
//------------------------------------------------
1401  
//------------------------------------------------
1402  
//
1402  
//
1403  
//  Update path parameters
1403  
//  Update path parameters
1404  
//
1404  
//
1405  
    // get the encoded_path with the replacements we applied
1405  
    // get the encoded_path with the replacements we applied
1406  
    if (s == "/")
1406  
    if (s == "/")
1407  
    {
1407  
    {
1408  
        // "/" maps to sequence {}
1408  
        // "/" maps to sequence {}
1409  
        impl_.nseg_ = 0;
1409  
        impl_.nseg_ = 0;
1410  
    }
1410  
    }
1411  
    else if (!s.empty())
1411  
    else if (!s.empty())
1412  
    {
1412  
    {
1413  
        if (s.starts_with("/./"))
1413  
        if (s.starts_with("/./"))
1414  
            s = s.substr(2);
1414  
            s = s.substr(2);
1415  
        // count segments as number of '/'s + 1
1415  
        // count segments as number of '/'s + 1
1416  
        impl_.nseg_ = detail::to_size_type(
1416  
        impl_.nseg_ = detail::to_size_type(
1417  
            std::count(
1417  
            std::count(
1418  
                s.begin() + 1, s.end(), '/') + 1);
1418  
                s.begin() + 1, s.end(), '/') + 1);
1419  
    }
1419  
    }
1420  
    else
1420  
    else
1421  
    {
1421  
    {
1422  
        // an empty relative path maps to sequence {}
1422  
        // an empty relative path maps to sequence {}
1423  
        impl_.nseg_ = 0;
1423  
        impl_.nseg_ = 0;
1424  
    }
1424  
    }
1425  

1425  

1426  
    check_invariants();
1426  
    check_invariants();
1427  
    return *this;
1427  
    return *this;
1428  
}
1428  
}
1429  

1429  

1430  
inline
1430  
inline
1431  
url_base&
1431  
url_base&
1432  
url_base::
1432  
url_base::
1433  
set_encoded_path(
1433  
set_encoded_path(
1434  
    pct_string_view s)
1434  
    pct_string_view s)
1435  
{
1435  
{
1436  
    op_t op(*this, &detail::ref(s));
1436  
    op_t op(*this, &detail::ref(s));
1437  

1437  

1438  
//------------------------------------------------
1438  
//------------------------------------------------
1439  
//
1439  
//
1440  
//  Calculate re-encoded output size
1440  
//  Calculate re-encoded output size
1441  
//
1441  
//
1442  
// - reserved path chars are re-encoded
1442  
// - reserved path chars are re-encoded
1443  
// - colons in first segment might need to be re-encoded
1443  
// - colons in first segment might need to be re-encoded
1444  
// - the path might need to receive a prefix
1444  
// - the path might need to receive a prefix
1445  
    auto const n = detail::re_encoded_size_unsafe(
1445  
    auto const n = detail::re_encoded_size_unsafe(
1446  
        s, detail::path_chars);
1446  
        s, detail::path_chars);
1447  
    std::size_t n_reencode_colons = 0;
1447  
    std::size_t n_reencode_colons = 0;
1448  
    core::string_view first_seg;
1448  
    core::string_view first_seg;
1449  
    if (!has_scheme() &&
1449  
    if (!has_scheme() &&
1450  
        !has_authority() &&
1450  
        !has_authority() &&
1451  
        !s.starts_with('/'))
1451  
        !s.starts_with('/'))
1452  
    {
1452  
    {
1453  
        // the first segment with unencoded colons would look
1453  
        // the first segment with unencoded colons would look
1454  
        // like the scheme
1454  
        // like the scheme
1455  
        first_seg = detail::to_sv(s);
1455  
        first_seg = detail::to_sv(s);
1456  
        std::size_t p = s.find('/');
1456  
        std::size_t p = s.find('/');
1457  
        if (p != core::string_view::npos)
1457  
        if (p != core::string_view::npos)
1458  
            first_seg = s.substr(0, p);
1458  
            first_seg = s.substr(0, p);
1459  
        n_reencode_colons = std::count(
1459  
        n_reencode_colons = std::count(
1460  
            first_seg.begin(), first_seg.end(), ':');
1460  
            first_seg.begin(), first_seg.end(), ':');
1461  
    }
1461  
    }
1462  
    // the authority can only be followed by an empty or relative path
1462  
    // the authority can only be followed by an empty or relative path
1463  
    // if we have an authority and the path is a non-empty relative path, we
1463  
    // if we have an authority and the path is a non-empty relative path, we
1464  
    // add the "/" prefix to make it valid.
1464  
    // add the "/" prefix to make it valid.
1465  
    bool make_absolute =
1465  
    bool make_absolute =
1466  
        has_authority() &&
1466  
        has_authority() &&
1467  
        !s.starts_with('/') &&
1467  
        !s.starts_with('/') &&
1468  
        !s.empty();
1468  
        !s.empty();
1469  
    // a path starting with "//" might look like the authority
1469  
    // a path starting with "//" might look like the authority
1470  
    // we add a "/." prefix to prevent that
1470  
    // we add a "/." prefix to prevent that
1471  
    bool add_dot_segment =
1471  
    bool add_dot_segment =
1472  
        !make_absolute &&
1472  
        !make_absolute &&
1473  
        !has_authority() &&
1473  
        !has_authority() &&
1474  
        s.starts_with("//");
1474  
        s.starts_with("//");
1475  

1475  

1476  
//------------------------------------------------
1476  
//------------------------------------------------
1477  
//
1477  
//
1478  
//  Re-encode data
1478  
//  Re-encode data
1479  
//
1479  
//
1480  
    auto dest = set_path_impl(
1480  
    auto dest = set_path_impl(
1481  
        n + make_absolute + 2 * n_reencode_colons + 2 * add_dot_segment, op);
1481  
        n + make_absolute + 2 * n_reencode_colons + 2 * add_dot_segment, op);
1482  
    impl_.decoded_[id_path] = 0;
1482  
    impl_.decoded_[id_path] = 0;
1483  
    if (!dest)
1483  
    if (!dest)
1484  
    {
1484  
    {
1485  
        impl_.nseg_ = 0;
1485  
        impl_.nseg_ = 0;
1486  
        return *this;
1486  
        return *this;
1487  
    }
1487  
    }
1488  
    if (make_absolute)
1488  
    if (make_absolute)
1489  
    {
1489  
    {
1490  
        *dest++ = '/';
1490  
        *dest++ = '/';
1491  
        impl_.decoded_[id_path] += 1;
1491  
        impl_.decoded_[id_path] += 1;
1492  
    }
1492  
    }
1493  
    else if (add_dot_segment)
1493  
    else if (add_dot_segment)
1494  
    {
1494  
    {
1495  
        *dest++ = '/';
1495  
        *dest++ = '/';
1496  
        *dest++ = '.';
1496  
        *dest++ = '.';
1497  
        impl_.decoded_[id_path] += 2;
1497  
        impl_.decoded_[id_path] += 2;
1498  
    }
1498  
    }
1499  
    impl_.decoded_[id_path] +=
1499  
    impl_.decoded_[id_path] +=
1500  
        detail::to_size_type(detail::re_encode_unsafe(
1500  
        detail::to_size_type(detail::re_encode_unsafe(
1501  
            dest,
1501  
            dest,
1502  
            impl_.get(id_query).data(),
1502  
            impl_.get(id_query).data(),
1503  
            first_seg,
1503  
            first_seg,
1504  
            detail::segment_chars - ':'));
1504  
            detail::segment_chars - ':'));
1505  
    impl_.decoded_[id_path] +=
1505  
    impl_.decoded_[id_path] +=
1506  
        detail::to_size_type(detail::re_encode_unsafe(
1506  
        detail::to_size_type(detail::re_encode_unsafe(
1507  
            dest,
1507  
            dest,
1508  
            impl_.get(id_query).data(),
1508  
            impl_.get(id_query).data(),
1509  
            s.substr(first_seg.size()),
1509  
            s.substr(first_seg.size()),
1510  
            detail::path_chars));
1510  
            detail::path_chars));
1511  
    BOOST_ASSERT(dest == impl_.get(id_query).data());
1511  
    BOOST_ASSERT(dest == impl_.get(id_query).data());
1512  
    BOOST_ASSERT(
1512  
    BOOST_ASSERT(
1513  
        impl_.decoded_[id_path] ==
1513  
        impl_.decoded_[id_path] ==
1514  
        s.decoded_size() + make_absolute + 2 * add_dot_segment);
1514  
        s.decoded_size() + make_absolute + 2 * add_dot_segment);
1515  

1515  

1516  
//------------------------------------------------
1516  
//------------------------------------------------
1517  
//
1517  
//
1518  
//  Update path parameters
1518  
//  Update path parameters
1519  
//
1519  
//
1520  
    // get the encoded_path with the replacements we applied
1520  
    // get the encoded_path with the replacements we applied
1521  
    if (s == "/")
1521  
    if (s == "/")
1522  
    {
1522  
    {
1523  
        // "/" maps to sequence {}
1523  
        // "/" maps to sequence {}
1524  
        impl_.nseg_ = 0;
1524  
        impl_.nseg_ = 0;
1525  
    }
1525  
    }
1526  
    else if (!s.empty())
1526  
    else if (!s.empty())
1527  
    {
1527  
    {
1528  
        if (s.starts_with("/./"))
1528  
        if (s.starts_with("/./"))
1529  
            s = s.substr(2);
1529  
            s = s.substr(2);
1530  
        // count segments as number of '/'s + 1
1530  
        // count segments as number of '/'s + 1
1531  
        impl_.nseg_ = detail::to_size_type(
1531  
        impl_.nseg_ = detail::to_size_type(
1532  
            std::count(
1532  
            std::count(
1533  
                s.begin() + 1, s.end(), '/') + 1);
1533  
                s.begin() + 1, s.end(), '/') + 1);
1534  
    }
1534  
    }
1535  
    else
1535  
    else
1536  
    {
1536  
    {
1537  
        // an empty relative path maps to sequence {}
1537  
        // an empty relative path maps to sequence {}
1538  
        impl_.nseg_ = 0;
1538  
        impl_.nseg_ = 0;
1539  
    }
1539  
    }
1540  

1540  

1541  
    check_invariants();
1541  
    check_invariants();
1542  
    return *this;
1542  
    return *this;
1543  
}
1543  
}
1544  

1544  

1545  
inline
1545  
inline
1546  
segments_ref
1546  
segments_ref
1547  
url_base::
1547  
url_base::
1548  
segments() noexcept
1548  
segments() noexcept
1549  
{
1549  
{
1550  
    return {*this};
1550  
    return {*this};
1551  
}
1551  
}
1552  

1552  

1553  
inline
1553  
inline
1554  
segments_encoded_ref
1554  
segments_encoded_ref
1555  
url_base::
1555  
url_base::
1556  
encoded_segments() noexcept
1556  
encoded_segments() noexcept
1557  
{
1557  
{
1558  
    return {*this};
1558  
    return {*this};
1559  
}
1559  
}
1560  

1560  

1561  
//------------------------------------------------
1561  
//------------------------------------------------
1562  
//
1562  
//
1563  
// Query
1563  
// Query
1564  
//
1564  
//
1565  
//------------------------------------------------
1565  
//------------------------------------------------
1566  

1566  

1567  
inline
1567  
inline
1568  
url_base&
1568  
url_base&
1569  
url_base::
1569  
url_base::
1570  
set_query(
1570  
set_query(
1571  
    core::string_view s)
1571  
    core::string_view s)
1572  
{
1572  
{
1573  
    edit_params(
1573  
    edit_params(
1574  
        detail::params_iter_impl(impl_),
1574  
        detail::params_iter_impl(impl_),
1575  
        detail::params_iter_impl(impl_, 0),
1575  
        detail::params_iter_impl(impl_, 0),
1576  
        detail::query_string_iter(s, true));
1576  
        detail::query_string_iter(s, true));
1577  
    return *this;
1577  
    return *this;
1578  
}
1578  
}
1579  

1579  

1580  
inline
1580  
inline
1581  
url_base&
1581  
url_base&
1582  
url_base::
1582  
url_base::
1583  
set_encoded_query(
1583  
set_encoded_query(
1584  
    pct_string_view s)
1584  
    pct_string_view s)
1585  
{
1585  
{
1586  
    op_t op(*this);
1586  
    op_t op(*this);
1587  
    std::size_t n = 0;      // encoded size
1587  
    std::size_t n = 0;      // encoded size
1588  
    std::size_t nparam = 1; // param count
1588  
    std::size_t nparam = 1; // param count
1589  
    auto const end = s.end();
1589  
    auto const end = s.end();
1590  
    auto p = s.begin();
1590  
    auto p = s.begin();
1591  

1591  

1592  
    // measure
1592  
    // measure
1593  
    while(p != end)
1593  
    while(p != end)
1594  
    {
1594  
    {
1595  
        if(*p == '&')
1595  
        if(*p == '&')
1596  
        {
1596  
        {
1597  
            ++p;
1597  
            ++p;
1598  
            ++n;
1598  
            ++n;
1599  
            ++nparam;
1599  
            ++nparam;
1600  
        }
1600  
        }
1601  
        else if(*p != '%')
1601  
        else if(*p != '%')
1602  
        {
1602  
        {
1603  
            if(detail::query_chars(*p))
1603  
            if(detail::query_chars(*p))
1604  
                n += 1; // allowed
1604  
                n += 1; // allowed
1605  
            else
1605  
            else
1606  
                n += 3; // escaped
1606  
                n += 3; // escaped
1607  
            ++p;
1607  
            ++p;
1608  
        }
1608  
        }
1609  
        else
1609  
        else
1610  
        {
1610  
        {
1611  
            // escape
1611  
            // escape
1612  
            n += 3;
1612  
            n += 3;
1613  
            p += 3;
1613  
            p += 3;
1614  
        }
1614  
        }
1615  
    }
1615  
    }
1616  

1616  

1617  
    // resize
1617  
    // resize
1618  
    auto dest = resize_impl(
1618  
    auto dest = resize_impl(
1619  
        id_query, n + 1, op);
1619  
        id_query, n + 1, op);
1620  
    *dest++ = '?';
1620  
    *dest++ = '?';
1621  

1621  

1622  
    // encode
1622  
    // encode
1623  
    impl_.decoded_[id_query] =
1623  
    impl_.decoded_[id_query] =
1624  
        detail::to_size_type(detail::re_encode_unsafe(
1624  
        detail::to_size_type(detail::re_encode_unsafe(
1625  
            dest,
1625  
            dest,
1626  
            dest + n,
1626  
            dest + n,
1627  
            s,
1627  
            s,
1628  
            detail::query_chars));
1628  
            detail::query_chars));
1629  
    BOOST_ASSERT(
1629  
    BOOST_ASSERT(
1630  
        impl_.decoded_[id_query] ==
1630  
        impl_.decoded_[id_query] ==
1631  
            s.decoded_size());
1631  
            s.decoded_size());
1632  
    impl_.nparam_ =
1632  
    impl_.nparam_ =
1633  
        detail::to_size_type(nparam);
1633  
        detail::to_size_type(nparam);
1634  
    return *this;
1634  
    return *this;
1635  
}
1635  
}
1636  

1636  

1637  
inline
1637  
inline
1638  
params_ref
1638  
params_ref
1639  
url_base::
1639  
url_base::
1640  
params() noexcept
1640  
params() noexcept
1641  
{
1641  
{
1642  
    return params_ref(
1642  
    return params_ref(
1643  
        *this,
1643  
        *this,
1644  
        encoding_opts{
1644  
        encoding_opts{
1645  
            true, false, false});
1645  
            true, false, false});
1646  
}
1646  
}
1647  

1647  

1648  
inline
1648  
inline
1649  
params_ref
1649  
params_ref
1650  
url_base::
1650  
url_base::
1651  
params(encoding_opts opt) noexcept
1651  
params(encoding_opts opt) noexcept
1652  
{
1652  
{
1653  
    return {*this, opt};
1653  
    return {*this, opt};
1654  
}
1654  
}
1655  

1655  

1656  
inline
1656  
inline
1657  
params_encoded_ref
1657  
params_encoded_ref
1658  
url_base::
1658  
url_base::
1659  
encoded_params() noexcept
1659  
encoded_params() noexcept
1660  
{
1660  
{
1661  
    return {*this};
1661  
    return {*this};
1662  
}
1662  
}
1663  

1663  

1664  
inline
1664  
inline
1665  
url_base&
1665  
url_base&
1666  
url_base::
1666  
url_base::
1667  
set_params(
1667  
set_params(
1668  
    std::initializer_list<param_view> ps,
1668  
    std::initializer_list<param_view> ps,
1669  
    encoding_opts opts) noexcept
1669  
    encoding_opts opts) noexcept
1670  
{
1670  
{
1671  
    params(opts).assign(ps);
1671  
    params(opts).assign(ps);
1672  
    return *this;
1672  
    return *this;
1673  
}
1673  
}
1674  

1674  

1675  
inline
1675  
inline
1676  
url_base&
1676  
url_base&
1677  
url_base::
1677  
url_base::
1678  
set_encoded_params( std::initializer_list< param_pct_view > ps ) noexcept
1678  
set_encoded_params( std::initializer_list< param_pct_view > ps ) noexcept
1679  
{
1679  
{
1680  
    encoded_params().assign(ps);
1680  
    encoded_params().assign(ps);
1681  
    return *this;
1681  
    return *this;
1682  
}
1682  
}
1683  

1683  

1684  
inline
1684  
inline
1685  
url_base&
1685  
url_base&
1686  
url_base::
1686  
url_base::
1687  
remove_query() noexcept
1687  
remove_query() noexcept
1688  
{
1688  
{
1689  
    op_t op(*this);
1689  
    op_t op(*this);
1690  
    resize_impl(id_query, 0, op);
1690  
    resize_impl(id_query, 0, op);
1691  
    impl_.nparam_ = 0;
1691  
    impl_.nparam_ = 0;
1692  
    impl_.decoded_[id_query] = 0;
1692  
    impl_.decoded_[id_query] = 0;
1693  
    return *this;
1693  
    return *this;
1694  
}
1694  
}
1695  

1695  

1696  
//------------------------------------------------
1696  
//------------------------------------------------
1697  
//
1697  
//
1698  
// Fragment
1698  
// Fragment
1699  
//
1699  
//
1700  
//------------------------------------------------
1700  
//------------------------------------------------
1701  

1701  

1702  
inline
1702  
inline
1703  
url_base&
1703  
url_base&
1704  
url_base::
1704  
url_base::
1705  
remove_fragment() noexcept
1705  
remove_fragment() noexcept
1706  
{
1706  
{
1707  
    op_t op(*this);
1707  
    op_t op(*this);
1708  
    resize_impl(id_frag, 0, op);
1708  
    resize_impl(id_frag, 0, op);
1709  
    impl_.decoded_[id_frag] = 0;
1709  
    impl_.decoded_[id_frag] = 0;
1710  
    return *this;
1710  
    return *this;
1711  
}
1711  
}
1712  

1712  

1713  
inline
1713  
inline
1714  
url_base&
1714  
url_base&
1715  
url_base::
1715  
url_base::
1716  
set_fragment(core::string_view s)
1716  
set_fragment(core::string_view s)
1717  
{
1717  
{
1718  
    op_t op(*this, &s);
1718  
    op_t op(*this, &s);
1719  
    encoding_opts opt;
1719  
    encoding_opts opt;
1720  
    auto const n = encoded_size(
1720  
    auto const n = encoded_size(
1721  
        s,
1721  
        s,
1722  
        detail::fragment_chars,
1722  
        detail::fragment_chars,
1723  
        opt);
1723  
        opt);
1724  
    auto dest = resize_impl(
1724  
    auto dest = resize_impl(
1725  
        id_frag, n + 1, op);
1725  
        id_frag, n + 1, op);
1726  
    *dest++ = '#';
1726  
    *dest++ = '#';
1727  
    encode_unsafe(
1727  
    encode_unsafe(
1728  
        dest,
1728  
        dest,
1729  
        n,
1729  
        n,
1730  
        s,
1730  
        s,
1731  
        detail::fragment_chars,
1731  
        detail::fragment_chars,
1732  
        opt);
1732  
        opt);
1733  
    impl_.decoded_[id_frag] =
1733  
    impl_.decoded_[id_frag] =
1734  
        detail::to_size_type(s.size());
1734  
        detail::to_size_type(s.size());
1735  
    return *this;
1735  
    return *this;
1736  
}
1736  
}
1737  

1737  

1738  
inline
1738  
inline
1739  
url_base&
1739  
url_base&
1740  
url_base::
1740  
url_base::
1741  
set_encoded_fragment(
1741  
set_encoded_fragment(
1742  
    pct_string_view s)
1742  
    pct_string_view s)
1743  
{
1743  
{
1744  
    op_t op(*this, &detail::ref(s));
1744  
    op_t op(*this, &detail::ref(s));
1745  
    auto const n =
1745  
    auto const n =
1746  
        detail::re_encoded_size_unsafe(
1746  
        detail::re_encoded_size_unsafe(
1747  
            s,
1747  
            s,
1748  
            detail::fragment_chars);
1748  
            detail::fragment_chars);
1749  
    auto dest = resize_impl(
1749  
    auto dest = resize_impl(
1750  
        id_frag, n + 1, op);
1750  
        id_frag, n + 1, op);
1751  
    *dest++ = '#';
1751  
    *dest++ = '#';
1752  
    impl_.decoded_[id_frag] =
1752  
    impl_.decoded_[id_frag] =
1753  
        detail::to_size_type(detail::re_encode_unsafe(
1753  
        detail::to_size_type(detail::re_encode_unsafe(
1754  
            dest,
1754  
            dest,
1755  
            dest + n,
1755  
            dest + n,
1756  
            s,
1756  
            s,
1757  
            detail::fragment_chars));
1757  
            detail::fragment_chars));
1758  
    BOOST_ASSERT(
1758  
    BOOST_ASSERT(
1759  
        impl_.decoded_[id_frag] ==
1759  
        impl_.decoded_[id_frag] ==
1760  
            s.decoded_size());
1760  
            s.decoded_size());
1761  
    return *this;
1761  
    return *this;
1762  
}
1762  
}
1763  

1763  

1764  
//------------------------------------------------
1764  
//------------------------------------------------
1765  
//
1765  
//
1766  
// Resolution
1766  
// Resolution
1767  
//
1767  
//
1768  
//------------------------------------------------
1768  
//------------------------------------------------
1769  

1769  

1770  
inline
1770  
inline
1771  
system::result<void>
1771  
system::result<void>
1772  
url_base::
1772  
url_base::
1773  
resolve(
1773  
resolve(
1774  
    url_view_base const& ref)
1774  
    url_view_base const& ref)
1775  
{
1775  
{
1776  
    if(! has_scheme())
1776  
    if(! has_scheme())
1777  
    {
1777  
    {
1778  
        BOOST_URL_RETURN_EC(error::not_a_base);
1778  
        BOOST_URL_RETURN_EC(error::not_a_base);
1779  
    }
1779  
    }
1780  

1780  

1781  
    if (this == &ref)
1781  
    if (this == &ref)
1782  
    {
1782  
    {
1783  
        normalize_path();
1783  
        normalize_path();
1784  
        return {};
1784  
        return {};
1785  
    }
1785  
    }
1786  

1786  

1787  
    op_t op(*this);
1787  
    op_t op(*this);
1788  

1788  

1789  
    //
1789  
    //
1790  
    // 5.2.2. Transform References
1790  
    // 5.2.2. Transform References
1791  
    // https://datatracker.ietf.org/doc/html/rfc3986#section-5.2.2
1791  
    // https://datatracker.ietf.org/doc/html/rfc3986#section-5.2.2
1792  
    //
1792  
    //
1793  

1793  

1794  
    if( ref.has_scheme() &&
1794  
    if( ref.has_scheme() &&
1795  
        ref.scheme() != scheme())
1795  
        ref.scheme() != scheme())
1796  
    {
1796  
    {
1797  
        reserve_impl(ref.size(), op);
1797  
        reserve_impl(ref.size(), op);
1798  
        copy(ref);
1798  
        copy(ref);
1799  
        normalize_path();
1799  
        normalize_path();
1800  
        return {};
1800  
        return {};
1801  
    }
1801  
    }
1802  
    if(ref.has_authority())
1802  
    if(ref.has_authority())
1803  
    {
1803  
    {
1804  
        reserve_impl(
1804  
        reserve_impl(
1805  
            impl_.offset(id_user) + ref.size(), op);
1805  
            impl_.offset(id_user) + ref.size(), op);
1806  
        set_encoded_authority(
1806  
        set_encoded_authority(
1807  
            ref.encoded_authority());
1807  
            ref.encoded_authority());
1808  
        set_encoded_path(
1808  
        set_encoded_path(
1809  
            ref.encoded_path());
1809  
            ref.encoded_path());
1810  
        if (ref.encoded_path().empty())
1810  
        if (ref.encoded_path().empty())
1811  
            set_path_absolute(false);
1811  
            set_path_absolute(false);
1812  
        else
1812  
        else
1813  
            normalize_path();
1813  
            normalize_path();
1814  
        if(ref.has_query())
1814  
        if(ref.has_query())
1815  
            set_encoded_query(
1815  
            set_encoded_query(
1816  
                ref.encoded_query());
1816  
                ref.encoded_query());
1817  
        else
1817  
        else
1818  
            remove_query();
1818  
            remove_query();
1819  
        if(ref.has_fragment())
1819  
        if(ref.has_fragment())
1820  
            set_encoded_fragment(
1820  
            set_encoded_fragment(
1821  
                ref.encoded_fragment());
1821  
                ref.encoded_fragment());
1822  
        else
1822  
        else
1823  
            remove_fragment();
1823  
            remove_fragment();
1824  
        return {};
1824  
        return {};
1825  
    }
1825  
    }
1826  
    if(ref.encoded_path().empty())
1826  
    if(ref.encoded_path().empty())
1827  
    {
1827  
    {
1828  
        reserve_impl(
1828  
        reserve_impl(
1829  
            impl_.offset(id_query) +
1829  
            impl_.offset(id_query) +
1830  
            ref.size(), op);
1830  
            ref.size(), op);
1831  
        normalize_path();
1831  
        normalize_path();
1832  
        if(ref.has_query())
1832  
        if(ref.has_query())
1833  
        {
1833  
        {
1834  
            set_encoded_query(
1834  
            set_encoded_query(
1835  
                ref.encoded_query());
1835  
                ref.encoded_query());
1836  
        }
1836  
        }
1837  
        if(ref.has_fragment())
1837  
        if(ref.has_fragment())
1838  
            set_encoded_fragment(
1838  
            set_encoded_fragment(
1839  
                ref.encoded_fragment());
1839  
                ref.encoded_fragment());
1840  
        else
1840  
        else
1841  
            remove_fragment();
1841  
            remove_fragment();
1842  
        return {};
1842  
        return {};
1843  
    }
1843  
    }
1844  
    if(ref.is_path_absolute())
1844  
    if(ref.is_path_absolute())
1845  
    {
1845  
    {
1846  
        reserve_impl(
1846  
        reserve_impl(
1847  
            impl_.offset(id_path) +
1847  
            impl_.offset(id_path) +
1848  
                ref.size(), op);
1848  
                ref.size(), op);
1849  
        set_encoded_path(
1849  
        set_encoded_path(
1850  
            ref.encoded_path());
1850  
            ref.encoded_path());
1851  
        normalize_path();
1851  
        normalize_path();
1852  
        if(ref.has_query())
1852  
        if(ref.has_query())
1853  
            set_encoded_query(
1853  
            set_encoded_query(
1854  
                ref.encoded_query());
1854  
                ref.encoded_query());
1855  
        else
1855  
        else
1856  
            remove_query();
1856  
            remove_query();
1857  
        if(ref.has_fragment())
1857  
        if(ref.has_fragment())
1858  
            set_encoded_fragment(
1858  
            set_encoded_fragment(
1859  
                ref.encoded_fragment());
1859  
                ref.encoded_fragment());
1860  
        else
1860  
        else
1861  
            remove_fragment();
1861  
            remove_fragment();
1862  
        return {};
1862  
        return {};
1863  
    }
1863  
    }
1864  
    // General case: ref is relative path
1864  
    // General case: ref is relative path
1865  
    reserve_impl(
1865  
    reserve_impl(
1866  
        impl_.offset(id_query) +
1866  
        impl_.offset(id_query) +
1867  
        ref.size(), op);
1867  
        ref.size(), op);
1868  
    // 5.2.3. Merge Paths
1868  
    // 5.2.3. Merge Paths
1869  
    auto es = encoded_segments();
1869  
    auto es = encoded_segments();
1870  
    if(es.size() > 0)
1870  
    if(es.size() > 0)
1871  
    {
1871  
    {
1872  
        es.pop_back();
1872  
        es.pop_back();
1873  
    }
1873  
    }
1874  
    es.insert(es.end(),
1874  
    es.insert(es.end(),
1875  
        ref.encoded_segments().begin(),
1875  
        ref.encoded_segments().begin(),
1876  
        ref.encoded_segments().end());
1876  
        ref.encoded_segments().end());
1877  
    normalize_path();
1877  
    normalize_path();
1878  
    if(ref.has_query())
1878  
    if(ref.has_query())
1879  
        set_encoded_query(
1879  
        set_encoded_query(
1880  
            ref.encoded_query());
1880  
            ref.encoded_query());
1881  
    else
1881  
    else
1882  
        remove_query();
1882  
        remove_query();
1883  
    if(ref.has_fragment())
1883  
    if(ref.has_fragment())
1884  
        set_encoded_fragment(
1884  
        set_encoded_fragment(
1885  
            ref.encoded_fragment());
1885  
            ref.encoded_fragment());
1886  
    else
1886  
    else
1887  
        remove_fragment();
1887  
        remove_fragment();
1888  
    return {};
1888  
    return {};
1889  
}
1889  
}
1890  

1890  

1891  
//------------------------------------------------
1891  
//------------------------------------------------
1892  
//
1892  
//
1893  
// Normalization
1893  
// Normalization
1894  
//
1894  
//
1895  
//------------------------------------------------
1895  
//------------------------------------------------
1896  

1896  

1897  
template <
1897  
template <
1898  
    class AllowedCharset,
1898  
    class AllowedCharset,
1899  
    class IgnoredCharset>
1899  
    class IgnoredCharset>
1900  
void
1900  
void
1901  
url_base::
1901  
url_base::
1902  
normalize_octets_impl(
1902  
normalize_octets_impl(
1903  
    int id,
1903  
    int id,
1904  
    AllowedCharset const& allowed,
1904  
    AllowedCharset const& allowed,
1905  
    IgnoredCharset const& ignored,
1905  
    IgnoredCharset const& ignored,
1906  
    op_t& op) noexcept
1906  
    op_t& op) noexcept
1907  
{
1907  
{
1908  
    char* it = s_ + impl_.offset(id);
1908  
    char* it = s_ + impl_.offset(id);
1909  
    char* end = s_ + impl_.offset(id + 1);
1909  
    char* end = s_ + impl_.offset(id + 1);
1910  
    char d = 0;
1910  
    char d = 0;
1911  
    char* dest = it;
1911  
    char* dest = it;
1912  
    while (it < end)
1912  
    while (it < end)
1913  
    {
1913  
    {
1914  
        if (*it != '%')
1914  
        if (*it != '%')
1915  
        {
1915  
        {
1916  
            *dest = *it;
1916  
            *dest = *it;
1917  
            ++it;
1917  
            ++it;
1918  
            ++dest;
1918  
            ++dest;
1919  
            continue;
1919  
            continue;
1920  
        }
1920  
        }
1921  
        BOOST_ASSERT(end - it >= 3);
1921  
        BOOST_ASSERT(end - it >= 3);
1922  

1922  

1923  
        // decode unreserved octets
1923  
        // decode unreserved octets
1924  
        d = detail::decode_one(it + 1);
1924  
        d = detail::decode_one(it + 1);
1925  
        if (allowed(d) &&
1925  
        if (allowed(d) &&
1926  
            !ignored(d))
1926  
            !ignored(d))
1927  
        {
1927  
        {
1928  
            *dest = d;
1928  
            *dest = d;
1929  
            it += 3;
1929  
            it += 3;
1930  
            ++dest;
1930  
            ++dest;
1931  
            continue;
1931  
            continue;
1932  
        }
1932  
        }
1933  

1933  

1934  
        // uppercase percent-encoding triplets
1934  
        // uppercase percent-encoding triplets
1935  
        *dest++ = '%';
1935  
        *dest++ = '%';
1936  
        ++it;
1936  
        ++it;
1937  
        *dest++ = grammar::to_upper(*it++);
1937  
        *dest++ = grammar::to_upper(*it++);
1938  
        *dest++ = grammar::to_upper(*it++);
1938  
        *dest++ = grammar::to_upper(*it++);
1939  
    }
1939  
    }
1940  
    if (it != dest)
1940  
    if (it != dest)
1941  
    {
1941  
    {
1942  
        auto diff = it - dest;
1942  
        auto diff = it - dest;
1943  
        auto n = impl_.len(id) - diff;
1943  
        auto n = impl_.len(id) - diff;
1944  
        shrink_impl(id, n, op);
1944  
        shrink_impl(id, n, op);
1945  
        s_[size()] = '\0';
1945  
        s_[size()] = '\0';
1946  
    }
1946  
    }
1947  
}
1947  
}
1948  

1948  

1949  
template<class CharSet>
1949  
template<class CharSet>
1950  
void
1950  
void
1951  
url_base::
1951  
url_base::
1952  
normalize_octets_impl(
1952  
normalize_octets_impl(
1953  
    int idx,
1953  
    int idx,
1954  
    CharSet const& allowed,
1954  
    CharSet const& allowed,
1955  
    op_t& op) noexcept
1955  
    op_t& op) noexcept
1956  
{
1956  
{
1957  
    return normalize_octets_impl(
1957  
    return normalize_octets_impl(
1958  
        idx, allowed, detail::empty_chars, op);
1958  
        idx, allowed, detail::empty_chars, op);
1959  
}
1959  
}
1960  

1960  

1961  
inline
1961  
inline
1962  
url_base&
1962  
url_base&
1963  
url_base::
1963  
url_base::
1964  
normalize_scheme()
1964  
normalize_scheme()
1965  
{
1965  
{
1966  
    to_lower_impl(id_scheme);
1966  
    to_lower_impl(id_scheme);
1967  
    return *this;
1967  
    return *this;
1968  
}
1968  
}
1969  

1969  

1970  
inline
1970  
inline
1971  
url_base&
1971  
url_base&
1972  
url_base::
1972  
url_base::
1973  
normalize_authority()
1973  
normalize_authority()
1974  
{
1974  
{
1975  
    op_t op(*this);
1975  
    op_t op(*this);
1976  

1976  

1977  
    // normalize host
1977  
    // normalize host
1978  
    if (host_type() == urls::host_type::name)
1978  
    if (host_type() == urls::host_type::name)
1979  
    {
1979  
    {
1980  
        normalize_octets_impl(
1980  
        normalize_octets_impl(
1981  
            id_host,
1981  
            id_host,
1982  
            detail::reg_name_chars, op);
1982  
            detail::reg_name_chars, op);
1983  
    }
1983  
    }
1984  
    decoded_to_lower_impl(id_host);
1984  
    decoded_to_lower_impl(id_host);
1985  

1985  

1986  
    // normalize password
1986  
    // normalize password
1987  
    normalize_octets_impl(id_pass, detail::password_chars, op);
1987  
    normalize_octets_impl(id_pass, detail::password_chars, op);
1988  

1988  

1989  
    // normalize user
1989  
    // normalize user
1990  
    normalize_octets_impl(id_user, detail::user_chars, op);
1990  
    normalize_octets_impl(id_user, detail::user_chars, op);
1991  
    return *this;
1991  
    return *this;
1992  
}
1992  
}
1993  

1993  

1994  
inline
1994  
inline
1995  
url_base&
1995  
url_base&
1996  
url_base::
1996  
url_base::
1997  
normalize_path()
1997  
normalize_path()
1998  
{
1998  
{
1999  
    op_t op(*this);
1999  
    op_t op(*this);
2000  

2000  

2001  
    //
2001  
    //
2002  
    // Step 1: Percent-encoding normalization
2002  
    // Step 1: Percent-encoding normalization
2003  
    //
2003  
    //
2004  
    // Decode percent-encoded characters that are
2004  
    // Decode percent-encoded characters that are
2005  
    // valid unencoded in path segments (pchar),
2005  
    // valid unencoded in path segments (pchar),
2006  
    // and uppercase remaining hex digits.
2006  
    // and uppercase remaining hex digits.
2007  
    //
2007  
    //
2008  
    // This can introduce:
2008  
    // This can introduce:
2009  
    //   - New '.' from %2E (dot is unreserved)
2009  
    //   - New '.' from %2E (dot is unreserved)
2010  
    //   - New ':' from %3A (colon is a pchar)
2010  
    //   - New ':' from %3A (colon is a pchar)
2011  
    // This cannot introduce:
2011  
    // This cannot introduce:
2012  
    //   - New '/' (%2F stays encoded, '/' is not
2012  
    //   - New '/' (%2F stays encoded, '/' is not
2013  
    //     a segment character)
2013  
    //     a segment character)
2014  
    //
2014  
    //
2015  
    // These new '.' and ':' characters can create
2015  
    // These new '.' and ':' characters can create
2016  
    // ambiguities that Steps 2 and 3 must handle.
2016  
    // ambiguities that Steps 2 and 3 must handle.
2017  
    //
2017  
    //
2018  
    normalize_octets_impl(id_path, detail::segment_chars, op);
2018  
    normalize_octets_impl(id_path, detail::segment_chars, op);
2019  
    core::string_view p = impl_.get(id_path);
2019  
    core::string_view p = impl_.get(id_path);
2020  
    char* p_dest = s_ + impl_.offset(id_path);
2020  
    char* p_dest = s_ + impl_.offset(id_path);
2021  
    char* p_end = s_ + impl_.offset(id_path + 1);
2021  
    char* p_end = s_ + impl_.offset(id_path + 1);
2022  
    auto pn = p.size();
2022  
    auto pn = p.size();
2023  

2023  

2024  
    //
2024  
    //
2025  
    // Step 2: Preserve existing path shields
2025  
    // Step 2: Preserve existing path shields
2026  
    //
2026  
    //
2027  
    // If the URL has no authority, a path starting
2027  
    // If the URL has no authority, a path starting
2028  
    // with "//" would be misinterpreted as an
2028  
    // with "//" would be misinterpreted as an
2029  
    // authority when serialized and re-parsed.
2029  
    // authority when serialized and re-parsed.
2030  
    // Some paths already have a dot prefix ("/."
2030  
    // Some paths already have a dot prefix ("/."
2031  
    // or "./") that shields against this. We
2031  
    // or "./") that shields against this. We
2032  
    // preserve them so remove_dot_segments (Step 3)
2032  
    // preserve them so remove_dot_segments (Step 3)
2033  
    // does not strip them. Step 1 can also reveal
2033  
    // does not strip them. Step 1 can also reveal
2034  
    // new shields by decoding %2E to '.'.
2034  
    // new shields by decoding %2E to '.'.
2035  
    //
2035  
    //
2036  
    // This is an optimization. Step 4 would create
2036  
    // This is an optimization. Step 4 would create
2037  
    // a new shield anyway if remove_dot_segments
2037  
    // a new shield anyway if remove_dot_segments
2038  
    // produces a problematic output, but preserving
2038  
    // produces a problematic output, but preserving
2039  
    // an existing shield avoids the memmove in
2039  
    // an existing shield avoids the memmove in
2040  
    // Step 4.
2040  
    // Step 4.
2041  
    //
2041  
    //
2042  
    // path_shield_len: number of leading bytes
2042  
    // path_shield_len: number of leading bytes
2043  
    // to skip during remove_dot_segments.
2043  
    // to skip during remove_dot_segments.
2044  
    // Always 0 (no shield) or 2 ("/." or "./").
2044  
    // Always 0 (no shield) or 2 ("/." or "./").
2045  
    //
2045  
    //
2046  
    auto path_shield_len = 0;
2046  
    auto path_shield_len = 0;
2047  
    if (!has_authority())
2047  
    if (!has_authority())
2048  
    {
2048  
    {
2049  
        if (p.starts_with("/./"))
2049  
        if (p.starts_with("/./"))
2050  
        {
2050  
        {
2051  
            // (a) Absolute path with "/." prefix.
2051  
            // (a) Absolute path with "/." prefix.
2052  
            //
2052  
            //
2053  
            // Check if "/." shields a "//"
2053  
            // Check if "/." shields a "//"
2054  
            // underneath (possibly through
2054  
            // underneath (possibly through
2055  
            // multiple "/./" layers like "/././/").
2055  
            // multiple "/./" layers like "/././/").
2056  
            // If so, preserve the first 2 chars.
2056  
            // If so, preserve the first 2 chars.
2057  
            //
2057  
            //
2058  
            // /.//evil -> /.//evil (preserved)
2058  
            // /.//evil -> /.//evil (preserved)
2059  
            //   Without it: //evil (ambiguous).
2059  
            //   Without it: //evil (ambiguous).
2060  
            // /././/evil -> /.//evil (same idea)
2060  
            // /././/evil -> /.//evil (same idea)
2061  
            //
2061  
            //
2062  
            // Requires no authority (with authority,
2062  
            // Requires no authority (with authority,
2063  
            // "//" in the path is unambiguous):
2063  
            // "//" in the path is unambiguous):
2064  
            //   http://h/.//x -> http://h//x (OK)
2064  
            //   http://h/.//x -> http://h//x (OK)
2065  
            // Scheme is irrelevant (absolute paths
2065  
            // Scheme is irrelevant (absolute paths
2066  
            // can't be confused with a scheme):
2066  
            // can't be confused with a scheme):
2067  
            //   scheme:/.//x and /.//x both need it.
2067  
            //   scheme:/.//x and /.//x both need it.
2068  
            //
2068  
            //
2069  
            path_shield_len = 2;
2069  
            path_shield_len = 2;
2070  
            while (p.substr(path_shield_len, 3).starts_with("/./"))
2070  
            while (p.substr(path_shield_len, 3).starts_with("/./"))
2071  
            {
2071  
            {
2072  
                path_shield_len += 2;
2072  
                path_shield_len += 2;
2073  
            }
2073  
            }
2074  
            if (p.substr(path_shield_len).starts_with("//"))
2074  
            if (p.substr(path_shield_len).starts_with("//"))
2075  
            {
2075  
            {
2076  
                path_shield_len = 2;
2076  
                path_shield_len = 2;
2077  
            }
2077  
            }
2078  
            else
2078  
            else
2079  
            {
2079  
            {
2080  
                path_shield_len = 0;
2080  
                path_shield_len = 0;
2081  
            }
2081  
            }
2082  
        }
2082  
        }
2083  
        else if (
2083  
        else if (
2084  
            !has_scheme() &&
2084  
            !has_scheme() &&
2085  
            p.starts_with("./"))
2085  
            p.starts_with("./"))
2086  
        {
2086  
        {
2087  
            // (b) Relative path with "./" prefix,
2087  
            // (b) Relative path with "./" prefix,
2088  
            //     no scheme.
2088  
            //     no scheme.
2089  
            //
2089  
            //
2090  
            // Check if "./" shields a "//"
2090  
            // Check if "./" shields a "//"
2091  
            // underneath. If so, preserve it.
2091  
            // underneath. If so, preserve it.
2092  
            //
2092  
            //
2093  
            // .//evil -> .//evil (preserved)
2093  
            // .//evil -> .//evil (preserved)
2094  
            //   Without it: //evil (ambiguous).
2094  
            //   Without it: //evil (ambiguous).
2095  
            // ././/evil -> .//evil (same idea)
2095  
            // ././/evil -> .//evil (same idea)
2096  
            //
2096  
            //
2097  
            // Requires no authority (with authority,
2097  
            // Requires no authority (with authority,
2098  
            // "//" in the path is unambiguous):
2098  
            // "//" in the path is unambiguous):
2099  
            //   //h/.//x -> //h//x (OK)
2099  
            //   //h/.//x -> //h//x (OK)
2100  
            // Requires no scheme: with a scheme,
2100  
            // Requires no scheme: with a scheme,
2101  
            // remove_dot_segments would strip "./"
2101  
            // remove_dot_segments would strip "./"
2102  
            // and Step 4 handles any resulting "//".
2102  
            // and Step 4 handles any resulting "//".
2103  
            //
2103  
            //
2104  
            path_shield_len = 1;
2104  
            path_shield_len = 1;
2105  
            while (p.substr(path_shield_len, 3).starts_with("/./"))
2105  
            while (p.substr(path_shield_len, 3).starts_with("/./"))
2106  
            {
2106  
            {
2107  
                path_shield_len += 2;
2107  
                path_shield_len += 2;
2108  
            }
2108  
            }
2109  
            if (p.substr(path_shield_len).starts_with("//"))
2109  
            if (p.substr(path_shield_len).starts_with("//"))
2110  
            {
2110  
            {
2111  
                path_shield_len = 2;
2111  
                path_shield_len = 2;
2112  
            }
2112  
            }
2113  
            else
2113  
            else
2114  
            {
2114  
            {
2115  
                path_shield_len = 0;
2115  
                path_shield_len = 0;
2116  
            }
2116  
            }
2117  
        }
2117  
        }
2118  
    }
2118  
    }
2119  

2119  

2120  
    //
2120  
    //
2121  
    // Step 3: Remove "." and ".." segments
2121  
    // Step 3: Remove "." and ".." segments
2122  
    //
2122  
    //
2123  
    // RFC 3986 Section 5.2.4.
2123  
    // RFC 3986 Section 5.2.4.
2124  
    //
2124  
    //
2125  
    // If path_shield_len > 0, the first
2125  
    // If path_shield_len > 0, the first
2126  
    // path_shield_len characters are an existing
2126  
    // path_shield_len characters are an existing
2127  
    // path shield ("/." or "./") that must
2127  
    // path shield ("/." or "./") that must
2128  
    // be preserved (see Step 2), we tell
2128  
    // be preserved (see Step 2), we tell
2129  
    // remove_dot_segments to start after that
2129  
    // remove_dot_segments to start after that
2130  
    // prefix.
2130  
    // prefix.
2131  
    //
2131  
    //
2132  
    // Note: remove_dot_segments only recognizes
2132  
    // Note: remove_dot_segments only recognizes
2133  
    // literal '.' and '..', not encoded forms like
2133  
    // literal '.' and '..', not encoded forms like
2134  
    // '%2E'. This is correct here because Step 1
2134  
    // '%2E'. This is correct here because Step 1
2135  
    // already decoded %2E to '.'. If this function
2135  
    // already decoded %2E to '.'. If this function
2136  
    // were called without Step 1, encoded dots
2136  
    // were called without Step 1, encoded dots
2137  
    // would be missed.
2137  
    // would be missed.
2138  
    //
2138  
    //
2139  
    bool was_absolute = is_path_absolute();
2139  
    bool was_absolute = is_path_absolute();
2140  
    p.remove_prefix(path_shield_len);
2140  
    p.remove_prefix(path_shield_len);
2141  
    p_dest += path_shield_len;
2141  
    p_dest += path_shield_len;
2142  
    auto n = detail::remove_dot_segments(
2142  
    auto n = detail::remove_dot_segments(
2143  
        p_dest, p_end, p);
2143  
        p_dest, p_end, p);
2144  

2144  

2145  
    //
2145  
    //
2146  
    // Step 4: Create path shield if needed,
2146  
    // Step 4: Create path shield if needed,
2147  
    // then shrink path to final size
2147  
    // then shrink path to final size
2148  
    //
2148  
    //
2149  
    // remove_dot_segments can produce output that
2149  
    // remove_dot_segments can produce output that
2150  
    // needs a 2-byte shield prefix, as explained
2150  
    // needs a 2-byte shield prefix, as explained
2151  
    // in step 2. The memmove below writes within
2151  
    // in step 2. The memmove below writes within
2152  
    // the original path region (before shrink_impl)
2152  
    // the original path region (before shrink_impl)
2153  
    // and always has room because ".." cancellation
2153  
    // and always has room because ".." cancellation
2154  
    // consumes >= 5 bytes but we only need 2 for the
2154  
    // consumes >= 5 bytes but we only need 2 for the
2155  
    // shield.
2155  
    // shield.
2156  
    //
2156  
    //
2157  
    bool needs_shield = [&]()
2157  
    bool needs_shield = [&]()
2158  
    {
2158  
    {
2159  
        if (path_shield_len)
2159  
        if (path_shield_len)
2160  
        {
2160  
        {
2161  
            // Step 2 already preserved a shield.
2161  
            // Step 2 already preserved a shield.
2162  
            return false;
2162  
            return false;
2163  
        }
2163  
        }
2164  
        if (has_authority())
2164  
        if (has_authority())
2165  
        {
2165  
        {
2166  
            // With an authority, "//" in the path
2166  
            // With an authority, "//" in the path
2167  
            // is unambiguous and the path is always
2167  
            // is unambiguous and the path is always
2168  
            // absolute (path-abempty).
2168  
            // absolute (path-abempty).
2169  
            return false;
2169  
            return false;
2170  
        }
2170  
        }
2171  
        if (n == 0)
2171  
        if (n == 0)
2172  
        {
2172  
        {
2173  
            // Empty output. Nothing to shield.
2173  
            // Empty output. Nothing to shield.
2174  
            return false;
2174  
            return false;
2175  
        }
2175  
        }
2176  
        if (p_dest[0] != '/')
2176  
        if (p_dest[0] != '/')
2177  
        {
2177  
        {
2178  
            // Output doesn't start with '/'.
2178  
            // Output doesn't start with '/'.
2179  
            // No authority ambiguity, and if it
2179  
            // No authority ambiguity, and if it
2180  
            // was relative, it stayed relative.
2180  
            // was relative, it stayed relative.
2181  
            return false;
2181  
            return false;
2182  
        }
2182  
        }
2183  
        if (n >= 2 && p_dest[1] == '/')
2183  
        if (n >= 2 && p_dest[1] == '/')
2184  
        {
2184  
        {
2185  
            // Output starts with "//": would be
2185  
            // Output starts with "//": would be
2186  
            // misinterpreted as authority.
2186  
            // misinterpreted as authority.
2187  
            //   /a/..//evil -> //evil
2187  
            //   /a/..//evil -> //evil
2188  
            // so we need a shield
2188  
            // so we need a shield
2189  
            //   /a/..//evil -> /.//evil
2189  
            //   /a/..//evil -> /.//evil
2190  
            return true;
2190  
            return true;
2191  
        }
2191  
        }
2192  
        if (!was_absolute)
2192  
        if (!was_absolute)
2193  
        {
2193  
        {
2194  
            // Relative path became absolute: ".."
2194  
            // Relative path became absolute: ".."
2195  
            // canceled all leading segments and
2195  
            // canceled all leading segments and
2196  
            // the remaining input started with "/"
2196  
            // the remaining input started with "/"
2197  
            // because of how remove_dot_segments is
2197  
            // because of how remove_dot_segments is
2198  
            // defined.
2198  
            // defined.
2199  
            //   a/..//evil -> /evil -> .//evil
2199  
            //   a/..//evil -> /evil -> .//evil
2200  
            //   a/b/../..//evil -> /evil -> .//evil
2200  
            //   a/b/../..//evil -> /evil -> .//evil
2201  
            return true;
2201  
            return true;
2202  
        }
2202  
        }
2203  
        return false;
2203  
        return false;
2204  
    }();
2204  
    }();
2205  
    if (needs_shield)
2205  
    if (needs_shield)
2206  
    {
2206  
    {
2207  
        BOOST_ASSERT(n + 2 <= pn);
2207  
        BOOST_ASSERT(n + 2 <= pn);
2208  
        std::memmove(p_dest + 2, p_dest, n);
2208  
        std::memmove(p_dest + 2, p_dest, n);
2209  
        if (was_absolute)
2209  
        if (was_absolute)
2210  
        {
2210  
        {
2211  
            // "/." keeps the path absolute.
2211  
            // "/." keeps the path absolute.
2212  
            p_dest[0] = '/';
2212  
            p_dest[0] = '/';
2213  
            p_dest[1] = '.';
2213  
            p_dest[1] = '.';
2214  
        }
2214  
        }
2215  
        else
2215  
        else
2216  
        {
2216  
        {
2217  
            // "./" keeps the path relative.
2217  
            // "./" keeps the path relative.
2218  
            p_dest[0] = '.';
2218  
            p_dest[0] = '.';
2219  
            p_dest[1] = '/';
2219  
            p_dest[1] = '/';
2220  
        }
2220  
        }
2221  
        path_shield_len = 2;
2221  
        path_shield_len = 2;
2222  
    }
2222  
    }
2223  
    if (n != pn)
2223  
    if (n != pn)
2224  
    {
2224  
    {
2225  
        BOOST_ASSERT(n < pn);
2225  
        BOOST_ASSERT(n < pn);
2226  
        shrink_impl(id_path, n + path_shield_len, op);
2226  
        shrink_impl(id_path, n + path_shield_len, op);
2227  
    }
2227  
    }
2228  

2228  

2229  
    //
2229  
    //
2230  
    // Step 5: Encode colons in first segment
2230  
    // Step 5: Encode colons in first segment
2231  
    //
2231  
    //
2232  
    // If the URL has no scheme and no authority,
2232  
    // If the URL has no scheme and no authority,
2233  
    // a colon in the first path segment would be
2233  
    // a colon in the first path segment would be
2234  
    // misinterpreted as a scheme delimiter when
2234  
    // misinterpreted as a scheme delimiter when
2235  
    // serialized and re-parsed.
2235  
    // serialized and re-parsed.
2236  
    //
2236  
    //
2237  
    // Colons can appear in the first segment either
2237  
    // Colons can appear in the first segment either
2238  
    // because they were already there (decoded from
2238  
    // because they were already there (decoded from
2239  
    // %3A in Step 1), or because remove_dot_segments
2239  
    // %3A in Step 1), or because remove_dot_segments
2240  
    // (Step 3) canceled preceding segments via ".."
2240  
    // (Step 3) canceled preceding segments via ".."
2241  
    // and exposed a colon that was deeper in the path.
2241  
    // and exposed a colon that was deeper in the path.
2242  
    //
2242  
    //
2243  
    // Example (pre-existing):
2243  
    // Example (pre-existing):
2244  
    //   my%3Asharona -> Step 1: my:sharona
2244  
    //   my%3Asharona -> Step 1: my:sharona
2245  
    //   -> Step 5: my%3Asharona
2245  
    //   -> Step 5: my%3Asharona
2246  
    // Example (exposed by dot removal):
2246  
    // Example (exposed by dot removal):
2247  
    //   a/../b:c -> Step 3: b:c
2247  
    //   a/../b:c -> Step 3: b:c
2248  
    //   -> Step 5: b%3Ac
2248  
    //   -> Step 5: b%3Ac
2249  
    //
2249  
    //
2250  
    // ALL colons in the first segment must be
2250  
    // ALL colons in the first segment must be
2251  
    // encoded, not just the first one. In a
2251  
    // encoded, not just the first one. In a
2252  
    // schemeless relative-reference, the first
2252  
    // schemeless relative-reference, the first
2253  
    // segment must be segment-nz-nc (RFC 3986
2253  
    // segment must be segment-nz-nc (RFC 3986
2254  
    // Section 4.2), which does not allow ':':
2254  
    // Section 4.2), which does not allow ':':
2255  
    //   path-noscheme = segment-nz-nc *( "/" segment )
2255  
    //   path-noscheme = segment-nz-nc *( "/" segment )
2256  
    //   segment-nz-nc = 1*( unreserved / pct-encoded
2256  
    //   segment-nz-nc = 1*( unreserved / pct-encoded
2257  
    //                     / sub-delims / "@" )
2257  
    //                     / sub-delims / "@" )
2258  
    // So "b%3Ac:d" would fail to re-parse.
2258  
    // So "b%3Ac:d" would fail to re-parse.
2259  
    // All colons must go: "b%3Ac%3Ad".
2259  
    // All colons must go: "b%3Ac%3Ad".
2260  
    //
2260  
    //
2261  
    // Requires no scheme (with a scheme, colons
2261  
    // Requires no scheme (with a scheme, colons
2262  
    // in the path are unambiguous):
2262  
    // in the path are unambiguous):
2263  
    //   scheme:a:b -> scheme:a:b (OK)
2263  
    //   scheme:a:b -> scheme:a:b (OK)
2264  
    // Requires no authority (with authority,
2264  
    // Requires no authority (with authority,
2265  
    // path starts with "/" so the first segment
2265  
    // path starts with "/" so the first segment
2266  
    // is empty, no colon issue).
2266  
    // is empty, no colon issue).
2267  
    //
2267  
    //
2268  
    if (!has_scheme() &&
2268  
    if (!has_scheme() &&
2269  
        !has_authority())
2269  
        !has_authority())
2270  
    {
2270  
    {
2271  
        p = impl_.get(id_path);
2271  
        p = impl_.get(id_path);
2272  
        core::string_view first_seg = p;
2272  
        core::string_view first_seg = p;
2273  
        auto i = first_seg.find('/');
2273  
        auto i = first_seg.find('/');
2274  
        if (i != core::string_view::npos)
2274  
        if (i != core::string_view::npos)
2275  
        {
2275  
        {
2276  
            first_seg = p.substr(0, i);
2276  
            first_seg = p.substr(0, i);
2277  
        }
2277  
        }
2278  
        if (first_seg.contains(':'))
2278  
        if (first_seg.contains(':'))
2279  
        {
2279  
        {
2280  
            pn = p.size();
2280  
            pn = p.size();
2281  
            auto cn =
2281  
            auto cn =
2282  
                std::count(
2282  
                std::count(
2283  
                    first_seg.begin(),
2283  
                    first_seg.begin(),
2284  
                    first_seg.end(),
2284  
                    first_seg.end(),
2285  
                    ':');
2285  
                    ':');
2286  
            resize_impl(
2286  
            resize_impl(
2287  
                id_path, pn + (2 * cn), op);
2287  
                id_path, pn + (2 * cn), op);
2288  
            auto begin = s_ + impl_.offset(id_path);
2288  
            auto begin = s_ + impl_.offset(id_path);
2289  
            auto it = begin;
2289  
            auto it = begin;
2290  
            auto end = begin + pn;
2290  
            auto end = begin + pn;
2291  
            while (it != end &&
2291  
            while (it != end &&
2292  
                   *it != '/')
2292  
                   *it != '/')
2293  
            {
2293  
            {
2294  
                ++it;
2294  
                ++it;
2295  
            }
2295  
            }
2296  
            std::memmove(it + (2 * cn), it, end - it);
2296  
            std::memmove(it + (2 * cn), it, end - it);
2297  
            auto src = s_ + impl_.offset(id_path) + pn;
2297  
            auto src = s_ + impl_.offset(id_path) + pn;
2298  
            auto dest = s_ + impl_.offset(id_query);
2298  
            auto dest = s_ + impl_.offset(id_query);
2299  
            src -= end - it;
2299  
            src -= end - it;
2300  
            dest -= end - it;
2300  
            dest -= end - it;
2301  
            pn -= end - it;
2301  
            pn -= end - it;
2302  
            do {
2302  
            do {
2303  
                --src;
2303  
                --src;
2304  
                --dest;
2304  
                --dest;
2305  
                if (*src != ':')
2305  
                if (*src != ':')
2306  
                {
2306  
                {
2307  
                    *dest = *src;
2307  
                    *dest = *src;
2308  
                }
2308  
                }
2309  
                else
2309  
                else
2310  
                {
2310  
                {
2311  
                    *dest-- = 'A';
2311  
                    *dest-- = 'A';
2312  
                    *dest-- = '3';
2312  
                    *dest-- = '3';
2313  
                    *dest = '%';
2313  
                    *dest = '%';
2314  
                }
2314  
                }
2315  
                --pn;
2315  
                --pn;
2316  
            } while (pn);
2316  
            } while (pn);
2317  
        }
2317  
        }
2318  
    }
2318  
    }
2319  

2319  

2320  
    //
2320  
    //
2321  
    // Step 6: Update path parameters
2321  
    // Step 6: Update path parameters
2322  
    //
2322  
    //
2323  
    {
2323  
    {
2324  
        p = encoded_path();
2324  
        p = encoded_path();
2325  
        if (p == "/")
2325  
        if (p == "/")
2326  
        {
2326  
        {
2327  
            impl_.nseg_ = 0;
2327  
            impl_.nseg_ = 0;
2328  
        }
2328  
        }
2329  
        else if (!p.empty())
2329  
        else if (!p.empty())
2330  
        {
2330  
        {
2331  
            impl_.nseg_ = detail::to_size_type(
2331  
            impl_.nseg_ = detail::to_size_type(
2332  
                std::count(
2332  
                std::count(
2333  
                    p.begin() + 1, p.end(), '/') + 1);
2333  
                    p.begin() + 1, p.end(), '/') + 1);
2334  
        }
2334  
        }
2335  
        else
2335  
        else
2336  
        {
2336  
        {
2337  
            impl_.nseg_ = 0;
2337  
            impl_.nseg_ = 0;
2338  
        }
2338  
        }
2339  
        impl_.decoded_[id_path] =
2339  
        impl_.decoded_[id_path] =
2340  
            detail::to_size_type(detail::decode_bytes_unsafe(
2340  
            detail::to_size_type(detail::decode_bytes_unsafe(
2341  
                impl_.get(id_path)));
2341  
                impl_.get(id_path)));
2342  
    }
2342  
    }
2343  
    return *this;
2343  
    return *this;
2344  
}
2344  
}
2345  

2345  

2346  
inline
2346  
inline
2347  
url_base&
2347  
url_base&
2348  
url_base::
2348  
url_base::
2349  
normalize_query()
2349  
normalize_query()
2350  
{
2350  
{
2351  
    op_t op(*this);
2351  
    op_t op(*this);
2352  
    normalize_octets_impl(
2352  
    normalize_octets_impl(
2353  
        id_query,
2353  
        id_query,
2354  
        detail::query_chars,
2354  
        detail::query_chars,
2355  
        detail::query_ignore_chars,
2355  
        detail::query_ignore_chars,
2356  
        op);
2356  
        op);
2357  
    return *this;
2357  
    return *this;
2358  
}
2358  
}
2359  

2359  

2360  
inline
2360  
inline
2361  
url_base&
2361  
url_base&
2362  
url_base::
2362  
url_base::
2363  
normalize_fragment()
2363  
normalize_fragment()
2364  
{
2364  
{
2365  
    op_t op(*this);
2365  
    op_t op(*this);
2366  
    normalize_octets_impl(
2366  
    normalize_octets_impl(
2367  
        id_frag, detail::fragment_chars, op);
2367  
        id_frag, detail::fragment_chars, op);
2368  
    return *this;
2368  
    return *this;
2369  
}
2369  
}
2370  

2370  

2371  
inline
2371  
inline
2372  
url_base&
2372  
url_base&
2373  
url_base::
2373  
url_base::
2374  
normalize()
2374  
normalize()
2375  
{
2375  
{
2376  
    normalize_fragment();
2376  
    normalize_fragment();
2377  
    normalize_query();
2377  
    normalize_query();
2378  
    normalize_path();
2378  
    normalize_path();
2379  
    normalize_authority();
2379  
    normalize_authority();
2380  
    normalize_scheme();
2380  
    normalize_scheme();
2381  
    return *this;
2381  
    return *this;
2382  
}
2382  
}
2383  

2383  

2384  
//------------------------------------------------
2384  
//------------------------------------------------
2385  
//
2385  
//
2386  
// Implementation
2386  
// Implementation
2387  
//
2387  
//
2388  
//------------------------------------------------
2388  
//------------------------------------------------
2389  

2389  

2390  
inline
2390  
inline
2391  
void
2391  
void
2392  
url_base::
2392  
url_base::
2393  
check_invariants() const noexcept
2393  
check_invariants() const noexcept
2394  
{
2394  
{
2395  
    BOOST_ASSERT(
2395  
    BOOST_ASSERT(
2396  
        impl_.len(id_scheme) == 0 ||
2396  
        impl_.len(id_scheme) == 0 ||
2397  
        impl_.get(id_scheme).ends_with(':'));
2397  
        impl_.get(id_scheme).ends_with(':'));
2398  
    BOOST_ASSERT(
2398  
    BOOST_ASSERT(
2399  
        impl_.len(id_user) == 0 ||
2399  
        impl_.len(id_user) == 0 ||
2400  
        impl_.get(id_user).starts_with("//"));
2400  
        impl_.get(id_user).starts_with("//"));
2401  
    BOOST_ASSERT(
2401  
    BOOST_ASSERT(
2402  
        impl_.len(id_pass) == 0 ||
2402  
        impl_.len(id_pass) == 0 ||
2403  
        impl_.get(id_user).starts_with("//"));
2403  
        impl_.get(id_user).starts_with("//"));
2404  
    BOOST_ASSERT(
2404  
    BOOST_ASSERT(
2405  
        impl_.len(id_pass) == 0 ||
2405  
        impl_.len(id_pass) == 0 ||
2406  
        (impl_.len(id_pass) == 1 &&
2406  
        (impl_.len(id_pass) == 1 &&
2407  
            impl_.get(id_pass) == "@") ||
2407  
            impl_.get(id_pass) == "@") ||
2408  
        (impl_.len(id_pass) > 1 &&
2408  
        (impl_.len(id_pass) > 1 &&
2409  
            impl_.get(id_pass).starts_with(':') &&
2409  
            impl_.get(id_pass).starts_with(':') &&
2410  
            impl_.get(id_pass).ends_with('@')));
2410  
            impl_.get(id_pass).ends_with('@')));
2411  
    BOOST_ASSERT(
2411  
    BOOST_ASSERT(
2412  
        impl_.len(id_user, id_path) == 0 ||
2412  
        impl_.len(id_user, id_path) == 0 ||
2413  
        impl_.get(id_user).starts_with("//"));
2413  
        impl_.get(id_user).starts_with("//"));
2414  
    BOOST_ASSERT(impl_.decoded_[id_path] >=
2414  
    BOOST_ASSERT(impl_.decoded_[id_path] >=
2415  
        ((impl_.len(id_path) + 2) / 3));
2415  
        ((impl_.len(id_path) + 2) / 3));
2416  
    BOOST_ASSERT(
2416  
    BOOST_ASSERT(
2417  
        impl_.len(id_port) == 0 ||
2417  
        impl_.len(id_port) == 0 ||
2418  
        impl_.get(id_port).starts_with(':'));
2418  
        impl_.get(id_port).starts_with(':'));
2419  
    BOOST_ASSERT(
2419  
    BOOST_ASSERT(
2420  
        impl_.len(id_query) == 0 ||
2420  
        impl_.len(id_query) == 0 ||
2421  
        impl_.get(id_query).starts_with('?'));
2421  
        impl_.get(id_query).starts_with('?'));
2422  
    BOOST_ASSERT(
2422  
    BOOST_ASSERT(
2423  
        (impl_.len(id_query) == 0 && impl_.nparam_ == 0) ||
2423  
        (impl_.len(id_query) == 0 && impl_.nparam_ == 0) ||
2424  
        (impl_.len(id_query) > 0 && impl_.nparam_ > 0));
2424  
        (impl_.len(id_query) > 0 && impl_.nparam_ > 0));
2425  
    BOOST_ASSERT(
2425  
    BOOST_ASSERT(
2426  
        impl_.len(id_frag) == 0 ||
2426  
        impl_.len(id_frag) == 0 ||
2427  
        impl_.get(id_frag).starts_with('#'));
2427  
        impl_.get(id_frag).starts_with('#'));
2428  
    BOOST_ASSERT(c_str()[size()] == '\0');
2428  
    BOOST_ASSERT(c_str()[size()] == '\0');
2429  
}
2429  
}
2430  

2430  

2431  
inline
2431  
inline
2432  
char*
2432  
char*
2433  
url_base::
2433  
url_base::
2434  
resize_impl(
2434  
resize_impl(
2435  
    int id,
2435  
    int id,
2436  
    std::size_t new_size,
2436  
    std::size_t new_size,
2437  
    op_t& op)
2437  
    op_t& op)
2438  
{
2438  
{
2439  
    return resize_impl(
2439  
    return resize_impl(
2440  
        id, id + 1, new_size, op);
2440  
        id, id + 1, new_size, op);
2441  
}
2441  
}
2442  

2442  

2443  
inline
2443  
inline
2444  
char*
2444  
char*
2445  
url_base::
2445  
url_base::
2446  
resize_impl(
2446  
resize_impl(
2447  
    int first,
2447  
    int first,
2448  
    int last,
2448  
    int last,
2449  
    std::size_t new_len,
2449  
    std::size_t new_len,
2450  
    op_t& op)
2450  
    op_t& op)
2451  
{
2451  
{
2452  
    auto const n0 = impl_.len(first, last);
2452  
    auto const n0 = impl_.len(first, last);
2453  
    if(new_len == 0 && n0 == 0)
2453  
    if(new_len == 0 && n0 == 0)
2454  
        return s_ + impl_.offset(first);
2454  
        return s_ + impl_.offset(first);
2455  
    if(new_len <= n0)
2455  
    if(new_len <= n0)
2456  
        return shrink_impl(
2456  
        return shrink_impl(
2457  
            first, last, new_len, op);
2457  
            first, last, new_len, op);
2458  

2458  

2459  
    // growing
2459  
    // growing
2460  
    std::size_t n = new_len - n0;
2460  
    std::size_t n = new_len - n0;
2461  
    reserve_impl(size() + n, op);
2461  
    reserve_impl(size() + n, op);
2462  
    auto const pos =
2462  
    auto const pos =
2463  
        impl_.offset(last);
2463  
        impl_.offset(last);
2464  
    // adjust chars
2464  
    // adjust chars
2465  
    op.move(
2465  
    op.move(
2466  
        s_ + pos + n,
2466  
        s_ + pos + n,
2467  
        s_ + pos,
2467  
        s_ + pos,
2468  
        impl_.offset(id_end) -
2468  
        impl_.offset(id_end) -
2469  
            pos + 1);
2469  
            pos + 1);
2470  
    // collapse (first, last)
2470  
    // collapse (first, last)
2471  
    impl_.collapse(first, last,
2471  
    impl_.collapse(first, last,
2472  
        impl_.offset(last) + n);
2472  
        impl_.offset(last) + n);
2473  
    // shift (last, end) right
2473  
    // shift (last, end) right
2474  
    impl_.adjust_right(last, id_end, n);
2474  
    impl_.adjust_right(last, id_end, n);
2475  
    s_[size()] = '\0';
2475  
    s_[size()] = '\0';
2476  
    return s_ + impl_.offset(first);
2476  
    return s_ + impl_.offset(first);
2477  
}
2477  
}
2478  

2478  

2479  
inline
2479  
inline
2480  
char*
2480  
char*
2481  
url_base::
2481  
url_base::
2482  
shrink_impl(
2482  
shrink_impl(
2483  
    int id,
2483  
    int id,
2484  
    std::size_t new_size,
2484  
    std::size_t new_size,
2485  
    op_t& op)
2485  
    op_t& op)
2486  
{
2486  
{
2487  
    return shrink_impl(
2487  
    return shrink_impl(
2488  
        id, id + 1, new_size, op);
2488  
        id, id + 1, new_size, op);
2489  
}
2489  
}
2490  

2490  

2491  
inline
2491  
inline
2492  
char*
2492  
char*
2493  
url_base::
2493  
url_base::
2494  
shrink_impl(
2494  
shrink_impl(
2495  
    int first,
2495  
    int first,
2496  
    int last,
2496  
    int last,
2497  
    std::size_t new_len,
2497  
    std::size_t new_len,
2498  
    op_t& op)
2498  
    op_t& op)
2499  
{
2499  
{
2500  
    // shrinking
2500  
    // shrinking
2501  
    auto const n0 = impl_.len(first, last);
2501  
    auto const n0 = impl_.len(first, last);
2502  
    BOOST_ASSERT(new_len <= n0);
2502  
    BOOST_ASSERT(new_len <= n0);
2503  
    std::size_t n = n0 - new_len;
2503  
    std::size_t n = n0 - new_len;
2504  
    auto const pos =
2504  
    auto const pos =
2505  
        impl_.offset(last);
2505  
        impl_.offset(last);
2506  
    // adjust chars
2506  
    // adjust chars
2507  
    op.move(
2507  
    op.move(
2508  
        s_ + pos - n,
2508  
        s_ + pos - n,
2509  
        s_ + pos,
2509  
        s_ + pos,
2510  
        impl_.offset(
2510  
        impl_.offset(
2511  
            id_end) - pos + 1);
2511  
            id_end) - pos + 1);
2512  
    // collapse (first, last)
2512  
    // collapse (first, last)
2513  
    impl_.collapse(first,  last,
2513  
    impl_.collapse(first,  last,
2514  
        impl_.offset(last) - n);
2514  
        impl_.offset(last) - n);
2515  
    // shift (last, end) left
2515  
    // shift (last, end) left
2516  
    impl_.adjust_left(last, id_end, n);
2516  
    impl_.adjust_left(last, id_end, n);
2517  
    s_[size()] = '\0';
2517  
    s_[size()] = '\0';
2518  
    return s_ + impl_.offset(first);
2518  
    return s_ + impl_.offset(first);
2519  
}
2519  
}
2520  

2520  

2521  
//------------------------------------------------
2521  
//------------------------------------------------
2522  

2522  

2523  
inline
2523  
inline
2524  
void
2524  
void
2525  
url_base::
2525  
url_base::
2526  
set_scheme_impl(
2526  
set_scheme_impl(
2527  
    core::string_view s,
2527  
    core::string_view s,
2528  
    urls::scheme id)
2528  
    urls::scheme id)
2529  
{
2529  
{
2530  
    op_t op(*this, &s);
2530  
    op_t op(*this, &s);
2531  
    check_invariants();
2531  
    check_invariants();
2532  
    grammar::parse(
2532  
    grammar::parse(
2533  
        s, detail::scheme_rule()
2533  
        s, detail::scheme_rule()
2534  
            ).value(BOOST_URL_POS);
2534  
            ).value(BOOST_URL_POS);
2535  
    auto const n = s.size();
2535  
    auto const n = s.size();
2536  
    auto const p = impl_.offset(id_path);
2536  
    auto const p = impl_.offset(id_path);
2537  

2537  

2538  
    // check for "./" prefix
2538  
    // check for "./" prefix
2539  
    bool const has_dot =
2539  
    bool const has_dot =
2540  
        [this, p]
2540  
        [this, p]
2541  
    {
2541  
    {
2542  
        if(impl_.nseg_ == 0)
2542  
        if(impl_.nseg_ == 0)
2543  
            return false;
2543  
            return false;
2544  
        if(first_segment().size() < 2)
2544  
        if(first_segment().size() < 2)
2545  
            return false;
2545  
            return false;
2546  
        auto const src = s_ + p;
2546  
        auto const src = s_ + p;
2547  
        if(src[0] != '.')
2547  
        if(src[0] != '.')
2548  
            return false;
2548  
            return false;
2549  
        if(src[1] != '/')
2549  
        if(src[1] != '/')
2550  
            return false;
2550  
            return false;
2551  
        return true;
2551  
        return true;
2552  
    }();
2552  
    }();
2553  

2553  

2554  
    // Remove "./"
2554  
    // Remove "./"
2555  
    if(has_dot)
2555  
    if(has_dot)
2556  
    {
2556  
    {
2557  
        // do this first, for
2557  
        // do this first, for
2558  
        // strong exception safety
2558  
        // strong exception safety
2559  
        reserve_impl(
2559  
        reserve_impl(
2560  
            size() + n + 1 - 2, op);
2560  
            size() + n + 1 - 2, op);
2561  
        op.move(
2561  
        op.move(
2562  
            s_ + p,
2562  
            s_ + p,
2563  
            s_ + p + 2,
2563  
            s_ + p + 2,
2564  
            size() + 1 -
2564  
            size() + 1 -
2565  
                (p + 2));
2565  
                (p + 2));
2566  
        impl_.set_size(
2566  
        impl_.set_size(
2567  
            id_path,
2567  
            id_path,
2568  
            impl_.len(id_path) - 2);
2568  
            impl_.len(id_path) - 2);
2569  
        s_[size()] = '\0';
2569  
        s_[size()] = '\0';
2570  
    }
2570  
    }
2571  

2571  

2572  
    auto dest = resize_impl(
2572  
    auto dest = resize_impl(
2573  
        id_scheme, n + 1, op);
2573  
        id_scheme, n + 1, op);
2574  
    s.copy(dest, n);
2574  
    s.copy(dest, n);
2575  
    dest[n] = ':';
2575  
    dest[n] = ':';
2576  
    impl_.scheme_ = id;
2576  
    impl_.scheme_ = id;
2577  
    check_invariants();
2577  
    check_invariants();
2578  
}
2578  
}
2579  

2579  

2580  
inline
2580  
inline
2581  
char*
2581  
char*
2582  
url_base::
2582  
url_base::
2583  
set_user_impl(
2583  
set_user_impl(
2584  
    std::size_t n,
2584  
    std::size_t n,
2585  
    op_t& op)
2585  
    op_t& op)
2586  
{
2586  
{
2587  
    check_invariants();
2587  
    check_invariants();
2588  
    if(impl_.len(id_pass) != 0)
2588  
    if(impl_.len(id_pass) != 0)
2589  
    {
2589  
    {
2590  
        // keep "//"
2590  
        // keep "//"
2591  
        auto dest = resize_impl(
2591  
        auto dest = resize_impl(
2592  
            id_user, 2 + n, op);
2592  
            id_user, 2 + n, op);
2593  
        check_invariants();
2593  
        check_invariants();
2594  
        return dest + 2;
2594  
        return dest + 2;
2595  
    }
2595  
    }
2596  
    // add authority
2596  
    // add authority
2597  
    bool const make_absolute =
2597  
    bool const make_absolute =
2598  
        !is_path_absolute() &&
2598  
        !is_path_absolute() &&
2599  
        !impl_.get(id_path).empty();
2599  
        !impl_.get(id_path).empty();
2600  
    auto dest = resize_impl(
2600  
    auto dest = resize_impl(
2601  
        id_user, 2 + n + 1 + make_absolute, op);
2601  
        id_user, 2 + n + 1 + make_absolute, op);
2602  
    impl_.split(id_user, 2 + n);
2602  
    impl_.split(id_user, 2 + n);
2603  
    dest[0] = '/';
2603  
    dest[0] = '/';
2604  
    dest[1] = '/';
2604  
    dest[1] = '/';
2605  
    dest[2 + n] = '@';
2605  
    dest[2 + n] = '@';
2606  
    if (make_absolute)
2606  
    if (make_absolute)
2607  
    {
2607  
    {
2608  
        impl_.split(id_pass, 1);
2608  
        impl_.split(id_pass, 1);
2609  
        impl_.split(id_host, 0);
2609  
        impl_.split(id_host, 0);
2610  
        impl_.split(id_port, 0);
2610  
        impl_.split(id_port, 0);
2611  
        dest[3 + n] = '/';
2611  
        dest[3 + n] = '/';
2612  
    }
2612  
    }
2613  
    check_invariants();
2613  
    check_invariants();
2614  
    return dest + 2;
2614  
    return dest + 2;
2615  
}
2615  
}
2616  

2616  

2617  
inline
2617  
inline
2618  
char*
2618  
char*
2619  
url_base::
2619  
url_base::
2620  
set_password_impl(
2620  
set_password_impl(
2621  
    std::size_t n,
2621  
    std::size_t n,
2622  
    op_t& op)
2622  
    op_t& op)
2623  
{
2623  
{
2624  
    check_invariants();
2624  
    check_invariants();
2625  
    if(impl_.len(id_user) != 0)
2625  
    if(impl_.len(id_user) != 0)
2626  
    {
2626  
    {
2627  
        // already have authority
2627  
        // already have authority
2628  
        auto const dest = resize_impl(
2628  
        auto const dest = resize_impl(
2629  
            id_pass, 1 + n + 1, op);
2629  
            id_pass, 1 + n + 1, op);
2630  
        dest[0] = ':';
2630  
        dest[0] = ':';
2631  
        dest[n + 1] = '@';
2631  
        dest[n + 1] = '@';
2632  
        check_invariants();
2632  
        check_invariants();
2633  
        return dest + 1;
2633  
        return dest + 1;
2634  
    }
2634  
    }
2635  
    // add authority
2635  
    // add authority
2636  
    bool const make_absolute =
2636  
    bool const make_absolute =
2637  
            !is_path_absolute() &&
2637  
            !is_path_absolute() &&
2638  
            !impl_.get(id_path).empty();
2638  
            !impl_.get(id_path).empty();
2639  
    auto const dest =
2639  
    auto const dest =
2640  
        resize_impl(
2640  
        resize_impl(
2641  
        id_user, id_host,
2641  
        id_user, id_host,
2642  
        2 + 1 + n + 1 + make_absolute, op);
2642  
        2 + 1 + n + 1 + make_absolute, op);
2643  
    impl_.split(id_user, 2);
2643  
    impl_.split(id_user, 2);
2644  
    dest[0] = '/';
2644  
    dest[0] = '/';
2645  
    dest[1] = '/';
2645  
    dest[1] = '/';
2646  
    dest[2] = ':';
2646  
    dest[2] = ':';
2647  
    dest[2 + n + 1] = '@';
2647  
    dest[2 + n + 1] = '@';
2648  
    if (make_absolute)
2648  
    if (make_absolute)
2649  
    {
2649  
    {
2650  
        impl_.split(id_pass, 2 + n);
2650  
        impl_.split(id_pass, 2 + n);
2651  
        impl_.split(id_host, 0);
2651  
        impl_.split(id_host, 0);
2652  
        impl_.split(id_port, 0);
2652  
        impl_.split(id_port, 0);
2653  
        dest[4 + n] = '/';
2653  
        dest[4 + n] = '/';
2654  
    }
2654  
    }
2655  
    check_invariants();
2655  
    check_invariants();
2656  
    return dest + 3;
2656  
    return dest + 3;
2657  
}
2657  
}
2658  

2658  

2659  
inline
2659  
inline
2660  
char*
2660  
char*
2661  
url_base::
2661  
url_base::
2662  
set_userinfo_impl(
2662  
set_userinfo_impl(
2663  
    std::size_t n,
2663  
    std::size_t n,
2664  
    op_t& op)
2664  
    op_t& op)
2665  
{
2665  
{
2666  
    // "//" {dest} "@"
2666  
    // "//" {dest} "@"
2667  
    check_invariants();
2667  
    check_invariants();
2668  
    bool const make_absolute =
2668  
    bool const make_absolute =
2669  
            !is_path_absolute() &&
2669  
            !is_path_absolute() &&
2670  
            !impl_.get(id_path).empty();
2670  
            !impl_.get(id_path).empty();
2671  
    auto dest = resize_impl(
2671  
    auto dest = resize_impl(
2672  
        id_user, id_host, n + 3 + make_absolute, op);
2672  
        id_user, id_host, n + 3 + make_absolute, op);
2673  
    impl_.split(id_user, n + 2);
2673  
    impl_.split(id_user, n + 2);
2674  
    dest[0] = '/';
2674  
    dest[0] = '/';
2675  
    dest[1] = '/';
2675  
    dest[1] = '/';
2676  
    dest[n + 2] = '@';
2676  
    dest[n + 2] = '@';
2677  
    if (make_absolute)
2677  
    if (make_absolute)
2678  
    {
2678  
    {
2679  
        impl_.split(id_pass, 1);
2679  
        impl_.split(id_pass, 1);
2680  
        impl_.split(id_host, 0);
2680  
        impl_.split(id_host, 0);
2681  
        impl_.split(id_port, 0);
2681  
        impl_.split(id_port, 0);
2682  
        dest[3 + n] = '/';
2682  
        dest[3 + n] = '/';
2683  
    }
2683  
    }
2684  
    check_invariants();
2684  
    check_invariants();
2685  
    return dest + 2;
2685  
    return dest + 2;
2686  
}
2686  
}
2687  

2687  

2688  
inline
2688  
inline
2689  
char*
2689  
char*
2690  
url_base::
2690  
url_base::
2691  
set_host_impl(
2691  
set_host_impl(
2692  
    std::size_t n,
2692  
    std::size_t n,
2693  
    op_t& op)
2693  
    op_t& op)
2694  
{
2694  
{
2695  
    check_invariants();
2695  
    check_invariants();
2696  
    if(impl_.len(id_user) == 0)
2696  
    if(impl_.len(id_user) == 0)
2697  
    {
2697  
    {
2698  
        // add authority
2698  
        // add authority
2699  
        bool make_absolute =
2699  
        bool make_absolute =
2700  
            !is_path_absolute() &&
2700  
            !is_path_absolute() &&
2701  
            impl_.len(id_path) != 0;
2701  
            impl_.len(id_path) != 0;
2702  
        auto pn = impl_.len(id_path);
2702  
        auto pn = impl_.len(id_path);
2703  
        auto dest = resize_impl(
2703  
        auto dest = resize_impl(
2704  
            id_user, n + 2 + make_absolute, op);
2704  
            id_user, n + 2 + make_absolute, op);
2705  
        impl_.split(id_user, 2);
2705  
        impl_.split(id_user, 2);
2706  
        impl_.split(id_pass, 0);
2706  
        impl_.split(id_pass, 0);
2707  
        impl_.split(id_host, n);
2707  
        impl_.split(id_host, n);
2708  
        impl_.split(id_port, 0);
2708  
        impl_.split(id_port, 0);
2709  
        impl_.split(id_path, pn + make_absolute);
2709  
        impl_.split(id_path, pn + make_absolute);
2710  
        if (make_absolute)
2710  
        if (make_absolute)
2711  
        {
2711  
        {
2712  
            dest[n + 2] = '/';
2712  
            dest[n + 2] = '/';
2713  
            ++impl_.decoded_[id_path];
2713  
            ++impl_.decoded_[id_path];
2714  
        }
2714  
        }
2715  
        dest[0] = '/';
2715  
        dest[0] = '/';
2716  
        dest[1] = '/';
2716  
        dest[1] = '/';
2717  
        check_invariants();
2717  
        check_invariants();
2718  
        return dest + 2;
2718  
        return dest + 2;
2719  
    }
2719  
    }
2720  
    // already have authority
2720  
    // already have authority
2721  
    auto const dest = resize_impl(
2721  
    auto const dest = resize_impl(
2722  
        id_host, n, op);
2722  
        id_host, n, op);
2723  
    check_invariants();
2723  
    check_invariants();
2724  
    return dest;
2724  
    return dest;
2725  
}
2725  
}
2726  

2726  

2727  
inline
2727  
inline
2728  
char*
2728  
char*
2729  
url_base::
2729  
url_base::
2730  
set_port_impl(
2730  
set_port_impl(
2731  
    std::size_t n,
2731  
    std::size_t n,
2732  
    op_t& op)
2732  
    op_t& op)
2733  
{
2733  
{
2734  
    check_invariants();
2734  
    check_invariants();
2735  
    if(impl_.len(id_user) != 0)
2735  
    if(impl_.len(id_user) != 0)
2736  
    {
2736  
    {
2737  
        // authority exists
2737  
        // authority exists
2738  
        auto dest = resize_impl(
2738  
        auto dest = resize_impl(
2739  
            id_port, n + 1, op);
2739  
            id_port, n + 1, op);
2740  
        dest[0] = ':';
2740  
        dest[0] = ':';
2741  
        check_invariants();
2741  
        check_invariants();
2742  
        return dest + 1;
2742  
        return dest + 1;
2743  
    }
2743  
    }
2744  
    bool make_absolute =
2744  
    bool make_absolute =
2745  
        !is_path_absolute() &&
2745  
        !is_path_absolute() &&
2746  
        impl_.len(id_path) != 0;
2746  
        impl_.len(id_path) != 0;
2747  
    auto dest = resize_impl(
2747  
    auto dest = resize_impl(
2748  
        id_user, 3 + n + make_absolute, op);
2748  
        id_user, 3 + n + make_absolute, op);
2749  
    impl_.split(id_user, 2);
2749  
    impl_.split(id_user, 2);
2750  
    impl_.split(id_pass, 0);
2750  
    impl_.split(id_pass, 0);
2751  
    impl_.split(id_host, 0);
2751  
    impl_.split(id_host, 0);
2752  
    dest[0] = '/';
2752  
    dest[0] = '/';
2753  
    dest[1] = '/';
2753  
    dest[1] = '/';
2754  
    dest[2] = ':';
2754  
    dest[2] = ':';
2755  
    if (make_absolute)
2755  
    if (make_absolute)
2756  
    {
2756  
    {
2757  
        impl_.split(id_port, n + 1);
2757  
        impl_.split(id_port, n + 1);
2758  
        dest[n + 3] = '/';
2758  
        dest[n + 3] = '/';
2759  
        ++impl_.decoded_[id_path];
2759  
        ++impl_.decoded_[id_path];
2760  
    }
2760  
    }
2761  
    check_invariants();
2761  
    check_invariants();
2762  
    return dest + 3;
2762  
    return dest + 3;
2763  
}
2763  
}
2764  

2764  

2765  
inline
2765  
inline
2766  
char*
2766  
char*
2767  
url_base::
2767  
url_base::
2768  
set_path_impl(
2768  
set_path_impl(
2769  
    std::size_t n,
2769  
    std::size_t n,
2770  
    op_t& op)
2770  
    op_t& op)
2771  
{
2771  
{
2772  
    check_invariants();
2772  
    check_invariants();
2773  
    auto const dest = resize_impl(
2773  
    auto const dest = resize_impl(
2774  
        id_path, n, op);
2774  
        id_path, n, op);
2775  
    return dest;
2775  
    return dest;
2776  
}
2776  
}
2777  

2777  

2778  

2778  

2779  
//------------------------------------------------
2779  
//------------------------------------------------
2780  

2780  

2781  
// return the first segment of the path.
2781  
// return the first segment of the path.
2782  
// this is needed for some algorithms.
2782  
// this is needed for some algorithms.
2783  
inline
2783  
inline
2784  
core::string_view
2784  
core::string_view
2785  
url_base::
2785  
url_base::
2786  
first_segment() const noexcept
2786  
first_segment() const noexcept
2787  
{
2787  
{
2788  
    if(impl_.nseg_ == 0)
2788  
    if(impl_.nseg_ == 0)
2789  
        return {};
2789  
        return {};
2790  
    auto const p0 = impl_.cs_ +
2790  
    auto const p0 = impl_.cs_ +
2791  
        impl_.offset(id_path) +
2791  
        impl_.offset(id_path) +
2792  
            detail::path_prefix(
2792  
            detail::path_prefix(
2793  
                impl_.get(id_path));
2793  
                impl_.get(id_path));
2794  
    auto const end = impl_.cs_ +
2794  
    auto const end = impl_.cs_ +
2795  
        impl_.offset(id_query);
2795  
        impl_.offset(id_query);
2796  
    if(impl_.nseg_ == 1)
2796  
    if(impl_.nseg_ == 1)
2797  
        return core::string_view(
2797  
        return core::string_view(
2798  
            p0, end - p0);
2798  
            p0, end - p0);
2799  
    auto p = p0;
2799  
    auto p = p0;
2800  
    while(*p != '/')
2800  
    while(*p != '/')
2801  
    {
2801  
    {
2802  
        BOOST_ASSERT(p < end);
2802  
        BOOST_ASSERT(p < end);
2803  
        ++p;
2803  
        ++p;
2804  
    }
2804  
    }
2805  
    return core::string_view(p0, p - p0);
2805  
    return core::string_view(p0, p - p0);
2806  
}
2806  
}
2807  

2807  

2808  
inline
2808  
inline
2809  
detail::segments_iter_impl
2809  
detail::segments_iter_impl
2810  
url_base::
2810  
url_base::
2811  
edit_segments(
2811  
edit_segments(
2812  
    detail::segments_iter_impl const& it0,
2812  
    detail::segments_iter_impl const& it0,
2813  
    detail::segments_iter_impl const& it1,
2813  
    detail::segments_iter_impl const& it1,
2814  
    detail::any_segments_iter&& src,
2814  
    detail::any_segments_iter&& src,
2815  
    // -1 = preserve
2815  
    // -1 = preserve
2816  
    //  0 = make relative (can fail)
2816  
    //  0 = make relative (can fail)
2817  
    //  1 = make absolute
2817  
    //  1 = make absolute
2818  
    int absolute)
2818  
    int absolute)
2819  
{
2819  
{
2820  
    // Iterator doesn't belong to this url
2820  
    // Iterator doesn't belong to this url
2821  
    BOOST_ASSERT(it0.ref.alias_of(impl_));
2821  
    BOOST_ASSERT(it0.ref.alias_of(impl_));
2822  

2822  

2823  
    // Iterator doesn't belong to this url
2823  
    // Iterator doesn't belong to this url
2824  
    BOOST_ASSERT(it1.ref.alias_of(impl_));
2824  
    BOOST_ASSERT(it1.ref.alias_of(impl_));
2825  

2825  

2826  
    // Iterator is in the wrong order
2826  
    // Iterator is in the wrong order
2827  
    BOOST_ASSERT(it0.index <= it1.index);
2827  
    BOOST_ASSERT(it0.index <= it1.index);
2828  

2828  

2829  
    // Iterator is out of range
2829  
    // Iterator is out of range
2830  
    BOOST_ASSERT(it0.index <= impl_.nseg_);
2830  
    BOOST_ASSERT(it0.index <= impl_.nseg_);
2831  
    BOOST_ASSERT(it0.pos <= impl_.len(id_path));
2831  
    BOOST_ASSERT(it0.pos <= impl_.len(id_path));
2832  

2832  

2833  
    // Iterator is out of range
2833  
    // Iterator is out of range
2834  
    BOOST_ASSERT(it1.index <= impl_.nseg_);
2834  
    BOOST_ASSERT(it1.index <= impl_.nseg_);
2835  
    BOOST_ASSERT(it1.pos <= impl_.len(id_path));
2835  
    BOOST_ASSERT(it1.pos <= impl_.len(id_path));
2836  

2836  

2837  
//------------------------------------------------
2837  
//------------------------------------------------
2838  
//
2838  
//
2839  
//  Calculate output prefix
2839  
//  Calculate output prefix
2840  
//
2840  
//
2841  
//  0 = ""
2841  
//  0 = ""
2842  
//  1 = "/"
2842  
//  1 = "/"
2843  
//  2 = "./"
2843  
//  2 = "./"
2844  
//  3 = "/./"
2844  
//  3 = "/./"
2845  
//
2845  
//
2846  
    bool const is_abs = is_path_absolute();
2846  
    bool const is_abs = is_path_absolute();
2847  
    if(has_authority())
2847  
    if(has_authority())
2848  
    {
2848  
    {
2849  
        // Check if the new
2849  
        // Check if the new
2850  
        // path would be empty
2850  
        // path would be empty
2851  
        if( src.fast_nseg == 0 &&
2851  
        if( src.fast_nseg == 0 &&
2852  
            it0.index == 0 &&
2852  
            it0.index == 0 &&
2853  
            it1.index == impl_.nseg_)
2853  
            it1.index == impl_.nseg_)
2854  
        {
2854  
        {
2855  
            // VFALCO we don't have
2855  
            // VFALCO we don't have
2856  
            // access to nchar this early
2856  
            // access to nchar this early
2857  
            //
2857  
            //
2858  
            //BOOST_ASSERT(nchar == 0);
2858  
            //BOOST_ASSERT(nchar == 0);
2859  
            absolute = 0;
2859  
            absolute = 0;
2860  
        }
2860  
        }
2861  
        else
2861  
        else
2862  
        {
2862  
        {
2863  
            // prefix "/" required
2863  
            // prefix "/" required
2864  
            absolute = 1;
2864  
            absolute = 1;
2865  
        }
2865  
        }
2866  
    }
2866  
    }
2867  
    else if(absolute < 0)
2867  
    else if(absolute < 0)
2868  
    {
2868  
    {
2869  
        absolute = is_abs; // preserve
2869  
        absolute = is_abs; // preserve
2870  
    }
2870  
    }
2871  
    auto const path_pos = impl_.offset(id_path);
2871  
    auto const path_pos = impl_.offset(id_path);
2872  

2872  

2873  
    std::size_t nchar = 0;
2873  
    std::size_t nchar = 0;
2874  
    std::size_t prefix = 0;
2874  
    std::size_t prefix = 0;
2875  
    bool encode_colons = false;
2875  
    bool encode_colons = false;
2876  
    bool cp_src_prefix = false;
2876  
    bool cp_src_prefix = false;
2877  
    if(it0.index > 0)
2877  
    if(it0.index > 0)
2878  
    {
2878  
    {
2879  
        // first segment unchanged
2879  
        // first segment unchanged
2880  
        prefix = src.fast_nseg > 0;
2880  
        prefix = src.fast_nseg > 0;
2881  
    }
2881  
    }
2882  
    else if(src.fast_nseg > 0)
2882  
    else if(src.fast_nseg > 0)
2883  
    {
2883  
    {
2884  
        // first segment from src
2884  
        // first segment from src
2885  
        if(! src.front.empty())
2885  
        if(! src.front.empty())
2886  
        {
2886  
        {
2887  
            if( src.front == "." &&
2887  
            if( src.front == "." &&
2888  
                    src.fast_nseg > 1)
2888  
                    src.fast_nseg > 1)
2889  
                if (src.s.empty())
2889  
                if (src.s.empty())
2890  
                {
2890  
                {
2891  
                    // if front is ".", we need the extra "." in the prefix
2891  
                    // if front is ".", we need the extra "." in the prefix
2892  
                    // which will maintain the invariant that segments represent
2892  
                    // which will maintain the invariant that segments represent
2893  
                    // {"."}
2893  
                    // {"."}
2894  
                    prefix = 2 + absolute;
2894  
                    prefix = 2 + absolute;
2895  
                }
2895  
                }
2896  
                else
2896  
                else
2897  
                {
2897  
                {
2898  
                    // if the "." prefix is explicitly required from set_path
2898  
                    // if the "." prefix is explicitly required from set_path
2899  
                    // we do not include an extra "." segment
2899  
                    // we do not include an extra "." segment
2900  
                    prefix = absolute;
2900  
                    prefix = absolute;
2901  
                    cp_src_prefix = true;
2901  
                    cp_src_prefix = true;
2902  
                }
2902  
                }
2903  
            else if(absolute)
2903  
            else if(absolute)
2904  
                prefix = 1;
2904  
                prefix = 1;
2905  
            else if(has_scheme() ||
2905  
            else if(has_scheme() ||
2906  
                    ! src.front.contains(':'))
2906  
                    ! src.front.contains(':'))
2907  
                prefix = 0;
2907  
                prefix = 0;
2908  
            else
2908  
            else
2909  
            {
2909  
            {
2910  
                prefix = 0;
2910  
                prefix = 0;
2911  
                encode_colons = true;
2911  
                encode_colons = true;
2912  
            }
2912  
            }
2913  
        }
2913  
        }
2914  
        else
2914  
        else
2915  
        {
2915  
        {
2916  
            prefix = 2 + absolute;
2916  
            prefix = 2 + absolute;
2917  
        }
2917  
        }
2918  
    }
2918  
    }
2919  
    else
2919  
    else
2920  
    {
2920  
    {
2921  
        // first segment from it1
2921  
        // first segment from it1
2922  
        auto const p =
2922  
        auto const p =
2923  
            impl_.cs_ + path_pos + it1.pos;
2923  
            impl_.cs_ + path_pos + it1.pos;
2924  
        switch(impl_.cs_ +
2924  
        switch(impl_.cs_ +
2925  
            impl_.offset(id_query) - p)
2925  
            impl_.offset(id_query) - p)
2926  
        {
2926  
        {
2927  
        case 0:
2927  
        case 0:
2928  
            // points to end
2928  
            // points to end
2929  
            prefix = absolute;
2929  
            prefix = absolute;
2930  
            break;
2930  
            break;
2931  
        default:
2931  
        default:
2932  
            BOOST_ASSERT(*p == '/');
2932  
            BOOST_ASSERT(*p == '/');
2933  
            if(p[1] != '/')
2933  
            if(p[1] != '/')
2934  
            {
2934  
            {
2935  
                if(absolute)
2935  
                if(absolute)
2936  
                    prefix = 1;
2936  
                    prefix = 1;
2937  
                else if(has_scheme() ||
2937  
                else if(has_scheme() ||
2938  
                        ! it1.dereference().contains(':'))
2938  
                        ! it1.dereference().contains(':'))
2939  
                    prefix = 0;
2939  
                    prefix = 0;
2940  
                else
2940  
                else
2941  
                    prefix = 2;
2941  
                    prefix = 2;
2942  
                break;
2942  
                break;
2943  
            }
2943  
            }
2944  
            // empty
2944  
            // empty
2945  
            BOOST_FALLTHROUGH;
2945  
            BOOST_FALLTHROUGH;
2946  
        case 1:
2946  
        case 1:
2947  
            // empty
2947  
            // empty
2948  
            BOOST_ASSERT(*p == '/');
2948  
            BOOST_ASSERT(*p == '/');
2949  
            prefix = 2 + absolute;
2949  
            prefix = 2 + absolute;
2950  
            break;
2950  
            break;
2951  
        }
2951  
        }
2952  
    }
2952  
    }
2953  

2953  

2954  
//  append '/' to new segs
2954  
//  append '/' to new segs
2955  
//  if inserting at front.
2955  
//  if inserting at front.
2956  
    std::size_t const suffix =
2956  
    std::size_t const suffix =
2957  
        it1.index == 0 &&
2957  
        it1.index == 0 &&
2958  
        impl_.nseg_ > 0 &&
2958  
        impl_.nseg_ > 0 &&
2959  
        src.fast_nseg > 0;
2959  
        src.fast_nseg > 0;
2960  

2960  

2961  
//------------------------------------------------
2961  
//------------------------------------------------
2962  
//
2962  
//
2963  
//  Measure the number of encoded characters
2963  
//  Measure the number of encoded characters
2964  
//  of output, and the number of inserted
2964  
//  of output, and the number of inserted
2965  
//  segments including internal separators.
2965  
//  segments including internal separators.
2966  
//
2966  
//
2967  
    src.encode_colons = encode_colons;
2967  
    src.encode_colons = encode_colons;
2968  
    std::size_t nseg = 0;
2968  
    std::size_t nseg = 0;
2969  
    if(src.measure(nchar))
2969  
    if(src.measure(nchar))
2970  
    {
2970  
    {
2971  
        src.encode_colons = false;
2971  
        src.encode_colons = false;
2972  
        for(;;)
2972  
        for(;;)
2973  
        {
2973  
        {
2974  
            ++nseg;
2974  
            ++nseg;
2975  
            if(! src.measure(nchar))
2975  
            if(! src.measure(nchar))
2976  
                break;
2976  
                break;
2977  
            ++nchar;
2977  
            ++nchar;
2978  
        }
2978  
        }
2979  
    }
2979  
    }
2980  

2980  

2981  
    switch(src.fast_nseg)
2981  
    switch(src.fast_nseg)
2982  
    {
2982  
    {
2983  
    case 0:
2983  
    case 0:
2984  
        BOOST_ASSERT(nseg == 0);
2984  
        BOOST_ASSERT(nseg == 0);
2985  
        break;
2985  
        break;
2986  
    case 1:
2986  
    case 1:
2987  
        BOOST_ASSERT(nseg == 1);
2987  
        BOOST_ASSERT(nseg == 1);
2988  
        break;
2988  
        break;
2989  
    case 2:
2989  
    case 2:
2990  
        BOOST_ASSERT(nseg >= 2);
2990  
        BOOST_ASSERT(nseg >= 2);
2991  
        break;
2991  
        break;
2992  
    }
2992  
    }
2993  

2993  

2994  
//------------------------------------------------
2994  
//------------------------------------------------
2995  
//
2995  
//
2996  
//  Calculate [pos0, pos1) to remove
2996  
//  Calculate [pos0, pos1) to remove
2997  
//
2997  
//
2998  
    auto pos0 = it0.pos;
2998  
    auto pos0 = it0.pos;
2999  
    if(it0.index == 0)
2999  
    if(it0.index == 0)
3000  
    {
3000  
    {
3001  
        // patch pos for prefix
3001  
        // patch pos for prefix
3002  
        pos0 = 0;
3002  
        pos0 = 0;
3003  
    }
3003  
    }
3004  
    auto pos1 = it1.pos;
3004  
    auto pos1 = it1.pos;
3005  
    if(it1.index == 0)
3005  
    if(it1.index == 0)
3006  
    {
3006  
    {
3007  
        // patch pos for prefix
3007  
        // patch pos for prefix
3008  
        pos1 = detail::path_prefix(
3008  
        pos1 = detail::path_prefix(
3009  
            impl_.get(id_path));
3009  
            impl_.get(id_path));
3010  
    }
3010  
    }
3011  
    else if(
3011  
    else if(
3012  
        it0.index == 0 &&
3012  
        it0.index == 0 &&
3013  
        it1.index < impl_.nseg_ &&
3013  
        it1.index < impl_.nseg_ &&
3014  
        nseg == 0)
3014  
        nseg == 0)
3015  
    {
3015  
    {
3016  
        // Remove the slash from segment it1
3016  
        // Remove the slash from segment it1
3017  
        // if it is becoming the new first
3017  
        // if it is becoming the new first
3018  
        // segment.
3018  
        // segment.
3019  
        ++pos1;
3019  
        ++pos1;
3020  
    }
3020  
    }
3021  
    // calc decoded size of old range
3021  
    // calc decoded size of old range
3022  
    auto const dn0 =
3022  
    auto const dn0 =
3023  
        detail::decode_bytes_unsafe(
3023  
        detail::decode_bytes_unsafe(
3024  
            core::string_view(
3024  
            core::string_view(
3025  
                impl_.cs_ +
3025  
                impl_.cs_ +
3026  
                    impl_.offset(id_path) +
3026  
                    impl_.offset(id_path) +
3027  
                    pos0,
3027  
                    pos0,
3028  
                pos1 - pos0));
3028  
                pos1 - pos0));
3029  

3029  

3030  
//------------------------------------------------
3030  
//------------------------------------------------
3031  
//
3031  
//
3032  
//  Resize
3032  
//  Resize
3033  
//
3033  
//
3034  
    op_t op(*this, &src.s);
3034  
    op_t op(*this, &src.s);
3035  
    char* dest;
3035  
    char* dest;
3036  
    char const* end;
3036  
    char const* end;
3037  
    {
3037  
    {
3038  
        auto const nremove = pos1 - pos0;
3038  
        auto const nremove = pos1 - pos0;
3039  
        // check overflow
3039  
        // check overflow
3040  
        if( nchar <= max_size() && (
3040  
        if( nchar <= max_size() && (
3041  
            prefix + suffix <=
3041  
            prefix + suffix <=
3042  
                max_size() - nchar))
3042  
                max_size() - nchar))
3043  
        {
3043  
        {
3044  
            nchar = prefix + nchar + suffix;
3044  
            nchar = prefix + nchar + suffix;
3045  
            if( nchar <= nremove ||
3045  
            if( nchar <= nremove ||
3046  
                nchar - nremove <=
3046  
                nchar - nremove <=
3047  
                    max_size() - size())
3047  
                    max_size() - size())
3048  
                goto ok;
3048  
                goto ok;
3049  
        }
3049  
        }
3050  
        // too large
3050  
        // too large
3051  
        detail::throw_length_error();
3051  
        detail::throw_length_error();
3052  
    ok:
3052  
    ok:
3053  
        auto const new_size =
3053  
        auto const new_size =
3054  
            size() + nchar - nremove;
3054  
            size() + nchar - nremove;
3055  
        reserve_impl(new_size, op);
3055  
        reserve_impl(new_size, op);
3056  
        dest = s_ + path_pos + pos0;
3056  
        dest = s_ + path_pos + pos0;
3057  
        op.move(
3057  
        op.move(
3058  
            dest + nchar,
3058  
            dest + nchar,
3059  
            s_ + path_pos + pos1,
3059  
            s_ + path_pos + pos1,
3060  
            size() - path_pos - pos1);
3060  
            size() - path_pos - pos1);
3061  
        impl_.set_size(
3061  
        impl_.set_size(
3062  
            id_path,
3062  
            id_path,
3063  
            impl_.len(id_path) + nchar - nremove);
3063  
            impl_.len(id_path) + nchar - nremove);
3064  
        BOOST_ASSERT(size() == new_size);
3064  
        BOOST_ASSERT(size() == new_size);
3065  
        end = dest + nchar;
3065  
        end = dest + nchar;
3066  
        auto const nseg1 =
3066  
        auto const nseg1 =
3067  
            static_cast<std::ptrdiff_t>(impl_.nseg_) +
3067  
            static_cast<std::ptrdiff_t>(impl_.nseg_) +
3068  
            static_cast<std::ptrdiff_t>(nseg) -
3068  
            static_cast<std::ptrdiff_t>(nseg) -
3069  
            static_cast<std::ptrdiff_t>(it1.index) +
3069  
            static_cast<std::ptrdiff_t>(it1.index) +
3070  
            static_cast<std::ptrdiff_t>(it0.index) -
3070  
            static_cast<std::ptrdiff_t>(it0.index) -
3071  
            static_cast<std::ptrdiff_t>(cp_src_prefix);
3071  
            static_cast<std::ptrdiff_t>(cp_src_prefix);
3072  
        BOOST_ASSERT(nseg1 >= 0);
3072  
        BOOST_ASSERT(nseg1 >= 0);
3073  
        impl_.nseg_ = detail::to_size_type(nseg1);
3073  
        impl_.nseg_ = detail::to_size_type(nseg1);
3074  
        if(s_)
3074  
        if(s_)
3075  
            s_[size()] = '\0';
3075  
            s_[size()] = '\0';
3076  
    }
3076  
    }
3077  

3077  

3078  
//------------------------------------------------
3078  
//------------------------------------------------
3079  
//
3079  
//
3080  
//  Output segments and internal separators:
3080  
//  Output segments and internal separators:
3081  
//
3081  
//
3082  
//  prefix [ segment [ '/' segment ] ] suffix
3082  
//  prefix [ segment [ '/' segment ] ] suffix
3083  
//
3083  
//
3084  
    auto const dest0 = dest;
3084  
    auto const dest0 = dest;
3085  
    switch(prefix)
3085  
    switch(prefix)
3086  
    {
3086  
    {
3087  
    case 3:
3087  
    case 3:
3088  
        *dest++ = '/';
3088  
        *dest++ = '/';
3089  
        *dest++ = '.';
3089  
        *dest++ = '.';
3090  
        *dest++ = '/';
3090  
        *dest++ = '/';
3091  
        break;
3091  
        break;
3092  
    case 2:
3092  
    case 2:
3093  
        *dest++ = '.';
3093  
        *dest++ = '.';
3094  
        BOOST_FALLTHROUGH;
3094  
        BOOST_FALLTHROUGH;
3095  
    case 1:
3095  
    case 1:
3096  
        *dest++ = '/';
3096  
        *dest++ = '/';
3097  
        break;
3097  
        break;
3098  
    default:
3098  
    default:
3099  
        break;
3099  
        break;
3100  
    }
3100  
    }
3101  
    src.rewind();
3101  
    src.rewind();
3102  
    if(nseg > 0)
3102  
    if(nseg > 0)
3103  
    {
3103  
    {
3104  
        src.encode_colons = encode_colons;
3104  
        src.encode_colons = encode_colons;
3105  
        for(;;)
3105  
        for(;;)
3106  
        {
3106  
        {
3107  
            src.copy(dest, end);
3107  
            src.copy(dest, end);
3108  
            if(--nseg == 0)
3108  
            if(--nseg == 0)
3109  
                break;
3109  
                break;
3110  
            *dest++ = '/';
3110  
            *dest++ = '/';
3111  
            src.encode_colons = false;
3111  
            src.encode_colons = false;
3112  
        }
3112  
        }
3113  
        if(suffix)
3113  
        if(suffix)
3114  
            *dest++ = '/';
3114  
            *dest++ = '/';
3115  
    }
3115  
    }
3116  
    BOOST_ASSERT(dest == dest0 + nchar);
3116  
    BOOST_ASSERT(dest == dest0 + nchar);
3117  

3117  

3118  
    // calc decoded size of new range,
3118  
    // calc decoded size of new range,
3119  
    auto const dn =
3119  
    auto const dn =
3120  
        detail::decode_bytes_unsafe(
3120  
        detail::decode_bytes_unsafe(
3121  
            core::string_view(dest0, dest - dest0));
3121  
            core::string_view(dest0, dest - dest0));
3122  
    if(dn >= dn0)
3122  
    if(dn >= dn0)
3123  
        impl_.decoded_[id_path] +=
3123  
        impl_.decoded_[id_path] +=
3124  
            detail::to_size_type(dn - dn0);
3124  
            detail::to_size_type(dn - dn0);
3125  
    else
3125  
    else
3126  
        impl_.decoded_[id_path] -=
3126  
        impl_.decoded_[id_path] -=
3127  
            detail::to_size_type(dn0 - dn);
3127  
            detail::to_size_type(dn0 - dn);
3128  

3128  

3129  
    return detail::segments_iter_impl(
3129  
    return detail::segments_iter_impl(
3130  
        impl_, pos0, it0.index);
3130  
        impl_, pos0, it0.index);
3131  
}
3131  
}
3132  

3132  

3133  
//------------------------------------------------
3133  
//------------------------------------------------
3134  

3134  

3135  
inline
3135  
inline
3136  
auto
3136  
auto
3137  
url_base::
3137  
url_base::
3138  
edit_params(
3138  
edit_params(
3139  
    detail::params_iter_impl const& it0,
3139  
    detail::params_iter_impl const& it0,
3140  
    detail::params_iter_impl const& it1,
3140  
    detail::params_iter_impl const& it1,
3141  
    detail::any_params_iter&& src) ->
3141  
    detail::any_params_iter&& src) ->
3142  
        detail::params_iter_impl
3142  
        detail::params_iter_impl
3143  
{
3143  
{
3144  
    auto pos0 = impl_.offset(id_query);
3144  
    auto pos0 = impl_.offset(id_query);
3145  
    auto pos1 = pos0 + it1.pos;
3145  
    auto pos1 = pos0 + it1.pos;
3146  
    pos0 = pos0 + it0.pos;
3146  
    pos0 = pos0 + it0.pos;
3147  

3147  

3148  
    // Iterators belong to this url
3148  
    // Iterators belong to this url
3149  
    BOOST_ASSERT(it0.ref.alias_of(impl_));
3149  
    BOOST_ASSERT(it0.ref.alias_of(impl_));
3150  
    BOOST_ASSERT(it1.ref.alias_of(impl_));
3150  
    BOOST_ASSERT(it1.ref.alias_of(impl_));
3151  

3151  

3152  
    // Iterators is in the right order
3152  
    // Iterators is in the right order
3153  
    BOOST_ASSERT(it0.index <= it1.index);
3153  
    BOOST_ASSERT(it0.index <= it1.index);
3154  

3154  

3155  
    // Iterators are within range
3155  
    // Iterators are within range
3156  
    BOOST_ASSERT(it0.index <= impl_.nparam_);
3156  
    BOOST_ASSERT(it0.index <= impl_.nparam_);
3157  
    BOOST_ASSERT(pos0 <= impl_.offset(id_frag));
3157  
    BOOST_ASSERT(pos0 <= impl_.offset(id_frag));
3158  
    BOOST_ASSERT(it1.index <= impl_.nparam_);
3158  
    BOOST_ASSERT(it1.index <= impl_.nparam_);
3159  
    BOOST_ASSERT(pos1 <= impl_.offset(id_frag));
3159  
    BOOST_ASSERT(pos1 <= impl_.offset(id_frag));
3160  

3160  

3161  
    // calc decoded size of old range,
3161  
    // calc decoded size of old range,
3162  
    // minus one if '?' or '&' prefixed
3162  
    // minus one if '?' or '&' prefixed
3163  
    auto dn0 =
3163  
    auto dn0 =
3164  
        static_cast<std::ptrdiff_t>(
3164  
        static_cast<std::ptrdiff_t>(
3165  
            detail::decode_bytes_unsafe(
3165  
            detail::decode_bytes_unsafe(
3166  
                core::string_view(
3166  
                core::string_view(
3167  
                    impl_.cs_ + pos0,
3167  
                    impl_.cs_ + pos0,
3168  
                    pos1 - pos0)));
3168  
                    pos1 - pos0)));
3169  
    if(impl_.len(id_query) > 0)
3169  
    if(impl_.len(id_query) > 0)
3170  
        dn0 -= 1;
3170  
        dn0 -= 1;
3171  
    if(dn0 < 0)
3171  
    if(dn0 < 0)
3172  
        dn0 = 0;
3172  
        dn0 = 0;
3173  

3173  

3174  
//------------------------------------------------
3174  
//------------------------------------------------
3175  
//
3175  
//
3176  
//  Measure the number of encoded characters
3176  
//  Measure the number of encoded characters
3177  
//  of output, and the number of inserted
3177  
//  of output, and the number of inserted
3178  
//  segments including internal separators.
3178  
//  segments including internal separators.
3179  
//
3179  
//
3180  

3180  

3181  
    std::size_t nchar = 0;
3181  
    std::size_t nchar = 0;
3182  
    std::size_t nparam = 0;
3182  
    std::size_t nparam = 0;
3183  
    if(src.measure(nchar))
3183  
    if(src.measure(nchar))
3184  
    {
3184  
    {
3185  
        ++nchar; // for '?' or '&'
3185  
        ++nchar; // for '?' or '&'
3186  
        for(;;)
3186  
        for(;;)
3187  
        {
3187  
        {
3188  
            ++nparam;
3188  
            ++nparam;
3189  
            if(! src.measure(nchar))
3189  
            if(! src.measure(nchar))
3190  
                break;
3190  
                break;
3191  
            ++nchar; // for '&'
3191  
            ++nchar; // for '&'
3192  
        }
3192  
        }
3193  
    }
3193  
    }
3194  

3194  

3195  
//------------------------------------------------
3195  
//------------------------------------------------
3196  
//
3196  
//
3197  
//  Resize
3197  
//  Resize
3198  
//
3198  
//
3199  
    op_t op(*this, &src.s0, &src.s1);
3199  
    op_t op(*this, &src.s0, &src.s1);
3200  
    char* dest;
3200  
    char* dest;
3201  
    char const* end;
3201  
    char const* end;
3202  
    {
3202  
    {
3203  
        auto const nremove = pos1 - pos0;
3203  
        auto const nremove = pos1 - pos0;
3204  
        // check overflow
3204  
        // check overflow
3205  
        if( nchar > nremove &&
3205  
        if( nchar > nremove &&
3206  
            nchar - nremove >
3206  
            nchar - nremove >
3207  
                max_size() - size())
3207  
                max_size() - size())
3208  
        {
3208  
        {
3209  
            // too large
3209  
            // too large
3210  
            detail::throw_length_error();
3210  
            detail::throw_length_error();
3211  
        }
3211  
        }
3212  
        auto const nparam1 =
3212  
        auto const nparam1 =
3213  
            static_cast<std::ptrdiff_t>(impl_.nparam_) +
3213  
            static_cast<std::ptrdiff_t>(impl_.nparam_) +
3214  
            static_cast<std::ptrdiff_t>(nparam) -
3214  
            static_cast<std::ptrdiff_t>(nparam) -
3215  
            static_cast<std::ptrdiff_t>(it1.index) +
3215  
            static_cast<std::ptrdiff_t>(it1.index) +
3216  
            static_cast<std::ptrdiff_t>(it0.index);
3216  
            static_cast<std::ptrdiff_t>(it0.index);
3217  
        BOOST_ASSERT(nparam1 >= 0);
3217  
        BOOST_ASSERT(nparam1 >= 0);
3218  
        reserve_impl(size() + nchar - nremove, op);
3218  
        reserve_impl(size() + nchar - nremove, op);
3219  
        dest = s_ + pos0;
3219  
        dest = s_ + pos0;
3220  
        end = dest + nchar;
3220  
        end = dest + nchar;
3221  
        if(impl_.nparam_ > 0)
3221  
        if(impl_.nparam_ > 0)
3222  
        {
3222  
        {
3223  
            // needed when we move
3223  
            // needed when we move
3224  
            // the beginning of the query
3224  
            // the beginning of the query
3225  
            s_[impl_.offset(id_query)] = '&';
3225  
            s_[impl_.offset(id_query)] = '&';
3226  
        }
3226  
        }
3227  
        op.move(
3227  
        op.move(
3228  
            dest + nchar,
3228  
            dest + nchar,
3229  
            impl_.cs_ + pos1,
3229  
            impl_.cs_ + pos1,
3230  
            size() - pos1);
3230  
            size() - pos1);
3231  
        impl_.set_size(
3231  
        impl_.set_size(
3232  
            id_query,
3232  
            id_query,
3233  
            impl_.len(id_query) +
3233  
            impl_.len(id_query) +
3234  
                nchar - nremove);
3234  
                nchar - nremove);
3235  
        impl_.nparam_ =
3235  
        impl_.nparam_ =
3236  
            detail::to_size_type(nparam1);
3236  
            detail::to_size_type(nparam1);
3237  
        if(nparam1 > 0)
3237  
        if(nparam1 > 0)
3238  
        {
3238  
        {
3239  
            // needed when we erase
3239  
            // needed when we erase
3240  
            // the beginning of the query
3240  
            // the beginning of the query
3241  
            s_[impl_.offset(id_query)] = '?';
3241  
            s_[impl_.offset(id_query)] = '?';
3242  
        }
3242  
        }
3243  
        if(s_)
3243  
        if(s_)
3244  
            s_[size()] = '\0';
3244  
            s_[size()] = '\0';
3245  
    }
3245  
    }
3246  
    auto const dest0 = dest;
3246  
    auto const dest0 = dest;
3247  

3247  

3248  
//------------------------------------------------
3248  
//------------------------------------------------
3249  
//
3249  
//
3250  
//  Output params and internal separators:
3250  
//  Output params and internal separators:
3251  
//
3251  
//
3252  
//  [ '?' param ] [ '&' param ]
3252  
//  [ '?' param ] [ '&' param ]
3253  
//
3253  
//
3254  
    if(nparam > 0)
3254  
    if(nparam > 0)
3255  
    {
3255  
    {
3256  
        if(it0.index == 0)
3256  
        if(it0.index == 0)
3257  
            *dest++ = '?';
3257  
            *dest++ = '?';
3258  
        else
3258  
        else
3259  
            *dest++ = '&';
3259  
            *dest++ = '&';
3260  
        src.rewind();
3260  
        src.rewind();
3261  
        for(;;)
3261  
        for(;;)
3262  
        {
3262  
        {
3263  
            src.copy(dest, end);
3263  
            src.copy(dest, end);
3264  
            if(--nparam == 0)
3264  
            if(--nparam == 0)
3265  
                break;
3265  
                break;
3266  
            *dest++ = '&';
3266  
            *dest++ = '&';
3267  
        }
3267  
        }
3268  
    }
3268  
    }
3269  

3269  

3270  
    // calc decoded size of new range,
3270  
    // calc decoded size of new range,
3271  
    // minus one if '?' or '&' prefixed
3271  
    // minus one if '?' or '&' prefixed
3272  
    auto dn =
3272  
    auto dn =
3273  
        static_cast<std::ptrdiff_t>(
3273  
        static_cast<std::ptrdiff_t>(
3274  
            detail::decode_bytes_unsafe(
3274  
            detail::decode_bytes_unsafe(
3275  
                core::string_view(dest0, dest - dest0)));
3275  
                core::string_view(dest0, dest - dest0)));
3276  
    if(impl_.len(id_query) > 0)
3276  
    if(impl_.len(id_query) > 0)
3277  
        dn -= 1;
3277  
        dn -= 1;
3278  
    if(dn < 0)
3278  
    if(dn < 0)
3279  
        dn = 0;
3279  
        dn = 0;
3280  

3280  

3281  
    if(dn >= dn0)
3281  
    if(dn >= dn0)
3282  
        impl_.decoded_[id_query] +=
3282  
        impl_.decoded_[id_query] +=
3283  
            detail::to_size_type(dn - dn0);
3283  
            detail::to_size_type(dn - dn0);
3284  
    else
3284  
    else
3285  
        impl_.decoded_[id_query] -=
3285  
        impl_.decoded_[id_query] -=
3286  
            detail::to_size_type(dn0 - dn);
3286  
            detail::to_size_type(dn0 - dn);
3287  

3287  

3288  
    return detail::params_iter_impl(
3288  
    return detail::params_iter_impl(
3289  
        impl_,
3289  
        impl_,
3290  
        pos0 - impl_.offset_[id_query],
3290  
        pos0 - impl_.offset_[id_query],
3291  
        it0.index);
3291  
        it0.index);
3292  
}
3292  
}
3293  

3293  

3294  
//------------------------------------------------
3294  
//------------------------------------------------
3295  

3295  

3296  
inline
3296  
inline
3297  
void
3297  
void
3298  
url_base::
3298  
url_base::
3299  
decoded_to_lower_impl(int id) noexcept
3299  
decoded_to_lower_impl(int id) noexcept
3300  
{
3300  
{
3301  
    char* it = s_ + impl_.offset(id);
3301  
    char* it = s_ + impl_.offset(id);
3302  
    char const* const end = s_ + impl_.offset(id + 1);
3302  
    char const* const end = s_ + impl_.offset(id + 1);
3303  
    while(it < end)
3303  
    while(it < end)
3304  
    {
3304  
    {
3305  
        if (*it != '%')
3305  
        if (*it != '%')
3306  
        {
3306  
        {
3307  
            *it = grammar::to_lower(
3307  
            *it = grammar::to_lower(
3308  
                *it);
3308  
                *it);
3309  
            ++it;
3309  
            ++it;
3310  
            continue;
3310  
            continue;
3311  
        }
3311  
        }
3312  
        it += 3;
3312  
        it += 3;
3313  
    }
3313  
    }
3314  
}
3314  
}
3315  

3315  

3316  
inline
3316  
inline
3317  
void
3317  
void
3318  
url_base::
3318  
url_base::
3319  
to_lower_impl(int id) noexcept
3319  
to_lower_impl(int id) noexcept
3320  
{
3320  
{
3321  
    char* it = s_ + impl_.offset(id);
3321  
    char* it = s_ + impl_.offset(id);
3322  
    char const* const end = s_ + impl_.offset(id + 1);
3322  
    char const* const end = s_ + impl_.offset(id + 1);
3323  
    while(it < end)
3323  
    while(it < end)
3324  
    {
3324  
    {
3325  
        *it = grammar::to_lower(
3325  
        *it = grammar::to_lower(
3326  
            *it);
3326  
            *it);
3327  
        ++it;
3327  
        ++it;
3328  
    }
3328  
    }
3329  
}
3329  
}
3330  

3330  

3331  
} // urls
3331  
} // urls
3332  
} // boost
3332  
} // boost
3333  

3333  

3334  
#endif
3334  
#endif