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  

11  

12  
#include <boost/url/detail/config.hpp>
12  
#include <boost/url/detail/config.hpp>
13  
#include <boost/url/url_base.hpp>
13  
#include <boost/url/url_base.hpp>
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 "detail/normalize.hpp"
24  
#include "detail/normalize.hpp"
25  
#include "detail/path.hpp"
25  
#include "detail/path.hpp"
26  
#include "detail/print.hpp"
26  
#include "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 "rfc/detail/charsets.hpp"
31  
#include "rfc/detail/charsets.hpp"
32  
#include "rfc/detail/host_rule.hpp"
32  
#include "rfc/detail/host_rule.hpp"
33  
#include "rfc/detail/ipvfuture_rule.hpp"
33  
#include "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 "rfc/detail/port_rule.hpp"
35  
#include "rfc/detail/port_rule.hpp"
36  
#include "rfc/detail/scheme_rule.hpp"
36  
#include "rfc/detail/scheme_rule.hpp"
37  
#include "rfc/detail/userinfo_rule.hpp"
37  
#include "rfc/detail/userinfo_rule.hpp"
38  
#include <boost/url/grammar/parse.hpp>
38  
#include <boost/url/grammar/parse.hpp>
39  
#include "detail/move_chars.hpp"
39  
#include "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  
url_base::
54  
url_base::
55  
op_t::
55  
op_t::
56  
~op_t()
56  
~op_t()
57  
{
57  
{
58  
    if(old)
58  
    if(old)
59  
        u.cleanup(*this);
59  
        u.cleanup(*this);
60  
    u.check_invariants();
60  
    u.check_invariants();
61  
}
61  
}
62  

62  

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

75  

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

97  

98  
//------------------------------------------------
98  
//------------------------------------------------
99  

99  

100  
// construct reference
100  
// construct reference
101  
url_base::
101  
url_base::
102  
url_base(
102  
url_base(
103  
    detail::url_impl const& impl) noexcept
103  
    detail::url_impl const& impl) noexcept
104  
    : url_view_base(impl)
104  
    : url_view_base(impl)
105  
{
105  
{
106  
}
106  
}
107  

107  

108  
void
108  
void
109  
url_base::
109  
url_base::
110  
reserve_impl(std::size_t n)
110  
reserve_impl(std::size_t n)
111  
{
111  
{
112  
    op_t op(*this);
112  
    op_t op(*this);
113  
    reserve_impl(n, op);
113  
    reserve_impl(n, op);
114  
    if(s_)
114  
    if(s_)
115  
        s_[size()] = '\0';
115  
        s_[size()] = '\0';
116  
}
116  
}
117  

117  

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

140  

141  
//------------------------------------------------
141  
//------------------------------------------------
142  
//
142  
//
143  
// Scheme
143  
// Scheme
144  
//
144  
//
145  
//------------------------------------------------
145  
//------------------------------------------------
146  

146  

147  
url_base&
147  
url_base&
148  
url_base::
148  
url_base::
149  
set_scheme(core::string_view s)
149  
set_scheme(core::string_view s)
150  
{
150  
{
151  
    set_scheme_impl(
151  
    set_scheme_impl(
152  
        s, string_to_scheme(s));
152  
        s, string_to_scheme(s));
153  
    return *this;
153  
    return *this;
154  
}
154  
}
155  

155  

156  
url_base&
156  
url_base&
157  
url_base::
157  
url_base::
158  
set_scheme_id(urls::scheme id)
158  
set_scheme_id(urls::scheme id)
159  
{
159  
{
160  
    if(id == urls::scheme::unknown)
160  
    if(id == urls::scheme::unknown)
161  
        detail::throw_invalid_argument();
161  
        detail::throw_invalid_argument();
162  
    if(id == urls::scheme::none)
162  
    if(id == urls::scheme::none)
163  
        return remove_scheme();
163  
        return remove_scheme();
164  
    set_scheme_impl(to_string(id), id);
164  
    set_scheme_impl(to_string(id), id);
165  
    return *this;
165  
    return *this;
166  
}
166  
}
167  

167  

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

221  

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

245  

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

274  

275  
//------------------------------------------------
275  
//------------------------------------------------
276  
//
276  
//
277  
// Authority
277  
// Authority
278  
//
278  
//
279  
//------------------------------------------------
279  
//------------------------------------------------
280  

280  

281  
url_base&
281  
url_base&
282  
url_base::
282  
url_base::
283  
set_encoded_authority(
283  
set_encoded_authority(
284  
    pct_string_view s)
284  
    pct_string_view s)
285  
{
285  
{
286  
    op_t op(*this, &detail::ref(s));
286  
    op_t op(*this, &detail::ref(s));
287  
    authority_view a = grammar::parse(
287  
    authority_view a = grammar::parse(
288  
        s, authority_rule
288  
        s, authority_rule
289  
            ).value(BOOST_URL_POS);
289  
            ).value(BOOST_URL_POS);
290  
    auto n = s.size() + 2;
290  
    auto n = s.size() + 2;
291  
    auto const need_slash =
291  
    auto const need_slash =
292  
        ! is_path_absolute() &&
292  
        ! is_path_absolute() &&
293  
        impl_.len(id_path) > 0;
293  
        impl_.len(id_path) > 0;
294  
    if(need_slash)
294  
    if(need_slash)
295  
        ++n;
295  
        ++n;
296  
    auto dest = resize_impl(
296  
    auto dest = resize_impl(
297  
        id_user, id_path, n, op);
297  
        id_user, id_path, n, op);
298  
    dest[0] = '/';
298  
    dest[0] = '/';
299  
    dest[1] = '/';
299  
    dest[1] = '/';
300  
    std::memcpy(dest + 2,
300  
    std::memcpy(dest + 2,
301  
        s.data(), s.size());
301  
        s.data(), s.size());
302  
    if(need_slash)
302  
    if(need_slash)
303  
        dest[n - 1] = '/';
303  
        dest[n - 1] = '/';
304  
    impl_.apply_authority(a);
304  
    impl_.apply_authority(a);
305  
    if(need_slash)
305  
    if(need_slash)
306  
        impl_.adjust_right(
306  
        impl_.adjust_right(
307  
                id_query, id_end, 1);
307  
                id_query, id_end, 1);
308  
    return *this;
308  
    return *this;
309  
}
309  
}
310  

310  

311  
url_base&
311  
url_base&
312  
url_base::
312  
url_base::
313  
remove_authority()
313  
remove_authority()
314  
{
314  
{
315  
    if(! has_authority())
315  
    if(! has_authority())
316  
        return *this;
316  
        return *this;
317  

317  

318  
    op_t op(*this);
318  
    op_t op(*this);
319  
    auto path = impl_.get(id_path);
319  
    auto path = impl_.get(id_path);
320  
    bool const need_dot = path.starts_with("//");
320  
    bool const need_dot = path.starts_with("//");
321  
    if(need_dot)
321  
    if(need_dot)
322  
    {
322  
    {
323  
        // prepend "/.", can't throw
323  
        // prepend "/.", can't throw
324  
        auto p = resize_impl(
324  
        auto p = resize_impl(
325  
            id_user, id_path, 2, op);
325  
            id_user, id_path, 2, op);
326  
        p[0] = '/';
326  
        p[0] = '/';
327  
        p[1] = '.';
327  
        p[1] = '.';
328  
        impl_.split(id_user, 0);
328  
        impl_.split(id_user, 0);
329  
        impl_.split(id_pass, 0);
329  
        impl_.split(id_pass, 0);
330  
        impl_.split(id_host, 0);
330  
        impl_.split(id_host, 0);
331  
        impl_.split(id_port, 0);
331  
        impl_.split(id_port, 0);
332  
    }
332  
    }
333  
    else
333  
    else
334  
    {
334  
    {
335  
        resize_impl(
335  
        resize_impl(
336  
            id_user, id_path, 0, op);
336  
            id_user, id_path, 0, op);
337  
    }
337  
    }
338  
    impl_.host_type_ =
338  
    impl_.host_type_ =
339  
        urls::host_type::none;
339  
        urls::host_type::none;
340  
    return *this;
340  
    return *this;
341  
}
341  
}
342  

342  

343  
//------------------------------------------------
343  
//------------------------------------------------
344  
//
344  
//
345  
// Userinfo
345  
// Userinfo
346  
//
346  
//
347  
//------------------------------------------------
347  
//------------------------------------------------
348  

348  

349  
url_base&
349  
url_base&
350  
url_base::
350  
url_base::
351  
set_userinfo(
351  
set_userinfo(
352  
    core::string_view s)
352  
    core::string_view s)
353  
{
353  
{
354  
    op_t op(*this, &s);
354  
    op_t op(*this, &s);
355  
    encoding_opts opt;
355  
    encoding_opts opt;
356  
    auto const n = encoded_size(
356  
    auto const n = encoded_size(
357  
        s, detail::userinfo_chars, opt);
357  
        s, detail::userinfo_chars, opt);
358  
    auto dest = set_userinfo_impl(n, op);
358  
    auto dest = set_userinfo_impl(n, op);
359  
    encode(
359  
    encode(
360  
        dest,
360  
        dest,
361  
        n,
361  
        n,
362  
        s,
362  
        s,
363  
        detail::userinfo_chars,
363  
        detail::userinfo_chars,
364  
        opt);
364  
        opt);
365  
    auto const pos = impl_.get(
365  
    auto const pos = impl_.get(
366  
        id_user, id_host
366  
        id_user, id_host
367  
            ).find_first_of(':');
367  
            ).find_first_of(':');
368  
    if(pos != core::string_view::npos)
368  
    if(pos != core::string_view::npos)
369  
    {
369  
    {
370  
        impl_.split(id_user, pos);
370  
        impl_.split(id_user, pos);
371  
        // find ':' in plain string
371  
        // find ':' in plain string
372  
        auto const pos2 =
372  
        auto const pos2 =
373  
            s.find_first_of(':');
373  
            s.find_first_of(':');
374  
        impl_.decoded_[id_user] =
374  
        impl_.decoded_[id_user] =
375  
            pos2 - 1;
375  
            pos2 - 1;
376  
        impl_.decoded_[id_pass] =
376  
        impl_.decoded_[id_pass] =
377  
            s.size() - pos2;
377  
            s.size() - pos2;
378  
    }
378  
    }
379  
    else
379  
    else
380  
    {
380  
    {
381  
        impl_.decoded_[id_user] = s.size();
381  
        impl_.decoded_[id_user] = s.size();
382  
        impl_.decoded_[id_pass] = 0;
382  
        impl_.decoded_[id_pass] = 0;
383  
    }
383  
    }
384  
    return *this;
384  
    return *this;
385  
}
385  
}
386  

386  

387  
url_base&
387  
url_base&
388  
url_base::
388  
url_base::
389  
set_encoded_userinfo(
389  
set_encoded_userinfo(
390  
    pct_string_view s)
390  
    pct_string_view s)
391  
{
391  
{
392  
    op_t op(*this, &detail::ref(s));
392  
    op_t op(*this, &detail::ref(s));
393  
    auto const pos = s.find_first_of(':');
393  
    auto const pos = s.find_first_of(':');
394  
    if(pos != core::string_view::npos)
394  
    if(pos != core::string_view::npos)
395  
    {
395  
    {
396  
        // user:pass
396  
        // user:pass
397  
        auto const s0 = s.substr(0, pos);
397  
        auto const s0 = s.substr(0, pos);
398  
        auto const s1 = s.substr(pos + 1);
398  
        auto const s1 = s.substr(pos + 1);
399  
        auto const n0 =
399  
        auto const n0 =
400  
            detail::re_encoded_size_unsafe(
400  
            detail::re_encoded_size_unsafe(
401  
                s0,
401  
                s0,
402  
                detail::user_chars);
402  
                detail::user_chars);
403  
        auto const n1 =
403  
        auto const n1 =
404  
            detail::re_encoded_size_unsafe(s1,
404  
            detail::re_encoded_size_unsafe(s1,
405  
                detail::password_chars);
405  
                detail::password_chars);
406  
        auto dest =
406  
        auto dest =
407  
            set_userinfo_impl(n0 + n1 + 1, op);
407  
            set_userinfo_impl(n0 + n1 + 1, op);
408  
        impl_.decoded_[id_user] =
408  
        impl_.decoded_[id_user] =
409  
            detail::re_encode_unsafe(
409  
            detail::re_encode_unsafe(
410  
                dest,
410  
                dest,
411  
                dest + n0,
411  
                dest + n0,
412  
                s0,
412  
                s0,
413  
                detail::user_chars);
413  
                detail::user_chars);
414  
        *dest++ = ':';
414  
        *dest++ = ':';
415  
        impl_.decoded_[id_pass] =
415  
        impl_.decoded_[id_pass] =
416  
            detail::re_encode_unsafe(
416  
            detail::re_encode_unsafe(
417  
                dest,
417  
                dest,
418  
                dest + n1,
418  
                dest + n1,
419  
                s1,
419  
                s1,
420  
                detail::password_chars);
420  
                detail::password_chars);
421  
        impl_.split(id_user, 2 + n0);
421  
        impl_.split(id_user, 2 + n0);
422  
    }
422  
    }
423  
    else
423  
    else
424  
    {
424  
    {
425  
        // user
425  
        // user
426  
        auto const n =
426  
        auto const n =
427  
            detail::re_encoded_size_unsafe(
427  
            detail::re_encoded_size_unsafe(
428  
                s, detail::user_chars);
428  
                s, detail::user_chars);
429  
        auto dest = set_userinfo_impl(n, op);
429  
        auto dest = set_userinfo_impl(n, op);
430  
        impl_.decoded_[id_user] =
430  
        impl_.decoded_[id_user] =
431  
            detail::re_encode_unsafe(
431  
            detail::re_encode_unsafe(
432  
                dest,
432  
                dest,
433  
                dest + n,
433  
                dest + n,
434  
                s,
434  
                s,
435  
                detail::user_chars);
435  
                detail::user_chars);
436  
        impl_.split(id_user, 2 + n);
436  
        impl_.split(id_user, 2 + n);
437  
        impl_.decoded_[id_pass] = 0;
437  
        impl_.decoded_[id_pass] = 0;
438  
    }
438  
    }
439  
    return *this;
439  
    return *this;
440  
}
440  
}
441  

441  

442  
url_base&
442  
url_base&
443  
url_base::
443  
url_base::
444  
remove_userinfo() noexcept
444  
remove_userinfo() noexcept
445  
{
445  
{
446  
    if(impl_.len(id_pass) == 0)
446  
    if(impl_.len(id_pass) == 0)
447  
        return *this; // no userinfo
447  
        return *this; // no userinfo
448  

448  

449  
    op_t op(*this);
449  
    op_t op(*this);
450  
    // keep authority '//'
450  
    // keep authority '//'
451  
    resize_impl(
451  
    resize_impl(
452  
        id_user, id_host, 2, op);
452  
        id_user, id_host, 2, op);
453  
    impl_.decoded_[id_user] = 0;
453  
    impl_.decoded_[id_user] = 0;
454  
    impl_.decoded_[id_pass] = 0;
454  
    impl_.decoded_[id_pass] = 0;
455  
    return *this;
455  
    return *this;
456  
}
456  
}
457  

457  

458  
//------------------------------------------------
458  
//------------------------------------------------
459  

459  

460  
url_base&
460  
url_base&
461  
url_base::
461  
url_base::
462  
set_user(core::string_view s)
462  
set_user(core::string_view s)
463  
{
463  
{
464  
    op_t op(*this, &s);
464  
    op_t op(*this, &s);
465  
    encoding_opts opt;
465  
    encoding_opts opt;
466  
    auto const n = encoded_size(
466  
    auto const n = encoded_size(
467  
        s, detail::user_chars, opt);
467  
        s, detail::user_chars, opt);
468  
    auto dest = set_user_impl(n, op);
468  
    auto dest = set_user_impl(n, op);
469  
    encode_unsafe(
469  
    encode_unsafe(
470  
        dest,
470  
        dest,
471  
        n,
471  
        n,
472  
        s,
472  
        s,
473  
        detail::user_chars,
473  
        detail::user_chars,
474  
        opt);
474  
        opt);
475  
    impl_.decoded_[id_user] = s.size();
475  
    impl_.decoded_[id_user] = s.size();
476  
    return *this;
476  
    return *this;
477  
}
477  
}
478  

478  

479  
url_base&
479  
url_base&
480  
url_base::
480  
url_base::
481  
set_encoded_user(
481  
set_encoded_user(
482  
    pct_string_view s)
482  
    pct_string_view s)
483  
{
483  
{
484  
    op_t op(*this, &detail::ref(s));
484  
    op_t op(*this, &detail::ref(s));
485  
    auto const n =
485  
    auto const n =
486  
        detail::re_encoded_size_unsafe(
486  
        detail::re_encoded_size_unsafe(
487  
            s, detail::user_chars);
487  
            s, detail::user_chars);
488  
    auto dest = set_user_impl(n, op);
488  
    auto dest = set_user_impl(n, op);
489  
    impl_.decoded_[id_user] =
489  
    impl_.decoded_[id_user] =
490  
        detail::re_encode_unsafe(
490  
        detail::re_encode_unsafe(
491  
            dest,
491  
            dest,
492  
            dest + n,
492  
            dest + n,
493  
            s,
493  
            s,
494  
            detail::user_chars);
494  
            detail::user_chars);
495  
    BOOST_ASSERT(
495  
    BOOST_ASSERT(
496  
        impl_.decoded_[id_user] ==
496  
        impl_.decoded_[id_user] ==
497  
            s.decoded_size());
497  
            s.decoded_size());
498  
    return *this;
498  
    return *this;
499  
}
499  
}
500  

500  

501  
//------------------------------------------------
501  
//------------------------------------------------
502  

502  

503  
url_base&
503  
url_base&
504  
url_base::
504  
url_base::
505  
set_password(core::string_view s)
505  
set_password(core::string_view s)
506  
{
506  
{
507  
    op_t op(*this, &s);
507  
    op_t op(*this, &s);
508  
    encoding_opts opt;
508  
    encoding_opts opt;
509  
    auto const n = encoded_size(
509  
    auto const n = encoded_size(
510  
        s, detail::password_chars, opt);
510  
        s, detail::password_chars, opt);
511  
    auto dest = set_password_impl(n, op);
511  
    auto dest = set_password_impl(n, op);
512  
    encode_unsafe(
512  
    encode_unsafe(
513  
        dest,
513  
        dest,
514  
        n,
514  
        n,
515  
        s,
515  
        s,
516  
        detail::password_chars,
516  
        detail::password_chars,
517  
        opt);
517  
        opt);
518  
    impl_.decoded_[id_pass] = s.size();
518  
    impl_.decoded_[id_pass] = s.size();
519  
    return *this;
519  
    return *this;
520  
}
520  
}
521  

521  

522  
url_base&
522  
url_base&
523  
url_base::
523  
url_base::
524  
set_encoded_password(
524  
set_encoded_password(
525  
    pct_string_view s)
525  
    pct_string_view s)
526  
{
526  
{
527  
    op_t op(*this, &detail::ref(s));
527  
    op_t op(*this, &detail::ref(s));
528  
    auto const n =
528  
    auto const n =
529  
        detail::re_encoded_size_unsafe(
529  
        detail::re_encoded_size_unsafe(
530  
            s,
530  
            s,
531  
            detail::password_chars);
531  
            detail::password_chars);
532  
    auto dest = set_password_impl(n, op);
532  
    auto dest = set_password_impl(n, op);
533  
    impl_.decoded_[id_pass] =
533  
    impl_.decoded_[id_pass] =
534  
        detail::re_encode_unsafe(
534  
        detail::re_encode_unsafe(
535  
            dest,
535  
            dest,
536  
            dest + n,
536  
            dest + n,
537  
            s,
537  
            s,
538  
            detail::password_chars);
538  
            detail::password_chars);
539  
    BOOST_ASSERT(
539  
    BOOST_ASSERT(
540  
        impl_.decoded_[id_pass] ==
540  
        impl_.decoded_[id_pass] ==
541  
            s.decoded_size());
541  
            s.decoded_size());
542  
    return *this;
542  
    return *this;
543  
}
543  
}
544  

544  

545  
url_base&
545  
url_base&
546  
url_base::
546  
url_base::
547  
remove_password() noexcept
547  
remove_password() noexcept
548  
{
548  
{
549  
    auto const n = impl_.len(id_pass);
549  
    auto const n = impl_.len(id_pass);
550  
    if(n < 2)
550  
    if(n < 2)
551  
        return *this; // no password
551  
        return *this; // no password
552  

552  

553  
    op_t op(*this);
553  
    op_t op(*this);
554  
    // clear password, retain '@'
554  
    // clear password, retain '@'
555  
    auto dest =
555  
    auto dest =
556  
        resize_impl(id_pass, 1, op);
556  
        resize_impl(id_pass, 1, op);
557  
    dest[0] = '@';
557  
    dest[0] = '@';
558  
    impl_.decoded_[id_pass] = 0;
558  
    impl_.decoded_[id_pass] = 0;
559  
    return *this;
559  
    return *this;
560  
}
560  
}
561  

561  

562  
//------------------------------------------------
562  
//------------------------------------------------
563  
//
563  
//
564  
// Host
564  
// Host
565  
//
565  
//
566  
//------------------------------------------------
566  
//------------------------------------------------
567  
/*
567  
/*
568  
host_type       host_type()                 // ipv4, ipv6, ipvfuture, name
568  
host_type       host_type()                 // ipv4, ipv6, ipvfuture, name
569  

569  

570  
std::string     host()                      // return encoded_host().decode()
570  
std::string     host()                      // return encoded_host().decode()
571  
pct_string_view encoded_host()              // return host part, as-is
571  
pct_string_view encoded_host()              // return host part, as-is
572  
std::string     host_address()              // return encoded_host_address().decode()
572  
std::string     host_address()              // return encoded_host_address().decode()
573  
pct_string_view encoded_host_address()      // ipv4, ipv6, ipvfut, or encoded name, no brackets
573  
pct_string_view encoded_host_address()      // ipv4, ipv6, ipvfut, or encoded name, no brackets
574  

574  

575  
ipv4_address    host_ipv4_address()         // return ipv4_address or {}
575  
ipv4_address    host_ipv4_address()         // return ipv4_address or {}
576  
ipv6_address    host_ipv6_address()         // return ipv6_address or {}
576  
ipv6_address    host_ipv6_address()         // return ipv6_address or {}
577  
core::string_view     host_ipvfuture()            // return ipvfuture or {}
577  
core::string_view     host_ipvfuture()            // return ipvfuture or {}
578  
std::string     host_name()                 // return decoded name or ""
578  
std::string     host_name()                 // return decoded name or ""
579  
pct_string_view encoded_host_name()         // return encoded host name or ""
579  
pct_string_view encoded_host_name()         // return encoded host name or ""
580  

580  

581  
--------------------------------------------------
581  
--------------------------------------------------
582  

582  

583  
set_host( core::string_view )                     // set host part from plain text
583  
set_host( core::string_view )                     // set host part from plain text
584  
set_encoded_host( pct_string_view )         // set host part from encoded text
584  
set_encoded_host( pct_string_view )         // set host part from encoded text
585  
set_host_address( core::string_view )             // set host from ipv4, ipv6, ipvfut, or plain reg-name string
585  
set_host_address( core::string_view )             // set host from ipv4, ipv6, ipvfut, or plain reg-name string
586  
set_encoded_host_address( pct_string_view ) // set host from ipv4, ipv6, ipvfut, or encoded reg-name string
586  
set_encoded_host_address( pct_string_view ) // set host from ipv4, ipv6, ipvfut, or encoded reg-name string
587  

587  

588  
set_host_ipv4( ipv4_address )               // set ipv4
588  
set_host_ipv4( ipv4_address )               // set ipv4
589  
set_host_ipv6( ipv6_address )               // set ipv6
589  
set_host_ipv6( ipv6_address )               // set ipv6
590  
set_host_ipvfuture( core::string_view )           // set ipvfuture
590  
set_host_ipvfuture( core::string_view )           // set ipvfuture
591  
set_host_name( core::string_view )                // set name from plain
591  
set_host_name( core::string_view )                // set name from plain
592  
set_encoded_host_name( pct_string_view )    // set name from encoded
592  
set_encoded_host_name( pct_string_view )    // set name from encoded
593  
*/
593  
*/
594  

