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 +
#ifndef BOOST_URL_IMPL_URL_BASE_HPP
 
12 +
#define BOOST_URL_IMPL_URL_BASE_HPP
 
13 +

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

 
614 +
--------------------------------------------------
 
615 +

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

 
1788 +
    op_t op(*this);
 
1789 +

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

 
2594 +

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

 
3148 +
#endif