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

10  

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

13  

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

44  

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

47  

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

49  

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

53  

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

63  

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

77  

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

100  

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

102  

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

111  

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

122  

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

146  

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

152  

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

162  

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

175  

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

230  

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

254  

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

283  

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

289  

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

320  

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

328  

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

353  

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

359  

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

410  

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

466  

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

474  

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

483  

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

485  

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

506  

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

529  

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

531  

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

552  

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

576  

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

585  

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

594  

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

602  

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

607  

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

613  

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

615  

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

620  

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

627  

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

685  

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

704  

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

767  

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

785  

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

822  

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

827  

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

836  

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

855  

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

896  

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

914  

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

932  

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

954  

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

964  

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

973  

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

982  

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

1021  

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

1059  

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

1082  

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

1099  

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

1117  

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

1134  

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

1152  

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

1154  

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

1172  

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

1193  

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

1204  

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

1210  

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

1221  

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

1227  

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

1235  

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

1244  

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

1252  

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

1261  

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

1269  

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

1285  

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

1293  

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

1299  

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

1309  

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

1318  

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

1358  

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

1400  

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

1425  

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

1429  

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

1437  

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

1475  

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

1515  

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

1540  

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

1544  

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

1552  

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

1560  

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

1566  

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

1579  

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

1591  

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

1616  

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

1621  

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

1636  

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

1647  

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

1655  

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

1663  

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

1674  

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

1683  

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

1695  

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

1701  

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

1712  

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

1737  

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

1763  

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

1769  

1770  
inline
1770  
inline
1771  
system::result<void>
1771  
system::result<void>
1772  
url_base::
1772  
url_base::
1773  
resolve(
1773  
resolve(
1774  
    url_view_base const& ref)
1774  
    url_view_base const& ref)
