// // 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_DECODE_VIEW_HPP #define BOOST_URL_DECODE_VIEW_HPP #include #include #include #include #include #include #include namespace boost { namespace urls { //------------------------------------------------ #ifndef BOOST_URL_DOCS class decode_view; namespace detail { // unchecked template decode_view make_decode_view( Args&&... args) noexcept; } // detail #endif //------------------------------------------------ /** A reference to a valid, percent-encoded string These views reference strings in parts of URLs or other components that are percent-encoded. The special characters (those not in the allowed character set) are stored as three character escapes that consist of a percent sign ('%%') followed by a two-digit hexadecimal number of the corresponding unescaped character code, which may be part of a UTF-8 code point depending on the context. The view refers to the original character buffer and only decodes escaped sequences when needed. In particular these operations perform percent-decoding automatically without the need to allocate memory: @li Iteration of the string @li Accessing the encoded character buffer @li Comparison to encoded or plain strings These objects can only be constructed from strings that have a valid percent-encoding, otherwise construction fails. The caller is responsible for ensuring that the lifetime of the character buffer from which the view is constructed extends unmodified until the view is no longer accessed. @par Operators The following operators are supported between @ref decode_view and any object that is convertible to `core::string_view` @code bool operator==( decode_view, decode_view ) noexcept; bool operator!=( decode_view, decode_view ) noexcept; bool operator<=( decode_view, decode_view ) noexcept; bool operator< ( decode_view, decode_view ) noexcept; bool operator> ( decode_view, decode_view ) noexcept; bool operator>=( decode_view, decode_view ) noexcept; @endcode */ class decode_view { char const* p_ = nullptr; std::size_t n_ = 0; std::size_t dn_ = 0; bool space_as_plus_ = true; #ifndef BOOST_URL_DOCS template friend decode_view detail::make_decode_view( Args&&... args) noexcept; #endif // unchecked BOOST_URL_DECL explicit decode_view( core::string_view s, std::size_t n, encoding_opts opt) noexcept; public: /** The value type */ using value_type = char; /** The reference type */ using reference = char; /// @copydoc reference using const_reference = char; /** The unsigned integer type */ using size_type = std::size_t; /** The signed integer type */ using difference_type = std::ptrdiff_t; /** An iterator of constant, decoded characters. This iterator is used to access the encoded string as a bidirectional range of characters with percent-decoding applied. Escape sequences are not decoded until the iterator is dereferenced. */ #ifdef BOOST_URL_DOCS using iterator = __see_below__; #else class iterator; #endif /// @copydoc iterator using const_iterator = iterator; //-------------------------------------------- // // Special Members // //-------------------------------------------- /** Constructor Default-constructed views represent empty strings. @par Example @code decode_view ds; @endcode @par Postconditions @code this->empty() == true @endcode @par Complexity Constant. @par Exception Safety Throws nothing. */ decode_view() noexcept = default; /** Constructor This constructs a view from the character buffer `s`, which must remain valid and unmodified until the view is no longer accessed. @par Example @code decode_view ds( "Program%20Files" ); @endcode @par Postconditions @code this->encoded() == s @endcode @par Complexity Linear in `s.size()`. @par Exception Safety Exceptions thrown on invalid input. @throw system_error The string contains an invalid percent encoding. @param s A percent-encoded string that has already been validated. @param opt The options for decoding. If this parameter is omitted, the default options are used. */ explicit decode_view( pct_string_view s, encoding_opts opt = {}) noexcept : decode_view( detail::to_sv(s), s.decoded_size(), opt) { } //-------------------------------------------- // // Observers // //-------------------------------------------- /** Return true if the string is empty @par Example @code assert( decode_view( "" ).empty() ); @endcode @par Complexity Constant. @par Exception Safety Throws nothing. */ bool empty() const noexcept { return n_ == 0; } /** Return the number of decoded characters @par Example @code assert( decode_view( "Program%20Files" ).size() == 13 ); @endcode @par Effects @code return std::distance( this->begin(), this->end() ); @endcode @par Complexity Constant. @par Exception Safety Throws nothing. */ size_type size() const noexcept { return dn_; } /** Return an iterator to the beginning @par Example @code auto it = this->begin(); @endcode @par Complexity Constant. @par Exception Safety Throws nothing. */ iterator begin() const noexcept; /** Return an iterator to the end @par Example @code auto it = this->end(); @endcode @par Complexity Constant. @par Exception Safety Throws nothing. */ iterator end() const noexcept; /** Return the first character @par Example @code assert( decode_view( "Program%20Files" ).front() == 'P' ); @endcode @par Preconditions @code not this->empty() @endcode @par Complexity Constant. @par Exception Safety Throws nothing. */ reference front() const noexcept; /** Return the last character @par Example @code assert( decode_view( "Program%20Files" ).back() == 's' ); @endcode @par Preconditions @code not this->empty() @endcode @par Complexity Constant. @par Exception Safety Throws nothing. */ reference back() const noexcept; /** Checks if the string begins with the given prefix @par Example @code assert( decode_view( "Program%20Files" ).starts_with("Program") ); @endcode @par Complexity Linear. @par Exception Safety Throws nothing. */ BOOST_URL_DECL bool starts_with( core::string_view s ) const noexcept; /** Checks if the string ends with the given prefix @par Example @code assert( decode_view( "Program%20Files" ).ends_with("Files") ); @endcode @par Complexity Linear. @par Exception Safety Throws nothing. */ BOOST_URL_DECL bool ends_with( core::string_view s ) const noexcept; /** Checks if the string begins with the given prefix @par Example @code assert( decode_view( "Program%20Files" ).starts_with('P') ); @endcode @par Complexity Constant. @par Exception Safety Throws nothing. */ BOOST_URL_DECL bool starts_with( char ch ) const noexcept; /** Checks if the string ends with the given prefix @par Example @code assert( decode_view( "Program%20Files" ).ends_with('s') ); @endcode @par Complexity Constant. @par Exception Safety Throws nothing. */ BOOST_URL_DECL bool ends_with( char ch ) const noexcept; /** Finds the first occurrence of character in this view @par Complexity Linear. @par Exception Safety Throws nothing. */ BOOST_URL_DECL const_iterator find( char ch ) const noexcept; /** Finds the first occurrence of character in this view @par Complexity Linear. @par Exception Safety Throws nothing. */ BOOST_URL_DECL const_iterator rfind( char ch ) const noexcept; /** Remove the first characters @par Example @code decode_view d( "Program%20Files" ); d.remove_prefix( 8 ); assert( d == "Files" ); @endcode @par Preconditions @code not this->empty() @endcode @par Complexity Linear. */ BOOST_URL_DECL void remove_prefix( size_type n ); /** Remove the last characters @par Example @code decode_view d( "Program%20Files" ); d.remove_prefix( 6 ); assert( d == "Program" ); @endcode @par Preconditions @code not this->empty() @endcode @par Complexity Linear. */ BOOST_URL_DECL void remove_suffix( size_type n ); /** Return the decoding options */ encoding_opts options() const noexcept { encoding_opts opt; opt.space_as_plus = space_as_plus_; return opt; } //-------------------------------------------- // // Comparison // //-------------------------------------------- /** Return the result of comparing to another string The length of the sequences to compare is the smaller of `size()` and `other.size()`. The function compares the two strings as if by calling `char_traits::compare(to_string().data(), v.data(), rlen)`. This means the comparison is performed with percent-decoding applied to the current string. @param other string to compare @return Negative value if this string is less than the other character sequence, zero if the both character sequences are equal, positive value if this string is greater than the other character sequence */ BOOST_URL_DECL int compare(core::string_view other) const noexcept; /** Return the result of comparing to another string The length of the sequences to compare is the smaller of `size()` and `other.size()`. The function compares the two strings as if by calling `char_traits::compare(to_string().data(), v.to_string().data(), rlen)`. This means the comparison is performed with percent-decoding applied to the current string. @param other string to compare @return Negative value if this string is less than the other character sequence, zero if the both character sequences are equal, positive value if this string is greater than the other character sequence */ BOOST_URL_DECL int compare(decode_view other) const noexcept; //-------------------------------------------- // relational operators #ifndef BOOST_URL_DOCS private: template using is_match = std::integral_constant::type, decode_view>::value || std::is_convertible::value) && ( std::is_same::type, decode_view>::value || std::is_convertible::value) && // not both are convertible to string view ( !std::is_convertible::value || !std::is_convertible::value)>; static int decode_compare(decode_view s0, decode_view s1) noexcept { return s0.compare(s1); } template static int decode_compare(decode_view s0, S const& s1) noexcept { return s0.compare(s1); } template static int decode_compare(S const& s0, decode_view s1) noexcept { return -s1.compare(s0); } public: template BOOST_CXX14_CONSTEXPR friend auto operator==( S0 const& s0, S1 const& s1) noexcept -> typename std::enable_if< is_match::value, bool>::type { return decode_compare(s0, s1) == 0; } template BOOST_CXX14_CONSTEXPR friend auto operator!=( S0 const& s0, S1 const& s1) noexcept -> typename std::enable_if< is_match::value, bool>::type { return decode_compare(s0, s1) != 0; } template BOOST_CXX14_CONSTEXPR friend auto operator<( S0 const& s0, S1 const& s1) noexcept -> typename std::enable_if< is_match::value, bool>::type { return decode_compare(s0, s1) < 0; } template BOOST_CXX14_CONSTEXPR friend auto operator<=( S0 const& s0, S1 const& s1) noexcept -> typename std::enable_if< is_match::value, bool>::type { return decode_compare(s0, s1) <= 0; } template BOOST_CXX14_CONSTEXPR friend auto operator>( S0 const& s0, S1 const& s1) noexcept -> typename std::enable_if< is_match::value, bool>::type { return decode_compare(s0, s1) > 0; } template BOOST_CXX14_CONSTEXPR friend auto operator>=( S0 const& s0, S1 const& s1) noexcept -> typename std::enable_if< is_match::value, bool>::type { return decode_compare(s0, s1) >= 0; } #endif // hidden friend friend std::ostream& operator<<( std::ostream& os, decode_view const& s) { // hidden friend s.write(os); return os; } private: BOOST_URL_DECL void write(std::ostream& os) const; }; /** Format the string with percent-decoding applied to the output stream This function serializes the decoded view to the output stream. @return A reference to the output stream, for chaining @param os The output stream to write to @param s The decoded view to write */ inline std::ostream& operator<<( std::ostream& os, decode_view const& s); //------------------------------------------------ inline decode_view pct_string_view::operator*() const noexcept { return decode_view(*this); } #ifndef BOOST_URL_DOCS namespace detail { template decode_view make_decode_view( Args&&... args) noexcept { return decode_view( std::forward(args)...); } } // detail #endif //------------------------------------------------ } // urls } // boost #include #endif