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