1 -
//
 
2 -
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
 
3 -
// Copyright (c) 2022 Alan de Freitas (alandefreitas@gmail.com)
 
4 -
//
 
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)
 
7 -
//
 
8 -
// Official repository: https://github.com/boostorg/url
 
9 -
//
 
10 -

 
11 -

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

 
399 -
url_base&
 
400 -
url_base::
 
401 -
set_encoded_userinfo(
 
402 -
    pct_string_view s)
 
403 -
{
 
404 -
    op_t op(*this, &detail::ref(s));
 
405 -
    auto const pos = s.find_first_of(':');
 
406 -
    if(pos != core::string_view::npos)
 
407 -
    {
 
408 -
        // user:pass
 
409 -
        auto const s0 = s.substr(0, pos);
 
410 -
        auto const s1 = s.substr(pos + 1);
 
411 -
        auto const n0 =
 
412 -
            detail::re_encoded_size_unsafe(
 
413 -
                s0,
 
414 -
                detail::user_chars);
 
415 -
        auto const n1 =
 
416 -
            detail::re_encoded_size_unsafe(s1,
 
417 -
                detail::password_chars);
 
418 -
        auto dest =
 
419 -
            set_userinfo_impl(n0 + n1 + 1, op);
 
420 -
        impl_.decoded_[id_user] =
 
421 -
            detail::to_size_type(detail::re_encode_unsafe(
 
422 -
                dest,
 
423 -
                dest + n0,
 
424 -
                s0,
 
425 -
                detail::user_chars));
 
426 -
        *dest++ = ':';
 
427 -
        impl_.decoded_[id_pass] =
 
428 -
            detail::to_size_type(detail::re_encode_unsafe(
 
429 -
                dest,
 
430 -
                dest + n1,
 
431 -
                s1,
 
432 -
                detail::password_chars));
 
433 -
        impl_.split(id_user, 2 + n0);
 
434 -
    }
 
435 -
    else
 
436 -
    {
 
437 -
        // user
 
438 -
        auto const n =
 
439 -
            detail::re_encoded_size_unsafe(
 
440 -
                s, detail::user_chars);
 
441 -
        auto dest = set_userinfo_impl(n, op);
 
442 -
        impl_.decoded_[id_user] =
 
443 -
            detail::to_size_type(detail::re_encode_unsafe(
 
444 -
                dest,
 
445 -
                dest + n,
 
446 -
                s,
 
447 -
                detail::user_chars));
 
448 -
        impl_.split(id_user, 2 + n);
 
449 -
        impl_.decoded_[id_pass] = 0;
 
450 -
    }
 
451 -
    return *this;
 
452 -
}
 
453 -

 
454 -
url_base&
 
455 -
url_base::
 
456 -
remove_userinfo() noexcept
 
457 -
{
 
458 -
    if(impl_.len(id_pass) == 0)
 
459 -
        return *this; // no userinfo
 
460 -

 
461 -
    op_t op(*this);
 
462 -
    // keep authority '//'
 
463 -
    resize_impl(
 
464 -
        id_user, id_host, 2, op);
 
465 -
    impl_.decoded_[id_user] = 0;
 
466 -
    impl_.decoded_[id_pass] = 0;
 
467 -
    return *this;
 
468 -
}
 
469 -

 
470 -
//------------------------------------------------
 
471 -

 
472 -
url_base&
 
473 -
url_base::
 
474 -
set_user(core::string_view s)
 
475 -
{
 
476 -
    op_t op(*this, &s);
 
477 -
    encoding_opts opt;
 
478 -
    auto const n = encoded_size(
 
479 -
        s, detail::user_chars, opt);
 
480 -
    auto dest = set_user_impl(n, op);
 
481 -
    encode_unsafe(
 
482 -
        dest,
 
483 -
        n,
 
484 -
        s,
 
485 -
        detail::user_chars,
 
486 -
        opt);
 
487 -
    impl_.decoded_[id_user] =
 
488 -
        detail::to_size_type(s.size());
 
489 -
    return *this;
 
490 -
}
 
491 -

 
492 -
url_base&
 
493 -
url_base::
 
494 -
set_encoded_user(
 
495 -
    pct_string_view s)
 
496 -
{
 
497 -
    op_t op(*this, &detail::ref(s));
 
498 -
    auto const n =
 
499 -
        detail::re_encoded_size_unsafe(
 
500 -
            s, detail::user_chars);
 
501 -
    auto dest = set_user_impl(n, op);
 
502 -
    impl_.decoded_[id_user] =
 
503 -
        detail::to_size_type(detail::re_encode_unsafe(
 
504 -
            dest,
 
505 -
            dest + n,
 
506 -
            s,
 
507 -
            detail::user_chars));
 
508 -
    BOOST_ASSERT(
 
509 -
        impl_.decoded_[id_user] ==
 
510 -
            s.decoded_size());
 
511 -
    return *this;
 
512 -
}
 
513 -

 
514 -
//------------------------------------------------
 
515 -

 
516 -
url_base&
 
517 -
url_base::
 
518 -
set_password(core::string_view s)
 
519 -
{
 
520 -
    op_t op(*this, &s);
 
521 -
    encoding_opts opt;
 
522 -
    auto const n = encoded_size(
 
523 -
        s, detail::password_chars, opt);
 
524 -
    auto dest = set_password_impl(n, op);
 
525 -
    encode_unsafe(
 
526 -
        dest,
 
527 -
        n,
 
528 -
        s,
 
529 -
        detail::password_chars,
 
530 -
        opt);
 
531 -
    impl_.decoded_[id_pass] =
 
532 -
        detail::to_size_type(s.size());
 
533 -
    return *this;
 
534 -
}
 
535 -

 
536 -
url_base&
 
537 -
url_base::
 
538 -
set_encoded_password(
 
539 -
    pct_string_view s)
 
540 -
{
 
541 -
    op_t op(*this, &detail::ref(s));
 
542 -
    auto const n =
 
543 -
        detail::re_encoded_size_unsafe(
 
544 -
            s,
 
545 -
            detail::password_chars);
 
546 -
    auto dest = set_password_impl(n, op);
 
547 -
    impl_.decoded_[id_pass] =
 
548 -
        detail::to_size_type(detail::re_encode_unsafe(
 
549 -
            dest,
 
550 -
            dest + n,
 
551 -
            s,
 
552 -
            detail::password_chars));
 
553 -
    BOOST_ASSERT(
 
554 -
        impl_.decoded_[id_pass] ==
 
555 -
            s.decoded_size());
 
556 -
    return *this;
 
557 -
}
 
558 -

 
559 -
url_base&
 
560 -
url_base::
 
561 -
remove_password() noexcept
 
562 -
{
 
563 -
    auto const n = impl_.len(id_pass);
 
564 -
    if(n < 2)
 
565 -
        return *this; // no password
 
566 -

 
567 -
    op_t op(*this);
 
568 -
    // clear password, retain '@'
 
569 -
    auto dest =
 
570 -
        resize_impl(id_pass, 1, op);
 
571 -
    dest[0] = '@';
 
572 -
    impl_.decoded_[id_pass] = 0;
 
573 -
    return *this;
 
574 -
}
 
575 -

 
576 -
//------------------------------------------------
 
577 -
//
 
578 -
// Host
 
579 -
//
 
580 -
//------------------------------------------------
 
581 -
/*
 
582 -
host_type       host_type()                 // ipv4, ipv6, ipvfuture, name
 
583 -

 
584 -
std::string     host()                      // return encoded_host().decode()
 
585 -
pct_string_view encoded_host()              // return host part, as-is
 
586 -
std::string     host_address()              // return encoded_host_address().decode()
 
587 -
pct_string_view encoded_host_address()      // ipv4, ipv6, ipvfut, or encoded name, no brackets
 
588 -

 
589 -
ipv4_address    host_ipv4_address()         // return ipv4_address or {}
 
590 -
ipv6_address    host_ipv6_address()         // return ipv6_address or {}
 
591 -
core::string_view     host_ipvfuture()            // return ipvfuture or {}
 
592 -
std::string     host_name()                 // return decoded name or ""
 
593 -
pct_string_view encoded_host_name()         // return encoded host name or ""
 
594 -

 
595 -
--------------------------------------------------
 
596 -

 
597 -
set_host( core::string_view )                     // set host part from plain text
 
598 -
set_encoded_host( pct_string_view )         // set host part from encoded text
 
599 -
set_host_address( core::string_view )             // set host from ipv4, ipv6, ipvfut, or plain reg-name string
 
600 -
set_encoded_host_address( pct_string_view ) // set host from ipv4, ipv6, ipvfut, or encoded reg-name string
 
601 -

 
602 -
set_host_ipv4( ipv4_address )               // set ipv4
 
603 -
set_host_ipv6( ipv6_address )               // set ipv6
 
604 -
set_host_ipvfuture( core::string_view )           // set ipvfuture
 
605 -
set_host_name( core::string_view )                // set name from plain
 
606 -
set_encoded_host_name( pct_string_view )    // set name from encoded
 
607 -
*/
 
608 -

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

 
666 -
    // reg-name
 
667 -
    op_t op(*this, &s);
 
668 -
    encoding_opts opt;
 
669 -
    auto const n = encoded_size(
 
670 -
        s, detail::host_chars, opt);
 
671 -
    auto dest = set_host_impl(n, op);
 
672 -
    encode(
 
673 -
        dest,
 
674 -
        impl_.get(id_path).data() - dest,
 
675 -
        s,
 
676 -
        detail::host_chars,
 
677 -
        opt);
 
678 -
    impl_.decoded_[id_host] =
 
679 -
        detail::to_size_type(s.size());
 
680 -
    impl_.host_type_ =
 
681 -
        urls::host_type::name;
 
682 -
    return *this;
 
683 -
}
 
684 -

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

 
747 -
    // reg-name
 
748 -
    op_t op(*this, &detail::ref(s));
 
749 -
    auto const n = detail::re_encoded_size_unsafe(
 
750 -
        s, detail::host_chars);
 
751 -
    auto dest = set_host_impl(n, op);
 
752 -
    impl_.decoded_[id_host] =
 
753 -
        detail::to_size_type(detail::re_encode_unsafe(
 
754 -
            dest,
 
755 -
            impl_.get(id_path).data(),
 
756 -
            s,
 
757 -
            detail::host_chars));
 
758 -
    BOOST_ASSERT(impl_.decoded_[id_host] ==
 
759 -
        s.decoded_size());
 
760 -
    impl_.host_type_ =
 
761 -
        urls::host_type::name;
 
762 -
    return *this;
 
763 -
}
 
764 -

 
765 -
url_base&
 
766 -
url_base::
 
767 -
set_host_address(
 
768 -
    core::string_view s)
 
769 -
{
 
770 -
    if (!s.empty())
 
771 -
    {
 
772 -
        // IP-literal
 
773 -
        if (s[0] != 'v')
 
774 -
        {
 
775 -
            // IPv6-address
 
776 -
            auto innerit = s.begin();
 
777 -
            auto endit = s.end();
 
778 -
            auto rv = grammar::parse(
 
779 -
                innerit,
 
780 -
                endit,
 
781 -
                ipv6_address_rule);
 
782 -
            if(rv)
 
783 -
            {
 
784 -
                if (innerit == endit)
 
785 -
                {
 
786 -
                    set_host_ipv6_and_encoded_zone_id(*rv, {});
 
787 -
                    return *this;
 
788 -
                }
 
789 -
                // IPv6addrz: https://datatracker.ietf.org/doc/html/rfc6874
 
790 -
                auto chars_left = endit - innerit;
 
791 -
                if (chars_left >= 2 &&
 
792 -
                    *innerit++ == '%')
 
793 -
                {
 
794 -
                    core::string_view zone_id_str = {&*innerit, std::size_t(chars_left - 1)};
 
795 -
                    set_host_ipv6_and_zone_id(*rv, zone_id_str);
 
796 -
                    return *this;
 
797 -
                }
 
798 -
            }
 
799 -
        }
 
800 -

 
801 -
        // IPvFuture
 
802 -
        auto rv = grammar::parse(s, detail::ipvfuture_rule);
 
803 -
        if(rv)
 
804 -
            return set_host_ipvfuture(rv->str);
 
805 -

 
806 -
        if(s.size() >= 7) // "0.0.0.0"
 
807 -
        {
 
808 -
            // IPv4-address
 
809 -
            auto rv2 = parse_ipv4_address(s);
 
810 -
            if(rv2)
 
811 -
                return set_host_ipv4(*rv2);
 
812 -
        }
 
813 -
    }
 
814 -

 
815 -
    // reg-name
 
816 -
    op_t op(*this, &s);
 
817 -
    encoding_opts opt;
 
818 -
    auto const n = encoded_size(
 
819 -
        s, detail::host_chars, opt);
 
820 -
    auto dest = set_host_impl(n, op);
 
821 -
    encode(
 
822 -
        dest,
 
823 -
        impl_.get(id_path).data() - dest,
 
824 -
        s,
 
825 -
        detail::host_chars,
 
826 -
        opt);
 
827 -
    impl_.decoded_[id_host] =
 
828 -
        detail::to_size_type(s.size());
 
829 -
    impl_.host_type_ =
 
830 -
        urls::host_type::name;
 
831 -
    return *this;
 
832 -
}
 
