GCC Code Coverage Report


Directory: libs/url/
File: src/url_base.cpp
Date: 2025-11-10 19:06:22
Exec Total Coverage
Lines: 1472 1477 99.7%
Functions: 80 80 100.0%
Branches: 739 959 77.1%

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