GCC Code Coverage Report


Directory: libs/url/
File: libs/url/src/url_base.cpp
Date: 2023-12-15 15:31:49
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 <boost/url/detail/decode.hpp>
24 #include <boost/url/detail/encode.hpp>
25 #include <boost/url/detail/except.hpp>
26 #include <boost/url/detail/move_chars.hpp>
27 #include <boost/url/detail/normalize.hpp>
28 #include <boost/url/detail/path.hpp>
29 #include <boost/url/detail/print.hpp>
30 #include <boost/url/grammar/ci_string.hpp>
31 #include <boost/url/rfc/authority_rule.hpp>
32 #include <boost/url/rfc/query_rule.hpp>
33 #include <boost/url/rfc/detail/charsets.hpp>
34 #include <boost/url/rfc/detail/host_rule.hpp>
35 #include <boost/url/rfc/detail/ipvfuture_rule.hpp>
36 #include <boost/url/rfc/detail/path_rules.hpp>
37 #include <boost/url/rfc/detail/port_rule.hpp>
38 #include <boost/url/rfc/detail/scheme_rule.hpp>
39 #include <boost/url/rfc/detail/userinfo_rule.hpp>
40 #include <boost/url/grammar/parse.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 7909 url_base::
56 op_t::
57 7909 ~op_t()
58 {
59
2/2
✓ Branch 0 taken 993 times.
✓ Branch 1 taken 6916 times.
7909 if(old)
60 993 u.cleanup(*this);
61 7909 u.check_invariants();
62 7909 }
63
64 7909 url_base::
65 op_t::
66 op_t(
67 url_base& impl_,
68 core::string_view* s0_,
69 7909 core::string_view* s1_) noexcept
70 : u(impl_)
71 , s0(s0_)
72 7909 , s1(s1_)
73 {
74 7909 u.check_invariants();
75 7909 }
76
77 void
78 2278 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 370 times.
✓ Branch 1 taken 1908 times.
2278 if(! n)
86 370 return;
87
2/2
✓ Branch 0 taken 1226 times.
✓ Branch 1 taken 682 times.
1908 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 682 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 3637 url_base::
122 copy(url_view_base const& u)
123 {
124
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3637 times.
3637 if (this == &u)
125 117 return;
126 3640 op_t op(*this);
127
2/2
✓ Branch 1 taken 117 times.
✓ Branch 2 taken 3520 times.
3637 if(u.size() == 0)
128 {
129 117 clear();
130 117 return;
131 }
132
2/2
✓ Branch 1 taken 3517 times.
✓ Branch 2 taken 3 times.
3520 reserve_impl(
133 3520 u.size(), op);
134 3517 impl_ = u.impl_;
135 3517 impl_.cs_ = s_;
136 3517 impl_.from_ = {from::url};
137 3517 std::memcpy(s_,
138 3517 u.data(), u.size());
139 3517 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(id_user, id_end, 0 - sn);
228 }
229 else
230 {
231 8 impl_.adjust(id_user, id_path, 0 - sn);
232 8 impl_.adjust(id_query, id_end, 0 - 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(
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 446 url_base::
1334 encoded_segments() noexcept
1335 {
1336 446 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 206 url_base::
1456 remove_query() noexcept
1457 {
1458 206 op_t op(*this);
1459 206 resize_impl(id_query, 0, op);
1460 206 impl_.nparam_ = 0;
1461 206 impl_.decoded_[id_query] = 0;
1462 206 return *this;
1463 }
1464
1465 //------------------------------------------------
1466 //
1467 // Fragment
1468 //
1469 //------------------------------------------------
1470
1471 url_base&
1472 199 url_base::
1473 remove_fragment() noexcept
1474 {
1475 199 op_t op(*this);
1476 199 resize_impl(id_frag, 0, op);
1477 199 impl_.decoded_[id_frag] = 0;
1478 199 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 446 url_base::
1540 resolve(
1541 url_view_base const& ref)
1542 {
1543
6/6
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 443 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 444 times.
449 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 442 times.
444 if(! has_scheme())
1551 {
1552 2 BOOST_URL_RETURN_EC(error::not_a_base);
1553 }
1554
1555 884 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 181 times.
✓ Branch 3 taken 198 times.
✓ Branch 4 taken 63 times.
✓ Branch 5 taken 198 times.
✓ Branch 6 taken 244 times.
703 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 174 times.
244 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 142 times.
174 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 107 times.
142 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 107 times.
✗ Branch 2 not taken.
107 reserve_impl(
1632 107 impl_.offset(id_query) +
1633 107 ref.size(), op);
1634 // 5.2.3. Merge Paths
1635 107 auto es = encoded_segments();
1636
2/2
✓ Branch 1 taken 102 times.
✓ Branch 2 taken 5 times.
107 if(es.size() > 0)
1637 {
1638 102 es.pop_back();
1639 }
1640 107 es.insert(es.end(),
1641 107 ref.encoded_segments().begin(),
1642
1/2
✓ Branch 5 taken 107 times.
✗ Branch 6 not taken.
214 ref.encoded_segments().end());
1643
1/2
✓ Branch 1 taken 107 times.
✗ Branch 2 not taken.
107 normalize_path();
1644
2/2
✓ Branch 1 taken 10 times.
✓ Branch 2 taken 97 times.
107 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 97 remove_query();
1649
2/2
✓ Branch 1 taken 10 times.
✓ Branch 2 taken 97 times.
107 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 97 remove_fragment();
1654 107 return {};
1655 }
1656
1657 //------------------------------------------------
1658 //
1659 // Normalization
1660 //
1661 //------------------------------------------------
1662
1663 template <class Charset>
1664 void
1665 1895 url_base::
1666 normalize_octets_impl(
1667 int id,
1668 Charset const& allowed,
1669 op_t& op) noexcept
1670 {
1671 1895 char* it = s_ + impl_.offset(id);
1672 1895 char* end = s_ + impl_.offset(id + 1);
1673 1895 char d = 0;
1674 1895 char* dest = it;
1675
2/2
✓ Branch 0 taken 8374 times.
✓ Branch 1 taken 1895 times.
10269 while (it < end)
1676 {
1677
2/2
✓ Branch 0 taken 8267 times.
✓ Branch 1 taken 107 times.
8374 if (*it != '%')
1678 {
1679 8267 *dest = *it;
1680 8267 ++it;
1681 8267 ++dest;
1682 8267 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 1871 times.
1895 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 1895 }
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 815 url_base::
1744 normalize_path()
1745 {
1746 815 op_t op(*this);
1747 815 normalize_octets_impl(id_path, detail::segment_chars, op);
1748 815 core::string_view p = impl_.get(id_path);
1749 815 char* p_dest = s_ + impl_.offset(id_path);
1750 815 char* p_end = s_ + impl_.offset(id_path + 1);
1751 815 auto pn = p.size();
1752 815 auto skip_dot = 0;
1753 815 bool encode_colons = false;
1754 815 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 815 if (
1762
6/6
✓ Branch 1 taken 248 times.
✓ Branch 2 taken 567 times.
✓ Branch 3 taken 14 times.
✓ Branch 4 taken 234 times.
✓ Branch 5 taken 14 times.
✓ Branch 6 taken 801 times.
1063 !has_authority() &&
1763 248 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 801 else if (
1776
4/4
✓ Branch 1 taken 30 times.
✓ Branch 2 taken 771 times.
✓ Branch 3 taken 30 times.
✓ Branch 4 taken 771 times.
831 !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 810 times.
815 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 815 p.remove_prefix(skip_dot);
1880 815 p_dest += skip_dot;
1881 815 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 118 times.
✓ Branch 1 taken 697 times.
815 if (n != pn)
1889 {
1890
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 118 times.
118 BOOST_ASSERT(n < pn);
1891
1/2
✓ Branch 1 taken 118 times.
✗ Branch 2 not taken.
118 shrink_impl(id_path, n + skip_dot, op);
1892 118 p = encoded_path();
1893
2/2
✓ Branch 2 taken 10 times.
✓ Branch 3 taken 108 times.
118 if (p == "/")
1894 10 impl_.nseg_ = 0;
1895
2/2
✓ Branch 1 taken 106 times.
✓ Branch 2 taken 2 times.
108 else if (!p.empty())
1896
1/2
✓ Branch 1 taken 106 times.
✗ Branch 2 not taken.
106 impl_.nseg_ = std::count(
1897 212 p.begin() + 1, p.end(), '/') + 1;
1898 else
1899 2 impl_.nseg_ = 0;
1900 118 impl_.decoded_[id_path] =
1901 118 detail::decode_bytes_unsafe(impl_.get(id_path));
1902 }
1903 1630 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 17507 url_base::
1946 check_invariants() const noexcept
1947 {
1948
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17507 times.
17507 BOOST_ASSERT(pi_);
1949
3/4
✓ Branch 1 taken 10324 times.
✓ Branch 2 taken 7183 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 10324 times.
17507 BOOST_ASSERT(
1950 impl_.len(id_scheme) == 0 ||
1951 impl_.get(id_scheme).ends_with(':'));
1952
3/4
✓ Branch 1 taken 8698 times.
✓ Branch 2 taken 8809 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 8698 times.
17507 BOOST_ASSERT(
1953 impl_.len(id_user) == 0 ||
1954 impl_.get(id_user).starts_with("//"));
1955
3/4
✓ Branch 1 taken 1629 times.
✓ Branch 2 taken 15878 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1629 times.
17507 BOOST_ASSERT(
1956 impl_.len(id_pass) == 0 ||
1957 impl_.get(id_user).starts_with("//"));
1958
8/12
✓ Branch 1 taken 1629 times.
✓ Branch 2 taken 15878 times.
✓ Branch 4 taken 691 times.
✓ Branch 5 taken 938 times.
✓ Branch 9 taken 691 times.
✗ Branch 10 not taken.
✓ Branch 12 taken 938 times.
✗ Branch 13 not taken.
✓ Branch 16 taken 938 times.
✗ Branch 17 not taken.
✓ Branch 20 taken 938 times.
✗ Branch 21 not taken.
17507 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 8698 times.
✓ Branch 2 taken 8809 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 8698 times.
17507 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 17507 times.
17507 BOOST_ASSERT(impl_.decoded_[id_path] >=
1969 ((impl_.len(id_path) + 2) / 3));
1970
3/4
✓ Branch 1 taken 952 times.
✓ Branch 2 taken 16555 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 952 times.
17507 BOOST_ASSERT(
1971 impl_.len(id_port) == 0 ||
1972 impl_.get(id_port).starts_with(':'));
1973
3/4
✓ Branch 1 taken 1869 times.
✓ Branch 2 taken 15638 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1869 times.
17507 BOOST_ASSERT(
1974 impl_.len(id_query) == 0 ||
1975 impl_.get(id_query).starts_with('?'));
1976
5/8
✓ Branch 1 taken 15638 times.
✓ Branch 2 taken 1869 times.
✓ Branch 3 taken 15638 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 1869 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 1869 times.
✗ Branch 9 not taken.
17507 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 585 times.
✓ Branch 2 taken 16922 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 585 times.
17507 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 17507 times.
17507 BOOST_ASSERT(c_str()[size()] == '\0');
1983 17507 }
1984
1985 char*
1986 1480 url_base::
1987 resize_impl(
1988 int id,
1989 std::size_t new_size,
1990 op_t& op)
1991 {
1992 1480 return resize_impl(
1993 1480 id, id + 1, new_size, op);
1994 }
1995
1996 char*
1997 1749 url_base::
1998 resize_impl(
1999 int first,
2000 int last,
2001 std::size_t new_len,
2002 op_t& op)
2003 {
2004 1749 auto const n0 = impl_.len(first, last);
2005
4/4
✓ Branch 0 taken 532 times.
✓ Branch 1 taken 1217 times.
✓ Branch 2 taken 339 times.
✓ Branch 3 taken 193 times.
1749 if(new_len == 0 && n0 == 0)
2006 339 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(last, id_end, n);
2027 909 s_[size()] = '\0';
2028 909 return s_ + impl_.offset(first);
2029 }
2030
2031 char*
2032 142 url_base::
2033 shrink_impl(
2034 int id,
2035 std::size_t new_size,
2036 op_t& op)
2037 {
2038 142 return shrink_impl(
2039 142 id, id + 1, new_size, op);
2040 }
2041
2042 char*
2043 643 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 643 auto const n0 = impl_.len(first, last);
2052
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 643 times.
643 BOOST_ASSERT(new_len <= n0);
2053 643 std::size_t n = n0 - new_len;
2054 auto const pos =
2055 643 impl_.offset(last);
2056 // adjust chars
2057 643 op.move(
2058 643 s_ + pos - n,
2059 643 s_ + pos,
2060 643 impl_.offset(
2061 643 id_end) - pos + 1);
2062 // collapse (first, last)
2063 643 impl_.collapse(first, last,
2064 643 impl_.offset(last) - n);
2065 // shift (last, end) left
2066 643 impl_.adjust(
2067 last, id_end, 0 - n);
2068 643 s_[size()] = '\0';
2069 643 return s_ + impl_.offset(first);
2070 }
2071
2072 //------------------------------------------------
2073
2074 void
2075 61 url_base::
2076 set_scheme_impl(
2077 core::string_view s,
2078 urls::scheme id)
2079 {
2080 122 op_t op(*this, &s);
2081 61 check_invariants();
2082 13 grammar::parse(
2083 13 s, detail::scheme_rule()
2084
2/2
✓ Branch 3 taken 48 times.
✓ Branch 4 taken 13 times.
61 ).value(BOOST_URL_POS);
2085 48 auto const n = s.size();
2086 48 auto const p = impl_.offset(id_path);
2087
2088 // check for "./" prefix
2089 bool const has_dot =
2090 75 [this, p]
2091 {
2092
2/2
✓ Branch 0 taken 30 times.
✓ Branch 1 taken 18 times.
48 if(impl_.nseg_ == 0)
2093 30 return false;
2094
2/2
✓ Branch 2 taken 9 times.
✓ Branch 3 taken 9 times.
18 if(first_segment().size() < 2)
2095 9 return false;
2096 9 auto const src = s_ + p;
2097
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 3 times.
9 if(src[0] != '.')
2098 6 return false;
2099
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if(src[1] != '/')
2100 return false;
2101 3 return true;
2102 48 }();
2103
2104 // Remove "./"
2105
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 45 times.
48 if(has_dot)
2106 {
2107 // do this first, for
2108 // strong exception safety
2109 3 reserve_impl(
2110
1/2
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
3 size() + n + 1 - 2, op);
2111 3 op.move(
2112 3 s_ + p,
2113 3 s_ + p + 2,
2114 3 size() + 1 -
2115 (p + 2));
2116 3 impl_.set_size(
2117 id_path,
2118 3 impl_.len(id_path) - 2);
2119 3 s_[size()] = '\0';
2120 }
2121
2122
1/2
✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
48 auto dest = resize_impl(
2123 id_scheme, n + 1, op);
2124
1/2
✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
48 s.copy(dest, n);
2125 48 dest[n] = ':';
2126 48 impl_.scheme_ = id;
2127 48 check_invariants();
2128 48 }
2129
2130 char*
2131 101 url_base::
2132 set_user_impl(
2133 std::size_t n,
2134 op_t& op)
2135 {
2136 101 check_invariants();
2137
2/2
✓ Branch 1 taken 50 times.
✓ Branch 2 taken 51 times.
101 if(impl_.len(id_pass) != 0)
2138 {
2139 // keep "//"
2140 50 auto dest = resize_impl(
2141 id_user, 2 + n, op);
2142 50 check_invariants();
2143 50 return dest + 2;
2144 }
2145 // add authority
2146 bool const make_absolute =
2147
2/2
✓ Branch 1 taken 40 times.
✓ Branch 2 taken 11 times.
91 !is_path_absolute() &&
2148
2/2
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 36 times.
40 !impl_.get(id_path).empty();
2149 102 auto dest = resize_impl(
2150 51 id_user, 2 + n + 1 + make_absolute, op);
2151 51 impl_.split(id_user, 2 + n);
2152 51 dest[0] = '/';
2153 51 dest[1] = '/';
2154 51 dest[2 + n] = '@';
2155
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 47 times.
51 if (make_absolute)
2156 {
2157 4 impl_.split(id_pass, 1);
2158 4 impl_.split(id_host, 0);
2159 4 impl_.split(id_port, 0);
2160 4 dest[3 + n] = '/';
2161 }
2162 51 check_invariants();
2163 51 return dest + 2;
2164 }
2165
2166 char*
2167 82 url_base::
2168 set_password_impl(
2169 std::size_t n,
2170 op_t& op)
2171 {
2172 82 check_invariants();
2173
2/2
✓ Branch 1 taken 66 times.
✓ Branch 2 taken 16 times.
82 if(impl_.len(id_user) != 0)
2174 {
2175 // already have authority
2176 66 auto const dest = resize_impl(
2177 id_pass, 1 + n + 1, op);
2178 66 dest[0] = ':';
2179 66 dest[n + 1] = '@';
2180 66 check_invariants();
2181 66 return dest + 1;
2182 }
2183 // add authority
2184 bool const make_absolute =
2185
2/2
✓ Branch 1 taken 9 times.
✓ Branch 2 taken 7 times.
25 !is_path_absolute() &&
2186
2/2
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 7 times.
9 !impl_.get(id_path).empty();
2187 auto const dest =
2188 32 resize_impl(
2189 id_user, id_host,
2190 16 2 + 1 + n + 1 + make_absolute, op);
2191 16 impl_.split(id_user, 2);
2192 16 dest[0] = '/';
2193 16 dest[1] = '/';
2194 16 dest[2] = ':';
2195 16 dest[2 + n + 1] = '@';
2196
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 14 times.
16 if (make_absolute)
2197 {
2198 2 impl_.split(id_pass, 2 + n);
2199 2 impl_.split(id_host, 0);
2200 2 impl_.split(id_port, 0);
2201 2 dest[4 + n] = '/';
2202 }
2203 16 check_invariants();
2204 16 return dest + 3;
2205 }
2206
2207 char*
2208 99 url_base::
2209 set_userinfo_impl(
2210 std::size_t n,
2211 op_t& op)
2212 {
2213 // "//" {dest} "@"
2214 99 check_invariants();
2215 bool const make_absolute =
2216
2/2
✓ Branch 1 taken 81 times.
✓ Branch 2 taken 18 times.
180 !is_path_absolute() &&
2217
2/2
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 79 times.
81 !impl_.get(id_path).empty();
2218 198 auto dest = resize_impl(
2219 99 id_user, id_host, n + 3 + make_absolute, op);
2220 99 impl_.split(id_user, n + 2);
2221 99 dest[0] = '/';
2222 99 dest[1] = '/';
2223 99 dest[n + 2] = '@';
2224
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 97 times.
99 if (make_absolute)
2225 {
2226 2 impl_.split(id_pass, 1);
2227 2 impl_.split(id_host, 0);
2228 2 impl_.split(id_port, 0);
2229 2 dest[3 + n] = '/';
2230 }
2231 99 check_invariants();
2232 99 return dest + 2;
2233 }
2234
2235 char*
2236 206 url_base::
2237 set_host_impl(
2238 std::size_t n,
2239 op_t& op)
2240 {
2241 206 check_invariants();
2242
2/2
✓ Branch 1 taken 94 times.
✓ Branch 2 taken 112 times.
206 if(impl_.len(id_user) == 0)
2243 {
2244 // add authority
2245 bool make_absolute =
2246
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() &&
2247 90 impl_.len(id_path) != 0;
2248 94 auto pn = impl_.len(id_path);
2249 188 auto dest = resize_impl(
2250 94 id_user, n + 2 + make_absolute, op);
2251 94 impl_.split(id_user, 2);
2252 94 impl_.split(id_pass, 0);
2253 94 impl_.split(id_host, n);
2254 94 impl_.split(id_port, 0);
2255 94 impl_.split(id_path, pn + make_absolute);
2256
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 87 times.
94 if (make_absolute)
2257 {
2258 7 dest[n + 2] = '/';
2259 7 ++impl_.decoded_[id_path];
2260 }
2261 94 dest[0] = '/';
2262 94 dest[1] = '/';
2263 94 check_invariants();
2264 94 return dest + 2;
2265 }
2266 // already have authority
2267 112 auto const dest = resize_impl(
2268 id_host, n, op);
2269 112 check_invariants();
2270 112 return dest;
2271 }
2272
2273 char*
2274 107 url_base::
2275 set_port_impl(
2276 std::size_t n,
2277 op_t& op)
2278 {
2279 107 check_invariants();
2280
2/2
✓ Branch 1 taken 85 times.
✓ Branch 2 taken 22 times.
107 if(impl_.len(id_user) != 0)
2281 {
2282 // authority exists
2283 85 auto dest = resize_impl(
2284 id_port, n + 1, op);
2285 85 dest[0] = ':';
2286 85 check_invariants();
2287 85 return dest + 1;
2288 }
2289 bool make_absolute =
2290
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() &&
2291 16 impl_.len(id_path) != 0;
2292 44 auto dest = resize_impl(
2293 22 id_user, 3 + n + make_absolute, op);
2294 22 impl_.split(id_user, 2);
2295 22 impl_.split(id_pass, 0);
2296 22 impl_.split(id_host, 0);
2297 22 dest[0] = '/';
2298 22 dest[1] = '/';
2299 22 dest[2] = ':';
2300
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 20 times.
22 if (make_absolute)
2301 {
2302 2 impl_.split(id_port, n + 1);
2303 2 dest[n + 3] = '/';
2304 2 ++impl_.decoded_[id_path];
2305 }
2306 22 check_invariants();
2307 22 return dest + 3;
2308 }
2309
2310 char*
2311 188 url_base::
2312 set_path_impl(
2313 std::size_t n,
2314 op_t& op)
2315 {
2316 188 check_invariants();
2317 188 auto const dest = resize_impl(
2318 id_path, n, op);
2319 188 return dest;
2320 }
2321
2322
2323 //------------------------------------------------
2324
2325 // return the first segment of the path.
2326 // this is needed for some algorithms.
2327 core::string_view
2328 45 url_base::
2329 first_segment() const noexcept
2330 {
2331
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 38 times.
45 if(impl_.nseg_ == 0)
2332 7 return {};
2333 38 auto const p0 = impl_.cs_ +
2334 38 impl_.offset(id_path) +
2335 38 detail::path_prefix(
2336 38 impl_.get(id_path));
2337 38 auto const end = impl_.cs_ +
2338 38 impl_.offset(id_query);
2339
2/2
✓ Branch 0 taken 21 times.
✓ Branch 1 taken 17 times.
38 if(impl_.nseg_ == 1)
2340 42 return core::string_view(
2341 21 p0, end - p0);
2342 17 auto p = p0;
2343
2/2
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 17 times.
39 while(*p != '/')
2344 22 ++p;
2345
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
17 BOOST_ASSERT(p < end);
2346 17 return core::string_view(p0, p - p0);
2347 }
2348
2349 detail::segments_iter_impl
2350 565 url_base::
2351 edit_segments(
2352 detail::segments_iter_impl const& it0,
2353 detail::segments_iter_impl const& it1,
2354 detail::any_segments_iter&& src,
2355 // -1 = preserve
2356 // 0 = make relative (can fail)
2357 // 1 = make absolute
2358 int absolute)
2359 {
2360 // Iterator doesn't belong to this url
2361
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 565 times.
565 BOOST_ASSERT(it0.ref.alias_of(impl_));
2362
2363 // Iterator doesn't belong to this url
2364
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 565 times.
565 BOOST_ASSERT(it1.ref.alias_of(impl_));
2365
2366 // Iterator is in the wrong order
2367
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 565 times.
565 BOOST_ASSERT(it0.index <= it1.index);
2368
2369 // Iterator is out of range
2370
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 565 times.
565 BOOST_ASSERT(it0.index <= impl_.nseg_);
2371
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 565 times.
565 BOOST_ASSERT(it0.pos <= impl_.len(id_path));
2372
2373 // Iterator is out of range
2374
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 565 times.
565 BOOST_ASSERT(it1.index <= impl_.nseg_);
2375
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 565 times.
565 BOOST_ASSERT(it1.pos <= impl_.len(id_path));
2376
2377 //------------------------------------------------
2378 //
2379 // Calculate output prefix
2380 //
2381 // 0 = ""
2382 // 1 = "/"
2383 // 2 = "./"
2384 // 3 = "/./"
2385 //
2386 565 bool const is_abs = is_path_absolute();
2387
2/2
✓ Branch 1 taken 213 times.
✓ Branch 2 taken 352 times.
565 if(has_authority())
2388 {
2389 // Check if the new
2390 // path would be empty
2391
2/2
✓ Branch 0 taken 108 times.
✓ Branch 1 taken 105 times.
213 if( src.fast_nseg == 0 &&
2392
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 90 times.
108 it0.index == 0 &&
2393
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 3 times.
18 it1.index == impl_.nseg_)
2394 {
2395 // VFALCO we don't have
2396 // access to nchar this early
2397 //
2398 //BOOST_ASSERT(nchar == 0);
2399 15 absolute = 0;
2400 }
2401 else
2402 {
2403 // prefix "/" required
2404 198 absolute = 1;
2405 }
2406 }
2407
1/2
✓ Branch 0 taken 352 times.
✗ Branch 1 not taken.
352 else if(absolute < 0)
2408 {
2409 352 absolute = is_abs; // preserve
2410 }
2411 565 auto const path_pos = impl_.offset(id_path);
2412
2413 565 std::size_t nchar = 0;
2414 565 std::size_t prefix = 0;
2415 565 bool encode_colons = false;
2416 565 bool cp_src_prefix = false;
2417
2/2
✓ Branch 0 taken 291 times.
✓ Branch 1 taken 274 times.
565 if(it0.index > 0)
2418 {
2419 // first segment unchanged
2420 291 prefix = src.fast_nseg > 0;
2421 }
2422
2/2
✓ Branch 0 taken 221 times.
✓ Branch 1 taken 53 times.
274 else if(src.fast_nseg > 0)
2423 {
2424 // first segment from src
2425
2/2
✓ Branch 1 taken 155 times.
✓ Branch 2 taken 66 times.
221 if(! src.front.empty())
2426 {
2427
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 == "." &&
2428
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 3 times.
7 src.fast_nseg > 1)
2429
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 if (src.s.empty())
2430 {
2431 // if front is ".", we need the extra "." in the prefix
2432 // which will maintain the invariant that segments represent
2433 // {"."}
2434 4 prefix = 2 + absolute;
2435 }
2436 else
2437 {
2438 // if the "." prefix is explicitly required from set_path
2439 // we do not include an extra "." segment
2440 prefix = absolute;
2441 cp_src_prefix = true;
2442 }
2443
2/2
✓ Branch 0 taken 79 times.
✓ Branch 1 taken 72 times.
151 else if(absolute)
2444 79 prefix = 1;
2445
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() ||
2446
2/2
✓ Branch 1 taken 63 times.
✓ Branch 2 taken 5 times.
68 ! src.front.contains(':'))
2447 67 prefix = 0;
2448 else
2449 {
2450 5 prefix = 0;
2451 5 encode_colons = true;
2452 }
2453 }
2454 else
2455 {
2456 66 prefix = 2 + absolute;
2457 }
2458 }
2459 else
2460 {
2461 // first segment from it1
2462 53 auto const p =
2463 53 impl_.cs_ + path_pos + it1.pos;
2464 106 switch(impl_.cs_ +
2465
3/3
✓ Branch 1 taken 34 times.
✓ Branch 2 taken 11 times.
✓ Branch 3 taken 8 times.
53 impl_.offset(id_query) - p)
2466 {
2467 34 case 0:
2468 // points to end
2469 34 prefix = absolute;
2470 34 break;
2471 11 default:
2472
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
11 BOOST_ASSERT(*p == '/');
2473
1/2
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
11 if(p[1] != '/')
2474 {
2475
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 6 times.
11 if(absolute)
2476 5 prefix = 1;
2477
2/2
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 1 times.
11 else if(has_scheme() ||
2478
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(':'))
2479 5 prefix = 0;
2480 else
2481 1 prefix = 2;
2482 11 break;
2483 }
2484 // empty
2485 BOOST_FALLTHROUGH;
2486 case 1:
2487 // empty
2488
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 BOOST_ASSERT(*p == '/');
2489 8 prefix = 2 + absolute;
2490 8 break;
2491 }
2492 }
2493
2494 // append '/' to new segs
2495 // if inserting at front.
2496 565 std::size_t const suffix =
2497 744 it1.index == 0 &&
2498
4/4
✓ Branch 0 taken 179 times.
✓ Branch 1 taken 386 times.
✓ Branch 2 taken 63 times.
✓ Branch 3 taken 116 times.
628 impl_.nseg_ > 0 &&
2499
1/2
✓ Branch 0 taken 63 times.
✗ Branch 1 not taken.
63 src.fast_nseg > 0;
2500
2501 //------------------------------------------------
2502 //
2503 // Measure the number of encoded characters
2504 // of output, and the number of inserted
2505 // segments including internal separators.
2506 //
2507 565 src.encode_colons = encode_colons;
2508 565 std::size_t nseg = 0;
2509
3/4
✓ Branch 1 taken 565 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 392 times.
✓ Branch 4 taken 173 times.
565 if(src.measure(nchar))
2510 {
2511 392 src.encode_colons = false;
2512 for(;;)
2513 {
2514 661 ++nseg;
2515
4/4
✓ Branch 1 taken 659 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 390 times.
✓ Branch 4 taken 269 times.
661 if(! src.measure(nchar))
2516 390 break;
2517 269 ++nchar;
2518 }
2519 }
2520
2521
3/4
✓ Branch 0 taken 173 times.
✓ Branch 1 taken 219 times.
✓ Branch 2 taken 171 times.
✗ Branch 3 not taken.
563 switch(src.fast_nseg)
2522 {
2523 173 case 0:
2524
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 173 times.
173 BOOST_ASSERT(nseg == 0);
2525 173 break;
2526 219 case 1:
2527
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 219 times.
219 BOOST_ASSERT(nseg == 1);
2528 219 break;
2529 171 case 2:
2530
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 171 times.
171 BOOST_ASSERT(nseg >= 2);
2531 171 break;
2532 }
2533
2534 //------------------------------------------------
2535 //
2536 // Calculate [pos0, pos1) to remove
2537 //
2538 563 auto pos0 = it0.pos;
2539
2/2
✓ Branch 0 taken 272 times.
✓ Branch 1 taken 291 times.
563 if(it0.index == 0)
2540 {
2541 // patch pos for prefix
2542 272 pos0 = 0;
2543 }
2544 563 auto pos1 = it1.pos;
2545
2/2
✓ Branch 0 taken 179 times.
✓ Branch 1 taken 384 times.
563 if(it1.index == 0)
2546 {
2547 // patch pos for prefix
2548 179 pos1 = detail::path_prefix(
2549 impl_.get(id_path));
2550 }
2551 384 else if(
2552
2/2
✓ Branch 0 taken 93 times.
✓ Branch 1 taken 291 times.
384 it0.index == 0 &&
2553
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_ &&
2554 nseg == 0)
2555 {
2556 // Remove the slash from segment it1
2557 // if it is becoming the new first
2558 // segment.
2559 19 ++pos1;
2560 }
2561 // calc decoded size of old range
2562 auto const dn0 =
2563 563 detail::decode_bytes_unsafe(
2564 core::string_view(
2565 563 impl_.cs_ +
2566 563 impl_.offset(id_path) +
2567 pos0,
2568 pos1 - pos0));
2569
2570 //------------------------------------------------
2571 //
2572 // Resize
2573 //
2574 1126 op_t op(*this, &src.s);
2575 char* dest;
2576 char const* end;
2577 {
2578 563 auto const nremove = pos1 - pos0;
2579 // check overflow
2580
2/4
✓ Branch 1 taken 563 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 563 times.
✗ Branch 4 not taken.
1126 if( nchar <= max_size() && (
2581 563 prefix + suffix <=
2582
1/2
✓ Branch 1 taken 563 times.
✗ Branch 2 not taken.
563 max_size() - nchar))
2583 {
2584 563 nchar = prefix + nchar + suffix;
2585
3/4
✓ Branch 0 taken 328 times.
✓ Branch 1 taken 235 times.
✓ Branch 2 taken 563 times.
✗ Branch 3 not taken.
891 if( nchar <= nremove ||
2586 328 nchar - nremove <=
2587
1/2
✓ Branch 2 taken 328 times.
✗ Branch 3 not taken.
328 max_size() - size())
2588 563 goto ok;
2589 }
2590 // too large
2591 detail::throw_length_error();
2592 563 ok:
2593 auto const new_size =
2594 563 size() + nchar - nremove;
2595
1/2
✓ Branch 1 taken 563 times.
✗ Branch 2 not taken.
563 reserve_impl(new_size, op);
2596 563 dest = s_ + path_pos + pos0;
2597 563 op.move(
2598 563 dest + nchar,
2599 563 s_ + path_pos + pos1,
2600 563 size() - path_pos - pos1);
2601 1126 impl_.set_size(
2602 id_path,
2603 563 impl_.len(id_path) + nchar - nremove);
2604
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 563 times.
563 BOOST_ASSERT(size() == new_size);
2605 563 end = dest + nchar;
2606 563 impl_.nseg_ = impl_.nseg_ + nseg - (
2607 563 it1.index - it0.index) - cp_src_prefix;
2608
2/2
✓ Branch 0 taken 561 times.
✓ Branch 1 taken 2 times.
563 if(s_)
2609 561 s_[size()] = '\0';
2610 }
2611
2612 //------------------------------------------------
2613 //
2614 // Output segments and internal separators:
2615 //
2616 // prefix [ segment [ '/' segment ] ] suffix
2617 //
2618 563 auto const dest0 = dest;
2619
4/4
✓ Branch 0 taken 38 times.
✓ Branch 1 taken 41 times.
✓ Branch 2 taken 266 times.
✓ Branch 3 taken 218 times.
563 switch(prefix)
2620 {
2621 38 case 3:
2622 38 *dest++ = '/';
2623 38 *dest++ = '.';
2624 38 *dest++ = '/';
2625 38 break;
2626 41 case 2:
2627 41 *dest++ = '.';
2628 BOOST_FALLTHROUGH;
2629 307 case 1:
2630 307 *dest++ = '/';
2631 307 break;
2632 218 default:
2633 218 break;
2634 }
2635 563 src.rewind();
2636
2/2
✓ Branch 0 taken 390 times.
✓ Branch 1 taken 173 times.
563 if(nseg > 0)
2637 {
2638 390 src.encode_colons = encode_colons;
2639 for(;;)
2640 {
2641 659 src.copy(dest, end);
2642
2/2
✓ Branch 0 taken 390 times.
✓ Branch 1 taken 269 times.
659 if(--nseg == 0)
2643 390 break;
2644 269 *dest++ = '/';
2645 269 src.encode_colons = false;
2646 }
2647
2/2
✓ Branch 0 taken 63 times.
✓ Branch 1 taken 327 times.
390 if(suffix)
2648 63 *dest++ = '/';
2649 }
2650
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 563 times.
563 BOOST_ASSERT(dest == dest0 + nchar);
2651
2652 // calc decoded size of new range,
2653 auto const dn =
2654 563 detail::decode_bytes_unsafe(
2655 563 core::string_view(dest0, dest - dest0));
2656 563 impl_.decoded_[id_path] += dn - dn0;
2657
2658 return detail::segments_iter_impl(
2659 1126 impl_, pos0, it0.index);
2660 }
2661
2662 //------------------------------------------------
2663
2664 auto
2665 138 url_base::
2666 edit_params(
2667 detail::params_iter_impl const& it0,
2668 detail::params_iter_impl const& it1,
2669 detail::any_params_iter&& src) ->
2670 detail::params_iter_impl
2671 {
2672 138 auto pos0 = impl_.offset(id_query);
2673 138 auto pos1 = pos0 + it1.pos;
2674 138 pos0 = pos0 + it0.pos;
2675
2676 // Iterator doesn't belong to this url
2677
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 138 times.
138 BOOST_ASSERT(it0.ref.alias_of(impl_));
2678
2679 // Iterator doesn't belong to this url
2680
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 138 times.
138 BOOST_ASSERT(it1.ref.alias_of(impl_));
2681
2682 // Iterator is in the wrong order
2683
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 138 times.
138 BOOST_ASSERT(it0.index <= it1.index);
2684
2685 // Iterator is out of range
2686
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 138 times.
138 BOOST_ASSERT(it0.index <= impl_.nparam_);
2687
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 138 times.
138 BOOST_ASSERT(pos0 <= impl_.offset(id_frag));
2688
2689 // Iterator is out of range
2690
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 138 times.
138 BOOST_ASSERT(it1.index <= impl_.nparam_);
2691
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 138 times.
138 BOOST_ASSERT(pos1 <= impl_.offset(id_frag));
2692
2693 // calc decoded size of old range,
2694 // minus one if '?' or '&' prefixed
2695 auto const dn0 =
2696 138 detail::decode_bytes_unsafe(
2697 core::string_view(
2698 138 impl_.cs_ + pos0,
2699 pos1 - pos0)) - (
2700 138 impl_.len(id_query) > 0);
2701
2702 //------------------------------------------------
2703 //
2704 // Measure the number of encoded characters
2705 // of output, and the number of inserted
2706 // segments including internal separators.
2707 //
2708
2709 138 std::size_t nchar = 0;
2710 138 std::size_t nparam = 0;
2711
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))
2712 {
2713 111 ++nchar; // for '?' or '&'
2714 for(;;)
2715 {
2716 176 ++nparam;
2717
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))
2718 111 break;
2719 65 ++nchar; // for '&'
2720 }
2721 }
2722
2723 //------------------------------------------------
2724 //
2725 // Resize
2726 //
2727 133 op_t op(*this, &src.s0, &src.s1);
2728 char* dest;
2729 char const* end;
2730 {
2731 133 auto const nremove = pos1 - pos0;
2732 // check overflow
2733
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 &&
2734 95 nchar - nremove >
2735
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 95 times.
95 max_size() - size())
2736 {
2737 // too large
2738 detail::throw_length_error();
2739 }
2740 133 auto const nparam1 =
2741 133 impl_.nparam_ + nparam - (
2742 133 it1.index - it0.index);
2743
1/2
✓ Branch 2 taken 133 times.
✗ Branch 3 not taken.
133 reserve_impl(size() + nchar - nremove, op);
2744 133 dest = s_ + pos0;
2745 133 end = dest + nchar;
2746
2/2
✓ Branch 0 taken 99 times.
✓ Branch 1 taken 34 times.
133 if(impl_.nparam_ > 0)
2747 {
2748 // needed when we move
2749 // the beginning of the query
2750 99 s_[impl_.offset(id_query)] = '&';
2751 }
2752 133 op.move(
2753 133 dest + nchar,
2754 133 impl_.cs_ + pos1,
2755 133 size() - pos1);
2756 266 impl_.set_size(
2757 id_query,
2758 133 impl_.len(id_query) +
2759 nchar - nremove);
2760 133 impl_.nparam_ = nparam1;
2761
1/2
✓ Branch 0 taken 133 times.
✗ Branch 1 not taken.
133 if(nparam1 > 0)
2762 {
2763 // needed when we erase
2764 // the beginning of the query
2765 133 s_[impl_.offset(id_query)] = '?';
2766 }
2767
1/2
✓ Branch 0 taken 133 times.
✗ Branch 1 not taken.
133 if(s_)
2768 133 s_[size()] = '\0';
2769 }
2770 133 auto const dest0 = dest;
2771
2772 //------------------------------------------------
2773 //
2774 // Output params and internal separators:
2775 //
2776 // [ '?' param ] [ '&' param ]
2777 //
2778
2/2
✓ Branch 0 taken 111 times.
✓ Branch 1 taken 22 times.
133 if(nparam > 0)
2779 {
2780
2/2
✓ Branch 0 taken 68 times.
✓ Branch 1 taken 43 times.
111 if(it0.index == 0)
2781 68 *dest++ = '?';
2782 else
2783 43 *dest++ = '&';
2784 111 src.rewind();
2785 for(;;)
2786 {
2787 176 src.copy(dest, end);
2788
2/2
✓ Branch 0 taken 111 times.
✓ Branch 1 taken 65 times.
176 if(--nparam == 0)
2789 111 break;
2790 65 *dest++ = '&';
2791 }
2792 }
2793
2794 // calc decoded size of new range,
2795 // minus one if '?' or '&' prefixed
2796 auto const dn =
2797 133 detail::decode_bytes_unsafe(
2798 133 core::string_view(dest0, dest - dest0)) - (
2799 133 impl_.len(id_query) > 0);
2800
2801 133 impl_.decoded_[id_query] += (dn - dn0);
2802
2803 return detail::params_iter_impl(
2804 133 impl_,
2805 133 pos0 - impl_.offset_[id_query],
2806 266 it0.index);
2807 }
2808
2809 //------------------------------------------------
2810
2811 void
2812 382 url_base::
2813 decoded_to_lower_impl(int id) noexcept
2814 {
2815 382 char* it = s_ + impl_.offset(id);
2816 382 char const* const end = s_ + impl_.offset(id + 1);
2817
2/2
✓ Branch 0 taken 1815 times.
✓ Branch 1 taken 382 times.
2197 while(it < end)
2818 {
2819
2/2
✓ Branch 0 taken 1810 times.
✓ Branch 1 taken 5 times.
1815 if (*it != '%')
2820 {
2821 3620 *it = grammar::to_lower(
2822 1810 *it);
2823 1810 ++it;
2824 1810 continue;
2825 }
2826 5 it += 3;
2827 }
2828 382 }
2829
2830 void
2831 37 url_base::
2832 to_lower_impl(int id) noexcept
2833 {
2834 37 char* it = s_ + impl_.offset(id);
2835 37 char const* const end = s_ + impl_.offset(id + 1);
2836
2/2
✓ Branch 0 taken 112 times.
✓ Branch 1 taken 37 times.
149 while(it < end)
2837 {
2838 224 *it = grammar::to_lower(
2839 112 *it);
2840 112 ++it;
2841 }
2842 37 }
2843
2844 } // urls
2845 } // boost
2846
2847 #endif
2848