diff --git a/doc/qbk/0.main.qbk b/doc/qbk/0.main.qbk index 5426029a6..28db40901 100644 --- a/doc/qbk/0.main.qbk +++ b/doc/qbk/0.main.qbk @@ -50,9 +50,12 @@ [def __MoveConstructible__ [@https://en.cppreference.com/w/cpp/named_req/MoveConstructible ['MoveConstructible]]] [def __SemiRegular__ [@https://en.cppreference.com/w/cpp/concepts/semiregular ['SemiRegular]]] [def __Swappable__ [@https://en.cppreference.com/w/cpp/named_req/Swappable ['Swappable]]] -[def __CharSet__ [link url.grammar_rules.charset ['CharSet]]] +[def __CharSet__ [link url.concepts.charset ['CharSet]]] +[def __MutableString__ [link url.concepts.mutablestring ['MutableString]]] [def __std_swap__ [@https://en.cppreference.com/w/cpp/algorithm/swap `std::swap`]] +[def __std_string__ [@https://en.cppreference.com/w/cpp/string/basic_string `std::string`]] + [def __authority_view__ [link url.ref.boost__urls__authority_view `authority_view`]] [def __segments_view__ [link url.ref.boost__urls__segments_view `segments_view`]] [def __segments_encoded_view__ [link url.ref.boost__urls__segments_encoded_view `segments_encoded_view`]] @@ -63,6 +66,7 @@ [def __static_pool__ [link url.ref.boost__urls__static_pool `static_pool`]] [def __static_url__ [link url.ref.boost__urls__static_url `static_url`]] [def __const_string__ [link url.ref.boost__urls__const_string `const_string`]] +[def __pct_encoded_view__ [link url.ref.boost__urls__pct_encoded_view `pct_encoded_view`]] [def __string_view__ [link url.ref.boost__urls__string_view `string_view`]] [def __url__ [link url.ref.boost__urls__url `url`]] [def __url_view__ [link url.ref.boost__urls__url_view `url_view`]] @@ -97,7 +101,6 @@ [section Grammar Rules] [include 5.0.grammars.qbk] [include 5.1.customization.qbk] -[include 5.2.CharSet.qbk] [endsect] @@ -108,4 +111,11 @@ [block''''''] [endsect] +[section:concepts Concepts] +[include 6.1.CharSet.qbk] +[include 6.2.MutableString.qbk] +[endsect] + + + [xinclude index.xml] diff --git a/doc/qbk/5.2.CharSet.qbk b/doc/qbk/6.1.CharSet.qbk similarity index 100% rename from doc/qbk/5.2.CharSet.qbk rename to doc/qbk/6.1.CharSet.qbk diff --git a/doc/qbk/6.2.MutableString.qbk b/doc/qbk/6.2.MutableString.qbk new file mode 100644 index 000000000..9c586a6e0 --- /dev/null +++ b/doc/qbk/6.2.MutableString.qbk @@ -0,0 +1,86 @@ +[/ + 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/CPPAlliance/url +] + +[section MutableString] + +A ['MutableString] is a container that has the `assign` and +`append` member functions that accept iterators to `char`. + +The class __pct_encoded_view__ provides a operations +to assign and append percent-decoded values to a +['MutableString]. + +When storing decoded values in a container, these functions +allow the caller to recycle the input string without +reallocating memory. + +[heading Related Identifiers] + +* __pct_encoded_view__ +* [link url.ref.boost__urls__pct_encoded_rule `pct_encoded_rule`] + +[heading Requirements] + +In this table: + +* `T` is a type meeting the requirements of ['MutableString] +* `s` is a mutable container of type `T` +* `first`, `last` are __InputIterator__s whose `value_type` is `char` + and which refer to the valid character sequence `[ first, last )` + +[table Valid expressions +[[Expression] [Type] [Semantics, Pre/Post-conditions]] +[ + [ + ``` + T::value_type + ``` + ] + [`char`] + [ + Requires: `std::is_same_v< T::value_type, char >` + ] +][ + [ + ``` + s.assign(first,last) + ``` + ] + [] + [ + This member function assigns the characters from the + range in `[first, last)` to `s`. + ] +][ + [ + ``` + s.append(first,last) + ``` + ] + [] + [ + This member function appends the characters from the + range in `[first, last)` to `s`. + ] +]] + +[heading Exemplar] + +[snippet_mutable_string_exemplar] + +[heading Models] + +* __std_string__ + +[heading Models] + +* __pct_encoded_view__ +* [def __pct_encoded_view__ [link url.ref.boost__urls__pct_encoded_view `pct_encoded_view`]] + +[endsect] diff --git a/doc/qbk/quickref.xml b/doc/qbk/quickref.xml index bc9f4da74..ac0d3f75e 100644 --- a/doc/qbk/quickref.xml +++ b/doc/qbk/quickref.xml @@ -28,6 +28,7 @@ authority_view const_string const_string::factory + pct_encoded_view ipv4_address ipv6_address params @@ -106,7 +107,8 @@ Concepts - CharSet + CharSet + MutableString @@ -224,6 +226,7 @@ Type Traits + is_mutable_string is_charset is_range diff --git a/doc/xsl/custom-overrides.xsl b/doc/xsl/custom-overrides.xsl index e0bb488e1..878f978c4 100644 --- a/doc/xsl/custom-overrides.xsl +++ b/doc/xsl/custom-overrides.xsl @@ -25,7 +25,8 @@ diff --git a/include/boost/url/grammar/is_mutable_string.hpp b/include/boost/url/grammar/is_mutable_string.hpp new file mode 100644 index 000000000..814cd0a94 --- /dev/null +++ b/include/boost/url/grammar/is_mutable_string.hpp @@ -0,0 +1,78 @@ +// +// Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot 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/CPPAlliance/url +// + +#ifndef BOOST_URL_IS_MUTABLE_STRING_HPP +#define BOOST_URL_IS_MUTABLE_STRING_HPP + +#include + +namespace boost { +namespace urls { +namespace grammar { + +/** Alias for `std::true_type` if `T` satisfies __MutableString__ + for the iterator type `I` +*/ +#ifdef BOOST_URL_DOCS +template +using is_mutable_string = __see_below__; +#else +namespace detail +{ +template +struct has_value_type : std::false_type {}; + +template +struct has_value_type> + : std::true_type +{}; + +template +struct has_assign_and_append : std::false_type {}; + +template +struct has_assign_and_append() + .append( + std::declval(), + std::declval()) + ), + // T::assign(I, I) + decltype( + std::declval() + .assign( + std::declval(), + std::declval()) + )>> + : std::true_type +{}; +} + +template +struct is_mutable_string : std::false_type {}; + +template +struct is_mutable_string< + T, I, + typename std::enable_if< + detail::has_value_type::value && + detail::has_assign_and_append::value + >::type> + : std::is_same +{}; +#endif + +} // grammar +} // urls +} // boost + +#endif diff --git a/include/boost/url/impl/pct_encoded_view.hpp b/include/boost/url/impl/pct_encoded_view.hpp new file mode 100644 index 000000000..9afa264bc --- /dev/null +++ b/include/boost/url/impl/pct_encoded_view.hpp @@ -0,0 +1,202 @@ +// +// 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/CPPAlliance/url +// + +#ifndef BOOST_URL_IMPL_PCT_ENCODED_VIEW_HPP +#define BOOST_URL_IMPL_PCT_ENCODED_VIEW_HPP + +#include + +namespace boost { +namespace urls { + +class pct_encoded_view::iterator +{ + char const* begin_{nullptr}; + char const* pos_{nullptr}; + bool plus_to_space_{true}; + + friend pct_encoded_view; + + iterator( + char const* str, + bool plus_to_space) noexcept + : begin_(str) + , pos_(str) + , plus_to_space_(plus_to_space) + {} + + // end ctor + iterator( + char const* str, + size_type n, + bool plus_to_space) noexcept + : begin_(str) + , pos_(str + n) + , plus_to_space_(plus_to_space) + {} + +public: + using value_type = char; + using reference = char; + using pointer = void const*; + using const_reference = char; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using iterator_category = + std::bidirectional_iterator_tag; + + iterator() = default; + + iterator(iterator const&) = default; + + iterator& + operator=(iterator const&) = default; + + BOOST_URL_DECL + reference + operator*() const noexcept; + + iterator& + operator++() noexcept + { + BOOST_ASSERT(pos_ != nullptr); + if (*pos_ != '%') + ++pos_; + else + pos_ += 3; + return *this; + } + + iterator& + operator--() noexcept + { + BOOST_ASSERT(pos_ != begin_); + if (pos_ - begin_ < 3 || + pos_[-3] != '%') + --pos_; + else + pos_ -= 3; + return *this; + } + + iterator + operator++(int) noexcept + { + auto tmp = *this; + ++*this; + return tmp; + } + + iterator + operator--(int) noexcept + { + auto tmp = *this; + --*this; + return tmp; + } + + bool + operator==( + iterator const& other) const noexcept + { + return pos_ == other.pos_; + } + + bool + operator!=( + iterator const& other) const noexcept + { + return !(*this == other); + } +}; + +inline +auto +pct_encoded_view:: +begin() const noexcept -> + const_iterator +{ + return {p_, plus_to_space_}; +} + +inline +auto +pct_encoded_view:: +end() const noexcept -> + const_iterator +{ + return {p_, n_, plus_to_space_}; +} + +inline +auto +pct_encoded_view:: +front() const noexcept -> + const_reference +{ + BOOST_ASSERT( !empty() ); + return *begin(); +} + +inline +auto +pct_encoded_view:: +back() const noexcept -> + const_reference +{ + BOOST_ASSERT( !empty() ); + return *--end(); +} + +template +MutableString& +pct_encoded_view:: +assign_to( + MutableString& s) const +{ + // If you get a compiler error here, it means that + // your String type is missing the necessary append + // or assign member functions, or that your + // value_type is not char! + // + // This function should only be used + // if the container `MutableString` has the + // member function `assign(iterator, iterator)`. + BOOST_STATIC_ASSERT( + grammar::is_mutable_string< + MutableString, iterator>::value); + s.assign(begin(), end()); + return s; +} + +template +MutableString& +pct_encoded_view:: +append_to( + MutableString& s) const +{ + // If you get a compiler error here, it means that + // your String type is missing the necessary append + // or assign member functions, or that your + // value_type is not char! + // + // This function should only be used + // if the container `MutableString` has the + // member function `append(iterator, iterator)`. + BOOST_STATIC_ASSERT( + grammar::is_mutable_string< + MutableString, iterator>::value); + s.append(begin(), end()); + return s; +} + +} // urls +} // boost + +#endif diff --git a/include/boost/url/impl/pct_encoded_view.ipp b/include/boost/url/impl/pct_encoded_view.ipp new file mode 100644 index 000000000..71c6558b5 --- /dev/null +++ b/include/boost/url/impl/pct_encoded_view.ipp @@ -0,0 +1,136 @@ +// +// 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/CPPAlliance/url +// + +#ifndef BOOST_URL_IMPL_PCT_ENCODED_VIEW_IPP +#define BOOST_URL_IMPL_PCT_ENCODED_VIEW_IPP + +#include +#include + +namespace boost { +namespace urls { + +auto +pct_encoded_view::iterator:: +operator*() const noexcept -> + reference +{ + if (plus_to_space_ && + *pos_ == '+') + return ' '; + if (*pos_ != '%') + return *pos_; + char d0; + grammar::hexdig_value(pos_[1], d0); + char d1; + grammar::hexdig_value(pos_[2], d1); + return static_cast( + ((static_cast< + unsigned char>(d0) << 4) + + (static_cast< + unsigned char>(d1)))); +} + +pct_encoded_view:: +pct_encoded_view( + string_view str, + pct_decode_opts opt) noexcept + : p_(str.data()) + , n_(str.size()) + , plus_to_space_(opt.plus_to_space) +{ + error_code ec; + opt.non_normal_is_error = false; + dn_ = validate_pct_encoding( + str, ec, opt, + [](unsigned char c) { return c != '%'; }); + if (ec.failed()) + detail::throw_invalid_argument( + BOOST_CURRENT_LOCATION); +} + +auto +pct_encoded_view:: +copy( + char* dest, + size_type count, + size_type pos) const -> + size_type +{ + if( pos > size() ) + detail::throw_invalid_argument( + BOOST_CURRENT_LOCATION); + std::size_t rlen = (std::min)(count, size() - pos); + auto first = std::next(begin(), pos); + auto last = std::next(first, rlen); + while (first != last) + *dest++ = *first++; + return rlen; +} + +namespace detail +{ +template +int +decoded_strcmp(pct_encoded_view s0, T s1) +{ + std::size_t rlen = + (std::min)(s0.size(), s1.size()); + auto it0 = s0.begin(); + auto it1 = s1.begin(); + char c0; + char c1; + std::size_t i = 0; + for (; i < rlen; ++i) + { + c0 = *it0; + c1 = *it1; + if (c0 != c1) + break; + ++it0; + ++it1; + } + if (i != rlen) + return 1 - (c0 == c1) - + 2 * (static_cast(c0) + < static_cast(c1)); + return 1 - (s0.size() == s1.size()) - + 2 * (s0.size() < s1.size()); +} +} + +int +pct_encoded_view:: +compare(string_view other) const noexcept +{ + return detail::decoded_strcmp(*this, other); +} + +int +pct_encoded_view:: +compare(pct_encoded_view other) const noexcept +{ + return detail::decoded_strcmp(*this, other); +} + +std::ostream& +operator<<( + std::ostream& os, + pct_encoded_view const& str) { + auto it = str.begin(); + auto end = str.end(); + for (; it != end; ++it) + os.put(*it); + return os; +} + +} // urls +} // boost + +#endif diff --git a/include/boost/url/pct_encoded_view.hpp b/include/boost/url/pct_encoded_view.hpp new file mode 100644 index 000000000..f6a83bd45 --- /dev/null +++ b/include/boost/url/pct_encoded_view.hpp @@ -0,0 +1,715 @@ +// +// Copyright (c) 2022 Alan 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/CPPAlliance/url +// + +#ifndef BOOST_URL_PCT_ENCODED_VIEW_HPP +#define BOOST_URL_PCT_ENCODED_VIEW_HPP + +#include +#include +#include +#include +#include +#include + +namespace boost { +namespace urls { + +/** A view of the percent decoded characters in a string + + Objects of type pct_encoded_view reference URL + or other component strings that are + percent-encoded. That is, the characters + which are special (not in the allowed + character set) are represented with escapes + that start with a percent followed by a + two-digit hexadecimal number of the + corresponding character code + (which may be part of a UTF-8 code + point depending on the context). + + The primary operations are: + + @li Comparison to other percent-encoded or plain strings + @li Appending the decoded characters to an existing container + @li Assigning the decoded characters to an existing container + @li Iterating over the represented decoded character range + @li Accessing the underlying percent-encoded character buffer + + These objects can only be constructed from + strings that have a valid percent-encoding, + otherwise construction fails. + + The pct_encoded_view iterators operate lazily on + underlying sequence transforming groups + of percent-encoded characters into single + decoded elements. + + The caller must opt-in to perform expensive, + possibly-allocating decoding operations which + produce a contiguous character buffer by + calling @ref append_to, @ref assign_to, or + @ref to_string. + + Any operation on the underlying percent-encoded + strings invalidates the view iterators. The view + should be recreated after any operation that + may mutate the underlying range. + +*/ +class pct_encoded_view +{ + char const* p_ = nullptr; + std::size_t n_ = 0; + std::size_t dn_ = 0; + bool plus_to_space_ = true; + + using traits_type = std::char_traits; + +public: + /** Type of a decoded character + */ + using value_type = char; + + /// @copydoc value_type + using reference = char; + + /// @copydoc value_type + using const_reference = char; + + /** The unsigned integer type used to represent size. + */ + using size_type = std::size_t; + + /** The signed integer type used to represent differences. + */ + using difference_type = std::ptrdiff_t; + + /** A read-only bidirectional iterator to the decoded range. + + This is a read-only bidirectional iterator to + the decoded characters. + + Any operation on the underlying percent-encoded + strings invalidates the view iterators. The view + should be recreated after any operation that + may mutate the underlying range. + + */ +#ifdef BOOST_URL_DOCS + using iterator = __see_below__; +#else + class iterator; +#endif + + /// @copydoc iterator + using const_iterator = iterator; + + /** Constructor + + Default-constructed objects represent + the empty string. + + */ + explicit + pct_encoded_view( + pct_decode_opts const& opt = {}) noexcept + : plus_to_space_(opt.plus_to_space) + {} + + /** Constructor + */ + BOOST_URL_DECL + explicit + pct_encoded_view( + string_view str, + pct_decode_opts opt = {}) noexcept; + + /** Returns an iterator to the beginning + */ + const_iterator + begin() const noexcept; + + /** Returns an iterator to the end + */ + const_iterator + end() const noexcept; + + /** Return the first decoded character + + @par Preconditions + + `empty() != true`. + + @return the first character + */ + const_reference + front() const noexcept; + + /** Return the last decoded character + + @par Preconditions + + `empty() != true`. + + @return the last character + */ + const_reference + back() const noexcept; + + /** Returns the encoded string view + + This function returns a string_view of + the underlying encoded string. + + @return The encoded string view. + */ + string_view + encoded() const noexcept + { + return {p_, n_}; + } + + /** Returns the number of char elements in the string + */ + size_type + size() const noexcept + { + return dn_; + } + + /** Return true the string is empty + */ + bool + empty() const noexcept + { + return n_ == 0; + } + + /** Copy a decoded substring to another character string + + This function copies a substring to the + character array pointed to by the + destination char string, where `rcount` + is the smaller of `count` and + `size() - pos`. + + @par Exception Safety + Strong guarantee. + Exceptions thrown on invalid positions. + + @par Preconditions + + @code + pos > size() + @endcode + + @throw std::out_of_range `pos > size()` + + @param dest pointer to the destination character string + @param count requested substring length + @param pos position of the first character + + @return Number of characters copied + */ + BOOST_URL_DECL + size_type + copy(char* dest, + size_type count, + size_type pos = 0) const; + + /** 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(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(pct_encoded_view other) const noexcept; + + /** Returns a string with the decoded data + */ + std::string + to_string() const + { + std::string r; + assign_to(r); + return r; + } + + /** Assigns the decoded characters to a container + + This function allows the caller to recycle + the input string. + + @return A string representing the + entire contents of the decoded range. + */ + template + MutableString& + assign_to(MutableString& s) const; + + /** Appends the decoded characters to a container + + This function allows the caller to recycle + the input string. + + @return A string representing the + entire contents of the decoded range. + */ + template + MutableString& + append_to(MutableString& s) const; + + /** Return true if two strings are equal + */ + friend + bool + operator==( + pct_encoded_view s0, + pct_encoded_view s1) noexcept + { + return s0.compare(s1) == 0; + } + + /** Return true if two strings are not equal + */ + friend + bool + operator!=( + pct_encoded_view s0, + pct_encoded_view s1) noexcept + { + return s0.compare(s1) != 0; + } + + /** Return true if s0 is less than s1 + */ + friend + bool + operator<( + pct_encoded_view s0, + pct_encoded_view s1) noexcept + { + return s0.compare(s1) < 0; + } + + /** Return true if s0 is less or equal to s1 + */ + friend + bool + operator<=( + pct_encoded_view s0, + pct_encoded_view s1) noexcept + { + return s0.compare(s1) <= 0; + } + + /** Return true if s0 is greater than s1 + */ + friend + bool + operator>( + pct_encoded_view s0, + pct_encoded_view s1) noexcept + { + return s0.compare(s1) > 0; + } + + /** Return true if s0 is greater or equal to s1 + */ + friend + bool + operator>=( + pct_encoded_view s0, + pct_encoded_view s1) noexcept + { + return s0.compare(s1) >= 0; + } + + /** Return true if two strings are equal + + @par Constraints + + @code + std::is_convertible< String, string_view > + @endcode + + */ + template + friend +#ifndef BOOST_URL_DOCS + typename std::enable_if< + std::is_convertible::value && + !std::is_same::type, pct_encoded_view>::value, + bool + >::type +#else + bool +#endif + operator==( + pct_encoded_view s0, + String const& s1) noexcept + { + return s0.compare(string_view(s1)) == 0; + } + + /** Return true if two strings are not equal + + @par Constraints + + @code + std::is_convertible< String, string_view > + @endcode + + */ + template + friend +#ifndef BOOST_URL_DOCS + typename std::enable_if< + std::is_convertible::value && + !std::is_same::type, pct_encoded_view>::value, + bool + >::type +#else + bool +#endif + operator!=( + pct_encoded_view s0, + String const& s1) noexcept + { + return s0.compare(string_view(s1)) != 0; + } + + /** Return true if s0 is less than s1 + + @par Constraints + + @code + std::is_convertible< String, string_view > + @endcode + + */ + template + friend +#ifndef BOOST_URL_DOCS + typename std::enable_if< + std::is_convertible::value && + !std::is_same::type, pct_encoded_view>::value, + bool + >::type +#else + bool +#endif + operator<( + pct_encoded_view s0, + String const& s1) noexcept + { + return s0.compare(string_view(s1)) < 0; + } + + /** Return true if s0 is less or equal to s1 + + @par Constraints + + @code + std::is_convertible< String, string_view > + @endcode + + */ + template + friend +#ifndef BOOST_URL_DOCS + typename std::enable_if< + std::is_convertible::value && + !std::is_same::type, pct_encoded_view>::value, + bool + >::type +#else + bool +#endif + operator<=( + pct_encoded_view s0, + String const& s1) noexcept + { + return s0.compare(string_view(s1)) <= 0; + } + + /** Return true if s0 is greater than s1 + + @par Constraints + + @code + std::is_convertible< String, string_view > + @endcode + + */ + template + friend +#ifndef BOOST_URL_DOCS + typename std::enable_if< + std::is_convertible::value && + !std::is_same::type, pct_encoded_view>::value, + bool + >::type +#else + bool +#endif + operator>( + pct_encoded_view s0, + String const& s1) noexcept + { + return s0.compare(string_view(s1)) > 0; + } + + /** Return true if s0 is greater or equal to s1 + + @par Constraints + + @code + std::is_convertible< String, string_view > + @endcode + + */ + template + friend +#ifndef BOOST_URL_DOCS + typename std::enable_if< + std::is_convertible::value && + !std::is_same::type, pct_encoded_view>::value, + bool + >::type +#else + bool +#endif + operator>=( + pct_encoded_view s0, + String const& s1) noexcept + { + return s0.compare(string_view(s1)) >= 0; + } + + /** Return true if two strings are equal + + @par Constraints + + @code + std::is_convertible< String, string_view > + @endcode + + */ + template + friend +#ifndef BOOST_URL_DOCS + typename std::enable_if< + std::is_convertible::value && + !std::is_same::type, pct_encoded_view>::value, + bool + >::type +#else + bool +#endif + operator==( + String const& s0, + pct_encoded_view s1) noexcept + { + return s1.compare(string_view(s0)) == 0; + } + + /** Return true if two strings are not equal + + @par Constraints + + @code + std::is_convertible< String, string_view > + @endcode + + */ + template + friend +#ifndef BOOST_URL_DOCS + typename std::enable_if< + std::is_convertible::value && + !std::is_same::type, pct_encoded_view>::value, + bool + >::type +#else + bool +#endif + operator!=( + String const& s0, + pct_encoded_view s1) noexcept + { + return s1.compare(string_view(s0)) != 0; + } + + /** Return true if s0 is less than s1 + + @par Constraints + + @code + std::is_convertible< String, string_view > + @endcode + + */ + template + friend +#ifndef BOOST_URL_DOCS + typename std::enable_if< + std::is_convertible::value && + !std::is_same::type, pct_encoded_view>::value, + bool + >::type +#else + bool +#endif + operator<( + String const& s0, + pct_encoded_view s1) noexcept + { + return s1.compare(string_view(s0)) > 0; + } + + /** Return true if s0 is less or equal to s1 + + @par Constraints + + @code + std::is_convertible< String, string_view > + @endcode + + */ + template + friend +#ifndef BOOST_URL_DOCS + typename std::enable_if< + std::is_convertible::value && + !std::is_same::type, pct_encoded_view>::value, + bool + >::type +#else + bool +#endif + operator<=( + String const& s0, + pct_encoded_view s1) noexcept + { + return s1.compare(string_view(s0)) >= 0; + } + + /** Return true if s0 is greater than s1 + + @par Constraints + + @code + std::is_convertible< String, string_view > + @endcode + + */ + template + friend +#ifndef BOOST_URL_DOCS + typename std::enable_if< + std::is_convertible::value && + !std::is_same::type, pct_encoded_view>::value, + bool + >::type +#else + bool +#endif + operator>( + String const& s0, + pct_encoded_view s1) noexcept + { + return s1.compare(string_view(s0)) < 0; + } + + /** Return true if s0 is greater or equal to s1 + + @par Constraints + + @code + std::is_convertible< String, string_view > + @endcode + + */ + template + friend +#ifndef BOOST_URL_DOCS + typename std::enable_if< + std::is_convertible::value && + !std::is_same::type, pct_encoded_view>::value, + bool + >::type +#else + bool +#endif + operator>=( + String const& s0, + pct_encoded_view s1) noexcept + { + return s1.compare(string_view(s0)) <= 0; + } + + /** Format the decoded view 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 + */ + friend BOOST_URL_DECL + std::ostream& + operator<<( + std::ostream& os, + pct_encoded_view const& s); +}; + +} // urls +} // boost + +#include + +#endif diff --git a/include/boost/url/src.hpp b/include/boost/url/src.hpp index 84877eb2b..954009fb0 100644 --- a/include/boost/url/src.hpp +++ b/include/boost/url/src.hpp @@ -49,6 +49,7 @@ in a translation unit of the program. #include #include #include +#include #include #include #include diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt index 7be6e622f..7b5248362 100644 --- a/test/unit/CMakeLists.txt +++ b/test/unit/CMakeLists.txt @@ -18,6 +18,7 @@ set(BOOST_URL_TESTS_FILES Jamfile test_rule.hpp authority_view.cpp + const_string.cpp error.cpp error_code.cpp grammar.cpp @@ -28,6 +29,7 @@ set(BOOST_URL_TESTS_FILES params_encoded.cpp params_encoded_view.cpp params_view.cpp + pct_encoded_view.cpp pct_encoding.cpp pct_encoding_types.cpp query_param.cpp @@ -39,7 +41,6 @@ set(BOOST_URL_TESTS_FILES snippets.cpp static_pool.cpp static_url.cpp - const_string.cpp string_view.cpp url.cpp url_view.cpp diff --git a/test/unit/Jamfile b/test/unit/Jamfile index 6dea12c28..7f9dd1f29 100644 --- a/test/unit/Jamfile +++ b/test/unit/Jamfile @@ -22,6 +22,7 @@ project local SOURCES = ../../extra/test_main.cpp authority_view.cpp + const_string.cpp error.cpp error_code.cpp grammar.cpp @@ -32,6 +33,7 @@ local SOURCES = params_encoded.cpp params_encoded_view.cpp params_view.cpp + pct_encoded_view.cpp pct_encoding.cpp pct_encoding_types.cpp query_param.cpp @@ -43,7 +45,6 @@ local SOURCES = snippets.cpp static_pool.cpp static_url.cpp - const_string.cpp string_view.cpp url.cpp url_view.cpp diff --git a/test/unit/pct_encoded_view.cpp b/test/unit/pct_encoded_view.cpp new file mode 100644 index 000000000..7a96e874b --- /dev/null +++ b/test/unit/pct_encoded_view.cpp @@ -0,0 +1,477 @@ +// +// 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/CPPAlliance/url +// + +// Test that header file is self-contained. +#include +#include + +#include + +#include "test_suite.hpp" + +namespace boost { +namespace urls { + +struct pct_encoded_view_test +{ + string_view str = "a%20uri+test"; + string_view dec_str = "a uri test"; + string_view no_plus_dec_str = "a uri+test"; + const std::size_t dn = 10; + pct_decode_opts no_plus_opt; + + pct_encoded_view_test() + { + no_plus_opt.plus_to_space = false; + } + + void + testDecodedView() + { + // pct_encoded_view() + { + pct_encoded_view s; + BOOST_TEST_EQ(s, ""); + BOOST_TEST_EQ(s.size(), 0u); + BOOST_TEST_EQ(s.encoded().size(), 0u); + } + + // pct_encoded_view(bool plus_to_space) + { + pct_encoded_view s(no_plus_opt); + BOOST_TEST_EQ(s, ""); + BOOST_TEST_EQ(s.size(), 0u); + BOOST_TEST_EQ(s.encoded().size(), 0u); + } + + // pct_encoded_view(char const*) + { + pct_encoded_view s(str.data()); + BOOST_TEST_EQ(s, dec_str); + BOOST_TEST_EQ(s.size(), dn); + BOOST_TEST_EQ(s.encoded().size(), str.size()); + } + + // pct_encoded_view(char const*, bool plus_to_space) + { + pct_encoded_view + s(str.data(), no_plus_opt); + BOOST_TEST_EQ(s, no_plus_dec_str); + BOOST_TEST_EQ(s.size(), dn); + BOOST_TEST_EQ(s.encoded().size(), str.size()); + } + + // pct_encoded_view(string_view) + { + pct_encoded_view s(str); + BOOST_TEST_EQ(s, dec_str); + BOOST_TEST_EQ(s.size(), dn); + BOOST_TEST_EQ(s.encoded().size(), str.size()); + } + + // pct_encoded_view(string_view, bool plus_to_space) + { + pct_encoded_view s(str, no_plus_opt); + BOOST_TEST_EQ(s, no_plus_dec_str); + BOOST_TEST_EQ(s.size(), dn); + BOOST_TEST_EQ(s.encoded().size(), str.size()); + } + +#if !defined(BOOST_NO_CXX17_HDR_STRING_VIEW) + // pct_encoded_view(string_view) + { + std::string_view std_str = str; + pct_encoded_view s(std_str); + BOOST_TEST_EQ(s, dec_str); + BOOST_TEST_EQ(s.size(), dn); + BOOST_TEST_EQ(s.encoded().size(), str.size()); + } + + // pct_encoded_view(string_view, bool plus_to_space) + { + std::string_view std_str = str; + pct_encoded_view s(std_str, no_plus_opt); + BOOST_TEST_EQ(s, no_plus_dec_str); + BOOST_TEST_EQ(s.size(), dn); + BOOST_TEST_EQ(s.encoded().size(), str.size()); + } +#endif + + // pct_encoded_view(string_view) + { + std::string ss(str); + pct_encoded_view s(ss); + BOOST_TEST_EQ(s, dec_str); + BOOST_TEST_EQ(s.size(), dn); + BOOST_TEST_EQ(s.encoded().size(), str.size()); + } + + // pct_encoded_view(string_view, bool plus_to_space) + { + std::string ss(str); + pct_encoded_view s(ss, no_plus_opt); + BOOST_TEST_EQ(s, no_plus_dec_str); + BOOST_TEST_EQ(s.size(), dn); + BOOST_TEST_EQ(s.encoded().size(), str.size()); + } + } + + void + testIter() + { + // begin() + { + pct_encoded_view s(str); + BOOST_TEST_EQ(*s.begin(), s.front()); + BOOST_TEST_NE(s.begin(), + pct_encoded_view::iterator{}); + } + + // end() + { + pct_encoded_view s(str); + auto l = s.end(); + --l; + BOOST_TEST_EQ(*l, s.back()); + BOOST_TEST_NE(l, + pct_encoded_view::iterator{}); + } + } + + void + testAccessors() + { + // front() + { + pct_encoded_view s(str); + BOOST_TEST_EQ(s.front(), 'a'); + } + + // back() + { + pct_encoded_view s(str); + BOOST_TEST_EQ(s.back(), 't'); + } + + // encoded().data() + { + pct_encoded_view s(str); + BOOST_TEST_EQ(s.encoded().data(), str.data()); + } + } + + void + testObservers() + { + // size() + { + pct_encoded_view s(str); + BOOST_TEST_EQ(s.size(), dn); + } + + // encoded().size() + { + pct_encoded_view s(str); + BOOST_TEST_EQ(s.encoded().size(), str.size()); + } + + // encoded().length() + { + pct_encoded_view s(str); + BOOST_TEST_EQ(s.encoded().length(), str.size()); + } + + // encoded().max_size() + { + pct_encoded_view s(str); + BOOST_TEST_GT(s.encoded().max_size(), 0u); + } + + // empty() + { + pct_encoded_view s; + BOOST_TEST(s.empty()); + + pct_encoded_view s2(str); + BOOST_TEST_NOT(s2.empty()); + } + } + + void + testCopy() + { + // copy() + { + pct_encoded_view s(str); + std::string out(s.size(), ' '); + s.copy(&out[0], s.size()); + BOOST_TEST_EQ(s, dec_str); + } + } + + void + testCompare() + { + // compare() + { + pct_encoded_view s(str); + BOOST_TEST_EQ(s.compare(dec_str), 0); + BOOST_TEST_EQ(s.compare("a a"), 1); + BOOST_TEST_EQ(s.compare("a z"), -1); + std::string bs = "z"; + BOOST_TEST_EQ(s.compare(bs), -1); + } + + // operators + { + pct_encoded_view s(str); + + // pct_encoded_view + { + pct_encoded_view s0(str); + pct_encoded_view s1("a%20tri+test"); + pct_encoded_view s2("a%20vri+test"); + BOOST_TEST(s == s0); + BOOST_TEST_NOT(s == s1); + BOOST_TEST(s != s2); + BOOST_TEST_NOT(s != s0); + BOOST_TEST(s < s2); + BOOST_TEST_NOT(s < s0); + BOOST_TEST(s <= s2); + BOOST_TEST(s <= s0); + BOOST_TEST(s > s1); + BOOST_TEST_NOT(s > s0); + BOOST_TEST(s >= s1); + BOOST_TEST(s >= s0); + } + + // string_view + { + string_view str0(dec_str); + string_view str1("a tri test"); + string_view str2("a vri test"); + BOOST_TEST(s == str0); + BOOST_TEST_NOT(s == str1); + BOOST_TEST(s != str2); + BOOST_TEST_NOT(s != str0); + BOOST_TEST(s < str2); + BOOST_TEST_NOT(s < str0); + BOOST_TEST(s <= str2); + BOOST_TEST(s <= str0); + BOOST_TEST(s > str1); + BOOST_TEST_NOT(s > str0); + BOOST_TEST(s >= str1); + BOOST_TEST(s >= str0); + } + + // string + { + std::string bstr0(dec_str); + std::string bstr1("a tri test"); + std::string bstr2("a vri test"); + BOOST_TEST(s == bstr0); + BOOST_TEST_NOT(s == bstr1); + BOOST_TEST(s != bstr2); + BOOST_TEST_NOT(s != bstr0); + BOOST_TEST(s < bstr2); + BOOST_TEST_NOT(s < bstr0); + BOOST_TEST(s <= bstr2); + BOOST_TEST(s <= bstr0); + BOOST_TEST(s > bstr1); + BOOST_TEST_NOT(s > bstr0); + BOOST_TEST(s >= bstr1); + BOOST_TEST(s >= bstr0); + } + + + // string literals + { + BOOST_TEST(s == "a uri test"); + BOOST_TEST_NOT(s == "a tri test"); + BOOST_TEST(s != "a vri test"); + BOOST_TEST_NOT(s != "a uri test"); + BOOST_TEST(s < "a vri test"); + BOOST_TEST_NOT(s < "a uri test"); + BOOST_TEST(s <= "a vri test"); + BOOST_TEST(s <= "a uri test"); + BOOST_TEST(s > "a tri test"); + BOOST_TEST_NOT(s > "a uri test"); + BOOST_TEST(s >= "a tri test"); + BOOST_TEST(s >= "a uri test"); + } + + } + } + + void + testConversion() + { + // to_string() + { + pct_encoded_view s(str); + BOOST_TEST_EQ(s.to_string(), dec_str); + } + + // append_to() + { + pct_encoded_view s(str); + std::string o = "init "; + s.append_to(o); + + std::string exp = std::string("init "); + exp.append(dec_str.data(), dec_str.size()); + + BOOST_TEST_EQ(o, exp); + } + + // assign_to() + { + pct_encoded_view s(str); + std::string o = "init "; + s.assign_to(o); + BOOST_TEST_EQ(o, dec_str); + } + + // pass it to a function taking a string_view + { + auto f = [this](string_view sv) + { + BOOST_TEST(sv == dec_str); + }; + pct_encoded_view s(str); + f(s.to_string()); + } + + // pass it to a function taking a char* + { + auto f = [this](char const* sv) + { + BOOST_TEST(sv == dec_str); + }; + pct_encoded_view s(str); + f(s.to_string().c_str()); + } + } + + void + testStream() + { + // operator<< + { + std::stringstream ss; + pct_encoded_view s(str); + ss << s; + BOOST_TEST_EQ(ss.str(), dec_str); + } + } + + void + testPR127Cases() + { + { + std::stringstream ss; + urls::pct_encoded_view ds("test+string"); + // no warning about object slicing + ss << ds; + } + + { + auto break_stuff = + [this](urls::string_view const& a) { + urls::string_view b{}; + b = a; + BOOST_TEST_EQ(b.size(), dn); + }; + break_stuff(pct_encoded_view{str}.to_string()); + } + + { +#if !defined(BOOST_NO_CXX17_HDR_STRING_VIEW) + struct A + { + A( std::string_view s) + { + boost::ignore_unused(s); + } + + A( std::string_view s, std::size_t dn ) + { + BOOST_TEST_EQ(s.size(), dn); + boost::ignore_unused(s, dn); + } + }; + A a1(pct_encoded_view{str}.to_string(), dn); + A a2(std::string_view(pct_encoded_view{str}.to_string()), dn); + A a3 = std::string_view(pct_encoded_view{str}.to_string()); + // https://en.cppreference.com/w/cpp/language/copy_initialization#Notes + // A a4 = pct_encoded_view{str}.to_string(); + boost::ignore_unused(a1, a2, a3); +#endif + struct B + { + B( urls::string_view s) + { + boost::ignore_unused(s); + } + + B( urls::string_view s, std::size_t dn ) + { + BOOST_TEST_EQ(s.size(), dn); + boost::ignore_unused(s, dn); + } + }; + B b1(pct_encoded_view{str}.to_string(), dn); + B b2(string_view( + pct_encoded_view{str}.to_string()), dn); + B b3 = string_view( + pct_encoded_view{str}.to_string()); + // https://en.cppreference.com/w/cpp/language/copy_initialization#Notes + // B b4 = pct_encoded_view{str}.to_string(); + boost::ignore_unused(b1, b2, b3); + } + + { +#if !defined(BOOST_NO_CXX17_HDR_STRING_VIEW) + auto f1 = []( std::string_view ) {}; + f1(pct_encoded_view{str}.to_string()); +#endif + auto f2 = []( urls::string_view ) {}; + f2(pct_encoded_view{str}.to_string()); + + auto f3 = []( std::string const& ) {}; + f3(pct_encoded_view{str}.to_string()); + + auto f4 = []( std::basic_string, + std::allocator> const&) {}; + f4(pct_encoded_view{str}.to_string()); + } + } + + void + run() + { + testDecodedView(); + testIter(); + testAccessors(); + testObservers(); + testCopy(); + testCompare(); + testConversion(); + testStream(); + testPR127Cases(); + } +}; + +TEST_SUITE( + pct_encoded_view_test, + "boost.url.pct_encoded_view"); + +} // urls +} // boost diff --git a/test/unit/snippets.cpp b/test/unit/snippets.cpp index 6a4e72606..c6e7824f9 100644 --- a/test/unit/snippets.cpp +++ b/test/unit/snippets.cpp @@ -931,6 +931,17 @@ struct CharSet }; //] +//[snippet_mutable_string_exemplar +struct MutableString +{ + template< class InputIt > + void assign( InputIt first, InputIt last ); + + template< class InputIt > + void append( InputIt first, InputIt last ); +}; +//] + void grammar_charset()