594  

595  
// set host part from plain text
595  
// set host part from plain text
596  
url_base&
596  
url_base&
597  
url_base::
597  
url_base::
598  
set_host(
598  
set_host(
599  
    core::string_view s)
599  
    core::string_view s)
600  
{
600  
{
601  
    if( s.size() > 2 &&
601  
    if( s.size() > 2 &&
602  
        s.front() == '[' &&
602  
        s.front() == '[' &&
603  
        s.back() == ']')
603  
        s.back() == ']')
604  
    {
604  
    {
605  
        // IP-literal
605  
        // IP-literal
606  
        if (s[1] != 'v')
606  
        if (s[1] != 'v')
607  
        {
607  
        {
608  
            // IPv6-address
608  
            // IPv6-address
609  
            auto innersv = s.substr(1, s.size() - 2);
609  
            auto innersv = s.substr(1, s.size() - 2);
610  
            auto innerit = innersv.begin();
610  
            auto innerit = innersv.begin();
611  
            auto endit = innersv.end();
611  
            auto endit = innersv.end();
612  
            auto rv = grammar::parse(
612  
            auto rv = grammar::parse(
613  
                innerit,
613  
                innerit,
614  
                endit,
614  
                endit,
615  
                ipv6_address_rule);
615  
                ipv6_address_rule);
616  
            if(rv)
616  
            if(rv)
617  
            {
617  
            {
618  
                if (innerit == endit)
618  
                if (innerit == endit)
619  
                {
619  
                {
620  
                    set_host_ipv6_and_encoded_zone_id(*rv, {});
620  
                    set_host_ipv6_and_encoded_zone_id(*rv, {});
621  
                    return *this;
621  
                    return *this;
622  
                }
622  
                }
623  
                // IPv6addrz: https://datatracker.ietf.org/doc/html/rfc6874
623  
                // IPv6addrz: https://datatracker.ietf.org/doc/html/rfc6874
624  
                auto chars_left = endit - innerit;
624  
                auto chars_left = endit - innerit;
625  
                if (chars_left >= 2 &&
625  
                if (chars_left >= 2 &&
626  
                    *innerit++ == '%')
626  
                    *innerit++ == '%')
627  
                {
627  
                {
628  
                    core::string_view zone_id_str = {&*innerit, std::size_t(chars_left - 1)};
628  
                    core::string_view zone_id_str = {&*innerit, std::size_t(chars_left - 1)};
629  
                    set_host_ipv6_and_zone_id(*rv, zone_id_str);
629  
                    set_host_ipv6_and_zone_id(*rv, zone_id_str);
630  
                    return *this;
630  
                    return *this;
631  
                }
631  
                }
632  
            }
632  
            }
633  
        }
633  
        }
634  
        else
634  
        else
635  
        {
635  
        {
636  
            // IPvFuture
636  
            // IPvFuture
637  
            auto rv = grammar::parse(
637  
            auto rv = grammar::parse(
638  
                s.substr(1, s.size() - 2),
638  
                s.substr(1, s.size() - 2),
639  
                detail::ipvfuture_rule);
639  
                detail::ipvfuture_rule);
640  
            if(rv)
640  
            if(rv)
641  
                return set_host_ipvfuture(rv->str);
641  
                return set_host_ipvfuture(rv->str);
642  
        }
642  
        }
643  
    }
643  
    }
644  
    else if(s.size() >= 7) // "0.0.0.0"
644  
    else if(s.size() >= 7) // "0.0.0.0"
645  
    {
645  
    {
646  
        // IPv4-address
646  
        // IPv4-address
647  
        auto rv = parse_ipv4_address(s);
647  
        auto rv = parse_ipv4_address(s);
648  
        if(rv)
648  
        if(rv)
649  
            return set_host_ipv4(*rv);
649  
            return set_host_ipv4(*rv);
650  
    }
650  
    }
651  

651  

652  
    // reg-name
652  
    // reg-name
653  
    op_t op(*this, &s);
653  
    op_t op(*this, &s);
654  
    encoding_opts opt;
654  
    encoding_opts opt;
655  
    auto const n = encoded_size(
655  
    auto const n = encoded_size(
656  
        s, detail::host_chars, opt);
656  
        s, detail::host_chars, opt);
657  
    auto dest = set_host_impl(n, op);
657  
    auto dest = set_host_impl(n, op);
658  
    encode(
658  
    encode(
659  
        dest,
659  
        dest,
660  
        impl_.get(id_path).data() - dest,
660  
        impl_.get(id_path).data() - dest,
661  
        s,
661  
        s,
662  
        detail::host_chars,
662  
        detail::host_chars,
663  
        opt);
663  
        opt);
664  
    impl_.decoded_[id_host] = s.size();
664  
    impl_.decoded_[id_host] = s.size();
665  
    impl_.host_type_ =
665  
    impl_.host_type_ =
666  
        urls::host_type::name;
666  
        urls::host_type::name;
667  
    return *this;
667  
    return *this;
668  
}
668  
}
669  

669  

670  
// set host part from encoded text
670  
// set host part from encoded text
671  
url_base&
671  
url_base&
672  
url_base::
672  
url_base::
673  
set_encoded_host(
673  
set_encoded_host(
674  
    pct_string_view s)
674  
    pct_string_view s)
675  
{
675  
{
676  
    if( s.size() > 2 &&
676  
    if( s.size() > 2 &&
677  
        s.front() == '[' &&
677  
        s.front() == '[' &&
678  
        s.back() == ']')
678  
        s.back() == ']')
679  
    {
679  
    {
680  
        // IP-literal
680  
        // IP-literal
681  
        if (s[1] != 'v')
681  
        if (s[1] != 'v')
682  
        {
682  
        {
683  
            // IPv6-address
683  
            // IPv6-address
684  
            auto innersv = s.substr(1, s.size() - 2);
684  
            auto innersv = s.substr(1, s.size() - 2);
685  
            auto innerit = innersv.begin();
685  
            auto innerit = innersv.begin();
686  
            auto endit = innersv.end();
686  
            auto endit = innersv.end();
687  
            auto rv = grammar::parse(
687  
            auto rv = grammar::parse(
688  
                innerit,
688  
                innerit,
689  
                endit,
689  
                endit,
690  
                ipv6_address_rule);
690  
                ipv6_address_rule);
691  
            if(rv)
691  
            if(rv)
692  
            {
692  
            {
693  
                if (innerit == endit)
693  
                if (innerit == endit)
694  
                {
694  
                {
695  
                    set_host_ipv6_and_encoded_zone_id(*rv, {});
695  
                    set_host_ipv6_and_encoded_zone_id(*rv, {});
696  
                    return *this;
696  
                    return *this;
697  
                }
697  
                }
698  
                // IPv6addrz: https://datatracker.ietf.org/doc/html/rfc6874
698  
                // IPv6addrz: https://datatracker.ietf.org/doc/html/rfc6874
699  
                auto chars_left = endit - innerit;
699  
                auto chars_left = endit - innerit;
700  
                if (chars_left >= 3 &&
700  
                if (chars_left >= 3 &&
701  
                    *innerit++ == '%' &&
701  
                    *innerit++ == '%' &&
702  
                    *innerit++ == '2' &&
702  
                    *innerit++ == '2' &&
703  
                    *innerit++ == '5')
703  
                    *innerit++ == '5')
704  
                {
704  
                {
705  
                    auto const nz = std::size_t(chars_left - 3);
705  
                    auto const nz = std::size_t(chars_left - 3);
706  
                    core::string_view zone_id_str = {&*innerit, std::size_t(chars_left - 3)};
706  
                    core::string_view zone_id_str = {&*innerit, std::size_t(chars_left - 3)};
707  
                    std::size_t dnz = detail::decode_bytes_unsafe(zone_id_str);
707  
                    std::size_t dnz = detail::decode_bytes_unsafe(zone_id_str);
708  
                    pct_string_view zone_id_pct = make_pct_string_view_unsafe(innerit, nz, dnz);
708  
                    pct_string_view zone_id_pct = make_pct_string_view_unsafe(innerit, nz, dnz);
709  
                    set_host_ipv6_and_encoded_zone_id(*rv, zone_id_pct);
709  
                    set_host_ipv6_and_encoded_zone_id(*rv, zone_id_pct);
710  
                    return *this;
710  
                    return *this;
711  
                }
711  
                }
712  
            }
712  
            }
713  
        }
713  
        }
714  
        else
714  
        else
715  
        {
715  
        {
716  
            // IPvFuture
716  
            // IPvFuture
717  
            auto rv = grammar::parse(
717  
            auto rv = grammar::parse(
718  
                s.substr(1, s.size() - 2),
718  
                s.substr(1, s.size() - 2),
719  
                    detail::ipvfuture_rule);
719  
                    detail::ipvfuture_rule);
720  
            if(rv)
720  
            if(rv)
721  
                return set_host_ipvfuture(rv->str);
721  
                return set_host_ipvfuture(rv->str);
722  
        }
722  
        }
723  
    }
723  
    }
724  
    else if(s.size() >= 7) // "0.0.0.0"
724  
    else if(s.size() >= 7) // "0.0.0.0"
725  
    {
725  
    {
726  
        // IPv4-address
726  
        // IPv4-address
727  
        auto rv = parse_ipv4_address(s);
727  
        auto rv = parse_ipv4_address(s);
728  
        if(rv)
728  
        if(rv)
729  
            return set_host_ipv4(*rv);
729  
            return set_host_ipv4(*rv);
730  
    }
730  
    }
731  

731  

732  
    // reg-name
732  
    // reg-name
733  
    op_t op(*this, &detail::ref(s));
733  
    op_t op(*this, &detail::ref(s));
734  
    auto const n = detail::re_encoded_size_unsafe(
734  
    auto const n = detail::re_encoded_size_unsafe(
735  
        s, detail::host_chars);
735  
        s, detail::host_chars);
736  
    auto dest = set_host_impl(n, op);
736  
    auto dest = set_host_impl(n, op);
737  
    impl_.decoded_[id_host] =
737  
    impl_.decoded_[id_host] =
738  
        detail::re_encode_unsafe(
738  
        detail::re_encode_unsafe(
739  
            dest,
739  
            dest,
740  
            impl_.get(id_path).data(),
740  
            impl_.get(id_path).data(),
741  
            s,
741  
            s,
742  
            detail::host_chars);
742  
            detail::host_chars);
743  
    BOOST_ASSERT(impl_.decoded_[id_host] ==
743  
    BOOST_ASSERT(impl_.decoded_[id_host] ==
744  
        s.decoded_size());
744  
        s.decoded_size());
745  
    impl_.host_type_ =
745  
    impl_.host_type_ =
746  
        urls::host_type::name;
746  
        urls::host_type::name;
747  
    return *this;
747  
    return *this;
748  
}
748  
}
749  

749  

750  
url_base&
750  
url_base&
751  
url_base::
751  
url_base::
752  
set_host_address(
752  
set_host_address(
753  
    core::string_view s)
753  
    core::string_view s)
754  
{
754  
{
755  
    if (!s.empty())
755  
    if (!s.empty())
756  
    {
756  
    {
757  
        // IP-literal
757  
        // IP-literal
758  
        if (s[0] != 'v')
758  
        if (s[0] != 'v')
759  
        {
759  
        {
760  
            // IPv6-address
760  
            // IPv6-address
761  
            auto innerit = s.begin();
761  
            auto innerit = s.begin();
762  
            auto endit = s.end();
762  
            auto endit = s.end();
763  
            auto rv = grammar::parse(
763  
            auto rv = grammar::parse(
764  
                innerit,
764  
                innerit,
765  
                endit,
765  
                endit,
766  
                ipv6_address_rule);
766  
                ipv6_address_rule);
767  
            if(rv)
767  
            if(rv)
768  
            {
768  
            {
769  
                if (innerit == endit)
769  
                if (innerit == endit)
770  
                {
770  
                {
771  
                    set_host_ipv6_and_encoded_zone_id(*rv, {});
771  
                    set_host_ipv6_and_encoded_zone_id(*rv, {});
772  
                    return *this;
772  
                    return *this;
773  
                }
773  
                }
774  
                // IPv6addrz: https://datatracker.ietf.org/doc/html/rfc6874
774  
                // IPv6addrz: https://datatracker.ietf.org/doc/html/rfc6874
775  
                auto chars_left = endit - innerit;
775  
                auto chars_left = endit - innerit;
776  
                if (chars_left >= 2 &&
776  
                if (chars_left >= 2 &&
777  
                    *innerit++ == '%')
777  
                    *innerit++ == '%')
778  
                {
778  
                {
779  
                    core::string_view zone_id_str = {&*innerit, std::size_t(chars_left - 1)};
779  
                    core::string_view zone_id_str = {&*innerit, std::size_t(chars_left - 1)};
780  
                    set_host_ipv6_and_zone_id(*rv, zone_id_str);
780  
                    set_host_ipv6_and_zone_id(*rv, zone_id_str);
781  
                    return *this;
781  
                    return *this;
782  
                }
782  
                }
783  
            }
783  
            }
784  
        }
784  
        }
785  

785  

786  
        // IPvFuture
786  
        // IPvFuture
787  
        auto rv = grammar::parse(s, detail::ipvfuture_rule);
787  
        auto rv = grammar::parse(s, detail::ipvfuture_rule);
788  
        if(rv)
788  
        if(rv)
789  
            return set_host_ipvfuture(rv->str);
789  
            return set_host_ipvfuture(rv->str);
790  

790  

791  
        if(s.size() >= 7) // "0.0.0.0"
791  
        if(s.size() >= 7) // "0.0.0.0"
792  
        {
792  
        {
793  
            // IPv4-address
793  
            // IPv4-address
794  
            auto rv2 = parse_ipv4_address(s);
794  
            auto rv2 = parse_ipv4_address(s);
795  
            if(rv2)
795  
            if(rv2)
796  
                return set_host_ipv4(*rv2);
796  
                return set_host_ipv4(*rv2);
797  
        }
797  
        }
798  
    }
798  
    }
799  

799  

800  
    // reg-name
800  
    // reg-name
801  
    op_t op(*this, &s);
801  
    op_t op(*this, &s);
802  
    encoding_opts opt;
802  
    encoding_opts opt;
803  
    auto const n = encoded_size(
803  
    auto const n = encoded_size(
804  
        s, detail::host_chars, opt);
804  
        s, detail::host_chars, opt);
805  
    auto dest = set_host_impl(n, op);
805  
    auto dest = set_host_impl(n, op);
806  
    encode(
806  
    encode(
807  
        dest,
807  
        dest,
808  
        impl_.get(id_path).data() - dest,
808  
        impl_.get(id_path).data() - dest,
809  
        s,
809  
        s,
810  
        detail::host_chars,
810  
        detail::host_chars,
811  
        opt);
811  
        opt);
812  
    impl_.decoded_[id_host] = s.size();
812  
    impl_.decoded_[id_host] = s.size();
813  
    impl_.host_type_ =
813  
    impl_.host_type_ =
814  
        urls::host_type::name;
814  
        urls::host_type::name;
815  
    return *this;
815  
    return *this;
816  
}
816  
}
817  

817  

818  
url_base&
818  
url_base&
819  
url_base::
819  
url_base::
820  
set_encoded_host_address(
820  
set_encoded_host_address(
821  
    pct_string_view s)
821  
    pct_string_view s)
822  
{
822  
{
823  
    if( !s.empty() )
823  
    if( !s.empty() )
824  
    {
824  
    {
825  
        // IP-literal
825  
        // IP-literal
826  
        if (s[0] != 'v')
826  
        if (s[0] != 'v')
827  
        {
827  
        {
828  
            // IPv6-address
828  
            // IPv6-address
829  
            auto innerit = s.begin();
829  
            auto innerit = s.begin();
830  
            auto endit = s.end();
830  
            auto endit = s.end();
831  
            auto rv = grammar::parse(
831  
            auto rv = grammar::parse(
832  
                innerit,
832  
                innerit,
833  
                endit,
833  
                endit,
834  
                ipv6_address_rule);
834  
                ipv6_address_rule);
835  
            if(rv)
835  
            if(rv)
836  
            {
836  
            {
837  
                if (innerit == endit)
837  
                if (innerit == endit)
838  
                {
838  
                {
839  
                    set_host_ipv6_and_encoded_zone_id(*rv, {});
839  
                    set_host_ipv6_and_encoded_zone_id(*rv, {});
840  
                    return *this;
840  
                    return *this;
841  
                }
841  
                }
842  
                // IPv6addrz: https://datatracker.ietf.org/doc/html/rfc6874
842  
                // IPv6addrz: https://datatracker.ietf.org/doc/html/rfc6874
843  
                auto chars_left = endit - innerit;
843  
                auto chars_left = endit - innerit;
844  
                if (chars_left >= 3 &&
844  
                if (chars_left >= 3 &&
845  
                    *innerit++ == '%' &&
845  
                    *innerit++ == '%' &&
846  
                    *innerit++ == '2' &&
846  
                    *innerit++ == '2' &&
847  
                    *innerit++ == '5')
847  
                    *innerit++ == '5')
848  
                {
848  
                {
849  
                    auto const nz = std::size_t(chars_left - 3);
849  
                    auto const nz = std::size_t(chars_left - 3);
850  
                    core::string_view zone_id_str = {&*innerit, std::size_t(chars_left - 3)};
850  
                    core::string_view zone_id_str = {&*innerit, std::size_t(chars_left - 3)};
851  
                    std::size_t dnz = detail::decode_bytes_unsafe(zone_id_str);
851  
                    std::size_t dnz = detail::decode_bytes_unsafe(zone_id_str);
852  
                    pct_string_view zone_id_pct = make_pct_string_view_unsafe(innerit, nz, dnz);
852  
                    pct_string_view zone_id_pct = make_pct_string_view_unsafe(innerit, nz, dnz);
853  
                    set_host_ipv6_and_encoded_zone_id(*rv, zone_id_pct);
853  
                    set_host_ipv6_and_encoded_zone_id(*rv, zone_id_pct);
854  
                    return *this;
854  
                    return *this;
855  
                }
855  
                }
856  
            }
856  
            }
857  

857  

858  
            if(s.size() >= 7) // "0.0.0.0"
858  
            if(s.size() >= 7) // "0.0.0.0"
859  
            {
859  
            {
860  
                // IPv4-address
860  
                // IPv4-address
861  
                auto rv2 = parse_ipv4_address(s);
861  
                auto rv2 = parse_ipv4_address(s);
862  
                if(rv2)
862  
                if(rv2)
863  
                   return set_host_ipv4(*rv2);
863  
                   return set_host_ipv4(*rv2);
864  
            }
864  
            }
865  
        }
865  
        }
866  
        else
866  
        else
867  
        {
867  
        {
868  
            // IPvFuture
868  
            // IPvFuture
869  
            auto rv = grammar::parse(
869  
            auto rv = grammar::parse(
870  
                s, detail::ipvfuture_rule);
870  
                s, detail::ipvfuture_rule);
871  
            if(rv)
871  
            if(rv)
872  
                return set_host_ipvfuture(rv->str);
872  
                return set_host_ipvfuture(rv->str);
873  
        }
873  
        }
874  
    }
874  
    }
875  

875  

876  
    // reg-name
876  
    // reg-name
877  
    op_t op(*this, &detail::ref(s));
877  
    op_t op(*this, &detail::ref(s));
878  
    auto const n = detail::re_encoded_size_unsafe(
878  
    auto const n = detail::re_encoded_size_unsafe(
879  
        s, detail::host_chars);
879  
        s, detail::host_chars);
880  
    auto dest = set_host_impl(n, op);
880  
    auto dest = set_host_impl(n, op);
881  
    impl_.decoded_[id_host] =
881  
    impl_.decoded_[id_host] =
882  
        detail::re_encode_unsafe(
882  
        detail::re_encode_unsafe(
883  
            dest,
883  
            dest,
884  
            impl_.get(id_path).data(),
884  
            impl_.get(id_path).data(),
885  
            s,
885  
            s,
886  
            detail::host_chars);
886  
            detail::host_chars);
887  
    BOOST_ASSERT(impl_.decoded_[id_host] ==
887  
    BOOST_ASSERT(impl_.decoded_[id_host] ==
888  
        s.decoded_size());
888  
        s.decoded_size());
889  
    impl_.host_type_ =
889  
    impl_.host_type_ =
890  
        urls::host_type::name;
890  
        urls::host_type::name;
891  
    return *this;
891  
    return *this;
892  
}
892  
}
893  

893  

894  
url_base&
894  
url_base&
895  
url_base::
895  
url_base::
896  
set_host_ipv4(
896  
set_host_ipv4(
897  
    ipv4_address const& addr)
897  
    ipv4_address const& addr)
898  
{
898  
{
899  
    op_t op(*this);
899  
    op_t op(*this);
900  
    char buf[urls::ipv4_address::max_str_len];
900  
    char buf[urls::ipv4_address::max_str_len];
901  
    auto s = addr.to_buffer(buf, sizeof(buf));
901  
    auto s = addr.to_buffer(buf, sizeof(buf));
902  
    auto dest = set_host_impl(s.size(), op);
902  
    auto dest = set_host_impl(s.size(), op);
903  
    std::memcpy(dest, s.data(), s.size());
903  
    std::memcpy(dest, s.data(), s.size());
904  
    impl_.decoded_[id_host] = impl_.len(id_host);
904  
    impl_.decoded_[id_host] = impl_.len(id_host);
905  
    impl_.host_type_ = urls::host_type::ipv4;
905  
    impl_.host_type_ = urls::host_type::ipv4;
906  
    auto bytes = addr.to_bytes();
906  
    auto bytes = addr.to_bytes();
907  
    std::memcpy(
907  
    std::memcpy(
908  
        impl_.ip_addr_,
908  
        impl_.ip_addr_,
909  
        bytes.data(),
909  
        bytes.data(),
910  
        bytes.size());
910  
        bytes.size());
911  
    return *this;
911  
    return *this;
912  
}
912  
}
913  

913  