833 -

 
834 -
url_base&
 
835 -
url_base::
 
836 -
set_encoded_host_address(
 
837 -
    pct_string_view s)
 
838 -
{
 
839 -
    if( !s.empty() )
 
840 -
    {
 
841 -
        // IP-literal
 
842 -
        if (s[0] != 'v')
 
843 -
        {
 
844 -
            // IPv6-address
 
845 -
            auto innerit = s.begin();
 
846 -
            auto endit = s.end();
 
847 -
            auto rv = grammar::parse(
 
848 -
                innerit,
 
849 -
                endit,
 
850 -
                ipv6_address_rule);
 
851 -
            if(rv)
 
852 -
            {
 
853 -
                if (innerit == endit)
 
854 -
                {
 
855 -
                    set_host_ipv6_and_encoded_zone_id(*rv, {});
 
856 -
                    return *this;
 
857 -
                }
 
858 -
                // IPv6addrz: https://datatracker.ietf.org/doc/html/rfc6874
 
859 -
                auto chars_left = endit - innerit;
 
860 -
                if (chars_left >= 3 &&
 
861 -
                    *innerit++ == '%' &&
 
862 -
                    *innerit++ == '2' &&
 
863 -
                    *innerit++ == '5')
 
864 -
                {
 
865 -
                    auto const nz = std::size_t(chars_left - 3);
 
866 -
                    core::string_view zone_id_str = {&*innerit, std::size_t(chars_left - 3)};
 
867 -
                    std::size_t dnz = detail::decode_bytes_unsafe(zone_id_str);
 
868 -
                    pct_string_view zone_id_pct = make_pct_string_view_unsafe(innerit, nz, dnz);
 
869 -
                    set_host_ipv6_and_encoded_zone_id(*rv, zone_id_pct);
 
870 -
                    return *this;
 
871 -
                }
 
872 -
            }
 
873 -

 
874 -
            if(s.size() >= 7) // "0.0.0.0"
 
875 -
            {
 
876 -
                // IPv4-address
 
877 -
                auto rv2 = parse_ipv4_address(s);
 
878 -
                if(rv2)
 
879 -
                   return set_host_ipv4(*rv2);
 
880 -
            }
 
881 -
        }
 
882 -
        else
 
883 -
        {
 
884 -
            // IPvFuture
 
885 -
            auto rv = grammar::parse(
 
886 -
                s, detail::ipvfuture_rule);
 
887 -
            if(rv)
 
888 -
                return set_host_ipvfuture(rv->str);
 
889 -
        }
 
890 -
    }
 
891 -

 
892 -
    // reg-name
 
893 -
    op_t op(*this, &detail::ref(s));
 
894 -
    auto const n = detail::re_encoded_size_unsafe(
 
895 -
        s, detail::host_chars);
 
896 -
    auto dest = set_host_impl(n, op);
 
897 -
    impl_.decoded_[id_host] =
 
898 -
        detail::to_size_type(detail::re_encode_unsafe(
 
899 -
            dest,
 
900 -
            impl_.get(id_path).data(),
 
901 -
            s,
 
902 -
            detail::host_chars));
 
903 -
    BOOST_ASSERT(impl_.decoded_[id_host] ==
 
904 -
        s.decoded_size());
 
905 -
    impl_.host_type_ =
 
906 -
        urls::host_type::name;
 
907 -
    return *this;
 
908 -
}
 
909 -

 
910 -
url_base&
 
911 -
url_base::
 
912 -
set_host_ipv4(
 
913 -
    ipv4_address const& addr)
 
914 -
{
 
915 -
    op_t op(*this);
 
916 -
    char buf[urls::ipv4_address::max_str_len];
 
917 -
    auto s = addr.to_buffer(buf, sizeof(buf));
 
918 -
    auto dest = set_host_impl(s.size(), op);
 
919 -
    std::memcpy(dest, s.data(), s.size());
 
920 -
    impl_.decoded_[id_host] =
 
921 -
        detail::to_size_type(impl_.len(id_host));
 
922 -
    impl_.host_type_ = urls::host_type::ipv4;
 
923 -
    auto bytes = addr.to_bytes();
 
924 -
    std::memcpy(
 
925 -
        impl_.ip_addr_,
 
926 -
        bytes.data(),
 
927 -
        bytes.size());
 
928 -
    return *this;
 
929 -
}
 
930 -

 
931 -
url_base&
 
932 -
url_base::
 
933 -
set_host_ipv6(
 
934 -
    ipv6_address const& addr)
 
935 -
{
 
936 -
    set_host_ipv6_and_encoded_zone_id(addr, encoded_zone_id());
 
937 -
    return *this;
 
938 -
}
 
939 -

 
940 -
url_base&
 
941 -
url_base::
 
942 -
set_zone_id(core::string_view s)
 
943 -
{
 
944 -
    set_host_ipv6_and_zone_id(host_ipv6_address(), s);
 
945 -
    return *this;
 
946 -
}
 
947 -

 
948 -
url_base&
 
949 -
url_base::
 
950 -
set_encoded_zone_id(pct_string_view s)
 
951 -
{
 
952 -
    set_host_ipv6_and_encoded_zone_id(host_ipv6_address(), s);
 
953 -
    return *this;
 
954 -
}
 
955 -

 
956 -
void
 
957 -
url_base::
 
958 -
set_host_ipv6_and_zone_id(
 
959 -
    ipv6_address const& addr,
 
960 -
    core::string_view zone_id)
 
961 -
{
 
962 -
    op_t op(*this, &zone_id);
 
963 -
    char ipv6_str_buf[urls::ipv6_address::max_str_len];
 
964 -
    auto ipv6_str = addr.to_buffer(ipv6_str_buf, sizeof(ipv6_str_buf));
 
965 -
    bool const has_zone_id = !zone_id.empty();
 
966 -
    encoding_opts opt;
 
967 -
    auto const ipn = ipv6_str.size();
 
968 -
    auto const zn = encoded_size(zone_id, unreserved_chars, opt);
 
969 -
    auto const n = ipn + 2 + has_zone_id * (3 + zn);
 
970 -
    auto dest = set_host_impl(n, op);
 
971 -
    *dest++ = '[';
 
972 -
    std::memcpy(dest, ipv6_str.data(), ipn);
 
973 -
    dest += ipn;
 
974 -
    if (has_zone_id)
 
975 -
    {
 
976 -
        *dest++ = '%';
 
977 -
        *dest++ = '2';
 
978 -
        *dest++ = '5';
 
979 -
        encode(dest, zn, zone_id, unreserved_chars, opt);
 
980 -
        dest += zn;
 
981 -
    }
 
982 -
    *dest++ = ']';
 
983 -
    // ipn + |"["| + |"]"| + (has_zone_id ? |"%"| + zn : 0)
 
984 -
    impl_.decoded_[id_host] = detail::to_size_type(
 
985 -
        ipn + 2 + has_zone_id * (1 + zone_id.size()));
 
986 -
    impl_.host_type_ = urls::host_type::ipv6;
 
987 -
    auto bytes = addr.to_bytes();
 
988 -
    std::memcpy(
 
989 -
        impl_.ip_addr_,
 
990 -
        bytes.data(),
 
991 -
        bytes.size());
 
992 -
}
 
993 -

 
994 -
void
 
995 -
url_base::
 
996 -
set_host_ipv6_and_encoded_zone_id(
 
997 -
    ipv6_address const& addr,
 
998 -
    pct_string_view zone_id)
 
999 -
{
 
1000 -
    op_t op(*this, &detail::ref(zone_id));
 
1001 -
    char ipv6_str_buf[urls::ipv6_address::max_str_len];
 
1002 -
    auto ipv6_str = addr.to_buffer(ipv6_str_buf, sizeof(ipv6_str_buf));
 
1003 -
    bool const has_zone_id = !zone_id.empty();
 
1004 -
    auto const ipn = ipv6_str.size();
 
1005 -
    auto const zn = detail::re_encoded_size_unsafe(zone_id, unreserved_chars);
 
1006 -
    auto const n = ipn + 2 + has_zone_id * (3 + zn);
 
1007 -
    auto dest = set_host_impl(n, op);
 
1008 -
    *dest++ = '[';
 
1009 -
    std::memcpy(dest, ipv6_str.data(), ipn);
 
1010 -
    dest += ipn;
 
1011 -
    std::size_t dzn = 0;
 
1012 -
    if (has_zone_id)
 
1013 -
    {
 
1014 -
        *dest++ = '%';
 
1015 -
        *dest++ = '2';
 
1016 -
        *dest++ = '5';
 
1017 -
        dzn = detail::re_encode_unsafe(dest, dest + zn, zone_id, unreserved_chars);
 
1018 -
    }
 
1019 -
    *dest++ = ']';
 
1020 -
    // ipn + |"["| + |"]"| + (has_zone_id ? |"%"| + zn : 0)
 
1021 -
    impl_.decoded_[id_host] = detail::to_size_type(
 
1022 -
        ipn + 2 + has_zone_id * (1 + dzn));
 
1023 -
    impl_.host_type_ = urls::host_type::ipv6;
 
1024 -
    auto bytes = addr.to_bytes();
 
1025 -
    std::memcpy(
 
1026 -
        impl_.ip_addr_,
 
1027 -
        bytes.data(),
 
1028 -
        bytes.size());
 
1029 -
}
 
1030 -

 
1031 -
url_base&
 
1032 -
url_base::
 
1033 -
set_host_ipvfuture(
 
1034 -
    core::string_view s)
 
1035 -
{
 
1036 -
    op_t op(*this, &s);
 
1037 -
    // validate
 
1038 -
    grammar::parse(s,
 
1039 -
        detail::ipvfuture_rule
 
1040 -
            ).value(BOOST_URL_POS);
 
1041 -
    auto dest = set_host_impl(
 
1042 -
        s.size() + 2, op);
 
1043 -
    *dest++ = '[';
 
1044 -
    dest += s.copy(dest, s.size());
 
1045 -
    *dest = ']';
 
1046 -
    impl_.host_type_ =
 
1047 -
        urls::host_type::ipvfuture;
 
1048 -
    impl_.decoded_[id_host] =
 
1049 -
        detail::to_size_type(s.size() + 2);
 
1050 -
    return *this;
 
1051 -
}
 
1052 -

 
1053 -
url_base&
 
1054 -
url_base::
 
1055 -
set_host_name(
 
1056 -
    core::string_view s)
 
1057 -
{
 
1058 -
    bool is_ipv4 = false;
 
1059 -
    if(s.size() >= 7) // "0.0.0.0"
 
1060 -
    {
 
1061 -
        // IPv4-address
 
1062 -
        if(parse_ipv4_address(s).has_value())
 
1063 -
            is_ipv4 = true;
 
1064 -
    }
 
1065 -
    auto allowed = detail::host_chars;
 
1066 -
    if(is_ipv4)
 
1067 -
        allowed = allowed - '.';
 
1068 -

 
1069 -
    op_t op(*this, &s);
 
1070 -
    encoding_opts opt;
 
1071 -
    auto const n = encoded_size(
 
1072 -
        s, allowed, opt);
 
1073 -
    auto dest = set_host_impl(n, op);
 
1074 -
    encode_unsafe(
 
1075 -
        dest,
 
1076 -
        n,
 
1077 -
        s,
 
1078 -
        allowed,
 
1079 -
        opt);
 
1080 -
    impl_.host_type_ =
 
1081 -
        urls::host_type::name;
 
1082 -
    impl_.decoded_[id_host] =
 
1083 -
        detail::to_size_type(s.size());
 
1084 -
    return *this;
 
1085 -
}
 
1086 -

 
1087 -
url_base&
 
1088 -
url_base::
 
1089 -
set_encoded_host_name(
 
1090 -
    pct_string_view s)
 
1091 -
{
 
1092 -
    bool is_ipv4 = false;
 
1093 -
    if(s.size() >= 7) // "0.0.0.0"
 
1094 -
    {
 
1095 -
        // IPv4-address
 
1096 -
        if(parse_ipv4_address(s).has_value())
 
1097 -
            is_ipv4 = true;
 
1098 -
    }
 
1099 -
    auto allowed = detail::host_chars;
 
1100 -
    if(is_ipv4)
 
1101 -
        allowed = allowed - '.';
 
1102 -

 
1103 -
    op_t op(*this, &detail::ref(s));
 
1104 -
    auto const n = detail::re_encoded_size_unsafe(
 
1105 -
        s, allowed);
 
1106 -
    auto dest = set_host_impl(n, op);
 
1107 -
    impl_.decoded_[id_host] =
 
1108 -
        detail::to_size_type(detail::re_encode_unsafe(
 
1109 -
            dest,
 
1110 -
            dest + n,
 
1111 -
            s,
 
1112 -
            allowed));
 
1113 -
    BOOST_ASSERT(
 
1114 -
        impl_.decoded_[id_host] ==
 
1115 -
            s.decoded_size());
 
1116 -
    impl_.host_type_ =
 
1117 -
        urls::host_type::name;
 
1118 -
    return *this;
 
1119 -
}
 
