libs/url/src/url_base.cpp

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