914  
url_base&
914  
url_base&
915  
url_base::
915  
url_base::
916  
set_host_ipv6(
916  
set_host_ipv6(
917  
    ipv6_address const& addr)
917  
    ipv6_address const& addr)
918  
{
918  
{
919  
    set_host_ipv6_and_encoded_zone_id(addr, encoded_zone_id());
919  
    set_host_ipv6_and_encoded_zone_id(addr, encoded_zone_id());
920  
    return *this;
920  
    return *this;
921  
}
921  
}
922  

922  

923  
url_base&
923  
url_base&
924  
url_base::
924  
url_base::
925  
set_zone_id(core::string_view s)
925  
set_zone_id(core::string_view s)
926  
{
926  
{
927  
    set_host_ipv6_and_zone_id(host_ipv6_address(), s);
927  
    set_host_ipv6_and_zone_id(host_ipv6_address(), s);
928  
    return *this;
928  
    return *this;
929  
}
929  
}
930  

930  

931  
url_base&
931  
url_base&
932  
url_base::
932  
url_base::
933  
set_encoded_zone_id(pct_string_view s)
933  
set_encoded_zone_id(pct_string_view s)
934  
{
934  
{
935  
    set_host_ipv6_and_encoded_zone_id(host_ipv6_address(), s);
935  
    set_host_ipv6_and_encoded_zone_id(host_ipv6_address(), s);
936  
    return *this;
936  
    return *this;
937  
}
937  
}
938  

938  

939  
void
939  
void
940  
url_base::
940  
url_base::
941  
set_host_ipv6_and_zone_id(
941  
set_host_ipv6_and_zone_id(
942  
    ipv6_address const& addr,
942  
    ipv6_address const& addr,
943  
    core::string_view zone_id)
943  
    core::string_view zone_id)
944  
{
944  
{
945  
    op_t op(*this, &zone_id);
945  
    op_t op(*this, &zone_id);
946  
    char ipv6_str_buf[urls::ipv6_address::max_str_len];
946  
    char ipv6_str_buf[urls::ipv6_address::max_str_len];
947  
    auto ipv6_str = addr.to_buffer(ipv6_str_buf, sizeof(ipv6_str_buf));
947  
    auto ipv6_str = addr.to_buffer(ipv6_str_buf, sizeof(ipv6_str_buf));
948  
    bool const has_zone_id = !zone_id.empty();
948  
    bool const has_zone_id = !zone_id.empty();
949  
    encoding_opts opt;
949  
    encoding_opts opt;
950  
    auto const ipn = ipv6_str.size();
950  
    auto const ipn = ipv6_str.size();
951  
    auto const zn = encoded_size(zone_id, unreserved_chars, opt);
951  
    auto const zn = encoded_size(zone_id, unreserved_chars, opt);
952  
    auto const n = ipn + 2 + has_zone_id * (3 + zn);
952  
    auto const n = ipn + 2 + has_zone_id * (3 + zn);
953  
    auto dest = set_host_impl(n, op);
953  
    auto dest = set_host_impl(n, op);
954  
    *dest++ = '[';
954  
    *dest++ = '[';
955  
    std::memcpy(dest, ipv6_str.data(), ipn);
955  
    std::memcpy(dest, ipv6_str.data(), ipn);
956  
    dest += ipn;
956  
    dest += ipn;
957  
    if (has_zone_id)
957  
    if (has_zone_id)
958  
    {
958  
    {
959  
        *dest++ = '%';
959  
        *dest++ = '%';
960  
        *dest++ = '2';
960  
        *dest++ = '2';
961  
        *dest++ = '5';
961  
        *dest++ = '5';
962  
        encode(dest, zn, zone_id, unreserved_chars, opt);
962  
        encode(dest, zn, zone_id, unreserved_chars, opt);
963  
        dest += zn;
963  
        dest += zn;
964  
    }
964  
    }
965  
    *dest++ = ']';
965  
    *dest++ = ']';
966  
    // ipn + |"["| + |"]"| + (has_zone_id ? |"%"| + zn : 0)
966  
    // ipn + |"["| + |"]"| + (has_zone_id ? |"%"| + zn : 0)
967  
    impl_.decoded_[id_host] = ipn + 2 + has_zone_id * (1 + zone_id.size());
967  
    impl_.decoded_[id_host] = ipn + 2 + has_zone_id * (1 + zone_id.size());
968  
    impl_.host_type_ = urls::host_type::ipv6;
968  
    impl_.host_type_ = urls::host_type::ipv6;
969  
    auto bytes = addr.to_bytes();
969  
    auto bytes = addr.to_bytes();
970  
    std::memcpy(
970  
    std::memcpy(
971  
        impl_.ip_addr_,
971  
        impl_.ip_addr_,
972  
        bytes.data(),
972  
        bytes.data(),
973  
        bytes.size());
973  
        bytes.size());
974  
}
974  
}
975  

975  

976  
void
976  
void
977  
url_base::
977  
url_base::
978  
set_host_ipv6_and_encoded_zone_id(
978  
set_host_ipv6_and_encoded_zone_id(
979  
    ipv6_address const& addr,
979  
    ipv6_address const& addr,
980  
    pct_string_view zone_id)
980  
    pct_string_view zone_id)
981  
{
981  
{
982  
    op_t op(*this, &detail::ref(zone_id));
982  
    op_t op(*this, &detail::ref(zone_id));
983  
    char ipv6_str_buf[urls::ipv6_address::max_str_len];
983  
    char ipv6_str_buf[urls::ipv6_address::max_str_len];
984  
    auto ipv6_str = addr.to_buffer(ipv6_str_buf, sizeof(ipv6_str_buf));
984  
    auto ipv6_str = addr.to_buffer(ipv6_str_buf, sizeof(ipv6_str_buf));
985  
    bool const has_zone_id = !zone_id.empty();
985  
    bool const has_zone_id = !zone_id.empty();
986  
    auto const ipn = ipv6_str.size();
986  
    auto const ipn = ipv6_str.size();
987  
    auto const zn = detail::re_encoded_size_unsafe(zone_id, unreserved_chars);
987  
    auto const zn = detail::re_encoded_size_unsafe(zone_id, unreserved_chars);
988  
    auto const n = ipn + 2 + has_zone_id * (3 + zn);
988  
    auto const n = ipn + 2 + has_zone_id * (3 + zn);
989  
    auto dest = set_host_impl(n, op);
989  
    auto dest = set_host_impl(n, op);
990  
    *dest++ = '[';
990  
    *dest++ = '[';
991  
    std::memcpy(dest, ipv6_str.data(), ipn);
991  
    std::memcpy(dest, ipv6_str.data(), ipn);
992  
    dest += ipn;
992  
    dest += ipn;
993  
    std::size_t dzn = 0;
993  
    std::size_t dzn = 0;
994  
    if (has_zone_id)
994  
    if (has_zone_id)
995  
    {
995  
    {
996  
        *dest++ = '%';
996  
        *dest++ = '%';
997  
        *dest++ = '2';
997  
        *dest++ = '2';
998  
        *dest++ = '5';
998  
        *dest++ = '5';
999  
        dzn = detail::re_encode_unsafe(dest, dest + zn, zone_id, unreserved_chars);
999  
        dzn = detail::re_encode_unsafe(dest, dest + zn, zone_id, unreserved_chars);
1000  
    }
1000  
    }
1001  
    *dest++ = ']';
1001  
    *dest++ = ']';
1002  
    // ipn + |"["| + |"]"| + (has_zone_id ? |"%"| + zn : 0)
1002  
    // ipn + |"["| + |"]"| + (has_zone_id ? |"%"| + zn : 0)
1003  
    impl_.decoded_[id_host] = ipn + 2 + has_zone_id * (1 + dzn);
1003  
    impl_.decoded_[id_host] = ipn + 2 + has_zone_id * (1 + dzn);
1004  
    impl_.host_type_ = urls::host_type::ipv6;
1004  
    impl_.host_type_ = urls::host_type::ipv6;
1005  
    auto bytes = addr.to_bytes();
1005  
    auto bytes = addr.to_bytes();
1006  
    std::memcpy(
1006  
    std::memcpy(
1007  
        impl_.ip_addr_,
1007  
        impl_.ip_addr_,
1008  
        bytes.data(),
1008  
        bytes.data(),
1009  
        bytes.size());
1009  
        bytes.size());
1010  
}
1010  
}
1011  

1011  

1012  
url_base&
1012  
url_base&
1013  
url_base::
1013  
url_base::
1014  
set_host_ipvfuture(
1014  
set_host_ipvfuture(
1015  
    core::string_view s)
1015  
    core::string_view s)
1016  
{
1016  
{
1017  
    op_t op(*this, &s);
1017  
    op_t op(*this, &s);
1018  
    // validate
1018  
    // validate
1019  
    grammar::parse(s,
1019  
    grammar::parse(s,
1020  
        detail::ipvfuture_rule
1020  
        detail::ipvfuture_rule
1021  
            ).value(BOOST_URL_POS);
1021  
            ).value(BOOST_URL_POS);
1022  
    auto dest = set_host_impl(
1022  
    auto dest = set_host_impl(
1023  
        s.size() + 2, op);
1023  
        s.size() + 2, op);
1024  
    *dest++ = '[';
1024  
    *dest++ = '[';
1025  
    dest += s.copy(dest, s.size());
1025  
    dest += s.copy(dest, s.size());
1026  
    *dest = ']';
1026  
    *dest = ']';
1027  
    impl_.host_type_ =
1027  
    impl_.host_type_ =
1028  
        urls::host_type::ipvfuture;
1028  
        urls::host_type::ipvfuture;
1029  
    impl_.decoded_[id_host] = s.size() + 2;
1029  
    impl_.decoded_[id_host] = s.size() + 2;
1030  
    return *this;
1030  
    return *this;
1031  
}
1031  
}
1032  

1032  

1033  
url_base&
1033  
url_base&
1034  
url_base::
1034  
url_base::
1035  
set_host_name(
1035  
set_host_name(
1036  
    core::string_view s)
1036  
    core::string_view s)
1037  
{
1037  
{
1038  
    bool is_ipv4 = false;
1038  
    bool is_ipv4 = false;
1039  
    if(s.size() >= 7) // "0.0.0.0"
1039  
    if(s.size() >= 7) // "0.0.0.0"
1040  
    {
1040  
    {
1041  
        // IPv4-address
1041  
        // IPv4-address
1042  
        if(parse_ipv4_address(s).has_value())
1042  
        if(parse_ipv4_address(s).has_value())
1043  
            is_ipv4 = true;
1043  
            is_ipv4 = true;
1044  
    }
1044  
    }
1045  
    auto allowed = detail::host_chars;
1045  
    auto allowed = detail::host_chars;
1046  
    if(is_ipv4)
1046  
    if(is_ipv4)
1047  
        allowed = allowed - '.';
1047  
        allowed = allowed - '.';
1048  

1048  

1049  
    op_t op(*this, &s);
1049  
    op_t op(*this, &s);
1050  
    encoding_opts opt;
1050  
    encoding_opts opt;
1051  
    auto const n = encoded_size(
1051  
    auto const n = encoded_size(
1052  
        s, allowed, opt);
1052  
        s, allowed, opt);
1053  
    auto dest = set_host_impl(n, op);
1053  
    auto dest = set_host_impl(n, op);
1054  
    encode_unsafe(
1054  
    encode_unsafe(
1055  
        dest,
1055  
        dest,
1056  
        n,
1056  
        n,
1057  
        s,
1057  
        s,
1058  
        allowed,
1058  
        allowed,
1059  
        opt);
1059  
        opt);
1060  
    impl_.host_type_ =
1060  
    impl_.host_type_ =
1061  
        urls::host_type::name;
1061  
        urls::host_type::name;
1062  
    impl_.decoded_[id_host] = s.size();
1062  
    impl_.decoded_[id_host] = s.size();
1063  
    return *this;
1063  
    return *this;
1064  
}
1064  
}
1065  

1065  

1066  
url_base&
1066  
url_base&
1067  
url_base::
1067  
url_base::
1068  
set_encoded_host_name(
1068  
set_encoded_host_name(
1069  
    pct_string_view s)
1069  
    pct_string_view s)
1070  
{
1070  
{
1071  
    bool is_ipv4 = false;
1071  
    bool is_ipv4 = false;
1072  
    if(s.size() >= 7) // "0.0.0.0"
1072  
    if(s.size() >= 7) // "0.0.0.0"
1073  
    {
1073  
    {
1074  
        // IPv4-address
1074  
        // IPv4-address
1075  
        if(parse_ipv4_address(s).has_value())
1075  
        if(parse_ipv4_address(s).has_value())
1076  
            is_ipv4 = true;
1076  
            is_ipv4 = true;
1077  
    }
1077  
    }
1078  
    auto allowed = detail::host_chars;
1078  
    auto allowed = detail::host_chars;
1079  
    if(is_ipv4)
1079  
    if(is_ipv4)
1080  
        allowed = allowed - '.';
1080  
        allowed = allowed - '.';
1081  

1081  

1082  
    op_t op(*this, &detail::ref(s));
1082  
    op_t op(*this, &detail::ref(s));
1083  
    auto const n = detail::re_encoded_size_unsafe(
1083  
    auto const n = detail::re_encoded_size_unsafe(
1084  
        s, allowed);
1084  
        s, allowed);
1085  
    auto dest = set_host_impl(n, op);
1085  
    auto dest = set_host_impl(n, op);
1086  
    impl_.decoded_[id_host] =
1086  
    impl_.decoded_[id_host] =
1087  
        detail::re_encode_unsafe(
1087  
        detail::re_encode_unsafe(
1088  
            dest,
1088  
            dest,
1089  
            dest + n,
1089  
            dest + n,
1090  
            s,
1090  
            s,
1091  
            allowed);
1091  
            allowed);
1092  
    BOOST_ASSERT(
1092  
    BOOST_ASSERT(
1093  
        impl_.decoded_[id_host] ==
1093  
        impl_.decoded_[id_host] ==
1094  
            s.decoded_size());
1094  
            s.decoded_size());
1095  
    impl_.host_type_ =
1095  
    impl_.host_type_ =
1096  
        urls::host_type::name;
1096  
        urls::host_type::name;
1097  
    return *this;
1097  
    return *this;
1098  
}
1098  
}
1099  

1099  

1100  
//------------------------------------------------
1100  
//------------------------------------------------
1101  

1101  

1102  
url_base&
1102  
url_base&
1103  
url_base::
1103  
url_base::
1104  
set_port_number(
1104  
set_port_number(
1105  
    std::uint16_t n)
1105  
    std::uint16_t n)
1106  
{
1106  
{
1107  
    op_t op(*this);
1107  
    op_t op(*this);
1108  
    auto s =
1108  
    auto s =
1109  
        detail::make_printed(n);
1109  
        detail::make_printed(n);
1110  
    auto dest = set_port_impl(
1110  
    auto dest = set_port_impl(
1111  
        s.string().size(), op);
1111  
        s.string().size(), op);
1112  
    std::memcpy(
1112  
    std::memcpy(
1113  
        dest, s.string().data(),
1113  
        dest, s.string().data(),
1114  
            s.string().size());
1114  
            s.string().size());
1115  
    impl_.port_number_ = n;
1115  
    impl_.port_number_ = n;
1116  
    return *this;
1116  
    return *this;
1117  
}
1117  
}
1118  

1118  

1119  
url_base&
1119  
url_base&
1120  
url_base::
1120  
url_base::
1121  
set_port(
1121  
set_port(
1122  
    core::string_view s)
1122  
    core::string_view s)
1123  
{
1123  
{
1124  
    op_t op(*this, &s);
1124  
    op_t op(*this, &s);
1125  
    auto t = grammar::parse(s,
1125  
    auto t = grammar::parse(s,
1126  
        detail::port_rule{}
1126  
        detail::port_rule{}
1127  
            ).value(BOOST_URL_POS);
1127  
            ).value(BOOST_URL_POS);
1128  
    auto dest =
1128  
    auto dest =
1129  
        set_port_impl(t.str.size(), op);
1129  
        set_port_impl(t.str.size(), op);
1130  
    std::memcpy(dest,
1130  
    std::memcpy(dest,
1131  
        t.str.data(), t.str.size());
1131  
        t.str.data(), t.str.size());
1132  
    if(t.has_number)
1132  
    if(t.has_number)
1133  
        impl_.port_number_ = t.number;
1133  
        impl_.port_number_ = t.number;
1134  
    else
1134  
    else
1135  
        impl_.port_number_ = 0;
1135  
        impl_.port_number_ = 0;
1136  
    return *this;
1136  
    return *this;
1137  
}
1137  
}
1138  

1138  

1139  
url_base&
1139  
url_base&
1140  
url_base::
1140  
url_base::
1141  
remove_port() noexcept
1141  
remove_port() noexcept
1142  
{
1142  
{
1143  
    op_t op(*this);
1143  
    op_t op(*this);
1144  
    resize_impl(id_port, 0, op);
1144  
    resize_impl(id_port, 0, op);
1145  
    impl_.port_number_ = 0;
1145  
    impl_.port_number_ = 0;
1146  
    return *this;
1146  
    return *this;
1147  
}
1147  
}
1148  

1148  

1149  
//------------------------------------------------
1149  
//------------------------------------------------
1150  
//
1150  
//
1151  
// Compound Fields
1151  
// Compound Fields
1152  
//
1152  
//
1153  
//------------------------------------------------
1153  
//------------------------------------------------
1154  

1154  

1155  
url_base&
1155  
url_base&
1156  
url_base::
1156  
url_base::
1157  
remove_origin()
1157  
remove_origin()
1158  
{
1158  
{
1159  
    // these two calls perform 2 memmoves instead of 1
1159  
    // these two calls perform 2 memmoves instead of 1
1160  
    remove_authority();
1160  
    remove_authority();
1161  
    remove_scheme();
1161  
    remove_scheme();
1162  
    return *this;
1162  
    return *this;
1163  
}
1163  
}
1164  

1164  

1165  
//------------------------------------------------
1165  
//------------------------------------------------
1166  
//
1166  
//
1167  
// Path
1167  
// Path
1168  
//
1168  
//
1169  
//------------------------------------------------
1169  
//------------------------------------------------
1170  

1170  

1171  
bool
1171  
bool
1172  
url_base::
1172  
url_base::
1173  
set_path_absolute(
1173  
set_path_absolute(
1174  
    bool absolute)
1174  
    bool absolute)
1175  
{
1175  
{
1176  
    op_t op(*this);
1176  
    op_t op(*this);
1177  

1177  

1178  
    // check if path empty
1178  
    // check if path empty
1179  
    if(impl_.len(id_path) == 0)
1179  
    if(impl_.len(id_path) == 0)
1180  
    {
1180  
    {
1181  
        if(! absolute)
1181  
        if(! absolute)
1182  
        {
1182  
        {
1183  
            // already not absolute
1183  
            // already not absolute
1184  
            return true;
1184  
            return true;
1185  
        }
1185  
        }
1186  

1186  

1187  
        // add '/'
1187  
        // add '/'
1188  
        auto dest = resize_impl(
1188  
        auto dest = resize_impl(
1189  
            id_path, 1, op);
1189  
            id_path, 1, op);
1190  
        *dest = '/';
1190  
        *dest = '/';
1191  
        ++impl_.decoded_[id_path];
1191  
        ++impl_.decoded_[id_path];
1192  
        return true;
1192  
        return true;
1193  
    }
1193  
    }
1194  

1194  

1195  
    // check if path absolute
1195  
    // check if path absolute
1196  
    if(s_[impl_.offset(id_path)] == '/')
1196  
    if(s_[impl_.offset(id_path)] == '/')
1197  
    {
1197  
    {
1198  
        if(absolute)
1198  
        if(absolute)
1199  
        {
1199  
        {
1200  
            // already absolute
1200  
            // already absolute
1201  
            return true;
1201  
            return true;
1202  
        }
1202  
        }
1203  

1203  

1204  
        if( has_authority() &&
1204  
        if( has_authority() &&
1205  
            impl_.len(id_path) > 1)
1205  
            impl_.len(id_path) > 1)
1206  
        {
1206  
        {
1207  
            // can't do it, paths are always
1207  
            // can't do it, paths are always
1208  
            // absolute when authority present!
1208  
            // absolute when authority present!
1209  
            return false;
1209  
            return false;
1210  
        }
1210  
        }
1211  

1211  

1212  
        auto p = encoded_path();
1212  
        auto p = encoded_path();
1213  
        auto pos = p.find_first_of(":/", 1);
1213  
        auto pos = p.find_first_of(":/", 1);
1214  
        if (pos != core::string_view::npos &&
1214  
        if (pos != core::string_view::npos &&
1215  
            p[pos] == ':')
1215  
            p[pos] == ':')
1216  
        {
1216  
        {
1217  
            // prepend with .
1217  
            // prepend with .
1218  
            auto n = impl_.len(id_path);
1218  
            auto n = impl_.len(id_path);
1219  
            resize_impl(id_path, n + 1, op);
1219  
            resize_impl(id_path, n + 1, op);
1220  
            std::memmove(
1220  
            std::memmove(
1221  
                s_ + impl_.offset(id_path) + 1,
1221  
                s_ + impl_.offset(id_path) + 1,
1222  
                s_ + impl_.offset(id_path), n);
1222  
                s_ + impl_.offset(id_path), n);
1223  
            *(s_ + impl_.offset(id_path)) = '.';
1223  
            *(s_ + impl_.offset(id_path)) = '.';
1224  
            ++impl_.decoded_[id_path];
1224  
            ++impl_.decoded_[id_path];
1225  
            return true;
1225  
            return true;
1226  
        }
1226  
        }
1227  

1227  

1228  
        // remove '/'
1228  
        // remove '/'
1229  
        auto n = impl_.len(id_port);
1229  
        auto n = impl_.len(id_port);
1230  
        impl_.split(id_port, n + 1);
1230  
        impl_.split(id_port, n + 1);
1231  
        resize_impl(id_port, n, op);
1231  
        resize_impl(id_port, n, op);
1232  
        --impl_.decoded_[id_path];
1232  
        --impl_.decoded_[id_path];
1233  
        return true;
1233  
        return true;
1234  
    }
1234  
    }
1235  

1235  

1236  
    if(! absolute)
1236  
    if(! absolute)
1237  
    {
1237  
    {
1238  
        // already not absolute
1238  
        // already not absolute
1239  
        return true;
1239  
        return true;
1240  
    }
1240  
    }
1241  

1241  

1242  
    // add '/'
1242  
    // add '/'
1243  
    auto n = impl_.len(id_port);
1243  
    auto n = impl_.len(id_port);
1244  
    auto dest = resize_impl(
1244  
    auto dest = resize_impl(
1245  
        id_port, n + 1, op) + n;
1245  
        id_port, n + 1, op) + n;
1246  
    impl_.split(id_port, n);
1246  
    impl_.split(id_port, n);
1247  
    *dest = '/';
1247  
    *dest = '/';
1248  
    ++impl_.decoded_[id_path];
1248  
    ++impl_.decoded_[id_path];
1249  
    return true;
1249  
    return true;
1250  
}
1250  
}
1251  

1251  

1252  
url_base&
1252  
url_base&
1253  
url_base::
1253  
url_base::
1254  
set_path(
1254  
set_path(
1255  
    core::string_view s)
1255  
    core::string_view s)
