include/boost/url/impl/url_view_base.hpp

98.6% Lines (291/295) 100.0% Functions (35/35)
include/boost/url/impl/url_view_base.hpp
Line TLA Hits Source Code
1 //
2 // Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
3 // Copyright (c) 2022 Alan de Freitas (alandefreitas@gmail.com)
4 //
5 // Distributed under the Boost Software License, Version 1.0. (See accompanying
6 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7 //
8 // Official repository: https://github.com/boostorg/url
9 //
10
11 #ifndef BOOST_URL_IMPL_URL_VIEW_BASE_HPP
12 #define BOOST_URL_IMPL_URL_VIEW_BASE_HPP
13
14 #include <boost/url/detail/memcpy.hpp>
15 #include <boost/url/detail/except.hpp>
16 #include <boost/url/detail/fnv_1a.hpp>
17 #include <boost/assert.hpp>
18 #include <cstring>
19 #include <memory>
20
21 namespace boost {
22 namespace urls {
23
24 namespace detail {
25
26 // Forward declarations for normalize functions
27 // defined in src/detail/normalize.cpp
28 BOOST_URL_DECL
29 void
30 ci_digest(
31 core::string_view s,
32 fnv_1a& hasher) noexcept;
33
34 BOOST_URL_DECL
35 void
36 digest_encoded(
37 core::string_view s,
38 fnv_1a& hasher) noexcept;
39
40 BOOST_URL_DECL
41 void
42 ci_digest_encoded(
43 core::string_view s,
44 fnv_1a& hasher) noexcept;
45
46 BOOST_URL_DECL
47 void
48 normalized_path_digest(
49 core::string_view str,
50 bool remove_unmatched,
51 fnv_1a& hasher) noexcept;
52
53 BOOST_URL_DECL
54 int
55 ci_compare(
56 core::string_view lhs,
57 core::string_view rhs) noexcept;
58
59 BOOST_URL_DECL
60 int
61 compare_encoded(
62 core::string_view lhs,
63 core::string_view rhs) noexcept;
64
65 BOOST_URL_DECL
66 int
67 compare_encoded_query(
68 core::string_view lhs,
69 core::string_view rhs) noexcept;
70
71 BOOST_URL_DECL
72 int
73 segments_compare(
74 segments_encoded_view seg0,
75 segments_encoded_view seg1) noexcept;
76
77 } // detail
78
79 //------------------------------------------------
80
81 inline
82 std::size_t
83 304 url_view_base::
84 digest(std::size_t salt) const noexcept
85 {
86 304 detail::fnv_1a h(salt);
87 304 detail::ci_digest(impl().get(id_scheme), h);
88 304 detail::digest_encoded(impl().get(id_user), h);
89 304 detail::digest_encoded(impl().get(id_pass), h);
90 304 detail::ci_digest_encoded(impl().get(id_host), h);
91 304 h.put(impl().get(id_port));
92 304 detail::normalized_path_digest(
93 304 impl().get(id_path), is_path_absolute(), h);
94 304 detail::digest_encoded(impl().get(id_query), h);
95 304 detail::digest_encoded(impl().get(id_frag), h);
96 304 return h.digest();
97 }
98
99 //------------------------------------------------
100 //
101 // Scheme
102 //
103 //------------------------------------------------
104
105 inline
106 bool
107 3809 url_view_base::
108 has_scheme() const noexcept
109 {
110 3809 auto const n = impl().len(
111 id_scheme);
112 3809 if(n == 0)
113 904 return false;
114 2905 BOOST_ASSERT(n > 1);
115 2905 BOOST_ASSERT(
116 impl().get(id_scheme
117 ).ends_with(':'));
118 2905 return true;
119 }
120
121 inline
122 core::string_view
123 1555 url_view_base::
124 scheme() const noexcept
125 {
126 1555 auto s = impl().get(id_scheme);
127 1555 if(! s.empty())
128 {
129 1454 BOOST_ASSERT(s.size() > 1);
130 1454 BOOST_ASSERT(s.ends_with(':'));
131 1454 s.remove_suffix(1);
132 }
133 1555 return s;
134 }
135
136 inline
137 urls::scheme
138 46 url_view_base::
139 scheme_id() const noexcept
140 {
141 46 return impl().scheme_;
142 }
143
144 //------------------------------------------------
145 //
146 // Authority
147 //
148 //------------------------------------------------
149
150 inline
151 authority_view
152 596 url_view_base::
153 authority() const noexcept
154 {
155 596 detail::url_impl u(from::authority);
156 596 u.cs_ = encoded_authority().data();
157 596 if(has_authority())
158 {
159 594 u.set_size(id_user, impl().len(id_user) - 2);
160 594 u.set_size(id_pass, impl().len(id_pass));
161 594 u.set_size(id_host, impl().len(id_host));
162 594 u.set_size(id_port, impl().len(id_port));
163 }
164 else
165 {
166 2 u.set_size(id_user, impl().len(id_user));
167 2 BOOST_ASSERT(impl().len(id_pass) == 0);
168 2 BOOST_ASSERT(impl().len(id_host) == 0);
169 2 BOOST_ASSERT(impl().len(id_port) == 0);
170 }
171 596 u.decoded_[id_user] = impl().decoded_[id_user];
172 596 u.decoded_[id_pass] = impl().decoded_[id_pass];
173 596 u.decoded_[id_host] = impl().decoded_[id_host];
174 detail::memcpy(
175 u.ip_addr_,
176 596 impl().ip_addr_,
177 16);
178 596 u.port_number_ = impl().port_number_;
179 596 u.host_type_ = impl().host_type_;
180 596 return authority_view(u);
181 }
182
183 inline
184 pct_string_view
185 772 url_view_base::
186 encoded_authority() const noexcept
187 {
188 772 auto s = impl().get(id_user, id_path);
189 772 if(! s.empty())
190 {
191 730 BOOST_ASSERT(has_authority());
192 730 s.remove_prefix(2);
193 }
194 772 return make_pct_string_view_unsafe(
195 s.data(),
196 s.size(),
197 772 impl().decoded_[id_user] +
198 772 impl().decoded_[id_pass] +
199 772 impl().decoded_[id_host] +
200 772 impl().decoded_[id_port] +
201 1544 has_password());
202 }
203
204 //------------------------------------------------
205 //
206 // Userinfo
207 //
208 //------------------------------------------------
209
210 inline
211 bool
212 264 url_view_base::
213 has_userinfo() const noexcept
214 {
215 264 auto n = impl().len(id_pass);
216 264 if(n == 0)
217 98 return false;
218 166 BOOST_ASSERT(has_authority());
219 166 BOOST_ASSERT(impl().get(
220 id_pass).ends_with('@'));
221 166 return true;
222 }
223
224 inline
225 bool
226 919 url_view_base::
227 has_password() const noexcept
228 {
229 919 auto const n = impl().len(id_pass);
230 919 if(n > 1)
231 {
232 129 BOOST_ASSERT(impl().get(id_pass
233 ).starts_with(':'));
234 129 BOOST_ASSERT(impl().get(id_pass
235 ).ends_with('@'));
236 129 return true;
237 }
238 790 BOOST_ASSERT(n == 0 || impl().get(
239 id_pass).ends_with('@'));
240 790 return false;
241 }
242
243 inline
244 pct_string_view
245 128 url_view_base::
246 encoded_userinfo() const noexcept
247 {
248 128 auto s = impl().get(
249 id_user, id_host);
250 128 if(s.empty())
251 8 return s;
252 120 BOOST_ASSERT(
253 has_authority());
254 120 s.remove_prefix(2);
255 120 if(s.empty())
256 34 return s;
257 86 BOOST_ASSERT(
258 s.ends_with('@'));
259 86 s.remove_suffix(1);
260 86 return make_pct_string_view_unsafe(
261 s.data(),
262 s.size(),
263 86 impl().decoded_[id_user] +
264 86 impl().decoded_[id_pass] +
265 172 has_password());
266 }
267
268 inline
269 pct_string_view
270 148 url_view_base::
271 encoded_user() const noexcept
272 {
273 148 auto s = impl().get(id_user);
274 148 if(! s.empty())
275 {
276 147 BOOST_ASSERT(
277 has_authority());
278 147 s.remove_prefix(2);
279 }
280 148 return make_pct_string_view_unsafe(
281 s.data(),
282 s.size(),
283 296 impl().decoded_[id_user]);
284 }
285
286 inline
287 pct_string_view
288 105 url_view_base::
289 encoded_password() const noexcept
290 {
291 105 auto s = impl().get(id_pass);
292 105 switch(s.size())
293 {
294 24 case 1:
295 24 BOOST_ASSERT(
296 s.starts_with('@'));
297 24 s.remove_prefix(1);
298 BOOST_FALLTHROUGH;
299 42 case 0:
300 42 return make_pct_string_view_unsafe(
301 42 s.data(), s.size(), 0);
302 63 default:
303 63 break;
304 }
305 63 BOOST_ASSERT(s.ends_with('@'));
306 63 BOOST_ASSERT(s.starts_with(':'));
307 63 return make_pct_string_view_unsafe(
308 63 s.data() + 1,
309 63 s.size() - 2,
310 126 impl().decoded_[id_pass]);
311 }
312
313 //------------------------------------------------
314 //
315 // Host
316 //
317 //------------------------------------------------
318 /*
319 host_type host_type() // ipv4, ipv6, ipvfuture, name
320
321 std::string host() // return encoded_host().decode()
322 pct_string_view encoded_host() // return host part, as-is
323 std::string host_address() // return encoded_host_address().decode()
324 pct_string_view encoded_host_address() // ipv4, ipv6, ipvfut, or encoded name, no brackets
325
326 ipv4_address host_ipv4_address() // return ipv4_address or {}
327 ipv6_address host_ipv6_address() // return ipv6_address or {}
328 core::string_view host_ipvfuture() // return ipvfuture or {}
329 std::string host_name() // return decoded name or ""
330 pct_string_view encoded_host_name() // return encoded host name or ""
331 */
332
333 inline
334 pct_string_view
335 576 url_view_base::
336 encoded_host() const noexcept
337 {
338 576 return impl().pct_get(id_host);
339 }
340
341 inline
342 pct_string_view
343 119 url_view_base::
344 encoded_host_address() const noexcept
345 {
346 119 core::string_view s = impl().get(id_host);
347 std::size_t n;
348 119 switch(impl().host_type_)
349 {
350 41 default:
351 case urls::host_type::none:
352 41 BOOST_ASSERT(s.empty());
353 41 n = 0;
354 41 break;
355
356 53 case urls::host_type::name:
357 case urls::host_type::ipv4:
358 53 n = impl().decoded_[id_host];
359 53 break;
360
361 25 case urls::host_type::ipv6:
362 case urls::host_type::ipvfuture:
363 {
364 25 BOOST_ASSERT(
365 impl().decoded_[id_host] ==
366 s.size() ||
367 !this->encoded_zone_id().empty());
368 25 BOOST_ASSERT(s.size() >= 2);
369 25 BOOST_ASSERT(s.front() == '[');
370 25 BOOST_ASSERT(s.back() == ']');
371 25 s = s.substr(1, s.size() - 2);
372 25 n = impl().decoded_[id_host] - 2;
373 25 break;
374 }
375 }
376 119 return make_pct_string_view_unsafe(
377 s.data(),
378 s.size(),
379 119 n);
380 }
381
382 inline
383 urls::ipv4_address
384 53 url_view_base::
385 host_ipv4_address() const noexcept
386 {
387 53 if(impl().host_type_ !=
388 urls::host_type::ipv4)
389 35 return {};
390 18 ipv4_address::bytes_type b{{}};
391 36 std::memcpy(
392 18 &b[0], &impl().ip_addr_[0], b.size());
393 18 return urls::ipv4_address(b);
394 }
395
396 inline
397 urls::ipv6_address
398 60 url_view_base::
399 host_ipv6_address() const noexcept
400 {
401 60 if(impl().host_type_ !=
402 urls::host_type::ipv6)
403 45 return {};
404 15 ipv6_address::bytes_type b{{}};
405 30 std::memcpy(
406 15 &b[0], &impl().ip_addr_[0], b.size());
407 15 return {b};
408 }
409
410 inline
411 core::string_view
412 51 url_view_base::
413 host_ipvfuture() const noexcept
414 {
415 51 if(impl().host_type_ !=
416 urls::host_type::ipvfuture)
417 44 return {};
418 7 core::string_view s = impl().get(id_host);
419 7 BOOST_ASSERT(s.size() >= 6);
420 7 BOOST_ASSERT(s.front() == '[');
421 7 BOOST_ASSERT(s.back() == ']');
422 7 s = s.substr(1, s.size() - 2);
423 7 return s;
424 }
425
426 inline
427 pct_string_view
428 146 url_view_base::
429 encoded_host_name() const noexcept
430 {
431 146 if(impl().host_type_ !=
432 urls::host_type::name)
433 78 return {};
434 68 core::string_view s = impl().get(id_host);
435 68 return make_pct_string_view_unsafe(
436 s.data(),
437 s.size(),
438 136 impl().decoded_[id_host]);
439 }
440
441 inline
442 pct_string_view
443 24 url_view_base::
444 encoded_zone_id() const noexcept
445 {
446 24 if(impl().host_type_ !=
447 urls::host_type::ipv6)
448 6 return {};
449 18 core::string_view s = impl().get(id_host);
450 18 BOOST_ASSERT(s.front() == '[');
451 18 BOOST_ASSERT(s.back() == ']');
452 18 s = s.substr(1, s.size() - 2);
453 18 auto pos = s.find("%25");
454 18 if (pos == core::string_view::npos)
455 2 return {};
456 16 s.remove_prefix(pos + 3);
457 16 return *make_pct_string_view(s);
458 }
459
460 //------------------------------------------------
461
462 inline
463 bool
464 389 url_view_base::
465 has_port() const noexcept
466 {
467 389 auto const n = impl().len(id_port);
468 389 if(n == 0)
469 89 return false;
470 300 BOOST_ASSERT(
471 impl().get(id_port).starts_with(':'));
472 300 return true;
473 }
474
475 inline
476 core::string_view
477 193 url_view_base::
478 port() const noexcept
479 {
480 193 auto s = impl().get(id_port);
481 193 if(s.empty())
482 58 return s;
483 135 BOOST_ASSERT(has_port());
484 135 return s.substr(1);
485 }
486
487 inline
488 std::uint16_t
489 108 url_view_base::
490 port_number() const noexcept
491 {
492 108 BOOST_ASSERT(
493 has_port() ||
494 impl().port_number_ == 0);
495 108 return impl().port_number_;
496 }
497
498 //------------------------------------------------
499 //
500 // Path
501 //
502 //------------------------------------------------
503
504 inline
505 pct_string_view
506 1486 url_view_base::
507 encoded_path() const noexcept
508 {
509 1486 return impl().pct_get(id_path);
510 }
511
512 inline
513 segments_view
514 5785 url_view_base::
515 segments() const noexcept
516 {
517 5785 return {detail::path_ref(impl())};
518 }
519
520 inline
521 segments_encoded_view
522 875 url_view_base::
523 encoded_segments() const noexcept
524 {
525 return segments_encoded_view(
526 875 detail::path_ref(impl()));
527 }
528
529 //------------------------------------------------
530 //
531 // Query
532 //
533 //------------------------------------------------
534
535 inline
536 bool
537 963 url_view_base::
538 has_query() const noexcept
539 {
540 963 auto const n = impl().len(
541 id_query);
542 963 if(n == 0)
543 792 return false;
544 171 BOOST_ASSERT(
545 impl().get(id_query).
546 starts_with('?'));
547 171 return true;
548 }
549
550 inline
551 pct_string_view
552 335 url_view_base::
553 encoded_query() const noexcept
554 {
555 335 auto s = impl().get(id_query);
556 335 if(s.empty())
557 10 return s;
558 325 BOOST_ASSERT(
559 s.starts_with('?'));
560 325 return s.substr(1);
561 }
562
563 inline
564 params_encoded_view
565 72 url_view_base::
566 encoded_params() const noexcept
567 {
568 72 return params_encoded_view(impl());
569 }
570
571 inline
572 params_view
573 5803 url_view_base::
574 params() const noexcept
575 {
576 return params_view(
577 impl(),
578 encoding_opts{
579 5803 true,false,false});
580 }
581
582 inline
583 params_view
584 url_view_base::
585 params(encoding_opts opt) const noexcept
586 {
587 return params_view(impl(), opt);
588 }
589
590 //------------------------------------------------
591 //
592 // Fragment
593 //
594 //------------------------------------------------
595
596 inline
597 bool
598 865 url_view_base::
599 has_fragment() const noexcept
600 {
601 865 auto const n = impl().len(id_frag);
602 865 if(n == 0)
603 706 return false;
604 159 BOOST_ASSERT(
605 impl().get(id_frag).
606 starts_with('#'));
607 159 return true;
608 }
609
610 inline
611 pct_string_view
612 198 url_view_base::
613 encoded_fragment() const noexcept
614 {
615 198 auto s = impl().get(id_frag);
616 198 if(! s.empty())
617 {
618 190 BOOST_ASSERT(
619 s.starts_with('#'));
620 190 s.remove_prefix(1);
621 }
622 198 return make_pct_string_view_unsafe(
623 s.data(),
624 s.size(),
625 396 impl().decoded_[id_frag]);
626 }
627
628 //------------------------------------------------
629 //
630 // Compound Fields
631 //
632 //------------------------------------------------
633
634 inline
635 pct_string_view
636 120 url_view_base::
637 encoded_host_and_port() const noexcept
638 {
639 120 return impl().pct_get(id_host, id_path);
640 }
641
642 inline
643 pct_string_view
644 16 url_view_base::
645 encoded_origin() const noexcept
646 {
647 16 if(impl().len(id_user) < 2)
648 14 return {};
649 2 return impl().get(id_scheme, id_path);
650 }
651
652 inline
653 pct_string_view
654 1 url_view_base::
655 encoded_resource() const noexcept
656 {
657 auto n =
658 1 impl().decoded_[id_path] +
659 1 impl().decoded_[id_query] +
660 1 impl().decoded_[id_frag];
661 1 if(has_query())
662 1 ++n;
663 1 if(has_fragment())
664 1 ++n;
665 1 BOOST_ASSERT(pct_string_view(
666 impl().get(id_path, id_end)
667 ).decoded_size() == n);
668 1 auto s = impl().get(id_path, id_end);
669 1 return make_pct_string_view_unsafe(
670 1 s.data(), s.size(), n);
671 }
672
673 inline
674 pct_string_view
675 2 url_view_base::
676 encoded_target() const noexcept
677 {
678 auto n =
679 2 impl().decoded_[id_path] +
680 2 impl().decoded_[id_query];
681 2 if(has_query())
682 1 ++n;
683 2 BOOST_ASSERT(pct_string_view(
684 impl().get(id_path, id_frag)
685 ).decoded_size() == n);
686 2 auto s = impl().get(id_path, id_frag);
687 2 return make_pct_string_view_unsafe(
688 2 s.data(), s.size(), n);
689 }
690
691 //------------------------------------------------
692 //
693 // Comparisons
694 //
695 //------------------------------------------------
696
697 inline
698 int
699 345 url_view_base::
700 compare(const url_view_base& other) const noexcept
701 {
702 int comp =
703 345 static_cast<int>(has_scheme()) -
704 345 static_cast<int>(other.has_scheme());
705 345 if ( comp != 0 )
706 return comp;
707
708 345 if (has_scheme())
709 {
710 220 comp = detail::ci_compare(
711 scheme(),
712 other.scheme());
713 220 if ( comp != 0 )
714 15 return comp;
715 }
716
717 330 comp =
718 330 static_cast<int>(has_authority()) -
719 330 static_cast<int>(other.has_authority());
720 330 if ( comp != 0 )
721 return comp;
722
723 330 if (has_authority())
724 {
725 203 comp = authority().compare(other.authority());
726 203 if ( comp != 0 )
727 91 return comp;
728 }
729
730 239 comp = detail::segments_compare(
731 encoded_segments(),
732 other.encoded_segments());
733 239 if ( comp != 0 )
734 54 return comp;
735
736 185 comp =
737 185 static_cast<int>(has_query()) -
738 185 static_cast<int>(other.has_query());
739 185 if ( comp != 0 )
740 return comp;
741
742 185 if (has_query())
743 {
744 56 comp = detail::compare_encoded_query(
745 28 encoded_query(),
746 28 other.encoded_query());
747 28 if ( comp != 0 )
748 19 return comp;
749 }
750
751 166 comp =
752 166 static_cast<int>(has_fragment()) -
753 166 static_cast<int>(other.has_fragment());
754 166 if ( comp != 0 )
755 return comp;
756
757 166 if (has_fragment())
758 {
759 52 comp = detail::compare_encoded(
760 26 encoded_fragment(),
761 26 other.encoded_fragment());
762 26 if ( comp != 0 )
763 21 return comp;
764 }
765
766 145 return 0;
767 }
768
769 } // urls
770 } // boost
771
772 #endif
773