GCC Code Coverage Report


Directory: libs/url/
File: libs/url/src/url_base.cpp
Date: 2024-02-29 20:02:56
Exec Total Coverage
Lines: 1338 1343 99.6%
Functions: 74 74 100.0%
Branches: 687 881 78.0%

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