1120 -

 
1121 -
//------------------------------------------------
 
1122 -

 
1123 -
url_base&
 
1124 -
url_base::
 
1125 -
set_port_number(
 
1126 -
    std::uint16_t n)
 
1127 -
{
 
1128 -
    op_t op(*this);
 
1129 -
    auto s =
 
1130 -
        detail::make_printed(n);
 
1131 -
    auto dest = set_port_impl(
 
1132 -
        s.string().size(), op);
 
1133 -
    std::memcpy(
 
1134 -
        dest, s.string().data(),
 
1135 -
            s.string().size());
 
1136 -
    impl_.port_number_ = n;
 
1137 -
    return *this;
 
1138 -
}
 
1139 -

 
1140 -
url_base&
 
1141 -
url_base::
 
1142 -
set_port(
 
1143 -
    core::string_view s)
 
1144 -
{
 
1145 -
    op_t op(*this, &s);
 
1146 -
    auto t = grammar::parse(s,
 
1147 -
        detail::port_rule{}
 
1148 -
            ).value(BOOST_URL_POS);
 
1149 -
    auto dest =
 
1150 -
        set_port_impl(t.str.size(), op);
 
1151 -
    std::memcpy(dest,
 
1152 -
        t.str.data(), t.str.size());
 
1153 -
    if(t.has_number)
 
1154 -
        impl_.port_number_ = t.number;
 
1155 -
    else
 
1156 -
        impl_.port_number_ = 0;
 
1157 -
    return *this;
 
1158 -
}
 
1159 -

 
1160 -
url_base&
 
1161 -
url_base::
 
1162 -
remove_port() noexcept
 
1163 -
{
 
1164 -
    op_t op(*this);
 
1165 -
    resize_impl(id_port, 0, op);
 
1166 -
    impl_.port_number_ = 0;
 
1167 -
    return *this;
 
1168 -
}
 
1169 -

 
1170 -
//------------------------------------------------
 
1171 -
//
 
1172 -
// Compound Fields
 
1173 -
//
 
1174 -
//------------------------------------------------
 
1175 -

 
1176 -
url_base&
 
1177 -
url_base::
 
1178 -
remove_origin()
 
1179 -
{
 
1180 -
    // these two calls perform 2 memmoves instead of 1
 
1181 -
    remove_authority();
 
1182 -
    remove_scheme();
 
1183 -
    return *this;
 
1184 -
}
 
1185 -

 
1186 -
//------------------------------------------------
 
1187 -
//
 
1188 -
// Path
 
1189 -
//
 
1190 -
//------------------------------------------------
 
1191 -

 
1192 -
bool
 
1193 -
url_base::
 
1194 -
set_path_absolute(
 
1195 -
    bool absolute)
 
1196 -
{
 
1197 -
    op_t op(*this);
 
1198 -

 
1199 -
    // check if path empty
 
1200 -
    if(impl_.len(id_path) == 0)
 
1201 -
    {
 
1202 -
        if(! absolute)
 
1203 -
        {
 
1204 -
            // already not absolute
 
1205 -
            return true;
 
1206 -
        }
 
1207 -

 
1208 -
        // add '/'
 
1209 -
        auto dest = resize_impl(
 
1210 -
            id_path, 1, op);
 
1211 -
        *dest = '/';
 
1212 -
        ++impl_.decoded_[id_path];
 
1213 -
        return true;
 
1214 -
    }
 
1215 -

 
1216 -
    // check if path absolute
 
1217 -
    if(s_[impl_.offset(id_path)] == '/')
 
1218 -
    {
 
1219 -
        if(absolute)
 
1220 -
        {
 
1221 -
            // already absolute
 
1222 -
            return true;
 
1223 -
        }
 
1224 -

 
1225 -
        if( has_authority() &&
 
1226 -
            impl_.len(id_path) > 1)
 
1227 -
        {
 
1228 -
            // can't do it, paths are always
 
1229 -
            // absolute when authority present!
 
1230 -
            return false;
 
1231 -
        }
 
1232 -

 
1233 -
        auto p = encoded_path();
 
1234 -
        auto pos = p.find_first_of(":/", 1);
 
1235 -
        if (pos != core::string_view::npos &&
 
1236 -
            p[pos] == ':')
 
1237 -
        {
 
1238 -
            // prepend with .
 
1239 -
            auto n = impl_.len(id_path);
 
1240 -
            resize_impl(id_path, n + 1, op);
 
1241 -
            std::memmove(
 
1242 -
                s_ + impl_.offset(id_path) + 1,
 
1243 -
                s_ + impl_.offset(id_path), n);
 
1244 -
            *(s_ + impl_.offset(id_path)) = '.';
 
1245 -
            ++impl_.decoded_[id_path];
 
1246 -
            return true;
 
1247 -
        }
 
1248 -

 
1249 -
        // remove '/'
 
1250 -
        auto n = impl_.len(id_port);
 
1251 -
        impl_.split(id_port, n + 1);
 
1252 -
        resize_impl(id_port, n, op);
 
1253 -
        --impl_.decoded_[id_path];
 
1254 -
        return true;
 
1255 -
    }
 
1256 -

 
1257 -
    if(! absolute)
 
1258 -
    {
 
1259 -
        // already not absolute
 
1260 -
        return true;
 
1261 -
    }
 
1262 -

 
1263 -
    // add '/'
 
1264 -
    auto n = impl_.len(id_port);
 
1265 -
    auto dest = resize_impl(
 
1266 -
        id_port, n + 1, op) + n;
 
1267 -
    impl_.split(id_port, n);
 
1268 -
    *dest = '/';
 
1269 -
    ++impl_.decoded_[id_path];
 
1270 -
    return true;
 
1271 -
}
 
1272 -

 
1273 -
url_base&
 
1274 -
url_base::
 
1275 -
set_path(
 
1276 -
    core::string_view s)
 
1277 -
{
 
1278 -
    op_t op(*this, &s);
 
1279 -
    encoding_opts opt;
 
1280 -

 
1281 -
//------------------------------------------------
 
1282 -
//
 
1283 -
//  Calculate encoded size
 
1284 -
//
 
1285 -
// - "/"s are not encoded
 
1286 -
// - "%2F"s are not encoded
 
1287 -
//
 
1288 -
// - reserved path chars are re-encoded
 
1289 -
// - colons in first segment might need to be re-encoded
 
1290 -
// - the path might need to receive a prefix
 
1291 -
    auto const n = encoded_size(
 
1292 -
        s, detail::path_chars, opt);
 
1293 -
    std::size_t n_reencode_colons = 0;
 
1294 -
    core::string_view first_seg;
 
1295 -
    if (!has_scheme() &&
 
1296 -
        !has_authority() &&
 
1297 -
        !s.starts_with('/'))
 
1298 -
    {
 
1299 -
        // the first segment with unencoded colons would look
 
1300 -
        // like the scheme
 
1301 -
        first_seg = detail::to_sv(s);
 
1302 -
        std::size_t p = s.find('/');
 
1303 -
        if (p != core::string_view::npos)
 
1304 -
            first_seg = s.substr(0, p);
 
1305 -
        n_reencode_colons = std::count(
 
1306 -
            first_seg.begin(), first_seg.end(), ':');
 
1307 -
    }
 
1308 -
    // the authority can only be followed by an empty or relative path
 
1309 -
    // if we have an authority and the path is a non-empty relative path, we
 
1310 -
    // add the "/" prefix to make it valid.
 
1311 -
    bool make_absolute =
 
1312 -
        has_authority() &&
 
1313 -
        !s.starts_with('/') &&
 
1314 -
        !s.empty();
 
1315 -
    // a path starting with "//" might look like the authority.
 
1316 -
    // we add a "/." prefix to prevent that
 
1317 -
    bool add_dot_segment =
 
1318 -
        !make_absolute &&
 
1319 -
        s.starts_with("//");
 
1320 -

 
1321 -
//------------------------------------------------
 
1322 -
//
 
1323 -
//  Re-encode data
 
1324 -
//
 
1325 -
    auto dest = set_path_impl(
 
1326 -
        n + make_absolute + 2 * n_reencode_colons + 2 * add_dot_segment, op);
 
1327 -
    impl_.decoded_[id_path] = 0;
 
1328 -
    if (!dest)
 
1329 -
    {
 
1330 -
        impl_.nseg_ = 0;
 
1331 -
        return *this;
 
1332 -
    }
 
1333 -
    if (make_absolute)
 
1334 -
    {
 
1335 -
        *dest++ = '/';
 
1336 -
        impl_.decoded_[id_path] += 1;
 
1337 -
    }
 
1338 -
    else if (add_dot_segment)
 
1339 -
    {
 
1340 -
        *dest++ = '/';
 
1341 -
        *dest++ = '.';
 
1342 -
        impl_.decoded_[id_path] += 2;
 
1343 -
    }
 
1344 -
    dest += encode_unsafe(
 
1345 -
        dest,
 
1346 -
        impl_.get(id_query).data() - dest,
 
1347 -
        first_seg,
 
1348 -
        detail::segment_chars - ':',
 
1349 -
        opt);
 
1350 -
    dest += encode_unsafe(
 
1351 -
        dest,
 
1352 -
        impl_.get(id_query).data() - dest,
 
1353 -
        s.substr(first_seg.size()),
 
1354 -
        detail::path_chars,
 
1355 -
        opt);
 
1356 -
    impl_.decoded_[id_path] +=
 
1357 -
        detail::to_size_type(s.size());
 
1358 -
    BOOST_ASSERT(!dest || dest == impl_.get(id_query).data());
 
1359 -
    BOOST_ASSERT(
 
1360 -
        impl_.decoded_[id_path] ==
 
1361 -
        s.size() + make_absolute + 2 * add_dot_segment);
 
1362 -

 
1363 -
//------------------------------------------------
 
1364 -
//
 
1365 -
//  Update path parameters
 
1366 -
//
 
1367 -
    // get the encoded_path with the replacements we applied
 
1368 -
    if (s == "/")
 
1369 -
    {
 
1370 -
        // "/" maps to sequence {}
 
1371 -
        impl_.nseg_ = 0;
 
1372 -
    }
 
1373 -
    else if (!s.empty())
 
1374 -
    {
 
1375 -
        if (s.starts_with("/./"))
 
1376 -
            s = s.substr(2);
 
1377 -
        // count segments as number of '/'s + 1
 
1378 -
        impl_.nseg_ = detail::to_size_type(
 
1379 -
            std::count(
 
1380 -
                s.begin() + 1, s.end(), '/') + 1);
 
1381 -
    }
 
1382 -
    else
 
1383 -
    {
 
1384 -
        // an empty relative path maps to sequence {}
 
1385 -
        impl_.nseg_ = 0;
 
1386 -
    }
 
1387 -

 
1388 -
    check_invariants();
 
1389 -
    return *this;
 
1390 -
}
 
1391 -

 
1392 -
url_base&
 
1393 -
url_base::
 
1394 -
set_encoded_path(
 
1395 -
    pct_string_view s)
 
