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