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