1256  
{
1256  
{
1257  
    op_t op(*this, &s);
1257  
    op_t op(*this, &s);
1258  
    encoding_opts opt;
1258  
    encoding_opts opt;
1259  

1259  

1260  
//------------------------------------------------
1260  
//------------------------------------------------
1261  
//
1261  
//
1262  
//  Calculate encoded size
1262  
//  Calculate encoded size
1263  
//
1263  
//
1264  
// - "/"s are not encoded
1264  
// - "/"s are not encoded
1265  
// - "%2F"s are not encoded
1265  
// - "%2F"s are not encoded
1266  
//
1266  
//
1267  
// - reserved path chars are re-encoded
1267  
// - reserved path chars are re-encoded
1268  
// - colons in first segment might need to be re-encoded
1268  
// - colons in first segment might need to be re-encoded
1269  
// - the path might need to receive a prefix
1269  
// - the path might need to receive a prefix
1270  
    auto const n = encoded_size(
1270  
    auto const n = encoded_size(
1271  
        s, detail::path_chars, opt);
1271  
        s, detail::path_chars, opt);
1272  
    std::size_t n_reencode_colons = 0;
1272  
    std::size_t n_reencode_colons = 0;
1273  
    core::string_view first_seg;
1273  
    core::string_view first_seg;
1274  
    if (!has_scheme() &&
1274  
    if (!has_scheme() &&
1275  
        !has_authority() &&
1275  
        !has_authority() &&
1276  
        !s.starts_with('/'))
1276  
        !s.starts_with('/'))
1277  
    {
1277  
    {
1278  
        // the first segment with unencoded colons would look
1278  
        // the first segment with unencoded colons would look
1279  
        // like the scheme
1279  
        // like the scheme
1280  
        first_seg = detail::to_sv(s);
1280  
        first_seg = detail::to_sv(s);
1281  
        std::size_t p = s.find('/');
1281  
        std::size_t p = s.find('/');
1282  
        if (p != core::string_view::npos)
1282  
        if (p != core::string_view::npos)
1283  
            first_seg = s.substr(0, p);
1283  
            first_seg = s.substr(0, p);
1284  
        n_reencode_colons = std::count(
1284  
        n_reencode_colons = std::count(
1285  
            first_seg.begin(), first_seg.end(), ':');
1285  
            first_seg.begin(), first_seg.end(), ':');
1286  
    }
1286  
    }
1287  
    // the authority can only be followed by an empty or relative path
1287  
    // the authority can only be followed by an empty or relative path
1288  
    // if we have an authority and the path is a non-empty relative path, we
1288  
    // if we have an authority and the path is a non-empty relative path, we
1289  
    // add the "/" prefix to make it valid.
1289  
    // add the "/" prefix to make it valid.
1290  
    bool make_absolute =
1290  
    bool make_absolute =
1291  
        has_authority() &&
1291  
        has_authority() &&
1292  
        !s.starts_with('/') &&
1292  
        !s.starts_with('/') &&
1293  
        !s.empty();
1293  
        !s.empty();
1294  
    // a path starting with "//" might look like the authority.
1294  
    // a path starting with "//" might look like the authority.
1295  
    // we add a "/." prefix to prevent that
1295  
    // we add a "/." prefix to prevent that
1296  
    bool add_dot_segment =
1296  
    bool add_dot_segment =
1297  
        !make_absolute &&
1297  
        !make_absolute &&
1298  
        s.starts_with("//");
1298  
        s.starts_with("//");
1299  

1299  

1300  
//------------------------------------------------
1300  
//------------------------------------------------
1301  
//
1301  
//
1302  
//  Re-encode data
1302  
//  Re-encode data
1303  
//
1303  
//
1304  
    auto dest = set_path_impl(
1304  
    auto dest = set_path_impl(
1305  
        n + make_absolute + 2 * n_reencode_colons + 2 * add_dot_segment, op);
1305  
        n + make_absolute + 2 * n_reencode_colons + 2 * add_dot_segment, op);
1306  
    impl_.decoded_[id_path] = 0;
1306  
    impl_.decoded_[id_path] = 0;
1307  
    if (!dest)
1307  
    if (!dest)
1308  
    {
1308  
    {
1309  
        impl_.nseg_ = 0;
1309  
        impl_.nseg_ = 0;
1310  
        return *this;
1310  
        return *this;
1311  
    }
1311  
    }
1312  
    if (make_absolute)
1312  
    if (make_absolute)
1313  
    {
1313  
    {
1314  
        *dest++ = '/';
1314  
        *dest++ = '/';
1315  
        impl_.decoded_[id_path] += 1;
1315  
        impl_.decoded_[id_path] += 1;
1316  
    }
1316  
    }
1317  
    else if (add_dot_segment)
1317  
    else if (add_dot_segment)
1318  
    {
1318  
    {
1319  
        *dest++ = '/';
1319  
        *dest++ = '/';
1320  
        *dest++ = '.';
1320  
        *dest++ = '.';
1321  
        impl_.decoded_[id_path] += 2;
1321  
        impl_.decoded_[id_path] += 2;
1322  
    }
1322  
    }
1323  
    dest += encode_unsafe(
1323  
    dest += encode_unsafe(
1324  
        dest,
1324  
        dest,
1325  
        impl_.get(id_query).data() - dest,
1325  
        impl_.get(id_query).data() - dest,
1326  
        first_seg,
1326  
        first_seg,
1327  
        detail::segment_chars - ':',
1327  
        detail::segment_chars - ':',
1328  
        opt);
1328  
        opt);
1329  
    dest += encode_unsafe(
1329  
    dest += encode_unsafe(
1330  
        dest,
1330  
        dest,
1331  
        impl_.get(id_query).data() - dest,
1331  
        impl_.get(id_query).data() - dest,
1332  
        s.substr(first_seg.size()),
1332  
        s.substr(first_seg.size()),
1333  
        detail::path_chars,
1333  
        detail::path_chars,
1334  
        opt);
1334  
        opt);
1335  
    impl_.decoded_[id_path] += s.size();
1335  
    impl_.decoded_[id_path] += s.size();
1336  
    BOOST_ASSERT(!dest || dest == impl_.get(id_query).data());
1336  
    BOOST_ASSERT(!dest || dest == impl_.get(id_query).data());
1337  
    BOOST_ASSERT(
1337  
    BOOST_ASSERT(
1338  
        impl_.decoded_[id_path] ==
1338  
        impl_.decoded_[id_path] ==
1339  
        s.size() + make_absolute + 2 * add_dot_segment);
1339  
        s.size() + make_absolute + 2 * add_dot_segment);
1340  

1340  

1341  
//------------------------------------------------
1341  
//------------------------------------------------
1342  
//
1342  
//
1343  
//  Update path parameters
1343  
//  Update path parameters
1344  
//
1344  
//
1345  
    // get the encoded_path with the replacements we applied
1345  
    // get the encoded_path with the replacements we applied
1346  
    if (s == "/")
1346  
    if (s == "/")
1347  
    {
1347  
    {
1348  
        // "/" maps to sequence {}
1348  
        // "/" maps to sequence {}
1349  
        impl_.nseg_ = 0;
1349  
        impl_.nseg_ = 0;
1350  
    }
1350  
    }
1351  
    else if (!s.empty())
1351  
    else if (!s.empty())
1352  
    {
1352  
    {
1353  
        if (s.starts_with("/./"))
1353  
        if (s.starts_with("/./"))
1354  
            s = s.substr(2);
1354  
            s = s.substr(2);
1355  
        // count segments as number of '/'s + 1
1355  
        // count segments as number of '/'s + 1
1356  
        impl_.nseg_ = std::count(
1356  
        impl_.nseg_ = std::count(
1357  
            s.begin() + 1, s.end(), '/') + 1;
1357  
            s.begin() + 1, s.end(), '/') + 1;
1358  
    }
1358  
    }
1359  
    else
1359  
    else
1360  
    {
1360  
    {
1361  
        // an empty relative path maps to sequence {}
1361  
        // an empty relative path maps to sequence {}
1362  
        impl_.nseg_ = 0;
1362  
        impl_.nseg_ = 0;
1363  
    }
1363  
    }
1364  

1364  

1365  
    check_invariants();
1365  
    check_invariants();
1366  
    return *this;
1366  
    return *this;
1367  
}
1367  
}
1368  

1368  

1369  
url_base&
1369  
url_base&
1370  
url_base::
1370  
url_base::
1371  
set_encoded_path(
1371  
set_encoded_path(
1372  
    pct_string_view s)
1372  
    pct_string_view s)
1373  
{
1373  
{
1374  
    op_t op(*this, &detail::ref(s));
1374  
    op_t op(*this, &detail::ref(s));
1375  

1375  

1376  
//------------------------------------------------
1376  
//------------------------------------------------
1377  
//
1377  
//
1378  
//  Calculate re-encoded output size
1378  
//  Calculate re-encoded output size
1379  
//
1379  
//
1380  
// - reserved path chars are re-encoded
1380  
// - reserved path chars are re-encoded
1381  
// - colons in first segment might need to be re-encoded
1381  
// - colons in first segment might need to be re-encoded
1382  
// - the path might need to receive a prefix
1382  
// - the path might need to receive a prefix
1383  
    auto const n = detail::re_encoded_size_unsafe(
1383  
    auto const n = detail::re_encoded_size_unsafe(
1384  
        s, detail::path_chars);
1384  
        s, detail::path_chars);
1385  
    std::size_t n_reencode_colons = 0;
1385  
    std::size_t n_reencode_colons = 0;
1386  
    core::string_view first_seg;
1386  
    core::string_view first_seg;
1387  
    if (!has_scheme() &&
1387  
    if (!has_scheme() &&
1388  
        !has_authority() &&
1388  
        !has_authority() &&
1389  
        !s.starts_with('/'))
1389  
        !s.starts_with('/'))
1390  
    {
1390  
    {
1391  
        // the first segment with unencoded colons would look
1391  
        // the first segment with unencoded colons would look
1392  
        // like the scheme
1392  
        // like the scheme
1393  
        first_seg = detail::to_sv(s);
1393  
        first_seg = detail::to_sv(s);
1394  
        std::size_t p = s.find('/');
1394  
        std::size_t p = s.find('/');
1395  
        if (p != core::string_view::npos)
1395  
        if (p != core::string_view::npos)
1396  
            first_seg = s.substr(0, p);
1396  
            first_seg = s.substr(0, p);
1397  
        n_reencode_colons = std::count(
1397  
        n_reencode_colons = std::count(
1398  
            first_seg.begin(), first_seg.end(), ':');
1398  
            first_seg.begin(), first_seg.end(), ':');
1399  
    }
1399  
    }
1400  
    // the authority can only be followed by an empty or relative path
1400  
    // the authority can only be followed by an empty or relative path
1401  
    // if we have an authority and the path is a non-empty relative path, we
1401  
    // if we have an authority and the path is a non-empty relative path, we
1402  
    // add the "/" prefix to make it valid.
1402  
    // add the "/" prefix to make it valid.
1403  
    bool make_absolute =
1403  
    bool make_absolute =
1404  
        has_authority() &&
1404  
        has_authority() &&
1405  
        !s.starts_with('/') &&
1405  
        !s.starts_with('/') &&
1406  
        !s.empty();
1406  
        !s.empty();
1407  
    // a path starting with "//" might look like the authority
1407  
    // a path starting with "//" might look like the authority
1408  
    // we add a "/." prefix to prevent that
1408  
    // we add a "/." prefix to prevent that
1409  
    bool add_dot_segment =
1409  
    bool add_dot_segment =
1410  
        !make_absolute &&
1410  
        !make_absolute &&
1411  
        !has_authority() &&
1411  
        !has_authority() &&
1412  
        s.starts_with("//");
1412  
        s.starts_with("//");
1413  

1413  

1414  
//------------------------------------------------
1414  
//------------------------------------------------
1415  
//
1415  
//
1416  
//  Re-encode data
1416  
//  Re-encode data
1417  
//
1417  
//
1418  
    auto dest = set_path_impl(
1418  
    auto dest = set_path_impl(
1419  
        n + make_absolute + 2 * n_reencode_colons + 2 * add_dot_segment, op);
1419  
        n + make_absolute + 2 * n_reencode_colons + 2 * add_dot_segment, op);
1420  
    impl_.decoded_[id_path] = 0;
1420  
    impl_.decoded_[id_path] = 0;
1421  
    if (!dest)
1421  
    if (!dest)
1422  
    {
1422  
    {
1423  
        impl_.nseg_ = 0;
1423  
        impl_.nseg_ = 0;
1424  
        return *this;
1424  
        return *this;
1425  
    }
1425  
    }
1426  
    if (make_absolute)
1426  
    if (make_absolute)
1427  
    {
1427  
    {
1428  
        *dest++ = '/';
1428  
        *dest++ = '/';
1429  
        impl_.decoded_[id_path] += 1;
1429  
        impl_.decoded_[id_path] += 1;
1430  
    }
1430  
    }
1431  
    else if (add_dot_segment)
1431  
    else if (add_dot_segment)
1432  
    {
1432  
    {
1433  
        *dest++ = '/';
1433  
        *dest++ = '/';
1434  
        *dest++ = '.';
1434  
        *dest++ = '.';
1435  
        impl_.decoded_[id_path] += 2;
1435  
        impl_.decoded_[id_path] += 2;
1436  
    }
1436  
    }
1437  
    impl_.decoded_[id_path] +=
1437  
    impl_.decoded_[id_path] +=
1438  
        detail::re_encode_unsafe(
1438  
        detail::re_encode_unsafe(
1439  
            dest,
1439  
            dest,
1440  
            impl_.get(id_query).data(),
1440  
            impl_.get(id_query).data(),
1441  
            first_seg,
1441  
            first_seg,
1442  
            detail::segment_chars - ':');
1442  
            detail::segment_chars - ':');
1443  
    impl_.decoded_[id_path] +=
1443  
    impl_.decoded_[id_path] +=
1444  
        detail::re_encode_unsafe(
1444  
        detail::re_encode_unsafe(
1445  
            dest,
1445  
            dest,
1446  
            impl_.get(id_query).data(),
1446  
            impl_.get(id_query).data(),
1447  
            s.substr(first_seg.size()),
1447  
            s.substr(first_seg.size()),
1448  
            detail::path_chars);
1448  
            detail::path_chars);
1449  
    BOOST_ASSERT(dest == impl_.get(id_query).data());
1449  
    BOOST_ASSERT(dest == impl_.get(id_query).data());
1450  
    BOOST_ASSERT(
1450  
    BOOST_ASSERT(
1451  
        impl_.decoded_[id_path] ==
1451  
        impl_.decoded_[id_path] ==
1452  
        s.decoded_size() + make_absolute + 2 * add_dot_segment);
1452  
        s.decoded_size() + make_absolute + 2 * add_dot_segment);
1453  

1453  

1454  
//------------------------------------------------
1454  
//------------------------------------------------
1455  
//
1455  
//
1456  
//  Update path parameters
1456  
//  Update path parameters
1457  
//
1457  
//
1458  
    // get the encoded_path with the replacements we applied
1458  
    // get the encoded_path with the replacements we applied
1459  
    if (s == "/")
1459  
    if (s == "/")
1460  
    {
1460  
    {
1461  
        // "/" maps to sequence {}
1461  
        // "/" maps to sequence {}
1462  
        impl_.nseg_ = 0;
1462  
        impl_.nseg_ = 0;
1463  
    }
1463  
    }
1464  
    else if (!s.empty())
1464  
    else if (!s.empty())
1465  
    {
1465  
    {
1466  
        if (s.starts_with("/./"))
1466  
        if (s.starts_with("/./"))
1467  
            s = s.substr(2);
1467  
            s = s.substr(2);
1468  
        // count segments as number of '/'s + 1
1468  
        // count segments as number of '/'s + 1
1469  
        impl_.nseg_ = std::count(
1469  
        impl_.nseg_ = std::count(
1470  
            s.begin() + 1, s.end(), '/') + 1;
1470  
            s.begin() + 1, s.end(), '/') + 1;
1471  
    }
1471  
    }
1472  
    else
1472  
    else
1473  
    {
1473  
    {
1474  
        // an empty relative path maps to sequence {}
1474  
        // an empty relative path maps to sequence {}
1475  
        impl_.nseg_ = 0;
1475  
        impl_.nseg_ = 0;
1476  
    }
1476  
    }
1477  

1477  

1478  
    check_invariants();
1478  
    check_invariants();
1479  
    return *this;
1479  
    return *this;
1480  
}
1480  
}
1481  

1481  

1482  
segments_ref
1482  
segments_ref
1483  
url_base::
1483  
url_base::
1484  
segments() noexcept
1484  
segments() noexcept
1485  
{
1485  
{
1486  
    return {*this};
1486  
    return {*this};
1487  
}
1487  
}
1488  

1488  

1489  
segments_encoded_ref
1489  
segments_encoded_ref
1490  
url_base::
1490  
url_base::
1491  
encoded_segments() noexcept
1491  
encoded_segments() noexcept
1492  
{
1492  
{
1493  
    return {*this};
1493  
    return {*this};
1494  
}
1494  
}
1495  

1495  

1496  
//------------------------------------------------
1496  
//------------------------------------------------
1497  
//
1497  
//
1498  
// Query
1498  
// Query
1499  
//
1499  
//
1500  
//------------------------------------------------
1500  
//------------------------------------------------
1501  

1501  

1502  
url_base&
1502  
url_base&
1503  
url_base::
1503  
url_base::
1504  
set_query(
1504  
set_query(
1505  
    core::string_view s)
1505  
    core::string_view s)
1506  
{
1506  
{
1507  
    edit_params(
1507  
    edit_params(
1508  
        detail::params_iter_impl(impl_),
1508  
        detail::params_iter_impl(impl_),
1509  
        detail::params_iter_impl(impl_, 0),
1509  
        detail::params_iter_impl(impl_, 0),
1510  
        detail::query_string_iter(s, true));
1510  
        detail::query_string_iter(s, true));
1511  
    return *this;
1511  
    return *this;
1512  
}
1512  
}
1513  

1513  

1514  
url_base&
1514  
url_base&
1515  
url_base::
1515  
url_base::
1516  
set_encoded_query(
1516  
set_encoded_query(
1517  
    pct_string_view s)
1517  
    pct_string_view s)
1518  
{
1518  
{
1519  
    op_t op(*this);
1519  
    op_t op(*this);
1520  
    std::size_t n = 0;      // encoded size
1520  
    std::size_t n = 0;      // encoded size
1521  
    std::size_t nparam = 1; // param count
1521  
    std::size_t nparam = 1; // param count
1522  
    auto const end = s.end();
1522  
    auto const end = s.end();
1523  
    auto p = s.begin();
1523  
    auto p = s.begin();
1524  

1524  

1525  
    // measure
1525  
    // measure
1526  
    while(p != end)
1526  
    while(p != end)
1527  
    {
1527  
    {
1528  
        if(*p == '&')
1528  
        if(*p == '&')
1529  
        {
1529  
        {
1530  
            ++p;
1530  
            ++p;
1531  
            ++n;
1531  
            ++n;
1532  
            ++nparam;
1532  
            ++nparam;
1533  
        }
1533  
        }
1534  
        else if(*p != '%')
1534  
        else if(*p != '%')
1535  
        {
1535  
        {
1536  
            if(detail::query_chars(*p))
1536  
            if(detail::query_chars(*p))
1537  
                n += 1; // allowed
1537  
                n += 1; // allowed
1538  
            else
1538  
            else
1539  
                n += 3; // escaped
1539  
                n += 3; // escaped
1540  
            ++p;
1540  
            ++p;
1541  
        }
1541  
        }
1542  
        else
1542  
        else
1543  
        {
1543  
        {
1544  
            // escape
1544  
            // escape
1545  
            n += 3;
1545  
            n += 3;
1546  
            p += 3;
1546  
            p += 3;
1547  
        }
1547  
        }
1548  
    }
1548  
    }
1549  

1549  

1550  
    // resize
1550  
    // resize
1551  
    auto dest = resize_impl(
1551  
    auto dest = resize_impl(
1552  
        id_query, n + 1, op);
1552  
        id_query, n + 1, op);
1553  
    *dest++ = '?';
1553  
    *dest++ = '?';
1554  

1554  

1555  
    // encode
1555  
    // encode
1556  
    impl_.decoded_[id_query] =
1556  
    impl_.decoded_[id_query] =
1557  
        detail::re_encode_unsafe(
1557  
        detail::re_encode_unsafe(
1558  
            dest,
1558  
            dest,
1559  
            dest + n,
1559  
            dest + n,
1560  
            s,
1560  
            s,
1561  
            detail::query_chars);
1561  
            detail::query_chars);
1562  
    BOOST_ASSERT(
1562  
    BOOST_ASSERT(
1563  
        impl_.decoded_[id_query] ==
1563  
        impl_.decoded_[id_query] ==
1564  
            s.decoded_size());
1564  
            s.decoded_size());
1565  
    impl_.nparam_ = nparam;
1565  
    impl_.nparam_ = nparam;
1566  
    return *this;
1566  
    return *this;
1567  
}
1567  
}
1568  

1568  

1569  
params_ref
1569  
params_ref
1570  
url_base::
1570  
url_base::
1571  
params() noexcept
1571  
params() noexcept
1572  
{
1572  
{
1573  
    return params_ref(
1573  
    return params_ref(
1574  
        *this,
1574  
        *this,
1575  
        encoding_opts{
1575  
        encoding_opts{
1576  
            true, false, false});
1576  
            true, false, false});
1577  
}
1577  
}
1578  

1578  

1579  
params_ref
1579  
params_ref
1580  
url_base::
1580  
url_base::
1581  
params(encoding_opts opt) noexcept
1581  
params(encoding_opts opt) noexcept
1582  
{
1582  
{
1583  
    return {*this, opt};
1583  
    return {*this, opt};
1584  
}
1584  
}
1585  

1585  

1586  
params_encoded_ref
1586  
params_encoded_ref
1587  
url_base::
1587  
url_base::
1588  
encoded_params() noexcept
1588  
encoded_params() noexcept
1589  
{
1589  
{
1590  
    return {*this};
1590  
    return {*this};
1591  
}
1591  
}
1592  

1592  

1593  
url_base&
1593  
url_base&
1594  
url_base::
1594  
url_base::
1595  
set_params(
1595  
set_params(
1596  
    std::initializer_list<param_view> ps,
1596  
    std::initializer_list<param_view> ps,
1597  
    encoding_opts opts) noexcept
1597  
    encoding_opts opts) noexcept
1598  
{
1598  
{
1599  
    params(opts).assign(ps);
1599  
    params(opts).assign(ps);
1600  
    return *this;
1600  
    return *this;
1601  
}
1601  
}
1602  

1602  

1603  
url_base&
1603  
url_base&
1604  
url_base::
1604  
url_base::
1605  
set_encoded_params( std::initializer_list< param_pct_view > ps ) noexcept
1605  
set_encoded_params( std::initializer_list< param_pct_view > ps ) noexcept
1606  
{
1606  
{
1607  
    encoded_params().assign(ps);
1607  
    encoded_params().assign(ps);
1608  
    return *this;
1608  
    return *this;
1609  
}
1609  
}
1610  

