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

10  

11  

11  

12  
#include <boost/url/detail/config.hpp>
12  
#include <boost/url/detail/config.hpp>
13  
#include <boost/url/url_view_base.hpp>
13  
#include <boost/url/url_view_base.hpp>
14  
#include <boost/url/url_view.hpp>
14  
#include <boost/url/url_view.hpp>
15  
#include <boost/url/detail/except.hpp>
15  
#include <boost/url/detail/except.hpp>
16  
#include "detail/normalize.hpp"
16  
#include "detail/normalize.hpp"
17  
#include "detail/over_allocator.hpp"
17  
#include "detail/over_allocator.hpp"
18  

18  

19  
namespace boost {
19  
namespace boost {
20  
namespace urls {
20  
namespace urls {
21  

21  

22  
// construct empty view
22  
// construct empty view
23  
url_view_base::
23  
url_view_base::
24  
url_view_base() noexcept
24  
url_view_base() noexcept
25  
    : impl_(from::url)
25  
    : impl_(from::url)
26  
    , pi_(&impl_)
26  
    , pi_(&impl_)
27  
{
27  
{
28  
}
28  
}
29  

29  

30  
// construct reference
30  
// construct reference
31  
url_view_base::
31  
url_view_base::
32  
url_view_base(
32  
url_view_base(
33  
    detail::url_impl const& impl) noexcept
33  
    detail::url_impl const& impl) noexcept
34  
    : impl_(impl)
34  
    : impl_(impl)
35  
    , pi_(&impl_)
35  
    , pi_(&impl_)
36  
{
36  
{
37  
}
37  
}
38  

38  

39  
//------------------------------------------------
39  
//------------------------------------------------
40  

40  

41  
std::size_t
41  
std::size_t
42  
url_view_base::
42  
url_view_base::
43  
digest(std::size_t salt) const noexcept
43  
digest(std::size_t salt) const noexcept
44  
{
44  
{
45  
    detail::fnv_1a h(salt);
45  
    detail::fnv_1a h(salt);
46  
    detail::ci_digest(pi_->get(id_scheme), h);
46  
    detail::ci_digest(pi_->get(id_scheme), h);
47  
    detail::digest_encoded(pi_->get(id_user), h);
47  
    detail::digest_encoded(pi_->get(id_user), h);
48  
    detail::digest_encoded(pi_->get(id_pass), h);
48  
    detail::digest_encoded(pi_->get(id_pass), h);
49  
    detail::ci_digest_encoded(pi_->get(id_host), h);
49  
    detail::ci_digest_encoded(pi_->get(id_host), h);
50  
    h.put(pi_->get(id_port));
50  
    h.put(pi_->get(id_port));
51  
    detail::normalized_path_digest(
51  
    detail::normalized_path_digest(
52  
        pi_->get(id_path), is_path_absolute(), h);
52  
        pi_->get(id_path), is_path_absolute(), h);
53  
    detail::digest_encoded(pi_->get(id_query), h);
53  
    detail::digest_encoded(pi_->get(id_query), h);
54  
    detail::digest_encoded(pi_->get(id_frag), h);
54  
    detail::digest_encoded(pi_->get(id_frag), h);
55  
    return h.digest();
55  
    return h.digest();
56  
}
56  
}
57  

57  

58  
//------------------------------------------------
58  
//------------------------------------------------
59  
//
59  
//
60  
// Observers
60  
// Observers
61  
//
61  
//
62  
//------------------------------------------------
62  
//------------------------------------------------
63  

63  

64  
struct url_view_base::shared_impl
64  
struct url_view_base::shared_impl
65  
    : url_view
65  
    : url_view
66  
{
66  
{
67  
    virtual
67  
    virtual
68  
    ~shared_impl()
68  
    ~shared_impl()
69  
    {
69  
    {
70  
    }
70  
    }
71  

71  

72  
    shared_impl(
72  
    shared_impl(
73  
        url_view const& u) noexcept
73  
        url_view const& u) noexcept
74  
        : url_view(u)
74  
        : url_view(u)
75  
    {
75  
    {
76  
        impl_.cs_ = reinterpret_cast<
76  
        impl_.cs_ = reinterpret_cast<
77  
            char const*>(this + 1);
77  
            char const*>(this + 1);
78  
    }
78  
    }
79  
};
79  
};
80  

80  

81  
std::shared_ptr<url_view const>
81  
std::shared_ptr<url_view const>
82  
url_view_base::
82  
url_view_base::
83  
persist() const
83  
persist() const
84  
{
84  
{
85  
    using T = shared_impl;
85  
    using T = shared_impl;
86  
    using Alloc = std::allocator<char>;
86  
    using Alloc = std::allocator<char>;
87  
    Alloc a;
87  
    Alloc a;
88  
    auto p = std::allocate_shared<T>(
88  
    auto p = std::allocate_shared<T>(
89  
        detail::over_allocator<T, Alloc>(
89  
        detail::over_allocator<T, Alloc>(
90  
            size(), a), url_view(*pi_));
90  
            size(), a), url_view(*pi_));
91  
    std::memcpy(
91  
    std::memcpy(
92  
        reinterpret_cast<char*>(
92  
        reinterpret_cast<char*>(
93  
            p.get() + 1), data(), size());
93  
            p.get() + 1), data(), size());
94  
    return p;
94  
    return p;
95  
}
95  
}
96  

96  

97  
//------------------------------------------------
97  
//------------------------------------------------
98  
//
98  
//
99  
// Scheme
99  
// Scheme
100  
//
100  
//
101  
//------------------------------------------------
101  
//------------------------------------------------
102  

102  

103  
bool
103  
bool
104  
url_view_base::
104  
url_view_base::
105  
has_scheme() const noexcept
105  
has_scheme() const noexcept
106  
{
106  
{
107  
    auto const n = pi_->len(
107  
    auto const n = pi_->len(
108  
        id_scheme);
108  
        id_scheme);
109  
    if(n == 0)
109  
    if(n == 0)
110  
        return false;
110  
        return false;
111  
    BOOST_ASSERT(n > 1);
111  
    BOOST_ASSERT(n > 1);
112  
    BOOST_ASSERT(
112  
    BOOST_ASSERT(
113  
        pi_->get(id_scheme
113  
        pi_->get(id_scheme
114  
            ).ends_with(':'));
114  
            ).ends_with(':'));
115  
    return true;
115  
    return true;
116  
}
116  
}
117  

117  

118  
core::string_view
118  
core::string_view
119  
url_view_base::
119  
url_view_base::
120  
scheme() const noexcept
120  
scheme() const noexcept
121  
{
121  
{
122  
    auto s = pi_->get(id_scheme);
122  
    auto s = pi_->get(id_scheme);
123  
    if(! s.empty())
123  
    if(! s.empty())
124  
    {
124  
    {
125  
        BOOST_ASSERT(s.size() > 1);
125  
        BOOST_ASSERT(s.size() > 1);
126  
        BOOST_ASSERT(s.ends_with(':'));
126  
        BOOST_ASSERT(s.ends_with(':'));
127  
        s.remove_suffix(1);
127  
        s.remove_suffix(1);
128  
    }
128  
    }
129  
    return s;
129  
    return s;
130  
}
130  
}
131  

131  

132  
urls::scheme
132  
urls::scheme
133  
url_view_base::
133  
url_view_base::
134  
scheme_id() const noexcept
134  
scheme_id() const noexcept
135  
{
135  
{
136  
    return pi_->scheme_;
136  
    return pi_->scheme_;
137  
}
137  
}
138  

138  

139  
//------------------------------------------------
139  
//------------------------------------------------
140  
//
140  
//
141  
// Authority
141  
// Authority
142  
//
142  
//
143  
//------------------------------------------------
143  
//------------------------------------------------
144  

144  

145  
authority_view
145  
authority_view
146  
url_view_base::
146  
url_view_base::
147  
authority() const noexcept
147  
authority() const noexcept
148  
{
148  
{
149  
    detail::url_impl u(from::authority);
149  
    detail::url_impl u(from::authority);
150  
    u.cs_ = encoded_authority().data();
150  
    u.cs_ = encoded_authority().data();
151  
    if(has_authority())
151  
    if(has_authority())
152  
    {
152  
    {
153  
        u.set_size(id_user, pi_->len(id_user) - 2);
153  
        u.set_size(id_user, pi_->len(id_user) - 2);
154  
        u.set_size(id_pass, pi_->len(id_pass));
154  
        u.set_size(id_pass, pi_->len(id_pass));
155  
        u.set_size(id_host, pi_->len(id_host));
155  
        u.set_size(id_host, pi_->len(id_host));
156  
        u.set_size(id_port, pi_->len(id_port));
156  
        u.set_size(id_port, pi_->len(id_port));
157  
    }
157  
    }
158  
    else
158  
    else
159  
    {
159  
    {
160  
        u.set_size(id_user, pi_->len(id_user));
160  
        u.set_size(id_user, pi_->len(id_user));
161  
        BOOST_ASSERT(pi_->len(id_pass) == 0);
161  
        BOOST_ASSERT(pi_->len(id_pass) == 0);
162  
        BOOST_ASSERT(pi_->len(id_host) == 0);
162  
        BOOST_ASSERT(pi_->len(id_host) == 0);
163  
        BOOST_ASSERT(pi_->len(id_port) == 0);
163  
        BOOST_ASSERT(pi_->len(id_port) == 0);
164  
    }
164  
    }
165  
    u.decoded_[id_user] = pi_->decoded_[id_user];
165  
    u.decoded_[id_user] = pi_->decoded_[id_user];
166  
    u.decoded_[id_pass] = pi_->decoded_[id_pass];
166  
    u.decoded_[id_pass] = pi_->decoded_[id_pass];
167  
    u.decoded_[id_host] = pi_->decoded_[id_host];
167  
    u.decoded_[id_host] = pi_->decoded_[id_host];
168  
    for (int i = 0; i < 16; ++i)
168  
    for (int i = 0; i < 16; ++i)
169  
        u.ip_addr_[i] = pi_->ip_addr_[i];
169  
        u.ip_addr_[i] = pi_->ip_addr_[i];
170  
    u.port_number_ = pi_->port_number_;
170  
    u.port_number_ = pi_->port_number_;
171  
    u.host_type_ = pi_->host_type_;
171  
    u.host_type_ = pi_->host_type_;
172  
    return u.construct_authority();
172  
    return u.construct_authority();
173  
}
173  
}
174  

174  

175  
pct_string_view
175  
pct_string_view
176  
url_view_base::
176  
url_view_base::
177  
encoded_authority() const noexcept
177  
encoded_authority() const noexcept
178  
{
178  
{
179  
    auto s = pi_->get(id_user, id_path);
179  
    auto s = pi_->get(id_user, id_path);
180  
    if(! s.empty())
180  
    if(! s.empty())
181  
    {
181  
    {
182  
        BOOST_ASSERT(has_authority());
182  
        BOOST_ASSERT(has_authority());
183  
        s.remove_prefix(2);
183  
        s.remove_prefix(2);
184  
    }
184  
    }
185  
    return make_pct_string_view_unsafe(
185  
    return make_pct_string_view_unsafe(
186  
        s.data(),
186  
        s.data(),
187  
        s.size(),
187  
        s.size(),
188  
        pi_->decoded_[id_user] +
188  
        pi_->decoded_[id_user] +
189  
            pi_->decoded_[id_pass] +
189  
            pi_->decoded_[id_pass] +
190  
            pi_->decoded_[id_host] +
190  
            pi_->decoded_[id_host] +
191  
            pi_->decoded_[id_port] +
191  
            pi_->decoded_[id_port] +
192  
            has_password());
192  
            has_password());
193  
}
193  
}
194  

194  

195  
//------------------------------------------------
195  
//------------------------------------------------
196  
//
196  
//
197  
// Userinfo
197  
// Userinfo
198  
//
198  
//
199  
//------------------------------------------------
199  
//------------------------------------------------
200  

200  

201  
bool
201  
bool
202  
url_view_base::
202  
url_view_base::
203  
has_userinfo() const noexcept
203  
has_userinfo() const noexcept
204  
{
204  
{
205  
    auto n = pi_->len(id_pass);
205  
    auto n = pi_->len(id_pass);
206  
    if(n == 0)
206  
    if(n == 0)
207  
        return false;
207  
        return false;
208  
    BOOST_ASSERT(has_authority());
208  
    BOOST_ASSERT(has_authority());
209  
    BOOST_ASSERT(pi_->get(
209  
    BOOST_ASSERT(pi_->get(
210  
        id_pass).ends_with('@'));
210  
        id_pass).ends_with('@'));
211  
    return true;
211  
    return true;
212  
}
212  
}
213  

213  

214  
bool
214  
bool
215  
url_view_base::
215  
url_view_base::
216  
has_password() const noexcept
216  
has_password() const noexcept
217  
{
217  
{
218  
    auto const n = pi_->len(id_pass);
218  
    auto const n = pi_->len(id_pass);
219  
    if(n > 1)
219  
    if(n > 1)
220  
    {
220  
    {
221  
        BOOST_ASSERT(pi_->get(id_pass
221  
        BOOST_ASSERT(pi_->get(id_pass
222  
            ).starts_with(':'));
222  
            ).starts_with(':'));
223  
        BOOST_ASSERT(pi_->get(id_pass
223  
        BOOST_ASSERT(pi_->get(id_pass
224  
            ).ends_with('@'));
224  
            ).ends_with('@'));
225  
        return true;
225  
        return true;
226  
    }
226  
    }
227  
    BOOST_ASSERT(n == 0 || pi_->get(
227  
    BOOST_ASSERT(n == 0 || pi_->get(
228  
        id_pass).ends_with('@'));
228  
        id_pass).ends_with('@'));
229  
    return false;
229  
    return false;
230  
}
230  
}
231  

231  

232  
pct_string_view
232  
pct_string_view
233  
url_view_base::
233  
url_view_base::
234  
encoded_userinfo() const noexcept
234  
encoded_userinfo() const noexcept
235  
{
235  
{
236  
    auto s = pi_->get(
236  
    auto s = pi_->get(
237  
        id_user, id_host);
237  
        id_user, id_host);
238  
    if(s.empty())
238  
    if(s.empty())
239  
        return s;
239  
        return s;
240  
    BOOST_ASSERT(
240  
    BOOST_ASSERT(
241  
        has_authority());
241  
        has_authority());
242  
    s.remove_prefix(2);
242  
    s.remove_prefix(2);
243  
    if(s.empty())
243  
    if(s.empty())
244  
        return s;
244  
        return s;
245  
    BOOST_ASSERT(
245  
    BOOST_ASSERT(
246  
        s.ends_with('@'));
246  
        s.ends_with('@'));
247  
    s.remove_suffix(1);
247  
    s.remove_suffix(1);
248  
    return make_pct_string_view_unsafe(
248  
    return make_pct_string_view_unsafe(
249  
        s.data(),
249  
        s.data(),
250  
        s.size(),
250  
        s.size(),
251  
        pi_->decoded_[id_user] +
251  
        pi_->decoded_[id_user] +
252  
            pi_->decoded_[id_pass] +
252  
            pi_->decoded_[id_pass] +
253  
            has_password());
253  
            has_password());
254  
}
254  
}
255  

255  

256  
pct_string_view
256  
pct_string_view
257  
url_view_base::
257  
url_view_base::
258  
encoded_user() const noexcept
258  
encoded_user() const noexcept
259  
{
259  
{
260  
    auto s = pi_->get(id_user);
260  
    auto s = pi_->get(id_user);
261  
    if(! s.empty())
261  
    if(! s.empty())
262  
    {
262  
    {
263  
        BOOST_ASSERT(
263  
        BOOST_ASSERT(
264  
            has_authority());
264  
            has_authority());
265  
        s.remove_prefix(2);
265  
        s.remove_prefix(2);
266  
    }
266  
    }
267  
    return make_pct_string_view_unsafe(
267  
    return make_pct_string_view_unsafe(
268  
        s.data(),
268  
        s.data(),
269  
        s.size(),
269  
        s.size(),
270  
        pi_->decoded_[id_user]);
270  
        pi_->decoded_[id_user]);
271  
}
271  
}
272  

272  

273  
pct_string_view
273  
pct_string_view
274  
url_view_base::
274  
url_view_base::
275  
encoded_password() const noexcept
275  
encoded_password() const noexcept
276  
{
276  
{
277  
    auto s = pi_->get(id_pass);
277  
    auto s = pi_->get(id_pass);
278  
    switch(s.size())
278  
    switch(s.size())
279  
    {
279  
    {
280  
    case 1:
280  
    case 1:
281  
        BOOST_ASSERT(
281  
        BOOST_ASSERT(
282  
            s.starts_with('@'));
282  
            s.starts_with('@'));
283  
        s.remove_prefix(1);
283  
        s.remove_prefix(1);
284  
        BOOST_FALLTHROUGH;
284  
        BOOST_FALLTHROUGH;
285  
    case 0:
285  
    case 0:
286  
        return make_pct_string_view_unsafe(
286  
        return make_pct_string_view_unsafe(
287  
            s.data(), s.size(), 0);
287  
            s.data(), s.size(), 0);
288  
    default:
288  
    default:
289  
        break;
289  
        break;
290  
    }
290  
    }
291  
    BOOST_ASSERT(s.ends_with('@'));
291  
    BOOST_ASSERT(s.ends_with('@'));
292  
    BOOST_ASSERT(s.starts_with(':'));
292  
    BOOST_ASSERT(s.starts_with(':'));
293  
    return make_pct_string_view_unsafe(
293  
    return make_pct_string_view_unsafe(
294  
        s.data() + 1,
294  
        s.data() + 1,
295  
        s.size() - 2,
295  
        s.size() - 2,
296  
        pi_->decoded_[id_pass]);
296  
        pi_->decoded_[id_pass]);
297  
}
297  
}
298  

298  

299  
//------------------------------------------------
299  
//------------------------------------------------
300  
//
300  
//
301  
// Host
301  
// Host
302  
//
302  
//
303  
//------------------------------------------------
303  
//------------------------------------------------
304  
/*
304  
/*
305  
host_type       host_type()                 // ipv4, ipv6, ipvfuture, name
305  
host_type       host_type()                 // ipv4, ipv6, ipvfuture, name
306  

306  

307  
std::string     host()                      // return encoded_host().decode()
307  
std::string     host()                      // return encoded_host().decode()
308  
pct_string_view encoded_host()              // return host part, as-is
308  
pct_string_view encoded_host()              // return host part, as-is
309  
std::string     host_address()              // return encoded_host_address().decode()
309  
std::string     host_address()              // return encoded_host_address().decode()
310  
pct_string_view encoded_host_address()      // ipv4, ipv6, ipvfut, or encoded name, no brackets
310  
pct_string_view encoded_host_address()      // ipv4, ipv6, ipvfut, or encoded name, no brackets
311  

311  

312  
ipv4_address    host_ipv4_address()         // return ipv4_address or {}
312  
ipv4_address    host_ipv4_address()         // return ipv4_address or {}
313  
ipv6_address    host_ipv6_address()         // return ipv6_address or {}
313  
ipv6_address    host_ipv6_address()         // return ipv6_address or {}
314  
core::string_view     host_ipvfuture()            // return ipvfuture or {}
314  
core::string_view     host_ipvfuture()            // return ipvfuture or {}
315  
std::string     host_name()                 // return decoded name or ""
315  
std::string     host_name()                 // return decoded name or ""
316  
pct_string_view encoded_host_name()         // return encoded host name or ""
316  
pct_string_view encoded_host_name()         // return encoded host name or ""
317  
*/
317  
*/
318  

318  

319  
pct_string_view
319  
pct_string_view
320  
url_view_base::
320  
url_view_base::
321  
encoded_host() const noexcept
321  
encoded_host() const noexcept
322  
{
322  
{
323  
    return pi_->pct_get(id_host);
323  
    return pi_->pct_get(id_host);
324  
}
324  
}
325  

325  

326  
pct_string_view
326  
pct_string_view
327  
url_view_base::
327  
url_view_base::
328  
encoded_host_address() const noexcept
328  
encoded_host_address() const noexcept
329  
{
329  
{
330  
    core::string_view s = pi_->get(id_host);
330  
    core::string_view s = pi_->get(id_host);
331  
    std::size_t n;
331  
    std::size_t n;
332  
    switch(pi_->host_type_)
332  
    switch(pi_->host_type_)
333  
    {
333  
    {
334  
    default:
334  
    default:
335  
    case urls::host_type::none:
335  
    case urls::host_type::none:
336  
        BOOST_ASSERT(s.empty());
336  
        BOOST_ASSERT(s.empty());
337  
        n = 0;
337  
        n = 0;
338  
        break;
338  
        break;
339  

339  

340  
    case urls::host_type::name:
340  
    case urls::host_type::name:
341  
    case urls::host_type::ipv4:
341  
    case urls::host_type::ipv4:
342  
        n = pi_->decoded_[id_host];
342  
        n = pi_->decoded_[id_host];
343  
        break;
343  
        break;
344  

344  

345  
    case urls::host_type::ipv6:
345  
    case urls::host_type::ipv6:
346  
    case urls::host_type::ipvfuture:
346  
    case urls::host_type::ipvfuture:
347  
    {
347  
    {
348  
        BOOST_ASSERT(
348  
        BOOST_ASSERT(
349  
            pi_->decoded_[id_host] ==
349  
            pi_->decoded_[id_host] ==
350  
                s.size() ||
350  
                s.size() ||
351  
            !this->encoded_zone_id().empty());
351  
            !this->encoded_zone_id().empty());
352  
        BOOST_ASSERT(s.size() >= 2);
352  
        BOOST_ASSERT(s.size() >= 2);
353  
        BOOST_ASSERT(s.front() == '[');
353  
        BOOST_ASSERT(s.front() == '[');
354  
        BOOST_ASSERT(s.back() == ']');
354  
        BOOST_ASSERT(s.back() == ']');
355  
        s = s.substr(1, s.size() - 2);
355  
        s = s.substr(1, s.size() - 2);
356  
        n = pi_->decoded_[id_host] - 2;
356  
        n = pi_->decoded_[id_host] - 2;
357  
        break;
357  
        break;
358  
    }
358  
    }
359  
    }
359  
    }
360  
    return make_pct_string_view_unsafe(
360  
    return make_pct_string_view_unsafe(
361  
        s.data(),
361  
        s.data(),
362  
        s.size(),
362  
        s.size(),
363  
        n);
363  
        n);
364  
}
364  
}
365  

365  

366  
urls::ipv4_address
366  
urls::ipv4_address
367  
url_view_base::
367  
url_view_base::
368  
host_ipv4_address() const noexcept
368  
host_ipv4_address() const noexcept
369  
{
369  
{
370  
    if(pi_->host_type_ !=
370  
    if(pi_->host_type_ !=
371  
            urls::host_type::ipv4)
371  
            urls::host_type::ipv4)
372  
        return {};
372  
        return {};
373  
    ipv4_address::bytes_type b{{}};
373  
    ipv4_address::bytes_type b{{}};
374  
    std::memcpy(
374  
    std::memcpy(
375  
        &b[0], &pi_->ip_addr_[0], b.size());
375  
        &b[0], &pi_->ip_addr_[0], b.size());
376  
    return urls::ipv4_address(b);
376  
    return urls::ipv4_address(b);
377  
}
377  
}
378  

378  

379  
urls::ipv6_address
379  
urls::ipv6_address
380  
url_view_base::
380  
url_view_base::
381  
host_ipv6_address() const noexcept
381  
host_ipv6_address() const noexcept
382  
{
382  
{
383  
    if(pi_->host_type_ !=
383  
    if(pi_->host_type_ !=
384  
            urls::host_type::ipv6)
384  
            urls::host_type::ipv6)
385  
        return {};
385  
        return {};
386  
    ipv6_address::bytes_type b{{}};
386  
    ipv6_address::bytes_type b{{}};
387  
    std::memcpy(
387  
    std::memcpy(
388  
        &b[0], &pi_->ip_addr_[0], b.size());
388  
        &b[0], &pi_->ip_addr_[0], b.size());
389  
    return {b};
389  
    return {b};
390  
}
390  
}
391  

391  

392  
core::string_view
392  
core::string_view
393  
url_view_base::
393  
url_view_base::
394  
host_ipvfuture() const noexcept
394  
host_ipvfuture() const noexcept
395  
{
395  
{
396  
    if(pi_->host_type_ !=
396  
    if(pi_->host_type_ !=
397  
            urls::host_type::ipvfuture)
397  
            urls::host_type::ipvfuture)
398  
        return {};
398  
        return {};
399  
    core::string_view s = pi_->get(id_host);
399  
    core::string_view s = pi_->get(id_host);
400  
    BOOST_ASSERT(s.size() >= 6);
400  
    BOOST_ASSERT(s.size() >= 6);
401  
    BOOST_ASSERT(s.front() == '[');
401  
    BOOST_ASSERT(s.front() == '[');
402  
    BOOST_ASSERT(s.back() == ']');
402  
    BOOST_ASSERT(s.back() == ']');
403  
    s = s.substr(1, s.size() - 2);
403  
    s = s.substr(1, s.size() - 2);
404  
    return s;
404  
    return s;
405  
}
405  
}
406  

406  

407  
pct_string_view
407  
pct_string_view
408  
url_view_base::
408  
url_view_base::
409  
encoded_host_name() const noexcept
409  
encoded_host_name() const noexcept
410  
{
410  
{
411  
    if(pi_->host_type_ !=
411  
    if(pi_->host_type_ !=
412  
            urls::host_type::name)
412  
            urls::host_type::name)
413  
        return {};
413  
        return {};
414  
    core::string_view s = pi_->get(id_host);
414  
    core::string_view s = pi_->get(id_host);
415  
    return make_pct_string_view_unsafe(
415  
    return make_pct_string_view_unsafe(
416  
        s.data(),
416  
        s.data(),
417  
        s.size(),
417  
        s.size(),
418  
        pi_->decoded_[id_host]);
418  
        pi_->decoded_[id_host]);
419  
}
419  
}
420  

420  

421  
pct_string_view
421  
pct_string_view
422  
url_view_base::
422  
url_view_base::
423  
encoded_zone_id() const noexcept
423  
encoded_zone_id() const noexcept
424  
{
424  
{
425  
    if(pi_->host_type_ !=
425  
    if(pi_->host_type_ !=
426  
        urls::host_type::ipv6)
426  
        urls::host_type::ipv6)
427  
        return {};
427  
        return {};
428  
    core::string_view s = pi_->get(id_host);
428  
    core::string_view s = pi_->get(id_host);
429  
    BOOST_ASSERT(s.front() == '[');
429  
    BOOST_ASSERT(s.front() == '[');
430  
    BOOST_ASSERT(s.back() == ']');
430  
    BOOST_ASSERT(s.back() == ']');
431  
    s = s.substr(1, s.size() - 2);
431  
    s = s.substr(1, s.size() - 2);
432  
    auto pos = s.find("%25");
432  
    auto pos = s.find("%25");
433  
    if (pos == core::string_view::npos)
433  
    if (pos == core::string_view::npos)
434  
        return {};
434  
        return {};
435  
    s.remove_prefix(pos + 3);
435  
    s.remove_prefix(pos + 3);
436  
    return *make_pct_string_view(s);
436  
    return *make_pct_string_view(s);
437  
}
437  
}
438  

438  

439  
//------------------------------------------------
439  
//------------------------------------------------
440  

440  

441  
bool
441  
bool
442  
url_view_base::
442  
url_view_base::
443  
has_port() const noexcept
443  
has_port() const noexcept
444  
{
444  
{
445  
    auto const n = pi_->len(id_port);
445  
    auto const n = pi_->len(id_port);
446  
    if(n == 0)
446  
    if(n == 0)
447  
        return false;
447  
        return false;
448  
    BOOST_ASSERT(
448  
    BOOST_ASSERT(
449  
        pi_->get(id_port).starts_with(':'));
449  
        pi_->get(id_port).starts_with(':'));
450  
    return true;
450  
    return true;
451  
}
451  
}
452  

452  

453  
core::string_view
453  
core::string_view
454  
url_view_base::
454  
url_view_base::
455  
port() const noexcept
455  
port() const noexcept
456  
{
456  
{
457  
    auto s = pi_->get(id_port);
457  
    auto s = pi_->get(id_port);
458  
    if(s.empty())
458  
    if(s.empty())
459  
        return s;
459  
        return s;
460  
    BOOST_ASSERT(has_port());
460  
    BOOST_ASSERT(has_port());
461  
    return s.substr(1);
461  
    return s.substr(1);
462  
}
462  
}
463  

463  

464  
std::uint16_t
464  
std::uint16_t
465  
url_view_base::
465  
url_view_base::
466  
port_number() const noexcept
466  
port_number() const noexcept
467  
{
467  
{
468  
    BOOST_ASSERT(
468  
    BOOST_ASSERT(
469  
        has_port() ||
469  
        has_port() ||
470  
        pi_->port_number_ == 0);
470  
        pi_->port_number_ == 0);
471  
    return pi_->port_number_;
471  
    return pi_->port_number_;
472  
}
472  
}
473  

473  

474  
//------------------------------------------------
474  
//------------------------------------------------
475  
//
475  
//
476  
// Path
476  
// Path
477  
//
477  
//
478  
//------------------------------------------------
478  
//------------------------------------------------
479  

479  

480  
pct_string_view
480  
pct_string_view
481  
url_view_base::
481  
url_view_base::
482  
encoded_path() const noexcept
482  
encoded_path() const noexcept
483  
{
483  
{
484  
    return pi_->pct_get(id_path);
484  
    return pi_->pct_get(id_path);
485  
}
485  
}
486  

486  

487  
segments_view
487  
segments_view
488  
url_view_base::
488  
url_view_base::
489  
segments() const noexcept
489  
segments() const noexcept
490  
{
490  
{
491  
    return {detail::path_ref(*pi_)};
491  
    return {detail::path_ref(*pi_)};
492  
}
492  
}
493  

493  

494  
segments_encoded_view
494  
segments_encoded_view
495  
url_view_base::
495  
url_view_base::
496  
encoded_segments() const noexcept
496  
encoded_segments() const noexcept
497  
{
497  
{
498  
    return segments_encoded_view(
498  
    return segments_encoded_view(
499  
        detail::path_ref(*pi_));
499  
        detail::path_ref(*pi_));
500  
}
500  
}
501  

501  

502  
//------------------------------------------------
502  
//------------------------------------------------
503  
//
503  
//
504  
// Query
504  
// Query
505  
//
505  
//
506  
//------------------------------------------------
506  
//------------------------------------------------
507  

507  

508  
bool
508  
bool
509  
url_view_base::
509  
url_view_base::
510  
has_query() const noexcept
510  
has_query() const noexcept
511  
{
511  
{
512  
    auto const n = pi_->len(
512  
    auto const n = pi_->len(
513  
        id_query);
513  
        id_query);
514  
    if(n == 0)
514  
    if(n == 0)
515  
        return false;
515  
        return false;
516  
    BOOST_ASSERT(
516  
    BOOST_ASSERT(
517  
        pi_->get(id_query).
517  
        pi_->get(id_query).
518  
            starts_with('?'));
518  
            starts_with('?'));
519  
    return true;
519  
    return true;
520  
}
520  
}
521  

521  

522  
pct_string_view
522  
pct_string_view
523  
url_view_base::
523  
url_view_base::
524  
encoded_query() const noexcept
524  
encoded_query() const noexcept
525  
{
525  
{
526  
    auto s = pi_->get(id_query);
526  
    auto s = pi_->get(id_query);
527  
    if(s.empty())
527  
    if(s.empty())
528  
        return s;
528  
        return s;
529  
    BOOST_ASSERT(
529  
    BOOST_ASSERT(
530  
        s.starts_with('?'));
530  
        s.starts_with('?'));
531  
    return s.substr(1);
531  
    return s.substr(1);
532  
}
532  
}
533  

533  

534  
params_encoded_view
534  
params_encoded_view
535  
url_view_base::
535  
url_view_base::
536  
encoded_params() const noexcept
536  
encoded_params() const noexcept
537  
{
537  
{
538  
    return params_encoded_view(*pi_);
538  
    return params_encoded_view(*pi_);
539  
}
539  
}
540  

540  

541  
params_view
541  
params_view
542  
url_view_base::
542  
url_view_base::
543  
params() const noexcept
543  
params() const noexcept
544  
{
544  
{
545  
    return params_view(
545  
    return params_view(
546  
        *pi_,
546  
        *pi_,
547  
        encoding_opts{
547  
        encoding_opts{
548  
            true,false,false});
548  
            true,false,false});
549  
}
549  
}
550  

550  

551  
params_view
551  
params_view
552  
url_view_base::
552  
url_view_base::
553  
params(encoding_opts opt) const noexcept
553  
params(encoding_opts opt) const noexcept
554  
{
554  
{
555  
    return params_view(*pi_, opt);
555  
    return params_view(*pi_, opt);
556  
}
556  
}
557  

557  

558  
//------------------------------------------------
558  
//------------------------------------------------
559  
//
559  
//
560  
// Fragment
560  
// Fragment
561  
//
561  
//
562  
//------------------------------------------------
562  
//------------------------------------------------
563  

563  

564  
bool
564  
bool
565  
url_view_base::
565  
url_view_base::
566  
has_fragment() const noexcept
566  
has_fragment() const noexcept
567  
{
567  
{
568  
    auto const n = pi_->len(id_frag);
568  
    auto const n = pi_->len(id_frag);
569  
    if(n == 0)
569  
    if(n == 0)
570  
        return false;
570  
        return false;
571  
    BOOST_ASSERT(
571  
    BOOST_ASSERT(
572  
        pi_->get(id_frag).
572  
        pi_->get(id_frag).
573  
            starts_with('#'));
573  
            starts_with('#'));
574  
    return true;
574  
    return true;
575  
}
575  
}
576  

576  

577  
pct_string_view
577  
pct_string_view
578  
url_view_base::
578  
url_view_base::
579  
encoded_fragment() const noexcept
579  
encoded_fragment() const noexcept
580  
{
580  
{
581  
    auto s = pi_->get(id_frag);
581  
    auto s = pi_->get(id_frag);
582  
    if(! s.empty())
582  
    if(! s.empty())
583  
    {
583  
    {
584  
        BOOST_ASSERT(
584  
        BOOST_ASSERT(
585  
            s.starts_with('#'));
585  
            s.starts_with('#'));
586  
        s.remove_prefix(1);
586  
        s.remove_prefix(1);
587  
    }
587  
    }
588  
    return make_pct_string_view_unsafe(
588  
    return make_pct_string_view_unsafe(
589  
        s.data(),
589  
        s.data(),
590  
        s.size(),
590  
        s.size(),
591  
        pi_->decoded_[id_frag]);
591  
        pi_->decoded_[id_frag]);
592  
}
592  
}
593  

593  

594  
//------------------------------------------------
594  
//------------------------------------------------
595  
//
595  
//
596  
// Compound Fields
596  
// Compound Fields
597  
//
597  
//
598  
//------------------------------------------------
598  
//------------------------------------------------
599  

599  

600  
pct_string_view
600  
pct_string_view
601  
url_view_base::
601  
url_view_base::
602  
encoded_host_and_port() const noexcept
602  
encoded_host_and_port() const noexcept
603  
{
603  
{
604  
    return pi_->pct_get(id_host, id_path);
604  
    return pi_->pct_get(id_host, id_path);
605  
}
605  
}
606  

606  

607  
pct_string_view
607  
pct_string_view
608  
url_view_base::
608  
url_view_base::
609  
encoded_origin() const noexcept
609  
encoded_origin() const noexcept
610  
{
610  
{
611  
    if(pi_->len(id_user) < 2)
611  
    if(pi_->len(id_user) < 2)
612  
        return {};
612  
        return {};
613  
    return pi_->get(id_scheme, id_path);
613  
    return pi_->get(id_scheme, id_path);
614  
}
614  
}
615  

615  

616  
pct_string_view
616  
pct_string_view
617  
url_view_base::
617  
url_view_base::
618  
encoded_resource() const noexcept
618  
encoded_resource() const noexcept
619  
{
619  
{
620  
    auto n =
620  
    auto n =
621  
        pi_->decoded_[id_path] +
621  
        pi_->decoded_[id_path] +
622  
        pi_->decoded_[id_query] +
622  
        pi_->decoded_[id_query] +
623  
        pi_->decoded_[id_frag];
623  
        pi_->decoded_[id_frag];
624  
    if(has_query())
624  
    if(has_query())
625  
        ++n;
625  
        ++n;
626  
    if(has_fragment())
626  
    if(has_fragment())
627  
        ++n;
627  
        ++n;
628  
    BOOST_ASSERT(pct_string_view(
628  
    BOOST_ASSERT(pct_string_view(
629  
        pi_->get(id_path, id_end)
629  
        pi_->get(id_path, id_end)
630  
            ).decoded_size() == n);
630  
            ).decoded_size() == n);
631  
    auto s = pi_->get(id_path, id_end);
631  
    auto s = pi_->get(id_path, id_end);
632  
    return make_pct_string_view_unsafe(
632  
    return make_pct_string_view_unsafe(
633  
        s.data(), s.size(), n);
633  
        s.data(), s.size(), n);
634  
}
634  
}
635  

635  

636  
pct_string_view
636  
pct_string_view
637  
url_view_base::
637  
url_view_base::
638  
encoded_target() const noexcept
638  
encoded_target() const noexcept
639  
{
639  
{
640  
    auto n =
640  
    auto n =
641  
        pi_->decoded_[id_path] +
641  
        pi_->decoded_[id_path] +
642  
        pi_->decoded_[id_query];
642  
        pi_->decoded_[id_query];
643  
    if(has_query())
643  
    if(has_query())
644  
        ++n;
644  
        ++n;
645  
    BOOST_ASSERT(pct_string_view(
645  
    BOOST_ASSERT(pct_string_view(
646  
        pi_->get(id_path, id_frag)
646  
        pi_->get(id_path, id_frag)
647  
            ).decoded_size() == n);
647  
            ).decoded_size() == n);
648  
    auto s = pi_->get(id_path, id_frag);
648  
    auto s = pi_->get(id_path, id_frag);
649  
    return make_pct_string_view_unsafe(
649  
    return make_pct_string_view_unsafe(
650  
        s.data(), s.size(), n);
650  
        s.data(), s.size(), n);
651  
}
651  
}
652  

652  

653  
//------------------------------------------------
653  
//------------------------------------------------
654  
//
654  
//
655  
// Comparisons
655  
// Comparisons
656  
//
656  
//
657  
//------------------------------------------------
657  
//------------------------------------------------
658  

658  

659  
int
659  
int
660  
url_view_base::
660  
url_view_base::
661  
compare(const url_view_base& other) const noexcept
661  
compare(const url_view_base& other) const noexcept
662  
{
662  
{
663  
    int comp =
663  
    int comp =
664  
        static_cast<int>(has_scheme()) -
664  
        static_cast<int>(has_scheme()) -
665  
        static_cast<int>(other.has_scheme());
665  
        static_cast<int>(other.has_scheme());
666  
    if ( comp != 0 )
666  
    if ( comp != 0 )
667  
        return comp;
667  
        return comp;
668  

668  

669  
    if (has_scheme())
669  
    if (has_scheme())
670  
    {
670  
    {
671  
        comp = detail::ci_compare(
671  
        comp = detail::ci_compare(
672  
            scheme(),
672  
            scheme(),
673  
            other.scheme());
673  
            other.scheme());
674  
        if ( comp != 0 )
674  
        if ( comp != 0 )
675  
            return comp;
675  
            return comp;
676  
    }
676  
    }
677  

677  

678  
    comp =
678  
    comp =
679  
        static_cast<int>(has_authority()) -
679  
        static_cast<int>(has_authority()) -
680  
        static_cast<int>(other.has_authority());
680  
        static_cast<int>(other.has_authority());
681  
    if ( comp != 0 )
681  
    if ( comp != 0 )
682  
        return comp;
682  
        return comp;
683  

683  

684  
    if (has_authority())
684  
    if (has_authority())
685  
    {
685  
    {
686  
        comp = authority().compare(other.authority());
686  
        comp = authority().compare(other.authority());
687  
        if ( comp != 0 )
687  
        if ( comp != 0 )
688  
            return comp;
688  
            return comp;
689  
    }
689  
    }
690  

690  

691  
    comp = detail::segments_compare(
691  
    comp = detail::segments_compare(
692  
        encoded_segments(),
692  
        encoded_segments(),
693  
        other.encoded_segments());
693  
        other.encoded_segments());
694  
    if ( comp != 0 )
694  
    if ( comp != 0 )
695  
        return comp;
695  
        return comp;
696  

696  

697  
    comp =
697  
    comp =
698  
        static_cast<int>(has_query()) -
698  
        static_cast<int>(has_query()) -
699  
        static_cast<int>(other.has_query());
699  
        static_cast<int>(other.has_query());
700  
    if ( comp != 0 )
700  
    if ( comp != 0 )
701  
        return comp;
701  
        return comp;
702  

702  

703  
    if (has_query())
703  
    if (has_query())
704  
    {
704  
    {
705  
        comp = detail::compare_encoded_query(
705  
        comp = detail::compare_encoded_query(
706  
            encoded_query(),
706  
            encoded_query(),
707  
            other.encoded_query());
707  
            other.encoded_query());
708  
        if ( comp != 0 )
708  
        if ( comp != 0 )
709  
            return comp;
709  
            return comp;
710  
    }
710  
    }
711  

711  

712  
    comp =
712  
    comp =
713  
        static_cast<int>(has_fragment()) -
713  
        static_cast<int>(has_fragment()) -
714  
        static_cast<int>(other.has_fragment());
714  
        static_cast<int>(other.has_fragment());
715  
    if ( comp != 0 )
715  
    if ( comp != 0 )
716  
        return comp;
716  
        return comp;
717  

717  

718  
    if (has_fragment())
718  
    if (has_fragment())
719  
    {
719  
    {
720  
        comp = detail::compare_encoded(
720  
        comp = detail::compare_encoded(
721  
            encoded_fragment(),
721  
            encoded_fragment(),
722  
            other.encoded_fragment());
722  
            other.encoded_fragment());
723  
        if ( comp != 0 )
723  
        if ( comp != 0 )
724  
            return comp;
724  
            return comp;
725  
    }
725  
    }
726  

726  

727  
    return 0;
727  
    return 0;
728  
}
728  
}
729  

729  

730  
} // urls
730  
} // urls
731  
} // boost
731  
} // boost
732  

732