TLA 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 HIT 18176 : url_base::
56 : op_t::
57 : ~op_t()
58 : {
59 18176 : if(old)
60 2561 : u.cleanup(*this);
61 18176 : u.check_invariants();
62 18176 : }
63 :
64 : inline
65 18176 : url_base::
66 : op_t::
67 : op_t(
68 : url_base& impl_,
69 : core::string_view* s0_,
70 18176 : core::string_view* s1_) noexcept
71 18176 : : u(impl_)
72 18176 : , s0(s0_)
73 18176 : , s1(s1_)
74 : {
75 18176 : u.check_invariants();
76 18176 : }
77 :
78 : inline
79 : void
80 9335 : url_base::
81 : op_t::
82 : move(
83 : char* dest,
84 : char const* src,
85 : std::size_t n) noexcept
86 : {
87 9335 : if(! n)
88 2519 : return;
89 6816 : if(s0)
90 : {
91 4885 : if(s1)
92 654 : return detail::move_chars(
93 654 : dest, src, n, *s0, *s1);
94 4231 : return detail::move_chars(
95 4231 : dest, src, n, *s0);
96 : }
97 1931 : detail::move_chars(
98 : dest, src, n);
99 : }
100 :
101 : //------------------------------------------------
102 :
103 : // construct reference
104 : inline
105 1647 : url_base::
106 : url_base(
107 1647 : detail::url_impl const& impl) noexcept
108 1647 : : url_view_base(impl)
109 : {
110 1647 : }
111 :
112 : inline
113 : void
114 305 : url_base::
115 : reserve_impl(std::size_t n)
116 : {
117 305 : op_t op(*this);
118 305 : reserve_impl(n, op);
119 304 : if(s_)
120 302 : s_[size()] = '\0';
121 305 : }
122 :
123 : // make a copy of u
124 : inline
125 : void
126 5189 : url_base::
127 : copy(url_view_base const& u)
128 : {
129 5189 : if (this == &u)
130 140 : return;
131 5175 : op_t op(*this);
132 5175 : if(u.size() == 0)
133 : {
134 126 : clear();
135 126 : return;
136 : }
137 5049 : reserve_impl(
138 : u.size(), op);
139 5046 : impl_ = u.impl();
140 5046 : impl_.cs_ = s_;
141 5046 : impl_.from_ = {from::url};
142 5046 : std::memcpy(s_,
143 5046 : u.data(), u.size());
144 5046 : s_[size()] = '\0';
145 5175 : }
146 :
147 : //------------------------------------------------
148 : //
149 : // Scheme
150 : //
151 : //------------------------------------------------
152 :
153 : inline
154 : url_base&
155 376 : url_base::
156 : set_scheme(core::string_view s)
157 : {
158 376 : set_scheme_impl(
159 : s, string_to_scheme(s));
160 359 : return *this;
161 : }
162 :
163 : inline
164 : url_base&
165 182 : url_base::
166 : set_scheme_id(urls::scheme id)
167 : {
168 182 : if(id == urls::scheme::unknown)
169 14 : detail::throw_invalid_argument();
170 168 : if(id == urls::scheme::none)
171 18 : return remove_scheme();
172 150 : set_scheme_impl(to_string(id), id);
173 119 : return *this;
174 : }
175 :
176 : inline
177 : url_base&
178 53 : url_base::
179 : remove_scheme()
180 : {
181 53 : op_t op(*this);
182 53 : auto const sn = impl_.len(id_scheme);
183 53 : if(sn == 0)
184 15 : return *this;
185 38 : auto const po = impl_.offset(id_path);
186 38 : auto fseg = first_segment();
187 : bool const encode_colon =
188 38 : !has_authority() &&
189 21 : impl_.nseg_ > 0 &&
190 70 : s_[po] != '/' &&
191 11 : fseg.contains(':');
192 38 : if(!encode_colon)
193 : {
194 : // just remove the scheme
195 29 : resize_impl(id_scheme, 0, op);
196 29 : impl_.scheme_ = urls::scheme::none;
197 29 : check_invariants();
198 29 : 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 != end &&
249 40 : *it != '/')
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 53 : }
283 :
284 : //------------------------------------------------
285 : //
286 : // Authority
287 : //
288 : //------------------------------------------------
289 :
290 : inline
291 : url_base&
292 120 : url_base::
293 : set_encoded_authority(
294 : pct_string_view s)
295 : {
296 120 : op_t op(*this, &detail::ref(s));
297 122 : authority_view a = grammar::parse(
298 : s, authority_rule
299 120 : ).value(BOOST_URL_POS);
300 119 : auto n = s.size() + 2;
301 : auto const need_slash =
302 141 : ! is_path_absolute() &&
303 22 : impl_.len(id_path) > 0;
304 119 : if(need_slash)
305 2 : ++n;
306 119 : auto dest = resize_impl(
307 : id_user, id_path, n, op);
308 119 : dest[0] = '/';
309 119 : dest[1] = '/';
310 119 : std::memcpy(dest + 2,
311 119 : s.data(), s.size());
312 119 : if(need_slash)
313 2 : dest[n - 1] = '/';
314 119 : impl_.apply_authority(a.u_);
315 119 : if(need_slash)
316 2 : impl_.adjust_right(
317 : id_query, id_end, 1);
318 119 : return *this;
319 120 : }
320 :
321 : inline
322 : url_base&
323 259 : url_base::
324 : remove_authority()
325 : {
326 259 : if(! has_authority())
327 70 : return *this;
328 :
329 189 : op_t op(*this);
330 189 : auto path = impl_.get(id_path);
331 189 : bool const need_dot = path.starts_with("//");
332 189 : if(need_dot)
333 : {
334 : // prepend "/.", can't throw
335 5 : auto p = resize_impl(
336 : id_user, id_path, 2, op);
337 5 : p[0] = '/';
338 5 : p[1] = '.';
339 5 : impl_.split(id_user, 0);
340 5 : impl_.split(id_pass, 0);
341 5 : impl_.split(id_host, 0);
342 5 : impl_.split(id_port, 0);
343 : }
344 : else
345 : {
346 184 : resize_impl(
347 : id_user, id_path, 0, op);
348 : }
349 189 : impl_.host_type_ =
350 : urls::host_type::none;
351 189 : return *this;
352 189 : }
353 :
354 : //------------------------------------------------
355 : //
356 : // Userinfo
357 : //
358 : //------------------------------------------------
359 :
360 : inline
361 : url_base&
362 225 : url_base::
363 : set_userinfo(
364 : core::string_view s)
365 : {
366 225 : op_t op(*this, &s);
367 225 : encoding_opts opt;
368 225 : auto const n = encoded_size(
369 : s, detail::userinfo_chars, opt);
370 225 : auto dest = set_userinfo_impl(n, op);
371 225 : encode(
372 : dest,
373 : n,
374 : s,
375 : detail::userinfo_chars,
376 : opt);
377 225 : auto const pos = impl_.get(
378 : id_user, id_host
379 225 : ).find_first_of(':');
380 225 : if(pos != core::string_view::npos)
381 : {
382 22 : impl_.split(id_user, pos);
383 : // find ':' in plain string
384 : auto const pos2 =
385 22 : s.find_first_of(':');
386 22 : 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 22 : impl_.decoded_[id_user] =
391 22 : detail::to_size_type(pos2);
392 22 : impl_.decoded_[id_pass] =
393 22 : detail::to_size_type(s.size() - pos2 - 1);
394 : }
395 : else
396 : {
397 MIS 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 HIT 203 : impl_.decoded_[id_user] =
405 203 : detail::to_size_type(s.size());
406 203 : impl_.decoded_[id_pass] = 0;
407 : }
408 225 : return *this;
409 225 : }
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 214 : url_base::
470 : remove_userinfo() noexcept
471 : {
472 214 : if(impl_.len(id_pass) == 0)
473 138 : return *this; // no userinfo
474 :
475 76 : op_t op(*this);
476 : // keep authority '//'
477 76 : resize_impl(
478 : id_user, id_host, 2, op);
479 76 : impl_.decoded_[id_user] = 0;
480 76 : impl_.decoded_[id_pass] = 0;
481 76 : return *this;
482 76 : }
483 :
484 : //------------------------------------------------
485 :
486 : inline
487 : url_base&
488 241 : url_base::
489 : set_user(core::string_view s)
490 : {
491 241 : op_t op(*this, &s);
492 241 : encoding_opts opt;
493 241 : auto const n = encoded_size(
494 : s, detail::user_chars, opt);
495 241 : auto dest = set_user_impl(n, op);
496 241 : encode_unsafe(
497 : dest,
498 : n,
499 : s,
500 : detail::user_chars,
501 : opt);
502 241 : impl_.decoded_[id_user] =
503 241 : detail::to_size_type(s.size());
504 241 : return *this;
505 241 : }
506 :
507 : inline
508 : url_base&
509 147 : url_base::
510 : set_encoded_user(
511 : pct_string_view s)
512 : {
513 147 : op_t op(*this, &detail::ref(s));
514 : auto const n =
515 147 : detail::re_encoded_size_unsafe(
516 : s, detail::user_chars);
517 147 : auto dest = set_user_impl(n, op);
518 147 : impl_.decoded_[id_user] =
519 294 : detail::to_size_type(detail::re_encode_unsafe(
520 : dest,
521 147 : dest + n,
522 : s,
523 : detail::user_chars));
524 147 : BOOST_ASSERT(
525 : impl_.decoded_[id_user] ==
526 : s.decoded_size());
527 147 : return *this;
528 147 : }
529 :
530 : //------------------------------------------------
531 :
532 : inline
533 : url_base&
534 231 : url_base::
535 : set_password(core::string_view s)
536 : {
537 231 : op_t op(*this, &s);
538 231 : encoding_opts opt;
539 231 : auto const n = encoded_size(
540 : s, detail::password_chars, opt);
541 231 : auto dest = set_password_impl(n, op);
542 231 : encode_unsafe(
543 : dest,
544 : n,
545 : s,
546 : detail::password_chars,
547 : opt);
548 231 : impl_.decoded_[id_pass] =
549 231 : detail::to_size_type(s.size());
550 231 : return *this;
551 231 : }
552 :
553 : inline
554 : url_base&
555 140 : url_base::
556 : set_encoded_password(
557 : pct_string_view s)
558 : {
559 140 : op_t op(*this, &detail::ref(s));
560 : auto const n =
561 140 : detail::re_encoded_size_unsafe(
562 : s,
563 : detail::password_chars);
564 140 : auto dest = set_password_impl(n, op);
565 140 : impl_.decoded_[id_pass] =
566 280 : detail::to_size_type(detail::re_encode_unsafe(
567 : dest,
568 140 : dest + n,
569 : s,
570 : detail::password_chars));
571 140 : BOOST_ASSERT(
572 : impl_.decoded_[id_pass] ==
573 : s.decoded_size());
574 140 : return *this;
575 140 : }
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 401 : url_base::
632 : set_host(
633 : core::string_view s)
634 : {
635 401 : if( s.size() > 2 &&
636 450 : s.front() == '[' &&
637 49 : s.back() == ']')
638 : {
639 : // IP-literal
640 49 : if (s[1] != 'v')
641 : {
642 : // IPv6-address
643 48 : auto innersv = s.substr(1, s.size() - 2);
644 48 : auto innerit = innersv.begin();
645 48 : auto endit = innersv.end();
646 48 : auto rv = grammar::parse(
647 : innerit,
648 : endit,
649 : ipv6_address_rule);
650 48 : if(rv)
651 : {
652 48 : if (innerit == endit)
653 : {
654 47 : set_host_ipv6_and_encoded_zone_id(*rv, {});
655 48 : 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 352 : else if(s.size() >= 7) // "0.0.0.0"
679 : {
680 : // IPv4-address
681 348 : auto rv = parse_ipv4_address(s);
682 348 : if(rv)
683 189 : return set_host_ipv4(*rv);
684 : }
685 :
686 : // reg-name
687 163 : op_t op(*this, &s);
688 163 : encoding_opts opt;
689 163 : auto const n = encoded_size(
690 : s, detail::host_chars, opt);
691 163 : auto dest = set_host_impl(n, op);
692 163 : encode(
693 : dest,
694 163 : impl_.get(id_path).data() - dest,
695 : s,
696 : detail::host_chars,
697 : opt);
698 163 : impl_.decoded_[id_host] =
699 163 : detail::to_size_type(s.size());
700 163 : impl_.host_type_ =
701 : urls::host_type::name;
702 163 : return *this;
703 163 : }
704 :
705 : // set host part from encoded text
706 : inline
707 : url_base&
708 218 : url_base::
709 : set_encoded_host(
710 : pct_string_view s)
711 : {
712 218 : if( s.size() > 2 &&
713 235 : 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 201 : else if(s.size() >= 7) // "0.0.0.0"
761 : {
762 : // IPv4-address
763 74 : auto rv = parse_ipv4_address(s);
764 74 : if(rv)
765 5 : return set_host_ipv4(*rv);
766 : }
767 :
768 : // reg-name
769 206 : op_t op(*this, &detail::ref(s));
770 206 : auto const n = detail::re_encoded_size_unsafe(
771 : s, detail::host_chars);
772 206 : auto dest = set_host_impl(n, op);
773 206 : impl_.decoded_[id_host] =
774 412 : detail::to_size_type(detail::re_encode_unsafe(
775 : dest,
776 206 : impl_.get(id_path).data(),
777 : s,
778 : detail::host_chars));
779 206 : BOOST_ASSERT(impl_.decoded_[id_host] ==
780 : s.decoded_size());
781 206 : impl_.host_type_ =
782 : urls::host_type::name;
783 206 : return *this;
784 206 : }
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 203 : url_base::
936 : set_host_ipv4(
937 : ipv4_address const& addr)
938 : {
939 203 : op_t op(*this);
940 : char buf[urls::ipv4_address::max_str_len];
941 203 : auto s = addr.to_buffer(buf, sizeof(buf));
942 203 : auto dest = set_host_impl(s.size(), op);
943 203 : std::memcpy(dest, s.data(), s.size());
944 203 : impl_.decoded_[id_host] =
945 203 : detail::to_size_type(impl_.len(id_host));
946 203 : impl_.host_type_ = urls::host_type::ipv4;
947 203 : auto bytes = addr.to_bytes();
948 203 : std::memcpy(
949 203 : impl_.ip_addr_,
950 203 : bytes.data(),
951 : bytes.size());
952 203 : return *this;
953 203 : }
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 64 : url_base::
1025 : set_host_ipv6_and_encoded_zone_id(
1026 : ipv6_address const& addr,
1027 : pct_string_view zone_id)
1028 : {
1029 64 : op_t op(*this, &detail::ref(zone_id));
1030 : char ipv6_str_buf[urls::ipv6_address::max_str_len];
1031 64 : auto ipv6_str = addr.to_buffer(ipv6_str_buf, sizeof(ipv6_str_buf));
1032 64 : bool const has_zone_id = !zone_id.empty();
1033 64 : auto const ipn = ipv6_str.size();
1034 64 : auto const zn = detail::re_encoded_size_unsafe(zone_id, unreserved_chars);
1035 64 : auto const n = ipn + 2 + has_zone_id * (3 + zn);
1036 64 : auto dest = set_host_impl(n, op);
1037 64 : *dest++ = '[';
1038 64 : std::memcpy(dest, ipv6_str.data(), ipn);
1039 64 : dest += ipn;
1040 64 : std::size_t dzn = 0;
1041 64 : 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 64 : *dest++ = ']';
1049 : // ipn + |"["| + |"]"| + (has_zone_id ? |"%"| + zn : 0)
1050 128 : impl_.decoded_[id_host] = detail::to_size_type(
1051 64 : ipn + 2 + has_zone_id * (1 + dzn));
1052 64 : impl_.host_type_ = urls::host_type::ipv6;
1053 64 : auto bytes = addr.to_bytes();
1054 64 : std::memcpy(
1055 64 : impl_.ip_addr_,
1056 64 : bytes.data(),
1057 : bytes.size());
1058 64 : }
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 215 : url_base::
1158 : set_port_number(
1159 : std::uint16_t n)
1160 : {
1161 215 : op_t op(*this);
1162 : auto s =
1163 215 : detail::make_printed(n);
1164 215 : auto dest = set_port_impl(
1165 215 : s.string().size(), op);
1166 215 : std::memcpy(
1167 215 : dest, s.string().data(),
1168 215 : s.string().size());
1169 215 : impl_.port_number_ = n;
1170 215 : return *this;
1171 215 : }
1172 :
1173 : inline
1174 : url_base&
1175 389 : url_base::
1176 : set_port(
1177 : core::string_view s)
1178 : {
1179 389 : op_t op(*this, &s);
1180 410 : auto t = grammar::parse(s,
1181 21 : detail::port_rule{}
1182 389 : ).value(BOOST_URL_POS);
1183 : auto dest =
1184 368 : set_port_impl(t.str.size(), op);
1185 368 : std::memcpy(dest,
1186 368 : t.str.data(), t.str.size());
1187 368 : if(t.has_number)
1188 208 : impl_.port_number_ = t.number;
1189 : else
1190 160 : impl_.port_number_ = 0;
1191 368 : return *this;
1192 389 : }
1193 :
1194 : inline
1195 : url_base&
1196 206 : url_base::
1197 : remove_port() noexcept
1198 : {
1199 206 : op_t op(*this);
1200 206 : resize_impl(id_port, 0, op);
1201 206 : impl_.port_number_ = 0;
1202 412 : return *this;
1203 206 : }
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 52 : url_base::
1231 : set_path_absolute(
1232 : bool absolute)
1233 : {
1234 52 : op_t op(*this);
1235 :
1236 : // check if path empty
1237 52 : if(impl_.len(id_path) == 0)
1238 : {
1239 40 : if(! absolute)
1240 : {
1241 : // already not absolute
1242 34 : 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 52 : }
1309 :
1310 : inline
1311 : url_base&
1312 298 : url_base::
1313 : set_path(
1314 : core::string_view s)
1315 : {
1316 298 : op_t op(*this, &s);
1317 298 : 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 298 : auto const n = encoded_size(
1330 : s, detail::path_chars, opt);
1331 298 : std::size_t n_reencode_colons = 0;
1332 298 : core::string_view first_seg;
1333 298 : if (!has_scheme() &&
1334 337 : !has_authority() &&
1335 39 : !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 298 : has_authority() &&
1351 305 : !s.starts_with('/') &&
1352 7 : !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 592 : !make_absolute &&
1357 294 : s.starts_with("//");
1358 :
1359 : //------------------------------------------------
1360 : //
1361 : // Re-encode data
1362 : //
1363 596 : auto dest = set_path_impl(
1364 298 : n + make_absolute + 2 * n_reencode_colons + 2 * add_dot_segment, op);
1365 298 : impl_.decoded_[id_path] = 0;
1366 298 : if (!dest)
1367 : {
1368 3 : impl_.nseg_ = 0;
1369 3 : return *this;
1370 : }
1371 295 : if (make_absolute)
1372 : {
1373 4 : *dest++ = '/';
1374 4 : impl_.decoded_[id_path] += 1;
1375 : }
1376 291 : else if (add_dot_segment)
1377 : {
1378 12 : *dest++ = '/';
1379 12 : *dest++ = '.';
1380 12 : impl_.decoded_[id_path] += 2;
1381 : }
1382 590 : dest += encode_unsafe(
1383 : dest,
1384 295 : impl_.get(id_query).data() - dest,
1385 : first_seg,
1386 295 : detail::segment_chars - ':',
1387 : opt);
1388 295 : dest += encode_unsafe(
1389 : dest,
1390 295 : impl_.get(id_query).data() - dest,
1391 : s.substr(first_seg.size()),
1392 : detail::path_chars,
1393 : opt);
1394 295 : impl_.decoded_[id_path] +=
1395 295 : detail::to_size_type(s.size());
1396 295 : BOOST_ASSERT(!dest || dest == impl_.get(id_query).data());
1397 295 : 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 295 : if (s == "/")
1407 : {
1408 : // "/" maps to sequence {}
1409 144 : impl_.nseg_ = 0;
1410 : }
1411 151 : else if (!s.empty())
1412 : {
1413 146 : if (s.starts_with("/./"))
1414 1 : s = s.substr(2);
1415 : // count segments as number of '/'s + 1
1416 292 : impl_.nseg_ = detail::to_size_type(
1417 146 : std::count(
1418 292 : s.begin() + 1, s.end(), '/') + 1);
1419 : }
1420 : else
1421 : {
1422 : // an empty relative path maps to sequence {}
1423 5 : impl_.nseg_ = 0;
1424 : }
1425 :
1426 295 : check_invariants();
1427 295 : return *this;
1428 298 : }
1429 :
1430 : inline
1431 : url_base&
1432 299 : url_base::
1433 : set_encoded_path(
1434 : pct_string_view s)
1435 : {
1436 299 : 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 299 : auto const n = detail::re_encoded_size_unsafe(
1446 : s, detail::path_chars);
1447 299 : std::size_t n_reencode_colons = 0;
1448 299 : core::string_view first_seg;
1449 299 : if (!has_scheme() &&
1450 332 : !has_authority() &&
1451 33 : !s.starts_with('/'))
1452 : {
1453 : // the first segment with unencoded colons would look
1454 : // like the scheme
1455 26 : first_seg = detail::to_sv(s);
1456 26 : std::size_t p = s.find('/');
1457 26 : if (p != core::string_view::npos)
1458 9 : first_seg = s.substr(0, p);
1459 26 : n_reencode_colons = std::count(
1460 52 : 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 299 : has_authority() &&
1467 443 : !s.starts_with('/') &&
1468 144 : !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 502 : !make_absolute &&
1473 362 : !has_authority() &&
1474 63 : s.starts_with("//");
1475 :
1476 : //------------------------------------------------
1477 : //
1478 : // Re-encode data
1479 : //
1480 598 : auto dest = set_path_impl(
1481 299 : n + make_absolute + 2 * n_reencode_colons + 2 * add_dot_segment, op);
1482 299 : impl_.decoded_[id_path] = 0;
1483 299 : if (!dest)
1484 : {
1485 2 : impl_.nseg_ = 0;
1486 2 : return *this;
1487 : }
1488 297 : if (make_absolute)
1489 : {
1490 96 : *dest++ = '/';
1491 96 : impl_.decoded_[id_path] += 1;
1492 : }
1493 201 : else if (add_dot_segment)
1494 : {
1495 5 : *dest++ = '/';
1496 5 : *dest++ = '.';
1497 5 : impl_.decoded_[id_path] += 2;
1498 : }
1499 297 : impl_.decoded_[id_path] +=
1500 297 : detail::to_size_type(detail::re_encode_unsafe(
1501 : dest,
1502 297 : impl_.get(id_query).data(),
1503 : first_seg,
1504 297 : detail::segment_chars - ':'));
1505 297 : impl_.decoded_[id_path] +=
1506 594 : detail::to_size_type(detail::re_encode_unsafe(
1507 : dest,
1508 297 : impl_.get(id_query).data(),
1509 : s.substr(first_seg.size()),
1510 : detail::path_chars));
1511 297 : BOOST_ASSERT(dest == impl_.get(id_query).data());
1512 297 : 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 297 : if (s == "/")
1522 : {
1523 : // "/" maps to sequence {}
1524 19 : impl_.nseg_ = 0;
1525 : }
1526 278 : else if (!s.empty())
1527 : {
1528 227 : if (s.starts_with("/./"))
1529 7 : s = s.substr(2);
1530 : // count segments as number of '/'s + 1
1531 454 : impl_.nseg_ = detail::to_size_type(
1532 227 : std::count(
1533 454 : s.begin() + 1, s.end(), '/') + 1);
1534 : }
1535 : else
1536 : {
1537 : // an empty relative path maps to sequence {}
1538 51 : impl_.nseg_ = 0;
1539 : }
1540 :
1541 297 : check_invariants();
1542 297 : return *this;
1543 299 : }
1544 :
1545 : inline
1546 : segments_ref
1547 1146 : url_base::
1548 : segments() noexcept
1549 : {
1550 1146 : return {*this};
1551 : }
1552 :
1553 : inline
1554 : segments_encoded_ref
1555 497 : url_base::
1556 : encoded_segments() noexcept
1557 : {
1558 497 : return {*this};
1559 : }
1560 :
1561 : //------------------------------------------------
1562 : //
1563 : // Query
1564 : //
1565 : //------------------------------------------------
1566 :
1567 : inline
1568 : url_base&
1569 169 : url_base::
1570 : set_query(
1571 : core::string_view s)
1572 : {
1573 169 : edit_params(
1574 169 : detail::params_iter_impl(impl_),
1575 169 : detail::params_iter_impl(impl_, 0),
1576 338 : detail::query_string_iter(s, true));
1577 169 : return *this;
1578 : }
1579 :
1580 : inline
1581 : url_base&
1582 179 : url_base::
1583 : set_encoded_query(
1584 : pct_string_view s)
1585 : {
1586 179 : op_t op(*this);
1587 179 : std::size_t n = 0; // encoded size
1588 179 : std::size_t nparam = 1; // param count
1589 179 : auto const end = s.end();
1590 179 : auto p = s.begin();
1591 :
1592 : // measure
1593 742 : while(p != end)
1594 : {
1595 563 : if(*p == '&')
1596 : {
1597 4 : ++p;
1598 4 : ++n;
1599 4 : ++nparam;
1600 : }
1601 559 : else if(*p != '%')
1602 : {
1603 533 : if(detail::query_chars(*p))
1604 518 : n += 1; // allowed
1605 : else
1606 15 : n += 3; // escaped
1607 533 : ++p;
1608 : }
1609 : else
1610 : {
1611 : // escape
1612 26 : n += 3;
1613 26 : p += 3;
1614 : }
1615 : }
1616 :
1617 : // resize
1618 179 : auto dest = resize_impl(
1619 179 : id_query, n + 1, op);
1620 179 : *dest++ = '?';
1621 :
1622 : // encode
1623 179 : impl_.decoded_[id_query] =
1624 358 : detail::to_size_type(detail::re_encode_unsafe(
1625 : dest,
1626 179 : dest + n,
1627 : s,
1628 : detail::query_chars));
1629 179 : BOOST_ASSERT(
1630 : impl_.decoded_[id_query] ==
1631 : s.decoded_size());
1632 179 : impl_.nparam_ =
1633 179 : detail::to_size_type(nparam);
1634 179 : return *this;
1635 179 : }
1636 :
1637 : inline
1638 : params_ref
1639 977 : url_base::
1640 : params() noexcept
1641 : {
1642 : return params_ref(
1643 : *this,
1644 : encoding_opts{
1645 977 : 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 990 : url_base::
1687 : remove_query() noexcept
1688 : {
1689 990 : op_t op(*this);
1690 990 : resize_impl(id_query, 0, op);
1691 990 : impl_.nparam_ = 0;
1692 990 : impl_.decoded_[id_query] = 0;
1693 1980 : return *this;
1694 990 : }
1695 :
1696 : //------------------------------------------------
1697 : //
1698 : // Fragment
1699 : //
1700 : //------------------------------------------------
1701 :
1702 : inline
1703 : url_base&
1704 407 : url_base::
1705 : remove_fragment() noexcept
1706 : {
1707 407 : op_t op(*this);
1708 407 : resize_impl(id_frag, 0, op);
1709 407 : impl_.decoded_[id_frag] = 0;
1710 814 : return *this;
1711 407 : }
1712 :
1713 : inline
1714 : url_base&
1715 151 : url_base::
1716 : set_fragment(core::string_view s)
1717 : {
1718 151 : op_t op(*this, &s);
1719 151 : encoding_opts opt;
1720 151 : auto const n = encoded_size(
1721 : s,
1722 : detail::fragment_chars,
1723 : opt);
1724 151 : auto dest = resize_impl(
1725 : id_frag, n + 1, op);
1726 151 : *dest++ = '#';
1727 151 : encode_unsafe(
1728 : dest,
1729 : n,
1730 : s,
1731 : detail::fragment_chars,
1732 : opt);
1733 151 : impl_.decoded_[id_frag] =
1734 151 : detail::to_size_type(s.size());
1735 151 : return *this;
1736 151 : }
1737 :
1738 : inline
1739 : url_base&
1740 183 : url_base::
1741 : set_encoded_fragment(
1742 : pct_string_view s)
1743 : {
1744 183 : op_t op(*this, &detail::ref(s));
1745 : auto const n =
1746 183 : detail::re_encoded_size_unsafe(
1747 : s,
1748 : detail::fragment_chars);
1749 183 : auto dest = resize_impl(
1750 183 : id_frag, n + 1, op);
1751 183 : *dest++ = '#';
1752 183 : impl_.decoded_[id_frag] =
1753 366 : detail::to_size_type(detail::re_encode_unsafe(
1754 : dest,
1755 183 : dest + n,
1756 : s,
1757 : detail::fragment_chars));
1758 183 : BOOST_ASSERT(
1759 : impl_.decoded_[id_frag] ==
1760 : s.decoded_size());
1761 183 : return *this;
1762 183 : }
1763 :
1764 : //------------------------------------------------
1765 : //
1766 : // Resolution
1767 : //
1768 : //------------------------------------------------
1769 :
1770 : inline
1771 : system::result<void>
1772 516 : url_base::
1773 : resolve(
1774 : url_view_base const& ref)
1775 : {
1776 519 : if (this == &ref &&
1777 3 : has_scheme())
1778 : {
1779 2 : normalize_path();
1780 2 : return {};
1781 : }
1782 :
1783 514 : if(! has_scheme())
1784 : {
1785 2 : BOOST_URL_RETURN_EC(error::not_a_base);
1786 : }
1787 :
1788 512 : 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 782 : if( ref.has_scheme() &&
1796 270 : ref.scheme() != scheme())
1797 : {
1798 202 : reserve_impl(ref.size(), op);
1799 202 : copy(ref);
1800 202 : normalize_path();
1801 202 : return {};
1802 : }
1803 310 : if(ref.has_authority())
1804 : {
1805 78 : reserve_impl(
1806 78 : impl_.offset(id_user) + ref.size(), op);
1807 78 : set_encoded_authority(
1808 : ref.encoded_authority());
1809 78 : set_encoded_path(
1810 : ref.encoded_path());
1811 78 : if (ref.encoded_path().empty())
1812 33 : set_path_absolute(false);
1813 : else
1814 45 : normalize_path();
1815 78 : if(ref.has_query())
1816 8 : set_encoded_query(
1817 : ref.encoded_query());
1818 : else
1819 70 : remove_query();
1820 78 : if(ref.has_fragment())
1821 7 : set_encoded_fragment(
1822 : ref.encoded_fragment());
1823 : else
1824 71 : remove_fragment();
1825 78 : return {};
1826 : }
1827 232 : if(ref.encoded_path().empty())
1828 : {
1829 43 : reserve_impl(
1830 43 : impl_.offset(id_query) +
1831 43 : ref.size(), op);
1832 43 : normalize_path();
1833 43 : if(ref.has_query())
1834 : {
1835 12 : set_encoded_query(
1836 : ref.encoded_query());
1837 : }
1838 43 : if(ref.has_fragment())
1839 20 : set_encoded_fragment(
1840 : ref.encoded_fragment());
1841 : else
1842 23 : remove_fragment();
1843 43 : return {};
1844 : }
1845 189 : if(ref.is_path_absolute())
1846 : {
1847 41 : reserve_impl(
1848 41 : impl_.offset(id_path) +
1849 41 : ref.size(), op);
1850 41 : set_encoded_path(
1851 : ref.encoded_path());
1852 41 : normalize_path();
1853 41 : if(ref.has_query())
1854 4 : set_encoded_query(
1855 : ref.encoded_query());
1856 : else
1857 37 : remove_query();
1858 41 : if(ref.has_fragment())
1859 2 : set_encoded_fragment(
1860 : ref.encoded_fragment());
1861 : else
1862 39 : remove_fragment();
1863 41 : return {};
1864 : }
1865 : // General case: ref is relative path
1866 148 : reserve_impl(
1867 148 : impl_.offset(id_query) +
1868 148 : ref.size(), op);
1869 : // 5.2.3. Merge Paths
1870 148 : auto es = encoded_segments();
1871 148 : if(es.size() > 0)
1872 : {
1873 141 : es.pop_back();
1874 : }
1875 296 : es.insert(es.end(),
1876 148 : ref.encoded_segments().begin(),
1877 148 : ref.encoded_segments().end());
1878 148 : normalize_path();
1879 148 : if(ref.has_query())
1880 10 : set_encoded_query(
1881 : ref.encoded_query());
1882 : else
1883 138 : remove_query();
1884 148 : if(ref.has_fragment())
1885 13 : set_encoded_fragment(
1886 : ref.encoded_fragment());
1887 : else
1888 135 : remove_fragment();
1889 148 : return {};
1890 512 : }
1891 :
1892 : //------------------------------------------------
1893 : //
1894 : // Normalization
1895 : //
1896 : //------------------------------------------------
1897 :
1898 : template <
1899 : class AllowedCharset,
1900 : class IgnoredCharset>
1901 : void
1902 2880 : url_base::
1903 : normalize_octets_impl(
1904 : int id,
1905 : AllowedCharset const& allowed,
1906 : IgnoredCharset const& ignored,
1907 : op_t& op) noexcept
1908 : {
1909 2880 : char* it = s_ + impl_.offset(id);
1910 2880 : char* end = s_ + impl_.offset(id + 1);
1911 2880 : char d = 0;
1912 2880 : char* dest = it;
1913 16129 : while (it < end)
1914 : {
1915 13249 : if (*it != '%')
1916 : {
1917 13031 : *dest = *it;
1918 13031 : ++it;
1919 13031 : ++dest;
1920 13031 : continue;
1921 : }
1922 218 : BOOST_ASSERT(end - it >= 3);
1923 :
1924 : // decode unreserved octets
1925 218 : d = detail::decode_one(it + 1);
1926 314 : if (allowed(d) &&
1927 96 : !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 131 : *dest++ = '%';
1937 131 : ++it;
1938 131 : *dest++ = grammar::to_upper(*it++);
1939 131 : *dest++ = grammar::to_upper(*it++);
1940 : }
1941 2880 : 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 2880 : }
1949 :
1950 : template<class CharSet>
1951 : void
1952 2681 : url_base::
1953 : normalize_octets_impl(
1954 : int idx,
1955 : CharSet const& allowed,
1956 : op_t& op) noexcept
1957 : {
1958 2681 : return normalize_octets_impl(
1959 2681 : idx, allowed, detail::empty_chars, op);
1960 : }
1961 :
1962 : inline
1963 : url_base&
1964 198 : url_base::
1965 : normalize_scheme()
1966 : {
1967 198 : to_lower_impl(id_scheme);
1968 198 : return *this;
1969 : }
1970 :
1971 : inline
1972 : url_base&
1973 544 : url_base::
1974 : normalize_authority()
1975 : {
1976 544 : op_t op(*this);
1977 :
1978 : // normalize host
1979 544 : if (host_type() == urls::host_type::name)
1980 : {
1981 351 : normalize_octets_impl(
1982 : id_host,
1983 : detail::reg_name_chars, op);
1984 : }
1985 544 : decoded_to_lower_impl(id_host);
1986 :
1987 : // normalize password
1988 544 : normalize_octets_impl(id_pass, detail::password_chars, op);
1989 :
1990 : // normalize user
1991 544 : normalize_octets_impl(id_user, detail::user_chars, op);
1992 1088 : return *this;
1993 544 : }
1994 :
1995 : inline
1996 : url_base&
1997 1046 : url_base::
1998 : normalize_path()
1999 : {
2000 1046 : op_t op(*this);
2001 1046 : normalize_octets_impl(id_path, detail::segment_chars, op);
2002 1046 : core::string_view p = impl_.get(id_path);
2003 1046 : char* p_dest = s_ + impl_.offset(id_path);
2004 1046 : char* p_end = s_ + impl_.offset(id_path + 1);
2005 1046 : auto pn = p.size();
2006 1046 : auto skip_dot = 0;
2007 1046 : bool encode_colons = false;
2008 1046 : 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 1046 : if (
2016 1340 : !has_authority() &&
2017 294 : 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 1032 : else if (
2030 1092 : !has_scheme() &&
2031 60 : !has_authority())
2032 : {
2033 41 : 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 34 : first_seg = p;
2064 34 : auto i = first_seg.find('/');
2065 34 : if (i != core::string_view::npos)
2066 17 : first_seg = p.substr(0, i);
2067 34 : encode_colons = first_seg.contains(':');
2068 : }
2069 : }
2070 :
2071 : //------------------------------------------------
2072 : //
2073 : // Encode colons in the first segment
2074 : //
2075 1046 : 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 != end &&
2093 52 : *it != '/')
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 1046 : p.remove_prefix(skip_dot);
2134 1046 : p_dest += skip_dot;
2135 1046 : auto n = detail::remove_dot_segments(
2136 : p_dest, p_end, p);
2137 :
2138 : //------------------------------------------------
2139 : //
2140 : // Update path parameters
2141 : //
2142 1046 : if (n != pn)
2143 : {
2144 140 : BOOST_ASSERT(n < pn);
2145 140 : shrink_impl(id_path, n + skip_dot, op);
2146 140 : p = encoded_path();
2147 140 : if (p == "/")
2148 9 : impl_.nseg_ = 0;
2149 131 : else if (!p.empty())
2150 258 : impl_.nseg_ = detail::to_size_type(
2151 129 : std::count(
2152 258 : p.begin() + 1, p.end(), '/') + 1);
2153 : else
2154 2 : impl_.nseg_ = 0;
2155 140 : impl_.decoded_[id_path] =
2156 140 : detail::to_size_type(detail::decode_bytes_unsafe(
2157 : impl_.get(id_path)));
2158 : }
2159 1046 : return *this;
2160 1046 : }
2161 :
2162 : inline
2163 : url_base&
2164 199 : url_base::
2165 : normalize_query()
2166 : {
2167 199 : op_t op(*this);
2168 199 : normalize_octets_impl(
2169 : id_query,
2170 : detail::query_chars,
2171 : detail::query_ignore_chars,
2172 : op);
2173 398 : return *this;
2174 199 : }
2175 :
2176 : inline
2177 : url_base&
2178 196 : url_base::
2179 : normalize_fragment()
2180 : {
2181 196 : op_t op(*this);
2182 196 : normalize_octets_impl(
2183 : id_frag, detail::fragment_chars, op);
2184 392 : return *this;
2185 196 : }
2186 :
2187 : inline
2188 : url_base&
2189 195 : url_base::
2190 : normalize()
2191 : {
2192 195 : normalize_fragment();
2193 195 : normalize_query();
2194 195 : normalize_path();
2195 195 : normalize_authority();
2196 195 : normalize_scheme();
2197 195 : return *this;
2198 : }
2199 :
2200 : //------------------------------------------------
2201 : //
2202 : // Implementation
2203 : //
2204 : //------------------------------------------------
2205 :
2206 : inline
2207 : void
2208 43324 : url_base::
2209 : check_invariants() const noexcept
2210 : {
2211 43324 : BOOST_ASSERT(
2212 : impl_.len(id_scheme) == 0 ||
2213 : impl_.get(id_scheme).ends_with(':'));
2214 43324 : BOOST_ASSERT(
2215 : impl_.len(id_user) == 0 ||
2216 : impl_.get(id_user).starts_with("//"));
2217 43324 : BOOST_ASSERT(
2218 : impl_.len(id_pass) == 0 ||
2219 : impl_.get(id_user).starts_with("//"));
2220 43324 : 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 43324 : BOOST_ASSERT(
2228 : impl_.len(id_user, id_path) == 0 ||
2229 : impl_.get(id_user).starts_with("//"));
2230 43324 : BOOST_ASSERT(impl_.decoded_[id_path] >=
2231 : ((impl_.len(id_path) + 2) / 3));
2232 43324 : BOOST_ASSERT(
2233 : impl_.len(id_port) == 0 ||
2234 : impl_.get(id_port).starts_with(':'));
2235 43324 : BOOST_ASSERT(
2236 : impl_.len(id_query) == 0 ||
2237 : impl_.get(id_query).starts_with('?'));
2238 43324 : BOOST_ASSERT(
2239 : (impl_.len(id_query) == 0 && impl_.nparam_ == 0) ||
2240 : (impl_.len(id_query) > 0 && impl_.nparam_ > 0));
2241 43324 : BOOST_ASSERT(
2242 : impl_.len(id_frag) == 0 ||
2243 : impl_.get(id_frag).starts_with('#'));
2244 43324 : BOOST_ASSERT(c_str()[size()] == '\0');
2245 43324 : }
2246 :
2247 : inline
2248 : char*
2249 5492 : url_base::
2250 : resize_impl(
2251 : int id,
2252 : std::size_t new_size,
2253 : op_t& op)
2254 : {
2255 5492 : return resize_impl(
2256 5492 : id, id + 1, new_size, op);
2257 : }
2258 :
2259 : inline
2260 : char*
2261 6217 : url_base::
2262 : resize_impl(
2263 : int first,
2264 : int last,
2265 : std::size_t new_len,
2266 : op_t& op)
2267 : {
2268 6217 : auto const n0 = impl_.len(first, last);
2269 6217 : if(new_len == 0 && n0 == 0)
2270 1090 : return s_ + impl_.offset(first);
2271 5127 : if(new_len <= n0)
2272 2193 : return shrink_impl(
2273 2193 : first, last, new_len, op);
2274 :
2275 : // growing
2276 2934 : std::size_t n = new_len - n0;
2277 2934 : reserve_impl(size() + n, op);
2278 : auto const pos =
2279 2934 : impl_.offset(last);
2280 : // adjust chars
2281 2934 : op.move(
2282 2934 : s_ + pos + n,
2283 2934 : s_ + pos,
2284 2934 : impl_.offset(id_end) -
2285 : pos + 1);
2286 : // collapse (first, last)
2287 2934 : impl_.collapse(first, last,
2288 2934 : impl_.offset(last) + n);
2289 : // shift (last, end) right
2290 2934 : impl_.adjust_right(last, id_end, n);
2291 2934 : s_[size()] = '\0';
2292 2934 : return s_ + impl_.offset(first);
2293 : }
2294 :
2295 : inline
2296 : char*
2297 175 : url_base::
2298 : shrink_impl(
2299 : int id,
2300 : std::size_t new_size,
2301 : op_t& op)
2302 : {
2303 175 : return shrink_impl(
2304 175 : id, id + 1, new_size, op);
2305 : }
2306 :
2307 : inline
2308 : char*
2309 2368 : 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 2368 : auto const n0 = impl_.len(first, last);
2318 2368 : BOOST_ASSERT(new_len <= n0);
2319 2368 : std::size_t n = n0 - new_len;
2320 : auto const pos =
2321 2368 : impl_.offset(last);
2322 : // adjust chars
2323 2368 : op.move(
2324 2368 : s_ + pos - n,
2325 2368 : s_ + pos,
2326 2368 : impl_.offset(
2327 2368 : id_end) - pos + 1);
2328 : // collapse (first, last)
2329 2368 : impl_.collapse(first, last,
2330 2368 : impl_.offset(last) - n);
2331 : // shift (last, end) left
2332 2368 : impl_.adjust_left(last, id_end, n);
2333 2368 : s_[size()] = '\0';
2334 2368 : return s_ + impl_.offset(first);
2335 : }
2336 :
2337 : //------------------------------------------------
2338 :
2339 : inline
2340 : void
2341 526 : url_base::
2342 : set_scheme_impl(
2343 : core::string_view s,
2344 : urls::scheme id)
2345 : {
2346 526 : op_t op(*this, &s);
2347 526 : check_invariants();
2348 574 : grammar::parse(
2349 48 : s, detail::scheme_rule()
2350 574 : ).value(BOOST_URL_POS);
2351 478 : auto const n = s.size();
2352 478 : auto const p = impl_.offset(id_path);
2353 :
2354 : // check for "./" prefix
2355 : bool const has_dot =
2356 1434 : [this, p]
2357 : {
2358 478 : if(impl_.nseg_ == 0)
2359 213 : return false;
2360 265 : if(first_segment().size() < 2)
2361 42 : return false;
2362 223 : auto const src = s_ + p;
2363 223 : if(src[0] != '.')
2364 220 : return false;
2365 3 : if(src[1] != '/')
2366 MIS 0 : return false;
2367 HIT 3 : return true;
2368 478 : }();
2369 :
2370 : // Remove "./"
2371 478 : 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 478 : auto dest = resize_impl(
2389 : id_scheme, n + 1, op);
2390 478 : s.copy(dest, n);
2391 478 : dest[n] = ':';
2392 478 : impl_.scheme_ = id;
2393 478 : check_invariants();
2394 526 : }
2395 :
2396 : inline
2397 : char*
2398 396 : url_base::
2399 : set_user_impl(
2400 : std::size_t n,
2401 : op_t& op)
2402 : {
2403 396 : check_invariants();
2404 396 : if(impl_.len(id_pass) != 0)
2405 : {
2406 : // keep "//"
2407 147 : auto dest = resize_impl(
2408 : id_user, 2 + n, op);
2409 147 : check_invariants();
2410 147 : return dest + 2;
2411 : }
2412 : // add authority
2413 : bool const make_absolute =
2414 333 : !is_path_absolute() &&
2415 84 : !impl_.get(id_path).empty();
2416 498 : auto dest = resize_impl(
2417 249 : id_user, 2 + n + 1 + make_absolute, op);
2418 249 : impl_.split(id_user, 2 + n);
2419 249 : dest[0] = '/';
2420 249 : dest[1] = '/';
2421 249 : dest[2 + n] = '@';
2422 249 : if (make_absolute)
2423 : {
2424 6 : impl_.split(id_pass, 1);
2425 6 : impl_.split(id_host, 0);
2426 6 : impl_.split(id_port, 0);
2427 6 : dest[3 + n] = '/';
2428 : }
2429 249 : check_invariants();
2430 249 : return dest + 2;
2431 : }
2432 :
2433 : inline
2434 : char*
2435 377 : url_base::
2436 : set_password_impl(
2437 : std::size_t n,
2438 : op_t& op)
2439 : {
2440 377 : check_invariants();
2441 377 : if(impl_.len(id_user) != 0)
2442 : {
2443 : // already have authority
2444 313 : auto const dest = resize_impl(
2445 : id_pass, 1 + n + 1, op);
2446 313 : dest[0] = ':';
2447 313 : dest[n + 1] = '@';
2448 313 : check_invariants();
2449 313 : return dest + 1;
2450 : }
2451 : // add authority
2452 : bool const make_absolute =
2453 104 : !is_path_absolute() &&
2454 40 : !impl_.get(id_path).empty();
2455 : auto const dest =
2456 128 : resize_impl(
2457 : id_user, id_host,
2458 64 : 2 + 1 + n + 1 + make_absolute, op);
2459 64 : impl_.split(id_user, 2);
2460 64 : dest[0] = '/';
2461 64 : dest[1] = '/';
2462 64 : dest[2] = ':';
2463 64 : dest[2 + n + 1] = '@';
2464 64 : if (make_absolute)
2465 : {
2466 7 : impl_.split(id_pass, 2 + n);
2467 7 : impl_.split(id_host, 0);
2468 7 : impl_.split(id_port, 0);
2469 7 : dest[4 + n] = '/';
2470 : }
2471 64 : check_invariants();
2472 64 : return dest + 3;
2473 : }
2474 :
2475 : inline
2476 : char*
2477 277 : url_base::
2478 : set_userinfo_impl(
2479 : std::size_t n,
2480 : op_t& op)
2481 : {
2482 : // "//" {dest} "@"
2483 277 : check_invariants();
2484 : bool const make_absolute =
2485 406 : !is_path_absolute() &&
2486 129 : !impl_.get(id_path).empty();
2487 554 : auto dest = resize_impl(
2488 277 : id_user, id_host, n + 3 + make_absolute, op);
2489 277 : impl_.split(id_user, n + 2);
2490 277 : dest[0] = '/';
2491 277 : dest[1] = '/';
2492 277 : dest[n + 2] = '@';
2493 277 : if (make_absolute)
2494 : {
2495 3 : impl_.split(id_pass, 1);
2496 3 : impl_.split(id_host, 0);
2497 3 : impl_.split(id_port, 0);
2498 3 : dest[3 + n] = '/';
2499 : }
2500 277 : check_invariants();
2501 277 : return dest + 2;
2502 : }
2503 :
2504 : inline
2505 : char*
2506 721 : url_base::
2507 : set_host_impl(
2508 : std::size_t n,
2509 : op_t& op)
2510 : {
2511 721 : check_invariants();
2512 721 : if(impl_.len(id_user) == 0)
2513 : {
2514 : // add authority
2515 : bool make_absolute =
2516 320 : !is_path_absolute() &&
2517 147 : impl_.len(id_path) != 0;
2518 173 : auto pn = impl_.len(id_path);
2519 346 : auto dest = resize_impl(
2520 173 : id_user, n + 2 + make_absolute, op);
2521 173 : impl_.split(id_user, 2);
2522 173 : impl_.split(id_pass, 0);
2523 173 : impl_.split(id_host, n);
2524 173 : impl_.split(id_port, 0);
2525 173 : impl_.split(id_path, pn + make_absolute);
2526 173 : if (make_absolute)
2527 : {
2528 9 : dest[n + 2] = '/';
2529 9 : ++impl_.decoded_[id_path];
2530 : }
2531 173 : dest[0] = '/';
2532 173 : dest[1] = '/';
2533 173 : check_invariants();
2534 173 : return dest + 2;
2535 : }
2536 : // already have authority
2537 548 : auto const dest = resize_impl(
2538 : id_host, n, op);
2539 548 : check_invariants();
2540 548 : return dest;
2541 : }
2542 :
2543 : inline
2544 : char*
2545 604 : url_base::
2546 : set_port_impl(
2547 : std::size_t n,
2548 : op_t& op)
2549 : {
2550 604 : check_invariants();
2551 604 : if(impl_.len(id_user) != 0)
2552 : {
2553 : // authority exists
2554 503 : auto dest = resize_impl(
2555 : id_port, n + 1, op);
2556 503 : dest[0] = ':';
2557 503 : check_invariants();
2558 503 : return dest + 1;
2559 : }
2560 : bool make_absolute =
2561 174 : !is_path_absolute() &&
2562 73 : impl_.len(id_path) != 0;
2563 202 : auto dest = resize_impl(
2564 101 : id_user, 3 + n + make_absolute, op);
2565 101 : impl_.split(id_user, 2);
2566 101 : impl_.split(id_pass, 0);
2567 101 : impl_.split(id_host, 0);
2568 101 : dest[0] = '/';
2569 101 : dest[1] = '/';
2570 101 : dest[2] = ':';
2571 101 : if (make_absolute)
2572 : {
2573 8 : impl_.split(id_port, n + 1);
2574 8 : dest[n + 3] = '/';
2575 8 : ++impl_.decoded_[id_path];
2576 : }
2577 101 : check_invariants();
2578 101 : return dest + 3;
2579 : }
2580 :
2581 : inline
2582 : char*
2583 597 : url_base::
2584 : set_path_impl(
2585 : std::size_t n,
2586 : op_t& op)
2587 : {
2588 597 : check_invariants();
2589 597 : auto const dest = resize_impl(
2590 : id_path, n, op);
2591 597 : 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 303 : url_base::
2602 : first_segment() const noexcept
2603 : {
2604 303 : if(impl_.nseg_ == 0)
2605 9 : return {};
2606 294 : auto const p0 = impl_.cs_ +
2607 294 : impl_.offset(id_path) +
2608 294 : detail::path_prefix(
2609 294 : impl_.get(id_path));
2610 294 : auto const end = impl_.cs_ +
2611 294 : impl_.offset(id_query);
2612 294 : if(impl_.nseg_ == 1)
2613 182 : return core::string_view(
2614 91 : p0, end - p0);
2615 203 : auto p = p0;
2616 910 : while(*p != '/')
2617 : {
2618 707 : BOOST_ASSERT(p < end);
2619 707 : ++p;
2620 : }
2621 203 : return core::string_view(p0, p - p0);
2622 : }
2623 :
2624 : inline
2625 : detail::segments_iter_impl
2626 2315 : url_base::
2627 : edit_segments(
2628 : detail::segments_iter_impl const& it0,
2629 : detail::segments_iter_impl const& it1,
2630 : detail::any_segments_iter&& src,
2631 : // -1 = preserve
2632 : // 0 = make relative (can fail)
2633 : // 1 = make absolute
2634 : int absolute)
2635 : {
2636 : // Iterator doesn't belong to this url
2637 2315 : BOOST_ASSERT(it0.ref.alias_of(impl_));
2638 :
2639 : // Iterator doesn't belong to this url
2640 2315 : BOOST_ASSERT(it1.ref.alias_of(impl_));
2641 :
2642 : // Iterator is in the wrong order
2643 2315 : BOOST_ASSERT(it0.index <= it1.index);
2644 :
2645 : // Iterator is out of range
2646 2315 : BOOST_ASSERT(it0.index <= impl_.nseg_);
2647 2315 : BOOST_ASSERT(it0.pos <= impl_.len(id_path));
2648 :
2649 : // Iterator is out of range
2650 2315 : BOOST_ASSERT(it1.index <= impl_.nseg_);
2651 2315 : BOOST_ASSERT(it1.pos <= impl_.len(id_path));
2652 :
2653 : //------------------------------------------------
2654 : //
2655 : // Calculate output prefix
2656 : //
2657 : // 0 = ""
2658 : // 1 = "/"
2659 : // 2 = "./"
2660 : // 3 = "/./"
2661 : //
2662 2315 : bool const is_abs = is_path_absolute();
2663 2315 : if(has_authority())
2664 : {
2665 : // Check if the new
2666 : // path would be empty
2667 1855 : if( src.fast_nseg == 0 &&
2668 911 : it0.index == 0 &&
2669 655 : it1.index == impl_.nseg_)
2670 : {
2671 : // VFALCO we don't have
2672 : // access to nchar this early
2673 : //
2674 : //BOOST_ASSERT(nchar == 0);
2675 627 : absolute = 0;
2676 : }
2677 : else
2678 : {
2679 : // prefix "/" required
2680 1228 : absolute = 1;
2681 : }
2682 : }
2683 460 : else if(absolute < 0)
2684 : {
2685 460 : absolute = is_abs; // preserve
2686 : }
2687 2315 : auto const path_pos = impl_.offset(id_path);
2688 :
2689 2315 : std::size_t nchar = 0;
2690 2315 : std::size_t prefix = 0;
2691 2315 : bool encode_colons = false;
2692 2315 : bool cp_src_prefix = false;
2693 2315 : if(it0.index > 0)
2694 : {
2695 : // first segment unchanged
2696 870 : prefix = src.fast_nseg > 0;
2697 : }
2698 1445 : else if(src.fast_nseg > 0)
2699 : {
2700 : // first segment from src
2701 718 : if(! src.front.empty())
2702 : {
2703 640 : if( src.front == "." &&
2704 7 : src.fast_nseg > 1)
2705 4 : if (src.s.empty())
2706 : {
2707 : // if front is ".", we need the extra "." in the prefix
2708 : // which will maintain the invariant that segments represent
2709 : // {"."}
2710 4 : prefix = 2 + absolute;
2711 : }
2712 : else
2713 : {
2714 : // if the "." prefix is explicitly required from set_path
2715 : // we do not include an extra "." segment
2716 MIS 0 : prefix = absolute;
2717 0 : cp_src_prefix = true;
2718 : }
2719 HIT 629 : else if(absolute)
2720 542 : prefix = 1;
2721 166 : else if(has_scheme() ||
2722 79 : ! src.front.contains(':'))
2723 82 : prefix = 0;
2724 : else
2725 : {
2726 5 : prefix = 0;
2727 5 : encode_colons = true;
2728 : }
2729 : }
2730 : else
2731 : {
2732 85 : prefix = 2 + absolute;
2733 : }
2734 : }
2735 : else
2736 : {
2737 : // first segment from it1
2738 727 : auto const p =
2739 727 : impl_.cs_ + path_pos + it1.pos;
2740 1454 : switch(impl_.cs_ +
2741 727 : impl_.offset(id_query) - p)
2742 : {
2743 680 : case 0:
2744 : // points to end
2745 680 : prefix = absolute;
2746 680 : break;
2747 36 : default:
2748 36 : BOOST_ASSERT(*p == '/');
2749 36 : if(p[1] != '/')
2750 : {
2751 35 : if(absolute)
2752 29 : prefix = 1;
2753 11 : else if(has_scheme() ||
2754 11 : ! it1.dereference().contains(':'))
2755 5 : prefix = 0;
2756 : else
2757 1 : prefix = 2;
2758 35 : break;
2759 : }
2760 : // empty
2761 : BOOST_FALLTHROUGH;
2762 : case 1:
2763 : // empty
2764 12 : BOOST_ASSERT(*p == '/');
2765 12 : prefix = 2 + absolute;
2766 12 : break;
2767 : }
2768 : }
2769 :
2770 : // append '/' to new segs
2771 : // if inserting at front.
2772 2315 : std::size_t const suffix =
2773 3234 : it1.index == 0 &&
2774 2494 : impl_.nseg_ > 0 &&
2775 179 : src.fast_nseg > 0;
2776 :
2777 : //------------------------------------------------
2778 : //
2779 : // Measure the number of encoded characters
2780 : // of output, and the number of inserted
2781 : // segments including internal separators.
2782 : //
2783 2315 : src.encode_colons = encode_colons;
2784 2315 : std::size_t nseg = 0;
2785 2315 : if(src.measure(nchar))
2786 : {
2787 1279 : src.encode_colons = false;
2788 : for(;;)
2789 : {
2790 1605 : ++nseg;
2791 1605 : if(! src.measure(nchar))
2792 1277 : break;
2793 326 : ++nchar;
2794 : }
2795 : }
2796 :
2797 2313 : switch(src.fast_nseg)
2798 : {
2799 1036 : case 0:
2800 1036 : BOOST_ASSERT(nseg == 0);
2801 1036 : break;
2802 1089 : case 1:
2803 1089 : BOOST_ASSERT(nseg == 1);
2804 1089 : break;
2805 188 : case 2:
2806 188 : BOOST_ASSERT(nseg >= 2);
2807 188 : break;
2808 : }
2809 :
2810 : //------------------------------------------------
2811 : //
2812 : // Calculate [pos0, pos1) to remove
2813 : //
2814 2313 : auto pos0 = it0.pos;
2815 2313 : if(it0.index == 0)
2816 : {
2817 : // patch pos for prefix
2818 1443 : pos0 = 0;
2819 : }
2820 2313 : auto pos1 = it1.pos;
2821 2313 : if(it1.index == 0)
2822 : {
2823 : // patch pos for prefix
2824 919 : pos1 = detail::path_prefix(
2825 : impl_.get(id_path));
2826 : }
2827 1394 : else if(
2828 1394 : it0.index == 0 &&
2829 524 : it1.index < impl_.nseg_ &&
2830 : nseg == 0)
2831 : {
2832 : // Remove the slash from segment it1
2833 : // if it is becoming the new first
2834 : // segment.
2835 47 : ++pos1;
2836 : }
2837 : // calc decoded size of old range
2838 : auto const dn0 =
2839 2313 : detail::decode_bytes_unsafe(
2840 : core::string_view(
2841 2313 : impl_.cs_ +
2842 2313 : impl_.offset(id_path) +
2843 : pos0,
2844 : pos1 - pos0));
2845 :
2846 : //------------------------------------------------
2847 : //
2848 : // Resize
2849 : //
2850 4626 : op_t op(*this, &src.s);
2851 : char* dest;
2852 : char const* end;
2853 : {
2854 2313 : auto const nremove = pos1 - pos0;
2855 : // check overflow
2856 4626 : if( nchar <= max_size() && (
2857 2313 : prefix + suffix <=
2858 2313 : max_size() - nchar))
2859 : {
2860 2313 : nchar = prefix + nchar + suffix;
2861 3484 : if( nchar <= nremove ||
2862 1171 : nchar - nremove <=
2863 1171 : max_size() - size())
2864 2313 : goto ok;
2865 : }
2866 : // too large
2867 MIS 0 : detail::throw_length_error();
2868 HIT 2313 : ok:
2869 : auto const new_size =
2870 2313 : size() + nchar - nremove;
2871 2313 : reserve_impl(new_size, op);
2872 2313 : dest = s_ + path_pos + pos0;
2873 2313 : op.move(
2874 2313 : dest + nchar,
2875 2313 : s_ + path_pos + pos1,
2876 2313 : size() - path_pos - pos1);
2877 4626 : impl_.set_size(
2878 : id_path,
2879 2313 : impl_.len(id_path) + nchar - nremove);
2880 2313 : BOOST_ASSERT(size() == new_size);
2881 2313 : end = dest + nchar;
2882 2313 : auto const nseg1 =
2883 2313 : static_cast<std::ptrdiff_t>(impl_.nseg_) +
2884 2313 : static_cast<std::ptrdiff_t>(nseg) -
2885 2313 : static_cast<std::ptrdiff_t>(it1.index) +
2886 2313 : static_cast<std::ptrdiff_t>(it0.index) -
2887 : static_cast<std::ptrdiff_t>(cp_src_prefix);
2888 2313 : BOOST_ASSERT(nseg1 >= 0);
2889 2313 : impl_.nseg_ = detail::to_size_type(nseg1);
2890 2313 : if(s_)
2891 2306 : s_[size()] = '\0';
2892 : }
2893 :
2894 : //------------------------------------------------
2895 : //
2896 : // Output segments and internal separators:
2897 : //
2898 : // prefix [ segment [ '/' segment ] ] suffix
2899 : //
2900 2313 : auto const dest0 = dest;
2901 2313 : switch(prefix)
2902 : {
2903 60 : case 3:
2904 60 : *dest++ = '/';
2905 60 : *dest++ = '.';
2906 60 : *dest++ = '/';
2907 60 : break;
2908 42 : case 2:
2909 42 : *dest++ = '.';
2910 : BOOST_FALLTHROUGH;
2911 1193 : case 1:
2912 1193 : *dest++ = '/';
2913 1193 : break;
2914 1060 : default:
2915 1060 : break;
2916 : }
2917 2313 : src.rewind();
2918 2313 : if(nseg > 0)
2919 : {
2920 1277 : src.encode_colons = encode_colons;
2921 : for(;;)
2922 : {
2923 1603 : src.copy(dest, end);
2924 1603 : if(--nseg == 0)
2925 1277 : break;
2926 326 : *dest++ = '/';
2927 326 : src.encode_colons = false;
2928 : }
2929 1277 : if(suffix)
2930 179 : *dest++ = '/';
2931 : }
2932 2313 : BOOST_ASSERT(dest == dest0 + nchar);
2933 :
2934 : // calc decoded size of new range,
2935 : auto const dn =
2936 2313 : detail::decode_bytes_unsafe(
2937 2313 : core::string_view(dest0, dest - dest0));
2938 2313 : if(dn >= dn0)
2939 1401 : impl_.decoded_[id_path] +=
2940 1401 : detail::to_size_type(dn - dn0);
2941 : else
2942 912 : impl_.decoded_[id_path] -=
2943 912 : detail::to_size_type(dn0 - dn);
2944 :
2945 : return detail::segments_iter_impl(
2946 4626 : impl_, pos0, it0.index);
2947 : }
2948 :
2949 : //------------------------------------------------
2950 :
2951 : inline
2952 : auto
2953 1695 : url_base::
2954 : edit_params(
2955 : detail::params_iter_impl const& it0,
2956 : detail::params_iter_impl const& it1,
2957 : detail::any_params_iter&& src) ->
2958 : detail::params_iter_impl
2959 : {
2960 1695 : auto pos0 = impl_.offset(id_query);
2961 1695 : auto pos1 = pos0 + it1.pos;
2962 1695 : pos0 = pos0 + it0.pos;
2963 :
2964 : // Iterators belong to this url
2965 1695 : BOOST_ASSERT(it0.ref.alias_of(impl_));
2966 1695 : BOOST_ASSERT(it1.ref.alias_of(impl_));
2967 :
2968 : // Iterators is in the right order
2969 1695 : BOOST_ASSERT(it0.index <= it1.index);
2970 :
2971 : // Iterators are within range
2972 1695 : BOOST_ASSERT(it0.index <= impl_.nparam_);
2973 1695 : BOOST_ASSERT(pos0 <= impl_.offset(id_frag));
2974 1695 : BOOST_ASSERT(it1.index <= impl_.nparam_);
2975 1695 : BOOST_ASSERT(pos1 <= impl_.offset(id_frag));
2976 :
2977 : // calc decoded size of old range,
2978 : // minus one if '?' or '&' prefixed
2979 : auto dn0 =
2980 : static_cast<std::ptrdiff_t>(
2981 1695 : detail::decode_bytes_unsafe(
2982 : core::string_view(
2983 1695 : impl_.cs_ + pos0,
2984 1695 : pos1 - pos0)));
2985 1695 : if(impl_.len(id_query) > 0)
2986 1157 : dn0 -= 1;
2987 1695 : if(dn0 < 0)
2988 552 : dn0 = 0;
2989 :
2990 : //------------------------------------------------
2991 : //
2992 : // Measure the number of encoded characters
2993 : // of output, and the number of inserted
2994 : // segments including internal separators.
2995 : //
2996 :
2997 1695 : std::size_t nchar = 0;
2998 1695 : std::size_t nparam = 0;
2999 1695 : if(src.measure(nchar))
3000 : {
3001 1430 : ++nchar; // for '?' or '&'
3002 : for(;;)
3003 : {
3004 1652 : ++nparam;
3005 1652 : if(! src.measure(nchar))
3006 1430 : break;
3007 222 : ++nchar; // for '&'
3008 : }
3009 : }
3010 :
3011 : //------------------------------------------------
3012 : //
3013 : // Resize
3014 : //
3015 1690 : op_t op(*this, &src.s0, &src.s1);
3016 : char* dest;
3017 : char const* end;
3018 : {
3019 1690 : auto const nremove = pos1 - pos0;
3020 : // check overflow
3021 2995 : if( nchar > nremove &&
3022 1305 : nchar - nremove >
3023 1305 : max_size() - size())
3024 : {
3025 : // too large
3026 MIS 0 : detail::throw_length_error();
3027 : }
3028 HIT 1690 : auto const nparam1 =
3029 1690 : static_cast<std::ptrdiff_t>(impl_.nparam_) +
3030 1690 : static_cast<std::ptrdiff_t>(nparam) -
3031 1690 : static_cast<std::ptrdiff_t>(it1.index) +
3032 1690 : static_cast<std::ptrdiff_t>(it0.index);
3033 1690 : BOOST_ASSERT(nparam1 >= 0);
3034 1690 : reserve_impl(size() + nchar - nremove, op);
3035 1690 : dest = s_ + pos0;
3036 1690 : end = dest + nchar;
3037 1690 : if(impl_.nparam_ > 0)
3038 : {
3039 : // needed when we move
3040 : // the beginning of the query
3041 1152 : s_[impl_.offset(id_query)] = '&';
3042 : }
3043 1690 : op.move(
3044 1690 : dest + nchar,
3045 1690 : impl_.cs_ + pos1,
3046 1690 : size() - pos1);
3047 3380 : impl_.set_size(
3048 : id_query,
3049 1690 : impl_.len(id_query) +
3050 : nchar - nremove);
3051 1690 : impl_.nparam_ =
3052 1690 : detail::to_size_type(nparam1);
3053 1690 : if(nparam1 > 0)
3054 : {
3055 : // needed when we erase
3056 : // the beginning of the query
3057 1587 : s_[impl_.offset(id_query)] = '?';
3058 : }
3059 1690 : if(s_)
3060 1690 : s_[size()] = '\0';
3061 : }
3062 1690 : auto const dest0 = dest;
3063 :
3064 : //------------------------------------------------
3065 : //
3066 : // Output params and internal separators:
3067 : //
3068 : // [ '?' param ] [ '&' param ]
3069 : //
3070 1690 : if(nparam > 0)
3071 : {
3072 1430 : if(it0.index == 0)
3073 887 : *dest++ = '?';
3074 : else
3075 543 : *dest++ = '&';
3076 1430 : src.rewind();
3077 : for(;;)
3078 : {
3079 1652 : src.copy(dest, end);
3080 1652 : if(--nparam == 0)
3081 1430 : break;
3082 222 : *dest++ = '&';
3083 : }
3084 : }
3085 :
3086 : // calc decoded size of new range,
3087 : // minus one if '?' or '&' prefixed
3088 : auto dn =
3089 : static_cast<std::ptrdiff_t>(
3090 1690 : detail::decode_bytes_unsafe(
3091 1690 : core::string_view(dest0, dest - dest0)));
3092 1690 : if(impl_.len(id_query) > 0)
3093 1587 : dn -= 1;
3094 1690 : if(dn < 0)
3095 157 : dn = 0;
3096 :
3097 1690 : if(dn >= dn0)
3098 1310 : impl_.decoded_[id_query] +=
3099 1310 : detail::to_size_type(dn - dn0);
3100 : else
3101 380 : impl_.decoded_[id_query] -=
3102 380 : detail::to_size_type(dn0 - dn);
3103 :
3104 : return detail::params_iter_impl(
3105 1690 : impl_,
3106 1690 : pos0 - impl_.offset_[id_query],
3107 3380 : it0.index);
3108 1690 : }
3109 :
3110 : //------------------------------------------------
3111 :
3112 : inline
3113 : void
3114 544 : url_base::
3115 : decoded_to_lower_impl(int id) noexcept
3116 : {
3117 544 : char* it = s_ + impl_.offset(id);
3118 544 : char const* const end = s_ + impl_.offset(id + 1);
3119 3540 : while(it < end)
3120 : {
3121 2996 : if (*it != '%')
3122 : {
3123 5962 : *it = grammar::to_lower(
3124 2981 : *it);
3125 2981 : ++it;
3126 2981 : continue;
3127 : }
3128 15 : it += 3;
3129 : }
3130 544 : }
3131 :
3132 : inline
3133 : void
3134 198 : url_base::
3135 : to_lower_impl(int id) noexcept
3136 : {
3137 198 : char* it = s_ + impl_.offset(id);
3138 198 : char const* const end = s_ + impl_.offset(id + 1);
3139 1027 : while(it < end)
3140 : {
3141 1658 : *it = grammar::to_lower(
3142 829 : *it);
3143 829 : ++it;
3144 : }
3145 198 : }
3146 :
3147 : } // urls
3148 : } // boost
3149 :
3150 : #endif
|