1610  

1611  
url_base&
1611  
url_base&
1612  
url_base::
1612  
url_base::
1613  
remove_query() noexcept
1613  
remove_query() noexcept
1614  
{
1614  
{
1615  
    op_t op(*this);
1615  
    op_t op(*this);
1616  
    resize_impl(id_query, 0, op);
1616  
    resize_impl(id_query, 0, op);
1617  
    impl_.nparam_ = 0;
1617  
    impl_.nparam_ = 0;
1618  
    impl_.decoded_[id_query] = 0;
1618  
    impl_.decoded_[id_query] = 0;
1619  
    return *this;
1619  
    return *this;
1620  
}
1620  
}
1621  

1621  

1622  
//------------------------------------------------
1622  
//------------------------------------------------
1623  
//
1623  
//
1624  
// Fragment
1624  
// Fragment
1625  
//
1625  
//
1626  
//------------------------------------------------
1626  
//------------------------------------------------
1627  

1627  

1628  
url_base&
1628  
url_base&
1629  
url_base::
1629  
url_base::
1630  
remove_fragment() noexcept
1630  
remove_fragment() noexcept
1631  
{
1631  
{
1632  
    op_t op(*this);
1632  
    op_t op(*this);
1633  
    resize_impl(id_frag, 0, op);
1633  
    resize_impl(id_frag, 0, op);
1634  
    impl_.decoded_[id_frag] = 0;
1634  
    impl_.decoded_[id_frag] = 0;
1635  
    return *this;
1635  
    return *this;
1636  
}
1636  
}
1637  

1637  

1638  
url_base&
1638  
url_base&
1639  
url_base::
1639  
url_base::
1640  
set_fragment(core::string_view s)
1640  
set_fragment(core::string_view s)
1641  
{
1641  
{
1642  
    op_t op(*this, &s);
1642  
    op_t op(*this, &s);
1643  
    encoding_opts opt;
1643  
    encoding_opts opt;
1644  
    auto const n = encoded_size(
1644  
    auto const n = encoded_size(
1645  
        s,
1645  
        s,
1646  
        detail::fragment_chars,
1646  
        detail::fragment_chars,
1647  
        opt);
1647  
        opt);
1648  
    auto dest = resize_impl(
1648  
    auto dest = resize_impl(
1649  
        id_frag, n + 1, op);
1649  
        id_frag, n + 1, op);
1650  
    *dest++ = '#';
1650  
    *dest++ = '#';
1651  
    encode_unsafe(
1651  
    encode_unsafe(
1652  
        dest,
1652  
        dest,
1653  
        n,
1653  
        n,
1654  
        s,
1654  
        s,
1655  
        detail::fragment_chars,
1655  
        detail::fragment_chars,
1656  
        opt);
1656  
        opt);
1657  
    impl_.decoded_[id_frag] = s.size();
1657  
    impl_.decoded_[id_frag] = s.size();
1658  
    return *this;
1658  
    return *this;
1659  
}
1659  
}
1660  

1660  

1661  
url_base&
1661  
url_base&
1662  
url_base::
1662  
url_base::
1663  
set_encoded_fragment(
1663  
set_encoded_fragment(
1664  
    pct_string_view s)
1664  
    pct_string_view s)
1665  
{
1665  
{
1666  
    op_t op(*this, &detail::ref(s));
1666  
    op_t op(*this, &detail::ref(s));
1667  
    auto const n =
1667  
    auto const n =
1668  
        detail::re_encoded_size_unsafe(
1668  
        detail::re_encoded_size_unsafe(
1669  
            s,
1669  
            s,
1670  
            detail::fragment_chars);
1670  
            detail::fragment_chars);
1671  
    auto dest = resize_impl(
1671  
    auto dest = resize_impl(
1672  
        id_frag, n + 1, op);
1672  
        id_frag, n + 1, op);
1673  
    *dest++ = '#';
1673  
    *dest++ = '#';
1674  
    impl_.decoded_[id_frag] =
1674  
    impl_.decoded_[id_frag] =
1675  
        detail::re_encode_unsafe(
1675  
        detail::re_encode_unsafe(
1676  
            dest,
1676  
            dest,
1677  
            dest + n,
1677  
            dest + n,
1678  
            s,
1678  
            s,
1679  
            detail::fragment_chars);
1679  
            detail::fragment_chars);
1680  
    BOOST_ASSERT(
1680  
    BOOST_ASSERT(
1681  
        impl_.decoded_[id_frag] ==
1681  
        impl_.decoded_[id_frag] ==
1682  
            s.decoded_size());
1682  
            s.decoded_size());
1683  
    return *this;
1683  
    return *this;
1684  
}
1684  
}
1685  

1685  

1686  
//------------------------------------------------
1686  
//------------------------------------------------
1687  
//
1687  
//
1688  
// Resolution
1688  
// Resolution
1689  
//
1689  
//
1690  
//------------------------------------------------
1690  
//------------------------------------------------
1691  

1691  

1692  
system::result<void>
1692  
system::result<void>
1693  
url_base::
1693  
url_base::
1694  
resolve(
1694  
resolve(
1695  
    url_view_base const& ref)
1695  
    url_view_base const& ref)
1696  
{
1696  
{
1697  
    if (this == &ref &&
1697  
    if (this == &ref &&
1698  
        has_scheme())
1698  
        has_scheme())
1699  
    {
1699  
    {
1700  
        normalize_path();
1700  
        normalize_path();
1701  
        return {};
1701  
        return {};
1702  
    }
1702  
    }
1703  

1703  

1704  
    if(! has_scheme())
1704  
    if(! has_scheme())
1705  
    {
1705  
    {
1706  
        BOOST_URL_RETURN_EC(error::not_a_base);
1706  
        BOOST_URL_RETURN_EC(error::not_a_base);
1707  
    }
1707  
    }
1708  

1708  

1709  
    op_t op(*this);
1709  
    op_t op(*this);
1710  

1710  

1711  
    //
1711  
    //
1712  
    // 5.2.2. Transform References
1712  
    // 5.2.2. Transform References
1713  
    // https://datatracker.ietf.org/doc/html/rfc3986#section-5.2.2
1713  
    // https://datatracker.ietf.org/doc/html/rfc3986#section-5.2.2
1714  
    //
1714  
    //
1715  

1715  

1716  
    if( ref.has_scheme() &&
1716  
    if( ref.has_scheme() &&
1717  
        ref.scheme() != scheme())
1717  
        ref.scheme() != scheme())
1718  
    {
1718  
    {
1719  
        reserve_impl(ref.size(), op);
1719  
        reserve_impl(ref.size(), op);
1720  
        copy(ref);
1720  
        copy(ref);
1721  
        normalize_path();
1721  
        normalize_path();
1722  
        return {};
1722  
        return {};
1723  
    }
1723  
    }
1724  
    if(ref.has_authority())
1724  
    if(ref.has_authority())
1725  
    {
1725  
    {
1726  
        reserve_impl(
1726  
        reserve_impl(
1727  
            impl_.offset(id_user) + ref.size(), op);
1727  
            impl_.offset(id_user) + ref.size(), op);
1728  
        set_encoded_authority(
1728  
        set_encoded_authority(
1729  
            ref.encoded_authority());
1729  
            ref.encoded_authority());
1730  
        set_encoded_path(
1730  
        set_encoded_path(
1731  
            ref.encoded_path());
1731  
            ref.encoded_path());
1732  
        if (ref.encoded_path().empty())
1732  
        if (ref.encoded_path().empty())
1733  
            set_path_absolute(false);
1733  
            set_path_absolute(false);
1734  
        else
1734  
        else
1735  
            normalize_path();
1735  
            normalize_path();
1736  
        if(ref.has_query())
1736  
        if(ref.has_query())
1737  
            set_encoded_query(
1737  
            set_encoded_query(
1738  
                ref.encoded_query());
1738  
                ref.encoded_query());
1739  
        else
1739  
        else
1740  
            remove_query();
1740  
            remove_query();
1741  
        if(ref.has_fragment())
1741  
        if(ref.has_fragment())
1742  
            set_encoded_fragment(
1742  
            set_encoded_fragment(
1743  
                ref.encoded_fragment());
1743  
                ref.encoded_fragment());
1744  
        else
1744  
        else
1745  
            remove_fragment();
1745  
            remove_fragment();
1746  
        return {};
1746  
        return {};
1747  
    }
1747  
    }
1748  
    if(ref.encoded_path().empty())
1748  
    if(ref.encoded_path().empty())
1749  
    {
1749  
    {
1750  
        reserve_impl(
1750  
        reserve_impl(
1751  
            impl_.offset(id_query) +
1751  
            impl_.offset(id_query) +
1752  
            ref.size(), op);
1752  
            ref.size(), op);
1753  
        normalize_path();
1753  
        normalize_path();
1754  
        if(ref.has_query())
1754  
        if(ref.has_query())
1755  
        {
1755  
        {
1756  
            set_encoded_query(
1756  
            set_encoded_query(
1757  
                ref.encoded_query());
1757  
                ref.encoded_query());
1758  
        }
1758  
        }
1759  
        if(ref.has_fragment())
1759  
        if(ref.has_fragment())
1760  
            set_encoded_fragment(
1760  
            set_encoded_fragment(
1761  
                ref.encoded_fragment());
1761  
                ref.encoded_fragment());
1762  
        else
1762  
        else
1763  
            remove_fragment();
1763  
            remove_fragment();
1764  
        return {};
1764  
        return {};
1765  
    }
1765  
    }
1766  
    if(ref.is_path_absolute())
1766  
    if(ref.is_path_absolute())
1767  
    {
1767  
    {
1768  
        reserve_impl(
1768  
        reserve_impl(
1769  
            impl_.offset(id_path) +
1769  
            impl_.offset(id_path) +
1770  
                ref.size(), op);
1770  
                ref.size(), op);
1771  
        set_encoded_path(
1771  
        set_encoded_path(
1772  
            ref.encoded_path());
1772  
            ref.encoded_path());
1773  
        normalize_path();
1773  
        normalize_path();
1774  
        if(ref.has_query())
1774  
        if(ref.has_query())
1775  
            set_encoded_query(
1775  
            set_encoded_query(
1776  
                ref.encoded_query());
1776  
                ref.encoded_query());
1777  
        else
1777  
        else
1778  
            remove_query();
1778  
            remove_query();
1779  
        if(ref.has_fragment())
1779  
        if(ref.has_fragment())
1780  
            set_encoded_fragment(
1780  
            set_encoded_fragment(
1781  
                ref.encoded_fragment());
1781  
                ref.encoded_fragment());
1782  
        else
1782  
        else
1783  
            remove_fragment();
1783  
            remove_fragment();
1784  
        return {};
1784  
        return {};
1785  
    }
1785  
    }
1786  
    // General case: ref is relative path
1786  
    // General case: ref is relative path
1787  
    reserve_impl(
1787  
    reserve_impl(
1788  
        impl_.offset(id_query) +
1788  
        impl_.offset(id_query) +
1789  
        ref.size(), op);
1789  
        ref.size(), op);
1790  
    // 5.2.3. Merge Paths
1790  
    // 5.2.3. Merge Paths
1791  
    auto es = encoded_segments();
1791  
    auto es = encoded_segments();
1792  
    if(es.size() > 0)
1792  
    if(es.size() > 0)
1793  
    {
1793  
    {
1794  
        es.pop_back();
1794  
        es.pop_back();
1795  
    }
1795  
    }
1796  
    es.insert(es.end(),
1796  
    es.insert(es.end(),
1797  
        ref.encoded_segments().begin(),
1797  
        ref.encoded_segments().begin(),
1798  
        ref.encoded_segments().end());
1798  
        ref.encoded_segments().end());
1799  
    normalize_path();
1799  
    normalize_path();
1800  
    if(ref.has_query())
1800  
    if(ref.has_query())
1801  
        set_encoded_query(
1801  
        set_encoded_query(
1802  
            ref.encoded_query());
1802  
            ref.encoded_query());
1803  
    else
1803  
    else
1804  
        remove_query();
1804  
        remove_query();
1805  
    if(ref.has_fragment())
1805  
    if(ref.has_fragment())
1806  
        set_encoded_fragment(
1806  
        set_encoded_fragment(
1807  
            ref.encoded_fragment());
1807  
            ref.encoded_fragment());
1808  
    else
1808  
    else
1809  
        remove_fragment();
1809  
        remove_fragment();
1810  
    return {};
1810  
    return {};
1811  
}
1811  
}
1812  

1812  

1813  
//------------------------------------------------
1813  
//------------------------------------------------
1814  
//
1814  
//
1815  
// Normalization
1815  
// Normalization
1816  
//
1816  
//
1817  
//------------------------------------------------
1817  
//------------------------------------------------
1818  

1818  

1819  
template <
1819  
template <
1820  
    class AllowedCharset,
1820  
    class AllowedCharset,
1821  
    class IgnoredCharset>
1821  
    class IgnoredCharset>
1822  
void
1822  
void
1823  
url_base::
1823  
url_base::
1824  
normalize_octets_impl(
1824  
normalize_octets_impl(
1825  
    int id,
1825  
    int id,
1826  
    AllowedCharset const& allowed,
1826  
    AllowedCharset const& allowed,
1827  
    IgnoredCharset const& ignored,
1827  
    IgnoredCharset const& ignored,
1828  
    op_t& op) noexcept
1828  
    op_t& op) noexcept
1829  
{
1829  
{
1830  
    char* it = s_ + impl_.offset(id);
1830  
    char* it = s_ + impl_.offset(id);
1831  
    char* end = s_ + impl_.offset(id + 1);
1831  
    char* end = s_ + impl_.offset(id + 1);
1832  
    char d = 0;
1832  
    char d = 0;
1833  
    char* dest = it;
1833  
    char* dest = it;
1834  
    while (it < end)
1834  
    while (it < end)
1835  
    {
1835  
    {
1836  
        if (*it != '%')
1836  
        if (*it != '%')
1837  
        {
1837  
        {
1838  
            *dest = *it;
1838  
            *dest = *it;
1839  
            ++it;
1839  
            ++it;
1840  
            ++dest;
1840  
            ++dest;
1841  
            continue;
1841  
            continue;
1842  
        }
1842  
        }
1843  
        BOOST_ASSERT(end - it >= 3);
1843  
        BOOST_ASSERT(end - it >= 3);
1844  

1844  

1845  
        // decode unreserved octets
1845  
        // decode unreserved octets
1846  
        d = detail::decode_one(it + 1);
1846  
        d = detail::decode_one(it + 1);
1847  
        if (allowed(d) &&
1847  
        if (allowed(d) &&
1848  
            !ignored(d))
1848  
            !ignored(d))
1849  
        {
1849  
        {
1850  
            *dest = d;
1850  
            *dest = d;
1851  
            it += 3;
1851  
            it += 3;
1852  
            ++dest;
1852  
            ++dest;
1853  
            continue;
1853  
            continue;
1854  
        }
1854  
        }
1855  

1855  

1856  
        // uppercase percent-encoding triplets
1856  
        // uppercase percent-encoding triplets
1857  
        *dest++ = '%';
1857  
        *dest++ = '%';
1858  
        ++it;
1858  
        ++it;
1859  
        *dest++ = grammar::to_upper(*it++);
1859  
        *dest++ = grammar::to_upper(*it++);
1860  
        *dest++ = grammar::to_upper(*it++);
1860  
        *dest++ = grammar::to_upper(*it++);
1861  
    }
1861  
    }
1862  
    if (it != dest)
1862  
    if (it != dest)
1863  
    {
1863  
    {
1864  
        auto diff = it - dest;
1864  
        auto diff = it - dest;
1865  
        auto n = impl_.len(id) - diff;
1865  
        auto n = impl_.len(id) - diff;
1866  
        shrink_impl(id, n, op);
1866  
        shrink_impl(id, n, op);
1867  
        s_[size()] = '\0';
1867  
        s_[size()] = '\0';
1868  
    }
1868  
    }
1869  
}
1869  
}
1870  

1870  

1871  
template<class CharSet>
1871  
template<class CharSet>
1872  
void
1872  
void
1873  
url_base::
1873  
url_base::
1874  
normalize_octets_impl(
1874  
normalize_octets_impl(
1875  
    int idx,
1875  
    int idx,
1876  
    CharSet const& allowed,
1876  
    CharSet const& allowed,
1877  
    op_t& op) noexcept
1877  
    op_t& op) noexcept
1878  
{
1878  
{
1879  
    return normalize_octets_impl(
1879  
    return normalize_octets_impl(
1880  
        idx, allowed, detail::empty_chars, op);
1880  
        idx, allowed, detail::empty_chars, op);
1881  
}
1881  
}
1882  

1882  

1883  
url_base&
1883  
url_base&
1884  
url_base::
1884  
url_base::
1885  
normalize_scheme()
1885  
normalize_scheme()
1886  
{
1886  
{
1887  
    to_lower_impl(id_scheme);
1887  
    to_lower_impl(id_scheme);
1888  
    return *this;
1888  
    return *this;
1889  
}
1889  
}
1890  

1890  

1891  
url_base&
1891  
url_base&
1892  
url_base::
1892  
url_base::
1893  
normalize_authority()
1893  
normalize_authority()
1894  
{
1894  
{
1895  
    op_t op(*this);
1895  
    op_t op(*this);
1896  

1896  

1897  
    // normalize host
1897  
    // normalize host
1898  
    if (host_type() == urls::host_type::name)
1898  
    if (host_type() == urls::host_type::name)
1899  
    {
1899  
    {
1900  
        normalize_octets_impl(
1900  
        normalize_octets_impl(
1901  
            id_host,
1901  
            id_host,
1902  
            detail::reg_name_chars, op);
1902  
            detail::reg_name_chars, op);
1903  
    }
1903  
    }
1904  
    decoded_to_lower_impl(id_host);
1904  
    decoded_to_lower_impl(id_host);
1905  

1905  

1906  
    // normalize password
1906  
    // normalize password
1907  
    normalize_octets_impl(id_pass, detail::password_chars, op);
1907  
    normalize_octets_impl(id_pass, detail::password_chars, op);
1908  

1908  

1909  
    // normalize user
1909  
    // normalize user
1910  
    normalize_octets_impl(id_user, detail::user_chars, op);
1910  
    normalize_octets_impl(id_user, detail::user_chars, op);
1911  
    return *this;
1911  
    return *this;
1912  
}
1912  
}
1913  

1913  

