// // Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) // Copyright (c) 2022 Alan de Freitas (alandefreitas@gmail.com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // // Official repository: https://github.com/boostorg/url // #ifndef BOOST_URL_IMPL_URL_VIEW_BASE_IPP #define BOOST_URL_IMPL_URL_VIEW_BASE_IPP #include #include #include namespace boost { namespace urls { // construct empty view url_view_base:: url_view_base() noexcept : impl_(from::url) , pi_(&impl_) { } // construct reference url_view_base:: url_view_base( detail::url_impl const& impl) noexcept : impl_(impl) , pi_(&impl_) { } //------------------------------------------------ std::size_t url_view_base:: digest(std::size_t salt) const noexcept { detail::fnv_1a h(salt); detail::ci_digest(pi_->get(id_scheme), h); detail::digest_encoded(pi_->get(id_user), h); detail::digest_encoded(pi_->get(id_pass), h); detail::ci_digest_encoded(pi_->get(id_host), h); h.put(pi_->get(id_port)); detail::normalized_path_digest( pi_->get(id_path), is_path_absolute(), h); detail::digest_encoded(pi_->get(id_query), h); detail::digest_encoded(pi_->get(id_frag), h); return h.digest(); } //------------------------------------------------ // // Observers // //------------------------------------------------ struct url_view_base::shared_impl : url_view { virtual ~shared_impl() { } shared_impl( url_view const& u) noexcept : url_view(u) { impl_.cs_ = reinterpret_cast< char const*>(this + 1); } }; std::shared_ptr url_view_base:: persist() const { using T = shared_impl; using Alloc = std::allocator; Alloc a; auto p = std::allocate_shared( detail::over_allocator( size(), a), url_view(*pi_)); std::memcpy( reinterpret_cast( p.get() + 1), data(), size()); return p; } //------------------------------------------------ // // Scheme // //------------------------------------------------ bool url_view_base:: has_scheme() const noexcept { auto const n = pi_->len( id_scheme); if(n == 0) return false; BOOST_ASSERT(n > 1); BOOST_ASSERT( pi_->get(id_scheme ).ends_with(':')); return true; } string_view url_view_base:: scheme() const noexcept { auto s = pi_->get(id_scheme); if(! s.empty()) { BOOST_ASSERT(s.size() > 1); BOOST_ASSERT(s.ends_with(':')); s.remove_suffix(1); } return s; } urls::scheme url_view_base:: scheme_id() const noexcept { return pi_->scheme_; } //------------------------------------------------ // // Authority // //------------------------------------------------ authority_view url_view_base:: authority() const noexcept { detail::url_impl u(from::authority); u.cs_ = encoded_authority().data(); if(has_authority()) { u.set_size(id_user, pi_->len(id_user) - 2); u.set_size(id_pass, pi_->len(id_pass)); u.set_size(id_host, pi_->len(id_host)); u.set_size(id_port, pi_->len(id_port)); } else { u.set_size(id_user, pi_->len(id_user)); BOOST_ASSERT(pi_->len(id_pass) == 0); BOOST_ASSERT(pi_->len(id_host) == 0); BOOST_ASSERT(pi_->len(id_port) == 0); } u.decoded_[id_user] = pi_->decoded_[id_user]; u.decoded_[id_pass] = pi_->decoded_[id_pass]; u.decoded_[id_host] = pi_->decoded_[id_host]; for (int i = 0; i < 16; ++i) u.ip_addr_[i] = pi_->ip_addr_[i]; u.port_number_ = pi_->port_number_; u.host_type_ = pi_->host_type_; return u.construct_authority(); } pct_string_view url_view_base:: encoded_authority() const noexcept { auto s = pi_->get(id_user, id_path); if(! s.empty()) { BOOST_ASSERT(has_authority()); s.remove_prefix(2); } return make_pct_string_view_unsafe( s.data(), s.size(), pi_->decoded_[id_user] + pi_->decoded_[id_pass] + pi_->decoded_[id_host] + pi_->decoded_[id_port] + has_password()); } //------------------------------------------------ // // Userinfo // //------------------------------------------------ bool url_view_base:: has_userinfo() const noexcept { auto n = pi_->len(id_pass); if(n == 0) return false; BOOST_ASSERT(has_authority()); BOOST_ASSERT(pi_->get( id_pass).ends_with('@')); return true; } bool url_view_base:: has_password() const noexcept { auto const n = pi_->len(id_pass); if(n > 1) { BOOST_ASSERT(pi_->get(id_pass ).starts_with(':')); BOOST_ASSERT(pi_->get(id_pass ).ends_with('@')); return true; } BOOST_ASSERT(n == 0 || pi_->get( id_pass).ends_with('@')); return false; } pct_string_view url_view_base:: encoded_userinfo() const noexcept { auto s = pi_->get( id_user, id_host); if(s.empty()) return s; BOOST_ASSERT( has_authority()); s.remove_prefix(2); if(s.empty()) return s; BOOST_ASSERT( s.ends_with('@')); s.remove_suffix(1); return make_pct_string_view_unsafe( s.data(), s.size(), pi_->decoded_[id_user] + pi_->decoded_[id_pass] + has_password()); } pct_string_view url_view_base:: encoded_user() const noexcept { auto s = pi_->get(id_user); if(! s.empty()) { BOOST_ASSERT( has_authority()); s.remove_prefix(2); } return make_pct_string_view_unsafe( s.data(), s.size(), pi_->decoded_[id_user]); } pct_string_view url_view_base:: encoded_password() const noexcept { auto s = pi_->get(id_pass); switch(s.size()) { case 1: BOOST_ASSERT( s.starts_with('@')); s.remove_prefix(1); BOOST_FALLTHROUGH; case 0: return make_pct_string_view_unsafe( s.data(), s.size(), 0); default: break; } BOOST_ASSERT(s.ends_with('@')); BOOST_ASSERT(s.starts_with(':')); return make_pct_string_view_unsafe( s.data() + 1, s.size() - 2, pi_->decoded_[id_pass]); } //------------------------------------------------ // // Host // //------------------------------------------------ /* host_type host_type() // ipv4, ipv6, ipvfuture, name std::string host() // return encoded_host().decode() pct_string_view encoded_host() // return host part, as-is std::string host_address() // return encoded_host_address().decode() pct_string_view encoded_host_address() // ipv4, ipv6, ipvfut, or encoded name, no brackets ipv4_address host_ipv4_address() // return ipv4_address or {} ipv6_address host_ipv6_address() // return ipv6_address or {} string_view host_ipvfuture() // return ipvfuture or {} std::string host_name() // return decoded name or "" pct_string_view encoded_host_name() // return encoded host name or "" */ pct_string_view url_view_base:: encoded_host() const noexcept { return pi_->pct_get(id_host); } pct_string_view url_view_base:: encoded_host_address() const noexcept { string_view s = pi_->get(id_host); std::size_t n; switch(pi_->host_type_) { default: case urls::host_type::none: BOOST_ASSERT(s.empty()); n = 0; break; case urls::host_type::name: case urls::host_type::ipv4: n = pi_->decoded_[id_host]; break; case urls::host_type::ipv6: case urls::host_type::ipvfuture: { BOOST_ASSERT( pi_->decoded_[id_host] == s.size()); BOOST_ASSERT(s.size() >= 2); BOOST_ASSERT(s.front() == '['); BOOST_ASSERT(s.back() == ']'); s = s.substr(1, s.size() - 2); n = pi_->decoded_[id_host] - 2; break; } } return make_pct_string_view_unsafe( s.data(), s.size(), n); } urls::ipv4_address url_view_base:: host_ipv4_address() const noexcept { if(pi_->host_type_ != urls::host_type::ipv4) return {}; ipv4_address::bytes_type b{{}}; std::memcpy( &b[0], &pi_->ip_addr_[0], b.size()); return urls::ipv4_address(b); } urls::ipv6_address url_view_base:: host_ipv6_address() const noexcept { if(pi_->host_type_ != urls::host_type::ipv6) return {}; ipv6_address::bytes_type b{{}}; std::memcpy( &b[0], &pi_->ip_addr_[0], b.size()); return urls::ipv6_address(b); } string_view url_view_base:: host_ipvfuture() const noexcept { if(pi_->host_type_ != urls::host_type::ipvfuture) return {}; string_view s = pi_->get(id_host); BOOST_ASSERT(s.size() >= 6); BOOST_ASSERT(s.front() == '['); BOOST_ASSERT(s.back() == ']'); s = s.substr(1, s.size() - 2); return s; } pct_string_view url_view_base:: encoded_host_name() const noexcept { if(pi_->host_type_ != urls::host_type::name) return {}; string_view s = pi_->get(id_host); return make_pct_string_view_unsafe( s.data(), s.size(), pi_->decoded_[id_host]); } //------------------------------------------------ bool url_view_base:: has_port() const noexcept { auto const n = pi_->len(id_port); if(n == 0) return false; BOOST_ASSERT( pi_->get(id_port).starts_with(':')); return true; } string_view url_view_base:: port() const noexcept { auto s = pi_->get(id_port); if(s.empty()) return s; BOOST_ASSERT(has_port()); return s.substr(1); } std::uint16_t url_view_base:: port_number() const noexcept { BOOST_ASSERT( has_port() || pi_->port_number_ == 0); return pi_->port_number_; } //------------------------------------------------ // // Path // //------------------------------------------------ pct_string_view url_view_base:: encoded_path() const noexcept { return pi_->pct_get(id_path); } segments_view url_view_base:: segments() const noexcept { return {detail::path_ref(*pi_)}; } segments_encoded_view url_view_base:: encoded_segments() const noexcept { return segments_encoded_view( detail::path_ref(*pi_)); } //------------------------------------------------ // // Query // //------------------------------------------------ bool url_view_base:: has_query() const noexcept { auto const n = pi_->len( id_query); if(n == 0) return false; BOOST_ASSERT( pi_->get(id_query). starts_with('?')); return true; } pct_string_view url_view_base:: encoded_query() const noexcept { auto s = pi_->get(id_query); if(s.empty()) return s; BOOST_ASSERT( s.starts_with('?')); return s.substr(1); } params_encoded_view url_view_base:: encoded_params() const noexcept { return params_encoded_view(*pi_); } params_view url_view_base:: params() const noexcept { return params_view( *pi_, encoding_opts{ true,false,false}); } params_view url_view_base:: params(encoding_opts opt) const noexcept { return params_view(*pi_, opt); } //------------------------------------------------ // // Fragment // //------------------------------------------------ bool url_view_base:: has_fragment() const noexcept { auto const n = pi_->len(id_frag); if(n == 0) return false; BOOST_ASSERT( pi_->get(id_frag). starts_with('#')); return true; } pct_string_view url_view_base:: encoded_fragment() const noexcept { auto s = pi_->get(id_frag); if(! s.empty()) { BOOST_ASSERT( s.starts_with('#')); s.remove_prefix(1); } return make_pct_string_view_unsafe( s.data(), s.size(), pi_->decoded_[id_frag]); } //------------------------------------------------ // // Compound Fields // //------------------------------------------------ pct_string_view url_view_base:: encoded_host_and_port() const noexcept { return pi_->pct_get(id_host, id_path); } pct_string_view url_view_base:: encoded_origin() const noexcept { if(pi_->len(id_user) < 2) return {}; return pi_->get(id_scheme, id_path); } pct_string_view url_view_base:: encoded_resource() const noexcept { auto n = pi_->decoded_[id_path] + pi_->decoded_[id_query] + pi_->decoded_[id_frag]; if(has_query()) ++n; if(has_fragment()) ++n; BOOST_ASSERT(pct_string_view( pi_->get(id_path, id_end) ).decoded_size() == n); auto s = pi_->get(id_path, id_end); return make_pct_string_view_unsafe( s.data(), s.size(), n); } pct_string_view url_view_base:: encoded_target() const noexcept { auto n = pi_->decoded_[id_path] + pi_->decoded_[id_query]; if(has_query()) ++n; BOOST_ASSERT(pct_string_view( pi_->get(id_path, id_frag) ).decoded_size() == n); auto s = pi_->get(id_path, id_frag); return make_pct_string_view_unsafe( s.data(), s.size(), n); } //------------------------------------------------ // // Comparisons // //------------------------------------------------ int url_view_base:: compare(const url_view_base& other) const noexcept { int comp = static_cast(has_scheme()) - static_cast(other.has_scheme()); if ( comp != 0 ) return comp; if (has_scheme()) { comp = detail::ci_compare( scheme(), other.scheme()); if ( comp != 0 ) return comp; } comp = static_cast(has_authority()) - static_cast(other.has_authority()); if ( comp != 0 ) return comp; if (has_authority()) { comp = authority().compare(other.authority()); if ( comp != 0 ) return comp; } comp = detail::segments_compare( encoded_segments(), other.encoded_segments()); if ( comp != 0 ) return comp; comp = static_cast(has_query()) - static_cast(other.has_query()); if ( comp != 0 ) return comp; if (has_query()) { comp = detail::compare_encoded( encoded_query(), other.encoded_query()); if ( comp != 0 ) return comp; } comp = static_cast(has_fragment()) - static_cast(other.has_fragment()); if ( comp != 0 ) return comp; if (has_fragment()) { comp = detail::compare_encoded( encoded_fragment(), other.encoded_fragment()); if ( comp != 0 ) return comp; } return 0; } } // urls } // boost #endif