1396 -
{
 
1397 -
    op_t op(*this, &detail::ref(s));
 
1398 -

 
1399 -
//------------------------------------------------
 
1400 -
//
 
1401 -
//  Calculate re-encoded output size
 
1402 -
//
 
1403 -
// - reserved path chars are re-encoded
 
1404 -
// - colons in first segment might need to be re-encoded
 
1405 -
// - the path might need to receive a prefix
 
1406 -
    auto const n = detail::re_encoded_size_unsafe(
 
1407 -
        s, detail::path_chars);
 
1408 -
    std::size_t n_reencode_colons = 0;
 
1409 -
    core::string_view first_seg;
 
1410 -
    if (!has_scheme() &&
 
1411 -
        !has_authority() &&
 
1412 -
        !s.starts_with('/'))
 
1413 -
    {
 
1414 -
        // the first segment with unencoded colons would look
 
1415 -
        // like the scheme
 
1416 -
        first_seg = detail::to_sv(s);
 
1417 -
        std::size_t p = s.find('/');
 
1418 -
        if (p != core::string_view::npos)
 
1419 -
            first_seg = s.substr(0, p);
 
1420 -
        n_reencode_colons = std::count(
 
1421 -
            first_seg.begin(), first_seg.end(), ':');
 
1422 -
    }
 
1423 -
    // the authority can only be followed by an empty or relative path
 
1424 -
    // if we have an authority and the path is a non-empty relative path, we
 
1425 -
    // add the "/" prefix to make it valid.
 
1426 -
    bool make_absolute =
 
1427 -
        has_authority() &&
 
1428 -
        !s.starts_with('/') &&
 
1429 -
        !s.empty();
 
1430 -
    // a path starting with "//" might look like the authority
 
1431 -
    // we add a "/." prefix to prevent that
 
1432 -
    bool add_dot_segment =
 
1433 -
        !make_absolute &&
 
1434 -
        !has_authority() &&
 
1435 -
        s.starts_with("//");
 
1436 -

 
1437 -
//------------------------------------------------
 
1438 -
//
 
1439 -
//  Re-encode data
 
1440 -
//
 
1441 -
    auto dest = set_path_impl(
 
1442 -
        n + make_absolute + 2 * n_reencode_colons + 2 * add_dot_segment, op);
 
1443 -
    impl_.decoded_[id_path] = 0;
 
1444 -
    if (!dest)
 
1445 -
    {
 
1446 -
        impl_.nseg_ = 0;
 
1447 -
        return *this;
 
1448 -
    }
 
1449 -
    if (make_absolute)
 
1450 -
    {
 
1451 -
        *dest++ = '/';
 
1452 -
        impl_.decoded_[id_path] += 1;
 
1453 -
    }
 
1454 -
    else if (add_dot_segment)
 
1455 -
    {
 
1456 -
        *dest++ = '/';
 
1457 -
        *dest++ = '.';
 
1458 -
        impl_.decoded_[id_path] += 2;
 
1459 -
    }
 
1460 -
    impl_.decoded_[id_path] +=
 
1461 -
        detail::to_size_type(detail::re_encode_unsafe(
 
1462 -
            dest,
 
1463 -
            impl_.get(id_query).data(),
 
1464 -
            first_seg,
 
1465 -
            detail::segment_chars - ':'));
 
1466 -
    impl_.decoded_[id_path] +=
 
1467 -
        detail::to_size_type(detail::re_encode_unsafe(
 
1468 -
            dest,
 
1469 -
            impl_.get(id_query).data(),
 
1470 -
            s.substr(first_seg.size()),
 
1471 -
            detail::path_chars));
 
1472 -
    BOOST_ASSERT(dest == impl_.get(id_query).data());
 
1473 -
    BOOST_ASSERT(
 
1474 -
        impl_.decoded_[id_path] ==
 
1475 -
        s.decoded_size() + make_absolute + 2 * add_dot_segment);
 
1476 -

 
1477 -
//------------------------------------------------
 
1478 -
//
 
1479 -
//  Update path parameters
 
1480 -
//
 
1481 -
    // get the encoded_path with the replacements we applied
 
1482 -
    if (s == "/")
 
1483 -
    {
 
1484 -
        // "/" maps to sequence {}
 
1485 -
        impl_.nseg_ = 0;
 
1486 -
    }
 
1487 -
    else if (!s.empty())
 
1488 -
    {
 
1489 -
        if (s.starts_with("/./"))
 
1490 -
            s = s.substr(2);
 
1491 -
        // count segments as number of '/'s + 1
 
1492 -
        impl_.nseg_ = detail::to_size_type(
 
1493 -
            std::count(
 
1494 -
                s.begin() + 1, s.end(), '/') + 1);
 
1495 -
    }
 
1496 -
    else
 
1497 -
    {
 
1498 -
        // an empty relative path maps to sequence {}
 
1499 -
        impl_.nseg_ = 0;
 
1500 -
    }
 
1501 -

 
1502 -
    check_invariants();
 
1503 -
    return *this;
 
1504 -
}
 
1505 -

 
1506 -
segments_ref
 
1507 -
url_base::
 
1508 -
segments() noexcept
 
1509 -
{
 
1510 -
    return {*this};
 
1511 -
}
 
1512 -

 
1513 -
segments_encoded_ref
 
1514 -
url_base::
 
1515 -
encoded_segments() noexcept
 
1516 -
{
 
1517 -
    return {*this};
 
1518 -
}
 
1519 -

 
1520 -
//------------------------------------------------
 
1521 -
//
 
1522 -
// Query
 
1523 -
//
 
1524 -
//------------------------------------------------
 
1525 -

 
1526 -
url_base&
 
1527 -
url_base::
 
1528 -
set_query(
 
1529 -
    core::string_view s)
 
1530 -
{
 
1531 -
    edit_params(
 
1532 -
        detail::params_iter_impl(impl_),
 
1533 -
        detail::params_iter_impl(impl_, 0),
 
1534 -
        detail::query_string_iter(s, true));
 
1535 -
    return *this;
 
1536 -
}
 
1537 -

 
1538 -
url_base&
 
1539 -
url_base::
 
1540 -
set_encoded_query(
 
1541 -
    pct_string_view s)
 
1542 -
{
 
1543 -
    op_t op(*this);
 
1544 -
    std::size_t n = 0;      // encoded size
 
1545 -
    std::size_t nparam = 1; // param count
 
1546 -
    auto const end = s.end();
 
1547 -
    auto p = s.begin();
 
1548 -

 
1549 -
    // measure
 
1550 -
    while(p != end)
 
1551 -
    {
 
1552 -
        if(*p == '&')
 
1553 -
        {
 
1554 -
            ++p;
 
1555 -
            ++n;
 
1556 -
            ++nparam;
 
1557 -
        }
 
1558 -
        else if(*p != '%')
 
1559 -
        {
 
1560 -
            if(detail::query_chars(*p))
 
1561 -
                n += 1; // allowed
 
1562 -
            else
 
1563 -
                n += 3; // escaped
 
1564 -
            ++p;
 
1565 -
        }
 
1566 -
        else
 
1567 -
        {
 
1568 -
            // escape
 
1569 -
            n += 3;
 
1570 -
            p += 3;
 
1571 -
        }
 
1572 -
    }
 
1573 -

 
1574 -
    // resize
 
1575 -
    auto dest = resize_impl(
 
1576 -
        id_query, n + 1, op);
 
1577 -
    *dest++ = '?';
 
1578 -

 
1579 -
    // encode
 
1580 -
    impl_.decoded_[id_query] =
 
1581 -
        detail::to_size_type(detail::re_encode_unsafe(
 
1582 -
            dest,
 
1583 -
            dest + n,
 
1584 -
            s,
 
1585 -
            detail::query_chars));
 
1586 -
    BOOST_ASSERT(
 
1587 -
        impl_.decoded_[id_query] ==
 
1588 -
            s.decoded_size());
 
1589 -
    impl_.nparam_ =
 
1590 -
        detail::to_size_type(nparam);
 
1591 -
    return *this;
 
1592 -
}
 
1593 -

 
1594 -
params_ref
 
1595 -
url_base::
 
1596 -
params() noexcept
 
1597 -
{
 
1598 -
    return params_ref(
 
1599 -
        *this,
 
1600 -
        encoding_opts{
 
1601 -
            true, false, false});
 
1602 -
}
 
1603 -

 
1604 -
params_ref
 
1605 -
url_base::
 
1606 -
params(encoding_opts opt) noexcept
 
1607 -
{
 
1608 -
    return {*this, opt};
 
1609 -
}
 
1610 -

 
1611 -
params_encoded_ref
 
1612 -
url_base::
 
1613 -
encoded_params() noexcept
 
1614 -
{
 
1615 -
    return {*this};
 
1616 -
}
 
1617 -

 
1618 -
url_base&
 
1619 -
url_base::
 
1620 -
set_params(
 
1621 -
    std::initializer_list<param_view> ps,
 
1622 -
    encoding_opts opts) noexcept
 
1623 -
{
 
1624 -
    params(opts).assign(ps);
 
1625 -
    return *this;
 
1626 -
}
 
1627 -

 
1628 -
url_base&
 
1629 -
url_base::
 
1630 -
set_encoded_params( std::initializer_list< param_pct_view > ps ) noexcept
 
1631 -
{
 
1632 -
    encoded_params().assign(ps);
 
1633 -
    return *this;
 
1634 -
}
 
1635 -

 
1636 -
url_base&
 
1637 -
url_base::
 
1638 -
remove_query() noexcept
 
1639 -
{
 
1640 -
    op_t op(*this);
 
1641 -
    resize_impl(id_query, 0, op);
 
1642 -
    impl_.nparam_ = 0;
 
1643 -
    impl_.decoded_[id_query] = 0;
 
1644 -
    return *this;
 
1645 -
}
 
1646 -

 
1647 -
//------------------------------------------------
 
1648 -
//
 
1649 -
// Fragment
 
1650 -
//
 
1651 -
//------------------------------------------------
 
1652 -

 
1653 -
url_base&
 
1654 -
url_base::
 
1655 -
remove_fragment() noexcept
 
1656 -
{
 
1657 -
    op_t op(*this);
 
1658 -
    resize_impl(id_frag, 0, op);
 
1659 -
    impl_.decoded_[id_frag] = 0;
 
1660 -
    return *this;
 
1661 -
}
 
1662 -

 
1663 -
url_base&
 
1664 -
url_base::
 
1665 -
set_fragment(core::string_view s)
 
1666 -
{
 
1667 -
    op_t op(*this, &s);
 
1668 -
    encoding_opts opt;
 
1669 -
    auto const n = encoded_size(
 
1670 -
        s,
 
1671 -
        detail::fragment_chars,
 
1672 -
        opt);
 
1673 -
    auto dest = resize_impl(
 
1674 -
        id_frag, n + 1, op);
 
1675 -
    *dest++ = '#';
 
1676 -
    encode_unsafe(
 
1677 -
        dest,
 
1678 -
        n,
 
1679 -
        s,
 
1680 -
        detail::fragment_chars,
 
1681 -
        opt);
 
1682 -
    impl_.decoded_[id_frag] =
 
1683 -
        detail::to_size_type(s.size());
 
1684 -
    return *this;
 
1685 -
}
 
1686 -

 
1687 -
url_base&
 
1688 -
url_base::
 
1689 -
set_encoded_fragment(
 
1690 -
    pct_string_view s)
 
1691 -
{
 
1692 -
    op_t op(*this, &detail::ref(s));
 
1693 -
    auto const n =
 
1694 -
        detail::re_encoded_size_unsafe(
 
1695 -
            s,
 
1696 -
            detail::fragment_chars);
 
1697 -
    auto dest = resize_impl(
 
1698 -
        id_frag, n + 1, op);
 
1699 -
    *dest++ = '#';
 
1700 -
    impl_.decoded_[id_frag] =
 
1701 -
        detail::to_size_type(detail::re_encode_unsafe(
 
1702 -
            dest,
 
1703 -
            dest + n,
 
1704 -
            s,
 
1705 -
            detail::fragment_chars));
 
1706 -
    BOOST_ASSERT(
 
1707 -
        impl_.decoded_[id_frag] ==
 
1708 -
            s.decoded_size());
 
1709 -
    return *this;
 
1710 -
}
 
1711 -

 
1712 -
//------------------------------------------------
 
1713 -
//
 
1714 -
// Resolution
 
1715 -
//
 
1716 -
//------------------------------------------------
 
1717 -

 
1718 -
system::result<void>
 
1719 -
url_base::
 
1720 -
resolve(
 
1721 -
    url_view_base const& ref)
 