1914  
url_base&
1914  
url_base&
1915  
url_base::
1915  
url_base::
1916  
normalize_path()
1916  
normalize_path()
1917  
{
1917  
{
1918  
    op_t op(*this);
1918  
    op_t op(*this);
1919  
    normalize_octets_impl(id_path, detail::segment_chars, op);
1919  
    normalize_octets_impl(id_path, detail::segment_chars, op);
1920  
    core::string_view p = impl_.get(id_path);
1920  
    core::string_view p = impl_.get(id_path);
1921  
    char* p_dest = s_ + impl_.offset(id_path);
1921  
    char* p_dest = s_ + impl_.offset(id_path);
1922  
    char* p_end = s_ + impl_.offset(id_path + 1);
1922  
    char* p_end = s_ + impl_.offset(id_path + 1);
1923  
    auto pn = p.size();
1923  
    auto pn = p.size();
1924  
    auto skip_dot = 0;
1924  
    auto skip_dot = 0;
1925  
    bool encode_colons = false;
1925  
    bool encode_colons = false;
1926  
    core::string_view first_seg;
1926  
    core::string_view first_seg;
1927  

1927  

1928  
//------------------------------------------------
1928  
//------------------------------------------------
1929  
//
1929  
//
1930  
//  Determine unnecessary initial dot segments to skip and
1930  
//  Determine unnecessary initial dot segments to skip and
1931  
//  if we need to encode colons in the first segment
1931  
//  if we need to encode colons in the first segment
1932  
//
1932  
//
1933  
    if (
1933  
    if (
1934  
        !has_authority() &&
1934  
        !has_authority() &&
1935  
        p.starts_with("/./"))
1935  
        p.starts_with("/./"))
1936  
    {
1936  
    {
1937  
        // check if removing the "/./" would result in "//"
1937  
        // check if removing the "/./" would result in "//"
1938  
        // ex: "/.//", "/././/", "/././/", ...
1938  
        // ex: "/.//", "/././/", "/././/", ...
1939  
        skip_dot = 2;
1939  
        skip_dot = 2;
1940  
        while (p.substr(skip_dot, 3).starts_with("/./"))
1940  
        while (p.substr(skip_dot, 3).starts_with("/./"))
1941  
            skip_dot += 2;
1941  
            skip_dot += 2;
1942  
        if (p.substr(skip_dot).starts_with("//"))
1942  
        if (p.substr(skip_dot).starts_with("//"))
1943  
            skip_dot = 2;
1943  
            skip_dot = 2;
1944  
        else
1944  
        else
1945  
            skip_dot = 0;
1945  
            skip_dot = 0;
1946  
    }
1946  
    }
1947  
    else if (
1947  
    else if (
1948  
        !has_scheme() &&
1948  
        !has_scheme() &&
1949  
        !has_authority())
1949  
        !has_authority())
1950  
    {
1950  
    {
1951  
        if (p.starts_with("./"))
1951  
        if (p.starts_with("./"))
1952  
        {
1952  
        {
1953  
            // check if removing the "./" would result in "//"
1953  
            // check if removing the "./" would result in "//"
1954  
            // ex: ".//", "././/", "././/", ...
1954  
            // ex: ".//", "././/", "././/", ...
1955  
            skip_dot = 1;
1955  
            skip_dot = 1;
1956  
            while (p.substr(skip_dot, 3).starts_with("/./"))
1956  
            while (p.substr(skip_dot, 3).starts_with("/./"))
1957  
                skip_dot += 2;
1957  
                skip_dot += 2;
1958  
            if (p.substr(skip_dot).starts_with("//"))
1958  
            if (p.substr(skip_dot).starts_with("//"))
1959  
                skip_dot = 2;
1959  
                skip_dot = 2;
1960  
            else
1960  
            else
1961  
                skip_dot = 0;
1961  
                skip_dot = 0;
1962  

1962  

1963  
            if ( !skip_dot )
1963  
            if ( !skip_dot )
1964  
            {
1964  
            {
1965  
                // check if removing "./"s would leave us
1965  
                // check if removing "./"s would leave us
1966  
                // a first segment with an ambiguous ":"
1966  
                // a first segment with an ambiguous ":"
1967  
                first_seg = p.substr(2);
1967  
                first_seg = p.substr(2);
1968  
                while (first_seg.starts_with("./"))
1968  
                while (first_seg.starts_with("./"))
1969  
                    first_seg = first_seg.substr(2);
1969  
                    first_seg = first_seg.substr(2);
1970  
                auto i = first_seg.find('/');
1970  
                auto i = first_seg.find('/');
1971  
                if (i != core::string_view::npos)
1971  
                if (i != core::string_view::npos)
1972  
                    first_seg = first_seg.substr(0, i);
1972  
                    first_seg = first_seg.substr(0, i);
1973  
                encode_colons = first_seg.contains(':');
1973  
                encode_colons = first_seg.contains(':');
1974  
            }
1974  
            }
1975  
        }
1975  
        }
1976  
        else
1976  
        else
1977  
        {
1977  
        {
1978  
            // check if normalize_octets_impl
1978  
            // check if normalize_octets_impl
1979  
            // didn't already create a ":"
1979  
            // didn't already create a ":"
1980  
            // in the first segment
1980  
            // in the first segment
1981  
            first_seg = p;
1981  
            first_seg = p;
1982  
            auto i = first_seg.find('/');
1982  
            auto i = first_seg.find('/');
1983  
            if (i != core::string_view::npos)
1983  
            if (i != core::string_view::npos)
1984  
                first_seg = p.substr(0, i);
1984  
                first_seg = p.substr(0, i);
1985  
            encode_colons = first_seg.contains(':');
1985  
            encode_colons = first_seg.contains(':');
1986  
        }
1986  
        }
1987  
    }
1987  
    }
1988  

1988  

1989  
//------------------------------------------------
1989  
//------------------------------------------------
1990  
//
1990  
//
1991  
//  Encode colons in the first segment
1991  
//  Encode colons in the first segment
1992  
//
1992  
//
1993  
    if (encode_colons)
1993  
    if (encode_colons)
1994  
    {
1994  
    {
1995  
        // prepend with "./"
1995  
        // prepend with "./"
1996  
        // (resize_impl never throws)
1996  
        // (resize_impl never throws)
1997  
        auto cn =
1997  
        auto cn =
1998  
            std::count(
1998  
            std::count(
1999  
                first_seg.begin(),
1999  
                first_seg.begin(),
2000  
                first_seg.end(),
2000  
                first_seg.end(),
2001  
                ':');
2001  
                ':');
2002  
        resize_impl(
2002  
        resize_impl(
2003  
            id_path, pn + (2 * cn), op);
2003  
            id_path, pn + (2 * cn), op);
2004  
        // move the 2nd, 3rd, ... segments
2004  
        // move the 2nd, 3rd, ... segments
2005  
        auto begin = s_ + impl_.offset(id_path);
2005  
        auto begin = s_ + impl_.offset(id_path);
2006  
        auto it = begin;
2006  
        auto it = begin;
2007  
        auto end = begin + pn;
2007  
        auto end = begin + pn;
2008  
        while (core::string_view(it, 2) == "./")
2008  
        while (core::string_view(it, 2) == "./")
2009  
            it += 2;
2009  
            it += 2;
2010  
        while (*it != '/' &&
2010  
        while (*it != '/' &&
2011  
               it != end)
2011  
               it != end)
2012  
            ++it;
2012  
            ++it;
2013  
        // we don't need op here because this is
2013  
        // we don't need op here because this is
2014  
        // an internal operation
2014  
        // an internal operation
2015  
        std::memmove(it + (2 * cn), it, end - it);
2015  
        std::memmove(it + (2 * cn), it, end - it);
2016  

2016  

2017  
        // move 1st segment
2017  
        // move 1st segment
2018  
        auto src = s_ + impl_.offset(id_path) + pn;
2018  
        auto src = s_ + impl_.offset(id_path) + pn;
2019  
        auto dest = s_ + impl_.offset(id_query);
2019  
        auto dest = s_ + impl_.offset(id_query);
2020  
        src -= end - it;
2020  
        src -= end - it;
2021  
        dest -= end - it;
2021  
        dest -= end - it;
2022  
        pn -= end - it;
2022  
        pn -= end - it;
2023  
        do {
2023  
        do {
2024  
            --src;
2024  
            --src;
2025  
            --dest;
2025  
            --dest;
2026  
            if (*src != ':')
2026  
            if (*src != ':')
2027  
            {
2027  
            {
2028  
                *dest = *src;
2028  
                *dest = *src;
2029  
            }
2029  
            }
2030  
            else
2030  
            else
2031  
            {
2031  
            {
2032  
                // use uppercase as required by
2032  
                // use uppercase as required by
2033  
                // syntax-based normalization
2033  
                // syntax-based normalization
2034  
                *dest-- = 'A';
2034  
                *dest-- = 'A';
2035  
                *dest-- = '3';
2035  
                *dest-- = '3';
2036  
                *dest = '%';
2036  
                *dest = '%';
2037  
            }
2037  
            }
2038  
            --pn;
2038  
            --pn;
2039  
        } while (pn);
2039  
        } while (pn);
2040  
        skip_dot = 0;
2040  
        skip_dot = 0;
2041  
        p = impl_.get(id_path);
2041  
        p = impl_.get(id_path);
2042  
        pn = p.size();
2042  
        pn = p.size();
2043  
        p_dest = s_ + impl_.offset(id_path);
2043  
        p_dest = s_ + impl_.offset(id_path);
2044  
        p_end = s_ + impl_.offset(id_path + 1);
2044  
        p_end = s_ + impl_.offset(id_path + 1);
2045  
    }
2045  
    }
2046  

2046  

2047  
//------------------------------------------------
2047  
//------------------------------------------------
2048  
//
2048  
//
2049  
//  Remove "." and ".." segments
2049  
//  Remove "." and ".." segments
2050  
//
2050  
//
2051  
    p.remove_prefix(skip_dot);
2051  
    p.remove_prefix(skip_dot);
2052  
    p_dest += skip_dot;
2052  
    p_dest += skip_dot;
2053  
    auto n = detail::remove_dot_segments(
2053  
    auto n = detail::remove_dot_segments(
2054  
        p_dest, p_end, p);
2054  
        p_dest, p_end, p);
2055  

2055  

2056  
//------------------------------------------------
2056  
//------------------------------------------------
2057  
//
2057  
//
2058  
//  Update path parameters
2058  
//  Update path parameters
2059  
//
2059  
//
2060  
    if (n != pn)
2060  
    if (n != pn)
2061  
    {
2061  
    {
2062  
        BOOST_ASSERT(n < pn);
2062  
        BOOST_ASSERT(n < pn);
2063  
        shrink_impl(id_path, n + skip_dot, op);
2063  
        shrink_impl(id_path, n + skip_dot, op);
2064  
        p = encoded_path();
2064  
        p = encoded_path();
2065  
        if (p == "/")
2065  
        if (p == "/")
2066  
            impl_.nseg_ = 0;
2066  
            impl_.nseg_ = 0;
2067  
        else if (!p.empty())
2067  
        else if (!p.empty())
2068  
            impl_.nseg_ = std::count(
2068  
            impl_.nseg_ = std::count(
2069  
                p.begin() + 1, p.end(), '/') + 1;
2069  
                p.begin() + 1, p.end(), '/') + 1;
2070  
        else
2070  
        else
2071  
            impl_.nseg_ = 0;
2071  
            impl_.nseg_ = 0;
2072  
        impl_.decoded_[id_path] =
2072  
        impl_.decoded_[id_path] =
2073  
            detail::decode_bytes_unsafe(impl_.get(id_path));
2073  
            detail::decode_bytes_unsafe(impl_.get(id_path));
2074  
    }
2074  
    }
2075  
    return *this;
2075  
    return *this;
2076  
}
2076  
}
2077  

2077  

2078  
url_base&
2078  
url_base&
2079  
url_base::
2079  
url_base::
2080  
normalize_query()
2080  
normalize_query()
2081  
{
2081  
{
2082  
    op_t op(*this);
2082  
    op_t op(*this);
2083  
    normalize_octets_impl(
2083  
    normalize_octets_impl(
2084  
        id_query,
2084  
        id_query,
2085  
        detail::query_chars,
2085  
        detail::query_chars,
2086  
        detail::query_ignore_chars,
2086  
        detail::query_ignore_chars,
2087  
        op);
2087  
        op);
2088  
    return *this;
2088  
    return *this;
2089  
}
2089  
}
2090  

2090  

2091  
url_base&
2091  
url_base&
2092  
url_base::
2092  
url_base::
2093  
normalize_fragment()
2093  
normalize_fragment()
2094  
{
2094  
{
2095  
    op_t op(*this);
2095  
    op_t op(*this);
2096  
    normalize_octets_impl(
2096  
    normalize_octets_impl(
2097  
        id_frag, detail::fragment_chars, op);
2097  
        id_frag, detail::fragment_chars, op);
2098  
    return *this;
2098  
    return *this;
2099  
}
2099  
}
2100  

2100  

2101  
url_base&
2101  
url_base&
2102  
url_base::
2102  
url_base::
2103  
normalize()
2103  
normalize()
2104  
{
2104  
{
2105  
    normalize_fragment();
2105  
    normalize_fragment();
2106  
    normalize_query();
2106  
    normalize_query();
2107  
    normalize_path();
2107  
    normalize_path();
2108  
    normalize_authority();
2108  
    normalize_authority();
2109  
    normalize_scheme();
2109  
    normalize_scheme();
2110  
    return *this;
2110  
    return *this;
2111  
}
2111  
}
2112  

2112  

2113  
//------------------------------------------------
2113  
//------------------------------------------------
2114  
//
2114  
//
2115  
// Implementation
2115  
// Implementation
2116  
//
2116  
//
2117  
//------------------------------------------------
2117  
//------------------------------------------------
2118  

2118  

2119  
void
2119  
void
2120  
url_base::
2120  
url_base::
2121  
check_invariants() const noexcept
2121  
check_invariants() const noexcept
2122  
{
2122  
{
2123  
    BOOST_ASSERT(pi_);
2123  
    BOOST_ASSERT(pi_);
2124  
    BOOST_ASSERT(
2124  
    BOOST_ASSERT(
2125  
        impl_.len(id_scheme) == 0 ||
2125  
        impl_.len(id_scheme) == 0 ||
2126  
        impl_.get(id_scheme).ends_with(':'));
2126  
        impl_.get(id_scheme).ends_with(':'));
2127  
    BOOST_ASSERT(
2127  
    BOOST_ASSERT(
2128  
        impl_.len(id_user) == 0 ||
2128  
        impl_.len(id_user) == 0 ||
2129  
        impl_.get(id_user).starts_with("//"));
2129  
        impl_.get(id_user).starts_with("//"));
2130  
    BOOST_ASSERT(
2130  
    BOOST_ASSERT(
2131  
        impl_.len(id_pass) == 0 ||
2131  
        impl_.len(id_pass) == 0 ||
2132  
        impl_.get(id_user).starts_with("//"));
2132  
        impl_.get(id_user).starts_with("//"));
2133  
    BOOST_ASSERT(
2133  
    BOOST_ASSERT(
2134  
        impl_.len(id_pass) == 0 ||
2134  
        impl_.len(id_pass) == 0 ||
2135  
        (impl_.len(id_pass) == 1 &&
2135  
        (impl_.len(id_pass) == 1 &&
2136  
            impl_.get(id_pass) == "@") ||
2136  
            impl_.get(id_pass) == "@") ||
2137  
        (impl_.len(id_pass) > 1 &&
2137  
        (impl_.len(id_pass) > 1 &&
2138  
            impl_.get(id_pass).starts_with(':') &&
2138  
            impl_.get(id_pass).starts_with(':') &&
2139  
            impl_.get(id_pass).ends_with('@')));
2139  
            impl_.get(id_pass).ends_with('@')));
2140  
    BOOST_ASSERT(
2140  
    BOOST_ASSERT(
2141  
        impl_.len(id_user, id_path) == 0 ||
2141  
        impl_.len(id_user, id_path) == 0 ||
2142  
        impl_.get(id_user).starts_with("//"));
2142  
        impl_.get(id_user).starts_with("//"));
2143  
    BOOST_ASSERT(impl_.decoded_[id_path] >=
2143  
    BOOST_ASSERT(impl_.decoded_[id_path] >=
2144  
        ((impl_.len(id_path) + 2) / 3));
2144  
        ((impl_.len(id_path) + 2) / 3));
2145  
    BOOST_ASSERT(
2145  
    BOOST_ASSERT(
2146  
        impl_.len(id_port) == 0 ||
2146  
        impl_.len(id_port) == 0 ||
2147  
        impl_.get(id_port).starts_with(':'));
2147  
        impl_.get(id_port).starts_with(':'));
2148  
    BOOST_ASSERT(
2148  
    BOOST_ASSERT(
2149  
        impl_.len(id_query) == 0 ||
2149  
        impl_.len(id_query) == 0 ||
2150  
        impl_.get(id_query).starts_with('?'));
2150  
        impl_.get(id_query).starts_with('?'));
2151  
    BOOST_ASSERT(
2151  
    BOOST_ASSERT(
2152  
        (impl_.len(id_query) == 0 && impl_.nparam_ == 0) ||
2152  
        (impl_.len(id_query) == 0 && impl_.nparam_ == 0) ||
2153  
        (impl_.len(id_query) > 0 && impl_.nparam_ > 0));
2153  
        (impl_.len(id_query) > 0 && impl_.nparam_ > 0));
2154  
    BOOST_ASSERT(
2154  
    BOOST_ASSERT(
2155  
        impl_.len(id_frag) == 0 ||
2155  
        impl_.len(id_frag) == 0 ||
2156  
        impl_.get(id_frag).starts_with('#'));
2156  
        impl_.get(id_frag).starts_with('#'));
2157  
    BOOST_ASSERT(c_str()[size()] == '\0');
2157  
    BOOST_ASSERT(c_str()[size()] == '\0');
2158  
}
2158  
}
2159  

2159  

2160  
char*
2160  
char*
2161  
url_base::
2161  
url_base::
2162  
resize_impl(
2162  
resize_impl(
2163  
    int id,
2163  
    int id,
2164  
    std::size_t new_size,
2164  
    std::size_t new_size,
2165  
    op_t& op)
2165  
    op_t& op)
2166  
{
2166  
{
2167  
    return resize_impl(
2167  
    return resize_impl(
2168  
        id, id + 1, new_size, op);
2168  
        id, id + 1, new_size, op);
2169  
}
2169  
}
2170  

2170  

2171  
char*
2171  
char*
2172  
url_base::
2172  
url_base::
2173  
resize_impl(
2173  
resize_impl(
2174  
    int first,
2174  
    int first,
2175  
    int last,
2175  
    int last,
2176  
    std::size_t new_len,
2176  
    std::size_t new_len,
2177  
    op_t& op)
2177  
    op_t& op)
2178  
{
2178  
{
2179  
    auto const n0 = impl_.len(first, last);
2179  
    auto const n0 = impl_.len(first, last);
2180  
    if(new_len == 0 && n0 == 0)
2180  
    if(new_len == 0 && n0 == 0)
2181  
        return s_ + impl_.offset(first);
2181  
        return s_ + impl_.offset(first);
2182  
    if(new_len <= n0)
2182  
    if(new_len <= n0)
2183  
        return shrink_impl(
2183  
        return shrink_impl(
2184  
            first, last, new_len, op);
2184  
            first, last, new_len, op);
2185  

2185  

2186  
    // growing
2186  
    // growing
2187  
    std::size_t n = new_len - n0;
2187  
    std::size_t n = new_len - n0;
2188  
    reserve_impl(size() + n, op);
2188  
    reserve_impl(size() + n, op);
2189  
    auto const pos =
2189  
    auto const pos =
2190  
        impl_.offset(last);
2190  
        impl_.offset(last);
2191  
    // adjust chars
2191  
    // adjust chars
2192  
    op.move(
2192  
    op.move(
2193  
        s_ + pos + n,
2193  
        s_ + pos + n,
2194  
        s_ + pos,
2194  
        s_ + pos,
2195  
        impl_.offset(id_end) -
2195  
        impl_.offset(id_end) -
2196  
            pos + 1);
2196  
            pos + 1);
2197  
    // collapse (first, last)
2197  
    // collapse (first, last)
2198  
    impl_.collapse(first, last,
2198  
    impl_.collapse(first, last,
2199  
        impl_.offset(last) + n);
2199  
        impl_.offset(last) + n);
2200  
    // shift (last, end) right
2200  
    // shift (last, end) right
2201  
    impl_.adjust_right(last, id_end, n);
2201  
    impl_.adjust_right(last, id_end, n);
2202  
    s_[size()] = '\0';
2202  
    s_[size()] = '\0';
2203  
    return s_ + impl_.offset(first);
2203  
    return s_ + impl_.offset(first);
2204  
}
2204  
}
2205  

2205  

2206  
char*
2206  
char*
2207  
url_base::
2207  
url_base::
2208  
shrink_impl(
2208  
shrink_impl(
2209  
    int id,
2209  
    int id,
2210  
    std::size_t new_size,
2210  
    std::size_t new_size,
2211  
    op_t& op)
2211  
    op_t& op)
2212  
{
2212  
{
2213  
    return shrink_impl(
2213  
    return shrink_impl(
2214  
        id, id + 1, new_size, op);
2214  
        id, id + 1, new_size, op);
2215  
}
2215  
}
2216  

2216  

2217  
char*
2217  
char*
2218  
url_base::
2218  
url_base::
2219  
shrink_impl(
2219  
shrink_impl(
2220  
    int first,
2220  
    int first,
2221  
    int last,
2221  
    int last,
2222  
    std::size_t new_len,
2222  
    std::size_t new_len,
2223  
    op_t& op)
2223  
    op_t& op)
2224  
{
2224  
{
2225  
    // shrinking
2225  
    // shrinking
2226  
    auto const n0 = impl_.len(first, last);
2226  
    auto const n0 = impl_.len(first, last);
2227  
    BOOST_ASSERT(new_len <= n0);
2227  
    BOOST_ASSERT(new_len <= n0);
2228  
    std::size_t n = n0 - new_len;
2228  
    std::size_t n = n0 - new_len;
2229  
    auto const pos =
2229  
    auto const pos =
2230  
        impl_.offset(last);
2230  
        impl_.offset(last);
2231  
    // adjust chars
2231  
    // adjust chars
2232  
    op.move(
2232  
    op.move(
2233  
        s_ + pos - n,
2233  
        s_ + pos - n,
2234  
        s_ + pos,
2234  
        s_ + pos,
2235  
        impl_.offset(
2235  
        impl_.offset(
2236  
            id_end) - pos + 1);
2236  
            id_end) - pos + 1);
2237  
    // collapse (first, last)
2237  
    // collapse (first, last)
2238  
    impl_.collapse(first,  last,
2238  
    impl_.collapse(first,  last,
2239  
        impl_.offset(last) - n);
2239  
        impl_.offset(last) - n);
2240  
    // shift (last, end) left
2240  
    // shift (last, end) left
2241  
    impl_.adjust_left(last, id_end, n);
2241  
    impl_.adjust_left(last, id_end, n);
2242  
    s_[size()] = '\0';
2242  
    s_[size()] = '\0';
2243  
    return s_ + impl_.offset(first);
2243  
    return s_ + impl_.offset(first);
2244  
}
2244  
}
2245  

2245  

2246  
//------------------------------------------------
2246  
//------------------------------------------------
2247  

2247  

2248  
void
2248  
void
2249  
url_base::
2249  
url_base::
2250  
set_scheme_impl(
2250  
set_scheme_impl(
2251  
    core::string_view s,
2251  
    core::string_view s,
2252  
    urls::scheme id)
2252  
    urls::scheme id)
2253  
{
2253  
{
2254  
    op_t op(*this, &s);
2254  
    op_t op(*this, &s);
2255  
    check_invariants();
2255  
    check_invariants();
2256  
    grammar::parse(
2256  
    grammar::parse(
2257  
        s, detail::scheme_rule()
2257  
        s, detail::scheme_rule()
2258  
            ).value(BOOST_URL_POS);
2258  
            ).value(BOOST_URL_POS);
2259  
    auto const n = s.size();
2259  
    auto const n = s.size();
2260  
    auto const p = impl_.offset(id_path);
2260  
    auto const p = impl_.offset(id_path);
2261  

2261  

2262  
    // check for "./" prefix
2262  
    // check for "./" prefix
2263  
    bool const has_dot =
2263  
    bool const has_dot =
2264  
        [this, p]
2264  
        [this, p]
2265  
    {
2265  
    {
2266  
        if(impl_.nseg_ == 0)
2266  
        if(impl_.nseg_ == 0)
2267  
            return false;
2267  
            return false;
2268  
        if(first_segment().size() < 2)
2268  
        if(first_segment().size() < 2)
2269  
            return false;
2269  
            return false;
2270  
        auto const src = s_ + p;
2270  
        auto const src = s_ + p;
2271  
        if(src[0] != '.')
2271  
        if(src[0] != '.')
2272  
            return false;
2272  
            return false;
2273  
        if(src[1] != '/')
2273  
        if(src[1] != '/')
2274  
            return false;
2274  
            return false;
2275  
        return true;
2275  
        return true;
2276  
    }();
2276  
    }();
2277  

2277  

2278  
    // Remove "./"
2278  
    // Remove "./"
2279  
    if(has_dot)
2279  
    if(has_dot)
2280  
    {
2280  
    {
2281  
        // do this first, for
2281  
        // do this first, for
2282  
        // strong exception safety
2282  
        // strong exception safety
2283  
        reserve_impl(
2283  
        reserve_impl(
2284  
            size() + n + 1 - 2, op);
2284  
            size() + n + 1 - 2, op);
2285  
        op.move(
2285  
        op.move(
2286  
            s_ + p,
2286  
            s_ + p,
2287  
            s_ + p + 2,
2287  
            s_ + p + 2,
2288  
            size() + 1 -
2288  
            size() + 1 -
2289  
                (p + 2));
2289  
                (p + 2));
2290  
        impl_.set_size(
2290  
        impl_.set_size(
2291  
            id_path,
2291  
            id_path,
2292  
            impl_.len(id_path) - 2);
2292  
            impl_.len(id_path) - 2);
2293  
        s_[size()] = '\0';
2293  
        s_[size()] = '\0';
2294  
    }
2294  
    }
2295  

2295  

2296  
    auto dest = resize_impl(
2296  
    auto dest = resize_impl(
2297  
        id_scheme, n + 1, op);
2297  
        id_scheme, n + 1, op);
2298  
    s.copy(dest, n);
2298  
    s.copy(dest, n);
2299  
    dest[n] = ':';
2299  
    dest[n] = ':';
2300  
    impl_.scheme_ = id;
2300  
    impl_.scheme_ = id;
2301  
    check_invariants();
2301  
    check_invariants();
2302  
}
2302  
}
2303  

2303  

2304  
char*
2304  
char*
2305  
url_base::
2305  
url_base::
2306  
set_user_impl(
2306  
set_user_impl(
2307  
    std::size_t n,
2307  
    std::size_t n,
2308  
    op_t& op)
2308  
    op_t& op)
