//
// 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_URL_VIEW_BASE_HPP
#define BOOST_URL_URL_VIEW_BASE_HPP

#include <boost/url/detail/config.hpp>
#include <boost/url/authority_view.hpp>
#include <boost/url/host_type.hpp>
#include <boost/url/ipv4_address.hpp>
#include <boost/url/ipv6_address.hpp>
#include <boost/url/params_view.hpp>
#include <boost/url/params_encoded_view.hpp>
#include <boost/url/pct_string_view.hpp>
#include <boost/url/scheme.hpp>
#include <boost/url/segments_encoded_view.hpp>
#include <boost/url/segments_view.hpp>
#include <boost/url/detail/url_impl.hpp>
#include <boost/url/grammar/string_token.hpp>
#include <boost/assert.hpp>
#include <cstddef>
#include <cstdint>
#include <iosfwd>
#include <memory>
#include <string>
#include <utility>

namespace boost {
namespace urls {

#ifndef BOOST_URL_DOCS
namespace detail {
struct pattern;
}
#endif


/** Common functionality for containers

    This base class is used by the library
    to provide common member functions for
    containers. This cannot be instantiated
    directly; Instead, use one of the
    containers or functions:

    @par Containers
        @li @ref url
        @li @ref url_view
        @li @ref static_url

    @par Functions
        @li @ref parse_absolute_uri
        @li @ref parse_origin_form
        @li @ref parse_relative_ref
        @li @ref parse_uri
        @li @ref parse_uri_reference
*/
class BOOST_SYMBOL_VISIBLE url_view_base
    : private detail::parts_base
{
    detail::url_impl impl_;
    detail::url_impl const* external_impl_;

    friend class url;
    friend class url_base;
    friend class url_view;
    friend class static_url_base;
    friend class params_base;
    friend class params_encoded_base;
    friend class params_encoded_ref;
    friend class params_encoded_view;
    friend class params_ref;
    friend class params_view;
    friend class segments_base;
    friend class segments_encoded_base;
    friend class segments_encoded_ref;
    friend class segments_encoded_view;
    friend class segments_ref;
    friend class segments_view;
    friend struct detail::pattern;

    struct shared_impl;

    // Returns reference to the active implementation.
    // Uses external_impl_ if set, otherwise local impl_.
    BOOST_URL_CXX14_CONSTEXPR
    detail::url_impl const&
    impl() const noexcept
    {
        return external_impl_ ? *external_impl_ : impl_;
    }

    BOOST_URL_CXX14_CONSTEXPR
    url_view_base() noexcept
        : impl_(detail::url_impl::from::url)
        , external_impl_(nullptr)
    {
    }

    BOOST_URL_CXX14_CONSTEXPR
    explicit url_view_base(
        detail::url_impl const& impl) noexcept
        : impl_(impl)
        , external_impl_(nullptr)
    {
    }

    ~url_view_base() = default;

    BOOST_URL_CXX14_CONSTEXPR
    url_view_base(
        url_view_base const& o) noexcept = default;

    BOOST_URL_CXX14_CONSTEXPR
    url_view_base(
        url_view_base&& o) noexcept = default;

    url_view_base& operator=(
        url_view_base const&) = delete;

protected:
    /** Calculate a hash of the url

        This function calculates a hash of the
        url as if it were always normalized.

        @par Complexity
        Linear in `this->size()`.

        @par Exception Safety
        Throws nothing.

        @param salt An initial value to add to
        the hash

        @return A hash value suitable for use
        in hash-based containers.
    */
    std::size_t
    digest(std::size_t salt = 0) const noexcept;

public:
    //--------------------------------------------
    //
    // Observers
    //
    //--------------------------------------------

    /** Return the maximum number of characters possible

        This represents the largest number
        of characters that are theoretically
        possible to represent in a url,
        not including any null terminator.
        In practice the actual possible size
        may be lower than this number.

        @par Complexity
        Constant.

        @par Exception Safety
        Throws nothing.

        @return The maximum number of characters.
    */
    static
    constexpr
    std::size_t
    max_size() noexcept
    {
        return BOOST_URL_MAX_SIZE;
    }

    /** Return the number of characters in the url

        This function returns the number of
        characters in the url's encoded string,
        not including any null terminator,
        if present.

        @par Example
        @code
        assert( url_view( "file:///Program%20Files" ).size() == 23 );
        @endcode

        @par Complexity
        Constant.

        @par Exception Safety
        Throws nothing.

        @return The number of characters in the url.
    */
    std::size_t
    size() const noexcept
    {
        return impl().offset(id_end);
    }

    /** Return true if the url is empty

        The empty string matches the
        <em>relative-ref</em> grammar.

        @par Example
        @code
        assert( url_view( "" ).empty() );
        @endcode

        @par Complexity
        Constant.

        @par Exception Safety
        Throws nothing.

        @par BNF
        @code
        relative-ref  = relative-part [ "?" query ] [ "#" fragment ]

        relative-part = "//" authority path-abempty
                      / path-absolute
                      / path-noscheme
                      / path-empty
        @endcode

        @par Specification
        @li <a href="https://datatracker.ietf.org/doc/html/rfc3986#section-4.2">4.2.  Relative Reference (rfc3986)</a>

        @return `true` if the url is empty.
    */
    bool
    empty() const noexcept
    {
        return impl().offset(id_end) == 0;
    }

    /** Return a pointer to the url's character buffer

        This function returns a pointer to
        the first character of the url, which
        is not guaranteed to be null-terminated.

        @par Complexity
        Constant.

        @par Exception Safety
        Throws nothing.

        @return A pointer to the first character.
    */
    char const*
    data() const noexcept
    {
        return impl().cs_;
    }

    /** Return the url string

        This function returns the entire url,
        which may contain percent escapes.

        @par Example
        @code
        assert( url_view( "http://www.example.com" ).buffer() == "http://www.example.com" );
        @endcode

        @par Complexity
        Constant.

        @par Exception Safety
        Throws nothing.

        @return The url as a string.
    */
    core::string_view
    buffer() const noexcept
    {
        return core::string_view(
            data(), size());
    }

    /** Return the URL as a core::string_view

        @par Complexity
        Constant.

        @par Exception Safety
        Throws nothing.

        @return A string view of the URL.
    */
    operator core::string_view() const noexcept
    {
        return buffer();
    }

    /** Return a shared, persistent copy of the url

        This function returns a read-only copy of
        the url, with shared lifetime. The returned
        value owns (persists) the underlying string.
        The algorithm used to create the value
        minimizes the number of individual memory
        allocations, making it more efficient than
        when using direct standard library functions.

        @par Example
        @code
        std::shared_ptr< url_view const > sp;
        {
            std::string s( "http://example.com" );
            url_view u( s );                        // u references characters in s

            assert( u.data() == s.data() );         // same buffer

            sp = u.persist();

            assert( sp->data() != s.data() );       // different buffer
            assert( sp->buffer() == s);             // same contents

            // s is destroyed and thus u
            // becomes invalid, but sp remains valid.
        }
        @endcode

        @par Complexity
        Linear in `this->size()`.

        @par Exception Safety
        Calls to allocate may throw.

        @return A shared pointer to a read-only url_view.
    */
    std::shared_ptr<
        url_view const> persist() const;

    //--------------------------------------------
    //
    // Scheme
    //
    //--------------------------------------------

    /** Return true a scheme is present

        This function returns true if this
        contains a scheme.

        @par Example
        @code
        assert( url_view( "http://www.example.com" ).has_scheme() );
        @endcode

        @par Complexity
        Constant.

        @par Exception Safety
        Throws nothing.

        @par BNF
        @code
        URI             = scheme ":" hier-part [ "?" query ] [ "#" fragment ]

        absolute-URI    = scheme ":" hier-part [ "?" query ]

        scheme          = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
        @endcode

        @par Specification
        @li <a href="https://datatracker.ietf.org/doc/html/rfc3986#section-3.1">3.1. Scheme (rfc3986)</a>

        @see
            @ref scheme,
            @ref scheme_id.

        @return `true` if the url contains a scheme.
    */
    bool
    has_scheme() const noexcept;

    /** Return the scheme

        This function returns the scheme if it
        exists, without a trailing colon (':').
        Otherwise it returns an empty string.
        Note that schemes are case-insensitive,
        and the canonical form is lowercased.

        @par Example
        @code
        assert( url_view( "http://www.example.com" ).scheme() == "http" );
        @endcode

        @par Exception Safety
        Throws nothing.

        @par BNF
        @code
        scheme          = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )

        URI             = scheme ":" hier-part [ "?" query ] [ "#" fragment ]

        absolute-URI    = scheme ":" hier-part [ "?" query ]
        @endcode

        @par Specification
        @li <a href="https://datatracker.ietf.org/doc/html/rfc3986#section-3.1">3.1. Scheme (rfc3986)</a>

        @see
            @ref has_scheme,
            @ref scheme_id.

        @return The scheme as a string.
    */
    core::string_view
    scheme() const noexcept;

    /** Return the scheme

        This function returns a value which
        depends on the scheme in the url:

        @li If the scheme is a well-known
        scheme, corresponding value from
        the enumeration @ref urls::scheme
        is returned.

        @li If a scheme is present but is not
        a well-known scheme, the value
        returned is @ref urls::scheme::unknown.

        @li Otherwise, if the scheme is absent
        the value returned is
        @ref urls::scheme::none.

        @par Example
        @code
        assert( url_view( "wss://www.example.com/crypto.cgi" ).scheme_id() == scheme::wss );
        @endcode

        @par Complexity
        Constant.

        @par Exception Safety
        Throws nothing.

        @par BNF
        @code
        URI             = scheme ":" hier-part [ "?" query ] [ "#" fragment ]

        absolute-URI    = scheme ":" hier-part [ "?" query ]

        scheme          = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
        @endcode

        @par Specification
        @li <a href="https://datatracker.ietf.org/doc/html/rfc3986#section-3.1">3.1. Scheme (rfc3986)</a>

        @see
            @ref has_scheme,
            @ref scheme.

        @return The scheme as an enumeration value.
    */
    urls::scheme
    scheme_id() const noexcept;

    //--------------------------------------------
    //
    // Authority
    //
    //--------------------------------------------

    /** Return true if an authority is present

        This function returns true if the url
        contains an authority. The presence of
        an authority is denoted by a double
        slash ("//") at the beginning or after
        the scheme.

        @par Example
        @code
        assert( url_view( "http://www.example.com/index.htm" ).has_authority() );
        @endcode

        @par Complexity
        Constant.

        @par Exception Safety
        Throws nothing.

        @par BNF
        @code
        authority       = [ userinfo "@" ] host [ ":" port ]

        URI             = scheme ":" hier-part [ "?" query ] [ "#" fragment ]

        absolute-URI    = scheme ":" hier-part [ "?" query ]

        URI-reference   = URI / relative-ref

        relative-ref    = relative-part [ "?" query ] [ "#" fragment ]

        hier-part       = "//" authority path-abempty
                        ; (more...)

        relative-part   = "//" authority path-abempty
                        ; (more...)

        @endcode

        @par Specification
        @li <a href="https://datatracker.ietf.org/doc/html/rfc3986#section-3.2">3.2. Authority (rfc3986)</a>

        @see
            @ref authority,
            @ref encoded_authority.

        @return `true` if the url contains an authority.
    */
    bool
    has_authority() const noexcept
    {
        return impl().len(id_user) > 0;
    }

    /** Return the authority

        This function returns the authority as
        an @ref authority_view.

        @par Example
        @code
        authority_view a = url_view( "https://www.example.com:8080/index.htm" ).authority();
        @endcode

        @par Complexity
        Constant.

        @par Exception Safety
        Throws nothing.

        @par BNF
        @code
        authority   = [ userinfo "@" ] host [ ":" port ]
        @endcode

        @par Specification
        @li <a href="https://datatracker.ietf.org/doc/html/rfc3986#section-3.2">3.2. Authority (rfc3986)</a>

        @see
            @ref encoded_authority,
            @ref has_authority.

        @return An authority_view representing the authority.
    */
    authority_view
    authority() const noexcept;

    /** Return the authority.

        If present, this function returns a
        string representing the authority (which
        may be empty).
        Otherwise it returns an empty string.
        The returned string may contain
        percent escapes.

        @par Example
        @code
        assert( url_view( "file://Network%20Drive/My%2DFiles" ).encoded_authority() == "Network%20Drive" );
        @endcode

        @par Complexity
        Constant.

        @par Exception Safety
        Throws nothing.

        @par BNF
        @code
        authority   = [ userinfo "@" ] host [ ":" port ]
        @endcode

        @par Specification
        @li <a href="https://datatracker.ietf.org/doc/html/rfc3986#section-3.2">3.2. Authority (rfc3986)</a>

        @see
            @ref authority,
            @ref has_authority.

        @return The authority as a string.
    */
    pct_string_view
    encoded_authority() const noexcept;

    //--------------------------------------------
    //
    // Userinfo
    //
    //--------------------------------------------

    /** Return true if a userinfo is present

        This function returns true if this
        contains a userinfo.

        @par Example
        @code
        assert( url_view( "http://jane%2Ddoe:pass@example.com" ).has_userinfo() );
        @endcode

        @par Complexity
        Constant.

        @par Exception Safety
        Throws nothing.

        @par BNF
        @code
        userinfo    = user [ ":" [ password ] ]

        authority   = [ userinfo "@" ] host [ ":" port ]
        @endcode

        @par Specification
        @li <a href="https://datatracker.ietf.org/doc/html/rfc3986#section-3.2.1">3.2.1. User Information (rfc3986)</a>

        @see
            @ref has_password,
            @ref encoded_password,
            @ref encoded_user,
            @ref encoded_userinfo,
            @ref password,
            @ref user,
            @ref userinfo.

        @return `true` if the userinfo is present.
    */
    bool
    has_userinfo() const noexcept;

    /** Return true if a password is present

        This function returns true if the
        userinfo is present and contains
        a password.

        @par Example
        @code
        assert( url_view( "http://jane%2Ddoe:pass@example.com" ).has_password() );
        @endcode

        @par Complexity
        Constant.

        @par Exception Safety
        Throws nothing.

        @par BNF
        @code
        userinfo    = user [ ":" [ password ] ]

        user        = *( unreserved / pct-encoded / sub-delims )
        password    = *( unreserved / pct-encoded / sub-delims / ":" )
        @endcode

        @par Specification
        @li <a href="https://datatracker.ietf.org/doc/html/rfc3986#section-3.2.1">3.2.1. User Information (rfc3986)</a>

        @see
            @ref has_userinfo,
            @ref encoded_password,
            @ref encoded_user,
            @ref encoded_userinfo,
            @ref password,
            @ref user,
            @ref userinfo.

        @return `true` if the userinfo contains a password.
    */
    bool
    has_password() const noexcept;

    /** Return the userinfo

        If present, this function returns a
        string representing the userinfo (which
        may be empty).
        Otherwise it returns an empty string.
        Any percent-escapes in the string are
        decoded first.

        @note
        This function uses the string token
        return type customization. Depending on
        the token passed, the return type and
        behavior of the function can be different.
        See @ref string_token::return_string
        for more information.

        @par Example
        @code
        assert( url_view( "http://jane%2Ddoe:pass@example.com" ).userinfo() == "jane-doe:pass" );
        @endcode

        @par Complexity
        Linear in `this->userinfo().size()`.

        @par Exception Safety
        Calls to allocate may throw.

        @return When called with no arguments,
        a value of type `std::string` is
        returned. Otherwise, the return type
        and meaning depends on the string token
        passed to the function.

        @par BNF
        @code
        userinfo    = user [ ":" [ password ] ]

        authority   = [ userinfo "@" ] host [ ":" port ]
        @endcode

        @par Specification
        @li <a href="https://datatracker.ietf.org/doc/html/rfc3986#section-3.2.1">3.2.1. User Information (rfc3986)</a>

        @see
            @ref has_password,
            @ref has_userinfo,
            @ref encoded_password,
            @ref encoded_user,
            @ref encoded_userinfo,
            @ref password,
            @ref user.

        @param token The string token to use.
        @return The userinfo as a string.
    */
    template<BOOST_URL_STRTOK_TPARAM>
    BOOST_URL_STRTOK_RETURN
    userinfo(
        StringToken&& token = {}) const
    {
        encoding_opts opt;
        opt.space_as_plus = false;
        return encoded_userinfo().decode(
            opt, std::forward<StringToken>(token));
    }

    /** Return the userinfo

        If present, this function returns a
        string representing the userinfo (which
        may be empty).
        Otherwise it returns an empty string.
        The returned string may contain
        percent escapes.

        @par Example
        @code
        assert( url_view( "http://jane%2Ddoe:pass@example.com" ).encoded_userinfo() == "jane%2Ddoe:pass" );
        @endcode

        @par Complexity
        Constant.

        @par Exception Safety
        Throws nothing

        @par BNF
        @code
        userinfo    = user [ ":" [ password ] ]

        authority   = [ userinfo "@" ] host [ ":" port ]
        @endcode

        @par Specification
        @li <a href="https://datatracker.ietf.org/doc/html/rfc3986#section-3.2.1">3.2.1. User Information (rfc3986)</a>

        @see
            @ref has_password,
            @ref has_userinfo,
            @ref encoded_password,
            @ref encoded_user,
            @ref password,
            @ref user,
            @ref userinfo.

        @return The userinfo as a string.
    */
    pct_string_view
    encoded_userinfo() const noexcept;

    //--------------------------------------------

    /** Return the user

        If present, this function returns a
        string representing the user (which
        may be empty).
        Otherwise it returns an empty string.
        Any percent-escapes in the string are
        decoded first.

        @par Example
        @code
        assert( url_view( "http://jane%2Ddoe:pass@example.com" ).user() == "jane-doe" );
        @endcode

        @par Complexity
        Linear in `this->user().size()`.

        @par Exception Safety
        Calls to allocate may throw.

        @par BNF
        @code
        userinfo    = user [ ":" [ password ] ]

        user        = *( unreserved / pct-encoded / sub-delims )
        password    = *( unreserved / pct-encoded / sub-delims / ":" )
        @endcode

        @par Specification
        @li <a href="https://datatracker.ietf.org/doc/html/rfc3986#section-3.2.1">3.2.1. User Information (rfc3986)</a>

        @see
            @ref has_password,
            @ref has_userinfo,
            @ref encoded_password,
            @ref encoded_user,
            @ref encoded_userinfo,
            @ref password,
            @ref userinfo.

        @param token The string token to use.
        @return The user as a string.
    */
    template<BOOST_URL_STRTOK_TPARAM>
    BOOST_URL_STRTOK_RETURN
    user(
        StringToken&& token = {}) const
    {
        encoding_opts opt;
        opt.space_as_plus = false;
        return encoded_user().decode(
            opt, std::forward<StringToken>(token));
    }

    /** Return the user

        If present, this function returns a
        string representing the user (which
        may be empty).
        Otherwise it returns an empty string.
        The returned string may contain
        percent escapes.

        @par Example
        @code
        assert( url_view( "http://jane%2Ddoe:pass@example.com" ).encoded_user() == "jane%2Ddoe" );
        @endcode

        @par Complexity
        Constant.

        @par Exception Safety
        Throws nothing.

        @par BNF
        @code
        userinfo    = user [ ":" [ password ] ]

        user        = *( unreserved / pct-encoded / sub-delims )
        password    = *( unreserved / pct-encoded / sub-delims / ":" )
        @endcode

        @par Specification
        @li <a href="https://datatracker.ietf.org/doc/html/rfc3986#section-3.2.1">3.2.1. User Information (rfc3986)</a>

        @see
            @ref has_password,
            @ref has_userinfo,
            @ref encoded_password,
            @ref encoded_userinfo,
            @ref password,
            @ref user,
            @ref userinfo.

        @return The user as a string.
    */
    pct_string_view
    encoded_user() const noexcept;

    /** Return the password

        If present, this function returns a
        string representing the password (which
        may be an empty string).
        Otherwise it returns an empty string.
        Any percent-escapes in the string are
        decoded first.

        @par Example
        @code
        assert( url_view( "http://jane%2Ddoe:pass@example.com" ).password() == "pass" );
        @endcode

        @par Complexity
        Linear in `this->password().size()`.

        @par Exception Safety
        Calls to allocate may throw.

        @par BNF
        @code
        userinfo    = user [ ":" [ password ] ]

        user        = *( unreserved / pct-encoded / sub-delims )
        password    = *( unreserved / pct-encoded / sub-delims / ":" )
        @endcode

        @par Specification
        @li <a href="https://datatracker.ietf.org/doc/html/rfc3986#section-3.2.1">3.2.1. User Information (rfc3986)</a>

        @see
            @ref has_password,
            @ref has_userinfo,
            @ref encoded_password,
            @ref encoded_user,
            @ref encoded_userinfo,
            @ref user,
            @ref userinfo.

        @param token The string token to use.
        @return The password as a string.
    */
    template<BOOST_URL_STRTOK_TPARAM>
    BOOST_URL_STRTOK_RETURN
    password(
        StringToken&& token = {}) const
    {
        encoding_opts opt;
        opt.space_as_plus = false;
        return encoded_password().decode(
            opt, std::forward<StringToken>(token));
    }

    /** Return the password

        This function returns the password portion
        of the userinfo as a percent-encoded string.

        @par Example
        @code
        assert( url_view( "http://jane%2Ddoe:pass@example.com" ).encoded_password() == "pass" );
        @endcode

        @par Complexity
        Constant.

        @par Exception Safety
        Throws nothing.

        @par BNF
        @code
        userinfo    = user [ ":" [ password ] ]

        user        = *( unreserved / pct-encoded / sub-delims )
        password    = *( unreserved / pct-encoded / sub-delims / ":" )
        @endcode

        @par Specification
        @li <a href="https://datatracker.ietf.org/doc/html/rfc3986#section-3.2.1">3.2.1. User Information (rfc3986)</a>

        @see
            @ref has_password,
            @ref has_userinfo,
            @ref encoded_user,
            @ref encoded_userinfo,
            @ref password,
            @ref user,
            @ref userinfo.

        @return The password as a string.
    */
    pct_string_view
    encoded_password() const noexcept;

    //--------------------------------------------
    //
    // Host
    //
    //--------------------------------------------

    /** Return the host type

        This function returns one of the
        following constants representing the
        type of host present.

        @li @ref host_type::ipv4
        @li @ref host_type::ipv6
        @li @ref host_type::ipvfuture
        @li @ref host_type::name
        @li @ref host_type::none

        When @ref has_authority is false, the
        host type is @ref host_type::none.

        @par Example
        @code
        assert( url_view( "https://192.168.0.1/local.htm" ).host_type() == host_type::ipv4 );
        @endcode

        @par Complexity
        Constant.

        @par Exception Safety
        Throws nothing.

        @par Specification
        @li <a href="https://datatracker.ietf.org/doc/html/rfc3986#section-3.2.2">3.2.2. Host (rfc3986)</a>

        @return The type of host present.
    */
    urls::host_type
    host_type() const noexcept
    {
        return impl().host_type_;
    }

    /** Return the host

        This function returns the host portion
        of the authority as a string, or the
        empty string if there is no authority.
        Any percent-escapes in the string are
        decoded first.

        @par Example
        @code
        assert( url_view( "https://www%2droot.example.com/" ).host() == "www-root.example.com" );
        @endcode

        @par Complexity
        Linear in `this->host().size()`.

        @par Exception Safety
        Calls to allocate may throw.

        @par BNF
        @code
        host        = IP-literal / IPv4address / reg-name

        IP-literal  = "[" ( IPv6address / IPvFuture  ) "]"

        reg-name    = *( unreserved / pct-encoded / "-" / ".")
        @endcode

        @par Specification
        @li <a href="https://datatracker.ietf.org/doc/html/rfc3986#section-3.2.2">3.2.2. Host (rfc3986)</a>

        @param token A string token customization
        @return The host address as a string.
    */
    template<BOOST_URL_STRTOK_TPARAM>
    BOOST_URL_STRTOK_RETURN
    host(
        StringToken&& token = {}) const
    {
        encoding_opts opt;
        opt.space_as_plus = false;
        return encoded_host().decode(
            opt, std::forward<StringToken>(token));
    }

    /** Return the host

        This function returns the host portion
        of the authority as a string, or the
        empty string if there is no authority.
        The returned string may contain
        percent escapes.

        @par Example
        @code
        assert( url_view( "https://www%2droot.example.com/" ).encoded_host() == "www%2droot.example.com" );
        @endcode

        @par Complexity
        Constant.

        @par Exception Safety
        Throws nothing.

        @par BNF
        @code
        host        = IP-literal / IPv4address / reg-name

        IP-literal  = "[" ( IPv6address / IPvFuture  ) "]"

        reg-name    = *( unreserved / pct-encoded / "-" / ".")
        @endcode

        @par Specification
        @li <a href="https://datatracker.ietf.org/doc/html/rfc3986#section-3.2.2">3.2.2. Host (rfc3986)</a>

        @return The host address as a string.
    */
    pct_string_view
    encoded_host() const noexcept;

    /** Return the host

        The value returned by this function
        depends on the type of host returned
        from the function @ref host_type.

        @li If the type is @ref host_type::ipv4,
        then the IPv4 address string is returned.

        @li If the type is @ref host_type::ipv6,
        then the IPv6 address string is returned,
        without any enclosing brackets.

        @li If the type is @ref host_type::ipvfuture,
        then the IPvFuture address string is returned,
        without any enclosing brackets.

        @li If the type is @ref host_type::name,
        then the host name string is returned.
        Any percent-escapes in the string are
        decoded first.

        @li If the type is @ref host_type::none,
        then an empty string is returned.

        @par Example
        @code
        assert( url_view( "https://[1::6:c0a8:1]/" ).host_address() == "1::6:c0a8:1" );
        @endcode

        @par Complexity
        Linear in `this->host_address().size()`.

        @par Exception Safety
        Calls to allocate may throw.

        @par BNF
        @code
        host        = IP-literal / IPv4address / reg-name

        IP-literal  = "[" ( IPv6address / IPvFuture  ) "]"

        reg-name    = *( unreserved / pct-encoded / "-" / ".")
        @endcode

        @par Specification
        @li <a href="https://datatracker.ietf.org/doc/html/rfc3986#section-3.2.2">3.2.2. Host (rfc3986)</a>

        @param token A string token customization
        @return The host address as a string.
    */
    template<BOOST_URL_STRTOK_TPARAM>
    BOOST_URL_STRTOK_RETURN
    host_address(
        StringToken&& token = {}) const
    {
        encoding_opts opt;
        opt.space_as_plus = false;
        return encoded_host_address().decode(
            opt, std::forward<StringToken>(token));
    }

    /** Return the host

        The value returned by this function
        depends on the type of host returned
        from the function @ref host_type.

        @li If the type is @ref host_type::ipv4,
        then the IPv4 address string is returned.

        @li If the type is @ref host_type::ipv6,
        then the IPv6 address string is returned,
        without any enclosing brackets.

        @li If the type is @ref host_type::ipvfuture,
        then the IPvFuture address string is returned,
        without any enclosing brackets.

        @li If the type is @ref host_type::name,
        then the host name string is returned.
        Any percent-escapes in the string are
        decoded first.

        @li If the type is @ref host_type::none,
        then an empty string is returned.
        The returned string may contain
        percent escapes.

        @par Example
        @code
        assert( url_view( "https://www%2droot.example.com/" ).encoded_host_address() == "www%2droot.example.com" );
        @endcode

        @par Complexity
        Constant.

        @par Exception Safety
        Throws nothing.

        @par BNF
        @code
        host        = IP-literal / IPv4address / reg-name

        IP-literal  = "[" ( IPv6address / IPvFuture  ) "]"

        reg-name    = *( unreserved / pct-encoded / "-" / ".")
        @endcode

        @par Specification
        @li <a href="https://datatracker.ietf.org/doc/html/rfc3986#section-3.2.2">3.2.2. Host (rfc3986)</a>

        @return The host address as a string.
    */
    pct_string_view
    encoded_host_address() const noexcept;

    /** Return the host IPv4 address

        If the host type is @ref host_type::ipv4,
        this function returns the address as
        a value of type @ref ipv4_address.
        Otherwise, if the host type is not an IPv4
        address, it returns a default-constructed
        value which is equal to the unspecified
        address "0.0.0.0".

        @par Example
        @code
        assert( url_view( "http://127.0.0.1/index.htm?user=win95" ).host_ipv4_address() == ipv4_address( "127.0.0.1" ) );
        @endcode

        @par Complexity
        Constant.

        @par Exception Safety
        Throws nothing.

        @par BNF
        @code
        IPv4address = dec-octet "." dec-octet "." dec-octet "." dec-octet

        dec-octet   = DIGIT                 ; 0-9
                    / %x31-39 DIGIT         ; 10-99
                    / "1" 2DIGIT            ; 100-199
                    / "2" %x30-34 DIGIT     ; 200-249
                    / "25" %x30-35          ; 250-255
        @endcode

        @par Specification
        @li <a href="https://datatracker.ietf.org/doc/html/rfc3986#section-3.2.2">3.2.2. Host (rfc3986)</a>

        @return The IPv4 address as a value of type @ref ipv4_address.
    */
    ipv4_address
    host_ipv4_address() const noexcept;

    /** Return the host IPv6 address

        If the host type is @ref host_type::ipv6,
        this function returns the address as
        a value of type @ref ipv6_address.
        Otherwise, if the host type is not an IPv6
        address, it returns a default-constructed
        value which is equal to the unspecified
        address "0:0:0:0:0:0:0:0".

        @par Example
        @code
        assert( url_view( "ftp://[::1]/" ).host_ipv6_address() == ipv6_address( "::1" ) );
        @endcode

        @par Complexity
        Constant.

        @par Exception Safety
        Throws nothing.

        @par BNF
        @code
        IPv6address =                            6( h16 ":" ) ls32
                    /                       "::" 5( h16 ":" ) ls32
                    / [               h16 ] "::" 4( h16 ":" ) ls32
                    / [ *1( h16 ":" ) h16 ] "::" 3( h16 ":" ) ls32
                    / [ *2( h16 ":" ) h16 ] "::" 2( h16 ":" ) ls32
                    / [ *3( h16 ":" ) h16 ] "::"    h16 ":"   ls32
                    / [ *4( h16 ":" ) h16 ] "::"              ls32
                    / [ *5( h16 ":" ) h16 ] "::"              h16
                    / [ *6( h16 ":" ) h16 ] "::"

        ls32        = ( h16 ":" h16 ) / IPv4address
                    ; least-significant 32 bits of address

        h16         = 1*4HEXDIG
                    ; 16 bits of address represented in hexadecimal
        @endcode

        @par Specification
        @li <a href="https://datatracker.ietf.org/doc/html/rfc3986#section-3.2.2">3.2.2. Host (rfc3986)</a>

        @return The IPv6 address as a value of type @ref ipv6_address.
    */
    ipv6_address
    host_ipv6_address() const noexcept;

    /** Return the host IPvFuture address

        If the host type is @ref host_type::ipvfuture,
        this function returns the address as
        a string.
        Otherwise, if the host type is not an
        IPvFuture address, it returns an
        empty string.

        @par Example
        @code
        assert( url_view( "http://[v1fe.d:9]/index.htm" ).host_ipvfuture() == "v1fe.d:9" );
        @endcode

        @par Complexity
        Constant.

        @par Exception Safety
        Throws nothing.

        @par BNF
        @code
        IPvFuture  = "v" 1*HEXDIG "." 1*( unreserved / sub-delims / ":" )
        @endcode

        @par Specification
        @li <a href="https://datatracker.ietf.org/doc/html/rfc3986#section-3.2.2">3.2.2. Host (rfc3986)</a>

        @return The IPvFuture address as a string.
    */
    core::string_view
    host_ipvfuture() const noexcept;

    /** Return the host name

        If the host type is @ref host_type::name,
        this function returns the name as
        a string. Otherwise an empty string is returned.
        Any percent-escapes in the string are
        decoded first.

        @par Example
        @code
        assert( url_view( "https://www%2droot.example.com/" ).host_name() == "www-root.example.com" );
        @endcode

        @par Complexity
        Linear in `this->host_name().size()`.

        @par Exception Safety
        Calls to allocate may throw.

        @par BNF
        @code
        host        = IP-literal / IPv4address / reg-name

        IP-literal  = "[" ( IPv6address / IPvFuture  ) "]"

        reg-name    = *( unreserved / pct-encoded / "-" / ".")
        @endcode

        @par Specification
        @li <a href="https://datatracker.ietf.org/doc/html/rfc3986#section-3.2.2">3.2.2. Host (rfc3986)</a>

        @param token A string token customization.
        @return The host name as a string.
    */
    template<BOOST_URL_STRTOK_TPARAM>
    BOOST_URL_STRTOK_RETURN
    host_name(
        StringToken&& token = {}) const
    {
        encoding_opts opt;
        opt.space_as_plus = false;
        return encoded_host_name().decode(
            opt, std::forward<StringToken>(token));
    }

    /** Return the host name

        If the host type is @ref host_type::name,
        this function returns the name as
        a string.
        Otherwise, if the host type is not an
        name, it returns an empty string.
        The returned string may contain
        percent escapes.

        @par Example
        @code
        assert( url_view( "https://www%2droot.example.com/" ).encoded_host_name() == "www%2droot.example.com" );
        @endcode

        @par Complexity
        Constant.

        @par Exception Safety
        Throws nothing.

        @par BNF
        @code
        host        = IP-literal / IPv4address / reg-name

        IP-literal  = "[" ( IPv6address / IPvFuture  ) "]"

        reg-name    = *( unreserved / pct-encoded / "-" / ".")
        @endcode

        @par Specification
        @li <a href="https://datatracker.ietf.org/doc/html/rfc3986#section-3.2.2">3.2.2. Host (rfc3986)</a>

        @return The host name as a percent-encoded string.
    */
    pct_string_view
    encoded_host_name() const noexcept;

    /** Return the IPv6 Zone ID

        If the host type is @ref host_type::ipv6,
        this function returns the Zone ID as
        a string. Otherwise an empty string is returned.
        Any percent-escapes in the string are
        decoded first.

        @par Example
        @code
        assert( url_view( "http://[fe80::1%25eth0]/" ).zone_id() == "eth0" );
        @endcode

        @par Complexity
        Linear in `this->encoded_zone_id().size()`.

        @par Exception Safety
        Calls to allocate may throw.

        @par BNF
        @code
        host        = IP-literal / IPv4address / reg-name

        IP-literal = "[" ( IPv6address / IPv6addrz / IPvFuture  ) "]"

        ZoneID = 1*( unreserved / pct-encoded )

        IPv6addrz = IPv6address "%25" ZoneID
        @endcode

        @par Specification
        @li <a href="https://datatracker.ietf.org/doc/html/rfc6874">Representing IPv6 Zone Identifiers in Address Literals and Uniform Resource Identifiers</a>

        @param token A string token customization.
        @return The Zone ID as a string.
    */
    template<BOOST_URL_STRTOK_TPARAM>
    BOOST_URL_STRTOK_RETURN
    zone_id(
        StringToken&& token = {}) const
    {
        encoding_opts opt;
        opt.space_as_plus = false;
        return encoded_zone_id().decode(
            opt, std::forward<StringToken>(token));
    }

    /** Return the IPv6 Zone ID

        If the host type is @ref host_type::ipv6,
        this function returns the Zone ID as
        a string. Otherwise an empty string is returned.
        The returned string may contain
        percent escapes.

        @par Example
        @code
        assert( url_view( "http://[fe80::1%25eth0]/" ).encoded_zone_id() == "eth0" );
        @endcode

        @par Complexity
        Constant.

        @par Exception Safety
        Throws nothing.

        @par BNF
        @code
        host        = IP-literal / IPv4address / reg-name

        IP-literal = "[" ( IPv6address / IPv6addrz / IPvFuture  ) "]"

        ZoneID = 1*( unreserved / pct-encoded )

        IPv6addrz = IPv6address "%25" ZoneID
        @endcode

        @par Specification
        @li <a href="https://datatracker.ietf.org/doc/html/rfc6874">Representing IPv6 Zone Identifiers in Address Literals and Uniform Resource Identifiers</a>

        @return The Zone ID as a percent-encoded string.
    */
    pct_string_view
    encoded_zone_id() const noexcept;

    //--------------------------------------------
    //
    // Port
    //
    //--------------------------------------------

    /** Return true if a port is present

        This function returns true if an
        authority is present and contains a port.

        @par Example
        @code
        assert( url_view( "wss://www.example.com:443" ).has_port() );
        @endcode

        @par Complexity
        Constant.

        @par Exception Safety
        Throws nothing.

        @par BNF
        @code
        authority   = [ userinfo "@" ] host [ ":" port ]

        port        = *DIGIT
        @endcode

        @par Specification
        @li <a href="https://datatracker.ietf.org/doc/html/rfc3986#section-3.2.3">3.2.3. Port (rfc3986)</a>

        @see
            @ref encoded_host_and_port,
            @ref port,
            @ref port_number.

        @return `true` if a port is present, `false` otherwise.
    */
    bool
    has_port() const noexcept;

    /** Return the port

        If present, this function returns a
        string representing the port (which
        may be empty).
        Otherwise it returns an empty string.

        @par Example
        @code
        assert( url_view( "http://localhost.com:8080" ).port() == "8080" );
        @endcode

        @par Complexity
        Constant.

        @par Exception Safety
        Throws nothing.

        @par BNF
        @code
        port        = *DIGIT
        @endcode

        @par Specification
        @li <a href="https://datatracker.ietf.org/doc/html/rfc3986#section-3.2.3">3.2.3. Port (rfc3986)</a>

        @see
            @ref encoded_host_and_port,
            @ref has_port,
            @ref port_number.

        @return The port as a string.
    */
    core::string_view
    port() const noexcept;

    /** Return the port

        If a port is present and the numerical
        value is representable, it is returned
        as an unsigned integer. Otherwise, the
        number zero is returned.

        @par Example
        @code
        assert( url_view( "http://localhost.com:8080" ).port_number() == 8080 );
        @endcode

        @par Complexity
        Constant.

        @par Exception Safety
        Throws nothing.

        @par BNF
        @code
        port        = *DIGIT
        @endcode

        @par Specification
        @li <a href="https://datatracker.ietf.org/doc/html/rfc3986#section-3.2.3">3.2.3. Port (rfc3986)</a>

        @see
            @ref encoded_host_and_port,
            @ref has_port,
            @ref port.

        @return The port number as an unsigned integer.
    */
    std::uint16_t
    port_number() const noexcept;

    //--------------------------------------------
    //
    // Path
    //
    //--------------------------------------------

    /** Return true if the path is absolute

        This function returns true if the path
        begins with a forward slash ('/').

        @par Example
        @code
        assert( url_view( "/path/to/file.txt" ).is_path_absolute() );
        @endcode

        @par Complexity
        Constant.

        @par Exception Safety
        Throws nothing.

        @par BNF
        @code
        path          = path-abempty    ; begins with "/" or is empty
                      / path-absolute   ; begins with "/" but not "//"
                      / path-noscheme   ; begins with a non-colon segment
                      / path-rootless   ; begins with a segment
                      / path-empty      ; zero characters

        path-abempty  = *( "/" segment )
        path-absolute = "/" [ segment-nz *( "/" segment ) ]
        path-noscheme = segment-nz-nc *( "/" segment )
        path-rootless = segment-nz *( "/" segment )
        path-empty    = 0<pchar>
        @endcode

        @par Specification
        @li <a href="https://datatracker.ietf.org/doc/html/rfc3986#section-3.3">3.3.  Path (rfc3986)</a>

        @see
            @ref encoded_path,
            @ref encoded_segments.
            @ref path,
            @ref segments.

        @return `true` if the path is absolute, `false` otherwise.
    */
    bool
    is_path_absolute() const noexcept
    {
        return
            impl().len(id_path) > 0 &&
            impl().cs_[impl().offset(id_path)] == '/';
    }

    /** Return the path

        This function returns the path as a
        string. The path may be empty.
        Any percent-escapes in the string are
        decoded first.

        @par Example
        @code
        assert( url_view( "file:///Program%20Files/Games/config.ini" ).path() == "/Program Files/Games/config.ini" );
        @endcode

        @par Complexity
        Linear in `this->path().size()`.

        @par Exception Safety
        Calls to allocate may throw.

        @par BNF
        @code
        path          = path-abempty    ; begins with "/" or is empty
                      / path-absolute   ; begins with "/" but not "//"
                      / path-noscheme   ; begins with a non-colon segment
                      / path-rootless   ; begins with a segment
                      / path-empty      ; zero characters

        path-abempty  = *( "/" segment )
        path-absolute = "/" [ segment-nz *( "/" segment ) ]
        path-noscheme = segment-nz-nc *( "/" segment )
        path-rootless = segment-nz *( "/" segment )
        path-empty    = 0<pchar>
        @endcode

        @par Specification
        @li <a href="https://datatracker.ietf.org/doc/html/rfc3986#section-3.3">3.3. Path (rfc3986)</a>

        @see
            @ref is_path_absolute,
            @ref encoded_path,
            @ref encoded_segments.
            @ref segments.

        @param token A string token to use for the result.
        @return The path as a string.
    */
    template<BOOST_URL_STRTOK_TPARAM>
    BOOST_URL_STRTOK_RETURN
    path(
        StringToken&& token = {}) const
    {
        encoding_opts opt;
        opt.space_as_plus = false;
        return encoded_path().decode(
            opt, std::forward<StringToken>(token));
    }

    /** Return the path

        This function returns the path as a
        string. The path may be empty.
        Any percent-escapes in the string are
        decoded first.

        @par Example
        @code
        assert( url_view( "file:///Program%20Files/Games/config.ini" ).encoded_path() == "/Program%20Files/Games/config.ini" );
        @endcode

        @par Complexity
        Constant.

        @par Exception Safety
        Throws nothing.

        @par BNF
        @code
        path          = path-abempty    ; begins with "/" or is empty
                      / path-absolute   ; begins with "/" but not "//"
                      / path-noscheme   ; begins with a non-colon segment
                      / path-rootless   ; begins with a segment
                      / path-empty      ; zero characters

        path-abempty  = *( "/" segment )
        path-absolute = "/" [ segment-nz *( "/" segment ) ]
        path-noscheme = segment-nz-nc *( "/" segment )
        path-rootless = segment-nz *( "/" segment )
        path-empty    = 0<pchar>
        @endcode

        @par Specification
        @li <a href="https://datatracker.ietf.org/doc/html/rfc3986#section-3.3">3.3. Path (rfc3986)</a>

        @see
            @ref is_path_absolute,
            @ref encoded_segments.
            @ref path,
            @ref segments.

        @return The path as a string.
    */
    pct_string_view
    encoded_path() const noexcept;

    /** Return the path as a container of segments

        This function returns a bidirectional
        view of strings over the path.
        The returned view references the same
        underlying character buffer; ownership
        is not transferred.
        Any percent-escapes in strings returned
        when iterating the view are decoded first.

        @par Example
        @code
        segments_view sv = url_view( "/path/to/file.txt" ).segments();
        @endcode

        @par Complexity
        Constant.

        @par Exception Safety
        Throws nothing.

        @par BNF
        @code
        path          = [ "/" ] segment *( "/" segment )
        @endcode

        @par Specification
        @li <a href="https://datatracker.ietf.org/doc/html/rfc3986#section-3.3">3.3. Path (rfc3986)</a>

        @see
            @ref is_path_absolute,
            @ref encoded_path,
            @ref encoded_segments.
            @ref path,
            @ref segments_view.

        @return A bidirectional view of segments.
    */
    segments_view
    segments() const noexcept;

    /** Return the path as a container of segments

        This function returns a bidirectional
        view of strings over the path.
        The returned view references the same
        underlying character buffer; ownership
        is not transferred.
        Strings returned when iterating the
        range may contain percent escapes.

        @par Example
        @code
        segments_encoded_view sv = url_view( "/path/to/file.txt" ).encoded_segments();
        @endcode

        @par Complexity
        Constant.

        @par Exception Safety
        Throws nothing.

        @par BNF
        @code
        path          = path-abempty    ; begins with "/" or is empty
                      / path-absolute   ; begins with "/" but not "//"
                      / path-noscheme   ; begins with a non-colon segment
                      / path-rootless   ; begins with a segment
                      / path-empty      ; zero characters

        path-abempty  = *( "/" segment )
        path-absolute = "/" [ segment-nz *( "/" segment ) ]
        path-noscheme = segment-nz-nc *( "/" segment )
        path-rootless = segment-nz *( "/" segment )
        path-empty    = 0<pchar>
        @endcode

        @par Specification
        @li <a href="https://datatracker.ietf.org/doc/html/rfc3986#section-3.3">3.3. Path (rfc3986)</a>

        @see
            @ref is_path_absolute,
            @ref encoded_path,
            @ref path,
            @ref segments,
            @ref segments_encoded_view.

        @return A bidirectional view of encoded segments.
    */
    segments_encoded_view
    encoded_segments() const noexcept;

    //--------------------------------------------
    //
    // Query
    //
    //--------------------------------------------

    /** Return true if a query is present

        This function returns true if this
        contains a query. An empty query is
        distinct from having no query.

        @par Example
        @code
        assert( url_view( "/sql?id=42&col=name&page-size=20" ).has_query() );
        @endcode

        @par Complexity
        Constant.

        @par Exception Safety
        Throws nothing.

        @par BNF
        @code
        query           = *( pchar / "/" / "?" )

        query-param     = key [ "=" value ]
        query-params    = [ query-param ] *( "&" query-param )
        @endcode

        @par Specification
        @li <a href="https://datatracker.ietf.org/doc/html/rfc3986#section-3.4">3.4.  Query (rfc3986)</a>
        @li <a href="https://en.wikipedia.org/wiki/Query_string">Query string (Wikipedia)</a>

        @see
            @ref encoded_params,
            @ref encoded_query,
            @ref params,
            @ref query.

        @return `true` if a query is present.
    */
    bool
    has_query() const noexcept;

    /** Return the query

        If this contains a query, it is returned
        as a string (which may be empty).
        Otherwise, an empty string is returned.
        Any percent-escapes in the string are
        decoded first.
        <br>

        Literal plus signs remain unchanged by
        default to match RFC 3986. To treat '+'
        as a space, supply decoding options with
        `space_as_plus = true` when calling this
        function.

        @par Example
        @code
        assert( url_view( "/sql?id=42&name=jane%2Ddoe&page+size=20" ).query() == "id=42&name=jane-doe&page size=20" );
        @endcode

        @par Complexity
        Linear in `this->query().size()`.

        @par Exception Safety
        Calls to allocate may throw.

        @par BNF
        @code
        query           = *( pchar / "/" / "?" )

        query-param     = key [ "=" value ]
        query-params    = [ query-param ] *( "&" query-param )
        @endcode

        @par Specification
        @li <a href="https://datatracker.ietf.org/doc/html/rfc3986#section-3.4">3.4.  Query (rfc3986)</a>
        @li <a href="https://en.wikipedia.org/wiki/Query_string">Query string (Wikipedia)</a>

        @see
            @ref encoded_params,
            @ref encoded_query,
            @ref has_query,
            @ref params.

        @param token A token to use for the returned string.
        @return The query as a string.
    */
    template<BOOST_URL_STRTOK_TPARAM>
    BOOST_URL_STRTOK_RETURN
    query(
        StringToken&& token = {}) const
    {
        // When interacting with the query as
        // an intact string, we do not treat
        // the plus sign as an encoded space.
        encoding_opts opt;
        opt.space_as_plus = false;
        return encoded_query().decode(
            opt, std::forward<StringToken>(token));
    }

    /** Return the query

        If this contains a query, it is returned
        as a string (which may be empty).
        Otherwise, an empty string is returned.
        The returned string may contain
        percent escapes.

        @par Example
        @code
        assert( url_view( "/sql?id=42&name=jane%2Ddoe&page+size=20" ).encoded_query() == "id=42&name=jane%2Ddoe&page+size=20" );
        @endcode

        @par Complexity
        Constant.

        @par Exception Safety
        Throws nothing.

        @par BNF
        @code
        query           = *( pchar / "/" / "?" )

        query-param     = key [ "=" value ]
        query-params    = [ query-param ] *( "&" query-param )
        @endcode

        @par Specification
        @li <a href="https://datatracker.ietf.org/doc/html/rfc3986#section-3.4">3.4. Query (rfc3986)</a>
        @li <a href="https://en.wikipedia.org/wiki/Query_string">Query string (Wikipedia)</a>

        @see
            @ref encoded_params,
            @ref has_query,
            @ref params,
            @ref query.

        @return The query as a string.
    */
    pct_string_view
    encoded_query() const noexcept;

    /** Return the query as a container of parameters

        This function returns a bidirectional
        view of key/value pairs over the query.
        The returned view references the same
        underlying character buffer; ownership
        is not transferred.
        Any percent-escapes in strings returned
        when iterating the view are decoded first.

        @par Example
        @code
        params_view pv = url_view( "/sql?id=42&name=jane%2Ddoe&page+size=20" ).params();
        @endcode

        @par Complexity
        Constant.

        @par Exception Safety
        Throws nothing.

        @par BNF
        @code
        query           = *( pchar / "/" / "?" )

        query-param     = key [ "=" value ]
        query-params    = [ query-param ] *( "&" query-param )
        @endcode

        @par Specification
        @li <a href="https://datatracker.ietf.org/doc/html/rfc3986#section-3.4">3.4.  Query (rfc3986)</a>
        @li <a href="https://en.wikipedia.org/wiki/Query_string">Query string (Wikipedia)</a>

        @see
            @ref encoded_params,
            @ref encoded_query,
            @ref has_query,
            @ref query.

        @return A bidirectional view of key/value pairs.
    */
    params_view
    params() const noexcept;

    params_view
    params(encoding_opts opt) const noexcept;

    /** Return the query as a container of parameters

        This function returns a bidirectional
        view of key/value pairs over the query.
        The returned view references the same
        underlying character buffer; ownership
        is not transferred.
        Strings returned when iterating the
        range may contain percent escapes.

        @par Example
        @code
        params_encoded_view pv = url_view( "/sql?id=42&name=jane%2Ddoe&page+size=20" ).encoded_params();
        @endcode

        @par Complexity
        Constant.

        @par Exception Safety
        Throws nothing.

        @par BNF
        @code
        query           = *( pchar / "/" / "?" )
        query-param     = key [ "=" value ]
        query-params    = [ query-param ] *( "&" query-param )
        @endcode

        @par Specification
        @li <a href="https://datatracker.ietf.org/doc/html/rfc3986#section-3.4">3.4. Query (rfc3986)</a>
        @li <a href="https://en.wikipedia.org/wiki/Query_string">Query string (Wikipedia)</a>

        @see
            @ref encoded_query,
            @ref has_query,
            @ref params,
            @ref query.

        @return A bidirectional view of key/value pairs.
    */
    params_encoded_view
    encoded_params() const noexcept;

    //--------------------------------------------
    //
    // Fragment
    //
    //--------------------------------------------

    /** Return true if a fragment is present

        This function returns true if the url
        contains a fragment.
        An empty fragment is distinct from
        no fragment.

        @par Example
        @code
        assert( url_view( "http://www.example.com/index.htm#anchor" ).has_fragment() );
        @endcode

        @par Complexity
        Constant.

        @par Exception Safety
        Throws nothing.

        @par BNF
        @code
        URI           = scheme ":" hier-part [ "?" query ] [ "#" fragment ]

        relative-ref  = relative-part [ "?" query ] [ "#" fragment ]
        @endcode

        @par Specification
        @li <a href="https://datatracker.ietf.org/doc/html/rfc3986#section-3.5">3.5. Fragment (rfc3986)</a>

        @see
            @ref encoded_fragment,
            @ref fragment.

        @return `true` if the url contains a fragment.
    */
    bool
    has_fragment() const noexcept;

    /** Return the fragment

        This function calculates the fragment
        of the url, with percent escapes decoded
        and without the leading pound sign ('#')
        whose presence indicates that the url
        contains a fragment.

        <br>

        This function accepts an optional
        <em>StringToken</em> parameter which
        controls the return type and behavior
        of the function:

        @li When called with no arguments,
        the return type of the function is
        `std::string`. Otherwise

        @li When called with a string token,
        the behavior and return type of the
        function depends on the type of string
        token being passed.

        @par Example
        @code
        assert( url_view( "http://www.example.com/index.htm#a%2D1" ).fragment() == "a-1" );
        @endcode

        @par Complexity
        Linear in `this->fragment().size()`.

        @par Exception Safety
        Calls to allocate may throw.
        String tokens may throw exceptions.

        @param token An optional string token to
        use. If this parameter is omitted, the
        function returns a new `std::string`.

        @return The fragment portion of the url.

        @par BNF
        @code
        fragment        = *( pchar / "/" / "?" )

        fragment-part   = [ "#" fragment ]
        @endcode

        @par Specification
        @li <a href="https://datatracker.ietf.org/doc/html/rfc3986#section-3.5">3.5. Fragment (rfc3986)</a>

        @see
            @ref encoded_fragment,
            @ref has_fragment.

    */
    template<BOOST_URL_STRTOK_TPARAM>
    BOOST_URL_STRTOK_RETURN
    fragment(
        StringToken&& token = {}) const
    {
        encoding_opts opt;
        opt.space_as_plus = false;
        return encoded_fragment().decode(
            opt, std::forward<StringToken>(token));
    }

    /** Return the fragment

        This function returns the fragment as a
        string with percent-escapes.
        Ownership is not transferred; the
        string returned references the underlying
        character buffer, which must remain valid
        or else undefined behavior occurs.

        @par Example
        @code
        assert( url_view( "http://www.example.com/index.htm#a%2D1" ).encoded_fragment() == "a%2D1" );
        @endcode

        @par Complexity
        Constant.

        @par Exception Safety
        Throws nothing.

        @par BNF
        @code
        fragment        = *( pchar / "/" / "?" )

        pchar           = unreserved / pct-encoded / sub-delims / ":" / "@"
        @endcode

        @par Specification
        @li <a href="https://datatracker.ietf.org/doc/html/rfc3986#section-3.5">3.5. Fragment (rfc3986)</a>

        @see
            @ref fragment,
            @ref has_fragment.

        @return The fragment portion of the url.
    */
    pct_string_view
    encoded_fragment() const noexcept;

    //--------------------------------------------
    //
    // Compound Fields
    //
    //--------------------------------------------

    /** Return the host and port

        If an authority is present, this
        function returns the host and optional
        port as a string, which may be empty.
        Otherwise it returns an empty string.
        The returned string may contain
        percent escapes.

        @par Example
        @code
        assert( url_view( "http://www.example.com:8080/index.htm" ).encoded_host_and_port() == "www.example.com:8080" );
        @endcode

        @par Complexity
        Constant.

        @par Exception Safety
        Throws nothing.

        @par BNF
        @code
        authority   = [ userinfo "@" ] host [ ":" port ]
        @endcode

        @par Specification
        @li <a href="https://datatracker.ietf.org/doc/html/rfc3986#section-3.2.2">3.2.2.  Host (rfc3986)</a>
        @li <a href="https://datatracker.ietf.org/doc/html/rfc3986#section-3.2.3">3.2.3. Port (rfc3986)</a>

        @see
            @ref has_port,
            @ref port,
            @ref port_number.

        @return The host and port portion of the url.
    */
    pct_string_view
    encoded_host_and_port() const noexcept;

    /** Return the origin

        If an authority is present, this
        function returns the scheme and
        authority portion of the url.
        Otherwise, an empty string is
        returned.
        The returned string may contain
        percent escapes.

        @par Example
        @code
        assert( url_view( "http://www.example.com:8080/index.htm?text=none#h1" ).encoded_origin() == "http://www.example.com:8080" );
        @endcode

        @par Complexity
        Constant.

        @par Exception Safety
        Throws nothing.

        @see
            @ref encoded_resource,
            @ref encoded_target.

        @return The origin portion of the url.
    */
    pct_string_view
    encoded_origin() const noexcept;

    /** Return the resource

        This function returns the resource, which
        is the portion of the url that includes
        only the path, query, and fragment.
        The returned string may contain
        percent escapes.

        @par Example
        @code
        assert( url_view( "http://www.example.com/index.html?query#frag" ).encoded_resource() == "/index.html?query#frag" );
        @endcode

        @par Complexity
        Constant.

        @par Exception Safety
        Throws nothing.

        @par Specification
        @li <a href="https://datatracker.ietf.org/doc/html/rfc3986#section-3.3">3.3. Path (rfc3986)</a>
        @li <a href="https://datatracker.ietf.org/doc/html/rfc3986#section-3.4">3.4. Query (rfc3986)</a>

        @see
            @ref encoded_origin,
            @ref encoded_target.

        @return The resource portion of the url.
    */
    pct_string_view
    encoded_resource() const noexcept;

    /** Return the target

        This function returns the target, which
        is the portion of the url that includes
        only the path and query.
        The returned string may contain
        percent escapes.

        @par Example
        @code
        assert( url_view( "http://www.example.com/index.html?query#frag" ).encoded_target() == "/index.html?query" );
        @endcode

        @par Complexity
        Constant.

        @par Exception Safety
        Throws nothing.

        @par Specification
        @li <a href="https://datatracker.ietf.org/doc/html/rfc3986#section-3.3">3.3. Path (rfc3986)</a>
        @li <a href="https://datatracker.ietf.org/doc/html/rfc3986#section-3.4">3.4. Query (rfc3986)</a>

        @see
            @ref encoded_origin,
            @ref encoded_resource.

        @return The target portion of the url.
    */
    pct_string_view
    encoded_target() const noexcept;

    //--------------------------------------------
    //
    // Comparison
    //
    //--------------------------------------------

    /** Return the result of comparing this with another url

        This function compares two URLs
        according to Syntax-Based comparison
        algorithm.

        @par Complexity
        Linear in `min( u0.size(), u1.size() )`

        @par Exception Safety
        Throws nothing.

        @par Specification
        @li <a href="https://datatracker.ietf.org/doc/html/rfc3986#section-6.2.2">6.2.2 Syntax-Based Normalization (rfc3986)</a>

        @param other The url to compare
        @return -1 if `*this < other`, 0 if `this == other`, and 1 if `this > other`.
    */
    int
    compare(url_view_base const& other) const noexcept;

    /** Return the result of comparing two URLs

        The URLs are compared component by
        component as if they were first
        normalized.

        @par Example
        @code
        url_view u0( "http://www.a.com/index.htm" );
        url_view u1( "http://www.a.com/index.htm" );
        assert( u0 == u1 );
        @endcode

        @par Effects
        @code
        url a(u0);
        a.normalize();
        url b(u1);
        b.normalize();
        return a.buffer() == b.buffer();
        @endcode

        @par Complexity
        Linear in `min( u0.size(), u1.size() )`

        @par Exception Safety
        Throws nothing

        @param u0 The first url to compare
        @param u1 The second url to compare
        @return `true` if `u0 == u1`

        @par Specification
        @li <a href="https://datatracker.ietf.org/doc/html/rfc3986#section-6.2.2">6.2.2 Syntax-Based Normalization (rfc3986)</a>
    */
    friend
    bool
    operator==(
        url_view_base const& u0,
        url_view_base const& u1) noexcept
    {
        return u0.compare(u1) == 0;
    }

    /** Return the result of comparing two URLs

        The URLs are compared component by
        component as if they were first
        normalized.

        @par Example
        @code
        url_view u0( "http://www.a.com/index.htm" );
        url_view u1( "http://www.b.com/index.htm" );
        assert( u0 != u1 );
        @endcode

        @par Effects
        @code
        url a(u0);
        a.normalize();
        url b(u1);
        b.normalize();
        return a.buffer() != b.buffer();
        @endcode

        @par Complexity
        Linear in `min( u0.size(), u1.size() )`

        @par Exception Safety
        Throws nothing

        @param u0 The first url to compare
        @param u1 The second url to compare
        @return `true` if `u0 != u1`

        @par Specification
        @li <a href="https://datatracker.ietf.org/doc/html/rfc3986#section-6.2.2">6.2.2 Syntax-Based Normalization (rfc3986)</a>
    */
    friend
    bool
    operator!=(
        url_view_base const& u0,
        url_view_base const& u1) noexcept
    {
        return ! (u0 == u1);
    }

    /** Return the result of comparing two URLs

        The URLs are compared component by
        component as if they were first
        normalized.

        @par Example
        @code
        url_view u0( "http://www.a.com/index.htm" );
        url_view u1( "http://www.b.com/index.htm" );
        assert( u0 < u1 );
        @endcode

        @par Effects
        @code
        url a(u0);
        a.normalize();
        url b(u1);
        b.normalize();
        return a.buffer() < b.buffer();
        @endcode

        @par Complexity
        Linear in `min( u0.size(), u1.size() )`

        @par Exception Safety
        Throws nothing

        @param u0 The first url to compare
        @param u1 The second url to compare
        @return `true` if `u0 < u1`

        @par Specification
        @li <a href="https://datatracker.ietf.org/doc/html/rfc3986#section-6.2.2">6.2.2 Syntax-Based Normalization (rfc3986)</a>
    */
    friend
    bool
    operator<(
        url_view_base const& u0,
        url_view_base const& u1) noexcept
    {
        return u0.compare(u1) < 0;
    }

    /** Return the result of comparing two URLs

        The URLs are compared component by
        component as if they were first
        normalized.

        @par Example
        @code
        url_view u0( "http://www.b.com/index.htm" );
        url_view u1( "http://www.b.com/index.htm" );
        assert( u0 <= u1 );
        @endcode

        @par Effects
        @code
        url a(u0);
        a.normalize();
        url b(u1);
        b.normalize();
        return a.buffer() <= b.buffer();
        @endcode

        @par Complexity
        Linear in `min( u0.size(), u1.size() )`

        @par Exception Safety
        Throws nothing

        @param u0 The first url to compare
        @param u1 The second url to compare
        @return `true` if `u0 <= u1`

        @par Specification
        @li <a href="https://datatracker.ietf.org/doc/html/rfc3986#section-6.2.2">6.2.2 Syntax-Based Normalization (rfc3986)</a>
    */
    friend
    bool
    operator<=(
        url_view_base const& u0,
        url_view_base const& u1) noexcept
    {
        return u0.compare(u1) <= 0;
    }

    /** Return the result of comparing two URLs

        The URLs are compared component by
        component as if they were first
        normalized.

        @par Example
        @code
        url_view u0( "http://www.b.com/index.htm" );
        url_view u1( "http://www.a.com/index.htm" );
        assert( u0 > u1 );
        @endcode

        @par Effects
        @code
        url a(u0);
        a.normalize();
        url b(u1);
        b.normalize();
        return a.buffer() > b.buffer();
        @endcode

        @par Complexity
        Linear in `min( u0.size(), u1.size() )`

        @par Exception Safety
        Throws nothing

        @param u0 The first url to compare
        @param u1 The second url to compare
        @return `true` if `u0 > u1`

        @par Specification
        @li <a href="https://datatracker.ietf.org/doc/html/rfc3986#section-6.2.2">6.2.2 Syntax-Based Normalization (rfc3986)</a>
    */
    friend
    bool
    operator>(
        url_view_base const& u0,
        url_view_base const& u1) noexcept
    {
        return u0.compare(u1) > 0;
    }

    /** Return the result of comparing two URLs

        The URLs are compared component by
        component as if they were first
        normalized.

        @par Example
        @code
        url_view u0( "http://www.a.com/index.htm" );
        url_view u1( "http://www.a.com/index.htm" );
        assert( u0 >= u1 );
        @endcode

        @par Effects
        @code
        url a(u0);
        a.normalize();
        url b(u1);
        b.normalize();
        return a.buffer() >= b.buffer();
        @endcode

        @par Complexity
        Linear in `min( u0.size(), u1.size() )`

        @par Exception Safety
        Throws nothing

        @param u0 The first url to compare
        @param u1 The second url to compare
        @return `true` if `u0 >= u1`

        @par Specification
        @li <a href="https://datatracker.ietf.org/doc/html/rfc3986#section-6.2.2">6.2.2 Syntax-Based Normalization (rfc3986)</a>
    */
    friend
    bool
    operator>=(
        url_view_base const& u0,
        url_view_base const& u1) noexcept
    {
        return u0.compare(u1) >= 0;
    }

    /** Format the url to the output stream

        This function serializes the url to
        the specified output stream. Any
        percent-escapes are emitted as-is;
        no decoding is performed.

        @par Example
        @code
        url_view u( "http://www.example.com/index.htm" );
        std::stringstream ss;
        ss << u;
        assert( ss.str() == "http://www.example.com/index.htm" );
        @endcode

        @par Effects
        @code
        return os << u.buffer();
        @endcode

        @par Complexity
        Linear in `u.buffer().size()`

        @par Exception Safety
        Basic guarantee.

        @return A reference to the output stream, for chaining

        @param os The output stream to write to.

        @param u The url to write.
    */
    friend
    std::ostream&
    operator<<(
        std::ostream& os,
        url_view_base const& u)
    {
        return os << u.buffer();
    }

private:
    //--------------------------------------------
    //
    // implementation
    //
    //--------------------------------------------
    static
    int
    segments_compare(
        segments_encoded_view seg0,
        segments_encoded_view seg1) noexcept;
};

//------------------------------------------------

/** Format the url to the output stream

    This function serializes the url to
    the specified output stream. Any
    percent-escapes are emitted as-is;
    no decoding is performed.

    @par Example
    @code
    url_view u( "http://www.example.com/index.htm" );
    std::stringstream ss;
    ss << u;
    assert( ss.str() == "http://www.example.com/index.htm" );
    @endcode

    @par Effects
    @code
    return os << u.buffer();
    @endcode

    @par Complexity
    Linear in `u.buffer().size()`

    @par Exception Safety
    Basic guarantee.

    @return A reference to the output stream, for chaining

    @param os The output stream to write to.

    @param u The url to write.
*/
std::ostream&
operator<<(
    std::ostream& os,
    url_view_base const& u);

} // urls
} // boost

#include <boost/url/impl/url_view_base.hpp>

#endif