1722 -
{
 
1723 -
    if (this == &ref &&
 
1724 -
        has_scheme())
 
1725 -
    {
 
1726 -
        normalize_path();
 
1727 -
        return {};
 
1728 -
    }
 
1729 -

 
1730 -
    if(! has_scheme())
 
1731 -
    {
 
1732 -
        BOOST_URL_RETURN_EC(error::not_a_base);
 
1733 -
    }
 
1734 -

 
1735 -
    op_t op(*this);
 
1736 -

 
1737 -
    //
 
1738 -
    // 5.2.2. Transform References
 
1739 -
    // https://datatracker.ietf.org/doc/html/rfc3986#section-5.2.2
 
1740 -
    //
 
1741 -

 
1742 -
    if( ref.has_scheme() &&
 
1743 -
        ref.scheme() != scheme())
 
1744 -
    {
 
1745 -
        reserve_impl(ref.size(), op);
 
1746 -
        copy(ref);
 
1747 -
        normalize_path();
 
1748 -
        return {};
 
1749 -
    }
 
1750 -
    if(ref.has_authority())
 
1751 -
    {
 
1752 -
        reserve_impl(
 
1753 -
            impl_.offset(id_user) + ref.size(), op);
 
1754 -
        set_encoded_authority(
 
1755 -
            ref.encoded_authority());
 
1756 -
        set_encoded_path(
 
1757 -
            ref.encoded_path());
 
1758 -
        if (ref.encoded_path().empty())
 
1759 -
            set_path_absolute(false);
 
1760 -
        else
 
1761 -
            normalize_path();
 
1762 -
        if(ref.has_query())
 
1763 -
            set_encoded_query(
 
1764 -
                ref.encoded_query());
 
1765 -
        else
 
1766 -
            remove_query();
 
1767 -
        if(ref.has_fragment())
 
1768 -
            set_encoded_fragment(
 
1769 -
                ref.encoded_fragment());
 
1770 -
        else
 
1771 -
            remove_fragment();
 
1772 -
        return {};
 
1773 -
    }
 
1774 -
    if(ref.encoded_path().empty())
 
1775 -
    {
 
1776 -
        reserve_impl(
 
1777 -
            impl_.offset(id_query) +
 
1778 -
            ref.size(), op);
 
1779 -
        normalize_path();
 
1780 -
        if(ref.has_query())
 
1781 -
        {
 
1782 -
            set_encoded_query(
 
1783 -
                ref.encoded_query());
 
1784 -
        }
 
1785 -
        if(ref.has_fragment())
 
1786 -
            set_encoded_fragment(
 
1787 -
                ref.encoded_fragment());
 
1788 -
        else
 
1789 -
            remove_fragment();
 
1790 -
        return {};
 
1791 -
    }
 
1792 -
    if(ref.is_path_absolute())
 
1793 -
    {
 
1794 -
        reserve_impl(
 
1795 -
            impl_.offset(id_path) +
 
1796 -
                ref.size(), op);
 
1797 -
        set_encoded_path(
 
1798 -
            ref.encoded_path());
 
1799 -
        normalize_path();
 
1800 -
        if(ref.has_query())
 
1801 -
            set_encoded_query(
 
1802 -
                ref.encoded_query());
 
1803 -
        else
 
1804 -
            remove_query();
 
1805 -
        if(ref.has_fragment())
 
1806 -
            set_encoded_fragment(
 
1807 -
                ref.encoded_fragment());
 
1808 -
        else
 
1809 -
            remove_fragment();
 
1810 -
        return {};
 
1811 -
    }
 
1812 -
    // General case: ref is relative path
 
1813 -
    reserve_impl(
 
1814 -
        impl_.offset(id_query) +
 
1815 -
        ref.size(), op);
 
1816 -
    // 5.2.3. Merge Paths
 
1817 -
    auto es = encoded_segments();
 
1818 -
    if(es.size() > 0)
 
1819 -
    {
 
1820 -
        es.pop_back();
 
1821 -
    }
 
1822 -
    es.insert(es.end(),
 
1823 -
        ref.encoded_segments().begin(),
 
1824 -
        ref.encoded_segments().end());
 
1825 -
    normalize_path();
 
1826 -
    if(ref.has_query())
 
1827 -
        set_encoded_query(
 
1828 -
            ref.encoded_query());
 
1829 -
    else
 
1830 -
        remove_query();
 
1831 -
    if(ref.has_fragment())
 
1832 -
        set_encoded_fragment(
 
1833 -
            ref.encoded_fragment());
 
1834 -
    else
 
1835 -
        remove_fragment();
 
1836 -
    return {};
 
1837 -
}
 
1838 -

 
1839 -
//------------------------------------------------
 
1840 -
//
 
1841 -
// Normalization
 
1842 -
//
 
1843 -
//------------------------------------------------
 
1844 -

 
1845 -
template <
 
1846 -
    class AllowedCharset,
 
1847 -
    class IgnoredCharset>
 
1848 -
void
 
1849 -
url_base::
 
1850 -
normalize_octets_impl(
 
1851 -
    int id,
 
1852 -
    AllowedCharset const& allowed,
 
1853 -
    IgnoredCharset const& ignored,
 
1854 -
    op_t& op) noexcept
 
1855 -
{
 
1856 -
    char* it = s_ + impl_.offset(id);
 
1857 -
    char* end = s_ + impl_.offset(id + 1);
 
1858 -
    char d = 0;
 
1859 -
    char* dest = it;
 
1860 -
    while (it < end)
 
1861 -
    {
 
1862 -
        if (*it != '%')
 
1863 -
        {
 
1864 -
            *dest = *it;
 
1865 -
            ++it;
 
1866 -
            ++dest;
 
1867 -
            continue;
 
1868 -
        }
 
1869 -
        BOOST_ASSERT(end - it >= 3);
 
1870 -

 
1871 -
        // decode unreserved octets
 
1872 -
        d = detail::decode_one(it + 1);
 
1873 -
        if (allowed(d) &&
 
1874 -
            !ignored(d))
 
1875 -
        {
 
1876 -
            *dest = d;
 
1877 -
            it += 3;
 
1878 -
            ++dest;
 
1879 -
            continue;
 
1880 -
        }
 
1881 -

 
1882 -
        // uppercase percent-encoding triplets
 
1883 -
        *dest++ = '%';
 
1884 -
        ++it;
 
1885 -
        *dest++ = grammar::to_upper(*it++);
 
1886 -
        *dest++ = grammar::to_upper(*it++);
 
1887 -
    }
 
1888 -
    if (it != dest)
 
1889 -
    {
 
1890 -
        auto diff = it - dest;
 
1891 -
        auto n = impl_.len(id) - diff;
 
1892 -
        shrink_impl(id, n, op);
 
1893 -
        s_[size()] = '\0';
 
1894 -
    }
 
1895 -
}
 
1896 -

 
1897 -
template<class CharSet>
 
1898 -
void
 
1899 -
url_base::
 
1900 -
normalize_octets_impl(
 
1901 -
    int idx,
 
1902 -
    CharSet const& allowed,
 
1903 -
    op_t& op) noexcept
 
1904 -
{
 
1905 -
    return normalize_octets_impl(
 
1906 -
        idx, allowed, detail::empty_chars, op);
 
1907 -
}
 
1908 -

 
1909 -
url_base&
 
1910 -
url_base::
 
1911 -
normalize_scheme()
 
1912 -
{
 
1913 -
    to_lower_impl(id_scheme);
 
1914 -
    return *this;
 
1915 -
}
 
1916 -

 
1917 -
url_base&
 
1918 -
url_base::
 
1919 -
normalize_authority()
 
1920 -
{
 
1921 -
    op_t op(*this);
 
1922 -

 
1923 -
    // normalize host
 
1924 -
    if (host_type() == urls::host_type::name)
 
1925 -
    {
 
1926 -
        normalize_octets_impl(
 
1927 -
            id_host,
 
1928 -
            detail::reg_name_chars, op);
 
1929 -
    }
 
1930 -
    decoded_to_lower_impl(id_host);
 
1931 -

 
1932 -
    // normalize password
 
1933 -
    normalize_octets_impl(id_pass, detail::password_chars, op);
 
1934 -

 
1935 -
    // normalize user
 
1936 -
    normalize_octets_impl(id_user, detail::user_chars, op);
 
1937 -
    return *this;
 
1938 -
}
 
1939 -

 
1940 -
url_base&
 
1941 -
url_base::
 
1942 -
normalize_path()
 
1943 -
{
 
1944 -
    op_t op(*this);
 
1945 -
    normalize_octets_impl(id_path, detail::segment_chars, op);
 
1946 -
    core::string_view p = impl_.get(id_path);
 
1947 -
    char* p_dest = s_ + impl_.offset(id_path);
 
1948 -
    char* p_end = s_ + impl_.offset(id_path + 1);
 
1949 -
    auto pn = p.size();
 
1950 -
    auto skip_dot = 0;
 
1951 -
    bool encode_colons = false;
 
1952 -
    core::string_view first_seg;
 
1953 -

 
1954 -
//------------------------------------------------
 
1955 -
//
 
1956 -
//  Determine unnecessary initial dot segments to skip and
 
1957 -
//  if we need to encode colons in the first segment
 
1958 -
//
 
1959 -
    if (
 
1960 -
        !has_authority() &&
 
1961 -
        p.starts_with("/./"))
 
1962 -
    {
 
1963 -
        // check if removing the "/./" would result in "//"
 
1964 -
        // ex: "/.//", "/././/", "/././/", ...
 
1965 -
        skip_dot = 2;
 
1966 -
        while (p.substr(skip_dot, 3).starts_with("/./"))
 
1967 -
            skip_dot += 2;
 
1968 -
        if (p.substr(skip_dot).starts_with("//"))
 
1969 -
            skip_dot = 2;
 
1970 -
        else
 
1971 -
            skip_dot = 0;
 
1972 -
    }
 
1973 -
    else if (
 
1974 -
        !has_scheme() &&
 
1975 -
        !has_authority())
 
1976 -
    {
 
1977 -
        if (p.starts_with("./"))
 
1978 -
        {
 
1979 -
            // check if removing the "./" would result in "//"
 
1980 -
            // ex: ".//", "././/", "././/", ...
 
1981 -
            skip_dot = 1;
 
1982 -
            while (p.substr(skip_dot, 3).starts_with("/./"))
 
1983 -
                skip_dot += 2;
 
1984 -
            if (p.substr(skip_dot).starts_with("//"))
 
1985 -
                skip_dot = 2;
 
1986 -
            else
 
1987 -
                skip_dot = 0;
 
1988 -

 
1989 -
            if ( !skip_dot )
 
1990 -
            {
 
1991 -
                // check if removing "./"s would leave us
 
1992 -
                // a first segment with an ambiguous ":"
 
1993 -
                first_seg = p.substr(2);
 
1994 -
                while (first_seg.starts_with("./"))
 
1995 -
                    first_seg = first_seg.substr(2);
 
1996 -
                auto i = first_seg.find('/');
 
1997 -
                if (i != core::string_view::npos)
 
1998 -
                    first_seg = first_seg.substr(0, i);
 
1999 -
                encode_colons = first_seg.contains(':');
 
2000 -
            }
 
2001 -
        }
 
2002 -
        else
 
2003 -
        {
 
2004 -
            // check if normalize_octets_impl
 
2005 -
            // didn't already create a ":"
 
2006 -
            // in the first segment
 
2007 -
            first_seg = p;
 
2008 -
            auto i = first_seg.find('/');
 
2009 -
            if (i != core::string_view::npos)
 
2010 -
                first_seg = p.substr(0, i);
 
2011 -
            encode_colons = first_seg.contains(':');
 
2012 -
        }
 
2013 -
    }
 
2014 -

 
2015 -
//------------------------------------------------
 
2016 -
//
 
2017 -
//  Encode colons in the first segment
 
2018 -
//
 
2019 -
    if (encode_colons)
 
2020 -
    {
 
2021 -
        // prepend with "./"
 
2022 -
        // (resize_impl never throws)
 
2023 -
        auto cn =
 
2024 -
            std::count(
 
2025 -
                first_seg.begin(),
 
2026 -
                first_seg.end(),
 
2027 -
                ':');
 
2028 -
        resize_impl(
 
2029 -
            id_path, pn + (2 * cn), op);
 
2030 -
        // move the 2nd, 3rd, ... segments
 
2031 -
        auto begin = s_ + impl_.offset(id_path);
 
2032 -
        auto it = begin;
 
2033 -
        auto end = begin + pn;
 
2034 -
        while (core::string_view(it, 2) == "./")
 
2035 -
            it += 2;
 
2036 -
        while (*it != '/' &&
 
2037 -
               it != end)
 
2038 -
            ++it;
 
2039 -
        // we don't need op here because this is
 
2040 -
        // an internal operation
 
2041 -
        std::memmove(it + (2 * cn), it, end - it);
 
2042 -

 
2043 -
        // move 1st segment
 
2044 -
        auto src = s_ + impl_.offset(id_path) + pn;
 
2045 -
        auto dest = s_ + impl_.offset(id_query);
 
2046 -
        src -= end - it;
 
2047 -
        dest -= end - it;
 
2048 -
        pn -= end - it;
 
2049 -
        do {
 
2050 -
            --src;
 
2051 -
            --dest;
 
2052 -
            if (*src != ':')
 
2053 -
            {
 
2054 -
                *dest = *src;
 
2055 -
            }
 
2056 -
            else
 
2057 -
            {
 
2058 -
                // use uppercase as required by
 
2059 -
                // syntax-based normalization
 
2060 -
                *dest-- = 'A';
 
2061 -
                *dest-- = '3';
 
2062 -
                *dest = '%';
 
2063 -
            }
 
2064 -
            --pn;
 
2065 -
        } while (pn);
 
2066 -
        skip_dot = 0;
 
2067 -
        p = impl_.get(id_path);
 
2068 -
        pn = p.size();
 
2069 -
        p_dest = s_ + impl_.offset(id_path);
 
2070 -
        p_end = s_ + impl_.offset(id_path + 1);
 
2071 -
    }
 
2072 -

 
2073 -
//------------------------------------------------
 
2074 -
//
 
2075 -
//  Remove "." and ".." segments
 
2076 -
//
 
2077 -
    p.remove_prefix(skip_dot);
 
2078 -
    p_dest += skip_dot;
 
2079 -
    auto n = detail::remove_dot_segments(
 
2080 -
        p_dest, p_end, p);
 
2081 -

 
2082 -
//------------------------------------------------
 
2083 -
//
 
2084 -
//  Update path parameters
 
2085 -
//
 
2086 -
    if (n != pn)
 
2087 -
    {
 
2088 -
        BOOST_ASSERT(n < pn);
 
2089 -
        shrink_impl(id_path, n + skip_dot, op);
 
2090 -
        p = encoded_path();
 
2091 -
        if (p == "/")
 
2092 -
            impl_.nseg_ = 0;
 
2093 -
        else if (!p.empty())
 
2094 -
            impl_.nseg_ = detail::to_size_type(
 
2095 -
                std::count(
 
2096 -
                    p.begin() + 1, p.end(), '/') + 1);
 
2097 -
        else
 
2098 -
            impl_.nseg_ = 0;
 
2099 -
        impl_.decoded_[id_path] =
 
2100 -
            detail::to_size_type(detail::decode_bytes_unsafe(
 
2101 -
                impl_.get(id_path)));
 
2102 -
    }
 
2103 -
    return *this;
 
2104 -
}
 