2309  
{
2309  
{
2310  
    check_invariants();
2310  
    check_invariants();
2311  
    if(impl_.len(id_pass) != 0)
2311  
    if(impl_.len(id_pass) != 0)
2312  
    {
2312  
    {
2313  
        // keep "//"
2313  
        // keep "//"
2314  
        auto dest = resize_impl(
2314  
        auto dest = resize_impl(
2315  
            id_user, 2 + n, op);
2315  
            id_user, 2 + n, op);
2316  
        check_invariants();
2316  
        check_invariants();
2317  
        return dest + 2;
2317  
        return dest + 2;
2318  
    }
2318  
    }
2319  
    // add authority
2319  
    // add authority
2320  
    bool const make_absolute =
2320  
    bool const make_absolute =
2321  
        !is_path_absolute() &&
2321  
        !is_path_absolute() &&
2322  
        !impl_.get(id_path).empty();
2322  
        !impl_.get(id_path).empty();
2323  
    auto dest = resize_impl(
2323  
    auto dest = resize_impl(
2324  
        id_user, 2 + n + 1 + make_absolute, op);
2324  
        id_user, 2 + n + 1 + make_absolute, op);
2325  
    impl_.split(id_user, 2 + n);
2325  
    impl_.split(id_user, 2 + n);
2326  
    dest[0] = '/';
2326  
    dest[0] = '/';
2327  
    dest[1] = '/';
2327  
    dest[1] = '/';
2328  
    dest[2 + n] = '@';
2328  
    dest[2 + n] = '@';
2329  
    if (make_absolute)
2329  
    if (make_absolute)
2330  
    {
2330  
    {
2331  
        impl_.split(id_pass, 1);
2331  
        impl_.split(id_pass, 1);
2332  
        impl_.split(id_host, 0);
2332  
        impl_.split(id_host, 0);
2333  
        impl_.split(id_port, 0);
2333  
        impl_.split(id_port, 0);
2334  
        dest[3 + n] = '/';
2334  
        dest[3 + n] = '/';
2335  
    }
2335  
    }
2336  
    check_invariants();
2336  
    check_invariants();
2337  
    return dest + 2;
2337  
    return dest + 2;
2338  
}
2338  
}
2339  

2339  

2340  
char*
2340  
char*
2341  
url_base::
2341  
url_base::
2342  
set_password_impl(
2342  
set_password_impl(
2343  
    std::size_t n,
2343  
    std::size_t n,
2344  
    op_t& op)
2344  
    op_t& op)
2345  
{
2345  
{
2346  
    check_invariants();
2346  
    check_invariants();
2347  
    if(impl_.len(id_user) != 0)
2347  
    if(impl_.len(id_user) != 0)
2348  
    {
2348  
    {
2349  
        // already have authority
2349  
        // already have authority
2350  
        auto const dest = resize_impl(
2350  
        auto const dest = resize_impl(
2351  
            id_pass, 1 + n + 1, op);
2351  
            id_pass, 1 + n + 1, op);
2352  
        dest[0] = ':';
2352  
        dest[0] = ':';
2353  
        dest[n + 1] = '@';
2353  
        dest[n + 1] = '@';
2354  
        check_invariants();
2354  
        check_invariants();
2355  
        return dest + 1;
2355  
        return dest + 1;
2356  
    }
2356  
    }
2357  
    // add authority
2357  
    // add authority
2358  
    bool const make_absolute =
2358  
    bool const make_absolute =
2359  
            !is_path_absolute() &&
2359  
            !is_path_absolute() &&
2360  
            !impl_.get(id_path).empty();
2360  
            !impl_.get(id_path).empty();
2361  
    auto const dest =
2361  
    auto const dest =
2362  
        resize_impl(
2362  
        resize_impl(
2363  
        id_user, id_host,
2363  
        id_user, id_host,
2364  
        2 + 1 + n + 1 + make_absolute, op);
2364  
        2 + 1 + n + 1 + make_absolute, op);
2365  
    impl_.split(id_user, 2);
2365  
    impl_.split(id_user, 2);
2366  
    dest[0] = '/';
2366  
    dest[0] = '/';
2367  
    dest[1] = '/';
2367  
    dest[1] = '/';
2368  
    dest[2] = ':';
2368  
    dest[2] = ':';
2369  
    dest[2 + n + 1] = '@';
2369  
    dest[2 + n + 1] = '@';
2370  
    if (make_absolute)
2370  
    if (make_absolute)
2371  
    {
2371  
    {
2372  
        impl_.split(id_pass, 2 + n);
2372  
        impl_.split(id_pass, 2 + n);
2373  
        impl_.split(id_host, 0);
2373  
        impl_.split(id_host, 0);
2374  
        impl_.split(id_port, 0);
2374  
        impl_.split(id_port, 0);
2375  
        dest[4 + n] = '/';
2375  
        dest[4 + n] = '/';
2376  
    }
2376  
    }
2377  
    check_invariants();
2377  
    check_invariants();
2378  
    return dest + 3;
2378  
    return dest + 3;
2379  
}
2379  
}
2380  

2380  

2381  
char*
2381  
char*
2382  
url_base::
2382  
url_base::
2383  
set_userinfo_impl(
2383  
set_userinfo_impl(
2384  
    std::size_t n,
2384  
    std::size_t n,
2385  
    op_t& op)
2385  
    op_t& op)
2386  
{
2386  
{
2387  
    // "//" {dest} "@"
2387  
    // "//" {dest} "@"
2388  
    check_invariants();
2388  
    check_invariants();
2389  
    bool const make_absolute =
2389  
    bool const make_absolute =
2390  
            !is_path_absolute() &&
2390  
            !is_path_absolute() &&
2391  
            !impl_.get(id_path).empty();
2391  
            !impl_.get(id_path).empty();
2392  
    auto dest = resize_impl(
2392  
    auto dest = resize_impl(
2393  
        id_user, id_host, n + 3 + make_absolute, op);
2393  
        id_user, id_host, n + 3 + make_absolute, op);
2394  
    impl_.split(id_user, n + 2);
2394  
    impl_.split(id_user, n + 2);
2395  
    dest[0] = '/';
2395  
    dest[0] = '/';
2396  
    dest[1] = '/';
2396  
    dest[1] = '/';
2397  
    dest[n + 2] = '@';
2397  
    dest[n + 2] = '@';
2398  
    if (make_absolute)
2398  
    if (make_absolute)
2399  
    {
2399  
    {
2400  
        impl_.split(id_pass, 1);
2400  
        impl_.split(id_pass, 1);
2401  
        impl_.split(id_host, 0);
2401  
        impl_.split(id_host, 0);
2402  
        impl_.split(id_port, 0);
2402  
        impl_.split(id_port, 0);
2403  
        dest[3 + n] = '/';
2403  
        dest[3 + n] = '/';
2404  
    }
2404  
    }
2405  
    check_invariants();
2405  
    check_invariants();
2406  
    return dest + 2;
2406  
    return dest + 2;
2407  
}
2407  
}
2408  

2408  

2409  
char*
2409  
char*
2410  
url_base::
2410  
url_base::
2411  
set_host_impl(
2411  
set_host_impl(
2412  
    std::size_t n,
2412  
    std::size_t n,
2413  
    op_t& op)
2413  
    op_t& op)
2414  
{
2414  
{
2415  
    check_invariants();
2415  
    check_invariants();
2416  
    if(impl_.len(id_user) == 0)
2416  
    if(impl_.len(id_user) == 0)
2417  
    {
2417  
    {
2418  
        // add authority
2418  
        // add authority
2419  
        bool make_absolute =
2419  
        bool make_absolute =
2420  
            !is_path_absolute() &&
2420  
            !is_path_absolute() &&
2421  
            impl_.len(id_path) != 0;
2421  
            impl_.len(id_path) != 0;
2422  
        auto pn = impl_.len(id_path);
2422  
        auto pn = impl_.len(id_path);
2423  
        auto dest = resize_impl(
2423  
        auto dest = resize_impl(
2424  
            id_user, n + 2 + make_absolute, op);
2424  
            id_user, n + 2 + make_absolute, op);
2425  
        impl_.split(id_user, 2);
2425  
        impl_.split(id_user, 2);
2426  
        impl_.split(id_pass, 0);
2426  
        impl_.split(id_pass, 0);
2427  
        impl_.split(id_host, n);
2427  
        impl_.split(id_host, n);
2428  
        impl_.split(id_port, 0);
2428  
        impl_.split(id_port, 0);
2429  
        impl_.split(id_path, pn + make_absolute);
2429  
        impl_.split(id_path, pn + make_absolute);
2430  
        if (make_absolute)
2430  
        if (make_absolute)
2431  
        {
2431  
        {
2432  
            dest[n + 2] = '/';
2432  
            dest[n + 2] = '/';
2433  
            ++impl_.decoded_[id_path];
2433  
            ++impl_.decoded_[id_path];
2434  
        }
2434  
        }
2435  
        dest[0] = '/';
2435  
        dest[0] = '/';
2436  
        dest[1] = '/';
2436  
        dest[1] = '/';
2437  
        check_invariants();
2437  
        check_invariants();
2438  
        return dest + 2;
2438  
        return dest + 2;
2439  
    }
2439  
    }
2440  
    // already have authority
2440  
    // already have authority
2441  
    auto const dest = resize_impl(
2441  
    auto const dest = resize_impl(
2442  
        id_host, n, op);
2442  
        id_host, n, op);
2443  
    check_invariants();
2443  
    check_invariants();
2444  
    return dest;
2444  
    return dest;
2445  
}
2445  
}
2446  

2446  

2447  
char*
2447  
char*
2448  
url_base::
2448  
url_base::
2449  
set_port_impl(
2449  
set_port_impl(
2450  
    std::size_t n,
2450  
    std::size_t n,
2451  
    op_t& op)
2451  
    op_t& op)
2452  
{
2452  
{
2453  
    check_invariants();
2453  
    check_invariants();
2454  
    if(impl_.len(id_user) != 0)
2454  
    if(impl_.len(id_user) != 0)
2455  
    {
2455  
    {
2456  
        // authority exists
2456  
        // authority exists
2457  
        auto dest = resize_impl(
2457  
        auto dest = resize_impl(
2458  
            id_port, n + 1, op);
2458  
            id_port, n + 1, op);
2459  
        dest[0] = ':';
2459  
        dest[0] = ':';
2460  
        check_invariants();
2460  
        check_invariants();
2461  
        return dest + 1;
2461  
        return dest + 1;
2462  
    }
2462  
    }
2463  
    bool make_absolute =
2463  
    bool make_absolute =
2464  
        !is_path_absolute() &&
2464  
        !is_path_absolute() &&
2465  
        impl_.len(id_path) != 0;
2465  
        impl_.len(id_path) != 0;
2466  
    auto dest = resize_impl(
2466  
    auto dest = resize_impl(
2467  
        id_user, 3 + n + make_absolute, op);
2467  
        id_user, 3 + n + make_absolute, op);
2468  
    impl_.split(id_user, 2);
2468  
    impl_.split(id_user, 2);
2469  
    impl_.split(id_pass, 0);
2469  
    impl_.split(id_pass, 0);
2470  
    impl_.split(id_host, 0);
2470  
    impl_.split(id_host, 0);
2471  
    dest[0] = '/';
2471  
    dest[0] = '/';
2472  
    dest[1] = '/';
2472  
    dest[1] = '/';
2473  
    dest[2] = ':';
2473  
    dest[2] = ':';
2474  
    if (make_absolute)
2474  
    if (make_absolute)
2475  
    {
2475  
    {
2476  
        impl_.split(id_port, n + 1);
2476  
        impl_.split(id_port, n + 1);
2477  
        dest[n + 3] = '/';
2477  
        dest[n + 3] = '/';
2478  
        ++impl_.decoded_[id_path];
2478  
        ++impl_.decoded_[id_path];
2479  
    }
2479  
    }
2480  
    check_invariants();
2480  
    check_invariants();
2481  
    return dest + 3;
2481  
    return dest + 3;
2482  
}
2482  
}
2483  

2483  

2484  
char*
2484  
char*
2485  
url_base::
2485  
url_base::
2486  
set_path_impl(
2486  
set_path_impl(
2487  
    std::size_t n,
2487  
    std::size_t n,
2488  
    op_t& op)
2488  
    op_t& op)
2489  
{
2489  
{
2490  
    check_invariants();
2490  
    check_invariants();
2491  
    auto const dest = resize_impl(
2491  
    auto const dest = resize_impl(
2492  
        id_path, n, op);
2492  
        id_path, n, op);
2493  
    return dest;
2493  
    return dest;
2494  
}
2494  
}
2495  

2495  

2496  

2496  

2497  
//------------------------------------------------
2497  
//------------------------------------------------
2498  

2498  

2499  
// return the first segment of the path.
2499  
// return the first segment of the path.
2500  
// this is needed for some algorithms.
2500  
// this is needed for some algorithms.
2501  
core::string_view
2501  
core::string_view
2502  
url_base::
2502  
url_base::
2503  
first_segment() const noexcept
2503  
first_segment() const noexcept
2504  
{
2504  
{
2505  
    if(impl_.nseg_ == 0)
2505  
    if(impl_.nseg_ == 0)
2506  
        return {};
2506  
        return {};
2507  
    auto const p0 = impl_.cs_ +
2507  
    auto const p0 = impl_.cs_ +
2508  
        impl_.offset(id_path) +
2508  
        impl_.offset(id_path) +
2509  
            detail::path_prefix(
2509  
            detail::path_prefix(
2510  
                impl_.get(id_path));
2510  
                impl_.get(id_path));
2511  
    auto const end = impl_.cs_ +
2511  
    auto const end = impl_.cs_ +
2512  
        impl_.offset(id_query);
2512  
        impl_.offset(id_query);
2513  
    if(impl_.nseg_ == 1)
2513  
    if(impl_.nseg_ == 1)
2514  
        return core::string_view(
2514  
        return core::string_view(
2515  
            p0, end - p0);
2515  
            p0, end - p0);
2516  
    auto p = p0;
2516  
    auto p = p0;
2517  
    while(*p != '/')
2517  
    while(*p != '/')
2518  
        ++p;
2518  
        ++p;
2519  
    BOOST_ASSERT(p < end);
2519  
    BOOST_ASSERT(p < end);
2520  
    return core::string_view(p0, p - p0);
2520  
    return core::string_view(p0, p - p0);
2521  
}
2521  
}
2522  

2522  

2523  
detail::segments_iter_impl
2523  
detail::segments_iter_impl
2524  
url_base::
2524  
url_base::
2525  
edit_segments(
2525  
edit_segments(
2526  
    detail::segments_iter_impl const& it0,
2526  
    detail::segments_iter_impl const& it0,
2527  
    detail::segments_iter_impl const& it1,
2527  
    detail::segments_iter_impl const& it1,
2528  
    detail::any_segments_iter&& src,
2528  
    detail::any_segments_iter&& src,
2529  
    // -1 = preserve
2529  
    // -1 = preserve
2530  
    //  0 = make relative (can fail)
2530  
    //  0 = make relative (can fail)
2531  
    //  1 = make absolute
2531  
    //  1 = make absolute
2532  
    int absolute)
2532  
    int absolute)