1775  
{
1775  
{
1776  
    if (this == &ref &&
1776  
    if (this == &ref &&
1777  
        has_scheme())
1777  
        has_scheme())
1778  
    {
1778  
    {
1779  
        normalize_path();
1779  
        normalize_path();
1780  
        return {};
1780  
        return {};
1781  
    }
1781  
    }
1782  

1782  

1783  
    if(! has_scheme())
1783  
    if(! has_scheme())
1784  
    {
1784  
    {
1785  
        BOOST_URL_RETURN_EC(error::not_a_base);
1785  
        BOOST_URL_RETURN_EC(error::not_a_base);
1786  
    }
1786  
    }
1787  

1787  

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

1789  

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

1794  

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

1891  

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

1897  

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

1923  

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

1934  

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

1949  

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

1961  

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

1970  

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

1977  

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

1986  

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

1989  

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

1994  

1995  
inline
1995  
inline
1996  
url_base&
1996  
url_base&
1997  
url_base::
1997  
url_base::
1998  
normalize_path()
1998  
normalize_path()
1999  
{
1999  
{
2000  
    op_t op(*this);
2000  
    op_t op(*this);
2001  
    normalize_octets_impl(id_path, detail::segment_chars, op);
2001  
    normalize_octets_impl(id_path, detail::segment_chars, op);
2002  
    core::string_view p = impl_.get(id_path);
2002  
    core::string_view p = impl_.get(id_path);
2003  
    char* p_dest = s_ + impl_.offset(id_path);
2003  
    char* p_dest = s_ + impl_.offset(id_path);
2004  
    char* p_end = s_ + impl_.offset(id_path + 1);
2004  
    char* p_end = s_ + impl_.offset(id_path + 1);
2005  
    auto pn = p.size();
2005  
    auto pn = p.size();
2006  
    auto skip_dot = 0;
2006  
    auto skip_dot = 0;
2007  
    bool encode_colons = false;
2007  
    bool encode_colons = false;
2008  
    core::string_view first_seg;
2008  
    core::string_view first_seg;
2009  

2009  

2010  
//------------------------------------------------
2010  
//------------------------------------------------
2011  
//
2011  
//
2012  
//  Determine unnecessary initial dot segments to skip and
2012  
//  Determine unnecessary initial dot segments to skip and
2013  
//  if we need to encode colons in the first segment
2013  
//  if we need to encode colons in the first segment
2014  
//
2014  
//
2015  
    if (
2015  
    if (
2016  
        !has_authority() &&
2016  
        !has_authority() &&
2017  
        p.starts_with("/./"))
2017  
        p.starts_with("/./"))
2018  
    {
2018  
    {
2019  
        // check if removing the "/./" would result in "//"
2019  
        // check if removing the "/./" would result in "//"
2020  
        // ex: "/.//", "/././/", "/././/", ...
2020  
        // ex: "/.//", "/././/", "/././/", ...
2021  
        skip_dot = 2;
2021  
        skip_dot = 2;
2022  
        while (p.substr(skip_dot, 3).starts_with("/./"))
2022  
        while (p.substr(skip_dot, 3).starts_with("/./"))
2023  
            skip_dot += 2;
2023  
            skip_dot += 2;
2024  
        if (p.substr(skip_dot).starts_with("//"))
2024  
        if (p.substr(skip_dot).starts_with("//"))
2025  
            skip_dot = 2;
2025  
            skip_dot = 2;
2026  
        else
2026  
        else
2027  
            skip_dot = 0;
2027  
            skip_dot = 0;
2028  
    }
2028  
    }
2029  
    else if (
2029  
    else if (
2030  
        !has_scheme() &&
2030  
        !has_scheme() &&
2031  
        !has_authority())
2031  
        !has_authority())
2032  
    {
2032  
    {
2033  
        if (p.starts_with("./"))
2033  
        if (p.starts_with("./"))
2034  
        {
2034  
        {
2035  
            // check if removing the "./" would result in "//"
2035  
            // check if removing the "./" would result in "//"
2036  
            // ex: ".//", "././/", "././/", ...
2036  
            // ex: ".//", "././/", "././/", ...
2037  
            skip_dot = 1;
2037  
            skip_dot = 1;
2038  
            while (p.substr(skip_dot, 3).starts_with("/./"))
2038  
            while (p.substr(skip_dot, 3).starts_with("/./"))
2039  
                skip_dot += 2;
2039  
                skip_dot += 2;
2040  
            if (p.substr(skip_dot).starts_with("//"))
2040  
            if (p.substr(skip_dot).starts_with("//"))
2041  
                skip_dot = 2;
2041  
                skip_dot = 2;
2042  
            else
2042  
            else
2043  
                skip_dot = 0;
2043  
                skip_dot = 0;
2044  

2044  

2045  
            if ( !skip_dot )
2045  
            if ( !skip_dot )
2046  
            {
2046  
            {
2047  
                // check if removing "./"s would leave us
2047  
                // check if removing "./"s would leave us
2048  
                // a first segment with an ambiguous ":"
2048  
                // a first segment with an ambiguous ":"
2049  
                first_seg = p.substr(2);
2049  
                first_seg = p.substr(2);
2050  
                while (first_seg.starts_with("./"))
2050  
                while (first_seg.starts_with("./"))
2051  
                    first_seg = first_seg.substr(2);
2051  
                    first_seg = first_seg.substr(2);
2052  
                auto i = first_seg.find('/');
2052  
                auto i = first_seg.find('/');
2053  
                if (i != core::string_view::npos)
2053  
                if (i != core::string_view::npos)
2054  
                    first_seg = first_seg.substr(0, i);
2054  
                    first_seg = first_seg.substr(0, i);
2055  
                encode_colons = first_seg.contains(':');
2055  
                encode_colons = first_seg.contains(':');
2056  
            }
2056  
            }
2057  
        }
2057  
        }
2058  
        else
2058  
        else
2059  
        {
2059  
        {
2060  
            // check if normalize_octets_impl
2060  
            // check if normalize_octets_impl
2061  
            // didn't already create a ":"
2061  
            // didn't already create a ":"
2062  
            // in the first segment
2062  
            // in the first segment
2063  
            first_seg = p;
2063  
            first_seg = p;
2064  
            auto i = first_seg.find('/');
2064  
            auto i = first_seg.find('/');
2065  
            if (i != core::string_view::npos)
2065  
            if (i != core::string_view::npos)
2066  
                first_seg = p.substr(0, i);
2066  
                first_seg = p.substr(0, i);
2067  
            encode_colons = first_seg.contains(':');
2067  
            encode_colons = first_seg.contains(':');
2068  
        }
2068  
        }
2069  
    }
2069  
    }
2070  

2070  

2071  
//------------------------------------------------
2071  
//------------------------------------------------
2072  
//
2072  
//
2073  
//  Encode colons in the first segment
2073  
//  Encode colons in the first segment
2074  
//
2074  
//
2075  
    if (encode_colons)
2075  
    if (encode_colons)
2076  
    {
2076  
    {
2077  
        // prepend with "./"
2077  
        // prepend with "./"
2078  
        // (resize_impl never throws)
2078  
        // (resize_impl never throws)
2079  
        auto cn =
2079  
        auto cn =
2080  
            std::count(
2080  
            std::count(
2081  
                first_seg.begin(),
2081  
                first_seg.begin(),
2082  
                first_seg.end(),
2082  
                first_seg.end(),
2083  
                ':');
2083  
                ':');
2084  
        resize_impl(
2084  
        resize_impl(
2085  
            id_path, pn + (2 * cn), op);
2085  
            id_path, pn + (2 * cn), op);
2086  
        // move the 2nd, 3rd, ... segments
2086  
        // move the 2nd, 3rd, ... segments
2087  
        auto begin = s_ + impl_.offset(id_path);
2087  
        auto begin = s_ + impl_.offset(id_path);
2088  
        auto it = begin;
2088  
        auto it = begin;
2089  
        auto end = begin + pn;
2089  
        auto end = begin + pn;
2090  
        while (core::string_view(it, 2) == "./")
2090  
        while (core::string_view(it, 2) == "./")
2091  
            it += 2;
2091  
            it += 2;
2092  
        while (*it != '/' &&
2092  
        while (*it != '/' &&
2093  
               it != end)
2093  
               it != end)
2094  
            ++it;
2094  
            ++it;
2095  
        // we don't need op here because this is
2095  
        // we don't need op here because this is
2096  
        // an internal operation
2096  
        // an internal operation
2097  
        std::memmove(it + (2 * cn), it, end - it);
2097  
        std::memmove(it + (2 * cn), it, end - it);
2098  

2098  

2099  
        // move 1st segment
2099  
        // move 1st segment
2100  
        auto src = s_ + impl_.offset(id_path) + pn;
2100  
        auto src = s_ + impl_.offset(id_path) + pn;
2101  
        auto dest = s_ + impl_.offset(id_query);
2101  
        auto dest = s_ + impl_.offset(id_query);
2102  
        src -= end - it;
2102  
        src -= end - it;
2103  
        dest -= end - it;
2103  
        dest -= end - it;
2104  
        pn -= end - it;
2104  
        pn -= end - it;
2105  
        do {
2105  
        do {
2106  
            --src;
2106  
            --src;
2107  
            --dest;
2107  
            --dest;
2108  
            if (*src != ':')
2108  
            if (*src != ':')
2109  
            {
2109  
            {
2110  
                *dest = *src;
2110  
                *dest = *src;
2111  
            }
2111  
            }
2112  
            else
2112  
            else
2113  
            {
2113  
            {
2114  
                // use uppercase as required by
2114  
                // use uppercase as required by
2115  
                // syntax-based normalization
2115  
                // syntax-based normalization
2116  
                *dest-- = 'A';
2116  
                *dest-- = 'A';
2117  
                *dest-- = '3';
2117  
                *dest-- = '3';
2118  
                *dest = '%';
2118  
                *dest = '%';
2119  
            }
2119  
            }
2120  
            --pn;
2120  
            --pn;
2121  
        } while (pn);
2121  
        } while (pn);
2122  
        skip_dot = 0;
2122  
        skip_dot = 0;
2123  
        p = impl_.get(id_path);
2123  
        p = impl_.get(id_path);
2124  
        pn = p.size();
2124  
        pn = p.size();
2125  
        p_dest = s_ + impl_.offset(id_path);
2125  
        p_dest = s_ + impl_.offset(id_path);
2126  
        p_end = s_ + impl_.offset(id_path + 1);
2126  
        p_end = s_ + impl_.offset(id_path + 1);
2127  
    }
2127  
    }
2128  

2128  

2129  
//------------------------------------------------
2129  
//------------------------------------------------
2130  
//
2130  
//
2131  
//  Remove "." and ".." segments
2131  
//  Remove "." and ".." segments
2132  
//
2132  
//
2133  
    p.remove_prefix(skip_dot);
2133  
    p.remove_prefix(skip_dot);
2134  
    p_dest += skip_dot;
2134  
    p_dest += skip_dot;
2135  
    auto n = detail::remove_dot_segments(
2135  
    auto n = detail::remove_dot_segments(
2136  
        p_dest, p_end, p);
2136  
        p_dest, p_end, p);
2137  

2137  

2138  
//------------------------------------------------
2138  
//------------------------------------------------
2139  
//
2139  
//
2140  
//  Update path parameters
2140  
//  Update path parameters
2141  
//
2141  
//
2142  
    if (n != pn)
2142  
    if (n != pn)
2143  
    {
2143  
    {
2144  
        BOOST_ASSERT(n < pn);
2144  
        BOOST_ASSERT(n < pn);
2145  
        shrink_impl(id_path, n + skip_dot, op);
2145  
        shrink_impl(id_path, n + skip_dot, op);
2146  
        p = encoded_path();
2146  
        p = encoded_path();
2147  
        if (p == "/")
2147  
        if (p == "/")
2148  
            impl_.nseg_ = 0;
2148  
            impl_.nseg_ = 0;
2149  
        else if (!p.empty())
2149  
        else if (!p.empty())
2150  
            impl_.nseg_ = detail::to_size_type(
2150  
            impl_.nseg_ = detail::to_size_type(
2151  
                std::count(
2151  
                std::count(
2152  
                    p.begin() + 1, p.end(), '/') + 1);
2152  
                    p.begin() + 1, p.end(), '/') + 1);
2153  
        else
2153  
        else
2154  
            impl_.nseg_ = 0;
2154  
            impl_.nseg_ = 0;
2155  
        impl_.decoded_[id_path] =
2155  
        impl_.decoded_[id_path] =
2156  
            detail::to_size_type(detail::decode_bytes_unsafe(
2156  
            detail::to_size_type(detail::decode_bytes_unsafe(
2157  
                impl_.get(id_path)));
2157  
                impl_.get(id_path)));
2158  
    }
2158  
    }
2159  
    return *this;
2159  
    return *this;
2160  
}
2160  
}
2161  

2161  

2162  
inline
2162  
inline
2163  
url_base&
2163  
url_base&
2164  
url_base::
2164  
url_base::
2165  
normalize_query()
2165  
normalize_query()
2166  
{
2166  
{
2167  
    op_t op(*this);
2167  
    op_t op(*this);
2168  
    normalize_octets_impl(
2168  
    normalize_octets_impl(
2169  
        id_query,
2169  
        id_query,
2170  
        detail::query_chars,
2170  
        detail::query_chars,
2171  
        detail::query_ignore_chars,
2171  
        detail::query_ignore_chars,
2172  
        op);
2172  
        op);
2173  
    return *this;
2173  
    return *this;
2174  
}
2174  
}
2175  

2175  

2176  
inline
2176  
inline
2177  
url_base&
2177  
url_base&
2178  
url_base::
2178  
url_base::
2179  
normalize_fragment()
2179  
normalize_fragment()
2180  
{
2180  
{
2181  
    op_t op(*this);
2181  
    op_t op(*this);
2182  
    normalize_octets_impl(
2182  
    normalize_octets_impl(
2183  
        id_frag, detail::fragment_chars, op);
2183  
        id_frag, detail::fragment_chars, op);
2184  
    return *this;
2184  
    return *this;
2185  
}
2185  
}
2186  

2186  

2187  
inline
2187  
inline
2188  
url_base&
2188  
url_base&
2189  
url_base::
2189  
url_base::
2190  
normalize()
2190  
normalize()
2191  
{
2191  
{
2192  
    normalize_fragment();
2192  
    normalize_fragment();
2193  
    normalize_query();
2193  
    normalize_query();
2194  
    normalize_path();
2194  
    normalize_path();
2195  
    normalize_authority();
2195  
    normalize_authority();
2196  
    normalize_scheme();
2196  
    normalize_scheme();
2197  
    return *this;
2197  
    return *this;
2198  
}
2198  
}
2199  

2199  

2200  
//------------------------------------------------
2200  
//------------------------------------------------
2201  
//
2201  
//
2202  
// Implementation
2202  
// Implementation
2203  
//
2203  
//
2204  
//------------------------------------------------
2204  
//------------------------------------------------
2205  

2205  

2206  
inline
2206  
inline
2207  
void
2207  
void
2208  
url_base::
2208  
url_base::
2209  
check_invariants() const noexcept
2209  
check_invariants() const noexcept
2210  
{
2210  
{
2211  
    BOOST_ASSERT(
2211  
    BOOST_ASSERT(
2212  
        impl_.len(id_scheme) == 0 ||
2212  
        impl_.len(id_scheme) == 0 ||
2213  
        impl_.get(id_scheme).ends_with(':'));
2213  
        impl_.get(id_scheme).ends_with(':'));
2214  
    BOOST_ASSERT(
2214  
    BOOST_ASSERT(
2215  
        impl_.len(id_user) == 0 ||
2215  
        impl_.len(id_user) == 0 ||
2216  
        impl_.get(id_user).starts_with("//"));
2216  
        impl_.get(id_user).starts_with("//"));
2217  
    BOOST_ASSERT(
2217  
    BOOST_ASSERT(
2218  
        impl_.len(id_pass) == 0 ||
2218  
        impl_.len(id_pass) == 0 ||
2219  
        impl_.get(id_user).starts_with("//"));
2219  
        impl_.get(id_user).starts_with("//"));
2220  
    BOOST_ASSERT(
2220  
    BOOST_ASSERT(
2221  
        impl_.len(id_pass) == 0 ||
2221  
        impl_.len(id_pass) == 0 ||
2222  
        (impl_.len(id_pass) == 1 &&
2222  
        (impl_.len(id_pass) == 1 &&
2223  
            impl_.get(id_pass) == "@") ||
2223  
            impl_.get(id_pass) == "@") ||
2224  
        (impl_.len(id_pass) > 1 &&
2224  
        (impl_.len(id_pass) > 1 &&
2225  
            impl_.get(id_pass).starts_with(':') &&
2225  
            impl_.get(id_pass).starts_with(':') &&
2226  
            impl_.get(id_pass).ends_with('@')));
2226  
            impl_.get(id_pass).ends_with('@')));
2227  
    BOOST_ASSERT(
2227  
    BOOST_ASSERT(
2228  
        impl_.len(id_user, id_path) == 0 ||
2228  
        impl_.len(id_user, id_path) == 0 ||
2229  
        impl_.get(id_user).starts_with("//"));
2229  
        impl_.get(id_user).starts_with("//"));
2230  
    BOOST_ASSERT(impl_.decoded_[id_path] >=
2230  
    BOOST_ASSERT(impl_.decoded_[id_path] >=
2231  
        ((impl_.len(id_path) + 2) / 3));
2231  
        ((impl_.len(id_path) + 2) / 3));
2232  
    BOOST_ASSERT(
2232  
    BOOST_ASSERT(
2233  
        impl_.len(id_port) == 0 ||
2233  
        impl_.len(id_port) == 0 ||
2234  
        impl_.get(id_port).starts_with(':'));
2234  
        impl_.get(id_port).starts_with(':'));
2235  
    BOOST_ASSERT(
2235  
    BOOST_ASSERT(
2236  
        impl_.len(id_query) == 0 ||
2236  
        impl_.len(id_query) == 0 ||
2237  
        impl_.get(id_query).starts_with('?'));
2237  
        impl_.get(id_query).starts_with('?'));
2238  
    BOOST_ASSERT(
2238  
    BOOST_ASSERT(
2239  
        (impl_.len(id_query) == 0 && impl_.nparam_ == 0) ||
2239  
        (impl_.len(id_query) == 0 && impl_.nparam_ == 0) ||
2240  
        (impl_.len(id_query) > 0 && impl_.nparam_ > 0));
2240  
        (impl_.len(id_query) > 0 && impl_.nparam_ > 0));
2241  
    BOOST_ASSERT(
2241  
    BOOST_ASSERT(
2242  
        impl_.len(id_frag) == 0 ||
2242  
        impl_.len(id_frag) == 0 ||
2243  
        impl_.get(id_frag).starts_with('#'));
2243  
        impl_.get(id_frag).starts_with('#'));
2244  
    BOOST_ASSERT(c_str()[size()] == '\0');
2244  
    BOOST_ASSERT(c_str()[size()] == '\0');
2245  
}
2245  
}
2246  

2246  

2247  
inline
2247  
inline
2248  
char*
2248  
char*
2249  
url_base::
2249  
url_base::
2250  
resize_impl(
2250  
resize_impl(
2251  
    int id,
2251  
    int id,
2252  
    std::size_t new_size,
2252  
    std::size_t new_size,
2253  
    op_t& op)
2253  
    op_t& op)
2254  
{
2254  
{
2255  
    return resize_impl(
2255  
    return resize_impl(
2256  
        id, id + 1, new_size, op);
2256  
        id, id + 1, new_size, op);
2257  
}
2257  
}
2258  

2258  

2259  
inline
2259  
inline
2260  
char*
2260  
char*
2261  
url_base::
2261  
url_base::
2262  
resize_impl(
2262  
resize_impl(
2263  
    int first,
2263  
    int first,
2264  
    int last,
2264  
    int last,
2265  
    std::size_t new_len,
2265  
    std::size_t new_len,
2266  
    op_t& op)
2266  
    op_t& op)
2267  
{
2267  
{
2268  
    auto const n0 = impl_.len(first, last);
2268  
    auto const n0 = impl_.len(first, last);
2269  
    if(new_len == 0 && n0 == 0)
2269  
    if(new_len == 0 && n0 == 0)
2270  
        return s_ + impl_.offset(first);
2270  
        return s_ + impl_.offset(first);
2271  
    if(new_len <= n0)
2271  
    if(new_len <= n0)
2272  
        return shrink_impl(
2272  
        return shrink_impl(
2273  
            first, last, new_len, op);
2273  
            first, last, new_len, op);
2274  

2274  

2275  
    // growing
2275  
    // growing
2276  
    std::size_t n = new_len - n0;
2276  
    std::size_t n = new_len - n0;
2277  
    reserve_impl(size() + n, op);
2277  
    reserve_impl(size() + n, op);
2278  
    auto const pos =
2278  
    auto const pos =
2279  
        impl_.offset(last);
2279  
        impl_.offset(last);
2280  
    // adjust chars
2280  
    // adjust chars
2281  
    op.move(
2281  
    op.move(
2282  
        s_ + pos + n,
2282  
        s_ + pos + n,
2283  
        s_ + pos,
2283  
        s_ + pos,
2284  
        impl_.offset(id_end) -
2284  
        impl_.offset(id_end) -
2285  
            pos + 1);
2285  
            pos + 1);
2286  
    // collapse (first, last)
2286  
    // collapse (first, last)
2287  
    impl_.collapse(first, last,
2287  
    impl_.collapse(first, last,
2288  
        impl_.offset(last) + n);
2288  
        impl_.offset(last) + n);
2289  
    // shift (last, end) right
2289  
    // shift (last, end) right
2290  
    impl_.adjust_right(last, id_end, n);
2290  
    impl_.adjust_right(last, id_end, n);
2291  
    s_[size()] = '\0';
2291  
    s_[size()] = '\0';
2292  
    return s_ + impl_.offset(first);
2292  
    return s_ + impl_.offset(first);
2293  
}
2293  
}
2294  

2294  

2295  
inline
2295  
inline
2296  
char*
2296  
char*
2297  
url_base::
2297  
url_base::
2298  
shrink_impl(
2298  
shrink_impl(
2299  
    int id,
2299  
    int id,
2300  
    std::size_t new_size,
2300  
    std::size_t new_size,
2301  
    op_t& op)
2301  
    op_t& op)
2302  
{
2302  
{
2303  
    return shrink_impl(
2303  
    return shrink_impl(
2304  
        id, id + 1, new_size, op);
2304  
        id, id + 1, new_size, op);
2305  
}
2305  
}
2306  

2306  

2307  
inline
2307  
inline
2308  
char*
2308  
char*
2309  
url_base::
2309  
url_base::
2310  
shrink_impl(
2310  
shrink_impl(
2311  
    int first,
2311  
    int first,
2312  
    int last,
2312  
    int last,
2313  
    std::size_t new_len,
2313  
    std::size_t new_len,
2314  
    op_t& op)
2314  
    op_t& op)
2315  
{
2315  
{
2316  
    // shrinking
2316  
    // shrinking
2317  
    auto const n0 = impl_.len(first, last);
2317  
    auto const n0 = impl_.len(first, last);
2318  
    BOOST_ASSERT(new_len <= n0);
2318  
    BOOST_ASSERT(new_len <= n0);
2319  
    std::size_t n = n0 - new_len;
2319  
    std::size_t n = n0 - new_len;
2320  
    auto const pos =
2320  
    auto const pos =
2321  
        impl_.offset(last);
2321  
        impl_.offset(last);
2322  
    // adjust chars
2322  
    // adjust chars
2323  
    op.move(
2323  
    op.move(
2324  
        s_ + pos - n,
2324  
        s_ + pos - n,
2325  
        s_ + pos,
2325  
        s_ + pos,
2326  
        impl_.offset(
2326  
        impl_.offset(
2327  
            id_end) - pos + 1);
2327  
            id_end) - pos + 1);
2328  
    // collapse (first, last)
2328  
    // collapse (first, last)
2329  
    impl_.collapse(first,  last,
2329  
    impl_.collapse(first,  last,
2330  
        impl_.offset(last) - n);
2330  
        impl_.offset(last) - n);
2331  
    // shift (last, end) left
2331  
    // shift (last, end) left
2332  
    impl_.adjust_left(last, id_end, n);
2332  
    impl_.adjust_left(last, id_end, n);
2333  
    s_[size()] = '\0';
2333  
    s_[size()] = '\0';
2334  
    return s_ + impl_.offset(first);
2334  
    return s_ + impl_.offset(first);
2335  
}
2335  
}
2336  

2336  

2337  
//------------------------------------------------
2337  
//------------------------------------------------
2338  

2338  

2339  
inline
2339  
inline
2340  
void
2340  
void
2341  
url_base::
2341  
url_base::
2342  
set_scheme_impl(
2342  
set_scheme_impl(
2343  
    core::string_view s,
2343  
    core::string_view s,
2344  
    urls::scheme id)
2344  
    urls::scheme id)
2345  
{
2345  
{
2346  
    op_t op(*this, &s);
2346  
    op_t op(*this, &s);
2347  
    check_invariants();
2347  
    check_invariants();
2348  
    grammar::parse(
2348  
    grammar::parse(
2349  
        s, detail::scheme_rule()
2349  
        s, detail::scheme_rule()
2350  
            ).value(BOOST_URL_POS);
2350  
            ).value(BOOST_URL_POS);
2351  
    auto const n = s.size();
2351  
    auto const n = s.size();
2352  
    auto const p = impl_.offset(id_path);
2352  
    auto const p = impl_.offset(id_path);
2353  

2353  

2354  
    // check for "./" prefix
2354  
    // check for "./" prefix
2355  
    bool const has_dot =
2355  
    bool const has_dot =
2356  
        [this, p]
2356  
        [this, p]
2357  
    {
2357  
    {
2358  
        if(impl_.nseg_ == 0)
2358  
        if(impl_.nseg_ == 0)
2359  
            return false;
2359  
            return false;
2360  
        if(first_segment().size() < 2)
2360  
        if(first_segment().size() < 2)
2361  
            return false;
2361  
            return false;
2362  
        auto const src = s_ + p;
2362  
        auto const src = s_ + p;
2363  
        if(src[0] != '.')
2363  
        if(src[0] != '.')
2364  
            return false;
2364  
            return false;
2365  
        if(src[1] != '/')
2365  
        if(src[1] != '/')
2366  
            return false;
2366  
            return false;
2367  
        return true;
2367  
        return true;
2368  
    }();
2368  
    }();
2369  

2369  

2370  
    // Remove "./"
2370  
    // Remove "./"
2371  
    if(has_dot)
2371  
    if(has_dot)
2372  
    {
2372  
    {
2373  
        // do this first, for
2373  
        // do this first, for
2374  
        // strong exception safety
2374  
        // strong exception safety
2375  
        reserve_impl(
2375  
        reserve_impl(
2376  
            size() + n + 1 - 2, op);
2376  
            size() + n + 1 - 2, op);
2377  
        op.move(
2377  
        op.move(
2378  
            s_ + p,
2378  
            s_ + p,
2379  
            s_ + p + 2,
2379  
            s_ + p + 2,
2380  
            size() + 1 -
2380  
            size() + 1 -
2381  
                (p + 2));
2381  
                (p + 2));
2382  
        impl_.set_size(
2382  
        impl_.set_size(
2383  
            id_path,
2383  
            id_path,
2384  
            impl_.len(id_path) - 2);
2384  
            impl_.len(id_path) - 2);
2385  
        s_[size()] = '\0';
2385  
        s_[size()] = '\0';
2386  
    }
2386  
    }
2387  

2387  

2388  
    auto dest = resize_impl(
2388  
    auto dest = resize_impl(
2389  
        id_scheme, n + 1, op);
2389  
        id_scheme, n + 1, op);
2390  
    s.copy(dest, n);
2390  
    s.copy(dest, n);
2391  
    dest[n] = ':';
2391  
    dest[n] = ':';
2392  
    impl_.scheme_ = id;
2392  
    impl_.scheme_ = id;
2393  
    check_invariants();
2393  
    check_invariants();
2394  
}
2394  
}
2395  

2395  

2396  
inline
2396  
inline
2397  
char*
2397  
char*
2398  
url_base::
2398  
url_base::
2399  
set_user_impl(
2399  
set_user_impl(
2400  
    std::size_t n,
2400  
    std::size_t n,
2401  
    op_t& op)
2401  
    op_t& op)
2402  
{
2402  
{
2403  
    check_invariants();
2403  
    check_invariants();
2404  
    if(impl_.len(id_pass) != 0)
2404  
    if(impl_.len(id_pass) != 0)
2405  
    {
2405  
    {
2406  
        // keep "//"
2406  
        // keep "//"
2407  
        auto dest = resize_impl(
2407  
        auto dest = resize_impl(
2408  
            id_user, 2 + n, op);
2408  
            id_user, 2 + n, op);
2409  
        check_invariants();
2409  
        check_invariants();
2410  
        return dest + 2;
2410  
        return dest + 2;
2411  
    }
2411  
    }
2412  
    // add authority
2412  
    // add authority
2413  
    bool const make_absolute =
2413  
    bool const make_absolute =
2414  
        !is_path_absolute() &&
2414  
        !is_path_absolute() &&
2415  
        !impl_.get(id_path).empty();
2415  
        !impl_.get(id_path).empty();
2416  
    auto dest = resize_impl(
2416  
    auto dest = resize_impl(
2417  
        id_user, 2 + n + 1 + make_absolute, op);
2417  
        id_user, 2 + n + 1 + make_absolute, op);
2418  
    impl_.split(id_user, 2 + n);
2418  
    impl_.split(id_user, 2 + n);
2419  
    dest[0] = '/';
2419  
    dest[0] = '/';
2420  
    dest[1] = '/';
2420  
    dest[1] = '/';
2421  
    dest[2 + n] = '@';
2421  
    dest[2 + n] = '@';
2422  
    if (make_absolute)
2422  
    if (make_absolute)
2423  
    {
2423  
    {
2424  
        impl_.split(id_pass, 1);
2424  
        impl_.split(id_pass, 1);
2425  
        impl_.split(id_host, 0);
2425  
        impl_.split(id_host, 0);
2426  
        impl_.split(id_port, 0);
2426  
        impl_.split(id_port, 0);
2427  
        dest[3 + n] = '/';
2427  
        dest[3 + n] = '/';
2428  
    }
2428  
    }
2429  
    check_invariants();
2429  
    check_invariants();
2430  
    return dest + 2;
2430  
    return dest + 2;
2431  
}
2431  
}
2432  

2432  

2433  
inline
2433  
inline
2434  
char*
2434  
char*
2435  
url_base::
2435  
url_base::
2436  
set_password_impl(
2436  
set_password_impl(
2437  
    std::size_t n,
2437  
    std::size_t n,
2438  
    op_t& op)
2438  
    op_t& op)
2439  
{
2439  
{
2440  
    check_invariants();
2440  
    check_invariants();
2441  
    if(impl_.len(id_user) != 0)
2441  
    if(impl_.len(id_user) != 0)
2442  
    {
2442  
    {
2443  
        // already have authority
2443  
        // already have authority
2444  
        auto const dest = resize_impl(
2444  
        auto const dest = resize_impl(
2445  
            id_pass, 1 + n + 1, op);
2445  
            id_pass, 1 + n + 1, op);
2446  
        dest[0] = ':';
2446  
        dest[0] = ':';
2447  
        dest[n + 1] = '@';
2447  
        dest[n + 1] = '@';
2448  
        check_invariants();
2448  
        check_invariants();
2449  
        return dest + 1;
2449  
        return dest + 1;
2450  
    }
2450  
    }
2451  
    // add authority
2451  
    // add authority
2452  
    bool const make_absolute =
2452  
    bool const make_absolute =
2453  
            !is_path_absolute() &&
2453  
            !is_path_absolute() &&
2454  
            !impl_.get(id_path).empty();
2454  
            !impl_.get(id_path).empty();
2455  
    auto const dest =
2455  
    auto const dest =
2456  
        resize_impl(
2456  
        resize_impl(
2457  
        id_user, id_host,
2457  
        id_user, id_host,
2458  
        2 + 1 + n + 1 + make_absolute, op);
2458  
        2 + 1 + n + 1 + make_absolute, op);
2459  
    impl_.split(id_user, 2);
2459  
    impl_.split(id_user, 2);
2460  
    dest[0] = '/';
2460  
    dest[0] = '/';
2461  
    dest[1] = '/';
2461  
    dest[1] = '/';
2462  
    dest[2] = ':';
2462  
    dest[2] = ':';
2463  
    dest[2 + n + 1] = '@';
2463  
    dest[2 + n + 1] = '@';
2464  
    if (make_absolute)
2464  
    if (make_absolute)
2465  
    {
2465  
    {
2466  
        impl_.split(id_pass, 2 + n);
2466  
        impl_.split(id_pass, 2 + n);
2467  
        impl_.split(id_host, 0);
2467  
        impl_.split(id_host, 0);
2468  
        impl_.split(id_port, 0);
2468  
        impl_.split(id_port, 0);
2469  
        dest[4 + n] = '/';
2469  
        dest[4 + n] = '/';
2470  
    }
2470  
    }
2471  
    check_invariants();
2471  
    check_invariants();
2472  
    return dest + 3;
2472  
    return dest + 3;
2473  
}
2473  
}
2474  

2474  

2475  
inline
2475  
inline
2476  
char*
2476  
char*
2477  
url_base::
2477  
url_base::
2478  
set_userinfo_impl(
2478  
set_userinfo_impl(
2479  
    std::size_t n,
2479  
    std::size_t n,
2480  
    op_t& op)
2480  
    op_t& op)
2481  
{
2481  
{
2482  
    // "//" {dest} "@"
2482  
    // "//" {dest} "@"
2483  
    check_invariants();
2483  
    check_invariants();
2484  
    bool const make_absolute =
2484  
    bool const make_absolute =
2485  
            !is_path_absolute() &&
2485  
            !is_path_absolute() &&
2486  
            !impl_.get(id_path).empty();
2486  
            !impl_.get(id_path).empty();
2487  
    auto dest = resize_impl(
2487  
    auto dest = resize_impl(
2488  
        id_user, id_host, n + 3 + make_absolute, op);
2488  
        id_user, id_host, n + 3 + make_absolute, op);
2489  
    impl_.split(id_user, n + 2);
2489  
    impl_.split(id_user, n + 2);
2490  
    dest[0] = '/';
2490  
    dest[0] = '/';
2491  
    dest[1] = '/';
2491  
    dest[1] = '/';
2492  
    dest[n + 2] = '@';
2492  
    dest[n + 2] = '@';
2493  
    if (make_absolute)
2493  
    if (make_absolute)
2494  
    {
2494  
    {
2495  
        impl_.split(id_pass, 1);
2495  
        impl_.split(id_pass, 1);
2496  
        impl_.split(id_host, 0);
2496  
        impl_.split(id_host, 0);
2497  
        impl_.split(id_port, 0);
2497  
        impl_.split(id_port, 0);
2498  
        dest[3 + n] = '/';
2498  
        dest[3 + n] = '/';
2499  
    }
2499  
    }
2500  
    check_invariants();
2500  
    check_invariants();
2501  
    return dest + 2;
2501  
    return dest + 2;
2502  
}
2502  
}
2503  

2503  

2504  
inline
2504  
inline
2505  
char*
2505  
char*
2506  
url_base::
2506  
url_base::
2507  
set_host_impl(
2507  
set_host_impl(
2508  
    std::size_t n,
2508  
    std::size_t n,
2509  
    op_t& op)
2509  
    op_t& op)
2510  
{
2510  
{
2511  
    check_invariants();
2511  
    check_invariants();
2512  
    if(impl_.len(id_user) == 0)
2512  
    if(impl_.len(id_user) == 0)
2513  
    {
2513  
    {
2514  
        // add authority
2514  
        // add authority
2515  
        bool make_absolute =
2515  
        bool make_absolute =
2516  
            !is_path_absolute() &&
2516  
            !is_path_absolute() &&
2517  
            impl_.len(id_path) != 0;
2517  
            impl_.len(id_path) != 0;
2518  
        auto pn = impl_.len(id_path);
2518  
        auto pn = impl_.len(id_path);
2519  
        auto dest = resize_impl(
2519  
        auto dest = resize_impl(
2520  
            id_user, n + 2 + make_absolute, op);
2520  
            id_user, n + 2 + make_absolute, op);
2521  
        impl_.split(id_user, 2);
2521  
        impl_.split(id_user, 2);
2522  
        impl_.split(id_pass, 0);
2522  
        impl_.split(id_pass, 0);
2523  
        impl_.split(id_host, n);
2523  
        impl_.split(id_host, n);
2524  
        impl_.split(id_port, 0);
2524  
        impl_.split(id_port, 0);
2525  
        impl_.split(id_path, pn + make_absolute);
2525  
        impl_.split(id_path, pn + make_absolute);
2526  
        if (make_absolute)
2526  
        if (make_absolute)
2527  
        {
2527  
        {
2528  
            dest[n + 2] = '/';
2528  
            dest[n + 2] = '/';
2529  
            ++impl_.decoded_[id_path];
2529  
            ++impl_.decoded_[id_path];
2530  
        }
2530  
        }
2531  
        dest[0] = '/';
2531  
        dest[0] = '/';
2532  
        dest[1] = '/';
2532  
        dest[1] = '/';
2533  
        check_invariants();
2533  
        check_invariants();
2534  
        return dest + 2;
2534  
        return dest + 2;
2535  
    }
2535  
    }
2536  
    // already have authority
2536  
    // already have authority
2537  
    auto const dest = resize_impl(
2537  
    auto const dest = resize_impl(
2538  
        id_host, n, op);
2538  
        id_host, n, op);
2539  
    check_invariants();
2539  
    check_invariants();
2540  
    return dest;
2540  
    return dest;
2541  
}
2541  
}
2542  

2542  

2543  
inline
2543  
inline
2544  
char*
2544  
char*
2545  
url_base::
2545  
url_base::
2546  
set_port_impl(
2546  
set_port_impl(
2547  
    std::size_t n,
2547  
    std::size_t n,
2548  
    op_t& op)
2548  
    op_t& op)
2549  
{
2549  
{
2550  
    check_invariants();
2550  
    check_invariants();
2551  
    if(impl_.len(id_user) != 0)
2551  
    if(impl_.len(id_user) != 0)
2552  
    {
2552  
    {
2553  
        // authority exists
2553  
        // authority exists
2554  
        auto dest = resize_impl(
2554  
        auto dest = resize_impl(
2555  
            id_port, n + 1, op);
2555  
            id_port, n + 1, op);
2556  
        dest[0] = ':';
2556  
        dest[0] = ':';
2557  
        check_invariants();
2557  
        check_invariants();
2558  
        return dest + 1;
2558  
        return dest + 1;
2559  
    }
2559  
    }
2560  
    bool make_absolute =
2560  
    bool make_absolute =
2561  
        !is_path_absolute() &&
2561  
        !is_path_absolute() &&
2562  
        impl_.len(id_path) != 0;
2562  
        impl_.len(id_path) != 0;
2563  
    auto dest = resize_impl(
2563  
    auto dest = resize_impl(
2564  
        id_user, 3 + n + make_absolute, op);
2564  
        id_user, 3 + n + make_absolute, op);
2565  
    impl_.split(id_user, 2);
2565  
    impl_.split(id_user, 2);
2566  
    impl_.split(id_pass, 0);
2566  
    impl_.split(id_pass, 0);
2567  
    impl_.split(id_host, 0);
2567  
    impl_.split(id_host, 0);
2568  
    dest[0] = '/';
2568  
    dest[0] = '/';
2569  
    dest[1] = '/';
2569  
    dest[1] = '/';
2570  
    dest[2] = ':';
2570  
    dest[2] = ':';
2571  
    if (make_absolute)
2571  
    if (make_absolute)
2572  
    {
2572  
    {
2573  
        impl_.split(id_port, n + 1);
2573  
        impl_.split(id_port, n + 1);
2574  
        dest[n + 3] = '/';
2574  
        dest[n + 3] = '/';
2575  
        ++impl_.decoded_[id_path];
2575  
        ++impl_.decoded_[id_path];
2576  
    }
2576  
    }
2577  
    check_invariants();
2577  
    check_invariants();
2578  
    return dest + 3;
2578  
    return dest + 3;
2579  
}
2579  
}
2580  

2580  

2581  
inline
2581  
inline
2582  
char*
2582  
char*
2583  
url_base::
2583  
url_base::
2584  
set_path_impl(
2584  
set_path_impl(
2585  
    std::size_t n,
2585  
    std::size_t n,
2586  
    op_t& op)
2586  
    op_t& op)
2587  
{
2587  
{
2588  
    check_invariants();
2588  
    check_invariants();
2589  
    auto const dest = resize_impl(
2589  
    auto const dest = resize_impl(
2590  
        id_path, n, op);
2590  
        id_path, n, op);
2591  
    return dest;
2591  
    return dest;
2592  
}
2592  
}
2593  

2593  

2594  

2594  

2595  
//------------------------------------------------
2595  
//------------------------------------------------
2596  

2596  

2597  
// return the first segment of the path.
2597  
// return the first segment of the path.
2598  
// this is needed for some algorithms.
2598  
// this is needed for some algorithms.
2599  
inline
2599  
inline
2600  
core::string_view
2600  
core::string_view
2601  
url_base::
2601  
url_base::
2602  
first_segment() const noexcept
2602  
first_segment() const noexcept
2603  
{
2603  
{
2604  
    if(impl_.nseg_ == 0)
2604  
    if(impl_.nseg_ == 0)
2605  
        return {};
2605  
        return {};
2606  
    auto const p0 = impl_.cs_ +
2606  
    auto const p0 = impl_.cs_ +
2607  
        impl_.offset(id_path) +
2607  
        impl_.offset(id_path) +
2608  
            detail::path_prefix(
2608  
            detail::path_prefix(
2609  
                impl_.get(id_path));
2609  
                impl_.get(id_path));
2610  
    auto const end = impl_.cs_ +
2610  
    auto const end = impl_.cs_ +
2611  
        impl_.offset(id_query);
2611  
        impl_.offset(id_query);
2612  
    if(impl_.nseg_ == 1)
2612  
    if(impl_.nseg_ == 1)
2613  
        return core::string_view(
2613  
        return core::string_view(
2614  
            p0, end - p0);
2614  
            p0, end - p0);
2615  
    auto p = p0;
2615  
    auto p = p0;
2616  
    while(*p != '/')
2616  
    while(*p != '/')
2617  
        ++p;
2617  
        ++p;
2618  
    BOOST_ASSERT(p < end);
2618  
    BOOST_ASSERT(p < end);
2619  
    return core::string_view(p0, p - p0);
2619  
    return core::string_view(p0, p - p0);
2620  
}
2620  
}
2621  

2621  

2622  
inline
2622  
inline
2623  
detail::segments_iter_impl
2623  
detail::segments_iter_impl
2624  
url_base::
2624  
url_base::
2625  
edit_segments(
2625  
edit_segments(
2626  
    detail::segments_iter_impl const& it0,
2626  
    detail::segments_iter_impl const& it0,
2627  
    detail::segments_iter_impl const& it1,
2627  
    detail::segments_iter_impl const& it1,
2628  
    detail::any_segments_iter&& src,
2628  
    detail::any_segments_iter&& src,
2629  
    // -1 = preserve
2629  
    // -1 = preserve
2630  
    //  0 = make relative (can fail)
2630  
    //  0 = make relative (can fail)
2631  
    //  1 = make absolute
2631  
    //  1 = make absolute
2632  
    int absolute)
2632  
    int absolute)
2633  
{
2633  
{
2634  
    // Iterator doesn't belong to this url
2634  
    // Iterator doesn't belong to this url
2635  
    BOOST_ASSERT(it0.ref.alias_of(impl_));
2635  
    BOOST_ASSERT(it0.ref.alias_of(impl_));
2636  

2636  

2637  
    // Iterator doesn't belong to this url
2637  
    // Iterator doesn't belong to this url
2638  
    BOOST_ASSERT(it1.ref.alias_of(impl_));
2638  
    BOOST_ASSERT(it1.ref.alias_of(impl_));
2639  

2639  

2640  
    // Iterator is in the wrong order
2640  
    // Iterator is in the wrong order
2641  
    BOOST_ASSERT(it0.index <= it1.index);
2641  
    BOOST_ASSERT(it0.index <= it1.index);
2642  

2642  

2643  
    // Iterator is out of range
2643  
    // Iterator is out of range
2644  
    BOOST_ASSERT(it0.index <= impl_.nseg_);
2644  
    BOOST_ASSERT(it0.index <= impl_.nseg_);
2645  
    BOOST_ASSERT(it0.pos <= impl_.len(id_path));
2645  
    BOOST_ASSERT(it0.pos <= impl_.len(id_path));
2646  

2646  

2647  
    // Iterator is out of range
2647  
    // Iterator is out of range
2648  
    BOOST_ASSERT(it1.index <= impl_.nseg_);
2648  
    BOOST_ASSERT(it1.index <= impl_.nseg_);
2649  
    BOOST_ASSERT(it1.pos <= impl_.len(id_path));
2649  
    BOOST_ASSERT(it1.pos <= impl_.len(id_path));
2650  

2650  

2651  
//------------------------------------------------
2651  
//------------------------------------------------
2652  
//
2652  
//
2653  
//  Calculate output prefix
2653  
//  Calculate output prefix
2654  
//
2654  
//
2655  
//  0 = ""
2655  
//  0 = ""
2656  
//  1 = "/"
2656  
//  1 = "/"
2657  
//  2 = "./"
2657  
//  2 = "./"
2658  
//  3 = "/./"
2658  
//  3 = "/./"
2659  
//
2659  
//
2660  
    bool const is_abs = is_path_absolute();
2660  
    bool const is_abs = is_path_absolute();
2661  
    if(has_authority())
2661  
    if(has_authority())
2662  
    {
2662  
    {
2663  
        // Check if the new
2663  
        // Check if the new
2664  
        // path would be empty
2664  
        // path would be empty
2665  
        if( src.fast_nseg == 0 &&
2665  
        if( src.fast_nseg == 0 &&
2666  
            it0.index == 0 &&
2666  
            it0.index == 0 &&
2667  
            it1.index == impl_.nseg_)
2667  
            it1.index == impl_.nseg_)
2668  
        {
2668  
        {
2669  
            // VFALCO we don't have
2669  
            // VFALCO we don't have
2670  
            // access to nchar this early
2670  
            // access to nchar this early
2671  
            //
2671  
            //
2672  
            //BOOST_ASSERT(nchar == 0);
2672  
            //BOOST_ASSERT(nchar == 0);
2673  
            absolute = 0;
2673  
            absolute = 0;
2674  
        }
2674  
        }
2675  
        else
2675  
        else
2676  
        {
2676  
        {
2677  
            // prefix "/" required
2677  
            // prefix "/" required
2678  
            absolute = 1;
2678  
            absolute = 1;
2679  
        }
2679  
        }
2680  
    }
2680  
    }
2681  
    else if(absolute < 0)
2681  
    else if(absolute < 0)
2682  
    {
2682  
    {
2683  
        absolute = is_abs; // preserve
2683  
        absolute = is_abs; // preserve
2684  
    }
2684  
    }
2685  
    auto const path_pos = impl_.offset(id_path);
2685  
    auto const path_pos = impl_.offset(id_path);
2686  

2686  

2687  
    std::size_t nchar = 0;
2687  
    std::size_t nchar = 0;
2688  
    std::size_t prefix = 0;
2688  
    std::size_t prefix = 0;
2689  
    bool encode_colons = false;
2689  
    bool encode_colons = false;
2690  
    bool cp_src_prefix = false;
2690  
    bool cp_src_prefix = false;
2691  
    if(it0.index > 0)
2691  
    if(it0.index > 0)
2692  
    {
2692  
    {
2693  
        // first segment unchanged
2693  
        // first segment unchanged
2694  
        prefix = src.fast_nseg > 0;
2694  
        prefix = src.fast_nseg > 0;
2695  
    }
2695  
    }
2696  
    else if(src.fast_nseg > 0)
2696  
    else if(src.fast_nseg > 0)
2697  
    {
2697  
    {
2698  
        // first segment from src
2698  
        // first segment from src
2699  
        if(! src.front.empty())
2699  
        if(! src.front.empty())
2700  
        {
2700  
        {
2701  
            if( src.front == "." &&
2701  
            if( src.front == "." &&
2702  
                    src.fast_nseg > 1)
2702  
                    src.fast_nseg > 1)
2703  
                if (src.s.empty())
2703  
                if (src.s.empty())
2704  
                {
2704  
                {
2705  
                    // if front is ".", we need the extra "." in the prefix
2705  
                    // if front is ".", we need the extra "." in the prefix
2706  
                    // which will maintain the invariant that segments represent
2706  
                    // which will maintain the invariant that segments represent
2707  
                    // {"."}
2707  
                    // {"."}
2708  
                    prefix = 2 + absolute;
2708  
                    prefix = 2 + absolute;
2709  
                }
2709  
                }
2710  
                else
2710  
                else
2711  
                {
2711  
                {
2712  
                    // if the "." prefix is explicitly required from set_path
2712  
                    // if the "." prefix is explicitly required from set_path
2713  
                    // we do not include an extra "." segment
2713  
                    // we do not include an extra "." segment
2714  
                    prefix = absolute;
2714  
                    prefix = absolute;
2715  
                    cp_src_prefix = true;
2715  
                    cp_src_prefix = true;
2716  
                }
2716  
                }
2717  
            else if(absolute)
2717  
            else if(absolute)
2718  
                prefix = 1;
2718  
                prefix = 1;
2719  
            else if(has_scheme() ||
2719  
            else if(has_scheme() ||
2720  
                    ! src.front.contains(':'))
2720  
                    ! src.front.contains(':'))
2721  
                prefix = 0;
2721  
                prefix = 0;
2722  
            else
2722  
            else
2723  
            {
2723  
            {
2724  
                prefix = 0;
2724  
                prefix = 0;
2725  
                encode_colons = true;
2725  
                encode_colons = true;
2726  
            }
2726  
            }
2727  
        }
2727  
        }
2728  
        else
2728  
        else
2729  
        {
2729  
        {
2730  
            prefix = 2 + absolute;
2730  
            prefix = 2 + absolute;
2731  
        }
2731  
        }
2732  
    }
2732  
    }
2733  
    else
2733  
    else
2734  
    {
2734  
    {
2735  
        // first segment from it1
2735  
        // first segment from it1
2736  
        auto const p =
2736  
        auto const p =
2737  
            impl_.cs_ + path_pos + it1.pos;
2737  
            impl_.cs_ + path_pos + it1.pos;
2738  
        switch(impl_.cs_ +
2738  
        switch(impl_.cs_ +
2739  
            impl_.offset(id_query) - p)
2739  
            impl_.offset(id_query) - p)
2740  
        {
2740  
        {
2741  
        case 0:
2741  
        case 0:
2742  
            // points to end
2742  
            // points to end
2743  
            prefix = absolute;
2743  
            prefix = absolute;
2744  
            break;
2744  
            break;
2745  
        default:
2745  
        default:
2746  
            BOOST_ASSERT(*p == '/');
2746  
            BOOST_ASSERT(*p == '/');
2747  
            if(p[1] != '/')
2747  
            if(p[1] != '/')
2748  
            {
2748  
            {
2749  
                if(absolute)
2749  
                if(absolute)
2750  
                    prefix = 1;
2750  
                    prefix = 1;
2751  
                else if(has_scheme() ||
2751  
                else if(has_scheme() ||
2752  
                        ! it1.dereference().contains(':'))
2752  
                        ! it1.dereference().contains(':'))
2753  
                    prefix = 0;
2753  
                    prefix = 0;
2754  
                else
2754  
                else
2755  
                    prefix = 2;
2755  
                    prefix = 2;
2756  
                break;
2756  
                break;
2757  
            }
2757  
            }
2758  
            // empty
2758  
            // empty
2759  
            BOOST_FALLTHROUGH;
2759  
            BOOST_FALLTHROUGH;
2760  
        case 1:
2760  
        case 1:
2761  
            // empty
2761  
            // empty
2762  
            BOOST_ASSERT(*p == '/');
2762  
            BOOST_ASSERT(*p == '/');
2763  
            prefix = 2 + absolute;
2763  
            prefix = 2 + absolute;
2764  
            break;
2764  
            break;
2765  
        }
2765  
        }
2766  
    }
2766  
    }
2767  

2767  

2768  
//  append '/' to new segs
2768  
//  append '/' to new segs
2769  
//  if inserting at front.
2769  
//  if inserting at front.
2770  
    std::size_t const suffix =
2770  
    std::size_t const suffix =
2771  
        it1.index == 0 &&
2771  
        it1.index == 0 &&
2772  
        impl_.nseg_ > 0 &&
2772  
        impl_.nseg_ > 0 &&
2773  
        src.fast_nseg > 0;
2773  
        src.fast_nseg > 0;
2774  

2774  

2775  
//------------------------------------------------
2775  
//------------------------------------------------
2776  
//
2776  
//
2777  
//  Measure the number of encoded characters
2777  
//  Measure the number of encoded characters
2778  
//  of output, and the number of inserted
2778  
//  of output, and the number of inserted
2779  
//  segments including internal separators.
2779  
//  segments including internal separators.
2780  
//
2780  
//
2781  
    src.encode_colons = encode_colons;
2781  
    src.encode_colons = encode_colons;
2782  
    std::size_t nseg = 0;
2782  
    std::size_t nseg = 0;
2783  
    if(src.measure(nchar))
2783  
    if(src.measure(nchar))
2784  
    {
2784  
    {
2785  
        src.encode_colons = false;
2785  
        src.encode_colons = false;
2786  
        for(;;)
2786  
        for(;;)
2787  
        {
2787  
        {
2788  
            ++nseg;
2788  
            ++nseg;
2789  
            if(! src.measure(nchar))
2789  
            if(! src.measure(nchar))
2790  
                break;
2790  
                break;
2791  
            ++nchar;
2791  
            ++nchar;
2792  
        }
2792  
        }
2793  
    }
2793  
    }
2794  

2794  

2795  
    switch(src.fast_nseg)
2795  
    switch(src.fast_nseg)
2796  
    {
2796  
    {
2797  
    case 0:
2797  
    case 0:
2798  
        BOOST_ASSERT(nseg == 0);
2798  
        BOOST_ASSERT(nseg == 0);
2799  
        break;
2799  
        break;
2800  
    case 1:
2800  
    case 1:
2801  
        BOOST_ASSERT(nseg == 1);
2801  
        BOOST_ASSERT(nseg == 1);
2802  
        break;
2802  
        break;
2803  
    case 2:
2803  
    case 2:
2804  
        BOOST_ASSERT(nseg >= 2);
2804  
        BOOST_ASSERT(nseg >= 2);
2805  
        break;
2805  
        break;
2806  
    }
2806  
    }
2807  

2807  

2808  
//------------------------------------------------
2808  
//------------------------------------------------
2809  
//
2809  
//
2810  
//  Calculate [pos0, pos1) to remove
2810  
//  Calculate [pos0, pos1) to remove
2811  
//
2811  
//
2812  
    auto pos0 = it0.pos;
2812  
    auto pos0 = it0.pos;
2813  
    if(it0.index == 0)
2813  
    if(it0.index == 0)
2814  
    {
2814  
    {
2815  
        // patch pos for prefix
2815  
        // patch pos for prefix
2816  
        pos0 = 0;
2816  
        pos0 = 0;
2817  
    }
2817  
    }
2818  
    auto pos1 = it1.pos;
2818  
    auto pos1 = it1.pos;
2819  
    if(it1.index == 0)
2819  
    if(it1.index == 0)
2820  
    {
2820  
    {
2821  
        // patch pos for prefix
2821  
        // patch pos for prefix
2822  
        pos1 = detail::path_prefix(
2822  
        pos1 = detail::path_prefix(
2823  
            impl_.get(id_path));
2823  
            impl_.get(id_path));
2824  
    }
2824  
    }
2825  
    else if(
2825  
    else if(
2826  
        it0.index == 0 &&
2826  
        it0.index == 0 &&
2827  
        it1.index < impl_.nseg_ &&
2827  
        it1.index < impl_.nseg_ &&
2828  
        nseg == 0)
2828  
        nseg == 0)
2829  
    {
2829  
    {
2830  
        // Remove the slash from segment it1
2830  
        // Remove the slash from segment it1
2831  
        // if it is becoming the new first
2831  
        // if it is becoming the new first
2832  
        // segment.
2832  
        // segment.
2833  
        ++pos1;
2833  
        ++pos1;
2834  
    }
2834  
    }
2835  
    // calc decoded size of old range
2835  
    // calc decoded size of old range
2836  
    auto const dn0 =
2836  
    auto const dn0 =
2837  
        detail::decode_bytes_unsafe(
2837  
        detail::decode_bytes_unsafe(
2838  
            core::string_view(
2838  
            core::string_view(
2839  
                impl_.cs_ +
2839  
                impl_.cs_ +
2840  
                    impl_.offset(id_path) +
2840  
                    impl_.offset(id_path) +
2841  
                    pos0,
2841  
                    pos0,
2842  
                pos1 - pos0));
2842  
                pos1 - pos0));
2843  

2843  

2844  
//------------------------------------------------
2844  
//------------------------------------------------
2845  
//
2845  
//
2846  
//  Resize
2846  
//  Resize
2847  
//
2847  
//
2848  
    op_t op(*this, &src.s);
2848  
    op_t op(*this, &src.s);
2849  
    char* dest;
2849  
    char* dest;
2850  
    char const* end;
2850  
    char const* end;
2851  
    {
2851  
    {
2852  
        auto const nremove = pos1 - pos0;
2852  
        auto const nremove = pos1 - pos0;
2853  
        // check overflow
2853  
        // check overflow
2854  
        if( nchar <= max_size() && (
2854  
        if( nchar <= max_size() && (
2855  
            prefix + suffix <=
2855  
            prefix + suffix <=
2856  
                max_size() - nchar))
2856  
                max_size() - nchar))
2857  
        {
2857  
        {
2858  
            nchar = prefix + nchar + suffix;
2858  
            nchar = prefix + nchar + suffix;
2859  
            if( nchar <= nremove ||
2859  
            if( nchar <= nremove ||
2860  
                nchar - nremove <=
2860  
                nchar - nremove <=
2861  
                    max_size() - size())
2861  
                    max_size() - size())
2862  
                goto ok;
2862  
                goto ok;
2863  
        }
2863  
        }
2864  
        // too large
2864  
        // too large
2865  
        detail::throw_length_error();
2865  
        detail::throw_length_error();
2866  
    ok:
2866  
    ok:
2867  
        auto const new_size =
2867  
        auto const new_size =
2868  
            size() + nchar - nremove;
2868  
            size() + nchar - nremove;
2869  
        reserve_impl(new_size, op);
2869  
        reserve_impl(new_size, op);
2870  
        dest = s_ + path_pos + pos0;
2870  
        dest = s_ + path_pos + pos0;
2871  
        op.move(
2871  
        op.move(
2872  
            dest + nchar,
2872  
            dest + nchar,
2873  
            s_ + path_pos + pos1,
2873  
            s_ + path_pos + pos1,
2874  
            size() - path_pos - pos1);
2874  
            size() - path_pos - pos1);
2875  
        impl_.set_size(
2875  
        impl_.set_size(
2876  
            id_path,
2876  
            id_path,
2877  
            impl_.len(id_path) + nchar - nremove);
2877  
            impl_.len(id_path) + nchar - nremove);
2878  
        BOOST_ASSERT(size() == new_size);
2878  
        BOOST_ASSERT(size() == new_size);
2879  
        end = dest + nchar;
2879  
        end = dest + nchar;
2880  
        auto const nseg1 =
2880  
        auto const nseg1 =
2881  
            static_cast<std::ptrdiff_t>(impl_.nseg_) +
2881  
            static_cast<std::ptrdiff_t>(impl_.nseg_) +
2882  
            static_cast<std::ptrdiff_t>(nseg) -
2882  
            static_cast<std::ptrdiff_t>(nseg) -
2883  
            static_cast<std::ptrdiff_t>(it1.index) +
2883  
            static_cast<std::ptrdiff_t>(it1.index) +
2884  
            static_cast<std::ptrdiff_t>(it0.index) -
2884  
            static_cast<std::ptrdiff_t>(it0.index) -
2885  
            static_cast<std::ptrdiff_t>(cp_src_prefix);
2885  
            static_cast<std::ptrdiff_t>(cp_src_prefix);
2886  
        BOOST_ASSERT(nseg1 >= 0);
2886  
        BOOST_ASSERT(nseg1 >= 0);
2887  
        impl_.nseg_ = detail::to_size_type(nseg1);
2887  
        impl_.nseg_ = detail::to_size_type(nseg1);
2888  
        if(s_)
2888  
        if(s_)
2889  
            s_[size()] = '\0';
2889  
            s_[size()] = '\0';
2890  
    }
2890  
    }
2891  

2891  

2892  
//------------------------------------------------
2892  
//------------------------------------------------
2893  
//
2893  
//
2894  
//  Output segments and internal separators:
2894  
//  Output segments and internal separators:
2895  
//
2895  
//
2896  
//  prefix [ segment [ '/' segment ] ] suffix
2896  
//  prefix [ segment [ '/' segment ] ] suffix
2897  
//
2897  
//
2898  
    auto const dest0 = dest;
2898  
    auto const dest0 = dest;
2899  
    switch(prefix)
2899  
    switch(prefix)
2900  
    {
2900  
    {
2901  
    case 3:
2901  
    case 3:
2902  
        *dest++ = '/';
2902  
        *dest++ = '/';
2903  
        *dest++ = '.';
2903  
        *dest++ = '.';
2904  
        *dest++ = '/';
2904  
        *dest++ = '/';
2905  
        break;
2905  
        break;
2906  
    case 2:
2906  
    case 2:
2907  
        *dest++ = '.';
2907  
        *dest++ = '.';
2908  
        BOOST_FALLTHROUGH;
2908  
        BOOST_FALLTHROUGH;
2909  
    case 1:
2909  
    case 1:
2910  
        *dest++ = '/';
2910  
        *dest++ = '/';
2911  
        break;
2911  
        break;
2912  
    default:
2912  
    default:
2913  
        break;
2913  
        break;
2914  
    }
2914  
    }
2915  
    src.rewind();
2915  
    src.rewind();
2916  
    if(nseg > 0)
2916  
    if(nseg > 0)
2917  
    {
2917  
    {
2918  
        src.encode_colons = encode_colons;
2918  
        src.encode_colons = encode_colons;
2919  
        for(;;)
2919  
        for(;;)
2920  
        {
2920  
        {
2921  
            src.copy(dest, end);
2921  
            src.copy(dest, end);
2922  
            if(--nseg == 0)
2922  
            if(--nseg == 0)
2923  
                break;
2923  
                break;
2924  
            *dest++ = '/';
2924  
            *dest++ = '/';
2925  
            src.encode_colons = false;
2925  
            src.encode_colons = false;
2926  
        }
2926  
        }
2927  
        if(suffix)
2927  
        if(suffix)
2928  
            *dest++ = '/';
2928  
            *dest++ = '/';
2929  
    }
2929  
    }
2930  
    BOOST_ASSERT(dest == dest0 + nchar);
2930  
    BOOST_ASSERT(dest == dest0 + nchar);
2931  

2931  

2932  
    // calc decoded size of new range,
2932  
    // calc decoded size of new range,
2933  
    auto const dn =
2933  
    auto const dn =
2934  
        detail::decode_bytes_unsafe(
2934  
        detail::decode_bytes_unsafe(
2935  
            core::string_view(dest0, dest - dest0));
2935  
            core::string_view(dest0, dest - dest0));
2936  
    if(dn >= dn0)
2936  
    if(dn >= dn0)
2937  
        impl_.decoded_[id_path] +=
2937  
        impl_.decoded_[id_path] +=
2938  
            detail::to_size_type(dn - dn0);
2938  
            detail::to_size_type(dn - dn0);
2939  
    else
2939  
    else
2940  
        impl_.decoded_[id_path] -=
2940  
        impl_.decoded_[id_path] -=
2941  
            detail::to_size_type(dn0 - dn);
2941  
            detail::to_size_type(dn0 - dn);
2942  

2942  

2943  
    return detail::segments_iter_impl(
2943  
    return detail::segments_iter_impl(
2944  
        impl_, pos0, it0.index);
2944  
        impl_, pos0, it0.index);
2945  
}
2945  
}
2946  

2946  

2947  
//------------------------------------------------
2947  
//------------------------------------------------
2948  

2948  

2949  
inline
2949  
inline
2950  
auto
2950  
auto
2951  
url_base::
2951  
url_base::
2952  
edit_params(
2952  
edit_params(
2953  
    detail::params_iter_impl const& it0,
2953  
    detail::params_iter_impl const& it0,
2954  
    detail::params_iter_impl const& it1,
2954  
    detail::params_iter_impl const& it1,
2955  
    detail::any_params_iter&& src) ->
2955  
    detail::any_params_iter&& src) ->
2956  
        detail::params_iter_impl
2956  
        detail::params_iter_impl
2957  
{
2957  
{
2958  
    auto pos0 = impl_.offset(id_query);
2958  
    auto pos0 = impl_.offset(id_query);
2959  
    auto pos1 = pos0 + it1.pos;
2959  
    auto pos1 = pos0 + it1.pos;
2960  
    pos0 = pos0 + it0.pos;
2960  
    pos0 = pos0 + it0.pos;
2961  

2961  

2962  
    // Iterators belong to this url
2962  
    // Iterators belong to this url
2963  
    BOOST_ASSERT(it0.ref.alias_of(impl_));
2963  
    BOOST_ASSERT(it0.ref.alias_of(impl_));
2964  
    BOOST_ASSERT(it1.ref.alias_of(impl_));
2964  
    BOOST_ASSERT(it1.ref.alias_of(impl_));
2965  

2965  

2966  
    // Iterators is in the right order
2966  
    // Iterators is in the right order
2967  
    BOOST_ASSERT(it0.index <= it1.index);
2967  
    BOOST_ASSERT(it0.index <= it1.index);
2968  

2968  

2969  
    // Iterators are within range
2969  
    // Iterators are within range
2970  
    BOOST_ASSERT(it0.index <= impl_.nparam_);
2970  
    BOOST_ASSERT(it0.index <= impl_.nparam_);
2971  
    BOOST_ASSERT(pos0 <= impl_.offset(id_frag));
2971  
    BOOST_ASSERT(pos0 <= impl_.offset(id_frag));
2972  
    BOOST_ASSERT(it1.index <= impl_.nparam_);
2972  
    BOOST_ASSERT(it1.index <= impl_.nparam_);
2973  
    BOOST_ASSERT(pos1 <= impl_.offset(id_frag));
2973  
    BOOST_ASSERT(pos1 <= impl_.offset(id_frag));
2974  

2974  

2975  
    // calc decoded size of old range,
2975  
    // calc decoded size of old range,
2976  
    // minus one if '?' or '&' prefixed
2976  
    // minus one if '?' or '&' prefixed
2977  
    auto dn0 =
2977  
    auto dn0 =
2978  
        static_cast<std::ptrdiff_t>(
2978  
        static_cast<std::ptrdiff_t>(
2979  
            detail::decode_bytes_unsafe(
2979  
            detail::decode_bytes_unsafe(
2980  
                core::string_view(
2980  
                core::string_view(
2981  
                    impl_.cs_ + pos0,
2981  
                    impl_.cs_ + pos0,
2982  
                    pos1 - pos0)));
2982  
                    pos1 - pos0)));
2983  
    if(impl_.len(id_query) > 0)
2983  
    if(impl_.len(id_query) > 0)
2984  
        dn0 -= 1;
2984  
        dn0 -= 1;
2985  
    if(dn0 < 0)
2985  
    if(dn0 < 0)
2986  
        dn0 = 0;
2986  
        dn0 = 0;
2987  

2987  

2988  
//------------------------------------------------
2988  
//------------------------------------------------
2989  
//
2989  
//
2990  
//  Measure the number of encoded characters
2990  
//  Measure the number of encoded characters
2991  
//  of output, and the number of inserted
2991  
//  of output, and the number of inserted
2992  
//  segments including internal separators.
2992  
//  segments including internal separators.
2993  
//
2993  
//
2994  

2994  

2995  
    std::size_t nchar = 0;
2995  
    std::size_t nchar = 0;
2996  
    std::size_t nparam = 0;
2996  
    std::size_t nparam = 0;
2997  
    if(src.measure(nchar))
2997  
    if(src.measure(nchar))
2998  
    {
2998  
    {
2999  
        ++nchar; // for '?' or '&'
2999  
        ++nchar; // for '?' or '&'
3000  
        for(;;)
3000  
        for(;;)
3001  
        {
3001  
        {
3002  
            ++nparam;
3002  
            ++nparam;
3003  
            if(! src.measure(nchar))
3003  
            if(! src.measure(nchar))
3004  
                break;
3004  
                break;
3005  
            ++nchar; // for '&'
3005  
            ++nchar; // for '&'
3006  
        }
3006  
        }
3007  
    }
3007  
    }
3008  

3008  

3009  
//------------------------------------------------
3009  
//------------------------------------------------
3010  
//
3010  
//
3011  
//  Resize
3011  
//  Resize
3012  
//
3012  
//
3013  
    op_t op(*this, &src.s0, &src.s1);
3013  
    op_t op(*this, &src.s0, &src.s1);
3014  
    char* dest;
3014  
    char* dest;
3015  
    char const* end;
3015  
    char const* end;
3016  
    {
3016  
    {
3017  
        auto const nremove = pos1 - pos0;
3017  
        auto const nremove = pos1 - pos0;
3018  
        // check overflow
3018  
        // check overflow
3019  
        if( nchar > nremove &&
3019  
        if( nchar > nremove &&
3020  
            nchar - nremove >
3020  
            nchar - nremove >
3021  
                max_size() - size())
3021  
                max_size() - size())
3022  
        {
3022  
        {
3023  
            // too large
3023  
            // too large
3024  
            detail::throw_length_error();
3024  
            detail::throw_length_error();
3025  
        }
3025  
        }
3026  
        auto const nparam1 =
3026  
        auto const nparam1 =
3027  
            static_cast<std::ptrdiff_t>(impl_.nparam_) +
3027  
            static_cast<std::ptrdiff_t>(impl_.nparam_) +
3028  
            static_cast<std::ptrdiff_t>(nparam) -
3028  
            static_cast<std::ptrdiff_t>(nparam) -
3029  
            static_cast<std::ptrdiff_t>(it1.index) +
3029  
            static_cast<std::ptrdiff_t>(it1.index) +
3030  
            static_cast<std::ptrdiff_t>(it0.index);
3030  
            static_cast<std::ptrdiff_t>(it0.index);
3031  
        BOOST_ASSERT(nparam1 >= 0);
3031  
        BOOST_ASSERT(nparam1 >= 0);
3032  
        reserve_impl(size() + nchar - nremove, op);
3032  
        reserve_impl(size() + nchar - nremove, op);
3033  
        dest = s_ + pos0;
3033  
        dest = s_ + pos0;
3034  
        end = dest + nchar;
3034  
        end = dest + nchar;
3035  
        if(impl_.nparam_ > 0)
3035  
        if(impl_.nparam_ > 0)
3036  
        {
3036  
        {
3037  
            // needed when we move
3037  
            // needed when we move
3038  
            // the beginning of the query
3038  
            // the beginning of the query
3039  
            s_[impl_.offset(id_query)] = '&';
3039  
            s_[impl_.offset(id_query)] = '&';
3040  
        }
3040  
        }
3041  
        op.move(
3041  
        op.move(
3042  
            dest + nchar,
3042  
            dest + nchar,
3043  
            impl_.cs_ + pos1,
3043  
            impl_.cs_ + pos1,
3044  
            size() - pos1);
3044  
            size() - pos1);
3045  
        impl_.set_size(
3045  
        impl_.set_size(
3046  
            id_query,
3046  
            id_query,
3047  
            impl_.len(id_query) +
3047  
            impl_.len(id_query) +
3048  
                nchar - nremove);
3048  
                nchar - nremove);
3049  
        impl_.nparam_ =
3049  
        impl_.nparam_ =
3050  
            detail::to_size_type(nparam1);
3050  
            detail::to_size_type(nparam1);
3051  
        if(nparam1 > 0)
3051  
        if(nparam1 > 0)
3052  
        {
3052  
        {
3053  
            // needed when we erase
3053  
            // needed when we erase
3054  
            // the beginning of the query
3054  
            // the beginning of the query
3055  
            s_[impl_.offset(id_query)] = '?';
3055  
            s_[impl_.offset(id_query)] = '?';
3056  
        }
3056  
        }
3057  
        if(s_)
3057  
        if(s_)
3058  
            s_[size()] = '\0';
3058  
            s_[size()] = '\0';
3059  
    }
3059  
    }
3060  
    auto const dest0 = dest;
3060  
    auto const dest0 = dest;
3061  

3061  

3062  
//------------------------------------------------
3062  
//------------------------------------------------
3063  
//
3063  
//
3064  
//  Output params and internal separators:
3064  
//  Output params and internal separators:
3065  
//
3065  
//
3066  
//  [ '?' param ] [ '&' param ]
3066  
//  [ '?' param ] [ '&' param ]
3067  
//
3067  
//
3068  
    if(nparam > 0)
3068  
    if(nparam > 0)
3069  
    {
3069  
    {
3070  
        if(it0.index == 0)
3070  
        if(it0.index == 0)
3071  
            *dest++ = '?';
3071  
            *dest++ = '?';
3072  
        else
3072  
        else
3073  
            *dest++ = '&';
3073  
            *dest++ = '&';
3074  
        src.rewind();
3074  
        src.rewind();
3075  
        for(;;)
3075  
        for(;;)
3076  
        {
3076  
        {
3077  
            src.copy(dest, end);
3077  
            src.copy(dest, end);
3078  
            if(--nparam == 0)
3078  
            if(--nparam == 0)
3079  
                break;
3079  
                break;
3080  
            *dest++ = '&';
3080  
            *dest++ = '&';
3081  
        }
3081  
        }
3082  
    }
3082  
    }
3083  

3083  

3084  
    // calc decoded size of new range,
3084  
    // calc decoded size of new range,
3085  
    // minus one if '?' or '&' prefixed
3085  
    // minus one if '?' or '&' prefixed
3086  
    auto dn =
3086  
    auto dn =
3087  
        static_cast<std::ptrdiff_t>(
3087  
        static_cast<std::ptrdiff_t>(
3088  
            detail::decode_bytes_unsafe(
3088  
            detail::decode_bytes_unsafe(
3089  
                core::string_view(dest0, dest - dest0)));
3089  
                core::string_view(dest0, dest - dest0)));
3090  
    if(impl_.len(id_query) > 0)
3090  
    if(impl_.len(id_query) > 0)
3091  
        dn -= 1;
3091  
        dn -= 1;
3092  
    if(dn < 0)
3092  
    if(dn < 0)
3093  
        dn = 0;
3093  
        dn = 0;
3094  

3094  

3095  
    if(dn >= dn0)
3095  
    if(dn >= dn0)
3096  
        impl_.decoded_[id_query] +=
3096  
        impl_.decoded_[id_query] +=
3097  
            detail::to_size_type(dn - dn0);
3097  
            detail::to_size_type(dn - dn0);
3098  
    else
3098  
    else
3099  
        impl_.decoded_[id_query] -=
3099  
        impl_.decoded_[id_query] -=
3100  
            detail::to_size_type(dn0 - dn);
3100  
            detail::to_size_type(dn0 - dn);
3101  

3101  

3102  
    return detail::params_iter_impl(
3102  
    return detail::params_iter_impl(
3103  
        impl_,
3103  
        impl_,
3104  
        pos0 - impl_.offset_[id_query],
3104  
        pos0 - impl_.offset_[id_query],
3105  
        it0.index);
3105  
        it0.index);
3106  
}
3106  
}
3107  

3107  

3108  
//------------------------------------------------
3108  
//------------------------------------------------
3109  

3109  

3110  
inline
3110  
inline
3111  
void
3111  
void
3112  
url_base::
3112  
url_base::
3113  
decoded_to_lower_impl(int id) noexcept
3113  
decoded_to_lower_impl(int id) noexcept
3114  
{
3114  
{
3115  
    char* it = s_ + impl_.offset(id);
3115  
    char* it = s_ + impl_.offset(id);
3116  
    char const* const end = s_ + impl_.offset(id + 1);
3116  
    char const* const end = s_ + impl_.offset(id + 1);
3117  
    while(it < end)
3117  
    while(it < end)
3118  
    {
3118  
    {
3119  
        if (*it != '%')
3119  
        if (*it != '%')
3120  
        {
3120  
        {
3121  
            *it = grammar::to_lower(
3121  
            *it = grammar::to_lower(
3122  
                *it);
3122  
                *it);
3123  
            ++it;
3123  
            ++it;
3124  
            continue;
3124  
            continue;
3125  
        }
3125  
        }
3126  
        it += 3;
3126  
        it += 3;
3127  
    }
3127  
    }
3128  
}
3128  
}
3129  

3129  

3130  
inline
3130  
inline
3131  
void
3131  
void
3132  
url_base::
3132  
url_base::
3133  
to_lower_impl(int id) noexcept
3133  
to_lower_impl(int id) noexcept
3134  
{
3134  
{
3135  
    char* it = s_ + impl_.offset(id);
3135  
    char* it = s_ + impl_.offset(id);
3136  
    char const* const end = s_ + impl_.offset(id + 1);
3136  
    char const* const end = s_ + impl_.offset(id + 1);
3137  
    while(it < end)
3137  
    while(it < end)
3138  
    {
3138  
    {
3139  
        *it = grammar::to_lower(
3139  
        *it = grammar::to_lower(
3140  
            *it);
3140  
            *it);
3141  
        ++it;
3141  
        ++it;
3142  
    }
3142  
    }
3143  
}
3143  
}
3144  

3144  

3145  
} // urls
3145  
} // urls
3146  
} // boost
3146  
} // boost
3147  

3147  

3148  
#endif
3148  
#endif