GCC Code Coverage Report


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

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