2533  
{
2533  
{
2534  
    // Iterator doesn't belong to this url
2534  
    // Iterator doesn't belong to this url
2535  
    BOOST_ASSERT(it0.ref.alias_of(impl_));
2535  
    BOOST_ASSERT(it0.ref.alias_of(impl_));
2536  

2536  

2537  
    // Iterator doesn't belong to this url
2537  
    // Iterator doesn't belong to this url
2538  
    BOOST_ASSERT(it1.ref.alias_of(impl_));
2538  
    BOOST_ASSERT(it1.ref.alias_of(impl_));
2539  

2539  

2540  
    // Iterator is in the wrong order
2540  
    // Iterator is in the wrong order
2541  
    BOOST_ASSERT(it0.index <= it1.index);
2541  
    BOOST_ASSERT(it0.index <= it1.index);
2542  

2542  

2543  
    // Iterator is out of range
2543  
    // Iterator is out of range
2544  
    BOOST_ASSERT(it0.index <= impl_.nseg_);
2544  
    BOOST_ASSERT(it0.index <= impl_.nseg_);
2545  
    BOOST_ASSERT(it0.pos <= impl_.len(id_path));
2545  
    BOOST_ASSERT(it0.pos <= impl_.len(id_path));
2546  

2546  

2547  
    // Iterator is out of range
2547  
    // Iterator is out of range
2548  
    BOOST_ASSERT(it1.index <= impl_.nseg_);
2548  
    BOOST_ASSERT(it1.index <= impl_.nseg_);
2549  
    BOOST_ASSERT(it1.pos <= impl_.len(id_path));
2549  
    BOOST_ASSERT(it1.pos <= impl_.len(id_path));
2550  

2550  

2551  
//------------------------------------------------
2551  
//------------------------------------------------
2552  
//
2552  
//
2553  
//  Calculate output prefix
2553  
//  Calculate output prefix
2554  
//
2554  
//
2555  
//  0 = ""
2555  
//  0 = ""
2556  
//  1 = "/"
2556  
//  1 = "/"
2557  
//  2 = "./"
2557  
//  2 = "./"
2558  
//  3 = "/./"
2558  
//  3 = "/./"
2559  
//
2559  
//
2560  
    bool const is_abs = is_path_absolute();
2560  
    bool const is_abs = is_path_absolute();
2561  
    if(has_authority())
2561  
    if(has_authority())
2562  
    {
2562  
    {
2563  
        // Check if the new
2563  
        // Check if the new
2564  
        // path would be empty
2564  
        // path would be empty
2565  
        if( src.fast_nseg == 0 &&
2565  
        if( src.fast_nseg == 0 &&
2566  
            it0.index == 0 &&
2566  
            it0.index == 0 &&
2567  
            it1.index == impl_.nseg_)
2567  
            it1.index == impl_.nseg_)
2568  
        {
2568  
        {
2569  
            // VFALCO we don't have
2569  
            // VFALCO we don't have
2570  
            // access to nchar this early
2570  
            // access to nchar this early
2571  
            //
2571  
            //
2572  
            //BOOST_ASSERT(nchar == 0);
2572  
            //BOOST_ASSERT(nchar == 0);
2573  
            absolute = 0;
2573  
            absolute = 0;
2574  
        }
2574  
        }
2575  
        else
2575  
        else
2576  
        {
2576  
        {
2577  
            // prefix "/" required
2577  
            // prefix "/" required
2578  
            absolute = 1;
2578  
            absolute = 1;
2579  
        }
2579  
        }
2580  
    }
2580  
    }
2581  
    else if(absolute < 0)
2581  
    else if(absolute < 0)
2582  
    {
2582  
    {
2583  
        absolute = is_abs; // preserve
2583  
        absolute = is_abs; // preserve
2584  
    }
2584  
    }
2585  
    auto const path_pos = impl_.offset(id_path);
2585  
    auto const path_pos = impl_.offset(id_path);
2586  

2586  

2587  
    std::size_t nchar = 0;
2587  
    std::size_t nchar = 0;
2588  
    std::size_t prefix = 0;
2588  
    std::size_t prefix = 0;
2589  
    bool encode_colons = false;
2589  
    bool encode_colons = false;
2590  
    bool cp_src_prefix = false;
2590  
    bool cp_src_prefix = false;
2591  
    if(it0.index > 0)
2591  
    if(it0.index > 0)
2592  
    {
2592  
    {
2593  
        // first segment unchanged
2593  
        // first segment unchanged
2594  
        prefix = src.fast_nseg > 0;
2594  
        prefix = src.fast_nseg > 0;
2595  
    }
2595  
    }
2596  
    else if(src.fast_nseg > 0)
2596  
    else if(src.fast_nseg > 0)
2597  
    {
2597  
    {
2598  
        // first segment from src
2598  
        // first segment from src
2599  
        if(! src.front.empty())
2599  
        if(! src.front.empty())
2600  
        {
2600  
        {
2601  
            if( src.front == "." &&
2601  
            if( src.front == "." &&
2602  
                    src.fast_nseg > 1)
2602  
                    src.fast_nseg > 1)
2603  
                if (src.s.empty())
2603  
                if (src.s.empty())
2604  
                {
2604  
                {
2605  
                    // if front is ".", we need the extra "." in the prefix
2605  
                    // if front is ".", we need the extra "." in the prefix
2606  
                    // which will maintain the invariant that segments represent
2606  
                    // which will maintain the invariant that segments represent
2607  
                    // {"."}
2607  
                    // {"."}
2608  
                    prefix = 2 + absolute;
2608  
                    prefix = 2 + absolute;
2609  
                }
2609  
                }
2610  
                else
2610  
                else
2611  
                {
2611  
                {
2612  
                    // if the "." prefix is explicitly required from set_path
2612  
                    // if the "." prefix is explicitly required from set_path
2613  
                    // we do not include an extra "." segment
2613  
                    // we do not include an extra "." segment
2614  
                    prefix = absolute;
2614  
                    prefix = absolute;
2615  
                    cp_src_prefix = true;
2615  
                    cp_src_prefix = true;
2616  
                }
2616  
                }
2617  
            else if(absolute)
2617  
            else if(absolute)
2618  
                prefix = 1;
2618  
                prefix = 1;
2619  
            else if(has_scheme() ||
2619  
            else if(has_scheme() ||
2620  
                    ! src.front.contains(':'))
2620  
                    ! src.front.contains(':'))
2621  
                prefix = 0;
2621  
                prefix = 0;
2622  
            else
2622  
            else
2623  
            {
2623  
            {
2624  
                prefix = 0;
2624  
                prefix = 0;
2625  
                encode_colons = true;
2625  
                encode_colons = true;
2626  
            }
2626  
            }
2627  
        }
2627  
        }
2628  
        else
2628  
        else
2629  
        {
2629  
        {
2630  
            prefix = 2 + absolute;
2630  
            prefix = 2 + absolute;
2631  
        }
2631  
        }
2632  
    }
2632  
    }
2633  
    else
2633  
    else
2634  
    {
2634  
    {
2635  
        // first segment from it1
2635  
        // first segment from it1
2636  
        auto const p =
2636  
        auto const p =
2637  
            impl_.cs_ + path_pos + it1.pos;
2637  
            impl_.cs_ + path_pos + it1.pos;
2638  
        switch(impl_.cs_ +
2638  
        switch(impl_.cs_ +
2639  
            impl_.offset(id_query) - p)
2639  
            impl_.offset(id_query) - p)
2640  
        {
2640  
        {
2641  
        case 0:
2641  
        case 0:
2642  
            // points to end
2642  
            // points to end
2643  
            prefix = absolute;
2643  
            prefix = absolute;
2644  
            break;
2644  
            break;
2645  
        default:
2645  
        default:
2646  
            BOOST_ASSERT(*p == '/');
2646  
            BOOST_ASSERT(*p == '/');
2647  
            if(p[1] != '/')
2647  
            if(p[1] != '/')
2648  
            {
2648  
            {
2649  
                if(absolute)
2649  
                if(absolute)
2650  
                    prefix = 1;
2650  
                    prefix = 1;
2651  
                else if(has_scheme() ||
2651  
                else if(has_scheme() ||
2652  
                        ! it1.dereference().contains(':'))
2652  
                        ! it1.dereference().contains(':'))
2653  
                    prefix = 0;
2653  
                    prefix = 0;
2654  
                else
2654  
                else
2655  
                    prefix = 2;
2655  
                    prefix = 2;
2656  
                break;
2656  
                break;
2657  
            }
2657  
            }
2658  
            // empty
2658  
            // empty
2659  
            BOOST_FALLTHROUGH;
2659  
            BOOST_FALLTHROUGH;
2660  
        case 1:
2660  
        case 1:
2661  
            // empty
2661  
            // empty
2662  
            BOOST_ASSERT(*p == '/');
2662  
            BOOST_ASSERT(*p == '/');
2663  
            prefix = 2 + absolute;
2663  
            prefix = 2 + absolute;
2664  
            break;
2664  
            break;
2665  
        }
2665  
        }
2666  
    }
2666  
    }
2667  

2667  

2668  
//  append '/' to new segs
2668  
//  append '/' to new segs
2669  
//  if inserting at front.
2669  
//  if inserting at front.
2670  
    std::size_t const suffix =
2670  
    std::size_t const suffix =
2671  
        it1.index == 0 &&
2671  
        it1.index == 0 &&
2672  
        impl_.nseg_ > 0 &&
2672  
        impl_.nseg_ > 0 &&
2673  
        src.fast_nseg > 0;
2673  
        src.fast_nseg > 0;
2674  

2674  

2675  
//------------------------------------------------
2675  
//------------------------------------------------
2676  
//
2676  
//
2677  
//  Measure the number of encoded characters
2677  
//  Measure the number of encoded characters
2678  
//  of output, and the number of inserted
2678  
//  of output, and the number of inserted
2679  
//  segments including internal separators.
2679  
//  segments including internal separators.
2680  
//
2680  
//
2681  
    src.encode_colons = encode_colons;
2681  
    src.encode_colons = encode_colons;
2682  
    std::size_t nseg = 0;
2682  
    std::size_t nseg = 0;
2683  
    if(src.measure(nchar))
2683  
    if(src.measure(nchar))
2684  
    {
2684  
    {
2685  
        src.encode_colons = false;
2685  
        src.encode_colons = false;
2686  
        for(;;)
2686  
        for(;;)
2687  
        {
2687  
        {
2688  
            ++nseg;
2688  
            ++nseg;
2689  
            if(! src.measure(nchar))
2689  
            if(! src.measure(nchar))
2690  
                break;
2690  
                break;
2691  
            ++nchar;
2691  
            ++nchar;
2692  
        }
2692  
        }
2693  
    }
2693  
    }
2694  

2694  

2695  
    switch(src.fast_nseg)
2695  
    switch(src.fast_nseg)
2696  
    {
2696  
    {
2697  
    case 0:
2697  
    case 0:
2698  
        BOOST_ASSERT(nseg == 0);
2698  
        BOOST_ASSERT(nseg == 0);
2699  
        break;
2699  
        break;
2700  
    case 1:
2700  
    case 1:
2701  
        BOOST_ASSERT(nseg == 1);
2701  
        BOOST_ASSERT(nseg == 1);
2702  
        break;
2702  
        break;
2703  
    case 2:
2703  
    case 2:
2704  
        BOOST_ASSERT(nseg >= 2);
2704  
        BOOST_ASSERT(nseg >= 2);
2705  
        break;
2705  
        break;
2706  
    }
2706  
    }
2707  

2707  

2708  
//------------------------------------------------
2708  
//------------------------------------------------
2709  
//
2709  
//
2710  
//  Calculate [pos0, pos1) to remove
2710  
//  Calculate [pos0, pos1) to remove
2711  
//
2711  
//
2712  
    auto pos0 = it0.pos;
2712  
    auto pos0 = it0.pos;
2713  
    if(it0.index == 0)
2713  
    if(it0.index == 0)
2714  
    {
2714  
    {
2715  
        // patch pos for prefix
2715  
        // patch pos for prefix
2716  
        pos0 = 0;
2716  
        pos0 = 0;
2717  
    }
2717  
    }
2718  
    auto pos1 = it1.pos;
2718  
    auto pos1 = it1.pos;
2719  
    if(it1.index == 0)
2719  
    if(it1.index == 0)
2720  
    {
2720  
    {
2721  
        // patch pos for prefix
2721  
        // patch pos for prefix
2722  
        pos1 = detail::path_prefix(
2722  
        pos1 = detail::path_prefix(
2723  
            impl_.get(id_path));
2723  
            impl_.get(id_path));
2724  
    }
2724  
    }
2725  
    else if(
2725  
    else if(
2726  
        it0.index == 0 &&
2726  
        it0.index == 0 &&
2727  
        it1.index < impl_.nseg_ &&
2727  
        it1.index < impl_.nseg_ &&
2728  
        nseg == 0)
2728  
        nseg == 0)
2729  
    {
2729  
    {
2730  
        // Remove the slash from segment it1
2730  
        // Remove the slash from segment it1
2731  
        // if it is becoming the new first
2731  
        // if it is becoming the new first
2732  
        // segment.
2732  
        // segment.
2733  
        ++pos1;
2733  
        ++pos1;
2734  
    }
2734  
    }
2735  
    // calc decoded size of old range
2735  
    // calc decoded size of old range
2736  
    auto const dn0 =
2736  
    auto const dn0 =
2737  
        detail::decode_bytes_unsafe(
2737  
        detail::decode_bytes_unsafe(
2738  
            core::string_view(
2738  
            core::string_view(
2739  
                impl_.cs_ +
2739  
                impl_.cs_ +
2740  
                    impl_.offset(id_path) +
2740  
                    impl_.offset(id_path) +
2741  
                    pos0,
2741  
                    pos0,
2742  
                pos1 - pos0));
2742  
                pos1 - pos0));
2743  

2743  

2744  
//------------------------------------------------
2744  
//------------------------------------------------
2745  
//
2745  
//
2746  
//  Resize
2746  
//  Resize
2747  
//
2747  
//
2748  
    op_t op(*this, &src.s);
2748  
    op_t op(*this, &src.s);
2749  
    char* dest;
2749  
    char* dest;
2750  
    char const* end;
2750  
    char const* end;
2751  
    {
2751  
    {
2752  
        auto const nremove = pos1 - pos0;
2752  
        auto const nremove = pos1 - pos0;
2753  
        // check overflow
2753  
        // check overflow
2754  
        if( nchar <= max_size() && (
2754  
        if( nchar <= max_size() && (
2755  
            prefix + suffix <=
2755  
            prefix + suffix <=
2756  
                max_size() - nchar))
2756  
                max_size() - nchar))
2757  
        {
2757  
        {
2758  
            nchar = prefix + nchar + suffix;
2758  
            nchar = prefix + nchar + suffix;
2759  
            if( nchar <= nremove ||
2759  
            if( nchar <= nremove ||
2760  
                nchar - nremove <=
2760  
                nchar - nremove <=
2761  
                    max_size() - size())
2761  
                    max_size() - size())
2762  
                goto ok;
2762  
                goto ok;
2763  
        }
2763  
        }
2764  
        // too large
2764  
        // too large
2765  
        detail::throw_length_error();
2765  
        detail::throw_length_error();
2766  
    ok:
2766  
    ok:
2767  
        auto const new_size =
2767  
        auto const new_size =
2768  
            size() + nchar - nremove;
2768  
            size() + nchar - nremove;
2769  
        reserve_impl(new_size, op);
2769  
        reserve_impl(new_size, op);
2770  
        dest = s_ + path_pos + pos0;
2770  
        dest = s_ + path_pos + pos0;
2771  
        op.move(
2771  
        op.move(
2772  
            dest + nchar,
2772  
            dest + nchar,
2773  
            s_ + path_pos + pos1,
2773  
            s_ + path_pos + pos1,
2774  
            size() - path_pos - pos1);
2774  
            size() - path_pos - pos1);
2775  
        impl_.set_size(
2775  
        impl_.set_size(
2776  
            id_path,
2776  
            id_path,
2777  
            impl_.len(id_path) + nchar - nremove);
2777  
            impl_.len(id_path) + nchar - nremove);
2778  
        BOOST_ASSERT(size() == new_size);
2778  
        BOOST_ASSERT(size() == new_size);
2779  
        end = dest + nchar;
2779  
        end = dest + nchar;
2780  
        impl_.nseg_ = impl_.nseg_ + nseg - (
2780  
        impl_.nseg_ = impl_.nseg_ + nseg - (
2781  
            it1.index - it0.index) - cp_src_prefix;
2781  
            it1.index - it0.index) - cp_src_prefix;
2782  
        if(s_)
2782  
        if(s_)
2783  
            s_[size()] = '\0';
2783  
            s_[size()] = '\0';
2784  
    }
2784  
    }
2785  

2785  

2786  
//------------------------------------------------
2786  
//------------------------------------------------
2787  
//
2787  
//
2788  
//  Output segments and internal separators:
2788  
//  Output segments and internal separators:
2789  
//
2789  
//
2790  
//  prefix [ segment [ '/' segment ] ] suffix
2790  
//  prefix [ segment [ '/' segment ] ] suffix
2791  
//
2791  
//
2792  
    auto const dest0 = dest;
2792  
    auto const dest0 = dest;
2793  
    switch(prefix)
2793  
    switch(prefix)
2794  
    {
2794  
    {
2795  
    case 3:
2795  
    case 3:
2796  
        *dest++ = '/';
2796  
        *dest++ = '/';
2797  
        *dest++ = '.';
2797  
        *dest++ = '.';
2798  
        *dest++ = '/';
2798  
        *dest++ = '/';
2799  
        break;
2799  
        break;
2800  
    case 2:
2800  
    case 2:
2801  
        *dest++ = '.';
2801  
        *dest++ = '.';
2802  
        BOOST_FALLTHROUGH;
2802  
        BOOST_FALLTHROUGH;
2803  
    case 1:
2803  
    case 1:
2804  
        *dest++ = '/';
2804  
        *dest++ = '/';
2805  
        break;
2805  
        break;
2806  
    default:
2806  
    default:
2807  
        break;
2807  
        break;
2808  
    }
2808  
    }
2809  
    src.rewind();
2809  
    src.rewind();
2810  
    if(nseg > 0)
2810  
    if(nseg > 0)
2811  
    {
2811  
    {
2812  
        src.encode_colons = encode_colons;
2812  
        src.encode_colons = encode_colons;
2813  
        for(;;)
2813  
        for(;;)
2814  
        {
2814  
        {
2815  
            src.copy(dest, end);
2815  
            src.copy(dest, end);
2816  
            if(--nseg == 0)
2816  
            if(--nseg == 0)
2817  
                break;
2817  
                break;
2818  
            *dest++ = '/';
2818  
            *dest++ = '/';
2819  
            src.encode_colons = false;
2819  
            src.encode_colons = false;
2820  
        }
2820  
        }
2821  
        if(suffix)
2821  
        if(suffix)
2822  
            *dest++ = '/';
2822  
            *dest++ = '/';
2823  
    }
2823  
    }
2824  
    BOOST_ASSERT(dest == dest0 + nchar);
2824  
    BOOST_ASSERT(dest == dest0 + nchar);
2825  

2825  

2826  
    // calc decoded size of new range,
2826  
    // calc decoded size of new range,
2827  
    auto const dn =
2827  
    auto const dn =
2828  
        detail::decode_bytes_unsafe(
2828  
        detail::decode_bytes_unsafe(
2829  
            core::string_view(dest0, dest - dest0));
2829  
            core::string_view(dest0, dest - dest0));
2830  
    impl_.decoded_[id_path] += dn - dn0;
2830  
    impl_.decoded_[id_path] += dn - dn0;
2831  

2831  

2832  
    return detail::segments_iter_impl(
2832  
    return detail::segments_iter_impl(
2833  
        impl_, pos0, it0.index);
2833  
        impl_, pos0, it0.index);
2834  
}
2834  
}
2835  

2835  

2836  
//------------------------------------------------
2836  
//------------------------------------------------
2837  

2837  

2838  
auto
2838  
auto
2839  
url_base::
2839  
url_base::
2840  
edit_params(
2840  
edit_params(
2841  
    detail::params_iter_impl const& it0,
2841  
    detail::params_iter_impl const& it0,
2842  
    detail::params_iter_impl const& it1,
2842  
    detail::params_iter_impl const& it1,
2843  
    detail::any_params_iter&& src) ->
2843  
    detail::any_params_iter&& src) ->
2844  
        detail::params_iter_impl
2844  
        detail::params_iter_impl
2845  
{
2845  
{
2846  
    auto pos0 = impl_.offset(id_query);
2846  
    auto pos0 = impl_.offset(id_query);
2847  
    auto pos1 = pos0 + it1.pos;
2847  
    auto pos1 = pos0 + it1.pos;
2848  
    pos0 = pos0 + it0.pos;
2848  
    pos0 = pos0 + it0.pos;
2849  

2849  

2850  
    // Iterators belong to this url
2850  
    // Iterators belong to this url
2851  
    BOOST_ASSERT(it0.ref.alias_of(impl_));
2851  
    BOOST_ASSERT(it0.ref.alias_of(impl_));
2852  
    BOOST_ASSERT(it1.ref.alias_of(impl_));
2852  
    BOOST_ASSERT(it1.ref.alias_of(impl_));
2853  

2853  

2854  
    // Iterators is in the right order
2854  
    // Iterators is in the right order
2855  
    BOOST_ASSERT(it0.index <= it1.index);
2855  
    BOOST_ASSERT(it0.index <= it1.index);
2856  

2856  

2857  
    // Iterators are within range
2857  
    // Iterators are within range
2858  
    BOOST_ASSERT(it0.index <= impl_.nparam_);
2858  
    BOOST_ASSERT(it0.index <= impl_.nparam_);
2859  
    BOOST_ASSERT(pos0 <= impl_.offset(id_frag));
2859  
    BOOST_ASSERT(pos0 <= impl_.offset(id_frag));
2860  
    BOOST_ASSERT(it1.index <= impl_.nparam_);
2860  
    BOOST_ASSERT(it1.index <= impl_.nparam_);
2861  
    BOOST_ASSERT(pos1 <= impl_.offset(id_frag));
2861  
    BOOST_ASSERT(pos1 <= impl_.offset(id_frag));
2862  

2862  

2863  
    // calc decoded size of old range,
2863  
    // calc decoded size of old range,
2864  
    // minus one if '?' or '&' prefixed
2864  
    // minus one if '?' or '&' prefixed
2865  
    auto const dn0 =
2865  
    auto const dn0 =
2866  
        detail::decode_bytes_unsafe(
2866  
        detail::decode_bytes_unsafe(
2867  
            core::string_view(
2867  
            core::string_view(
2868  
                impl_.cs_ + pos0,
2868  
                impl_.cs_ + pos0,
2869  
                pos1 - pos0)) - (
2869  
                pos1 - pos0)) - (
2870  
                    impl_.len(id_query) > 0);
2870  
                    impl_.len(id_query) > 0);
2871  

2871  

2872  
//------------------------------------------------
2872  
//------------------------------------------------
2873  
//
2873  
//
2874  
//  Measure the number of encoded characters
2874  
//  Measure the number of encoded characters
2875  
//  of output, and the number of inserted
2875  
//  of output, and the number of inserted
2876  
//  segments including internal separators.
2876  
//  segments including internal separators.
2877  
//
2877  
//
2878  

2878  

2879  
    std::size_t nchar = 0;
2879  
    std::size_t nchar = 0;
2880  
    std::size_t nparam = 0;
2880  
    std::size_t nparam = 0;
2881  
    if(src.measure(nchar))
2881  
    if(src.measure(nchar))
2882  
    {
2882  
    {
2883  
        ++nchar; // for '?' or '&'
2883  
        ++nchar; // for '?' or '&'
2884  
        for(;;)
2884  
        for(;;)
2885  
        {
2885  
        {
2886  
            ++nparam;
2886  
            ++nparam;
2887  
            if(! src.measure(nchar))
2887  
            if(! src.measure(nchar))
2888  
                break;
2888  
                break;
2889  
            ++nchar; // for '&'
2889  
            ++nchar; // for '&'
2890  
        }
2890  
        }
2891  
    }
2891  
    }
2892  

2892  

2893  
//------------------------------------------------
2893  
//------------------------------------------------
2894  
//
2894  
//
2895  
//  Resize
2895  
//  Resize
2896  
//
2896  
//
2897  
    op_t op(*this, &src.s0, &src.s1);
2897  
    op_t op(*this, &src.s0, &src.s1);
2898  
    char* dest;
2898  
    char* dest;
2899  
    char const* end;
2899  
    char const* end;
2900  
    {
2900  
    {
2901  
        auto const nremove = pos1 - pos0;
2901  
        auto const nremove = pos1 - pos0;
2902  
        // check overflow
2902  
        // check overflow
2903  
        if( nchar > nremove &&
2903  
        if( nchar > nremove &&
2904  
            nchar - nremove >
2904  
            nchar - nremove >
2905  
                max_size() - size())
2905  
                max_size() - size())
2906  
        {
2906  
        {
2907  
            // too large
2907  
            // too large
2908  
            detail::throw_length_error();
2908  
            detail::throw_length_error();
2909  
        }
2909  
        }
2910  
        auto const nparam1 =
2910  
        auto const nparam1 =
2911  
            impl_.nparam_ + nparam - (
2911  
            impl_.nparam_ + nparam - (
2912  
                it1.index - it0.index);
2912  
                it1.index - it0.index);
2913  
        reserve_impl(size() + nchar - nremove, op);
2913  
        reserve_impl(size() + nchar - nremove, op);
2914  
        dest = s_ + pos0;
2914  
        dest = s_ + pos0;
2915  
        end = dest + nchar;
2915  
        end = dest + nchar;
2916  
        if(impl_.nparam_ > 0)
2916  
        if(impl_.nparam_ > 0)
2917  
        {
2917  
        {
2918  
            // needed when we move
2918  
            // needed when we move
2919  
            // the beginning of the query
2919  
            // the beginning of the query
2920  
            s_[impl_.offset(id_query)] = '&';
2920  
            s_[impl_.offset(id_query)] = '&';
2921  
        }
2921  
        }
2922  
        op.move(
2922  
        op.move(
2923  
            dest + nchar,
2923  
            dest + nchar,
2924  
            impl_.cs_ + pos1,
2924  
            impl_.cs_ + pos1,
2925  
            size() - pos1);
2925  
            size() - pos1);
2926  
        impl_.set_size(
2926  
        impl_.set_size(
2927  
            id_query,
2927  
            id_query,
2928  
            impl_.len(id_query) +
2928  
            impl_.len(id_query) +
2929  
                nchar - nremove);
2929  
                nchar - nremove);
2930  
        impl_.nparam_ = nparam1;
2930  
        impl_.nparam_ = nparam1;
2931  
        if(nparam1 > 0)
2931  
        if(nparam1 > 0)
2932  
        {
2932  
        {
2933  
            // needed when we erase
2933  
            // needed when we erase
2934  
            // the beginning of the query
2934  
            // the beginning of the query
2935  
            s_[impl_.offset(id_query)] = '?';
2935  
            s_[impl_.offset(id_query)] = '?';
2936  
        }
2936  
        }
2937  
        if(s_)
2937  
        if(s_)
2938  
            s_[size()] = '\0';
2938  
            s_[size()] = '\0';
2939  
    }
2939  
    }
2940  
    auto const dest0 = dest;
2940  
    auto const dest0 = dest;
2941  

2941  

2942  
//------------------------------------------------
2942  
//------------------------------------------------
2943  
//
2943  
//
2944  
//  Output params and internal separators:
2944  
//  Output params and internal separators:
2945  
//
2945  
//
2946  
//  [ '?' param ] [ '&' param ]
2946  
//  [ '?' param ] [ '&' param ]
2947  
//
2947  
//
2948  
    if(nparam > 0)
2948  
    if(nparam > 0)
2949  
    {
2949  
    {
2950  
        if(it0.index == 0)
2950  
        if(it0.index == 0)
2951  
            *dest++ = '?';
2951  
            *dest++ = '?';
2952  
        else
2952  
        else
2953  
            *dest++ = '&';
2953  
            *dest++ = '&';
2954  
        src.rewind();
2954  
        src.rewind();
2955  
        for(;;)
2955  
        for(;;)
2956  
        {
2956  
        {
2957  
            src.copy(dest, end);
2957  
            src.copy(dest, end);
2958  
            if(--nparam == 0)
2958  
            if(--nparam == 0)
2959  
                break;
2959  
                break;
2960  
            *dest++ = '&';
2960  
            *dest++ = '&';
2961  
        }
2961  
        }
2962  
    }
2962  
    }
2963  

2963  

2964  
    // calc decoded size of new range,
2964  
    // calc decoded size of new range,
2965  
    // minus one if '?' or '&' prefixed
2965  
    // minus one if '?' or '&' prefixed
2966  
    auto const dn =
2966  
    auto const dn =
2967  
        detail::decode_bytes_unsafe(
2967  
        detail::decode_bytes_unsafe(
2968  
            core::string_view(dest0, dest - dest0)) - (
2968  
            core::string_view(dest0, dest - dest0)) - (
2969  
                impl_.len(id_query) > 0);
2969  
                impl_.len(id_query) > 0);
2970  

2970  

2971  
    impl_.decoded_[id_query] += (dn - dn0);
2971  
    impl_.decoded_[id_query] += (dn - dn0);
2972  

2972  

2973  
    return detail::params_iter_impl(
2973  
    return detail::params_iter_impl(
2974  
        impl_,
2974  
        impl_,
2975  
        pos0 - impl_.offset_[id_query],
2975  
        pos0 - impl_.offset_[id_query],
2976  
        it0.index);
2976  
        it0.index);
2977  
}
2977  
}
2978  

2978  

2979  
//------------------------------------------------
2979  
//------------------------------------------------
2980  

2980  

2981  
void
2981  
void
2982  
url_base::
2982  
url_base::
2983  
decoded_to_lower_impl(int id) noexcept
2983  
decoded_to_lower_impl(int id) noexcept
2984  
{
2984  
{
2985  
    char* it = s_ + impl_.offset(id);
2985  
    char* it = s_ + impl_.offset(id);
2986  
    char const* const end = s_ + impl_.offset(id + 1);
2986  
    char const* const end = s_ + impl_.offset(id + 1);
2987  
    while(it < end)
2987  
    while(it < end)
2988  
    {
2988  
    {
2989  
        if (*it != '%')
2989  
        if (*it != '%')
2990  
        {
2990  
        {
2991  
            *it = grammar::to_lower(
2991  
            *it = grammar::to_lower(
2992  
                *it);
2992  
                *it);
2993  
            ++it;
2993  
            ++it;
2994  
            continue;
2994  
            continue;
2995  
        }
2995  
        }
2996  
        it += 3;
2996  
        it += 3;
2997  
    }
2997  
    }
2998  
}
2998  
}
2999  

2999  

3000  
void
3000  
void
3001  
url_base::
3001  
url_base::
3002  
to_lower_impl(int id) noexcept
3002  
to_lower_impl(int id) noexcept
3003  
{
3003  
{
3004  
    char* it = s_ + impl_.offset(id);
3004  
    char* it = s_ + impl_.offset(id);
3005  
    char const* const end = s_ + impl_.offset(id + 1);
3005  
    char const* const end = s_ + impl_.offset(id + 1);
3006  
    while(it < end)
3006  
    while(it < end)
3007  
    {
3007  
    {
3008  
        *it = grammar::to_lower(
3008  
        *it = grammar::to_lower(
3009  
            *it);
3009  
            *it);
3010  
        ++it;
3010  
        ++it;
3011  
    }
3011  
    }
3012  
}
3012  
}
3013  

3013  

3014  
} // urls
3014  
} // urls
3015  
} // boost
3015  
} // boost