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 2916 url_view_base::
108 has_scheme() const noexcept
109 {
110 2916 auto const n = impl().len(
111 id_scheme);
112 2916 if(n == 0)
113 592 return false;
114 2324 BOOST_ASSERT(n > 1);
115 2324 BOOST_ASSERT(
116 impl().get(id_scheme
117 ).ends_with(':'));
118 2324 return true;
119 }
120
121 inline
122 core::string_view
123 1496 url_view_base::
124 scheme() const noexcept
125 {
126 1496 auto s = impl().get(id_scheme);
127 1496 if(! s.empty())
128 {
129 1399 BOOST_ASSERT(s.size() > 1);
130 1399 BOOST_ASSERT(s.ends_with(':'));
131 1399 s.remove_suffix(1);
132 }
133 1496 return s;
134 }
135
136 inline
137 urls::scheme
138 45 url_view_base::
139 scheme_id() const noexcept
140 {
141 45 return impl().scheme_;
142 }
143
144 //------------------------------------------------
145 //
146 // Authority
147 //
148 //------------------------------------------------
149
150 inline
151 authority_view
152 512 url_view_base::
153 authority() const noexcept
154 {
155 512 detail::url_impl u(from::authority);
156 512 u.cs_ = encoded_authority().data();
157 512 if(has_authority())
158 {
159 510 u.set_size(id_user, impl().len(id_user) - 2);
160 510 u.set_size(id_pass, impl().len(id_pass));
161 510 u.set_size(id_host, impl().len(id_host));
162 510 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 512 u.decoded_[id_user] = impl().decoded_[id_user];
172 512 u.decoded_[id_pass] = impl().decoded_[id_pass];
173 512 u.decoded_[id_host] = impl().decoded_[id_host];
174 detail::memcpy(
175 u.ip_addr_,
176 512 impl().ip_addr_,
177 16);
178 512 u.port_number_ = impl().port_number_;
179 512 u.host_type_ = impl().host_type_;
180 512 return authority_view(u);
181 }
182
183 inline
184 pct_string_view
185 680 url_view_base::
186 encoded_authority() const noexcept
187 {
188 680 auto s = impl().get(id_user, id_path);
189 680 if(! s.empty())
190 {
191 638 BOOST_ASSERT(has_authority());
192 638 s.remove_prefix(2);
193 }
194 680 return make_pct_string_view_unsafe(
195 s.data(),
196 s.size(),
197 680 impl().decoded_[id_user] +
198 680 impl().decoded_[id_pass] +
199 680 impl().decoded_[id_host] +
200 680 impl().decoded_[id_port] +
201 1360 has_password());
202 }
203
204 //------------------------------------------------
205 //
206 // Userinfo
207 //
208 //------------------------------------------------
209
210 inline
211 bool
212 261 url_view_base::
213 has_userinfo() const noexcept
214 {
215 261 auto n = impl().len(id_pass);
216 261 if(n == 0)
217 97 return false;
218 164 BOOST_ASSERT(has_authority());
219 164 BOOST_ASSERT(impl().get(
220 id_pass).ends_with('@'));
221 164 return true;
222 }
223
224 inline
225 bool
226 824 url_view_base::
227 has_password() const noexcept
228 {
229 824 auto const n = impl().len(id_pass);
230 824 if(n > 1)
231 {
232 125 BOOST_ASSERT(impl().get(id_pass
233 ).starts_with(':'));
234 125 BOOST_ASSERT(impl().get(id_pass
235 ).ends_with('@'));
236 125 return true;
237 }
238 699 BOOST_ASSERT(n == 0 || impl().get(
239 id_pass).ends_with('@'));
240 699 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 137 url_view_base::
271 encoded_user() const noexcept
272 {
273 137 auto s = impl().get(id_user);
274 137 if(! s.empty())
275 {
276 136 BOOST_ASSERT(
277 has_authority());
278 136 s.remove_prefix(2);
279 }
280 137 return make_pct_string_view_unsafe(
281 s.data(),
282 s.size(),
283 274 impl().decoded_[id_user]);
284 }
285
286 inline
287 pct_string_view
288 100 url_view_base::
289 encoded_password() const noexcept
290 {
291 100 auto s = impl().get(id_pass);
292 100 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 58 default:
303 58 break;
304 }
305 58 BOOST_ASSERT(s.ends_with('@'));
306 58 BOOST_ASSERT(s.starts_with(':'));
307 58 return make_pct_string_view_unsafe(
308 58 s.data() + 1,
309 58 s.size() - 2,
310 116 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 568 url_view_base::
336 encoded_host() const noexcept
337 {
338 568 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 51 url_view_base::
385 host_ipv4_address() const noexcept
386 {
387 51 if(impl().host_type_ !=
388 urls::host_type::ipv4)
389 35 return {};
390 16 ipv4_address::bytes_type b{{}};
391 32 std::memcpy(
392 16 &b[0], &impl().ip_addr_[0], b.size());
393 16 return urls::ipv4_address(b);
394 }
395
396 inline
397 urls::ipv6_address
398 59 url_view_base::
399 host_ipv6_address() const noexcept
400 {
401 59 if(impl().host_type_ !=
402 urls::host_type::ipv6)
403 45 return {};
404 14 ipv6_address::bytes_type b{{}};
405 28 std::memcpy(
406 14 &b[0], &impl().ip_addr_[0], b.size());
407 14 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 376 url_view_base::
465 has_port() const noexcept
466 {
467 376 auto const n = impl().len(id_port);
468 376 if(n == 0)
469 87 return false;
470 289 BOOST_ASSERT(
471 impl().get(id_port).starts_with(':'));
472 289 return true;
473 }
474
475 inline
476 core::string_view
477 189 url_view_base::
478 port() const noexcept
479 {
480 189 auto s = impl().get(id_port);
481 189 if(s.empty())
482 58 return s;
483 131 BOOST_ASSERT(has_port());
484 131 return s.substr(1);
485 }
486
487 inline
488 std::uint16_t
489 102 url_view_base::
490 port_number() const noexcept
491 {
492 102 BOOST_ASSERT(
493 has_port() ||
494 impl().port_number_ == 0);
495 102 return impl().port_number_;
496 }
497
498 //------------------------------------------------
499 //
500 // Path
501 //
502 //------------------------------------------------
503
504 inline
505 pct_string_view
506 1399 url_view_base::
507 encoded_path() const noexcept
508 {
509 1399 return impl().pct_get(id_path);
510 }
511
512 inline
513 segments_view
514 60 url_view_base::
515 segments() const noexcept
516 {
517 60 return {detail::path_ref(impl())};
518 }
519
520 inline
521 segments_encoded_view
522 707 url_view_base::
523 encoded_segments() const noexcept
524 {
525 return segments_encoded_view(
526 707 detail::path_ref(impl()));
527 }
528
529 //------------------------------------------------
530 //
531 // Query
532 //
533 //------------------------------------------------
534
535 inline
536 bool
537 772 url_view_base::
538 has_query() const noexcept
539 {
540 772 auto const n = impl().len(
541 id_query);
542 772 if(n == 0)
543 620 return false;
544 152 BOOST_ASSERT(
545 impl().get(id_query).
546 starts_with('?'));
547 152 return true;
548 }
549
550 inline
551 pct_string_view
552 311 url_view_base::
553 encoded_query() const noexcept
554 {
555 311 auto s = impl().get(id_query);
556 311 if(s.empty())
557 10 return s;
558 301 BOOST_ASSERT(
559 s.starts_with('?'));
560 301 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 79 url_view_base::
574 params() const noexcept
575 {
576 return params_view(
577 impl(),
578 encoding_opts{
579 79 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 675 url_view_base::
599 has_fragment() const noexcept
600 {
601 675 auto const n = impl().len(id_frag);
602 675 if(n == 0)
603 537 return false;
604 138 BOOST_ASSERT(
605 impl().get(id_frag).
606 starts_with('#'));
607 138 return true;
608 }
609
610 inline
611 pct_string_view
612 180 url_view_base::
613 encoded_fragment() const noexcept
614 {
615 180 auto s = impl().get(id_frag);
616 180 if(! s.empty())
617 {
618 172 BOOST_ASSERT(
619 s.starts_with('#'));
620 172 s.remove_prefix(1);
621 }
622 180 return make_pct_string_view_unsafe(
623 s.data(),
624 s.size(),
625 360 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 284 url_view_base::
700 compare(const url_view_base& other) const noexcept
701 {
702 int comp =
703 284 static_cast<int>(has_scheme()) -
704 284 static_cast<int>(other.has_scheme());
705 284 if ( comp != 0 )
706 return comp;
707
708 284 if (has_scheme())
709 {
710 204 comp = detail::ci_compare(
711 scheme(),
712 other.scheme());
713 204 if ( comp != 0 )
714 14 return comp;
715 }
716
717 270 comp =
718 270 static_cast<int>(has_authority()) -
719 270 static_cast<int>(other.has_authority());
720 270 if ( comp != 0 )
721 return comp;
722
723 270 if (has_authority())
724 {
725 190 comp = authority().compare(other.authority());
726 190 if ( comp != 0 )
727 89 return comp;
728 }
729
730 181 comp = detail::segments_compare(
731 encoded_segments(),
732 other.encoded_segments());
733 181 if ( comp != 0 )
734 43 return comp;
735
736 138 comp =
737 138 static_cast<int>(has_query()) -
738 138 static_cast<int>(other.has_query());
739 138 if ( comp != 0 )
740 return comp;
741
742 138 if (has_query())
743 {
744 48 comp = detail::compare_encoded_query(
745 24 encoded_query(),
746 24 other.encoded_query());
747 24 if ( comp != 0 )
748 19 return comp;
749 }
750
751 119 comp =
752 119 static_cast<int>(has_fragment()) -
753 119 static_cast<int>(other.has_fragment());
754 119 if ( comp != 0 )
755 return comp;
756
757 119 if (has_fragment())
758 {
759 44 comp = detail::compare_encoded(
760 22 encoded_fragment(),
761 22 other.encoded_fragment());
762 22 if ( comp != 0 )
763 21 return comp;
764 }
765
766 98 return 0;
767 }
768
769 } // urls
770 } // boost
771
772 #endif
773