2105 -

 
2106 -
url_base&
 
2107 -
url_base::
 
2108 -
normalize_query()
 
2109 -
{
 
2110 -
    op_t op(*this);
 
2111 -
    normalize_octets_impl(
 
2112 -
        id_query,
 
2113 -
        detail::query_chars,
 
2114 -
        detail::query_ignore_chars,
 
2115 -
        op);
 
2116 -
    return *this;
 
2117 -
}
 
2118 -

 
2119 -
url_base&
 
2120 -
url_base::
 
2121 -
normalize_fragment()
 
2122 -
{
 
2123 -
    op_t op(*this);
 
2124 -
    normalize_octets_impl(
 
2125 -
        id_frag, detail::fragment_chars, op);
 
2126 -
    return *this;
 
2127 -
}
 
2128 -

 
2129 -
url_base&
 
2130 -
url_base::
 
2131 -
normalize()
 
2132 -
{
 
2133 -
    normalize_fragment();
 
2134 -
    normalize_query();
 
2135 -
    normalize_path();
 
2136 -
    normalize_authority();
 
2137 -
    normalize_scheme();
 
2138 -
    return *this;
 
2139 -
}
 
2140 -

 
2141 -
//------------------------------------------------
 
2142 -
//
 
2143 -
// Implementation
 
2144 -
//
 
2145 -
//------------------------------------------------
 
2146 -

 
2147 -
void
 
2148 -
url_base::
 
2149 -
check_invariants() const noexcept
 
2150 -
{
 
2151 -
    BOOST_ASSERT(pi_);
 
2152 -
    BOOST_ASSERT(
 
2153 -
        impl_.len(id_scheme) == 0 ||
 
2154 -
        impl_.get(id_scheme).ends_with(':'));
 
2155 -
    BOOST_ASSERT(
 
2156 -
        impl_.len(id_user) == 0 ||
 
2157 -
        impl_.get(id_user).starts_with("//"));
 
2158 -
    BOOST_ASSERT(
 
2159 -
        impl_.len(id_pass) == 0 ||
 
2160 -
        impl_.get(id_user).starts_with("//"));
 
2161 -
    BOOST_ASSERT(
 
2162 -
        impl_.len(id_pass) == 0 ||
 
2163 -
        (impl_.len(id_pass) == 1 &&
 
2164 -
            impl_.get(id_pass) == "@") ||
 
2165 -
        (impl_.len(id_pass) > 1 &&
 
2166 -
            impl_.get(id_pass).starts_with(':') &&
 
2167 -
            impl_.get(id_pass).ends_with('@')));
 
2168 -
    BOOST_ASSERT(
 
2169 -
        impl_.len(id_user, id_path) == 0 ||
 
2170 -
        impl_.get(id_user).starts_with("//"));
 
2171 -
    BOOST_ASSERT(impl_.decoded_[id_path] >=
 
2172 -
        ((impl_.len(id_path) + 2) / 3));
 
2173 -
    BOOST_ASSERT(
 
2174 -
        impl_.len(id_port) == 0 ||
 
2175 -
        impl_.get(id_port).starts_with(':'));
 
2176 -
    BOOST_ASSERT(
 
2177 -
        impl_.len(id_query) == 0 ||
 
2178 -
        impl_.get(id_query).starts_with('?'));
 
2179 -
    BOOST_ASSERT(
 
2180 -
        (impl_.len(id_query) == 0 && impl_.nparam_ == 0) ||
 
2181 -
        (impl_.len(id_query) > 0 && impl_.nparam_ > 0));
 
2182 -
    BOOST_ASSERT(
 
2183 -
        impl_.len(id_frag) == 0 ||
 
2184 -
        impl_.get(id_frag).starts_with('#'));
 
2185 -
    BOOST_ASSERT(c_str()[size()] == '\0');
 
2186 -
}
 
2187 -

 
2188 -
char*
 
2189 -
url_base::
 
2190 -
resize_impl(
 
2191 -
    int id,
 
2192 -
    std::size_t new_size,
 
2193 -
    op_t& op)
 
2194 -
{
 
2195 -
    return resize_impl(
 
2196 -
        id, id + 1, new_size, op);
 
2197 -
}
 
2198 -

 
2199 -
char*
 
2200 -
url_base::
 
2201 -
resize_impl(
 
2202 -
    int first,
 
2203 -
    int last,
 
2204 -
    std::size_t new_len,
 
2205 -
    op_t& op)
 
2206 -
{
 
2207 -
    auto const n0 = impl_.len(first, last);
 
2208 -
    if(new_len == 0 && n0 == 0)
 
2209 -
        return s_ + impl_.offset(first);
 
2210 -
    if(new_len <= n0)
 
2211 -
        return shrink_impl(
 
2212 -
            first, last, new_len, op);
 
2213 -

 
2214 -
    // growing
 
2215 -
    std::size_t n = new_len - n0;
 
2216 -
    reserve_impl(size() + n, op);
 
2217 -
    auto const pos =
 
2218 -
        impl_.offset(last);
 
2219 -
    // adjust chars
 
2220 -
    op.move(
 
2221 -
        s_ + pos + n,
 
2222 -
        s_ + pos,
 
2223 -
        impl_.offset(id_end) -
 
2224 -
            pos + 1);
 
2225 -
    // collapse (first, last)
 
2226 -
    impl_.collapse(first, last,
 
2227 -
        impl_.offset(last) + n);
 
2228 -
    // shift (last, end) right
 
2229 -
    impl_.adjust_right(last, id_end, n);
 
2230 -
    s_[size()] = '\0';
 
2231 -
    return s_ + impl_.offset(first);
 
2232 -
}
 
2233 -

 
2234 -
char*
 
2235 -
url_base::
 
2236 -
shrink_impl(
 
2237 -
    int id,
 
2238 -
    std::size_t new_size,
 
2239 -
    op_t& op)
 
2240 -
{
 
2241 -
    return shrink_impl(
 
2242 -
        id, id + 1, new_size, op);
 
2243 -
}
 
2244 -

 
2245 -
char*
 
2246 -
url_base::
 
2247 -
shrink_impl(
 
2248 -
    int first,
 
2249 -
    int last,
 
2250 -
    std::size_t new_len,
 
2251 -
    op_t& op)
 
2252 -
{
 
2253 -
    // shrinking
 
2254 -
    auto const n0 = impl_.len(first, last);
 
2255 -
    BOOST_ASSERT(new_len <= n0);
 
2256 -
    std::size_t n = n0 - new_len;
 
2257 -
    auto const pos =
 
2258 -
        impl_.offset(last);
 
2259 -
    // adjust chars
 
2260 -
    op.move(
 
2261 -
        s_ + pos - n,
 
2262 -
        s_ + pos,
 
2263 -
        impl_.offset(
 
2264 -
            id_end) - pos + 1);
 
2265 -
    // collapse (first, last)
 
2266 -
    impl_.collapse(first,  last,
 
2267 -
        impl_.offset(last) - n);
 
2268 -
    // shift (last, end) left
 
2269 -
    impl_.adjust_left(last, id_end, n);
 
2270 -
    s_[size()] = '\0';
 
2271 -
    return s_ + impl_.offset(first);
 
2272 -
}
 
2273 -

 
2274 -
//------------------------------------------------
 
2275 -

 
2276 -
void
 
2277 -
url_base::
 
2278 -
set_scheme_impl(
 
2279 -
    core::string_view s,
 
2280 -
    urls::scheme id)
 
2281 -
{
 
2282 -
    op_t op(*this, &s);
 
2283 -
    check_invariants();
 
2284 -
    grammar::parse(
 
2285 -
        s, detail::scheme_rule()
 
2286 -
            ).value(BOOST_URL_POS);
 
2287 -
    auto const n = s.size();
 
2288 -
    auto const p = impl_.offset(id_path);
 
2289 -

 
2290 -
    // check for "./" prefix
 
2291 -
    bool const has_dot =
 
2292 -
        [this, p]
 
2293 -
    {
 
2294 -
        if(impl_.nseg_ == 0)
 
2295 -
            return false;
 
2296 -
        if(first_segment().size() < 2)
 
2297 -
            return false;
 
2298 -
        auto const src = s_ + p;
 
2299 -
        if(src[0] != '.')
 
2300 -
            return false;
 
2301 -
        if(src[1] != '/')
 
2302 -
            return false;
 
2303 -
        return true;
 
2304 -
    }();
 
2305 -

 
2306 -
    // Remove "./"
 
2307 -
    if(has_dot)
 
2308 -
    {
 
2309 -
        // do this first, for
 
2310 -
        // strong exception safety
 
2311 -
        reserve_impl(
 
2312 -
            size() + n + 1 - 2, op);
 
2313 -
        op.move(
 
2314 -
            s_ + p,
 
2315 -
            s_ + p + 2,
 
2316 -
            size() + 1 -
 
2317 -
                (p + 2));
 
2318 -
        impl_.set_size(
 
2319 -
            id_path,
 
2320 -
            impl_.len(id_path) - 2);
 
2321 -
        s_[size()] = '\0';
 
2322 -
    }
 
2323 -

 
2324 -
    auto dest = resize_impl(
 
2325 -
        id_scheme, n + 1, op);
 
2326 -
    s.copy(dest, n);
 
2327 -
    dest[n] = ':';
 
2328 -
    impl_.scheme_ = id;
 
2329 -
    check_invariants();
 
2330 -
}
 
2331 -

 
2332 -
char*
 
2333 -
url_base::
 
2334 -
set_user_impl(
 
2335 -
    std::size_t n,
 
2336 -
    op_t& op)
 
2337 -
{
 
2338 -
    check_invariants();
 
2339 -
    if(impl_.len(id_pass) != 0)
 
2340 -
    {
 
2341 -
        // keep "//"
 
2342 -
        auto dest = resize_impl(
 
2343 -
            id_user, 2 + n, op);
 
2344 -
        check_invariants();
 
2345 -
        return dest + 2;
 
2346 -
    }
 
2347 -
    // add authority
 
2348 -
    bool const make_absolute =
 
2349 -
        !is_path_absolute() &&
 
2350 -
        !impl_.get(id_path).empty();
 
2351 -
    auto dest = resize_impl(
 
2352 -
        id_user, 2 + n + 1 + make_absolute, op);
 
2353 -
    impl_.split(id_user, 2 + n);
 
2354 -
    dest[0] = '/';
 
2355 -
    dest[1] = '/';
 
2356 -
    dest[2 + n] = '@';
 
2357 -
    if (make_absolute)
 
2358 -
    {
 
2359 -
        impl_.split(id_pass, 1);
 
2360 -
        impl_.split(id_host, 0);
 
2361 -
        impl_.split(id_port, 0);
 
2362 -
        dest[3 + n] = '/';
 
2363 -
    }
 
2364 -
    check_invariants();
 
2365 -
    return dest + 2;
 
2366 -
}
 
2367 -

 
2368 -
char*
 
2369 -
url_base::
 
2370 -
set_password_impl(
 
2371 -
    std::size_t n,
 
2372 -
    op_t& op)
 
2373 -
{
 
2374 -
    check_invariants();
 
2375 -
    if(impl_.len(id_user) != 0)
 
2376 -
    {
 
2377 -
        // already have authority
 
2378 -
        auto const dest = resize_impl(
 
2379 -
            id_pass, 1 + n + 1, op);
 
2380 -
        dest[0] = ':';
 
2381 -
        dest[n + 1] = '@';
 
2382 -
        check_invariants();
 
2383 -
        return dest + 1;
 
2384 -
    }
 
2385 -
    // add authority
 
2386 -
    bool const make_absolute =
 
2387 -
            !is_path_absolute() &&
 
2388 -
            !impl_.get(id_path).empty();
 
2389 -
    auto const dest =
 
2390 -
        resize_impl(
 
2391 -
        id_user, id_host,
 
2392 -
        2 + 1 + n + 1 + make_absolute, op);
 
2393 -
    impl_.split(id_user, 2);
 
2394 -
    dest[0] = '/';
 
2395 -
    dest[1] = '/';
 
2396 -
    dest[2] = ':';
 
2397 -
    dest[2 + n + 1] = '@';
 
2398 -
    if (make_absolute)
 
2399 -
    {
 
2400 -
        impl_.split(id_pass, 2 + n);
 
2401 -
        impl_.split(id_host, 0);
 
2402 -
        impl_.split(id_port, 0);
 
2403 -
        dest[4 + n] = '/';
 
2404 -
    }
 
2405 -
    check_invariants();
 
2406 -
    return dest + 3;
 
2407 -
}
 
