Line data Source code
1 : //
2 : // Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
3 : // Copyright (c) 2022 Alan de Freitas (alandefreitas@gmail.com)
4 : //
5 : // Distributed under the Boost Software License, Version 1.0. (See accompanying
6 : // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7 : //
8 : // Official repository: https://github.com/boostorg/url
9 : //
10 :
11 :
12 : #include <boost/url/detail/config.hpp>
13 : #include <boost/url/url_base.hpp>
14 : #include <boost/url/encode.hpp>
15 : #include <boost/url/error.hpp>
16 : #include <boost/url/host_type.hpp>
17 : #include <boost/url/scheme.hpp>
18 : #include <boost/url/url_view.hpp>
19 : #include <boost/url/detail/any_params_iter.hpp>
20 : #include <boost/url/detail/any_segments_iter.hpp>
21 : #include "detail/decode.hpp"
22 : #include <boost/url/detail/encode.hpp>
23 : #include <boost/url/detail/except.hpp>
24 : #include "detail/normalize.hpp"
25 : #include "detail/path.hpp"
26 : #include "detail/print.hpp"
27 : #include <boost/url/grammar/ci_string.hpp>
28 : #include <boost/url/rfc/authority_rule.hpp>
29 : #include <boost/url/rfc/query_rule.hpp>
30 : #include <boost/url/rfc/ipv6_address_rule.hpp>
31 : #include "rfc/detail/charsets.hpp"
32 : #include "rfc/detail/host_rule.hpp"
33 : #include "rfc/detail/ipvfuture_rule.hpp"
34 : #include "boost/url/rfc/detail/path_rules.hpp"
35 : #include "rfc/detail/port_rule.hpp"
36 : #include "rfc/detail/scheme_rule.hpp"
37 : #include "rfc/detail/userinfo_rule.hpp"
38 : #include <boost/url/grammar/parse.hpp>
39 : #include "detail/move_chars.hpp"
40 : #include <cstring>
41 : #include <iostream>
42 : #include <stdexcept>
43 : #include <utility>
44 :
45 : namespace boost {
46 : namespace urls {
47 :
48 : //------------------------------------------------
49 :
50 : // these objects help handle the cases
51 : // where the user passes in strings that
52 : // come from inside the url buffer.
53 :
54 8117 : url_base::
55 : op_t::
56 : ~op_t()
57 : {
58 8117 : if(old)
59 1020 : u.cleanup(*this);
60 8117 : u.check_invariants();
61 8117 : }
62 :
63 8117 : url_base::
64 : op_t::
65 : op_t(
66 : url_base& impl_,
67 : core::string_view* s0_,
68 8117 : core::string_view* s1_) noexcept
69 8117 : : u(impl_)
70 8117 : , s0(s0_)
71 8117 : , s1(s1_)
72 : {
73 8117 : u.check_invariants();
74 8117 : }
75 :
76 : void
77 2355 : url_base::
78 : op_t::
79 : move(
80 : char* dest,
81 : char const* src,
82 : std::size_t n) noexcept
83 : {
84 2355 : if(! n)
85 407 : return;
86 1948 : if(s0)
87 : {
88 1253 : if(s1)
89 62 : return detail::move_chars(
90 62 : dest, src, n, *s0, *s1);
91 1191 : return detail::move_chars(
92 1191 : dest, src, n, *s0);
93 : }
94 695 : detail::move_chars(
95 : dest, src, n);
96 : }
97 :
98 : //------------------------------------------------
99 :
100 : // construct reference
101 1500 : url_base::
102 : url_base(
103 1500 : detail::url_impl const& impl) noexcept
104 1500 : : url_view_base(impl)
105 : {
106 1500 : }
107 :
108 : void
109 150 : url_base::
110 : reserve_impl(std::size_t n)
111 : {
112 150 : op_t op(*this);
113 150 : reserve_impl(n, op);
114 149 : if(s_)
115 147 : s_[size()] = '\0';
116 150 : }
117 :
118 : // make a copy of u
119 : void
120 3687 : url_base::
121 : copy(url_view_base const& u)
122 : {
123 3687 : if (this == &u)
124 117 : return;
125 3687 : op_t op(*this);
126 3687 : if(u.size() == 0)
127 : {
128 117 : clear();
129 117 : return;
130 : }
131 3570 : reserve_impl(
132 : u.size(), op);
133 3567 : impl_ = *u.pi_;
134 3567 : impl_.cs_ = s_;
135 3567 : impl_.from_ = {from::url};
136 3567 : std::memcpy(s_,
137 3567 : u.data(), u.size());
138 3567 : s_[size()] = '\0';
139 3687 : }
140 :
141 : //------------------------------------------------
142 : //
143 : // Scheme
144 : //
145 : //------------------------------------------------
146 :
147 : url_base&
148 52 : url_base::
149 : set_scheme(core::string_view s)
150 : {
151 52 : set_scheme_impl(
152 : s, string_to_scheme(s));
153 39 : return *this;
154 : }
155 :
156 : url_base&
157 11 : url_base::
158 : set_scheme_id(urls::scheme id)
159 : {
160 11 : if(id == urls::scheme::unknown)
161 1 : detail::throw_invalid_argument();
162 10 : if(id == urls::scheme::none)
163 1 : return remove_scheme();
164 9 : set_scheme_impl(to_string(id), id);
165 9 : return *this;
166 : }
167 :
168 : url_base&
169 36 : url_base::
170 : remove_scheme()
171 : {
172 36 : op_t op(*this);
173 36 : auto const sn = impl_.len(id_scheme);
174 36 : if(sn == 0)
175 9 : return *this;
176 27 : auto const po = impl_.offset(id_path);
177 27 : auto fseg = first_segment();
178 : bool const encode_colon =
179 27 : !has_authority() &&
180 20 : impl_.nseg_ > 0 &&
181 58 : s_[po] != '/' &&
182 11 : fseg.contains(':');
183 27 : if(!encode_colon)
184 : {
185 : // just remove the scheme
186 18 : resize_impl(id_scheme, 0, op);
187 18 : impl_.scheme_ = urls::scheme::none;
188 18 : check_invariants();
189 18 : return *this;
190 : }
191 : // encode any ":" in the first path segment
192 9 : BOOST_ASSERT(sn >= 2);
193 9 : auto pn = impl_.len(id_path);
194 9 : std::size_t cn = 0;
195 46 : for (char c: fseg)
196 37 : cn += c == ':';
197 : std::size_t new_size =
198 9 : size() - sn + 2 * cn;
199 9 : bool need_resize = new_size > size();
200 9 : if (need_resize)
201 : {
202 1 : resize_impl(
203 1 : id_path, pn + 2 * cn, op);
204 : }
205 : // move [id_scheme, id_path) left
206 9 : op.move(
207 : s_,
208 9 : s_ + sn,
209 : po - sn);
210 : // move [id_path, id_query) left
211 9 : auto qo = impl_.offset(id_query);
212 9 : op.move(
213 9 : s_ + po - sn,
214 9 : s_ + po,
215 : qo - po);
216 : // move [id_query, id_end) left
217 9 : op.move(
218 9 : s_ + qo - sn + 2 * cn,
219 9 : s_ + qo,
220 9 : impl_.offset(id_end) - qo);
221 :
222 : // adjust part offsets.
223 : // (po and qo are invalidated)
224 9 : if (need_resize)
225 : {
226 1 : impl_.adjust_left(id_user, id_end, sn);
227 : }
228 : else
229 : {
230 8 : impl_.adjust_left(id_user, id_path, sn);
231 8 : impl_.adjust_left(id_query, id_end, sn - 2 * cn);
232 : }
233 9 : if (encode_colon)
234 : {
235 : // move the 2nd, 3rd, ... segments
236 9 : auto begin = s_ + impl_.offset(id_path);
237 9 : auto it = begin;
238 9 : auto end = begin + pn;
239 46 : while (*it != '/' &&
240 : it != end)
241 37 : ++it;
242 : // we don't need op here because this is
243 : // an internal operation
244 9 : std::memmove(it + (2 * cn), it, end - it);
245 :
246 : // move 1st segment
247 9 : auto src = s_ + impl_.offset(id_path) + pn;
248 9 : auto dest = s_ + impl_.offset(id_query);
249 9 : src -= end - it;
250 9 : dest -= end - it;
251 9 : pn -= end - it;
252 : do {
253 37 : --src;
254 37 : --dest;
255 37 : if (*src != ':')
256 : {
257 25 : *dest = *src;
258 : }
259 : else
260 : {
261 : // use uppercase as required by
262 : // syntax-based normalization
263 12 : *dest-- = 'A';
264 12 : *dest-- = '3';
265 12 : *dest = '%';
266 : }
267 37 : --pn;
268 37 : } while (pn);
269 : }
270 9 : s_[size()] = '\0';
271 9 : impl_.scheme_ = urls::scheme::none;
272 9 : return *this;
273 36 : }
274 :
275 : //------------------------------------------------
276 : //
277 : // Authority
278 : //
279 : //------------------------------------------------
280 :
281 : url_base&
282 112 : url_base::
283 : set_encoded_authority(
284 : pct_string_view s)
285 : {
286 112 : op_t op(*this, &detail::ref(s));
287 114 : authority_view a = grammar::parse(
288 : s, authority_rule
289 112 : ).value(BOOST_URL_POS);
290 111 : auto n = s.size() + 2;
291 : auto const need_slash =
292 133 : ! is_path_absolute() &&
293 22 : impl_.len(id_path) > 0;
294 111 : if(need_slash)
295 2 : ++n;
296 111 : auto dest = resize_impl(
297 : id_user, id_path, n, op);
298 111 : dest[0] = '/';
299 111 : dest[1] = '/';
300 111 : std::memcpy(dest + 2,
301 111 : s.data(), s.size());
302 111 : if(need_slash)
303 2 : dest[n - 1] = '/';
304 111 : impl_.apply_authority(a);
305 111 : if(need_slash)
306 2 : impl_.adjust_right(
307 : id_query, id_end, 1);
308 111 : return *this;
309 112 : }
310 :
311 : url_base&
312 57 : url_base::
313 : remove_authority()
314 : {
315 57 : if(! has_authority())
316 30 : return *this;
317 :
318 27 : op_t op(*this);
319 27 : auto path = impl_.get(id_path);
320 27 : bool const need_dot = path.starts_with("//");
321 27 : if(need_dot)
322 : {
323 : // prepend "/.", can't throw
324 4 : auto p = resize_impl(
325 : id_user, id_path, 2, op);
326 4 : p[0] = '/';
327 4 : p[1] = '.';
328 4 : impl_.split(id_user, 0);
329 4 : impl_.split(id_pass, 0);
330 4 : impl_.split(id_host, 0);
331 4 : impl_.split(id_port, 0);
332 : }
333 : else
334 : {
335 23 : resize_impl(
336 : id_user, id_path, 0, op);
337 : }
338 27 : impl_.host_type_ =
339 : urls::host_type::none;
340 27 : return *this;
341 27 : }
342 :
343 : //------------------------------------------------
344 : //
345 : // Userinfo
346 : //
347 : //------------------------------------------------
348 :
349 : url_base&
350 47 : url_base::
351 : set_userinfo(
352 : core::string_view s)
353 : {
354 47 : op_t op(*this, &s);
355 47 : encoding_opts opt;
356 47 : auto const n = encoded_size(
357 : s, detail::userinfo_chars, opt);
358 47 : auto dest = set_userinfo_impl(n, op);
359 47 : encode(
360 : dest,
361 : n,
362 : s,
363 : detail::userinfo_chars,
364 : opt);
365 47 : auto const pos = impl_.get(
366 : id_user, id_host
367 47 : ).find_first_of(':');
368 47 : if(pos != core::string_view::npos)
369 : {
370 9 : impl_.split(id_user, pos);
371 : // find ':' in plain string
372 : auto const pos2 =
373 9 : s.find_first_of(':');
374 9 : impl_.decoded_[id_user] =
375 9 : pos2 - 1;
376 9 : impl_.decoded_[id_pass] =
377 9 : s.size() - pos2;
378 : }
379 : else
380 : {
381 38 : impl_.decoded_[id_user] = s.size();
382 38 : impl_.decoded_[id_pass] = 0;
383 : }
384 47 : return *this;
385 47 : }
386 :
387 : url_base&
388 52 : url_base::
389 : set_encoded_userinfo(
390 : pct_string_view s)
391 : {
392 52 : op_t op(*this, &detail::ref(s));
393 52 : auto const pos = s.find_first_of(':');
394 52 : if(pos != core::string_view::npos)
395 : {
396 : // user:pass
397 7 : auto const s0 = s.substr(0, pos);
398 7 : auto const s1 = s.substr(pos + 1);
399 : auto const n0 =
400 7 : detail::re_encoded_size_unsafe(
401 : s0,
402 : detail::user_chars);
403 : auto const n1 =
404 7 : detail::re_encoded_size_unsafe(s1,
405 : detail::password_chars);
406 : auto dest =
407 7 : set_userinfo_impl(n0 + n1 + 1, op);
408 7 : impl_.decoded_[id_user] =
409 7 : detail::re_encode_unsafe(
410 : dest,
411 7 : dest + n0,
412 : s0,
413 : detail::user_chars);
414 7 : *dest++ = ':';
415 7 : impl_.decoded_[id_pass] =
416 7 : detail::re_encode_unsafe(
417 : dest,
418 7 : dest + n1,
419 : s1,
420 : detail::password_chars);
421 7 : impl_.split(id_user, 2 + n0);
422 : }
423 : else
424 : {
425 : // user
426 : auto const n =
427 45 : detail::re_encoded_size_unsafe(
428 : s, detail::user_chars);
429 45 : auto dest = set_userinfo_impl(n, op);
430 45 : impl_.decoded_[id_user] =
431 45 : detail::re_encode_unsafe(
432 : dest,
433 45 : dest + n,
434 : s,
435 : detail::user_chars);
436 45 : impl_.split(id_user, 2 + n);
437 45 : impl_.decoded_[id_pass] = 0;
438 : }
439 52 : return *this;
440 52 : }
441 :
442 : url_base&
443 22 : url_base::
444 : remove_userinfo() noexcept
445 : {
446 22 : if(impl_.len(id_pass) == 0)
447 6 : return *this; // no userinfo
448 :
449 16 : op_t op(*this);
450 : // keep authority '//'
451 16 : resize_impl(
452 : id_user, id_host, 2, op);
453 16 : impl_.decoded_[id_user] = 0;
454 16 : impl_.decoded_[id_pass] = 0;
455 16 : return *this;
456 16 : }
457 :
458 : //------------------------------------------------
459 :
460 : url_base&
461 50 : url_base::
462 : set_user(core::string_view s)
463 : {
464 50 : op_t op(*this, &s);
465 50 : encoding_opts opt;
466 50 : auto const n = encoded_size(
467 : s, detail::user_chars, opt);
468 50 : auto dest = set_user_impl(n, op);
469 50 : encode_unsafe(
470 : dest,
471 : n,
472 : s,
473 : detail::user_chars,
474 : opt);
475 50 : impl_.decoded_[id_user] = s.size();
476 50 : return *this;
477 50 : }
478 :
479 : url_base&
480 43 : url_base::
481 : set_encoded_user(
482 : pct_string_view s)
483 : {
484 43 : op_t op(*this, &detail::ref(s));
485 : auto const n =
486 43 : detail::re_encoded_size_unsafe(
487 : s, detail::user_chars);
488 43 : auto dest = set_user_impl(n, op);
489 43 : impl_.decoded_[id_user] =
490 43 : detail::re_encode_unsafe(
491 : dest,
492 43 : dest + n,
493 : s,
494 : detail::user_chars);
495 43 : BOOST_ASSERT(
496 : impl_.decoded_[id_user] ==
497 : s.decoded_size());
498 43 : return *this;
499 43 : }
500 :
501 : //------------------------------------------------
502 :
503 : url_base&
504 37 : url_base::
505 : set_password(core::string_view s)
506 : {
507 37 : op_t op(*this, &s);
508 37 : encoding_opts opt;
509 37 : auto const n = encoded_size(
510 : s, detail::password_chars, opt);
511 37 : auto dest = set_password_impl(n, op);
512 37 : encode_unsafe(
513 : dest,
514 : n,
515 : s,
516 : detail::password_chars,
517 : opt);
518 37 : impl_.decoded_[id_pass] = s.size();
519 37 : return *this;
520 37 : }
521 :
522 : url_base&
523 39 : url_base::
524 : set_encoded_password(
525 : pct_string_view s)
526 : {
527 39 : op_t op(*this, &detail::ref(s));
528 : auto const n =
529 39 : detail::re_encoded_size_unsafe(
530 : s,
531 : detail::password_chars);
532 39 : auto dest = set_password_impl(n, op);
533 39 : impl_.decoded_[id_pass] =
534 39 : detail::re_encode_unsafe(
535 : dest,
536 39 : dest + n,
537 : s,
538 : detail::password_chars);
539 39 : BOOST_ASSERT(
540 : impl_.decoded_[id_pass] ==
541 : s.decoded_size());
542 39 : return *this;
543 39 : }
544 :
545 : url_base&
546 19 : url_base::
547 : remove_password() noexcept
548 : {
549 19 : auto const n = impl_.len(id_pass);
550 19 : if(n < 2)
551 12 : return *this; // no password
552 :
553 7 : op_t op(*this);
554 : // clear password, retain '@'
555 : auto dest =
556 7 : resize_impl(id_pass, 1, op);
557 7 : dest[0] = '@';
558 7 : impl_.decoded_[id_pass] = 0;
559 7 : return *this;
560 7 : }
561 :
562 : //------------------------------------------------
563 : //
564 : // Host
565 : //
566 : //------------------------------------------------
567 : /*
568 : host_type host_type() // ipv4, ipv6, ipvfuture, name
569 :
570 : std::string host() // return encoded_host().decode()
571 : pct_string_view encoded_host() // return host part, as-is
572 : std::string host_address() // return encoded_host_address().decode()
573 : pct_string_view encoded_host_address() // ipv4, ipv6, ipvfut, or encoded name, no brackets
574 :
575 : ipv4_address host_ipv4_address() // return ipv4_address or {}
576 : ipv6_address host_ipv6_address() // return ipv6_address or {}
577 : core::string_view host_ipvfuture() // return ipvfuture or {}
578 : std::string host_name() // return decoded name or ""
579 : pct_string_view encoded_host_name() // return encoded host name or ""
580 :
581 : --------------------------------------------------
582 :
583 : set_host( core::string_view ) // set host part from plain text
584 : set_encoded_host( pct_string_view ) // set host part from encoded text
585 : set_host_address( core::string_view ) // set host from ipv4, ipv6, ipvfut, or plain reg-name string
586 : set_encoded_host_address( pct_string_view ) // set host from ipv4, ipv6, ipvfut, or encoded reg-name string
587 :
588 : set_host_ipv4( ipv4_address ) // set ipv4
589 : set_host_ipv6( ipv6_address ) // set ipv6
590 : set_host_ipvfuture( core::string_view ) // set ipvfuture
591 : set_host_name( core::string_view ) // set name from plain
592 : set_encoded_host_name( pct_string_view ) // set name from encoded
593 : */
594 :
595 : // set host part from plain text
596 : url_base&
597 15 : url_base::
598 : set_host(
599 : core::string_view s)
600 : {
601 15 : if( s.size() > 2 &&
602 18 : s.front() == '[' &&
603 3 : s.back() == ']')
604 : {
605 : // IP-literal
606 3 : if (s[1] != 'v')
607 : {
608 : // IPv6-address
609 2 : auto innersv = s.substr(1, s.size() - 2);
610 2 : auto innerit = innersv.begin();
611 2 : auto endit = innersv.end();
612 2 : auto rv = grammar::parse(
613 : innerit,
614 : endit,
615 : ipv6_address_rule);
616 2 : if(rv)
617 : {
618 2 : if (innerit == endit)
619 : {
620 1 : set_host_ipv6_and_encoded_zone_id(*rv, {});
621 2 : return *this;
622 : }
623 : // IPv6addrz: https://datatracker.ietf.org/doc/html/rfc6874
624 1 : auto chars_left = endit - innerit;
625 2 : if (chars_left >= 2 &&
626 1 : *innerit++ == '%')
627 : {
628 1 : core::string_view zone_id_str = {&*innerit, std::size_t(chars_left - 1)};
629 1 : set_host_ipv6_and_zone_id(*rv, zone_id_str);
630 1 : return *this;
631 : }
632 : }
633 : }
634 : else
635 : {
636 : // IPvFuture
637 1 : auto rv = grammar::parse(
638 1 : s.substr(1, s.size() - 2),
639 : detail::ipvfuture_rule);
640 1 : if(rv)
641 1 : return set_host_ipvfuture(rv->str);
642 : }
643 : }
644 12 : else if(s.size() >= 7) // "0.0.0.0"
645 : {
646 : // IPv4-address
647 10 : auto rv = parse_ipv4_address(s);
648 10 : if(rv)
649 4 : return set_host_ipv4(*rv);
650 : }
651 :
652 : // reg-name
653 8 : op_t op(*this, &s);
654 8 : encoding_opts opt;
655 8 : auto const n = encoded_size(
656 : s, detail::host_chars, opt);
657 8 : auto dest = set_host_impl(n, op);
658 8 : encode(
659 : dest,
660 8 : impl_.get(id_path).data() - dest,
661 : s,
662 : detail::host_chars,
663 : opt);
664 8 : impl_.decoded_[id_host] = s.size();
665 8 : impl_.host_type_ =
666 : urls::host_type::name;
667 8 : return *this;
668 8 : }
669 :
670 : // set host part from encoded text
671 : url_base&
672 116 : url_base::
673 : set_encoded_host(
674 : pct_string_view s)
675 : {
676 116 : if( s.size() > 2 &&
677 133 : s.front() == '[' &&
678 17 : s.back() == ']')
679 : {
680 : // IP-literal
681 17 : if (s[1] != 'v')
682 : {
683 : // IPv6-address
684 16 : auto innersv = s.substr(1, s.size() - 2);
685 16 : auto innerit = innersv.begin();
686 16 : auto endit = innersv.end();
687 16 : auto rv = grammar::parse(
688 : innerit,
689 : endit,
690 : ipv6_address_rule);
691 16 : if(rv)
692 : {
693 8 : if (innerit == endit)
694 : {
695 5 : set_host_ipv6_and_encoded_zone_id(*rv, {});
696 6 : return *this;
697 : }
698 : // IPv6addrz: https://datatracker.ietf.org/doc/html/rfc6874
699 3 : auto chars_left = endit - innerit;
700 4 : if (chars_left >= 3 &&
701 1 : *innerit++ == '%' &&
702 5 : *innerit++ == '2' &&
703 1 : *innerit++ == '5')
704 : {
705 1 : auto const nz = std::size_t(chars_left - 3);
706 1 : core::string_view zone_id_str = {&*innerit, std::size_t(chars_left - 3)};
707 1 : std::size_t dnz = detail::decode_bytes_unsafe(zone_id_str);
708 1 : pct_string_view zone_id_pct = make_pct_string_view_unsafe(innerit, nz, dnz);
709 1 : set_host_ipv6_and_encoded_zone_id(*rv, zone_id_pct);
710 1 : return *this;
711 : }
712 : }
713 : }
714 : else
715 : {
716 : // IPvFuture
717 1 : auto rv = grammar::parse(
718 1 : s.substr(1, s.size() - 2),
719 : detail::ipvfuture_rule);
720 1 : if(rv)
721 1 : return set_host_ipvfuture(rv->str);
722 : }
723 : }
724 99 : else if(s.size() >= 7) // "0.0.0.0"
725 : {
726 : // IPv4-address
727 55 : auto rv = parse_ipv4_address(s);
728 55 : if(rv)
729 5 : return set_host_ipv4(*rv);
730 : }
731 :
732 : // reg-name
733 104 : op_t op(*this, &detail::ref(s));
734 104 : auto const n = detail::re_encoded_size_unsafe(
735 : s, detail::host_chars);
736 104 : auto dest = set_host_impl(n, op);
737 104 : impl_.decoded_[id_host] =
738 208 : detail::re_encode_unsafe(
739 : dest,
740 104 : impl_.get(id_path).data(),
741 : s,
742 : detail::host_chars);
743 104 : BOOST_ASSERT(impl_.decoded_[id_host] ==
744 : s.decoded_size());
745 104 : impl_.host_type_ =
746 : urls::host_type::name;
747 104 : return *this;
748 104 : }
749 :
750 : url_base&
751 10 : url_base::
752 : set_host_address(
753 : core::string_view s)
754 : {
755 10 : if (!s.empty())
756 : {
757 : // IP-literal
758 9 : if (s[0] != 'v')
759 : {
760 : // IPv6-address
761 8 : auto innerit = s.begin();
762 8 : auto endit = s.end();
763 8 : auto rv = grammar::parse(
764 : innerit,
765 : endit,
766 : ipv6_address_rule);
767 8 : if(rv)
768 : {
769 2 : if (innerit == endit)
770 : {
771 1 : set_host_ipv6_and_encoded_zone_id(*rv, {});
772 2 : return *this;
773 : }
774 : // IPv6addrz: https://datatracker.ietf.org/doc/html/rfc6874
775 1 : auto chars_left = endit - innerit;
776 2 : if (chars_left >= 2 &&
777 1 : *innerit++ == '%')
778 : {
779 1 : core::string_view zone_id_str = {&*innerit, std::size_t(chars_left - 1)};
780 1 : set_host_ipv6_and_zone_id(*rv, zone_id_str);
781 1 : return *this;
782 : }
783 : }
784 : }
785 :
786 : // IPvFuture
787 7 : auto rv = grammar::parse(s, detail::ipvfuture_rule);
788 7 : if(rv)
789 1 : return set_host_ipvfuture(rv->str);
790 :
791 6 : if(s.size() >= 7) // "0.0.0.0"
792 : {
793 : // IPv4-address
794 5 : auto rv2 = parse_ipv4_address(s);
795 5 : if(rv2)
796 2 : return set_host_ipv4(*rv2);
797 : }
798 : }
799 :
800 : // reg-name
801 5 : op_t op(*this, &s);
802 5 : encoding_opts opt;
803 5 : auto const n = encoded_size(
804 : s, detail::host_chars, opt);
805 5 : auto dest = set_host_impl(n, op);
806 5 : encode(
807 : dest,
808 5 : impl_.get(id_path).data() - dest,
809 : s,
810 : detail::host_chars,
811 : opt);
812 5 : impl_.decoded_[id_host] = s.size();
813 5 : impl_.host_type_ =
814 : urls::host_type::name;
815 5 : return *this;
816 5 : }
817 :
818 : url_base&
819 8 : url_base::
820 : set_encoded_host_address(
821 : pct_string_view s)
822 : {
823 8 : if( !s.empty() )
824 : {
825 : // IP-literal
826 7 : if (s[0] != 'v')
827 : {
828 : // IPv6-address
829 6 : auto innerit = s.begin();
830 6 : auto endit = s.end();
831 6 : auto rv = grammar::parse(
832 : innerit,
833 : endit,
834 : ipv6_address_rule);
835 6 : if(rv)
836 : {
837 2 : if (innerit == endit)
838 : {
839 1 : set_host_ipv6_and_encoded_zone_id(*rv, {});
840 3 : return *this;
841 : }
842 : // IPv6addrz: https://datatracker.ietf.org/doc/html/rfc6874
843 1 : auto chars_left = endit - innerit;
844 2 : if (chars_left >= 3 &&
845 1 : *innerit++ == '%' &&
846 3 : *innerit++ == '2' &&
847 1 : *innerit++ == '5')
848 : {
849 1 : auto const nz = std::size_t(chars_left - 3);
850 1 : core::string_view zone_id_str = {&*innerit, std::size_t(chars_left - 3)};
851 1 : std::size_t dnz = detail::decode_bytes_unsafe(zone_id_str);
852 1 : pct_string_view zone_id_pct = make_pct_string_view_unsafe(innerit, nz, dnz);
853 1 : set_host_ipv6_and_encoded_zone_id(*rv, zone_id_pct);
854 1 : return *this;
855 : }
856 : }
857 :
858 4 : if(s.size() >= 7) // "0.0.0.0"
859 : {
860 : // IPv4-address
861 3 : auto rv2 = parse_ipv4_address(s);
862 3 : if(rv2)
863 1 : return set_host_ipv4(*rv2);
864 : }
865 : }
866 : else
867 : {
868 : // IPvFuture
869 1 : auto rv = grammar::parse(
870 : s, detail::ipvfuture_rule);
871 1 : if(rv)
872 1 : return set_host_ipvfuture(rv->str);
873 : }
874 : }
875 :
876 : // reg-name
877 4 : op_t op(*this, &detail::ref(s));
878 4 : auto const n = detail::re_encoded_size_unsafe(
879 : s, detail::host_chars);
880 4 : auto dest = set_host_impl(n, op);
881 4 : impl_.decoded_[id_host] =
882 8 : detail::re_encode_unsafe(
883 : dest,
884 4 : impl_.get(id_path).data(),
885 : s,
886 : detail::host_chars);
887 4 : BOOST_ASSERT(impl_.decoded_[id_host] ==
888 : s.decoded_size());
889 4 : impl_.host_type_ =
890 : urls::host_type::name;
891 4 : return *this;
892 4 : }
893 :
894 : url_base&
895 16 : url_base::
896 : set_host_ipv4(
897 : ipv4_address const& addr)
898 : {
899 16 : op_t op(*this);
900 : char buf[urls::ipv4_address::max_str_len];
901 16 : auto s = addr.to_buffer(buf, sizeof(buf));
902 16 : auto dest = set_host_impl(s.size(), op);
903 16 : std::memcpy(dest, s.data(), s.size());
904 16 : impl_.decoded_[id_host] = impl_.len(id_host);
905 16 : impl_.host_type_ = urls::host_type::ipv4;
906 16 : auto bytes = addr.to_bytes();
907 16 : std::memcpy(
908 16 : impl_.ip_addr_,
909 16 : bytes.data(),
910 : bytes.size());
911 16 : return *this;
912 16 : }
913 :
914 : url_base&
915 5 : url_base::
916 : set_host_ipv6(
917 : ipv6_address const& addr)
918 : {
919 5 : set_host_ipv6_and_encoded_zone_id(addr, encoded_zone_id());
920 5 : return *this;
921 : }
922 :
923 : url_base&
924 3 : url_base::
925 : set_zone_id(core::string_view s)
926 : {
927 3 : set_host_ipv6_and_zone_id(host_ipv6_address(), s);
928 3 : return *this;
929 : }
930 :
931 : url_base&
932 3 : url_base::
933 : set_encoded_zone_id(pct_string_view s)
934 : {
935 3 : set_host_ipv6_and_encoded_zone_id(host_ipv6_address(), s);
936 3 : return *this;
937 : }
938 :
939 : void
940 5 : url_base::
941 : set_host_ipv6_and_zone_id(
942 : ipv6_address const& addr,
943 : core::string_view zone_id)
944 : {
945 5 : op_t op(*this, &zone_id);
946 : char ipv6_str_buf[urls::ipv6_address::max_str_len];
947 5 : auto ipv6_str = addr.to_buffer(ipv6_str_buf, sizeof(ipv6_str_buf));
948 5 : bool const has_zone_id = !zone_id.empty();
949 5 : encoding_opts opt;
950 5 : auto const ipn = ipv6_str.size();
951 5 : auto const zn = encoded_size(zone_id, unreserved_chars, opt);
952 5 : auto const n = ipn + 2 + has_zone_id * (3 + zn);
953 5 : auto dest = set_host_impl(n, op);
954 5 : *dest++ = '[';
955 5 : std::memcpy(dest, ipv6_str.data(), ipn);
956 5 : dest += ipn;
957 5 : if (has_zone_id)
958 : {
959 5 : *dest++ = '%';
960 5 : *dest++ = '2';
961 5 : *dest++ = '5';
962 5 : encode(dest, zn, zone_id, unreserved_chars, opt);
963 5 : dest += zn;
964 : }
965 5 : *dest++ = ']';
966 : // ipn + |"["| + |"]"| + (has_zone_id ? |"%"| + zn : 0)
967 5 : impl_.decoded_[id_host] = ipn + 2 + has_zone_id * (1 + zone_id.size());
968 5 : impl_.host_type_ = urls::host_type::ipv6;
969 5 : auto bytes = addr.to_bytes();
970 5 : std::memcpy(
971 5 : impl_.ip_addr_,
972 5 : bytes.data(),
973 : bytes.size());
974 5 : }
975 :
976 : void
977 18 : url_base::
978 : set_host_ipv6_and_encoded_zone_id(
979 : ipv6_address const& addr,
980 : pct_string_view zone_id)
981 : {
982 18 : op_t op(*this, &detail::ref(zone_id));
983 : char ipv6_str_buf[urls::ipv6_address::max_str_len];
984 18 : auto ipv6_str = addr.to_buffer(ipv6_str_buf, sizeof(ipv6_str_buf));
985 18 : bool const has_zone_id = !zone_id.empty();
986 18 : auto const ipn = ipv6_str.size();
987 18 : auto const zn = detail::re_encoded_size_unsafe(zone_id, unreserved_chars);
988 18 : auto const n = ipn + 2 + has_zone_id * (3 + zn);
989 18 : auto dest = set_host_impl(n, op);
990 18 : *dest++ = '[';
991 18 : std::memcpy(dest, ipv6_str.data(), ipn);
992 18 : dest += ipn;
993 18 : std::size_t dzn = 0;
994 18 : if (has_zone_id)
995 : {
996 6 : *dest++ = '%';
997 6 : *dest++ = '2';
998 6 : *dest++ = '5';
999 6 : dzn = detail::re_encode_unsafe(dest, dest + zn, zone_id, unreserved_chars);
1000 : }
1001 18 : *dest++ = ']';
1002 : // ipn + |"["| + |"]"| + (has_zone_id ? |"%"| + zn : 0)
1003 18 : impl_.decoded_[id_host] = ipn + 2 + has_zone_id * (1 + dzn);
1004 18 : impl_.host_type_ = urls::host_type::ipv6;
1005 18 : auto bytes = addr.to_bytes();
1006 18 : std::memcpy(
1007 18 : impl_.ip_addr_,
1008 18 : bytes.data(),
1009 : bytes.size());
1010 18 : }
1011 :
1012 : url_base&
1013 7 : url_base::
1014 : set_host_ipvfuture(
1015 : core::string_view s)
1016 : {
1017 7 : op_t op(*this, &s);
1018 : // validate
1019 8 : grammar::parse(s,
1020 : detail::ipvfuture_rule
1021 8 : ).value(BOOST_URL_POS);
1022 6 : auto dest = set_host_impl(
1023 6 : s.size() + 2, op);
1024 6 : *dest++ = '[';
1025 6 : dest += s.copy(dest, s.size());
1026 6 : *dest = ']';
1027 6 : impl_.host_type_ =
1028 : urls::host_type::ipvfuture;
1029 6 : impl_.decoded_[id_host] = s.size() + 2;
1030 6 : return *this;
1031 7 : }
1032 :
1033 : url_base&
1034 4 : url_base::
1035 : set_host_name(
1036 : core::string_view s)
1037 : {
1038 4 : bool is_ipv4 = false;
1039 4 : if(s.size() >= 7) // "0.0.0.0"
1040 : {
1041 : // IPv4-address
1042 3 : if(parse_ipv4_address(s).has_value())
1043 1 : is_ipv4 = true;
1044 : }
1045 4 : auto allowed = detail::host_chars;
1046 4 : if(is_ipv4)
1047 1 : allowed = allowed - '.';
1048 :
1049 4 : op_t op(*this, &s);
1050 4 : encoding_opts opt;
1051 4 : auto const n = encoded_size(
1052 : s, allowed, opt);
1053 4 : auto dest = set_host_impl(n, op);
1054 4 : encode_unsafe(
1055 : dest,
1056 : n,
1057 : s,
1058 : allowed,
1059 : opt);
1060 4 : impl_.host_type_ =
1061 : urls::host_type::name;
1062 4 : impl_.decoded_[id_host] = s.size();
1063 4 : return *this;
1064 4 : }
1065 :
1066 : url_base&
1067 4 : url_base::
1068 : set_encoded_host_name(
1069 : pct_string_view s)
1070 : {
1071 4 : bool is_ipv4 = false;
1072 4 : if(s.size() >= 7) // "0.0.0.0"
1073 : {
1074 : // IPv4-address
1075 3 : if(parse_ipv4_address(s).has_value())
1076 1 : is_ipv4 = true;
1077 : }
1078 4 : auto allowed = detail::host_chars;
1079 4 : if(is_ipv4)
1080 1 : allowed = allowed - '.';
1081 :
1082 4 : op_t op(*this, &detail::ref(s));
1083 4 : auto const n = detail::re_encoded_size_unsafe(
1084 : s, allowed);
1085 4 : auto dest = set_host_impl(n, op);
1086 4 : impl_.decoded_[id_host] =
1087 4 : detail::re_encode_unsafe(
1088 : dest,
1089 4 : dest + n,
1090 : s,
1091 : allowed);
1092 4 : BOOST_ASSERT(
1093 : impl_.decoded_[id_host] ==
1094 : s.decoded_size());
1095 4 : impl_.host_type_ =
1096 : urls::host_type::name;
1097 4 : return *this;
1098 4 : }
1099 :
1100 : //------------------------------------------------
1101 :
1102 : url_base&
1103 23 : url_base::
1104 : set_port_number(
1105 : std::uint16_t n)
1106 : {
1107 23 : op_t op(*this);
1108 : auto s =
1109 23 : detail::make_printed(n);
1110 23 : auto dest = set_port_impl(
1111 23 : s.string().size(), op);
1112 23 : std::memcpy(
1113 23 : dest, s.string().data(),
1114 23 : s.string().size());
1115 23 : impl_.port_number_ = n;
1116 23 : return *this;
1117 23 : }
1118 :
1119 : url_base&
1120 90 : url_base::
1121 : set_port(
1122 : core::string_view s)
1123 : {
1124 90 : op_t op(*this, &s);
1125 109 : auto t = grammar::parse(s,
1126 19 : detail::port_rule{}
1127 90 : ).value(BOOST_URL_POS);
1128 : auto dest =
1129 71 : set_port_impl(t.str.size(), op);
1130 71 : std::memcpy(dest,
1131 71 : t.str.data(), t.str.size());
1132 71 : if(t.has_number)
1133 35 : impl_.port_number_ = t.number;
1134 : else
1135 36 : impl_.port_number_ = 0;
1136 71 : return *this;
1137 90 : }
1138 :
1139 : url_base&
1140 25 : url_base::
1141 : remove_port() noexcept
1142 : {
1143 25 : op_t op(*this);
1144 25 : resize_impl(id_port, 0, op);
1145 25 : impl_.port_number_ = 0;
1146 50 : return *this;
1147 25 : }
1148 :
1149 : //------------------------------------------------
1150 : //
1151 : // Compound Fields
1152 : //
1153 : //------------------------------------------------
1154 :
1155 : url_base&
1156 14 : url_base::
1157 : remove_origin()
1158 : {
1159 : // these two calls perform 2 memmoves instead of 1
1160 14 : remove_authority();
1161 14 : remove_scheme();
1162 14 : return *this;
1163 : }
1164 :
1165 : //------------------------------------------------
1166 : //
1167 : // Path
1168 : //
1169 : //------------------------------------------------
1170 :
1171 : bool
1172 50 : url_base::
1173 : set_path_absolute(
1174 : bool absolute)
1175 : {
1176 50 : op_t op(*this);
1177 :
1178 : // check if path empty
1179 50 : if(impl_.len(id_path) == 0)
1180 : {
1181 38 : if(! absolute)
1182 : {
1183 : // already not absolute
1184 32 : return true;
1185 : }
1186 :
1187 : // add '/'
1188 6 : auto dest = resize_impl(
1189 : id_path, 1, op);
1190 6 : *dest = '/';
1191 6 : ++impl_.decoded_[id_path];
1192 6 : return true;
1193 : }
1194 :
1195 : // check if path absolute
1196 12 : if(s_[impl_.offset(id_path)] == '/')
1197 : {
1198 9 : if(absolute)
1199 : {
1200 : // already absolute
1201 2 : return true;
1202 : }
1203 :
1204 11 : if( has_authority() &&
1205 4 : impl_.len(id_path) > 1)
1206 : {
1207 : // can't do it, paths are always
1208 : // absolute when authority present!
1209 2 : return false;
1210 : }
1211 :
1212 5 : auto p = encoded_path();
1213 5 : auto pos = p.find_first_of(":/", 1);
1214 6 : if (pos != core::string_view::npos &&
1215 1 : p[pos] == ':')
1216 : {
1217 : // prepend with .
1218 1 : auto n = impl_.len(id_path);
1219 1 : resize_impl(id_path, n + 1, op);
1220 1 : std::memmove(
1221 2 : s_ + impl_.offset(id_path) + 1,
1222 1 : s_ + impl_.offset(id_path), n);
1223 1 : *(s_ + impl_.offset(id_path)) = '.';
1224 1 : ++impl_.decoded_[id_path];
1225 1 : return true;
1226 : }
1227 :
1228 : // remove '/'
1229 4 : auto n = impl_.len(id_port);
1230 4 : impl_.split(id_port, n + 1);
1231 4 : resize_impl(id_port, n, op);
1232 4 : --impl_.decoded_[id_path];
1233 4 : return true;
1234 : }
1235 :
1236 3 : if(! absolute)
1237 : {
1238 : // already not absolute
1239 1 : return true;
1240 : }
1241 :
1242 : // add '/'
1243 2 : auto n = impl_.len(id_port);
1244 2 : auto dest = resize_impl(
1245 2 : id_port, n + 1, op) + n;
1246 2 : impl_.split(id_port, n);
1247 2 : *dest = '/';
1248 2 : ++impl_.decoded_[id_path];
1249 2 : return true;
1250 50 : }
1251 :
1252 : url_base&
1253 26 : url_base::
1254 : set_path(
1255 : core::string_view s)
1256 : {
1257 26 : op_t op(*this, &s);
1258 26 : encoding_opts opt;
1259 :
1260 : //------------------------------------------------
1261 : //
1262 : // Calculate encoded size
1263 : //
1264 : // - "/"s are not encoded
1265 : // - "%2F"s are not encoded
1266 : //
1267 : // - reserved path chars are re-encoded
1268 : // - colons in first segment might need to be re-encoded
1269 : // - the path might need to receive a prefix
1270 26 : auto const n = encoded_size(
1271 : s, detail::path_chars, opt);
1272 26 : std::size_t n_reencode_colons = 0;
1273 26 : core::string_view first_seg;
1274 26 : if (!has_scheme() &&
1275 41 : !has_authority() &&
1276 15 : !s.starts_with('/'))
1277 : {
1278 : // the first segment with unencoded colons would look
1279 : // like the scheme
1280 6 : first_seg = detail::to_sv(s);
1281 6 : std::size_t p = s.find('/');
1282 6 : if (p != core::string_view::npos)
1283 2 : first_seg = s.substr(0, p);
1284 6 : n_reencode_colons = std::count(
1285 12 : first_seg.begin(), first_seg.end(), ':');
1286 : }
1287 : // the authority can only be followed by an empty or relative path
1288 : // if we have an authority and the path is a non-empty relative path, we
1289 : // add the "/" prefix to make it valid.
1290 : bool make_absolute =
1291 26 : has_authority() &&
1292 31 : !s.starts_with('/') &&
1293 5 : !s.empty();
1294 : // a path starting with "//" might look like the authority.
1295 : // we add a "/." prefix to prevent that
1296 : bool add_dot_segment =
1297 49 : !make_absolute &&
1298 23 : s.starts_with("//");
1299 :
1300 : //------------------------------------------------
1301 : //
1302 : // Re-encode data
1303 : //
1304 52 : auto dest = set_path_impl(
1305 26 : n + make_absolute + 2 * n_reencode_colons + 2 * add_dot_segment, op);
1306 26 : impl_.decoded_[id_path] = 0;
1307 26 : if (!dest)
1308 : {
1309 3 : impl_.nseg_ = 0;
1310 3 : return *this;
1311 : }
1312 23 : if (make_absolute)
1313 : {
1314 3 : *dest++ = '/';
1315 3 : impl_.decoded_[id_path] += 1;
1316 : }
1317 20 : else if (add_dot_segment)
1318 : {
1319 1 : *dest++ = '/';
1320 1 : *dest++ = '.';
1321 1 : impl_.decoded_[id_path] += 2;
1322 : }
1323 46 : dest += encode_unsafe(
1324 : dest,
1325 23 : impl_.get(id_query).data() - dest,
1326 : first_seg,
1327 23 : detail::segment_chars - ':',
1328 : opt);
1329 23 : dest += encode_unsafe(
1330 : dest,
1331 23 : impl_.get(id_query).data() - dest,
1332 : s.substr(first_seg.size()),
1333 : detail::path_chars,
1334 : opt);
1335 23 : impl_.decoded_[id_path] += s.size();
1336 23 : BOOST_ASSERT(!dest || dest == impl_.get(id_query).data());
1337 23 : BOOST_ASSERT(
1338 : impl_.decoded_[id_path] ==
1339 : s.size() + make_absolute + 2 * add_dot_segment);
1340 :
1341 : //------------------------------------------------
1342 : //
1343 : // Update path parameters
1344 : //
1345 : // get the encoded_path with the replacements we applied
1346 23 : if (s == "/")
1347 : {
1348 : // "/" maps to sequence {}
1349 3 : impl_.nseg_ = 0;
1350 : }
1351 20 : else if (!s.empty())
1352 : {
1353 16 : if (s.starts_with("/./"))
1354 1 : s = s.substr(2);
1355 : // count segments as number of '/'s + 1
1356 16 : impl_.nseg_ = std::count(
1357 32 : s.begin() + 1, s.end(), '/') + 1;
1358 : }
1359 : else
1360 : {
1361 : // an empty relative path maps to sequence {}
1362 4 : impl_.nseg_ = 0;
1363 : }
1364 :
1365 23 : check_invariants();
1366 23 : return *this;
1367 26 : }
1368 :
1369 : url_base&
1370 166 : url_base::
1371 : set_encoded_path(
1372 : pct_string_view s)
1373 : {
1374 166 : op_t op(*this, &detail::ref(s));
1375 :
1376 : //------------------------------------------------
1377 : //
1378 : // Calculate re-encoded output size
1379 : //
1380 : // - reserved path chars are re-encoded
1381 : // - colons in first segment might need to be re-encoded
1382 : // - the path might need to receive a prefix
1383 166 : auto const n = detail::re_encoded_size_unsafe(
1384 : s, detail::path_chars);
1385 166 : std::size_t n_reencode_colons = 0;
1386 166 : core::string_view first_seg;
1387 166 : if (!has_scheme() &&
1388 183 : !has_authority() &&
1389 17 : !s.starts_with('/'))
1390 : {
1391 : // the first segment with unencoded colons would look
1392 : // like the scheme
1393 10 : first_seg = detail::to_sv(s);
1394 10 : std::size_t p = s.find('/');
1395 10 : if (p != core::string_view::npos)
1396 5 : first_seg = s.substr(0, p);
1397 10 : n_reencode_colons = std::count(
1398 20 : first_seg.begin(), first_seg.end(), ':');
1399 : }
1400 : // the authority can only be followed by an empty or relative path
1401 : // if we have an authority and the path is a non-empty relative path, we
1402 : // add the "/" prefix to make it valid.
1403 : bool make_absolute =
1404 166 : has_authority() &&
1405 214 : !s.starts_with('/') &&
1406 48 : !s.empty();
1407 : // a path starting with "//" might look like the authority
1408 : // we add a "/." prefix to prevent that
1409 : bool add_dot_segment =
1410 319 : !make_absolute &&
1411 203 : !has_authority() &&
1412 37 : s.starts_with("//");
1413 :
1414 : //------------------------------------------------
1415 : //
1416 : // Re-encode data
1417 : //
1418 332 : auto dest = set_path_impl(
1419 166 : n + make_absolute + 2 * n_reencode_colons + 2 * add_dot_segment, op);
1420 166 : impl_.decoded_[id_path] = 0;
1421 166 : if (!dest)
1422 : {
1423 1 : impl_.nseg_ = 0;
1424 1 : return *this;
1425 : }
1426 165 : if (make_absolute)
1427 : {
1428 13 : *dest++ = '/';
1429 13 : impl_.decoded_[id_path] += 1;
1430 : }
1431 152 : else if (add_dot_segment)
1432 : {
1433 3 : *dest++ = '/';
1434 3 : *dest++ = '.';
1435 3 : impl_.decoded_[id_path] += 2;
1436 : }
1437 165 : impl_.decoded_[id_path] +=
1438 165 : detail::re_encode_unsafe(
1439 : dest,
1440 165 : impl_.get(id_query).data(),
1441 : first_seg,
1442 165 : detail::segment_chars - ':');
1443 165 : impl_.decoded_[id_path] +=
1444 330 : detail::re_encode_unsafe(
1445 : dest,
1446 165 : impl_.get(id_query).data(),
1447 : s.substr(first_seg.size()),
1448 : detail::path_chars);
1449 165 : BOOST_ASSERT(dest == impl_.get(id_query).data());
1450 165 : BOOST_ASSERT(
1451 : impl_.decoded_[id_path] ==
1452 : s.decoded_size() + make_absolute + 2 * add_dot_segment);
1453 :
1454 : //------------------------------------------------
1455 : //
1456 : // Update path parameters
1457 : //
1458 : // get the encoded_path with the replacements we applied
1459 165 : if (s == "/")
1460 : {
1461 : // "/" maps to sequence {}
1462 16 : impl_.nseg_ = 0;
1463 : }
1464 149 : else if (!s.empty())
1465 : {
1466 112 : if (s.starts_with("/./"))
1467 7 : s = s.substr(2);
1468 : // count segments as number of '/'s + 1
1469 112 : impl_.nseg_ = std::count(
1470 224 : s.begin() + 1, s.end(), '/') + 1;
1471 : }
1472 : else
1473 : {
1474 : // an empty relative path maps to sequence {}
1475 37 : impl_.nseg_ = 0;
1476 : }
1477 :
1478 165 : check_invariants();
1479 165 : return *this;
1480 166 : }
1481 :
1482 : segments_ref
1483 266 : url_base::
1484 : segments() noexcept
1485 : {
1486 266 : return {*this};
1487 : }
1488 :
1489 : segments_encoded_ref
1490 462 : url_base::
1491 : encoded_segments() noexcept
1492 : {
1493 462 : return {*this};
1494 : }
1495 :
1496 : //------------------------------------------------
1497 : //
1498 : // Query
1499 : //
1500 : //------------------------------------------------
1501 :
1502 : url_base&
1503 10 : url_base::
1504 : set_query(
1505 : core::string_view s)
1506 : {
1507 10 : edit_params(
1508 10 : detail::params_iter_impl(impl_),
1509 10 : detail::params_iter_impl(impl_, 0),
1510 20 : detail::query_string_iter(s, true));
1511 10 : return *this;
1512 : }
1513 :
1514 : url_base&
1515 50 : url_base::
1516 : set_encoded_query(
1517 : pct_string_view s)
1518 : {
1519 50 : op_t op(*this);
1520 50 : std::size_t n = 0; // encoded size
1521 50 : std::size_t nparam = 1; // param count
1522 50 : auto const end = s.end();
1523 50 : auto p = s.begin();
1524 :
1525 : // measure
1526 226 : while(p != end)
1527 : {
1528 176 : if(*p == '&')
1529 : {
1530 3 : ++p;
1531 3 : ++n;
1532 3 : ++nparam;
1533 : }
1534 173 : else if(*p != '%')
1535 : {
1536 165 : if(detail::query_chars(*p))
1537 162 : n += 1; // allowed
1538 : else
1539 3 : n += 3; // escaped
1540 165 : ++p;
1541 : }
1542 : else
1543 : {
1544 : // escape
1545 8 : n += 3;
1546 8 : p += 3;
1547 : }
1548 : }
1549 :
1550 : // resize
1551 50 : auto dest = resize_impl(
1552 50 : id_query, n + 1, op);
1553 50 : *dest++ = '?';
1554 :
1555 : // encode
1556 50 : impl_.decoded_[id_query] =
1557 50 : detail::re_encode_unsafe(
1558 : dest,
1559 50 : dest + n,
1560 : s,
1561 : detail::query_chars);
1562 50 : BOOST_ASSERT(
1563 : impl_.decoded_[id_query] ==
1564 : s.decoded_size());
1565 50 : impl_.nparam_ = nparam;
1566 50 : return *this;
1567 50 : }
1568 :
1569 : params_ref
1570 93 : url_base::
1571 : params() noexcept
1572 : {
1573 : return params_ref(
1574 : *this,
1575 : encoding_opts{
1576 93 : true, false, false});
1577 : }
1578 :
1579 : params_ref
1580 4 : url_base::
1581 : params(encoding_opts opt) noexcept
1582 : {
1583 4 : return {*this, opt};
1584 : }
1585 :
1586 : params_encoded_ref
1587 77 : url_base::
1588 : encoded_params() noexcept
1589 : {
1590 77 : return {*this};
1591 : }
1592 :
1593 : url_base&
1594 1 : url_base::
1595 : set_params(
1596 : std::initializer_list<param_view> ps,
1597 : encoding_opts opts) noexcept
1598 : {
1599 1 : params(opts).assign(ps);
1600 1 : return *this;
1601 : }
1602 :
1603 : url_base&
1604 1 : url_base::
1605 : set_encoded_params( std::initializer_list< param_pct_view > ps ) noexcept
1606 : {
1607 1 : encoded_params().assign(ps);
1608 1 : return *this;
1609 : }
1610 :
1611 : url_base&
1612 224 : url_base::
1613 : remove_query() noexcept
1614 : {
1615 224 : op_t op(*this);
1616 224 : resize_impl(id_query, 0, op);
1617 224 : impl_.nparam_ = 0;
1618 224 : impl_.decoded_[id_query] = 0;
1619 448 : return *this;
1620 224 : }
1621 :
1622 : //------------------------------------------------
1623 : //
1624 : // Fragment
1625 : //
1626 : //------------------------------------------------
1627 :
1628 : url_base&
1629 232 : url_base::
1630 : remove_fragment() noexcept
1631 : {
1632 232 : op_t op(*this);
1633 232 : resize_impl(id_frag, 0, op);
1634 232 : impl_.decoded_[id_frag] = 0;
1635 464 : return *this;
1636 232 : }
1637 :
1638 : url_base&
1639 7 : url_base::
1640 : set_fragment(core::string_view s)
1641 : {
1642 7 : op_t op(*this, &s);
1643 7 : encoding_opts opt;
1644 7 : auto const n = encoded_size(
1645 : s,
1646 : detail::fragment_chars,
1647 : opt);
1648 7 : auto dest = resize_impl(
1649 : id_frag, n + 1, op);
1650 7 : *dest++ = '#';
1651 7 : encode_unsafe(
1652 : dest,
1653 : n,
1654 : s,
1655 : detail::fragment_chars,
1656 : opt);
1657 7 : impl_.decoded_[id_frag] = s.size();
1658 7 : return *this;
1659 7 : }
1660 :
1661 : url_base&
1662 57 : url_base::
1663 : set_encoded_fragment(
1664 : pct_string_view s)
1665 : {
1666 57 : op_t op(*this, &detail::ref(s));
1667 : auto const n =
1668 57 : detail::re_encoded_size_unsafe(
1669 : s,
1670 : detail::fragment_chars);
1671 57 : auto dest = resize_impl(
1672 57 : id_frag, n + 1, op);
1673 57 : *dest++ = '#';
1674 57 : impl_.decoded_[id_frag] =
1675 57 : detail::re_encode_unsafe(
1676 : dest,
1677 57 : dest + n,
1678 : s,
1679 : detail::fragment_chars);
1680 57 : BOOST_ASSERT(
1681 : impl_.decoded_[id_frag] ==
1682 : s.decoded_size());
1683 57 : return *this;
1684 57 : }
1685 :
1686 : //------------------------------------------------
1687 : //
1688 : // Resolution
1689 : //
1690 : //------------------------------------------------
1691 :
1692 : system::result<void>
1693 465 : url_base::
1694 : resolve(
1695 : url_view_base const& ref)
1696 : {
1697 468 : if (this == &ref &&
1698 3 : has_scheme())
1699 : {
1700 2 : normalize_path();
1701 2 : return {};
1702 : }
1703 :
1704 463 : if(! has_scheme())
1705 : {
1706 2 : BOOST_URL_RETURN_EC(error::not_a_base);
1707 : }
1708 :
1709 461 : op_t op(*this);
1710 :
1711 : //
1712 : // 5.2.2. Transform References
1713 : // https://datatracker.ietf.org/doc/html/rfc3986#section-5.2.2
1714 : //
1715 :
1716 722 : if( ref.has_scheme() &&
1717 261 : ref.scheme() != scheme())
1718 : {
1719 198 : reserve_impl(ref.size(), op);
1720 198 : copy(ref);
1721 198 : normalize_path();
1722 198 : return {};
1723 : }
1724 263 : if(ref.has_authority())
1725 : {
1726 70 : reserve_impl(
1727 70 : impl_.offset(id_user) + ref.size(), op);
1728 70 : set_encoded_authority(
1729 : ref.encoded_authority());
1730 70 : set_encoded_path(
1731 : ref.encoded_path());
1732 70 : if (ref.encoded_path().empty())
1733 31 : set_path_absolute(false);
1734 : else
1735 39 : normalize_path();
1736 70 : if(ref.has_query())
1737 5 : set_encoded_query(
1738 : ref.encoded_query());
1739 : else
1740 65 : remove_query();
1741 70 : if(ref.has_fragment())
1742 5 : set_encoded_fragment(
1743 : ref.encoded_fragment());
1744 : else
1745 65 : remove_fragment();
1746 70 : return {};
1747 : }
1748 193 : if(ref.encoded_path().empty())
1749 : {
1750 33 : reserve_impl(
1751 33 : impl_.offset(id_query) +
1752 33 : ref.size(), op);
1753 33 : normalize_path();
1754 33 : if(ref.has_query())
1755 : {
1756 11 : set_encoded_query(
1757 : ref.encoded_query());
1758 : }
1759 33 : if(ref.has_fragment())
1760 18 : set_encoded_fragment(
1761 : ref.encoded_fragment());
1762 : else
1763 15 : remove_fragment();
1764 33 : return {};
1765 : }
1766 160 : if(ref.is_path_absolute())
1767 : {
1768 37 : reserve_impl(
1769 37 : impl_.offset(id_path) +
1770 37 : ref.size(), op);
1771 37 : set_encoded_path(
1772 : ref.encoded_path());
1773 37 : normalize_path();
1774 37 : if(ref.has_query())
1775 3 : set_encoded_query(
1776 : ref.encoded_query());
1777 : else
1778 34 : remove_query();
1779 37 : if(ref.has_fragment())
1780 2 : set_encoded_fragment(
1781 : ref.encoded_fragment());
1782 : else
1783 35 : remove_fragment();
1784 37 : return {};
1785 : }
1786 : // General case: ref is relative path
1787 123 : reserve_impl(
1788 123 : impl_.offset(id_query) +
1789 123 : ref.size(), op);
1790 : // 5.2.3. Merge Paths
1791 123 : auto es = encoded_segments();
1792 123 : if(es.size() > 0)
1793 : {
1794 118 : es.pop_back();
1795 : }
1796 246 : es.insert(es.end(),
1797 123 : ref.encoded_segments().begin(),
1798 123 : ref.encoded_segments().end());
1799 123 : normalize_path();
1800 123 : if(ref.has_query())
1801 10 : set_encoded_query(
1802 : ref.encoded_query());
1803 : else
1804 113 : remove_query();
1805 123 : if(ref.has_fragment())
1806 10 : set_encoded_fragment(
1807 : ref.encoded_fragment());
1808 : else
1809 113 : remove_fragment();
1810 123 : return {};
1811 461 : }
1812 :
1813 : //------------------------------------------------
1814 : //
1815 : // Normalization
1816 : //
1817 : //------------------------------------------------
1818 :
1819 : template <
1820 : class AllowedCharset,
1821 : class IgnoredCharset>
1822 : void
1823 1929 : url_base::
1824 : normalize_octets_impl(
1825 : int id,
1826 : AllowedCharset const& allowed,
1827 : IgnoredCharset const& ignored,
1828 : op_t& op) noexcept
1829 : {
1830 1929 : char* it = s_ + impl_.offset(id);
1831 1929 : char* end = s_ + impl_.offset(id + 1);
1832 1929 : char d = 0;
1833 1929 : char* dest = it;
1834 10668 : while (it < end)
1835 : {
1836 8739 : if (*it != '%')
1837 : {
1838 8621 : *dest = *it;
1839 8621 : ++it;
1840 8621 : ++dest;
1841 8621 : continue;
1842 : }
1843 118 : BOOST_ASSERT(end - it >= 3);
1844 :
1845 : // decode unreserved octets
1846 118 : d = detail::decode_one(it + 1);
1847 202 : if (allowed(d) &&
1848 84 : !ignored(d))
1849 : {
1850 76 : *dest = d;
1851 76 : it += 3;
1852 76 : ++dest;
1853 76 : continue;
1854 : }
1855 :
1856 : // uppercase percent-encoding triplets
1857 42 : *dest++ = '%';
1858 42 : ++it;
1859 42 : *dest++ = grammar::to_upper(*it++);
1860 42 : *dest++ = grammar::to_upper(*it++);
1861 : }
1862 1929 : if (it != dest)
1863 : {
1864 24 : auto diff = it - dest;
1865 24 : auto n = impl_.len(id) - diff;
1866 24 : shrink_impl(id, n, op);
1867 24 : s_[size()] = '\0';
1868 : }
1869 1929 : }
1870 :
1871 : template<class CharSet>
1872 : void
1873 1889 : url_base::
1874 : normalize_octets_impl(
1875 : int idx,
1876 : CharSet const& allowed,
1877 : op_t& op) noexcept
1878 : {
1879 1889 : return normalize_octets_impl(
1880 1889 : idx, allowed, detail::empty_chars, op);
1881 : }
1882 :
1883 : url_base&
1884 39 : url_base::
1885 : normalize_scheme()
1886 : {
1887 39 : to_lower_impl(id_scheme);
1888 39 : return *this;
1889 : }
1890 :
1891 : url_base&
1892 384 : url_base::
1893 : normalize_authority()
1894 : {
1895 384 : op_t op(*this);
1896 :
1897 : // normalize host
1898 384 : if (host_type() == urls::host_type::name)
1899 : {
1900 248 : normalize_octets_impl(
1901 : id_host,
1902 : detail::reg_name_chars, op);
1903 : }
1904 384 : decoded_to_lower_impl(id_host);
1905 :
1906 : // normalize password
1907 384 : normalize_octets_impl(id_pass, detail::password_chars, op);
1908 :
1909 : // normalize user
1910 384 : normalize_octets_impl(id_user, detail::user_chars, op);
1911 768 : return *this;
1912 384 : }
1913 :
1914 : url_base&
1915 836 : url_base::
1916 : normalize_path()
1917 : {
1918 836 : op_t op(*this);
1919 836 : normalize_octets_impl(id_path, detail::segment_chars, op);
1920 836 : core::string_view p = impl_.get(id_path);
1921 836 : char* p_dest = s_ + impl_.offset(id_path);
1922 836 : char* p_end = s_ + impl_.offset(id_path + 1);
1923 836 : auto pn = p.size();
1924 836 : auto skip_dot = 0;
1925 836 : bool encode_colons = false;
1926 836 : core::string_view first_seg;
1927 :
1928 : //------------------------------------------------
1929 : //
1930 : // Determine unnecessary initial dot segments to skip and
1931 : // if we need to encode colons in the first segment
1932 : //
1933 836 : if (
1934 1100 : !has_authority() &&
1935 264 : p.starts_with("/./"))
1936 : {
1937 : // check if removing the "/./" would result in "//"
1938 : // ex: "/.//", "/././/", "/././/", ...
1939 14 : skip_dot = 2;
1940 15 : while (p.substr(skip_dot, 3).starts_with("/./"))
1941 1 : skip_dot += 2;
1942 14 : if (p.substr(skip_dot).starts_with("//"))
1943 11 : skip_dot = 2;
1944 : else
1945 3 : skip_dot = 0;
1946 : }
1947 822 : else if (
1948 852 : !has_scheme() &&
1949 30 : !has_authority())
1950 : {
1951 30 : if (p.starts_with("./"))
1952 : {
1953 : // check if removing the "./" would result in "//"
1954 : // ex: ".//", "././/", "././/", ...
1955 7 : skip_dot = 1;
1956 10 : while (p.substr(skip_dot, 3).starts_with("/./"))
1957 3 : skip_dot += 2;
1958 7 : if (p.substr(skip_dot).starts_with("//"))
1959 2 : skip_dot = 2;
1960 : else
1961 5 : skip_dot = 0;
1962 :
1963 7 : if ( !skip_dot )
1964 : {
1965 : // check if removing "./"s would leave us
1966 : // a first segment with an ambiguous ":"
1967 5 : first_seg = p.substr(2);
1968 7 : while (first_seg.starts_with("./"))
1969 2 : first_seg = first_seg.substr(2);
1970 5 : auto i = first_seg.find('/');
1971 5 : if (i != core::string_view::npos)
1972 1 : first_seg = first_seg.substr(0, i);
1973 5 : encode_colons = first_seg.contains(':');
1974 : }
1975 : }
1976 : else
1977 : {
1978 : // check if normalize_octets_impl
1979 : // didn't already create a ":"
1980 : // in the first segment
1981 23 : first_seg = p;
1982 23 : auto i = first_seg.find('/');
1983 23 : if (i != core::string_view::npos)
1984 17 : first_seg = p.substr(0, i);
1985 23 : encode_colons = first_seg.contains(':');
1986 : }
1987 : }
1988 :
1989 : //------------------------------------------------
1990 : //
1991 : // Encode colons in the first segment
1992 : //
1993 836 : if (encode_colons)
1994 : {
1995 : // prepend with "./"
1996 : // (resize_impl never throws)
1997 : auto cn =
1998 5 : std::count(
1999 : first_seg.begin(),
2000 : first_seg.end(),
2001 5 : ':');
2002 5 : resize_impl(
2003 5 : id_path, pn + (2 * cn), op);
2004 : // move the 2nd, 3rd, ... segments
2005 5 : auto begin = s_ + impl_.offset(id_path);
2006 5 : auto it = begin;
2007 5 : auto end = begin + pn;
2008 11 : while (core::string_view(it, 2) == "./")
2009 6 : it += 2;
2010 57 : while (*it != '/' &&
2011 : it != end)
2012 52 : ++it;
2013 : // we don't need op here because this is
2014 : // an internal operation
2015 5 : std::memmove(it + (2 * cn), it, end - it);
2016 :
2017 : // move 1st segment
2018 5 : auto src = s_ + impl_.offset(id_path) + pn;
2019 5 : auto dest = s_ + impl_.offset(id_query);
2020 5 : src -= end - it;
2021 5 : dest -= end - it;
2022 5 : pn -= end - it;
2023 : do {
2024 64 : --src;
2025 64 : --dest;
2026 64 : if (*src != ':')
2027 : {
2028 57 : *dest = *src;
2029 : }
2030 : else
2031 : {
2032 : // use uppercase as required by
2033 : // syntax-based normalization
2034 7 : *dest-- = 'A';
2035 7 : *dest-- = '3';
2036 7 : *dest = '%';
2037 : }
2038 64 : --pn;
2039 64 : } while (pn);
2040 5 : skip_dot = 0;
2041 5 : p = impl_.get(id_path);
2042 5 : pn = p.size();
2043 5 : p_dest = s_ + impl_.offset(id_path);
2044 5 : p_end = s_ + impl_.offset(id_path + 1);
2045 : }
2046 :
2047 : //------------------------------------------------
2048 : //
2049 : // Remove "." and ".." segments
2050 : //
2051 836 : p.remove_prefix(skip_dot);
2052 836 : p_dest += skip_dot;
2053 836 : auto n = detail::remove_dot_segments(
2054 : p_dest, p_end, p);
2055 :
2056 : //------------------------------------------------
2057 : //
2058 : // Update path parameters
2059 : //
2060 836 : if (n != pn)
2061 : {
2062 134 : BOOST_ASSERT(n < pn);
2063 134 : shrink_impl(id_path, n + skip_dot, op);
2064 134 : p = encoded_path();
2065 134 : if (p == "/")
2066 9 : impl_.nseg_ = 0;
2067 125 : else if (!p.empty())
2068 123 : impl_.nseg_ = std::count(
2069 246 : p.begin() + 1, p.end(), '/') + 1;
2070 : else
2071 2 : impl_.nseg_ = 0;
2072 134 : impl_.decoded_[id_path] =
2073 134 : detail::decode_bytes_unsafe(impl_.get(id_path));
2074 : }
2075 836 : return *this;
2076 836 : }
2077 :
2078 : url_base&
2079 40 : url_base::
2080 : normalize_query()
2081 : {
2082 40 : op_t op(*this);
2083 40 : normalize_octets_impl(
2084 : id_query,
2085 : detail::query_chars,
2086 : detail::query_ignore_chars,
2087 : op);
2088 80 : return *this;
2089 40 : }
2090 :
2091 : url_base&
2092 37 : url_base::
2093 : normalize_fragment()
2094 : {
2095 37 : op_t op(*this);
2096 37 : normalize_octets_impl(
2097 : id_frag, detail::fragment_chars, op);
2098 74 : return *this;
2099 37 : }
2100 :
2101 : url_base&
2102 37 : url_base::
2103 : normalize()
2104 : {
2105 37 : normalize_fragment();
2106 37 : normalize_query();
2107 37 : normalize_path();
2108 37 : normalize_authority();
2109 37 : normalize_scheme();
2110 37 : return *this;
2111 : }
2112 :
2113 : //------------------------------------------------
2114 : //
2115 : // Implementation
2116 : //
2117 : //------------------------------------------------
2118 :
2119 : void
2120 17957 : url_base::
2121 : check_invariants() const noexcept
2122 : {
2123 17957 : BOOST_ASSERT(pi_);
2124 17957 : BOOST_ASSERT(
2125 : impl_.len(id_scheme) == 0 ||
2126 : impl_.get(id_scheme).ends_with(':'));
2127 17957 : BOOST_ASSERT(
2128 : impl_.len(id_user) == 0 ||
2129 : impl_.get(id_user).starts_with("//"));
2130 17957 : BOOST_ASSERT(
2131 : impl_.len(id_pass) == 0 ||
2132 : impl_.get(id_user).starts_with("//"));
2133 17957 : BOOST_ASSERT(
2134 : impl_.len(id_pass) == 0 ||
2135 : (impl_.len(id_pass) == 1 &&
2136 : impl_.get(id_pass) == "@") ||
2137 : (impl_.len(id_pass) > 1 &&
2138 : impl_.get(id_pass).starts_with(':') &&
2139 : impl_.get(id_pass).ends_with('@')));
2140 17957 : BOOST_ASSERT(
2141 : impl_.len(id_user, id_path) == 0 ||
2142 : impl_.get(id_user).starts_with("//"));
2143 17957 : BOOST_ASSERT(impl_.decoded_[id_path] >=
2144 : ((impl_.len(id_path) + 2) / 3));
2145 17957 : BOOST_ASSERT(
2146 : impl_.len(id_port) == 0 ||
2147 : impl_.get(id_port).starts_with(':'));
2148 17957 : BOOST_ASSERT(
2149 : impl_.len(id_query) == 0 ||
2150 : impl_.get(id_query).starts_with('?'));
2151 17957 : BOOST_ASSERT(
2152 : (impl_.len(id_query) == 0 && impl_.nparam_ == 0) ||
2153 : (impl_.len(id_query) > 0 && impl_.nparam_ > 0));
2154 17957 : BOOST_ASSERT(
2155 : impl_.len(id_frag) == 0 ||
2156 : impl_.get(id_frag).starts_with('#'));
2157 17957 : BOOST_ASSERT(c_str()[size()] == '\0');
2158 17957 : }
2159 :
2160 : char*
2161 1551 : url_base::
2162 : resize_impl(
2163 : int id,
2164 : std::size_t new_size,
2165 : op_t& op)
2166 : {
2167 1551 : return resize_impl(
2168 1551 : id, id + 1, new_size, op);
2169 : }
2170 :
2171 : char*
2172 1820 : url_base::
2173 : resize_impl(
2174 : int first,
2175 : int last,
2176 : std::size_t new_len,
2177 : op_t& op)
2178 : {
2179 1820 : auto const n0 = impl_.len(first, last);
2180 1820 : if(new_len == 0 && n0 == 0)
2181 386 : return s_ + impl_.offset(first);
2182 1434 : if(new_len <= n0)
2183 515 : return shrink_impl(
2184 515 : first, last, new_len, op);
2185 :
2186 : // growing
2187 919 : std::size_t n = new_len - n0;
2188 919 : reserve_impl(size() + n, op);
2189 : auto const pos =
2190 919 : impl_.offset(last);
2191 : // adjust chars
2192 919 : op.move(
2193 919 : s_ + pos + n,
2194 919 : s_ + pos,
2195 919 : impl_.offset(id_end) -
2196 : pos + 1);
2197 : // collapse (first, last)
2198 919 : impl_.collapse(first, last,
2199 919 : impl_.offset(last) + n);
2200 : // shift (last, end) right
2201 919 : impl_.adjust_right(last, id_end, n);
2202 919 : s_[size()] = '\0';
2203 919 : return s_ + impl_.offset(first);
2204 : }
2205 :
2206 : char*
2207 158 : url_base::
2208 : shrink_impl(
2209 : int id,
2210 : std::size_t new_size,
2211 : op_t& op)
2212 : {
2213 158 : return shrink_impl(
2214 158 : id, id + 1, new_size, op);
2215 : }
2216 :
2217 : char*
2218 673 : url_base::
2219 : shrink_impl(
2220 : int first,
2221 : int last,
2222 : std::size_t new_len,
2223 : op_t& op)
2224 : {
2225 : // shrinking
2226 673 : auto const n0 = impl_.len(first, last);
2227 673 : BOOST_ASSERT(new_len <= n0);
2228 673 : std::size_t n = n0 - new_len;
2229 : auto const pos =
2230 673 : impl_.offset(last);
2231 : // adjust chars
2232 673 : op.move(
2233 673 : s_ + pos - n,
2234 673 : s_ + pos,
2235 673 : impl_.offset(
2236 673 : id_end) - pos + 1);
2237 : // collapse (first, last)
2238 673 : impl_.collapse(first, last,
2239 673 : impl_.offset(last) - n);
2240 : // shift (last, end) left
2241 673 : impl_.adjust_left(last, id_end, n);
2242 673 : s_[size()] = '\0';
2243 673 : return s_ + impl_.offset(first);
2244 : }
2245 :
2246 : //------------------------------------------------
2247 :
2248 : void
2249 61 : url_base::
2250 : set_scheme_impl(
2251 : core::string_view s,
2252 : urls::scheme id)
2253 : {
2254 61 : op_t op(*this, &s);
2255 61 : check_invariants();
2256 74 : grammar::parse(
2257 13 : s, detail::scheme_rule()
2258 74 : ).value(BOOST_URL_POS);
2259 48 : auto const n = s.size();
2260 48 : auto const p = impl_.offset(id_path);
2261 :
2262 : // check for "./" prefix
2263 : bool const has_dot =
2264 144 : [this, p]
2265 : {
2266 48 : if(impl_.nseg_ == 0)
2267 30 : return false;
2268 18 : if(first_segment().size() < 2)
2269 9 : return false;
2270 9 : auto const src = s_ + p;
2271 9 : if(src[0] != '.')
2272 6 : return false;
2273 3 : if(src[1] != '/')
2274 0 : return false;
2275 3 : return true;
2276 48 : }();
2277 :
2278 : // Remove "./"
2279 48 : if(has_dot)
2280 : {
2281 : // do this first, for
2282 : // strong exception safety
2283 6 : reserve_impl(
2284 3 : size() + n + 1 - 2, op);
2285 3 : op.move(
2286 3 : s_ + p,
2287 3 : s_ + p + 2,
2288 3 : size() + 1 -
2289 : (p + 2));
2290 3 : impl_.set_size(
2291 : id_path,
2292 3 : impl_.len(id_path) - 2);
2293 3 : s_[size()] = '\0';
2294 : }
2295 :
2296 48 : auto dest = resize_impl(
2297 : id_scheme, n + 1, op);
2298 48 : s.copy(dest, n);
2299 48 : dest[n] = ':';
2300 48 : impl_.scheme_ = id;
2301 48 : check_invariants();
2302 61 : }
2303 :
2304 : char*
2305 101 : url_base::
2306 : set_user_impl(
2307 : std::size_t n,
2308 : op_t& op)
2309 : {
2310 101 : check_invariants();
2311 101 : if(impl_.len(id_pass) != 0)
2312 : {
2313 : // keep "//"
2314 50 : auto dest = resize_impl(
2315 : id_user, 2 + n, op);
2316 50 : check_invariants();
2317 50 : return dest + 2;
2318 : }
2319 : // add authority
2320 : bool const make_absolute =
2321 91 : !is_path_absolute() &&
2322 40 : !impl_.get(id_path).empty();
2323 102 : auto dest = resize_impl(
2324 51 : id_user, 2 + n + 1 + make_absolute, op);
2325 51 : impl_.split(id_user, 2 + n);
2326 51 : dest[0] = '/';
2327 51 : dest[1] = '/';
2328 51 : dest[2 + n] = '@';
2329 51 : if (make_absolute)
2330 : {
2331 4 : impl_.split(id_pass, 1);
2332 4 : impl_.split(id_host, 0);
2333 4 : impl_.split(id_port, 0);
2334 4 : dest[3 + n] = '/';
2335 : }
2336 51 : check_invariants();
2337 51 : return dest + 2;
2338 : }
2339 :
2340 : char*
2341 82 : url_base::
2342 : set_password_impl(
2343 : std::size_t n,
2344 : op_t& op)
2345 : {
2346 82 : check_invariants();
2347 82 : if(impl_.len(id_user) != 0)
2348 : {
2349 : // already have authority
2350 66 : auto const dest = resize_impl(
2351 : id_pass, 1 + n + 1, op);
2352 66 : dest[0] = ':';
2353 66 : dest[n + 1] = '@';
2354 66 : check_invariants();
2355 66 : return dest + 1;
2356 : }
2357 : // add authority
2358 : bool const make_absolute =
2359 25 : !is_path_absolute() &&
2360 9 : !impl_.get(id_path).empty();
2361 : auto const dest =
2362 32 : resize_impl(
2363 : id_user, id_host,
2364 16 : 2 + 1 + n + 1 + make_absolute, op);
2365 16 : impl_.split(id_user, 2);
2366 16 : dest[0] = '/';
2367 16 : dest[1] = '/';
2368 16 : dest[2] = ':';
2369 16 : dest[2 + n + 1] = '@';
2370 16 : if (make_absolute)
2371 : {
2372 2 : impl_.split(id_pass, 2 + n);
2373 2 : impl_.split(id_host, 0);
2374 2 : impl_.split(id_port, 0);
2375 2 : dest[4 + n] = '/';
2376 : }
2377 16 : check_invariants();
2378 16 : return dest + 3;
2379 : }
2380 :
2381 : char*
2382 99 : url_base::
2383 : set_userinfo_impl(
2384 : std::size_t n,
2385 : op_t& op)
2386 : {
2387 : // "//" {dest} "@"
2388 99 : check_invariants();
2389 : bool const make_absolute =
2390 180 : !is_path_absolute() &&
2391 81 : !impl_.get(id_path).empty();
2392 198 : auto dest = resize_impl(
2393 99 : id_user, id_host, n + 3 + make_absolute, op);
2394 99 : impl_.split(id_user, n + 2);
2395 99 : dest[0] = '/';
2396 99 : dest[1] = '/';
2397 99 : dest[n + 2] = '@';
2398 99 : if (make_absolute)
2399 : {
2400 2 : impl_.split(id_pass, 1);
2401 2 : impl_.split(id_host, 0);
2402 2 : impl_.split(id_port, 0);
2403 2 : dest[3 + n] = '/';
2404 : }
2405 99 : check_invariants();
2406 99 : return dest + 2;
2407 : }
2408 :
2409 : char*
2410 219 : url_base::
2411 : set_host_impl(
2412 : std::size_t n,
2413 : op_t& op)
2414 : {
2415 219 : check_invariants();
2416 219 : if(impl_.len(id_user) == 0)
2417 : {
2418 : // add authority
2419 : bool make_absolute =
2420 192 : !is_path_absolute() &&
2421 94 : impl_.len(id_path) != 0;
2422 98 : auto pn = impl_.len(id_path);
2423 196 : auto dest = resize_impl(
2424 98 : id_user, n + 2 + make_absolute, op);
2425 98 : impl_.split(id_user, 2);
2426 98 : impl_.split(id_pass, 0);
2427 98 : impl_.split(id_host, n);
2428 98 : impl_.split(id_port, 0);
2429 98 : impl_.split(id_path, pn + make_absolute);
2430 98 : if (make_absolute)
2431 : {
2432 7 : dest[n + 2] = '/';
2433 7 : ++impl_.decoded_[id_path];
2434 : }
2435 98 : dest[0] = '/';
2436 98 : dest[1] = '/';
2437 98 : check_invariants();
2438 98 : return dest + 2;
2439 : }
2440 : // already have authority
2441 121 : auto const dest = resize_impl(
2442 : id_host, n, op);
2443 121 : check_invariants();
2444 121 : return dest;
2445 : }
2446 :
2447 : char*
2448 107 : url_base::
2449 : set_port_impl(
2450 : std::size_t n,
2451 : op_t& op)
2452 : {
2453 107 : check_invariants();
2454 107 : if(impl_.len(id_user) != 0)
2455 : {
2456 : // authority exists
2457 85 : auto dest = resize_impl(
2458 : id_port, n + 1, op);
2459 85 : dest[0] = ':';
2460 85 : check_invariants();
2461 85 : return dest + 1;
2462 : }
2463 : bool make_absolute =
2464 38 : !is_path_absolute() &&
2465 16 : impl_.len(id_path) != 0;
2466 44 : auto dest = resize_impl(
2467 22 : id_user, 3 + n + make_absolute, op);
2468 22 : impl_.split(id_user, 2);
2469 22 : impl_.split(id_pass, 0);
2470 22 : impl_.split(id_host, 0);
2471 22 : dest[0] = '/';
2472 22 : dest[1] = '/';
2473 22 : dest[2] = ':';
2474 22 : if (make_absolute)
2475 : {
2476 2 : impl_.split(id_port, n + 1);
2477 2 : dest[n + 3] = '/';
2478 2 : ++impl_.decoded_[id_path];
2479 : }
2480 22 : check_invariants();
2481 22 : return dest + 3;
2482 : }
2483 :
2484 : char*
2485 192 : url_base::
2486 : set_path_impl(
2487 : std::size_t n,
2488 : op_t& op)
2489 : {
2490 192 : check_invariants();
2491 192 : auto const dest = resize_impl(
2492 : id_path, n, op);
2493 192 : return dest;
2494 : }
2495 :
2496 :
2497 : //------------------------------------------------
2498 :
2499 : // return the first segment of the path.
2500 : // this is needed for some algorithms.
2501 : core::string_view
2502 45 : url_base::
2503 : first_segment() const noexcept
2504 : {
2505 45 : if(impl_.nseg_ == 0)
2506 7 : return {};
2507 38 : auto const p0 = impl_.cs_ +
2508 38 : impl_.offset(id_path) +
2509 38 : detail::path_prefix(
2510 38 : impl_.get(id_path));
2511 38 : auto const end = impl_.cs_ +
2512 38 : impl_.offset(id_query);
2513 38 : if(impl_.nseg_ == 1)
2514 42 : return core::string_view(
2515 21 : p0, end - p0);
2516 17 : auto p = p0;
2517 39 : while(*p != '/')
2518 22 : ++p;
2519 17 : BOOST_ASSERT(p < end);
2520 17 : return core::string_view(p0, p - p0);
2521 : }
2522 :
2523 : detail::segments_iter_impl
2524 597 : url_base::
2525 : edit_segments(
2526 : detail::segments_iter_impl const& it0,
2527 : detail::segments_iter_impl const& it1,
2528 : detail::any_segments_iter&& src,
2529 : // -1 = preserve
2530 : // 0 = make relative (can fail)
2531 : // 1 = make absolute
2532 : int absolute)
2533 : {
2534 : // Iterator doesn't belong to this url
2535 597 : BOOST_ASSERT(it0.ref.alias_of(impl_));
2536 :
2537 : // Iterator doesn't belong to this url
2538 597 : BOOST_ASSERT(it1.ref.alias_of(impl_));
2539 :
2540 : // Iterator is in the wrong order
2541 597 : BOOST_ASSERT(it0.index <= it1.index);
2542 :
2543 : // Iterator is out of range
2544 597 : BOOST_ASSERT(it0.index <= impl_.nseg_);
2545 597 : BOOST_ASSERT(it0.pos <= impl_.len(id_path));
2546 :
2547 : // Iterator is out of range
2548 597 : BOOST_ASSERT(it1.index <= impl_.nseg_);
2549 597 : BOOST_ASSERT(it1.pos <= impl_.len(id_path));
2550 :
2551 : //------------------------------------------------
2552 : //
2553 : // Calculate output prefix
2554 : //
2555 : // 0 = ""
2556 : // 1 = "/"
2557 : // 2 = "./"
2558 : // 3 = "/./"
2559 : //
2560 597 : bool const is_abs = is_path_absolute();
2561 597 : if(has_authority())
2562 : {
2563 : // Check if the new
2564 : // path would be empty
2565 213 : if( src.fast_nseg == 0 &&
2566 108 : it0.index == 0 &&
2567 18 : it1.index == impl_.nseg_)
2568 : {
2569 : // VFALCO we don't have
2570 : // access to nchar this early
2571 : //
2572 : //BOOST_ASSERT(nchar == 0);
2573 15 : absolute = 0;
2574 : }
2575 : else
2576 : {
2577 : // prefix "/" required
2578 198 : absolute = 1;
2579 : }
2580 : }
2581 384 : else if(absolute < 0)
2582 : {
2583 384 : absolute = is_abs; // preserve
2584 : }
2585 597 : auto const path_pos = impl_.offset(id_path);
2586 :
2587 597 : std::size_t nchar = 0;
2588 597 : std::size_t prefix = 0;
2589 597 : bool encode_colons = false;
2590 597 : bool cp_src_prefix = false;
2591 597 : if(it0.index > 0)
2592 : {
2593 : // first segment unchanged
2594 323 : prefix = src.fast_nseg > 0;
2595 : }
2596 274 : else if(src.fast_nseg > 0)
2597 : {
2598 : // first segment from src
2599 221 : if(! src.front.empty())
2600 : {
2601 162 : if( src.front == "." &&
2602 7 : src.fast_nseg > 1)
2603 4 : if (src.s.empty())
2604 : {
2605 : // if front is ".", we need the extra "." in the prefix
2606 : // which will maintain the invariant that segments represent
2607 : // {"."}
2608 4 : prefix = 2 + absolute;
2609 : }
2610 : else
2611 : {
2612 : // if the "." prefix is explicitly required from set_path
2613 : // we do not include an extra "." segment
2614 0 : prefix = absolute;
2615 0 : cp_src_prefix = true;
2616 : }
2617 151 : else if(absolute)
2618 79 : prefix = 1;
2619 140 : else if(has_scheme() ||
2620 68 : ! src.front.contains(':'))
2621 67 : prefix = 0;
2622 : else
2623 : {
2624 5 : prefix = 0;
2625 5 : encode_colons = true;
2626 : }
2627 : }
2628 : else
2629 : {
2630 66 : prefix = 2 + absolute;
2631 : }
2632 : }
2633 : else
2634 : {
2635 : // first segment from it1
2636 53 : auto const p =
2637 53 : impl_.cs_ + path_pos + it1.pos;
2638 106 : switch(impl_.cs_ +
2639 53 : impl_.offset(id_query) - p)
2640 : {
2641 34 : case 0:
2642 : // points to end
2643 34 : prefix = absolute;
2644 34 : break;
2645 11 : default:
2646 11 : BOOST_ASSERT(*p == '/');
2647 11 : if(p[1] != '/')
2648 : {
2649 11 : if(absolute)
2650 5 : prefix = 1;
2651 11 : else if(has_scheme() ||
2652 11 : ! it1.dereference().contains(':'))
2653 5 : prefix = 0;
2654 : else
2655 1 : prefix = 2;
2656 11 : break;
2657 : }
2658 : // empty
2659 : BOOST_FALLTHROUGH;
2660 : case 1:
2661 : // empty
2662 8 : BOOST_ASSERT(*p == '/');
2663 8 : prefix = 2 + absolute;
2664 8 : break;
2665 : }
2666 : }
2667 :
2668 : // append '/' to new segs
2669 : // if inserting at front.
2670 597 : std::size_t const suffix =
2671 776 : it1.index == 0 &&
2672 660 : impl_.nseg_ > 0 &&
2673 63 : src.fast_nseg > 0;
2674 :
2675 : //------------------------------------------------
2676 : //
2677 : // Measure the number of encoded characters
2678 : // of output, and the number of inserted
2679 : // segments including internal separators.
2680 : //
2681 597 : src.encode_colons = encode_colons;
2682 597 : std::size_t nseg = 0;
2683 597 : if(src.measure(nchar))
2684 : {
2685 408 : src.encode_colons = false;
2686 : for(;;)
2687 : {
2688 733 : ++nseg;
2689 733 : if(! src.measure(nchar))
2690 406 : break;
2691 325 : ++nchar;
2692 : }
2693 : }
2694 :
2695 595 : switch(src.fast_nseg)
2696 : {
2697 189 : case 0:
2698 189 : BOOST_ASSERT(nseg == 0);
2699 189 : break;
2700 219 : case 1:
2701 219 : BOOST_ASSERT(nseg == 1);
2702 219 : break;
2703 187 : case 2:
2704 187 : BOOST_ASSERT(nseg >= 2);
2705 187 : break;
2706 : }
2707 :
2708 : //------------------------------------------------
2709 : //
2710 : // Calculate [pos0, pos1) to remove
2711 : //
2712 595 : auto pos0 = it0.pos;
2713 595 : if(it0.index == 0)
2714 : {
2715 : // patch pos for prefix
2716 272 : pos0 = 0;
2717 : }
2718 595 : auto pos1 = it1.pos;
2719 595 : if(it1.index == 0)
2720 : {
2721 : // patch pos for prefix
2722 179 : pos1 = detail::path_prefix(
2723 : impl_.get(id_path));
2724 : }
2725 416 : else if(
2726 416 : it0.index == 0 &&
2727 93 : it1.index < impl_.nseg_ &&
2728 : nseg == 0)
2729 : {
2730 : // Remove the slash from segment it1
2731 : // if it is becoming the new first
2732 : // segment.
2733 19 : ++pos1;
2734 : }
2735 : // calc decoded size of old range
2736 : auto const dn0 =
2737 595 : detail::decode_bytes_unsafe(
2738 : core::string_view(
2739 595 : impl_.cs_ +
2740 595 : impl_.offset(id_path) +
2741 : pos0,
2742 : pos1 - pos0));
2743 :
2744 : //------------------------------------------------
2745 : //
2746 : // Resize
2747 : //
2748 1190 : op_t op(*this, &src.s);
2749 : char* dest;
2750 : char const* end;
2751 : {
2752 595 : auto const nremove = pos1 - pos0;
2753 : // check overflow
2754 1190 : if( nchar <= max_size() && (
2755 595 : prefix + suffix <=
2756 595 : max_size() - nchar))
2757 : {
2758 595 : nchar = prefix + nchar + suffix;
2759 939 : if( nchar <= nremove ||
2760 344 : nchar - nremove <=
2761 344 : max_size() - size())
2762 595 : goto ok;
2763 : }
2764 : // too large
2765 0 : detail::throw_length_error();
2766 595 : ok:
2767 : auto const new_size =
2768 595 : size() + nchar - nremove;
2769 595 : reserve_impl(new_size, op);
2770 595 : dest = s_ + path_pos + pos0;
2771 595 : op.move(
2772 595 : dest + nchar,
2773 595 : s_ + path_pos + pos1,
2774 595 : size() - path_pos - pos1);
2775 1190 : impl_.set_size(
2776 : id_path,
2777 595 : impl_.len(id_path) + nchar - nremove);
2778 595 : BOOST_ASSERT(size() == new_size);
2779 595 : end = dest + nchar;
2780 595 : impl_.nseg_ = impl_.nseg_ + nseg - (
2781 595 : it1.index - it0.index) - cp_src_prefix;
2782 595 : if(s_)
2783 593 : s_[size()] = '\0';
2784 : }
2785 :
2786 : //------------------------------------------------
2787 : //
2788 : // Output segments and internal separators:
2789 : //
2790 : // prefix [ segment [ '/' segment ] ] suffix
2791 : //
2792 595 : auto const dest0 = dest;
2793 595 : switch(prefix)
2794 : {
2795 38 : case 3:
2796 38 : *dest++ = '/';
2797 38 : *dest++ = '.';
2798 38 : *dest++ = '/';
2799 38 : break;
2800 41 : case 2:
2801 41 : *dest++ = '.';
2802 : BOOST_FALLTHROUGH;
2803 323 : case 1:
2804 323 : *dest++ = '/';
2805 323 : break;
2806 234 : default:
2807 234 : break;
2808 : }
2809 595 : src.rewind();
2810 595 : if(nseg > 0)
2811 : {
2812 406 : src.encode_colons = encode_colons;
2813 : for(;;)
2814 : {
2815 731 : src.copy(dest, end);
2816 731 : if(--nseg == 0)
2817 406 : break;
2818 325 : *dest++ = '/';
2819 325 : src.encode_colons = false;
2820 : }
2821 406 : if(suffix)
2822 63 : *dest++ = '/';
2823 : }
2824 595 : BOOST_ASSERT(dest == dest0 + nchar);
2825 :
2826 : // calc decoded size of new range,
2827 : auto const dn =
2828 595 : detail::decode_bytes_unsafe(
2829 595 : core::string_view(dest0, dest - dest0));
2830 595 : impl_.decoded_[id_path] += dn - dn0;
2831 :
2832 : return detail::segments_iter_impl(
2833 1190 : impl_, pos0, it0.index);
2834 : }
2835 :
2836 : //------------------------------------------------
2837 :
2838 : auto
2839 143 : url_base::
2840 : edit_params(
2841 : detail::params_iter_impl const& it0,
2842 : detail::params_iter_impl const& it1,
2843 : detail::any_params_iter&& src) ->
2844 : detail::params_iter_impl
2845 : {
2846 143 : auto pos0 = impl_.offset(id_query);
2847 143 : auto pos1 = pos0 + it1.pos;
2848 143 : pos0 = pos0 + it0.pos;
2849 :
2850 : // Iterators belong to this url
2851 143 : BOOST_ASSERT(it0.ref.alias_of(impl_));
2852 143 : BOOST_ASSERT(it1.ref.alias_of(impl_));
2853 :
2854 : // Iterators is in the right order
2855 143 : BOOST_ASSERT(it0.index <= it1.index);
2856 :
2857 : // Iterators are within range
2858 143 : BOOST_ASSERT(it0.index <= impl_.nparam_);
2859 143 : BOOST_ASSERT(pos0 <= impl_.offset(id_frag));
2860 143 : BOOST_ASSERT(it1.index <= impl_.nparam_);
2861 143 : BOOST_ASSERT(pos1 <= impl_.offset(id_frag));
2862 :
2863 : // calc decoded size of old range,
2864 : // minus one if '?' or '&' prefixed
2865 : auto const dn0 =
2866 143 : detail::decode_bytes_unsafe(
2867 : core::string_view(
2868 143 : impl_.cs_ + pos0,
2869 : pos1 - pos0)) - (
2870 143 : impl_.len(id_query) > 0);
2871 :
2872 : //------------------------------------------------
2873 : //
2874 : // Measure the number of encoded characters
2875 : // of output, and the number of inserted
2876 : // segments including internal separators.
2877 : //
2878 :
2879 143 : std::size_t nchar = 0;
2880 143 : std::size_t nparam = 0;
2881 143 : if(src.measure(nchar))
2882 : {
2883 116 : ++nchar; // for '?' or '&'
2884 : for(;;)
2885 : {
2886 182 : ++nparam;
2887 182 : if(! src.measure(nchar))
2888 116 : break;
2889 66 : ++nchar; // for '&'
2890 : }
2891 : }
2892 :
2893 : //------------------------------------------------
2894 : //
2895 : // Resize
2896 : //
2897 138 : op_t op(*this, &src.s0, &src.s1);
2898 : char* dest;
2899 : char const* end;
2900 : {
2901 138 : auto const nremove = pos1 - pos0;
2902 : // check overflow
2903 238 : if( nchar > nremove &&
2904 100 : nchar - nremove >
2905 100 : max_size() - size())
2906 : {
2907 : // too large
2908 0 : detail::throw_length_error();
2909 : }
2910 138 : auto const nparam1 =
2911 138 : impl_.nparam_ + nparam - (
2912 138 : it1.index - it0.index);
2913 138 : reserve_impl(size() + nchar - nremove, op);
2914 138 : dest = s_ + pos0;
2915 138 : end = dest + nchar;
2916 138 : if(impl_.nparam_ > 0)
2917 : {
2918 : // needed when we move
2919 : // the beginning of the query
2920 100 : s_[impl_.offset(id_query)] = '&';
2921 : }
2922 138 : op.move(
2923 138 : dest + nchar,
2924 138 : impl_.cs_ + pos1,
2925 138 : size() - pos1);
2926 276 : impl_.set_size(
2927 : id_query,
2928 138 : impl_.len(id_query) +
2929 : nchar - nremove);
2930 138 : impl_.nparam_ = nparam1;
2931 138 : if(nparam1 > 0)
2932 : {
2933 : // needed when we erase
2934 : // the beginning of the query
2935 138 : s_[impl_.offset(id_query)] = '?';
2936 : }
2937 138 : if(s_)
2938 138 : s_[size()] = '\0';
2939 : }
2940 138 : auto const dest0 = dest;
2941 :
2942 : //------------------------------------------------
2943 : //
2944 : // Output params and internal separators:
2945 : //
2946 : // [ '?' param ] [ '&' param ]
2947 : //
2948 138 : if(nparam > 0)
2949 : {
2950 116 : if(it0.index == 0)
2951 72 : *dest++ = '?';
2952 : else
2953 44 : *dest++ = '&';
2954 116 : src.rewind();
2955 : for(;;)
2956 : {
2957 182 : src.copy(dest, end);
2958 182 : if(--nparam == 0)
2959 116 : break;
2960 66 : *dest++ = '&';
2961 : }
2962 : }
2963 :
2964 : // calc decoded size of new range,
2965 : // minus one if '?' or '&' prefixed
2966 : auto const dn =
2967 138 : detail::decode_bytes_unsafe(
2968 138 : core::string_view(dest0, dest - dest0)) - (
2969 138 : impl_.len(id_query) > 0);
2970 :
2971 138 : impl_.decoded_[id_query] += (dn - dn0);
2972 :
2973 : return detail::params_iter_impl(
2974 138 : impl_,
2975 138 : pos0 - impl_.offset_[id_query],
2976 276 : it0.index);
2977 138 : }
2978 :
2979 : //------------------------------------------------
2980 :
2981 : void
2982 384 : url_base::
2983 : decoded_to_lower_impl(int id) noexcept
2984 : {
2985 384 : char* it = s_ + impl_.offset(id);
2986 384 : char const* const end = s_ + impl_.offset(id + 1);
2987 2215 : while(it < end)
2988 : {
2989 1831 : if (*it != '%')
2990 : {
2991 3652 : *it = grammar::to_lower(
2992 1826 : *it);
2993 1826 : ++it;
2994 1826 : continue;
2995 : }
2996 5 : it += 3;
2997 : }
2998 384 : }
2999 :
3000 : void
3001 39 : url_base::
3002 : to_lower_impl(int id) noexcept
3003 : {
3004 39 : char* it = s_ + impl_.offset(id);
3005 39 : char const* const end = s_ + impl_.offset(id + 1);
3006 162 : while(it < end)
3007 : {
3008 246 : *it = grammar::to_lower(
3009 123 : *it);
3010 123 : ++it;
3011 : }
3012 39 : }
3013 :
3014 : } // urls
3015 : } // boost
3016 :
|