include/boost/url/impl/url_base.hpp

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