2408 -

 
2409 -
char*
 
2410 -
url_base::
 
2411 -
set_userinfo_impl(
 
2412 -
    std::size_t n,
 
2413 -
    op_t& op)
 
2414 -
{
 
2415 -
    // "//" {dest} "@"
 
2416 -
    check_invariants();
 
2417 -
    bool const make_absolute =
 
2418 -
            !is_path_absolute() &&
 
2419 -
            !impl_.get(id_path).empty();
 
2420 -
    auto dest = resize_impl(
 
2421 -
        id_user, id_host, n + 3 + make_absolute, op);
 
2422 -
    impl_.split(id_user, n + 2);
 
2423 -
    dest[0] = '/';
 
2424 -
    dest[1] = '/';
 
2425 -
    dest[n + 2] = '@';
 
2426 -
    if (make_absolute)
 
2427 -
    {
 
2428 -
        impl_.split(id_pass, 1);
 
2429 -
        impl_.split(id_host, 0);
 
2430 -
        impl_.split(id_port, 0);
 
2431 -
        dest[3 + n] = '/';
 
2432 -
    }
 
2433 -
    check_invariants();
 
2434 -
    return dest + 2;
 
2435 -
}
 
2436 -

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

 
2475 -
char*
 
2476 -
url_base::
 
2477 -
set_port_impl(
 
2478 -
    std::size_t n,
 
2479 -
    op_t& op)
 
2480 -
{
 
2481 -
    check_invariants();
 
2482 -
    if(impl_.len(id_user) != 0)
 
2483 -
    {
 
2484 -
        // authority exists
 
2485 -
        auto dest = resize_impl(
 
2486 -
            id_port, n + 1, op);
 
2487 -
        dest[0] = ':';
 
2488 -
        check_invariants();
 
2489 -
        return dest + 1;
 
2490 -
    }
 
2491 -
    bool make_absolute =
 
2492 -
        !is_path_absolute() &&
 
2493 -
        impl_.len(id_path) != 0;
 
2494 -
    auto dest = resize_impl(
 
2495 -
        id_user, 3 + n + make_absolute, op);
 
2496 -
    impl_.split(id_user, 2);
 
2497 -
    impl_.split(id_pass, 0);
 
2498 -
    impl_.split(id_host, 0);
 
2499 -
    dest[0] = '/';
 
2500 -
    dest[1] = '/';
 
2501 -
    dest[2] = ':';
 
2502 -
    if (make_absolute)
 
2503 -
    {
 
2504 -
        impl_.split(id_port, n + 1);
 
2505 -
        dest[n + 3] = '/';
 
2506 -
        ++impl_.decoded_[id_path];
 
2507 -
    }
 
2508 -
    check_invariants();
 
2509 -
    return dest + 3;
 
2510 -
}
 
2511 -

 
2512 -
char*
 
2513 -
url_base::
 
2514 -
set_path_impl(
 
2515 -
    std::size_t n,
 
2516 -
    op_t& op)
 
2517 -
{
 
2518 -
    check_invariants();
 
2519 -
    auto const dest = resize_impl(
 
2520 -
        id_path, n, op);
 
2521 -
    return dest;
 
2522 -
}
 
2523 -

 
2524 -

 
2525 -
//------------------------------------------------
 
2526 -

 
2527 -
// return the first segment of the path.
 
2528 -
// this is needed for some algorithms.
 
2529 -
core::string_view
 
2530 -
url_base::
 
2531 -
first_segment() const noexcept
 
2532 -
{
 
2533 -
    if(impl_.nseg_ == 0)
 
2534 -
        return {};
 
2535 -
    auto const p0 = impl_.cs_ +
 
2536 -
        impl_.offset(id_path) +
 
2537 -
            detail::path_prefix(
 
2538 -
                impl_.get(id_path));
 
2539 -
    auto const end = impl_.cs_ +
 
2540 -
        impl_.offset(id_query);
 
2541 -
    if(impl_.nseg_ == 1)
 
2542 -
        return core::string_view(
 
2543 -
            p0, end - p0);
 
2544 -
    auto p = p0;
 
2545 -
    while(*p != '/')
 
2546 -
        ++p;
 
2547 -
    BOOST_ASSERT(p < end);
 
2548 -
    return core::string_view(p0, p - p0);
 
2549 -
}
 
2550 -

 
2551 -
detail::segments_iter_impl
 
2552 -
url_base::
 
2553 -
edit_segments(
 
2554 -
    detail::segments_iter_impl const& it0,
 
2555 -
    detail::segments_iter_impl const& it1,
 
2556 -
    detail::any_segments_iter&& src,
 
2557 -
    // -1 = preserve
 
2558 -
    //  0 = make relative (can fail)
 
2559 -
    //  1 = make absolute
 
2560 -
    int absolute)
 
2561 -
{
 
2562 -
    // Iterator doesn't belong to this url
 
2563 -
    BOOST_ASSERT(it0.ref.alias_of(impl_));
 
2564 -

 
2565 -
    // Iterator doesn't belong to this url
 
2566 -
    BOOST_ASSERT(it1.ref.alias_of(impl_));
 
2567 -

 
2568 -
    // Iterator is in the wrong order
 
2569 -
    BOOST_ASSERT(it0.index <= it1.index);
 
2570 -

 
2571 -
    // Iterator is out of range
 
2572 -
    BOOST_ASSERT(it0.index <= impl_.nseg_);
 
2573 -
    BOOST_ASSERT(it0.pos <= impl_.len(id_path));
 
2574 -

 
2575 -
    // Iterator is out of range
 
2576 -
    BOOST_ASSERT(it1.index <= impl_.nseg_);
 
2577 -
    BOOST_ASSERT(it1.pos <= impl_.len(id_path));
 
2578 -

 
2579 -
//------------------------------------------------
 
2580 -
//
 
2581 -
//  Calculate output prefix
 
2582 -
//
 
2583 -
//  0 = ""
 
2584 -
//  1 = "/"
 
2585 -
//  2 = "./"
 
2586 -
//  3 = "/./"
 
2587 -
//
 
2588 -
    bool const is_abs = is_path_absolute();
 
2589 -
    if(has_authority())
 
2590 -
    {
 
2591 -
        // Check if the new
 
2592 -
        // path would be empty
 
2593 -
        if( src.fast_nseg == 0 &&
 
2594 -
            it0.index == 0 &&
 
2595 -
            it1.index == impl_.nseg_)
 
2596 -
        {
 
2597 -
            // VFALCO we don't have
 
2598 -
            // access to nchar this early
 
2599 -
            //
 
2600 -
            //BOOST_ASSERT(nchar == 0);
 
2601 -
            absolute = 0;
 
2602 -
        }
 
2603 -
        else
 
2604 -
        {
 
2605 -
            // prefix "/" required
 
2606 -
            absolute = 1;
 
2607 -
        }
 
2608 -
    }
 
2609 -
    else if(absolute < 0)
 
2610 -
    {
 
2611 -
        absolute = is_abs; // preserve
 
2612 -
    }
 
2613 -
    auto const path_pos = impl_.offset(id_path);
 
2614 -

 
2615 -
    std::size_t nchar = 0;
 
2616 -
    std::size_t prefix = 0;
 
2617 -
    bool encode_colons = false;
 
2618 -
    bool cp_src_prefix = false;
 
2619 -
    if(it0.index > 0)
 
2620 -
    {
 
2621 -
        // first segment unchanged
 
2622 -
        prefix = src.fast_nseg > 0;
 
2623 -
    }
 
2624 -
    else if(src.fast_nseg > 0)
 
2625 -
    {
 
2626 -
        // first segment from src
 
2627 -
        if(! src.front.empty())
 
2628 -
        {
 
2629 -
            if( src.front == "." &&
 
2630 -
                    src.fast_nseg > 1)
 
2631 -
                if (src.s.empty())
 
2632 -
                {
 
2633 -
                    // if front is ".", we need the extra "." in the prefix
 
2634 -
                    // which will maintain the invariant that segments represent
 
2635 -
                    // {"."}
 
2636 -
                    prefix = 2 + absolute;
 
2637 -
                }
 
2638 -
                else
 
2639 -
                {
 
2640 -
                    // if the "." prefix is explicitly required from set_path
 
2641 -
                    // we do not include an extra "." segment
 
2642 -
                    prefix = absolute;
 
2643 -
                    cp_src_prefix = true;
 
2644 -
                }
 
2645 -
            else if(absolute)
 
2646 -
                prefix = 1;
 
2647 -
            else if(has_scheme() ||
 
2648 -
                    ! src.front.contains(':'))
 
2649 -
                prefix = 0;
 
2650 -
            else
 
2651 -
            {
 
2652 -
                prefix = 0;
 
2653 -
                encode_colons = true;
 
2654 -
            }
 
2655 -
        }
 
2656 -
        else
 
2657 -
        {
 
2658 -
            prefix = 2 + absolute;
 
2659 -
        }
 
2660 -
    }
 
2661 -
    else
 
2662 -
    {
 
2663 -
        // first segment from it1
 
2664 -
        auto const p =
 
2665 -
            impl_.cs_ + path_pos + it1.pos;
 
2666 -
        switch(impl_.cs_ +
 
2667 -
            impl_.offset(id_query) - p)
 
2668 -
        {
 
2669 -
        case 0:
 
2670 -
            // points to end
 
2671 -
            prefix = absolute;
 
2672 -
            break;
 
2673 -
        default:
 
2674 -
            BOOST_ASSERT(*p == '/');
 
2675 -
            if(p[1] != '/')
 
2676 -
            {
 
2677 -
                if(absolute)
 
2678 -
                    prefix = 1;
 
2679 -
                else if(has_scheme() ||
 
2680 -
                        ! it1.dereference().contains(':'))
 
2681 -
                    prefix = 0;
 
2682 -
                else
 
2683 -
                    prefix = 2;
 
2684 -
                break;
 
2685 -
            }
 
2686 -
            // empty
 
2687 -
            BOOST_FALLTHROUGH;
 
2688 -
        case 1:
 
2689 -
            // empty
 
2690 -
            BOOST_ASSERT(*p == '/');
 
2691 -
            prefix = 2 + absolute;
 
2692 -
            break;
 
2693 -
        }
 
2694 -
    }
 
2695 -

 
2696 -
//  append '/' to new segs
 
2697 -
//  if inserting at front.
 
2698 -
    std::size_t const suffix =
 
2699 -
        it1.index == 0 &&
 
2700 -
        impl_.nseg_ > 0 &&
 
2701 -
        src.fast_nseg > 0;
 
2702 -

 
2703 -
//------------------------------------------------
 
2704 -
//
 
2705 -
//  Measure the number of encoded characters
 
2706 -
//  of output, and the number of inserted
 
2707 -
//  segments including internal separators.
 
2708 -
//
 
2709 -
    src.encode_colons = encode_colons;
 
2710 -
    std::size_t nseg = 0;
 
2711 -
    if(src.measure(nchar))
 
2712 -
    {
 
2713 -
        src.encode_colons = false;
 
2714 -
        for(;;)
 
2715 -
        {
 
2716 -
            ++nseg;
 
2717 -
            if(! src.measure(nchar))
 
2718 -
                break;
 
2719 -
            ++nchar;
 
2720 -
        }
 
2721 -
    }
 
2722 -

 
2723 -
    switch(src.fast_nseg)
 
2724 -
    {
 
2725 -
    case 0:
 
2726 -
        BOOST_ASSERT(nseg == 0);
 
2727 -
        break;
 
2728 -
    case 1:
 
2729 -
        BOOST_ASSERT(nseg == 1);
 
2730 -
        break;
 
2731 -
    case 2:
 
2732 -
        BOOST_ASSERT(nseg >= 2);
 
2733 -
        break;
 
2734 -
    }
 
2735 -

 
2736 -
//------------------------------------------------
 
2737 -
//
 
2738 -
//  Calculate [pos0, pos1) to remove
 
2739 -
//
 
2740 -
    auto pos0 = it0.pos;
 
2741 -
    if(it0.index == 0)
 
2742 -
    {
 
2743 -
        // patch pos for prefix
 
2744 -
        pos0 = 0;
 
2745 -
    }
 
2746 -
    auto pos1 = it1.pos;
 
2747 -
    if(it1.index == 0)
 
2748 -
    {
 
2749 -
        // patch pos for prefix
 
2750 -
        pos1 = detail::path_prefix(
 
2751 -
            impl_.get(id_path));
 
2752 -
    }
 
2753 -
    else if(
 
2754 -
        it0.index == 0 &&
 
2755 -
        it1.index < impl_.nseg_ &&
 
2756 -
        nseg == 0)
 
2757 -
    {
 
2758 -
        // Remove the slash from segment it1
 
2759 -
        // if it is becoming the new first
 
2760 -
        // segment.
 
2761 -
        ++pos1;
 
2762 -
    }
 
2763 -
    // calc decoded size of old range
 
2764 -
    auto const dn0 =
 
2765 -
        detail::decode_bytes_unsafe(
 
2766 -
            core::string_view(
 
2767 -
                impl_.cs_ +
 
2768 -
                    impl_.offset(id_path) +
 
2769 -
                    pos0,
 
2770 -
                pos1 - pos0));
 
2771 -

 
2772 -
//------------------------------------------------
 
2773 -
//
 
2774 -
//  Resize
 
2775 -
//
 
2776 -
    op_t op(*this, &src.s);
 
2777 -
    char* dest;
 
2778 -
    char const* end;
 
2779 -
    {
 
2780 -
        auto const nremove = pos1 - pos0;
 
2781 -
        // check overflow
 
2782 -
        if( nchar <= max_size() && (
 
2783 -
            prefix + suffix <=
 
2784 -
                max_size() - nchar))
 
2785 -
        {
 
2786 -
            nchar = prefix + nchar + suffix;
 
2787 -
            if( nchar <= nremove ||
 
2788 -
                nchar - nremove <=
 
2789 -
                    max_size() - size())
 
2790 -
                goto ok;
 
2791 -
        }
 
2792 -
        // too large
 
2793 -
        detail::throw_length_error();
 
2794 -
    ok:
 
2795 -
        auto const new_size =
 
2796 -
            size() + nchar - nremove;
 
2797 -
        reserve_impl(new_size, op);
 
2798 -
        dest = s_ + path_pos + pos0;
 
2799 -
        op.move(
 
2800 -
            dest + nchar,
 
2801 -
            s_ + path_pos + pos1,
 
2802 -
            size() - path_pos - pos1);
 
2803 -
        impl_.set_size(
 
2804 -
            id_path,
 
2805 -
            impl_.len(id_path) + nchar - nremove);
 
2806 -
        BOOST_ASSERT(size() == new_size);
 
2807 -
        end = dest + nchar;
 
2808 -
        auto const nseg1 =
 
2809 -
            static_cast<std::ptrdiff_t>(impl_.nseg_) +
 
2810 -
            static_cast<std::ptrdiff_t>(nseg) -
 
2811 -
            static_cast<std::ptrdiff_t>(it1.index) +
 
2812 -
            static_cast<std::ptrdiff_t>(it0.index) -
 
2813 -
            static_cast<std::ptrdiff_t>(cp_src_prefix);
 
2814 -
        BOOST_ASSERT(nseg1 >= 0);
 
2815 -
        impl_.nseg_ = detail::to_size_type(nseg1);
 
2816 -
        if(s_)
 
2817 -
            s_[size()] = '\0';
 
2818 -
    }
 
2819 -

 
2820 -
//------------------------------------------------
 
2821 -
//
 
2822 -
//  Output segments and internal separators:
 
2823 -
//
 
2824 -
//  prefix [ segment [ '/' segment ] ] suffix
 
2825 -
//
 
2826 -
    auto const dest0 = dest;
 
2827 -
    switch(prefix)
 
2828 -
    {
 
2829 -
    case 3:
 
2830 -
        *dest++ = '/';
 
2831 -
        *dest++ = '.';
 
2832 -
        *dest++ = '/';
 
2833 -
        break;
 
2834 -
    case 2:
 
2835 -
        *dest++ = '.';
 
2836 -
        BOOST_FALLTHROUGH;
 
2837 -
    case 1:
 
2838 -
        *dest++ = '/';
 
2839 -
        break;
 
2840 -
    default:
 
2841 -
        break;
 
2842 -
    }
 
2843 -
    src.rewind();
 
2844 -
    if(nseg > 0)
 
2845 -
    {
 
2846 -
        src.encode_colons = encode_colons;
 
2847 -
        for(;;)
 
2848 -
        {
 
2849 -
            src.copy(dest, end);
 
2850 -
            if(--nseg == 0)
 
2851 -
                break;
 
2852 -
            *dest++ = '/';
 
2853 -
            src.encode_colons = false;
 
2854 -
        }
 
2855 -
        if(suffix)
 
2856 -
            *dest++ = '/';
 
2857 -
    }
 
2858 -
    BOOST_ASSERT(dest == dest0 + nchar);
 
2859 -

 
2860 -
    // calc decoded size of new range,
 
2861 -
    auto const dn =
 
2862 -
        detail::decode_bytes_unsafe(
 
2863 -
            core::string_view(dest0, dest - dest0));
 
2864 -
    if(dn >= dn0)
 
2865 -
        impl_.decoded_[id_path] +=
 
2866 -
            detail::to_size_type(dn - dn0);
 
2867 -
    else
 
2868 -
        impl_.decoded_[id_path] -=
 
2869 -
            detail::to_size_type(dn0 - dn);
 
2870 -

 
2871 -
    return detail::segments_iter_impl(
 
2872 -
        impl_, pos0, it0.index);
 
2873 -
}
 
