GCC Code Coverage Report


Directory: libs/url/
File: src/url_base.cpp
Date: 2025-05-15 19:47:38
Exec Total Coverage
Lines: 1371 1376 99.6%
Functions: 76 76 100.0%
Branches: 693 887 78.1%

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