2874 -

 
2875 -
//------------------------------------------------
 
2876 -

 
2877 -
auto
 
2878 -
url_base::
 
2879 -
edit_params(
 
2880 -
    detail::params_iter_impl const& it0,
 
2881 -
    detail::params_iter_impl const& it1,
 
2882 -
    detail::any_params_iter&& src) ->
 
2883 -
        detail::params_iter_impl
 
2884 -
{
 
2885 -
    auto pos0 = impl_.offset(id_query);
 
2886 -
    auto pos1 = pos0 + it1.pos;
 
2887 -
    pos0 = pos0 + it0.pos;
 
2888 -

 
2889 -
    // Iterators belong to this url
 
2890 -
    BOOST_ASSERT(it0.ref.alias_of(impl_));
 
2891 -
    BOOST_ASSERT(it1.ref.alias_of(impl_));
 
2892 -

 
2893 -
    // Iterators is in the right order
 
2894 -
    BOOST_ASSERT(it0.index <= it1.index);
 
2895 -

 
2896 -
    // Iterators are within range
 
2897 -
    BOOST_ASSERT(it0.index <= impl_.nparam_);
 
2898 -
    BOOST_ASSERT(pos0 <= impl_.offset(id_frag));
 
2899 -
    BOOST_ASSERT(it1.index <= impl_.nparam_);
 
2900 -
    BOOST_ASSERT(pos1 <= impl_.offset(id_frag));
 
2901 -

 
2902 -
    // calc decoded size of old range,
 
2903 -
    // minus one if '?' or '&' prefixed
 
2904 -
    auto dn0 =
 
2905 -
        static_cast<std::ptrdiff_t>(
 
2906 -
            detail::decode_bytes_unsafe(
 
2907 -
                core::string_view(
 
2908 -
                    impl_.cs_ + pos0,
 
2909 -
                    pos1 - pos0)));
 
2910 -
    if(impl_.len(id_query) > 0)
 
2911 -
        dn0 -= 1;
 
2912 -
    if(dn0 < 0)
 
2913 -
        dn0 = 0;
 
2914 -

 
2915 -
//------------------------------------------------
 
2916 -
//
 
2917 -
//  Measure the number of encoded characters
 
2918 -
//  of output, and the number of inserted
 
2919 -
//  segments including internal separators.
 
2920 -
//
 
2921 -

 
2922 -
    std::size_t nchar = 0;
 
2923 -
    std::size_t nparam = 0;
 
2924 -
    if(src.measure(nchar))
 
2925 -
    {
 
2926 -
        ++nchar; // for '?' or '&'
 
2927 -
        for(;;)
 
2928 -
        {
 
2929 -
            ++nparam;
 
2930 -
            if(! src.measure(nchar))
 
2931 -
                break;
 
2932 -
            ++nchar; // for '&'
 
2933 -
        }
 
2934 -
    }
 
2935 -

 
2936 -
//------------------------------------------------
 
2937 -
//
 
2938 -
//  Resize
 
2939 -
//
 
2940 -
    op_t op(*this, &src.s0, &src.s1);
 
2941 -
    char* dest;
 
2942 -
    char const* end;
 
2943 -
    {
 
2944 -
        auto const nremove = pos1 - pos0;
 
2945 -
        // check overflow
 
2946 -
        if( nchar > nremove &&
 
2947 -
            nchar - nremove >
 
2948 -
                max_size() - size())
 
2949 -
        {
 
2950 -
            // too large
 
2951 -
            detail::throw_length_error();
 
2952 -
        }
 
2953 -
        auto const nparam1 =
 
2954 -
            static_cast<std::ptrdiff_t>(impl_.nparam_) +
 
2955 -
            static_cast<std::ptrdiff_t>(nparam) -
 
2956 -
            static_cast<std::ptrdiff_t>(it1.index) +
 
2957 -
            static_cast<std::ptrdiff_t>(it0.index);
 
2958 -
        BOOST_ASSERT(nparam1 >= 0);
 
2959 -
        reserve_impl(size() + nchar - nremove, op);
 
2960 -
        dest = s_ + pos0;
 
2961 -
        end = dest + nchar;
 
2962 -
        if(impl_.nparam_ > 0)
 
2963 -
        {
 
2964 -
            // needed when we move
 
2965 -
            // the beginning of the query
 
2966 -
            s_[impl_.offset(id_query)] = '&';
 
2967 -
        }
 
2968 -
        op.move(
 
2969 -
            dest + nchar,
 
2970 -
            impl_.cs_ + pos1,
 
2971 -
            size() - pos1);
 
2972 -
        impl_.set_size(
 
2973 -
            id_query,
 
2974 -
            impl_.len(id_query) +
 
2975 -
                nchar - nremove);
 
2976 -
        impl_.nparam_ =
 
2977 -
            detail::to_size_type(nparam1);
 
2978 -
        if(nparam1 > 0)
 
2979 -
        {
 
2980 -
            // needed when we erase
 
2981 -
            // the beginning of the query
 
2982 -
            s_[impl_.offset(id_query)] = '?';
 
2983 -
        }
 
2984 -
        if(s_)
 
2985 -
            s_[size()] = '\0';
 
2986 -
    }
 
2987 -
    auto const dest0 = dest;
 
2988 -

 
2989 -
//------------------------------------------------
 
2990 -
//
 
2991 -
//  Output params and internal separators:
 
2992 -
//
 
2993 -
//  [ '?' param ] [ '&' param ]
 
2994 -
//
 
2995 -
    if(nparam > 0)
 
2996 -
    {
 
2997 -
        if(it0.index == 0)
 
2998 -
            *dest++ = '?';
 
2999 -
        else
 
3000 -
            *dest++ = '&';
 
3001 -
        src.rewind();
 
3002 -
        for(;;)
 
3003 -
        {
 
3004 -
            src.copy(dest, end);
 
3005 -
            if(--nparam == 0)
 
3006 -
                break;
 
3007 -
            *dest++ = '&';
 
3008 -
        }
 
3009 -
    }
 
3010 -

 
3011 -
    // calc decoded size of new range,
 
3012 -
    // minus one if '?' or '&' prefixed
 
3013 -
    auto dn =
 
3014 -
        static_cast<std::ptrdiff_t>(
 
3015 -
            detail::decode_bytes_unsafe(
 
3016 -
                core::string_view(dest0, dest - dest0)));
 
3017 -
    if(impl_.len(id_query) > 0)
 
3018 -
        dn -= 1;
 
3019 -
    if(dn < 0)
 
3020 -
        dn = 0;
 
3021 -

 
3022 -
    if(dn >= dn0)
 
3023 -
        impl_.decoded_[id_query] +=
 
3024 -
            detail::to_size_type(dn - dn0);
 
3025 -
    else
 
3026 -
        impl_.decoded_[id_query] -=
 
3027 -
            detail::to_size_type(dn0 - dn);
 
3028 -

 
3029 -
    return detail::params_iter_impl(
 
3030 -
        impl_,
 
3031 -
        pos0 - impl_.offset_[id_query],
 
3032 -
        it0.index);
 
3033 -
}
 
3034 -

 
3035 -
//------------------------------------------------
 
3036 -

 
3037 -
void
 
3038 -
url_base::
 
3039 -
decoded_to_lower_impl(int id) noexcept
 
3040 -
{
 
3041 -
    char* it = s_ + impl_.offset(id);
 
3042 -
    char const* const end = s_ + impl_.offset(id + 1);
 
3043 -
    while(it < end)
 
3044 -
    {
 
3045 -
        if (*it != '%')
 
3046 -
        {
 
3047 -
            *it = grammar::to_lower(
 
3048 -
                *it);
 
3049 -
            ++it;
 
3050 -
            continue;
 
3051 -
        }
 
3052 -
        it += 3;
 
3053 -
    }
 
3054 -
}
 
3055 -

 
3056 -
void
 
3057 -
url_base::
 
3058 -
to_lower_impl(int id) noexcept
 
3059 -
{
 
3060 -
    char* it = s_ + impl_.offset(id);
 
3061 -
    char const* const end = s_ + impl_.offset(id + 1);
 
3062 -
    while(it < end)
 
3063 -
    {
 
3064 -
        *it = grammar::to_lower(
 
3065 -
            *it);
 
3066 -
        ++it;
 
3067 -
    }
 
3068 -
}
 
3069 -

 
3070 -
} // urls
 
3071 -
} // boost