From 4bbbc15ecc350fc625ec8c8cfb77df4fcd165ca5 Mon Sep 17 00:00:00 2001 From: Vinnie Falco Date: Sat, 25 Sep 2021 19:14:45 -0700 Subject: [PATCH] segments refactoring --- include/boost/url.hpp | 1 + include/boost/url/arrow_proxy.hpp | 34 + include/boost/url/detail/any_path_iter.hpp | 76 ++ include/boost/url/detail/copied_strings.hpp | 4 - .../boost/url/detail/impl/any_path_iter.ipp | 28 +- .../boost/url/detail/optional_allocator.hpp | 76 ++ include/boost/url/detail/over_allocator.hpp | 1 + include/boost/url/impl/segments.hpp | 1099 +++++++++++++++++ include/boost/url/impl/segments_encoded.hpp | 278 ++--- include/boost/url/impl/segments_view.hpp | 236 +--- include/boost/url/impl/segments_view.ipp | 193 +++ include/boost/url/impl/static_pool.ipp | 87 +- include/boost/url/impl/string.hpp | 241 ++++ include/boost/url/impl/url.hpp | 9 + include/boost/url/impl/url.ipp | 23 +- include/boost/url/impl/url_view.ipp | 23 +- include/boost/url/segments.hpp | 938 ++++++++++++++ include/boost/url/segments_encoded_view.hpp | 1 - include/boost/url/segments_view.hpp | 27 +- include/boost/url/src.hpp | 1 + include/boost/url/static_pool.hpp | 12 +- include/boost/url/string.hpp | 55 +- include/boost/url/url.hpp | 53 +- include/boost/url/url_view.hpp | 49 +- test/unit/CMakeLists.txt | 1 + test/unit/Jamfile | 1 + test/unit/segments.cpp | 818 ++++++++++++ test/unit/segments_encoded.cpp | 64 +- test/unit/string.cpp | 20 +- test/unit/url.cpp | 4 - test/unit/url_view.cpp | 4 - 31 files changed, 3763 insertions(+), 694 deletions(-) create mode 100644 include/boost/url/arrow_proxy.hpp create mode 100644 include/boost/url/detail/optional_allocator.hpp create mode 100644 include/boost/url/impl/segments.hpp create mode 100644 include/boost/url/impl/segments_view.ipp create mode 100644 include/boost/url/impl/string.hpp create mode 100644 include/boost/url/segments.hpp create mode 100644 test/unit/segments.cpp diff --git a/include/boost/url.hpp b/include/boost/url.hpp index 07eb5c57..42780a5b 100644 --- a/include/boost/url.hpp +++ b/include/boost/url.hpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include diff --git a/include/boost/url/arrow_proxy.hpp b/include/boost/url/arrow_proxy.hpp new file mode 100644 index 00000000..da1865e0 --- /dev/null +++ b/include/boost/url/arrow_proxy.hpp @@ -0,0 +1,34 @@ +// +// Copyright (c) 2019 Vinnie Falco (vinnie.falco@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_ARROW_PROXY_HPP +#define BOOST_URL_ARROW_PROXY_HPP + +#include + +namespace boost { +namespace urls { + +// https://quuxplusone.github.io/blog/2019/02/06/arrow-proxy/ + +template +struct arrow_proxy +{ + T t; + + T *operator->() noexcept + { + return &t; + } +}; + +} // urls +} // boost + +#endif diff --git a/include/boost/url/detail/any_path_iter.hpp b/include/boost/url/detail/any_path_iter.hpp index ab567bfd..2823f1b8 100644 --- a/include/boost/url/detail/any_path_iter.hpp +++ b/include/boost/url/detail/any_path_iter.hpp @@ -169,6 +169,73 @@ public: } }; +//------------------------------------------------ + +class plain_segs_iter_base +{ +protected: + BOOST_URL_DECL + static + void + measure_impl( + string_view s, + std::size_t& n) noexcept; + + BOOST_URL_DECL + static + void + copy_impl( + string_view s, + char*& dest, + char const* end) noexcept; +}; + +// iterates segments in an +// encoded segment range +template +class plain_segs_iter + : public any_path_iter + , public plain_segs_iter_base +{ + FwdIt it_; + FwdIt end_; + +public: + plain_segs_iter( + FwdIt first, + FwdIt last) noexcept + : it_(first) + , end_(last) + { + } + + bool + measure( + std::size_t& n, + error_code& + ) noexcept override + { + if(it_ == end_) + return false; + measure_impl(*it_, n); + ++it_; + return true; + } + + void + copy( + char*& dest, + char const* end + ) noexcept override + { + copy_impl(*it_, + dest, end); + ++it_; + } +}; + +//------------------------------------------------ + template enc_segs_iter make_enc_segs_iter( @@ -178,6 +245,15 @@ make_enc_segs_iter( first, last); } +template +plain_segs_iter +make_plain_segs_iter( + FwdIt first, FwdIt last) +{ + return plain_segs_iter( + first, last); +} + } // detail } // urls } // boost diff --git a/include/boost/url/detail/copied_strings.hpp b/include/boost/url/detail/copied_strings.hpp index beb02f10..33a1d739 100644 --- a/include/boost/url/detail/copied_strings.hpp +++ b/include/boost/url/detail/copied_strings.hpp @@ -10,10 +10,6 @@ #ifndef BOOST_URL_DETAIL_COPIED_STRINGS_HPP #define BOOST_URL_DETAIL_COPIED_STRINGS_HPP -#ifndef BOOST_URL_SOURCE -#error -#endif - #include namespace boost { diff --git a/include/boost/url/detail/impl/any_path_iter.ipp b/include/boost/url/detail/impl/any_path_iter.ipp index f8feec6b..2c72e90e 100644 --- a/include/boost/url/detail/impl/any_path_iter.ipp +++ b/include/boost/url/detail/impl/any_path_iter.ipp @@ -21,7 +21,7 @@ namespace urls { namespace detail { any_path_iter:: -~any_path_iter() = default; +~any_path_iter() noexcept = default; //------------------------------------------------ @@ -91,6 +91,7 @@ copy( char*& dest, char const* end) noexcept { + (void)end; BOOST_ASSERT(static_cast< std::size_t>( end - dest) >= n_); @@ -200,6 +201,7 @@ copy_impl( char*& dest, char const* end) noexcept { + (void)end; BOOST_ASSERT(static_cast< std::size_t>(end - dest) >= s.size()); @@ -213,6 +215,30 @@ copy_impl( //------------------------------------------------ +void +plain_segs_iter_base:: +measure_impl( + string_view s, + std::size_t& n) noexcept +{ + n += pct_encode_size( + s, pchars); +} + +void +plain_segs_iter_base:: +copy_impl( + string_view s, + char*& dest, + char const* end) noexcept +{ + dest = pct_encode( + dest, end, s, {}, + pchars); +} + +//------------------------------------------------ + } // detail } // urls } // boost diff --git a/include/boost/url/detail/optional_allocator.hpp b/include/boost/url/detail/optional_allocator.hpp new file mode 100644 index 00000000..a96f531e --- /dev/null +++ b/include/boost/url/detail/optional_allocator.hpp @@ -0,0 +1,76 @@ +// +// Copyright (c) 2019 Vinnie Falco (vinnie.falco@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_DETAIL_OPTIONAL_ALLOCATOR_HPP +#define BOOST_URL_DETAIL_OPTIONAL_ALLOCATOR_HPP + +#include + +namespace boost { +namespace urls { +namespace detail { + +// VFALCO This is so we can make +// iterators default-constructible +template +class optional_allocator +{ + char buf_[sizeof(Allocator)]; + bool has_value_ = false; + +public: + ~optional_allocator() + { + if(has_value_) + (*(*this)).~Allocator(); + } + + optional_allocator() = default; + + explicit + optional_allocator( + Allocator const& a) noexcept + : has_value_(true) + { + ::new(buf_) Allocator(a); + } + + optional_allocator( + optional_allocator const& other) noexcept + : has_value_(other.has_value_) + { + if(has_value_) + ::new(buf_) Allocator(*other); + } + + optional_allocator& + operator=(optional_allocator const& other + ) noexcept + { + if(has_value_) + (*(*this)).~Allocator(); + has_value_ = other.has_value_; + if(has_value_) + ::new(buf_) Allocator(*other); + return *this; + } + + Allocator const& + operator*() const noexcept + { + return *reinterpret_cast< + Allocator const*>(buf_); + } +}; + +} // detail +} // urls +} // boost + +#endif diff --git a/include/boost/url/detail/over_allocator.hpp b/include/boost/url/detail/over_allocator.hpp index a4608d8a..f79353ee 100644 --- a/include/boost/url/detail/over_allocator.hpp +++ b/include/boost/url/detail/over_allocator.hpp @@ -11,6 +11,7 @@ #define BOOST_URL_DETAIL_OVER_ALLOCATOR_HPP #include +#include #include #include #include diff --git a/include/boost/url/impl/segments.hpp b/include/boost/url/impl/segments.hpp new file mode 100644 index 00000000..97cdacc3 --- /dev/null +++ b/include/boost/url/impl/segments.hpp @@ -0,0 +1,1099 @@ +// +// Copyright (c) 2019 Vinnie Falco (vinnie.falco@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_SEGMENTS_HPP +#define BOOST_URL_IMPL_SEGMENTS_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace boost { +namespace urls { + +/** A proxy to a path segment +*/ +template +class segments:: + reference +{ + url* u_ = nullptr; + std::size_t i_ = 0; + Allocator a_; + + friend class segments; + friend class iterator; + + reference( + url& u, + std::size_t i, + Allocator const& a) noexcept + : u_(&u) + , i_(i) + , a_(a) + { + } + + string_type + operator*() const noexcept + { + return urls::pct_decode( + u_->encoded_segment(i_), + pchars, {}, a_); + } + +public: + /** Assignment + */ + reference& + operator=( + reference const& other) + { + if( u_ == other.u_ && + i_ == other.i_) + return *this; + auto s = *other; + u_->edit_segments( + i_, i_ + 1, + detail::make_plain_segs_iter( + &s, &s + 1), + detail::make_plain_segs_iter( + &s, &s + 1)); + return *this; + } + + /** Assignment + */ + reference& + operator=( + const_reference const& other) + { + if( u_ == other.u_ && + i_ == other.i_) + return *this; + auto s = *other; + u_->edit_segments( + i_, i_ + 1, + detail::make_plain_segs_iter( + &s, &s + 1), + detail::make_plain_segs_iter( + &s, &s + 1)); + return *this; + } + + /** Assignment + + This function participates in overload + resolution only if + `is_stringlike::value == true`. + */ + template +#ifdef BOOST_URL_DOCS + reference& +#else + typename std::enable_if< + is_stringlike::value, + reference&>::type +#endif + operator=(T const& t) + { + *this = to_string_view(t); + return *this; + } + + /** Assignment + */ + reference& + operator=(string_view s) + { + u_->edit_segments( + i_, i_ + 1, + detail::make_plain_segs_iter( + &s, &s + 1), + detail::make_plain_segs_iter( + &s, &s + 1)); + return *this; + } + + //--- + + /** Conversion + */ + operator + value_type() const noexcept + { + return **this; + } + + /** Swap two elements + */ + template + friend + void + swap( + typename segments::reference& r0, + typename segments::reference& r1); + + //-------------------------------------------- + + template::value>::type + #endif + > + friend + bool + operator==( + reference const& x1, + T const& x2 ) noexcept + { + return *x1 == to_string_view(x2); + } + + template::value>::type + #endif + > + friend + bool + operator==( + T const& x1, + reference const& x2) noexcept + { + return to_string_view(x1) == *x2; + } + + template::value>::type + #endif + > + friend + bool + operator!=( + reference const& x1, + T const& x2 ) noexcept + { + return !(x1 == x2); + } + + template::value>::type + #endif + > + friend + bool + operator!=( + T const& x1, + reference const& x2) noexcept + { + return !(x1 == x2); + } +}; + +//------------------------------------------------ + +/** A proxy to a path segment +*/ +template +class segments:: + const_reference +{ + url const* u_ = nullptr; + std::size_t i_ = 0; + Allocator a_; + + friend class segments; + friend class const_iterator; + + const_reference( + url const& u, + std::size_t i, + Allocator const& a) noexcept + : u_(&u) + , i_(i) + , a_(a) + { + } + + string_type + operator*() const noexcept + { + return urls::pct_decode( + u_->encoded_segment(i_), + pchars, {}, a_); + } + +public: + const_reference( + reference const& other) noexcept + : u_(other.u_) + , i_(other.i_) + , a_(other.a_) + { + } + + /** Conversion + */ + operator + value_type() const noexcept + { + return **this; + } + + //-------------------------------------------- + // + // Comparison + // + //-------------------------------------------- + + friend + bool + operator==( + const_reference const& x1, + const_reference const& x2) noexcept + { + return *x1 == *x2; + } + + friend + bool + operator!=( + const_reference const& x1, + const_reference const& x2) noexcept + { + return *x1 != *x2; + } + + template::value>::type + #endif + > + friend + bool + operator==( + const_reference const& x1, + T const& x2 ) noexcept + { + return *x1 == to_string_view(x2); + } + + template::value>::type + #endif + > + friend + bool + operator==( + T const& x1, + const_reference const& x2) noexcept + { + return to_string_view(x1) == *x2; + } + + template::value>::type + #endif + > + friend + bool + operator!=( + const_reference const& x1, + T const& x2 ) noexcept + { + return !(x1 == x2); + } + + template::value>::type + #endif + > + friend + bool + operator!=( + T const& x1, + const_reference const& x2) noexcept + { + return !(x1 == x2); + } +}; + +//------------------------------------------------ + +template +class segments::iterator +{ + url* u_ = nullptr; + std::size_t i_ = 0; + detail::optional_allocator< + Allocator> a_; + + friend class segments; + + iterator( + url& u, + std::size_t i, + Allocator const& a) noexcept + : u_(&u) + , i_(i) + , a_(a) + { + } + +public: + using value_type = + string_type; + using reference = segments< + Allocator>::reference; + using const_reference = segments< + Allocator>::const_reference; + using pointer = void*; + using const_pointer = void const*; + using difference_type = std::ptrdiff_t; + using iterator_category = + std::random_access_iterator_tag; + + iterator() = default; + + iterator& + operator++() noexcept + { + ++i_; + return *this; + } + + iterator + operator++(int) noexcept + { + auto tmp = *this; + ++*this; + return tmp; + } + + iterator& + operator--() noexcept + { + --i_; + return *this; + } + + iterator + operator--(int) noexcept + { + auto tmp = *this; + --*this; + return tmp; + } + + reference + operator*() const noexcept + { + return reference(*u_, i_, *a_); + } + + friend + bool + operator==( + iterator a, + iterator b) noexcept + { + BOOST_ASSERT(a.u_ == b.u_); + return a.u_ == b.u_ && + a.i_ == b.i_; + } + + friend + bool + operator!=( + iterator a, + iterator b) noexcept + { + BOOST_ASSERT(a.u_ == b.u_); + return a.u_ != b.u_ || + a.i_ != b.i_; + } + + // LegacyRandomAccessIterator + + iterator& + operator+=(ptrdiff_t n) noexcept + { + i_ += n; + return *this; + } + + friend + iterator + operator+( + iterator it, + ptrdiff_t n) noexcept + { + return { *it.u_, it.i_ + n, *it.a_ }; + } + + friend + iterator + operator+( + ptrdiff_t n, + iterator it) noexcept + { + return { *it.u_, it.i_ + n, *it.a_ }; + } + + iterator& + operator-=(ptrdiff_t n) noexcept + { + i_ -= n; + return *this; + } + + friend + iterator + operator-( + iterator it, + ptrdiff_t n) noexcept + { + return { *it.u_, it.i_ - n, *it.a_ }; + } + + friend + std::ptrdiff_t + operator-( + iterator a, + iterator b) noexcept + { + BOOST_ASSERT(a.u_ == b.u_); + return static_cast( + a.i_) - b.i_; + } + + reference + operator[](ptrdiff_t n) const + { + return *(*this + n); + } + + friend + bool + operator<( + iterator a, + iterator b) + { + BOOST_ASSERT(a.u_ == b.u_); + return a.i_ < b.i_; + } + + friend + bool + operator>( + iterator a, + iterator b) + { + return b < a; + } + + friend + bool + operator>=( + iterator a, + iterator b) + { + return !(a < b); + } + + friend + bool + operator<=( + iterator a, + iterator b) + { + return !(a > b); + } +}; + +//------------------------------------------------ + +/** A random-access iterator referencing segments in a url path +*/ +template +class segments::const_iterator +{ + url const* u_ = nullptr; + std::size_t i_ = 0; + detail::optional_allocator< + Allocator> a_; + + friend class segments; + + const_iterator( + url const& u, + std::size_t i, + Allocator const& a) noexcept + : u_(&u) + , i_(i) + , a_(a) + { + } + +public: + using value_type = + string_type; + using const_reference = segments< + Allocator>::const_reference; + using reference = const_reference; + using pointer = void const*; + using const_pointer = void const*; + using difference_type = std::ptrdiff_t; + using iterator_category = + std::random_access_iterator_tag; + + const_iterator() = default; + + const_iterator( + iterator const& it) noexcept + : u_(it.u_) + , i_(it.i_) + , a_(it.a_) + { + } + + const_iterator& + operator++() noexcept + { + ++i_; + return *this; + } + + const_iterator + operator++(int) noexcept + { + auto tmp = *this; + ++*this; + return tmp; + } + + const_iterator& + operator--() noexcept + { + --i_; + return *this; + } + + const_iterator + operator--(int) noexcept + { + auto tmp = *this; + --*this; + return tmp; + } + + const_reference + operator*() const noexcept + { + return const_reference(*u_, i_, *a_); + } + + friend + bool + operator==( + const_iterator a, + const_iterator b) noexcept + { + return a.u_ == b.u_ && + a.i_ == b.i_; + } + + friend + bool + operator!=( + const_iterator a, + const_iterator b) noexcept + { + return a.u_ != b.u_ || + a.i_ != b.i_; + } + + // LegacyRandomAccessIterator + + const_iterator& + operator+=(ptrdiff_t n) noexcept + { + i_ += n; + return *this; + } + + friend + const_iterator + operator+( + const_iterator it, + ptrdiff_t n) noexcept + { + return { *it.u_, it.i_ + n, *it.a_ }; + } + + friend + const_iterator + operator+( + ptrdiff_t n, + const_iterator it) noexcept + { + return { *it.u_, it.i_ + n, *it.a_ }; + } + + const_iterator& + operator-=(ptrdiff_t n) noexcept + { + i_ -= n; + return *this; + } + + friend + const_iterator + operator-( + const_iterator it, + ptrdiff_t n) noexcept + { + return { *it.u_, it.i_ - n, *it.a_ }; + } + + friend + std::ptrdiff_t + operator-( + const_iterator a, + const_iterator b) noexcept + { + BOOST_ASSERT(a.u_ == b.u_); + return static_cast( + a.i_) - b.i_; + } + + const_reference + operator[](ptrdiff_t n) const + { + return *(*this + n); + } + + friend + bool + operator<( + const_iterator a, + const_iterator b) + { + BOOST_ASSERT(a.u_ == b.u_); + return a.i_ < b.i_; + } + + friend + bool + operator>( + const_iterator a, + const_iterator b) + { + return b < a; + } + + friend + bool + operator>=( + const_iterator a, + const_iterator b) + { + return !(a < b); + } + + friend + bool + operator<=( + const_iterator a, + const_iterator b) + { + return !(a > b); + } +}; + +//------------------------------------------------ +// +// Members +// +//------------------------------------------------ + +template +template +typename std::enable_if< + is_stringlike::value_type>::value, + void>::type +segments:: +assign(FwdIt first, FwdIt last) +{ + u_->edit_segments( + 0, + size(), + detail::make_plain_segs_iter( + first, last), + detail::make_plain_segs_iter( + first, last)); +} + +//------------------------------------------------ +// +// Element Access +// +//------------------------------------------------ + +template +auto +segments:: +at(std::size_t i) -> + reference +{ + if(i >= size()) + detail::throw_out_of_range( + BOOST_CURRENT_LOCATION); + return (*this)[i]; +} + +template +auto +segments:: +at(std::size_t i) const -> + const_reference +{ + if(i >= size()) + detail::throw_out_of_range( + BOOST_CURRENT_LOCATION); + return (*this)[i]; +} + +template +auto +segments:: +operator[](std::size_t i) noexcept -> + reference +{ + return { *u_, i, a_ }; +} + +template +auto +segments:: +operator[](std::size_t i) const noexcept -> + const_reference +{ + return { *u_, i, a_ }; +} + +template +auto +segments:: +front() const -> + const_reference +{ + return { *u_, 0, a_ }; +} + +template +auto +segments:: +front() -> + reference +{ + return { *u_, 0, a_ }; +} + +template +auto +segments:: +back() const -> + const_reference +{ + return { *u_, size() - 1, a_ }; +} + +template +auto +segments:: +back() -> + reference +{ + return { *u_, size() - 1, a_ }; +} + +//------------------------------------------------ +// +// Iterators +// +//------------------------------------------------ + +template +auto +segments:: +begin() noexcept -> + iterator +{ + return iterator( + *u_, 0, a_); +} + +template +auto +segments:: +begin() const noexcept -> + const_iterator +{ + return const_iterator( + *u_, 0, a_); +} + +template +auto +segments:: +cbegin() const noexcept -> + const_iterator +{ + return const_iterator( + *u_, 0, a_); +} + +template +auto +segments:: +end() noexcept -> + iterator +{ + return iterator( + *u_, size(), a_); +} + +template +auto +segments:: +end() const noexcept -> + const_iterator +{ + return const_iterator( + *u_, size(), a_); +} + +template +auto +segments:: +cend() const noexcept -> + const_iterator +{ + return const_iterator( + *u_, size(), a_); +} + +//------------------------------------------------ +// +// Capacity +// +//------------------------------------------------ + +template +std::size_t +segments:: +size() const noexcept +{ + return u_->segment_count(); +} + +//------------------------------------------------ +// +// Modifiers +// +//------------------------------------------------ + +template +void +segments:: +clear() noexcept +{ + erase(begin(), end()); +} + +template +auto +segments:: +insert( + const_iterator before, + string_view s) -> + iterator +{ + BOOST_ASSERT(before.u_ == u_); + u_->edit_segments( + before.i_, + before.i_, + detail::make_plain_segs_iter( + &s, &s + 1), + detail::make_plain_segs_iter( + &s, &s + 1)); + return { *u_, before.i_, a_ }; +} + +template +template +auto +segments:: +insert( + const_iterator before, + FwdIt first, + FwdIt last, + std::forward_iterator_tag) -> + iterator +{ + u_->edit_segments( + before.i_, + before.i_, + detail::make_plain_segs_iter( + first, last), + detail::make_plain_segs_iter( + first, last)); + return { *u_, before.i_, a_ }; +} + +template +template +auto +segments:: +insert( + const_iterator before, + std::initializer_list init) -> + typename std::enable_if< + is_stringlike::value, + iterator>::type +{ + return insert(before, + init.begin(), init.end()); +} + +template +template +auto +segments:: +insert( + const_iterator before, + T const& t) -> + iterator +{ + return insert(before, + to_string_view(t)); +} + +template +template +auto +segments:: +insert( + const_iterator before, + FwdIt first, + FwdIt last) -> + typename std::enable_if< + is_stringlike::value_type>::value, + iterator>::type +{ + return insert(before, first, last, + typename std::iterator_traits< + FwdIt>::iterator_category{}); +} + +template +auto +segments:: +erase( + const_iterator pos) noexcept -> + iterator +{ + return erase(pos, pos + 1); +} + +template +auto +segments:: +segments:: +erase( + const_iterator first, + const_iterator last) noexcept -> + iterator +{ + BOOST_ASSERT(first.u_ == u_); + BOOST_ASSERT(last.u_ == u_); + string_view s; + u_->edit_segments( + first.i_, last.i_, + detail::make_enc_segs_iter(&s, &s), + detail::make_enc_segs_iter(&s, &s)); + return { *u_, first.i_, a_ }; +} + +template +void +segments:: +push_back( + string_view s) +{ + insert(end(), s); +} + +template +void +segments:: +pop_back() noexcept +{ + erase(end() - 1); +} + +//------------------------------------------------ +// +// nested types +// +//------------------------------------------------ + +template +void +swap( + typename segments::reference& r0, + typename segments::reference& r1) +{ + detail::copied_strings cs( + r0.u_->encoded_url()); + auto tmp = cs.maybe_copy( + string_view(r0)); + r0 = r1; + r1 = tmp; +} + +} // urls +} // boost + +#endif diff --git a/include/boost/url/impl/segments_encoded.hpp b/include/boost/url/impl/segments_encoded.hpp index 7af28770..628c2063 100644 --- a/include/boost/url/impl/segments_encoded.hpp +++ b/include/boost/url/impl/segments_encoded.hpp @@ -44,6 +44,9 @@ public: reference& operator=(reference const& other) { + if( u_ == other.u_ && + i_ == other.i_) + return *this; *this = string_view(other); return *this; } @@ -108,100 +111,6 @@ public: } #endif - /** Comparison - - This function participates in overload - resolution only if - `is_stringlike::value == true`. - */ - template::value && - ! std::is_same::value, - bool>::type -#endif - > - friend - bool - operator==( - reference const& x1, - T const& x2 ) noexcept - { - return - string_view(x1) == - to_string_view(x2); - } - - /** Comparison - - This function participates in overload - resolution only if - `is_stringlike::value == true`. - */ - template::value && - ! std::is_same::value, - bool>::type -#endif - > - friend - bool - operator==( - T const& x1, - reference x2 ) noexcept - { - return - to_string_view(x1) == - string_view(x2); - } - - /** Comparison - - This function participates in overload - resolution only if - `is_stringlike::value == true`. - */ - template::value, - bool>::type -#endif - > - friend - bool - operator!=( - reference const& x1, - T const& x2 ) noexcept - { - return !( x1 == x2 ); - } - - /** Comparison - - This function participates in overload - resolution only if - `is_stringlike::value == true`. - */ - template::value, - bool>::type -#endif - > - friend - bool - operator!=( - T const& x1, - reference const& x2 ) noexcept - { - return !( x1 == x2); - } - /** Swap two elements */ BOOST_URL_DECL @@ -266,100 +175,6 @@ public: return { s.data(), s.size() }; } #endif - - /** Comparison - - This function participates in overload - resolution only if - `is_stringlike::value == true`. - */ - template::value && - ! std::is_same::value, - bool>::type -#endif - > - friend - bool - operator==( - const_reference const& x1, - T const& x2 ) noexcept - { - return - string_view(x1) == - to_string_view(x2); - } - - /** Comparison - - This function participates in overload - resolution only if - `is_stringlike::value == true`. - */ - template::value && - ! std::is_same::value, - bool>::type -#endif - > - friend - bool - operator==( - T const& x1, - const_reference x2 ) noexcept - { - return - to_string_view(x1) == - string_view(x2); - } - - /** Comparison - - This function participates in overload - resolution only if - `is_stringlike::value == true`. - */ - template::value, - bool>::type -#endif - > - friend - bool - operator!=( - const_reference const& x1, - T const& x2 ) noexcept - { - return !( x1 == x2 ); - } - - /** Comparison - - This function participates in overload - resolution only if - `is_stringlike::value == true`. - */ - template::value, - bool>::type -#endif - > - friend - bool - operator!=( - T const& x1, - const_reference const& x2 ) noexcept - { - return !(x1 == x2); - } }; //------------------------------------------------ @@ -1006,52 +821,99 @@ operator=( const_reference const& other) -> reference& { + if( u_ == other.u_ && + i_ == other.i_) + return *this; *this = string_view(other); return *this; } -inline +/** Comparison + + This function participates in overload + resolution only if + `is_stringlike::value == true`. +*/ +template::value>::type +#endif +> bool operator==( segments_encoded:: - const_reference const& c, - segments_encoded:: - reference const& r) noexcept + const_reference const& x1, + T const& x2 ) noexcept { - return string_view(c) == string_view(r); + return + string_view(x1) == + to_string_view(x2); } -inline +/** Comparison + + This function participates in overload + resolution only if + `is_stringlike::value == true`. +*/ +template::value>::type +#endif +> bool operator==( + T const& x1, segments_encoded:: - reference const& r, - segments_encoded:: - const_reference const& c) noexcept + const_reference const& x2 ) noexcept { - return string_view(r) == string_view(c); + return + to_string_view(x1) == + string_view(x2); } -inline +/** Comparison + + This function participates in overload + resolution only if + `is_stringlike::value == true`. +*/ +template::value>::type +#endif +> bool operator!=( segments_encoded:: - const_reference const& c, - segments_encoded:: - reference const& r) noexcept + const_reference const& x1, + T const& x2 ) noexcept { - return string_view(c) != string_view(r); + return !( x1 == x2 ); } -inline +/** Comparison + + This function participates in overload + resolution only if + `is_stringlike::value == true`. +*/ +template::value>::type +#endif +> bool operator!=( + T const& x1, segments_encoded:: - reference const& r, - segments_encoded:: - const_reference const& c) noexcept + const_reference const& x2 ) noexcept { - return string_view(r) != string_view(c); + return !(x1 == x2); } } // urls diff --git a/include/boost/url/impl/segments_view.hpp b/include/boost/url/impl/segments_view.hpp index ced460af..757d43f8 100644 --- a/include/boost/url/impl/segments_view.hpp +++ b/include/boost/url/impl/segments_view.hpp @@ -10,69 +10,67 @@ #ifndef BOOST_URL_IMPL_SEGMENTS_VIEW_HPP #define BOOST_URL_IMPL_SEGMENTS_VIEW_HPP -#include -#include -#include -#include -#include +#include +#include +#include namespace boost { namespace urls { -template -class segments_view:: +class segments_view:: iterator { - std::size_t i_ = 0; - string_type s_; char const* begin_ = nullptr; char const* pos_ = nullptr; char const* next_ = nullptr; char const* end_ = nullptr; + string_value::allocator a_; + pct_encoded_str t_; friend segments_view; - explicit + BOOST_URL_DECL iterator( string_view s, - Alloc const& a); + string_value:: + allocator const& a) noexcept; // end ctor + BOOST_URL_DECL iterator( std::size_t n, string_view s, - Alloc const& a) noexcept; + string_value:: + allocator const& a) noexcept; public: - using value_type = string_type; + using value_type = string_value; using pointer = value_type const*; using reference = value_type const&; using difference_type = std::ptrdiff_t; using iterator_category = std::bidirectional_iterator_tag; - iterator() noexcept = default; + BOOST_URL_DECL + iterator() noexcept; + + BOOST_URL_DECL iterator( - iterator const&) noexcept = default; - iterator& operator=( - iterator const&) noexcept = default; + iterator const&) noexcept; - std::size_t - index() const noexcept - { - return i_; - } + BOOST_URL_DECL + iterator& + operator=( + iterator const&) noexcept; - value_type const& - operator*() const noexcept - { - return s_; - } + BOOST_URL_DECL + string_value + operator*() const noexcept; - value_type const* + arrow_proxy operator->() const noexcept { - return &s_; + return {**this}; } bool @@ -93,9 +91,11 @@ public: end_ != other.end_; } + BOOST_URL_DECL iterator& operator++() noexcept; + BOOST_URL_DECL iterator& operator--() noexcept; @@ -116,184 +116,6 @@ public: } }; -//------------------------------------------------ - -template -segments_view:: -segments_view( - segments_encoded_view const& sv, - Alloc const& a) noexcept - : s_(sv.s_) - , n_(sv.n_) - , a_(a) -{ -} - -//------------------------------------------------ - -template -segments_view:: -iterator:: -iterator( - string_view s, - Alloc const& a) - : s_(a) - , begin_(s.data()) - , pos_(s.data()) - , next_(s.data()) - , end_(s.data() + s.size()) -{ - using bnf::parse; - using bnf_t = path_rootless_bnf; - using detail::pct_decode_unchecked; - if(next_ == end_) - { - next_ = nullptr; - return; - } - error_code ec; - if(*next_ == '/') - { - // "/" segment - pct_encoded_str t; - bnf_t::increment(next_, - end_, ec, t); - BOOST_ASSERT(! ec); - s_ = pct_decode_unchecked( - t.str, t.decoded_size, {}, - s_.get_allocator()); - } - else - { - // segment-nz - pct_encoded_str t; - bnf_t::begin(next_, - end_, ec, t); - BOOST_ASSERT(! ec); - s_ = pct_decode_unchecked( - t.str, t.decoded_size, {}, - s_.get_allocator()); - } -} - -template -segments_view:: -iterator:: -iterator( - std::size_t n, - string_view s, - Alloc const& a) noexcept - : i_(n) - , s_(a) - , begin_(s.data()) - , pos_(s.data() + s.size()) - , end_(s.data() + s.size()) -{ -} - -template -auto -segments_view:: -iterator:: -operator++() noexcept -> - iterator& -{ - using bnf::parse; - using bnf_t = path_rootless_bnf; - using detail::pct_decode_unchecked; - BOOST_ASSERT(next_ != nullptr); - ++i_; - pos_ = next_; - error_code ec; - // "/" segment - pct_encoded_str t; - bnf_t::increment( - next_, end_, ec, t); - if(ec == error::end) - { - next_ = nullptr; - return *this; - } - BOOST_ASSERT(! ec); - s_ = pct_decode_unchecked( - t.str, t.decoded_size, {}, - s_.get_allocator()); - return *this; -} - -template -auto -segments_view:: -iterator:: -operator--() noexcept -> - iterator& -{ - using bnf::parse; - using bnf_t = path_rootless_bnf; - using detail::pct_decode_unchecked; - BOOST_ASSERT(i_ != 0); - BOOST_ASSERT(pos_ != begin_); - --i_; - error_code ec; - while(--pos_ != begin_) - { - if(*pos_ != '/') - continue; - // "/" segment - next_ = pos_; - pct_encoded_str t; - bnf_t::increment(next_, - end_, ec, t); - BOOST_ASSERT(! ec); - s_ = pct_decode_unchecked( - t.str, t.decoded_size, {}, - s_.get_allocator()); - return *this; - } - next_ = pos_; - if(*next_ == '/') - { - // "/" segment - pct_encoded_str t; - bnf_t::increment(next_, - end_, ec, t); - BOOST_ASSERT(! ec); - s_ = pct_decode_unchecked( - t.str, t.decoded_size, {}, - s_.get_allocator()); - } - else - { - // segment-nz - pct_encoded_str t; - bnf_t::begin(next_, - end_, ec, t); - BOOST_ASSERT(! ec); - s_ = pct_decode_unchecked( - t.str, t.decoded_size, {}, - s_.get_allocator()); - } - return *this; -} - -template -auto -segments_view:: -begin() const noexcept -> - iterator -{ - return iterator(s_, a_); -} - -template -auto -segments_view:: -end() const noexcept -> - iterator -{ - return iterator(n_, s_, a_); -} - } // urls } // boost diff --git a/include/boost/url/impl/segments_view.ipp b/include/boost/url/impl/segments_view.ipp new file mode 100644 index 00000000..ecf4c7dc --- /dev/null +++ b/include/boost/url/impl/segments_view.ipp @@ -0,0 +1,193 @@ +// +// Copyright (c) 2019 Vinnie Falco (vinnie.falco@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_SEGMENTS_VIEW_IPP +#define BOOST_URL_IMPL_SEGMENTS_VIEW_IPP + +#include +#include +#include +#include +#include + +namespace boost { +namespace urls { + +segments_view:: +segments_view( + segments_view const&) noexcept = default; + +//------------------------------------------------ + +segments_view:: +iterator:: +iterator( + string_view s, + string_value::allocator const& a) noexcept + : begin_(s.data()) + , pos_(s.data()) + , next_(s.data()) + , end_(s.data() + s.size()) + , a_(a) +{ + using bnf::parse; + using bnf_t = path_rootless_bnf; + using detail::pct_decode_unchecked; + if(next_ == end_) + { + next_ = nullptr; + return; + } + error_code ec; + if(*next_ == '/') + { + // "/" segment + bnf_t::increment(next_, + end_, ec, t_); + BOOST_ASSERT(! ec); + } + else + { + // segment-nz + bnf_t::begin(next_, + end_, ec, t_); + BOOST_ASSERT(! ec); + } +} + +segments_view:: +iterator:: +iterator( + std::size_t, + string_view s, + string_value:: + allocator const& a) noexcept + : begin_(s.data()) + , pos_(s.data() + s.size()) + , end_(s.data() + s.size()) + , a_(a) +{ +} + +segments_view:: +iterator:: +iterator() noexcept = default; + +segments_view:: +iterator:: +iterator( + iterator const&) noexcept = default; + +auto +segments_view:: +iterator:: +operator=( + iterator const&) noexcept -> + iterator& = default; + +string_value +segments_view:: +iterator:: +operator*() const noexcept +{ + char* dest; + auto s = a_.make_string_value( + t_.decoded_size, dest); + detail::pct_decode_unchecked( + dest, + dest + t_.decoded_size, + t_.str, + {}); + return s; +} + +auto +segments_view:: +iterator:: +operator++() noexcept -> + iterator& +{ + using bnf::parse; + using bnf_t = path_rootless_bnf; + using detail::pct_decode_unchecked; + BOOST_ASSERT(next_ != nullptr); + pos_ = next_; + error_code ec; + // "/" segment + bnf_t::increment( + next_, end_, ec, t_); + if(ec == error::end) + { + next_ = nullptr; + return *this; + } + BOOST_ASSERT(! ec); + return *this; +} + +auto +segments_view:: +iterator:: +operator--() noexcept -> + iterator& +{ + using bnf::parse; + using bnf_t = path_rootless_bnf; + using detail::pct_decode_unchecked; + BOOST_ASSERT(pos_ != begin_); + error_code ec; + while(--pos_ != begin_) + { + if(*pos_ != '/') + continue; + // "/" segment + next_ = pos_; + bnf_t::increment(next_, + end_, ec, t_); + BOOST_ASSERT(! ec); + return *this; + } + next_ = pos_; + if(*next_ == '/') + { + // "/" segment + bnf_t::increment(next_, + end_, ec, t_); + BOOST_ASSERT(! ec); + } + else + { + // segment-nz + bnf_t::begin(next_, + end_, ec, t_); + BOOST_ASSERT(! ec); + } + return *this; +} + +auto +segments_view:: +begin() const noexcept -> + iterator +{ + return iterator(s_, a_); +} + +auto +segments_view:: +end() const noexcept -> + iterator +{ + return iterator(n_, s_, a_); +} + +} // urls +} // boost + +#endif diff --git a/include/boost/url/impl/static_pool.ipp b/include/boost/url/impl/static_pool.ipp index 5a9a27b9..3429c375 100644 --- a/include/boost/url/impl/static_pool.ipp +++ b/include/boost/url/impl/static_pool.ipp @@ -16,90 +16,22 @@ namespace boost { namespace urls { -struct basic_static_pool::item -{ - void* p; - std::size_t n; - - bool - in_use() const noexcept - { - return (reinterpret_cast< - std::uintptr_t>(p) & 1) != 0; - } - - void - use() noexcept - { - p = reinterpret_cast< - void*>((reinterpret_cast< - std::uintptr_t>(p) | 1)); - } - - void - free() noexcept - { - p = reinterpret_cast< - void*>((reinterpret_cast< - std::uintptr_t>(p) & ~1)); - } - - void* - ptr() const noexcept - { - return reinterpret_cast< - void*>((reinterpret_cast< - std::uintptr_t>(p) & ~1)); - } -}; - void* basic_static_pool:: allocate( std::size_t bytes, std::size_t align) { - if( align < 2) - align = 2; - bytes = alignment::align_up( + auto n = alignment::align_up( bytes, align); - item* it0 = reinterpret_cast< - item*>(base_); - item* it1 = it0 + n_; - { - // find best fit - item* best = nullptr; - std::size_t bestn = - std::size_t(-1); - auto it = it0; - while(it != it1) - { - if( ! it->in_use() && - it->n >= bytes && - it->n < bestn) - { - best = it; - bestn = it->n; - } - ++it; - } - if(best) - { - it->use(); - return it->p; - } - } - auto const u0 = std::uintptr_t(top_); - auto const u = align * ( - (u0 + align - 1) / align); - auto const p = - reinterpret_cast(u); - if( u < u0 || bytes > - capacity_ - (p - base_)) + auto p = reinterpret_cast( + reinterpret_cast< + std::uintptr_t>(top_ - n) & + ~(align - 1)); + if(p < begin_) detail::throw_bad_alloc( BOOST_CURRENT_LOCATION); - top_ = reinterpret_cast< - char*>(p + bytes); + ++n_; return p; } @@ -110,6 +42,11 @@ deallocate( std::size_t, std::size_t) noexcept { + BOOST_ASSERT(n_ > 0); + --n_; + if(n_ > 0) + return; + top_ = end_; } } // urls diff --git a/include/boost/url/impl/string.hpp b/include/boost/url/impl/string.hpp new file mode 100644 index 00000000..c4b2f537 --- /dev/null +++ b/include/boost/url/impl/string.hpp @@ -0,0 +1,241 @@ +// +// Copyright (c) 2019 Vinnie Falco (vinnie.falco@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/CPPAllinace/url +// + +#ifndef BOOST_URL_IMPL_STRING_HPP +#define BOOST_URL_IMPL_STRING_HPP + +#include + +namespace boost { +namespace urls { + +struct string_value::base +{ + std::size_t refs = 1; + virtual void destroy() noexcept = 0; +}; + +template +auto +string_value:: +construct( + std::size_t n, + Allocator const& a, + char*& dest) -> + base* +{ + class impl; + + using allocator_type = + detail::over_allocator< + impl, Allocator>; + + class impl : public base + { + allocator_type a_; + + public: + explicit + impl( + allocator_type const& a) + : a_(a) + { + } + + void + destroy() noexcept override + { + auto a(a_); + a.deallocate(this, 1); + } + }; + + if(n == 0) + { + dest = nullptr; + return nullptr; + } + allocator_type al(n, a); + auto p = ::new( + al.allocate(1)) impl(al); + dest = reinterpret_cast< + char*>(p + 1); + static_cast( + *this) = { dest, n }; + return p; +} + +string_value:: +~string_value() +{ + if( p_ && + --p_->refs == 0) + p_->destroy(); +} + +template +string_value:: +string_value( + std::size_t n, + Allocator const& a, + char*& dest) + : p_(construct(n, a, dest)) +{ +} + +template +string_value:: +string_value( + string_view s, + Allocator const& a) +{ + char* dest; + p_ = construct( + s.size(), a, dest); + std::memcpy(dest, + s.data(), s.size()); +} + +string_value:: +string_value( + string_value const& other) noexcept + : string_view(other) + , p_(other.p_) +{ + if(p_) + ++p_->refs; +} + +string_value& +string_value:: +operator=( + string_value const& other) noexcept +{ + if( p_ && + --p_->refs == 0) + p_->destroy(); + p_ = other.p_; + if(p_) + ++p_->refs; + static_cast( + *this) = other; + return *this; +} + +//------------------------------------------------ + +class string_value::allocator +{ + struct base + { + std::size_t refs = 1; + + virtual + ~base() + { + } + + virtual + string_value + alloc( + std::size_t n, + char*& dest) = 0; + + virtual + void + destroy() noexcept = 0; + }; + + base* p_ = nullptr; + +public: + ~allocator() + { + if( p_ && + --p_->refs == 0) + p_->destroy(); + } + + allocator() = default; + + allocator( + allocator const& other) noexcept + : p_(other.p_) + { + ++p_->refs; + } + + allocator& + operator=( + allocator const& other) noexcept + { + if( p_ && + --p_->refs) + p_->destroy(); + p_ = other.p_; + if(p_) + ++p_->refs; + return *this; + } + + template + explicit + allocator(Allocator const& a) + { + class impl; + + using allocator_type = typename + detail::allocator_traits< + Allocator>::template + rebind_alloc; + + class impl : public base + { + allocator_type a_; + + public: + impl(allocator_type const& a) + : a_(a) + { + } + + string_value + alloc( + std::size_t n, + char*& dest) override + { + return string_value( + n, a_, dest); + } + + void + destroy() noexcept override + { + auto a(a_); + a.deallocate(this, 1); + } + }; + + allocator_type al(a); + p_ = ::new(al.allocate(1)) impl(al); + } + + string_value + make_string_value( + std::size_t n, + char*& dest) const + { + return p_->alloc(n, dest); + } +}; + +} // urls +} // boost + +#endif diff --git a/include/boost/url/impl/url.hpp b/include/boost/url/impl/url.hpp index 8493d1ea..2ef4c5ad 100644 --- a/include/boost/url/impl/url.hpp +++ b/include/boost/url/impl/url.hpp @@ -13,6 +13,15 @@ namespace boost { namespace urls { +template +segments +url:: +segments(Allocator const& a) noexcept +{ + return urls::segments( + *this, a); +} + } // urls } // boost diff --git a/include/boost/url/impl/url.ipp b/include/boost/url/impl/url.ipp index 5324ea3c..935e3fe6 100644 --- a/include/boost/url/impl/url.ipp +++ b/include/boost/url/impl/url.ipp @@ -715,6 +715,7 @@ set_encoded_host(string_view s) case urls::host_type::ipv6: return set_host(t.ipv6); + case urls::host_type::none: case urls::host_type::name: { auto dest = @@ -1296,26 +1297,12 @@ set_path( string_view url:: encoded_segment( - int index) const noexcept + std::size_t i) const noexcept { - std::size_t i; raw_segment r; - if(index >= 0) - { - i = static_cast< - std::size_t>(index); - if(i >= nseg_) - return empty_; - r = get_segment(i); - } - else - { - i = static_cast< - std::size_t>(-index); - if(i > nseg_) - return empty_; - r = get_segment(nseg_ - i); - } + if(i >= nseg_) + return empty_; + r = get_segment(i); string_view s = { s_ + r.pos, r.len }; if(s.starts_with('/')) diff --git a/include/boost/url/impl/url_view.ipp b/include/boost/url/impl/url_view.ipp index 4f86ca7e..668059ee 100644 --- a/include/boost/url/impl/url_view.ipp +++ b/include/boost/url/impl/url_view.ipp @@ -384,29 +384,14 @@ segment_count() const noexcept string_view url_view:: encoded_segment( - int index) const noexcept + std::size_t i) const noexcept { - std::size_t i; - if(index >= 0) - { - i = static_cast< - std::size_t>(index); - if(i >= nseg_) - return empty_; - auto pv = encoded_segments(); - auto it = pv.begin(); - while(i--) - ++it; - return *it; - } - i = static_cast< - std::size_t>(-index); - if(i > nseg_) + if(i >= nseg_) return empty_; auto pv = encoded_segments(); - auto it = pv.end(); + auto it = pv.begin(); while(i--) - --it; + ++it; return *it; } diff --git a/include/boost/url/segments.hpp b/include/boost/url/segments.hpp new file mode 100644 index 00000000..1862c425 --- /dev/null +++ b/include/boost/url/segments.hpp @@ -0,0 +1,938 @@ +// +// Copyright (c) 2019 Vinnie Falco (vinnie.falco@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_SEGMENTS_HPP +#define BOOST_URL_SEGMENTS_HPP + +#include +#include +#include +#include +#include +#include +#include + +namespace boost { +namespace urls { + +#ifndef BOOST_URL_DOCS +class url; +#endif + +/** A reference-like container to modifiable URL segments + + This class implements a RandomAccessContainer + representing the path segments in a @ref url as + percent-encoded strings. Ownership of the segments + is not transferred; the container references the + buffer in the url. Therefore, the lifetime of the + url must remain valid until this container no + longer exists. + + Objects of this type are not constructed directly; + Instead, call the corresponding non-const member + function of @ref url to obtain an instance of + the container: + + @par Example + @code + url u = parse_relative_ref( "/path/to/file.txt" ); + + segments se = u.segments(); + + for( segments::value_type s : se ) + std::cout << s << std::endl; + @endcode + + The @ref reference and @ref const_reference + nested types are defined as publicly accessible + nested classes. They proxy the behavior of a + reference to a percent-encoded string in the + underlying URL. The primary use of these + references is to provide l-values that can be + returned from element-accessing operations. + Any reads or writes which happen through a + @ref reference or @ref const_reference + potentially read or write the underlying + @ref url. + + @see + @ref url. +*/ +template +class segments + : private detail::parts_base +{ + url* u_ = nullptr; + Allocator a_; + + friend class url; + + explicit + segments( + url& u, + Allocator const& a) noexcept + : u_(&u) + , a_(a) + { + } + +public: +#ifdef BOOST_URL_DOCS + /** A random-access iterator referencing segments in a url path + + When dereferenced, this iterator returns a + proxy which allows conversion to stringlike + types, assignments which change the underlying + container, and comparisons. + */ + using iterator = __see_below__; + + /** A random-access iterator referencing segments in a url path + + When dereferenced, this iterator returns a + proxy which allows conversion to stringlike + types, and comparisons. + */ + using const_iterator = __see_below__; + + /** A proxy for a percent-encoded path segment + + This type is a proxy for a modifiable + percent-encoded path segment. It supports + assignment, conversion to stringlike types, + and comparison. + */ + using reference = __see_below__; + + /** A proxy for a percent-encoded path segment + + This type is a proxy for a read-only + percent-encoded path segment. It supports + conversion to stringlike types, and comparison. + */ + using const_reference = __see_below__; +#else + class iterator; + class const_iterator; + class reference; + class const_reference; +#endif + + /** A type which can represent a segment as a value + + This type allows for making a copy of + a segment where ownership is retained + in the copy. + */ + using value_type = string_type; + + /** An unsigned integer type + */ + using size_type = std::size_t; + + /** A signed integer type + */ + using difference_type = std::ptrdiff_t; + + //-------------------------------------------- + // + // Members + // + //-------------------------------------------- + + /** Replace the contents of the container + + This function replaces the contents + with an initializer list of + percent-encoded strings. + Each string must contain a valid + percent-encoding or else an + exception is thrown. + The behavior is undefined any string + refers to the contents of `*this`. + All iterators and references to elements + of the container are invalidated, + including the @ref end iterator. + + @par Requires + @code + is_stringlike< T >::value == true + @endcode + + @par Example + @code + url u = parse_relative_uri( "/path/to/file.txt" ); + + u.segments() = { "etc", "init.rc" }; + + assert( u.encoded_path() == "/etc/init.rc") ); + @endcode + + @par Exception Safety + Strong guarantee. + Calls to allocate may throw. + Exceptions thrown on invalid input. + + @param init An initializer list of strings. + + @throw std::invalid_argument invalid percent-encoding + */ + template +#ifdef BOOST_URL_DOCS + segments& +#else + typename std::enable_if< + is_stringlike::value, + segments&>::type +#endif + operator=(std::initializer_list init) + { + assign(init); + return *this; + } + + /** Replace the contents of the container + + This function replaces the contents + with a range of percent-encoded + strings. + Each string must contain a valid + percent-encoding or else an + exception is thrown. + The behavior is undefined if either + argument is an iterator into `*this`. + All iterators and references to elements + of the container are invalidated, + including the @ref end iterator. + + @par Requires + @code + is_stringlike< std::iterator_traits< FwdIt >::value_type >::value == true + @endcode + + @par Example + @code + url u = parse_relative_uri( "/path/to/file.txt" ); + + segments se = u.segments(); + + std::vector< std::string > v = { "etc", "init.rc" }; + + se.insert( u.end() - 1, v.begin(), v.end() ); + + assert( u.encoded_path() == "/etc/init.rc") ); + @endcode + + @par Exception Safety + Strong guarantee. + Calls to allocate may throw. + Exceptions thrown on invalid input. + + @param first An iterator to the first + element in the range + + @param last An iterator to one past the + last element in the range + + @throw std::invalid_argument invalid percent-encoding + */ + template +#ifdef BOOST_URL_DOCS + void +#else + typename std::enable_if< + is_stringlike::value_type>::value, + void>::type +#endif + assign(FwdIt first, FwdIt last); + + /** Replace the contents of the container + + This function replaces the contents + with an initializer list of + percent-encoded strings. + Each string must contain a valid + percent-encoding or else an + exception is thrown. + The behavior is undefined any string + refers to the contents of `*this`. + All iterators and references to elements + of the container are invalidated, + including the @ref end iterator. + + @par Requires + @code + is_stringlike< T >::value == true + @endcode + + @par Example + @code + url u = parse_relative_uri( "/path/to/file.txt" ); + + u.segments().assign( { "etc", "init.rc" } ); + + assert( u.encoded_path() == "/etc/init.rc") ); + @endcode + + @par Exception Safety + Strong guarantee. + Calls to allocate may throw. + Exceptions thrown on invalid input. + + @param init An initializer list of strings. + + @throw std::invalid_argument invalid percent-encoding + */ + template +#ifdef BOOST_URL_DOCS + void +#else + typename std::enable_if< + is_stringlike::value, + void>::type +#endif + assign(std::initializer_list init) + { + assign(init.begin(), init.end()); + } + + //-------------------------------------------- + // + // Element Access + // + //-------------------------------------------- + + /** Return an element with bounds checking + + This function returns a proxy reference + to the i-th element. If i is greater than + @ref size, an exception is thrown. + + @par Exception Safety + Strong guarantee. + Exception thrown on invalid parameter. + + @throws std::out_of_range `i >= size()` + + @return A proxy reference to the element. + + @param i The zero-based index of the + element. + */ + inline + reference + at(std::size_t i); + + /** Return an element with bounds checking + + This function returns a proxy reference + to the i-th element. If i is greater than + @ref size, an exception is thrown. + + @par Exception Safety + Strong guarantee. + Exception thrown on invalid parameter. + + @throws std::out_of_range `i >= size()` + + @return A proxy reference to the element. + + @param i The zero-based index of the + element. + */ + inline + const_reference + at(std::size_t i) const; + + /** Return an element + + This function returns a proxy reference + to the i-th element. + + @par Preconditions + @code + i < size() + @endcode + + @par Exception Safety + Strong guarantee. + + @return A proxy reference to the element. + + @param i The zero-based index of the + element. + */ + inline + reference + operator[](std::size_t i) noexcept; + + /** Return an element + + This function returns a proxy reference + to the i-th element. + + @par Preconditions + @code + i < size() + @endcode + + @par Exception Safety + Strong guarantee. + + @return A proxy reference to the element. + + @param i The zero-based index of the + element. + */ + inline + const_reference + operator[](std::size_t i) const noexcept; + + /** Return the first element + */ + inline + const_reference + front() const; + + /** Return the first element + */ + inline + reference + front(); + + /** Return the last element + */ + inline + const_reference + back() const; + + /** Return the last element + */ + inline + reference + back(); + + //-------------------------------------------- + // + // Iterators + // + //-------------------------------------------- + + /** Return an iterator to the beginning + */ + inline + iterator + begin() noexcept; + + /** Return an iterator to the beginning + */ + inline + const_iterator + begin() const noexcept; + + /** Return an iterator to the beginning + */ + inline + const_iterator + cbegin() const noexcept; + + /** Return an iterator to the end + */ + inline + iterator + end() noexcept; + + /** Return an iterator to the end + */ + inline + const_iterator + end() const noexcept; + + /** Return an iterator to the end + */ + inline + const_iterator + cend() const noexcept; + + //-------------------------------------------- + // + // Capacity + // + //-------------------------------------------- + + /** Return true if the container is empty + + This function returns true if there are + no elements in the container. That is, if + the underlying path is the empty string. + */ + bool + empty() const noexcept + { + return size() == 0; + } + + /** Return the number of elements in the container + + This function returns the number of + elements in the underlying path. Empty + segments count towards this total. + + @par Exception Safety + Throws nothing. + */ + BOOST_URL_DECL + std::size_t + size() const noexcept; + + //-------------------------------------------- + // + // Modifiers + // + //-------------------------------------------- + +private: + template + iterator + insert( + const_iterator before, + FwdIt first, + FwdIt last, + std::input_iterator_tag) = delete; + + template + iterator + insert( + const_iterator before, + FwdIt first, + FwdIt last, + std::forward_iterator_tag); +public: + + /** Remove the contents of the container + + This function removes all the segments + from the container, leaving the + underlying URL with an empty path. + + @par Postconditions + @code + empty() == true + @endcode + + @par Exception Safety + Throws nothing. + */ + inline + void + clear() noexcept; + + /** Insert an element + + This function inserts a segment specified + by the percent-encoded string `s`, at the + position preceding `before`. + The string must contain a valid + percent-encoding, or else an exception + is thrown. + All references and iterators starting + from the newly inserted element and + up to and including the last element + and @ref end iterators are invalidated. + + @par Exception Safety + Strong guarantee. + Calls to allocate may throw. + Exceptions thrown on invalid input. + + @return An iterator pointing to the + inserted value. + + @param before An iterator before which the + new element should be inserted. + + @param s A valid percent-encoded string + to be inserted. + + @throw std::invalid_argument invalid percent-encoding + */ + BOOST_URL_DECL + iterator + insert( + const_iterator before, + string_view s); + + /** Insert an element + + This function inserts a segment specified + by the percent-encoded stringlike `t`, + at the position preceding `before`. + The stringlike must contain a valid + percent-encoding, or else an exception + is thrown. + All references and iterators starting + from the newly inserted element and + up to and including the last element + and @ref end iterators are invalidated. + This function participates in overload + resolution only if + `is_stringlike::value == true`. + + @par Exception Safety + Strong guarantee. + Calls to allocate may throw. + Exceptions thrown on invalid input. + + @return An iterator pointing to the + inserted value. + + @param before An iterator before which the + new element should be inserted. + + @param t The stringlike value to insert. + + @throw std::invalid_argument invalid percent-encoding + */ + template::value, + bool>::type +#endif + > + iterator + insert( + const_iterator before, + T const& t); + + /** Insert a range of segments + + This function inserts a range + of percent-encoded strings. + Each string must contain a valid + percent-encoding or else an + exception is thrown. + The behavior is undefined if either + argument is an iterator into `this`. + All references and iterators starting + from the newly inserted elements and + up to and including the last element + and @ref end iterators are invalidated. + + @par Requires + @code + is_stringlike< std::iterator_traits< FwdIt >::value_type >::value == true + @endcode + + @par Example + @code + url u = parse_relative_uri( "/path/file.txt" ); + + segments se = u.segments(); + + std::vector< std::string > v = { "to", "the" }; + + se.insert( u.end() - 1, v.begin(), v.end() ); + + assert( u.encoded_path() == "/path/to/the/file.txt") ); + @endcode + + @par Exception Safety + Strong guarantee. + Calls to allocate may throw. + Exceptions thrown on invalid input. + + @return An iterator to one past the last + newly inserted element or `before` if + the range is empty. + + @param before An iterator before which the + new element should be inserted. + + @param first An iterator to the first + element to insert. + + @param last An iterator to one past the + last element to insert. + + @throw std::invalid_argument invalid percent-encoding + */ + template +#ifdef BOOST_URL_DOCS + iterator +#else + typename std::enable_if< + is_stringlike::value_type>::value, + iterator>::type +#endif + insert( + const_iterator before, + FwdIt first, + FwdIt last); + + /** Insert a range of segments + + This function inserts a range of + percent-encoded strings passed as + an initializer-list. + Each string must contain a valid + percent-encoding or else an exception + is thrown. + All references and iterators starting + from the newly inserted elements and + up to and including the last element + and @ref end iterators are invalidated. + + @par Requires + @code + is_stringlike< T >::value == true + @endcode + + @par Example + @code + url u = parse_relative_uri( "/path/file.txt" ); + + segments se = u.segments(); + + se.insert( u.end() - 1, { "to", "the" } ); + + assert( u.encoded_path() == "/path/to/the/file.txt") ); + @endcode + + @par Exception Safety + Strong guarantee. + Calls to allocate may throw. + Exceptions thrown on invalid input. + + @return An iterator to one past the last + newly inserted element or `before` if + the range is empty. + + @param before An iterator before which the + new elements should be inserted. + + @param init The initializer list containing + percent-encoded segments to insert. + + @throw std::invalid_argument invalid percent-encoding + */ + template +#ifdef BOOST_URL_DOCS + iterator +#else + typename std::enable_if< + is_stringlike::value, + iterator>::type +#endif + insert( + const_iterator before, + std::initializer_list init); + + /** Erase an element + + This function erases the element pointed + to by `pos`, which must be a valid + iterator for the container. + All references and iterators starting + from pos and up to and including + the last element and @ref end iterators + are invalidated. + + @par Preconditions + `pos` points to a valid element in + this container. + + @par Example + @code + url u = parse_relative_uri( "/path/to/file.txt" ); + + segments se = u.segments(); + + se.erase( se.begin() + 1 ); + + assert( u.encoded_path() == "/path/file.txt" ); + @endcode + + @par Exception Safety + Throws nothing. + + @return An iterator following + the last element erased. + + @param pos An iterator to the + element to erase. + */ + inline + iterator + erase( + const_iterator pos) noexcept; + + /** Erase a range of elements + + This function erases the elements + in the range `[first, last)`, which + must be a valid range in the container. + All references and iterators starting + from `first` and up to and including + the last element and @ref end iterators + are invalidated. + + @par Preconditions + `[first, last)` is a valid range in + this container. + + @par Example + @code + url u = parse_relative_uri( "/path/to/the/file.txt" ); + + segments se = u.segments(); + + se.erase( se.begin() + 1, se.begin() + 3 ); + + assert( u.encoded_path() == "/path/file.txt" ); + @endcode + + @return An iterator following + the last element erased. + + @param first The beginning of the + range to erase. + + @param last The end of the range + to erase. + + @throw std::invalid_argument invalid percent-encoding + */ + BOOST_URL_DECL + iterator + erase( + const_iterator first, + const_iterator last) noexcept; + + /** Add an element to the end + + This function appends a segment + containing the percent-encoded string + `s` to the end of the container. + The percent-encoding must be valid or + else an exception is thrown. + All @ref end iterators are invalidated. + + @par Example + @code + url u = parse_relative_uri( "/path/to" ); + + u.segments().push_back( "file.txt" ); + + assert( u.encoded_path() == "/path/to/file.txt" ); + @endcode + + @par Exception Safety + Strong guarantee. + Calls to allocate may throw. + Exceptions thrown on invalid input. + + @param s The string to add + + @throw std::invalid_argument invalid percent-encoding + */ + inline + void + push_back( + string_view s); + + /** Add an element to the end + + This function appends a segment + containing the percent-encoded stringlike + `t` to the end of the container. + The percent-encoding must be valid + or else an exception is thrown. + All @ref end iterators are invalidated. + The function participates in overload + resolution only if + `is_stringlike::value == true`. + + @par Example + @code + url u = parse_relative_uri( "/path/to" ); + + u.segments().push_back( "file.txt" ); + + assert( u.encoded_path() == "/path/to/file.txt" ); + @endcode + + @par Exception Safety + Strong guarantee. + Calls to allocate may throw. + Exceptions thrown on invalid input. + + @param t The stringlike to add + + @throw std::invalid_argument invalid percent-encoding + */ + template::value, + bool>::type +#endif + > + void + push_back( + T const& t) + { + return push_back( + to_string_view(t)); + } + + /** Remove the last element + + This function removes the last element + from the container, which must not be + empty or else undefined behavior occurs. + Iterators and references to + the last element, as well as the + @ref end iterator, are invalidated. + + @par Preconditions + @code + not empty() + @endcode + + @par Example + @code + url u = parse_relative_uri( "/path/to/file.txt" ); + + u.segments().pop_back(); + + assert( u.encoded_path() == "/path/to" ); + @endcode + + @par Exception Safety + Throws nothing. + */ + inline + void + pop_back() noexcept; +}; + +} // urls +} // boost + +// VFALCO This include is at the bottom of +// url.hpp because of a circular dependency +//#include + +#endif diff --git a/include/boost/url/segments_encoded_view.hpp b/include/boost/url/segments_encoded_view.hpp index 3bab5326..4d90e836 100644 --- a/include/boost/url/segments_encoded_view.hpp +++ b/include/boost/url/segments_encoded_view.hpp @@ -37,7 +37,6 @@ class segments_encoded_view string_view s_; std::size_t n_; - template friend class segments_view; friend class url_view; diff --git a/include/boost/url/segments_view.hpp b/include/boost/url/segments_view.hpp index 0ba8cfea..99daa892 100644 --- a/include/boost/url/segments_view.hpp +++ b/include/boost/url/segments_view.hpp @@ -12,50 +12,43 @@ #include #include -#include -#include +#include #include -#include namespace boost { namespace urls { -class segments_encoded_view; - /** A BidirectionalRange view of read-only path segments with percent-decoding applied */ -template class segments_view { string_view s_; std::size_t n_; - Allocator a_; + string_value::allocator a_; friend class url; friend class url_view; + template segments_view( string_view s, std::size_t n, - Allocator const& alloc) noexcept + Allocator const& a) : s_(s) , n_(n) - , a_(alloc) + , a_(a) { } public: class iterator; + BOOST_URL_DECL segments_view( - segments_view const&) noexcept = default; - segments_view& operator=( - segments_view const&) noexcept = default; + segments_view const&) noexcept; - explicit - segments_view( - segments_encoded_view const& sv, - Allocator const& = {}) noexcept; + segments_view& operator=( + segments_view const&) noexcept = delete; /** Return true if the range contains no elements */ @@ -75,11 +68,13 @@ public: /** Return an iterator to the beginning of the range */ + BOOST_URL_DECL iterator begin() const noexcept; /** Return an iterator to the end of the range */ + BOOST_URL_DECL iterator end() const noexcept; }; diff --git a/include/boost/url/src.hpp b/include/boost/url/src.hpp index dc08a8f4..a8a4baad 100644 --- a/include/boost/url/src.hpp +++ b/include/boost/url/src.hpp @@ -37,6 +37,7 @@ in a translation unit of the program. #include #include #include +#include #include #include #include diff --git a/include/boost/url/static_pool.hpp b/include/boost/url/static_pool.hpp index c4cdaa15..b472fcdc 100644 --- a/include/boost/url/static_pool.hpp +++ b/include/boost/url/static_pool.hpp @@ -21,13 +21,11 @@ namespace urls { */ class basic_static_pool { - char* const base_; - std::size_t const capacity_; + char* begin_; + char* end_; char* top_; std::size_t n_ = 0; - struct item; - BOOST_URL_DECL void* allocate( @@ -52,9 +50,9 @@ public: basic_static_pool( char* buffer, std::size_t size) - : base_(buffer) - , capacity_(size) - , top_(buffer) + : begin_(buffer) + , end_(buffer + size) + , top_(end_) { } diff --git a/include/boost/url/string.hpp b/include/boost/url/string.hpp index 26adca15..4d398685 100644 --- a/include/boost/url/string.hpp +++ b/include/boost/url/string.hpp @@ -13,13 +13,13 @@ #include #include #include +#include #include -#include - #ifndef BOOST_NO_CXX17_HDR_STRING_VIEW -#include -#define BOOST_URL_HAS_STRING_VIEW +# include +# define BOOST_URL_HAS_STRING_VIEW #endif +#include namespace boost { namespace urls { @@ -160,7 +160,54 @@ to_string_view( } #endif +//------------------------------------------------ + +class string_value : public string_view +{ + struct base; + + base* p_ = nullptr; + + template + base* + construct( + std::size_t n, + Allocator const& a, + char*& dest); + +public: + class allocator; + + inline + ~string_value(); + + string_value() = default; + + template + string_value( + std::size_t n, + Allocator const& a, + char*& dest); + + template< class Allocator = + std::allocator > + explicit + string_value( + string_view s, + Allocator const& a = {}); + + inline + string_value( + string_value const& other) noexcept; + + inline + string_value& + operator=(string_value const& other) noexcept; +}; + } // urls } // boost +#include + #endif diff --git a/include/boost/url/url.hpp b/include/boost/url/url.hpp index 13ba19e7..249d535b 100644 --- a/include/boost/url/url.hpp +++ b/include/boost/url/url.hpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -57,8 +58,9 @@ struct any_path_iter; class BOOST_SYMBOL_VISIBLE url : public url_view { + template + friend class urls::segments; friend class segments_encoded; - friend class segments_encoded::reference; #ifndef BOOST_URL_DOCS protected: @@ -946,43 +948,13 @@ public: /** Return a path segment by index - This function returns an indexed - path segment as a percent-encoded - string. The behavior depends on - `i`: + This function returns a zero-based, + indexed path segment as a percent-encoded + string. - @li If `i` is 0 the first path - segment is returned; - - @li If `i` is positive, then - the `i` + 1th path segment is - returned. For example if `i == 2` - then the third segment is returned. - In other words, `i` is zero based. - - @li If `i` is negative, then the - function negates `i`, and counts from - the end of the path rather than the - beginning. For example if `i == -1` - then the last path segment is returned. - - If the `i` is out of range, an empty - string is returned. To determine the - number of segments, call @ref segment_count. - - @par Example + @par Preconditions @code - url_view u = parse_relative_ref( "/path/to/the/file.txt" ); - - assert( u.encoded_segment( -2 ) == "the" ); - assert( u.encoded_segment( -1 ) == "file.txt" ); - assert( u.encoded_segment( 0 ) == "path" ); - assert( u.encoded_segment( 1 ) == "to" ); - @endcode - - @par BNF - @code - path = [ "/" ] segment *( "/" segment ) + i < segment_count() @endcode @par Exception Safety @@ -998,12 +970,17 @@ public: virtual string_view encoded_segment( - int i) const noexcept override; + std::size_t i) const noexcept override; BOOST_URL_DECL segments_encoded encoded_segments() noexcept; + template> + urls::segments + segments(Allocator const& = {}) noexcept; + //-------------------------------------------- // // Query @@ -1237,6 +1214,8 @@ operator<<(std::ostream& os, url const& u); } // urls } // boost +#include #include +#include #endif diff --git a/include/boost/url/url_view.hpp b/include/boost/url/url_view.hpp index 50a4dea1..5936c1f7 100644 --- a/include/boost/url/url_view.hpp +++ b/include/boost/url/url_view.hpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -1344,44 +1345,10 @@ public: /** Return a path segment by index - This function returns an indexed - path segment as a percent-encoded - string. The behavior depends on - `i`: - - @li If `i` is 0 the first path - segment is returned; - - @li If `i` is positive, then - the `i` + 1th path segment is - returned. For example if `i == 2` - then the third segment is returned. - In other words, `i` is zero based. - - @li If `i` is negative, then the - function negates `i`, and counts from - the end of the path rather than the - beginning. For example if `i == -1` - then the last path segment is returned. - - If the `i` is out of range, an empty - string is returned. To determine the - number of segments, call @ref segment_count. - - @par Example - @code - url_view u = parse_relative_ref( "/path/to/the/file.txt" ); - - assert( u.encoded_segment( -2 ) == "the" ); - assert( u.encoded_segment( -1 ) == "file.txt" ); - assert( u.encoded_segment( 0 ) == "path" ); - assert( u.encoded_segment( 1 ) == "to" ); - @endcode - - @par BNF - @code - path = [ "/" ] segment *( "/" segment ) - @endcode + This function returns a zero-based, + indexed path segment as a percent-encoded + string. If `i >= segment_count()`, an + empty string is returned. @par Exception Safety Throws nothing. @@ -1396,7 +1363,7 @@ public: virtual string_view encoded_segment( - int i) const noexcept; + std::size_t i) const noexcept; /** Return a path segment by index @@ -1502,10 +1469,10 @@ public: */ template> - segments_view + segments_view segments(Allocator const& alloc = {}) const noexcept { - return segments_view( + return segments_view( encoded_path(), nseg_, alloc); } diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt index ccd3bded..5a0036fe 100644 --- a/test/unit/CMakeLists.txt +++ b/test/unit/CMakeLists.txt @@ -24,6 +24,7 @@ set(BOOST_URL_TESTS_FILES pct_encoding.cpp query_params_view.cpp scheme.cpp + segments.cpp segments_encoded.cpp segments_encoded_view.cpp segments_view.cpp diff --git a/test/unit/Jamfile b/test/unit/Jamfile index a5408a34..70e74e60 100644 --- a/test/unit/Jamfile +++ b/test/unit/Jamfile @@ -28,6 +28,7 @@ local SOURCES = pct_encoding.cpp query_params_view.cpp scheme.cpp + segments.cpp segments_encoded.cpp segments_encoded_view.cpp segments_view.cpp diff --git a/test/unit/segments.cpp b/test/unit/segments.cpp new file mode 100644 index 00000000..3fb5782b --- /dev/null +++ b/test/unit/segments.cpp @@ -0,0 +1,818 @@ +// +// Copyright (c) 2019 Vinnie Falco (vinnie.falco@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 +#include +#include +#if __cpp_lib_ranges >= 201911 +//#include +#endif +#include +#include "test_suite.hpp" + +namespace boost { +namespace urls { + +//------------------------------------------------ + +class segments_test +{ +public: + using Alloc = std::allocator; + using alloc_type = + basic_static_pool::allocator_type; + +#if __cpp_lib_ranges >= 201911 + /* + BOOST_STATIC_ASSERT( + std::random_access_range< + segments>); + */ + + BOOST_STATIC_ASSERT( + std::random_access_iterator< + segments::iterator>); + + BOOST_STATIC_ASSERT( + std::random_access_iterator< + segments::const_iterator>); +#endif + + // const_reference + + BOOST_STATIC_ASSERT( + ! std::is_convertible< + segments::const_reference, + string_view>::value); + + BOOST_STATIC_ASSERT( + std::is_convertible< + segments::const_reference, + std::string>::value); + +#ifdef BOOST_URL_HAS_STRING_VIEW + BOOST_STATIC_ASSERT( + ! std::is_convertible< + segments::const_reference, + std::string_view>::value); +#endif + + BOOST_STATIC_ASSERT( + ! std::is_convertible< + segments::const_reference, + std::wstring>::value); + + BOOST_STATIC_ASSERT( + ! std::is_assignable< + segments::const_reference, + string_view>::value); + + BOOST_STATIC_ASSERT( + ! std::is_assignable< + segments::const_reference, + std::string>::value); + +#ifdef BOOST_URL_HAS_STRING_VIEW + BOOST_STATIC_ASSERT( + ! std::is_assignable< + segments::const_reference, + std::string_view>::value); +#endif + + BOOST_STATIC_ASSERT( + ! std::is_assignable< + segments::const_reference, + char const(&)[2]>::value); + + BOOST_STATIC_ASSERT( + ! std::is_assignable< + segments::const_reference, + char const*>::value); + + // reference + + BOOST_STATIC_ASSERT( + ! std::is_convertible< + segments::reference, + string_view>::value); + + BOOST_STATIC_ASSERT( + std::is_convertible< + segments::reference, + std::string>::value); + +#ifdef BOOST_URL_HAS_STRING_VIEW + BOOST_STATIC_ASSERT( + ! std::is_convertible< + segments::reference, + std::string_view>::value); +#endif + + BOOST_STATIC_ASSERT( + ! std::is_convertible< + segments::reference, + std::wstring>::value); + + BOOST_STATIC_ASSERT( + std::is_assignable< + segments::reference, + string_view>::value); + + BOOST_STATIC_ASSERT( + std::is_assignable< + segments::reference, + std::string>::value); + +#ifdef BOOST_URL_HAS_STRING_VIEW + BOOST_STATIC_ASSERT( + std::is_assignable< + segments::reference, + std::string_view>::value); +#endif + + BOOST_STATIC_ASSERT( + std::is_assignable< + segments::reference, + char const(&)[2]>::value); + + BOOST_STATIC_ASSERT( + std::is_assignable< + segments::reference, + char const*>::value); + + static_pool<4096> p_; + + void + testMembers() + { + url_view const u0 = parse_uri( + "x://y/path/to/the/file.txt?q#f"); + + { + url u = u0; + u.segments(p_.allocator()) = { "etc", "index.htm" }; + BOOST_TEST(u.encoded_path() == "/etc/index.htm"); + BOOST_TEST(u.encoded_url() == "x://y/etc/index.htm?q#f"); + } + } + + void + testElementAccess() + { + url_view const u0 = parse_relative_ref( + "/path/to/the/file.txt"); + + // at + { + url u = u0; + auto se = u.segments(p_.allocator()); + auto const& cs = se; + + BOOST_TEST(se.at(0) == "path"); + BOOST_TEST(se.at(1) == "to"); + BOOST_TEST(se.at(2) == "the"); + BOOST_TEST(se.at(3) == "file.txt"); + BOOST_TEST_THROWS(se.at(4), std::out_of_range); + + BOOST_TEST(cs.at(0) == "path"); + BOOST_TEST(cs.at(1) == "to"); + BOOST_TEST(cs.at(2) == "the"); + BOOST_TEST(cs.at(3) == "file.txt"); + BOOST_TEST_THROWS(cs.at(4), std::out_of_range); + + // assign + se.at(1) = "from"; + // comparison + BOOST_TEST(se.at(1) == "from"); + BOOST_TEST(cs.at(1) == "from"); + BOOST_TEST(se.at(1) != "path"); + BOOST_TEST(cs.at(1) != "path"); + } + + // operator[] + { + url u = u0; + auto se = u.segments(p_.allocator()); + auto const& cs = se; + + BOOST_TEST(se[0] == "path"); + BOOST_TEST(se[1] == "to"); + BOOST_TEST(se[2] == "the"); + BOOST_TEST(se[3] == "file.txt"); + + BOOST_TEST(cs[0] == "path"); + BOOST_TEST(cs[1] == "to"); + BOOST_TEST(cs[2] == "the"); + BOOST_TEST(cs[3] == "file.txt"); + + // assign + se[1] = "from"; + // comparison + BOOST_TEST(se[1] == "from"); + BOOST_TEST(cs[1] == "from"); + BOOST_TEST(se[1] != "path"); + BOOST_TEST(cs[1] != "path"); + } + + // front + { + url u = u0; + auto se = u.segments(p_.allocator()); + auto const& cs = se; + + BOOST_TEST(se.front() == "path"); + BOOST_TEST(cs.front() == "path"); + + // assign + se.front() = "etc"; + // comparison + BOOST_TEST(se.front() == "etc"); + BOOST_TEST(cs.front() == "etc"); + BOOST_TEST(se.front() != "path"); + BOOST_TEST(cs.front() != "path"); + } + + // back + { + url u = u0; + auto se = u.segments(p_.allocator()); + auto const& cs = se; + + BOOST_TEST(se.back() == "file.txt"); + BOOST_TEST(cs.back() == "file.txt"); + + // assign + se.back() = "index.htm"; + // comparison + BOOST_TEST(se.back() == "index.htm"); + BOOST_TEST(cs.back() == "index.htm"); + BOOST_TEST(se.back() != "file.txt"); + BOOST_TEST(cs.back() != "file.txt"); + } + } + + void + testIterators() + { + url_view const u0 = parse_uri( + "x://y/path/to/the/file.txt"); + + // (default-ctor) + { + segments::iterator it; + segments::const_iterator cit; + + url u = u0; + auto se = u.segments(); + + it = se.begin(); + + // assign from non-const + cit = se.begin(); + + // heterogeneous comparison + BOOST_TEST(cit == it); + BOOST_TEST(it != se.end()); + BOOST_TEST(it != se.cend()); + BOOST_TEST(cit != se.end()); + } + + // begin + { + url u = u0; + auto se = u.segments(p_.allocator()); + auto const& cs = se; + + BOOST_TEST(se.begin() == cs.begin()); + BOOST_TEST(se.cbegin() == cs.begin()); + BOOST_TEST(se.end() != se.begin()); + BOOST_TEST(se.end() != se.cbegin()); + } + + // end + { + url u = u0; + auto se = u.segments(p_.allocator()); + auto const& cs = se; + + BOOST_TEST(se.end() == cs.end()); + BOOST_TEST(se.cend() == cs.end()); + BOOST_TEST(se.begin() != se.end()); + BOOST_TEST(se.begin() != se.cend()); + } + + // + // iterator + // + + { + url u = u0; + auto se = u.segments(); + auto const& cs(se); + + segments::iterator it = se.begin(); + BOOST_TEST(*it == "path"); + BOOST_TEST(*++it == "to"); + BOOST_TEST(*it++ == "to"); + BOOST_TEST(*it-- == "the"); + BOOST_TEST(*it == "to"); + BOOST_TEST(*--it == "path"); + BOOST_TEST(it == se.begin()); + BOOST_TEST(it != se.end()); + + BOOST_TEST(*(it += 1) == "to"); + BOOST_TEST(*(it + 1) == "the"); + BOOST_TEST(*(1 + it) == "the"); + BOOST_TEST(*(it -= 1) == "path"); + it += 2; + BOOST_TEST(*(it - 1) == "to"); + --it; + BOOST_TEST(it - se.begin() == 1); + BOOST_TEST(it - se.cbegin() == 1); + BOOST_TEST(se.end() - it == 3); + BOOST_TEST(se.cend() - it == 3); + + BOOST_TEST(it[0] == "to"); + BOOST_TEST(it[1] == "the") +; + BOOST_TEST(it < se.end()); + BOOST_TEST(it < se.cend()); + BOOST_TEST(it < cs.end()); + BOOST_TEST(it <= se.end()); + BOOST_TEST(it <= se.cend()); + BOOST_TEST(it <= cs.end()); + BOOST_TEST(it > se.begin()); + BOOST_TEST(it > se.cbegin()); + BOOST_TEST(it > cs.begin()); + BOOST_TEST(it >= se.begin()); + BOOST_TEST(it >= se.cbegin()); + BOOST_TEST(it >= cs.begin()); + BOOST_TEST(it != se.begin()); + BOOST_TEST(it != se.cbegin()); + BOOST_TEST(it != cs.begin()); + + segments::const_iterator cit = + cs.cbegin(); + BOOST_TEST(*cit == "path"); + BOOST_TEST(*++cit == "to"); + BOOST_TEST(*cit++ == "to"); + BOOST_TEST(*cit-- == "the"); + BOOST_TEST(*cit == "to"); + BOOST_TEST(*--cit == "path"); + BOOST_TEST(cit == se.begin()); + BOOST_TEST(cit != se.end()); + + BOOST_TEST(*(cit += 1) == "to"); + BOOST_TEST(*(cit + 1) == "the"); + BOOST_TEST(*(1 + cit) == "the"); + BOOST_TEST(*(cit -= 1) == "path"); + cit += 2; + BOOST_TEST(*(cit - 1) == "to"); + --cit; + BOOST_TEST(cit - se.begin() == 1); + BOOST_TEST(cit - se.cbegin() == 1); + BOOST_TEST(se.end() - cit == 3); + BOOST_TEST(se.cend() - cit == 3); + + BOOST_TEST(cit[0] == "to"); + BOOST_TEST(cit[1] == "the") +; + BOOST_TEST(cit < se.end()); + BOOST_TEST(cit < se.cend()); + BOOST_TEST(cit < cs.end()); + BOOST_TEST(cit <= se.end()); + BOOST_TEST(cit <= se.cend()); + BOOST_TEST(cit <= cs.end()); + BOOST_TEST(cit > se.begin()); + BOOST_TEST(cit > se.cbegin()); + BOOST_TEST(cit > cs.begin()); + BOOST_TEST(cit >= se.begin()); + BOOST_TEST(cit >= se.cbegin()); + BOOST_TEST(cit >= cs.begin()); + BOOST_TEST(cit != se.begin()); + BOOST_TEST(cit != se.cbegin()); + BOOST_TEST(cit != cs.begin()); + + // heterogeneous assignment + cit = it; + BOOST_TEST(cit == it); + ++it; + BOOST_TEST(cit != it); + BOOST_TEST(it > cit); + BOOST_TEST(cit < it); + } + + } + + void + testCapacity() + { + url_view const u0 = parse_uri( + "x://y/path/to/the/file.txt"); + + // empty + { + url u = u0; + auto se = u.segments(p_.allocator()); + auto const& cs = se; + + BOOST_TEST(! se.empty()); + BOOST_TEST(! cs.empty()); + } + + // size + { + url u = u0; + auto se = u.segments(p_.allocator()); + auto const& cs = se; + + BOOST_TEST(se.size() == 4); + BOOST_TEST(cs.size() == 4); + } + } + + void + testModifiers() + { + // clear + { + url u = parse_uri("x://y/path/to/the/file.txt"); + auto se = u.segments(p_.allocator()); + + BOOST_TEST(! se.empty()); + BOOST_TEST(se.size() == 4); + se.clear(); + BOOST_TEST(se.empty()); + BOOST_TEST(se.size() == 0); + BOOST_TEST(u.encoded_path() == ""); + BOOST_TEST(u.encoded_url() == "x://y"); + } + + // insert( const_iterator, string_view ) + { + url u = parse_uri("x://y/path/file.txt?q#f"); + auto se = u.segments(p_.allocator()); + auto const& cs(se); + + BOOST_TEST(se.size() == 2); + auto it = + se.insert(se.begin() + 1, "to"); + BOOST_TEST(se.size() == 3); + BOOST_TEST(u.encoded_path() == "/path/to/file.txt"); + BOOST_TEST(u.encoded_url() == "x://y/path/to/file.txt?q#f"); + BOOST_TEST(*it == "to"); + + it = se.insert(cs.end(), ""); + BOOST_TEST(se.size() == 4); + BOOST_TEST(u.encoded_path() == "/path/to/file.txt/"); + BOOST_TEST(u.encoded_url() == "x://y/path/to/file.txt/?q#f"); + BOOST_TEST(*it == ""); + BOOST_TEST(it[-1] == "file.txt"); + + it = se.insert(se.cbegin(), "etc"); + BOOST_TEST(se.size() == 5); + BOOST_TEST(u.encoded_path() == "/etc/path/to/file.txt/"); + BOOST_TEST(u.encoded_url() == "x://y/etc/path/to/file.txt/?q#f"); + BOOST_TEST(*it == "etc"); + } + + { + // rootless + url u = parse_uri("x:path/file.txt?q#f"); + auto se = u.segments(p_.allocator()); + auto const& cs(se); + + BOOST_TEST(se.size() == 2); + auto it = + se.insert(se.begin() + 1, "to"); + BOOST_TEST(se.size() == 3); + BOOST_TEST(u.encoded_path() == "path/to/file.txt"); + BOOST_TEST(u.encoded_url() == "x:path/to/file.txt?q#f"); + BOOST_TEST(*it == "to"); + + it = se.insert(cs.end(), ""); + BOOST_TEST(se.size() == 4); + BOOST_TEST(u.encoded_path() == "path/to/file.txt/"); + BOOST_TEST(u.encoded_url() == "x:path/to/file.txt/?q#f"); + BOOST_TEST(*it == ""); + BOOST_TEST(it[-1] == "file.txt"); + + it = se.insert(se.cbegin(), "etc"); + BOOST_TEST(se.size() == 5); + BOOST_TEST(u.encoded_path() == "etc/path/to/file.txt/"); + BOOST_TEST(u.encoded_url() == "x:etc/path/to/file.txt/?q#f"); + BOOST_TEST(*it == "etc"); + } + + // insert( const_iterator, FwdIt, FwdIt ) + { + url u = parse_uri("x://y/path/file.txt?q#f"); + auto se = u.segments(p_.allocator()); + auto const& cs(se); + + std::initializer_list init = {"to", "the" }; + auto it = se.insert( + se.begin() + 1, init.begin(), init.end()); + BOOST_TEST(cs.size() == 4); + BOOST_TEST(*it == "to"); + BOOST_TEST(u.encoded_path() == "/path/to/the/file.txt"); + BOOST_TEST(u.encoded_url() == "x://y/path/to/the/file.txt?q#f"); + + // empty range + it = se.insert(se.begin() + 1, + init.begin(), init.begin()); + BOOST_TEST(u.encoded_path() == "/path/to/the/file.txt"); + BOOST_TEST(it == se.begin() + 1); + } + { + // rootless + url u = parse_uri("x:the/file.txt?q#f"); + auto se = u.segments(p_.allocator()); + auto const& cs(se); + + std::initializer_list init = {"path", "to" }; + auto it = se.insert( + se.begin(), init.begin(), init.end()); + BOOST_TEST(cs.size() == 4); + BOOST_TEST(*it == "path"); + BOOST_TEST(u.encoded_path() == "path/to/the/file.txt"); + BOOST_TEST(u.encoded_url() == "x:path/to/the/file.txt?q#f"); + + // empty range + it = se.insert(se.begin() + 1, + init.begin(), init.begin()); + BOOST_TEST(u.encoded_path() == "path/to/the/file.txt"); + BOOST_TEST(it == se.begin() + 1); + } + + // insert( const_iterator, initializer_list ) + { + url u = parse_uri("x://y/path/file.txt?q#f"); + auto se = u.segments(p_.allocator()); + auto const& cs(se); + + std::initializer_list< + string_view> init = { + "to", "the" }; + auto it = se.insert(se.begin() + 1, init); + BOOST_TEST(cs.size() == 4); + BOOST_TEST(*it == "to"); + BOOST_TEST(u.encoded_path() == "/path/to/the/file.txt"); + BOOST_TEST(u.encoded_url() == "x://y/path/to/the/file.txt?q#f"); + } + + // erase( const_iterator ) + { + url u = parse_uri("x://y/path/to/the/file.txt?q#f"); + auto se = u.segments(p_.allocator()); + + se.erase(se.begin() + 1); + BOOST_TEST(se.size() == 3); + BOOST_TEST(u.encoded_path() == "/path/the/file.txt"); + BOOST_TEST(u.encoded_url() == "x://y/path/the/file.txt?q#f"); + + se.erase(se.begin()); + BOOST_TEST(se.size() == 2); + BOOST_TEST(u.encoded_path() == "/the/file.txt"); + BOOST_TEST(u.encoded_url() == "x://y/the/file.txt?q#f"); + + se.erase(se.end() - 1); + BOOST_TEST(se.size() == 1); + BOOST_TEST(u.encoded_path() == "/the"); + BOOST_TEST(u.encoded_url() == "x://y/the?q#f"); + + se.erase(se.begin()); + BOOST_TEST(se.empty()); + BOOST_TEST(u.encoded_path().empty()); + BOOST_TEST(u.encoded_url() == "x://y?q#f"); + } + + // erase( const_iterator, const_iterator ) + { + url u = parse_uri("x://y/home/etc/path/to/the/file.txt?q#f"); + auto se = u.segments(p_.allocator()); + + se.erase(se.begin(), se.begin() + 2); + BOOST_TEST(u.encoded_path() == "/path/to/the/file.txt"); + BOOST_TEST(u.encoded_url() == "x://y/path/to/the/file.txt?q#f"); + + se.erase(se.begin(), se.end()); + BOOST_TEST(u.encoded_path() == ""); + BOOST_TEST(u.encoded_url() == "x://y?q#f"); + } + + // push_back + { + // VFALCO TODO + } + + // pop_back + { + url u = parse_uri("x://y/path/to/file.txt?q#f"); + auto se = u.segments(p_.allocator()); + + BOOST_TEST(se.size() == 3); + se.pop_back(); + BOOST_TEST(se.size() == 2); + BOOST_TEST(u.encoded_path() == "/path/to"); + BOOST_TEST(u.encoded_url() == "x://y/path/to?q#f"); + se.pop_back(); + BOOST_TEST(se.size() == 1); + BOOST_TEST(u.encoded_path() == "/path"); + BOOST_TEST(u.encoded_url() == "x://y/path?q#f"); + se.pop_back(); + BOOST_TEST(se.size() == 0); + BOOST_TEST(u.encoded_path().empty()); + BOOST_TEST(u.encoded_url() == "x://y?q#f"); + } + } + + void + testConversion() + { + // reference + { + url u = parse_uri("x://y/path/to/file.txt?q#f"); + auto se = u.segments(p_.allocator()); + + auto r = se[2]; + BOOST_TEST(r == "file.txt"); + BOOST_TEST("file.txt" == r); + BOOST_TEST("path" != r); + } + + // const_reference + { + url u = parse_uri("x://y/path/to/file.txt?q#f"); + auto se = u.segments(p_.allocator()); + auto const& cs(se); + + auto cr = cs[1]; + BOOST_TEST(cr == "to"); + BOOST_TEST("to" == cr); + BOOST_TEST("path" != cr); + // force operator std::string() + + auto r = se[2]; + + // construct from reference + segments::const_reference cr2(r); + BOOST_TEST(cr2 == r); + BOOST_TEST(r == cr2); + BOOST_TEST(cr2 == "file.txt"); + BOOST_TEST("file.txt" == cr2); + + segments::const_reference cr3(se[0]); + BOOST_TEST(cr3 != r); + BOOST_TEST(r != cr3); + } + } + + void + testAssign() + { + // assign stringish to reference + { + url u = parse_relative_ref("/"); + auto se = + u.segments(p_.allocator()); + + *se.begin() = string_view(); + *se.begin() = std::string(); + *se.begin() = "x"; + } + { + url u = parse_relative_ref( + "/path/to/file.txt"); + auto se = u.segments(p_.allocator()); + + BOOST_TEST(se.front() == "path"); + BOOST_TEST(se.back() == "file.txt"); + + se.front() = "x"; + se[1] = "p_"; + se[2] = "i.htm"; + se[0] = "path"; + BOOST_TEST(u.encoded_url() == + "/path/p_/i.htm"); + se[1] = "to"; + se[2] = "file.txt"; + se[1] = se[2]; + se[0] = se[2]; + BOOST_TEST(u.encoded_url() == + "/file.txt/file.txt/file.txt"); + } +#if 0 + // VFALCO BROKEN + { + url u = parse_relative_ref( + "/path/to/file.txt"); + segments seg = + u.segments(p_.allocator()); + std::reverse( + seg.begin(), seg.end()); + BOOST_TEST(u.encoded_url() == + "/file.txt/to/path"); + seg[1] = ""; + BOOST_TEST(u.encoded_url() == + "/file.txt//path"); + } +#endif + + // assign const_reference to reference + { + url u = parse_relative_ref( + "/path/to/file.txt"); + auto se = u.segments(p_.allocator()); + auto const& cs(se); + + se[0] = cs[2]; + BOOST_TEST(u.encoded_url() == "/file.txt/to/file.txt"); + } + + // grammar compliance + { + // path-noscheme + url u = parse_relative_ref("y?q#f"); + u.segments(p_.allocator())[0] = "a:b"; + BOOST_TEST(u.encoded_path() == "./a:b"); + BOOST_TEST(u.encoded_url() == "./a:b?q#f"); + } + { + // path-absolute + url u = parse_relative_ref("/a/b/c?q#f"); + u.segments(p_.allocator()) = { "", "" }; + BOOST_TEST(u.encoded_path() == "/.//"); + BOOST_TEST(u.encoded_url() == "/.//?q#f"); + } + } + + void + testAlgorithms() + { + // these should compile + url u; + { + std::stringstream ss; + auto seg = + u.segments(p_.allocator()); + std::copy( + seg.begin(), + seg.end(), + std::ostream_iterator< + string_type>( ss ) ); + } + { + auto seg = + u.segments(p_.allocator()); + (void)std::remove( + seg.begin(), + seg.end(), + "" ); + } + { + auto seg = + u.segments(p_.allocator()); + (void)std::remove( + seg.begin(), + seg.end(), + std::string("") ); + } + } + + void + run() + { + testMembers(); + testElementAccess(); + testIterators(); + testCapacity(); + testModifiers(); + + testConversion(); + testAssign(); + testAlgorithms(); + + { + + } + } +}; + +TEST_SUITE( + segments_test, + "boost.url.segments"); + +} // urls +} // boost diff --git a/test/unit/segments_encoded.cpp b/test/unit/segments_encoded.cpp index d07b7793..2f61b812 100644 --- a/test/unit/segments_encoded.cpp +++ b/test/unit/segments_encoded.cpp @@ -23,8 +23,6 @@ namespace boost { namespace urls { -//BOOST_ASSERT(std::_Is_string_view_ish::value); - //------------------------------------------------ class segments_encoded_test @@ -175,32 +173,22 @@ public: url_view const u0 = parse_relative_ref( "/path/to/the/file.txt"); - { - using T1 = segments_encoded::iterator; - using T2 = segments_encoded::const_iterator; - using T3 = segments_encoded::reference; - using T4 = segments_encoded::const_reference; - using T5 = segments_encoded::value_type; - using T6 = segments_encoded::size_type; - using T7 = segments_encoded::difference_type; - } - // at { url u = u0; segments_encoded se = u.encoded_segments(); segments_encoded const& cs = se; - BOOST_TEST(string_view(se.at(0)) == "path"); - BOOST_TEST(string_view(se.at(1)) == "to"); - BOOST_TEST(string_view(se.at(2)) == "the"); - BOOST_TEST(string_view(se.at(3)) == "file.txt"); + BOOST_TEST(se.at(0) == "path"); + BOOST_TEST(se.at(1) == "to"); + BOOST_TEST(se.at(2) == "the"); + BOOST_TEST(se.at(3) == "file.txt"); BOOST_TEST_THROWS(se.at(4), std::out_of_range); - BOOST_TEST(string_view(cs.at(0)) == "path"); - BOOST_TEST(string_view(cs.at(1)) == "to"); - BOOST_TEST(string_view(cs.at(2)) == "the"); - BOOST_TEST(string_view(cs.at(3)) == "file.txt"); + BOOST_TEST(cs.at(0) == "path"); + BOOST_TEST(cs.at(1) == "to"); + BOOST_TEST(cs.at(2) == "the"); + BOOST_TEST(cs.at(3) == "file.txt"); BOOST_TEST_THROWS(cs.at(4), std::out_of_range); // assign @@ -218,15 +206,15 @@ public: segments_encoded se = u.encoded_segments(); segments_encoded const& cs = se; - BOOST_TEST(string_view(se[0]) == "path"); - BOOST_TEST(string_view(se[1]) == "to"); - BOOST_TEST(string_view(se[2]) == "the"); - BOOST_TEST(string_view(se[3]) == "file.txt"); + BOOST_TEST(se[0] == "path"); + BOOST_TEST(se[1] == "to"); + BOOST_TEST(se[2] == "the"); + BOOST_TEST(se[3] == "file.txt"); - BOOST_TEST(string_view(cs[0]) == "path"); - BOOST_TEST(string_view(cs[1]) == "to"); - BOOST_TEST(string_view(cs[2]) == "the"); - BOOST_TEST(string_view(cs[3]) == "file.txt"); + BOOST_TEST(cs[0] == "path"); + BOOST_TEST(cs[1] == "to"); + BOOST_TEST(cs[2] == "the"); + BOOST_TEST(cs[3] == "file.txt"); // assign se[1] = "from"; @@ -277,23 +265,6 @@ public: void testIterators() { - auto const f = []( - segments_encoded::iterator i, - segments_encoded::const_iterator j, - std::ptrdiff_t n) - { - // check that these expressions - // compile without executing them - ++i; - i++; - i += n; - j + n; - n + j; - i -= n; - j - n; - j[n]; - }; - url_view const u0 = parse_uri( "x://y/path/to/the/file.txt"); @@ -415,8 +386,7 @@ public: BOOST_TEST(se.cend() - cit == 3); BOOST_TEST(cit[0] == "to"); - BOOST_TEST(cit[1] == "the") -; + BOOST_TEST(cit[1] == "the"); BOOST_TEST(cit < se.end()); BOOST_TEST(cit < se.cend()); BOOST_TEST(cit < cs.end()); diff --git a/test/unit/string.cpp b/test/unit/string.cpp index 19da8ceb..c6793b48 100644 --- a/test/unit/string.cpp +++ b/test/unit/string.cpp @@ -10,7 +10,7 @@ // Test that header file is self-contained. #include -#include +#include "test_suite.hpp" namespace boost { namespace urls { @@ -20,5 +20,23 @@ BOOST_STATIC_ASSERT(is_stringlike< std::string_view>::value); #endif +class string_test +{ +public: + void + run() + { + string_value sv("hello"); + auto sv2 = sv; + BOOST_TEST(sv2 == sv); + sv = {}; + BOOST_TEST(sv2 != sv); + } +}; + +TEST_SUITE( + string_test, + "boost.url.string"); + } // urls } // boost diff --git a/test/unit/url.cpp b/test/unit/url.cpp index 3e29fadf..09c37522 100644 --- a/test/unit/url.cpp +++ b/test/unit/url.cpp @@ -991,10 +991,6 @@ public: BOOST_TEST(u.encoded_segment(1) == "to"); BOOST_TEST(u.encoded_segment(2) == "file.txt"); BOOST_TEST(u.encoded_segment(3) == ""); - BOOST_TEST(u.encoded_segment(-1) == "file.txt"); - BOOST_TEST(u.encoded_segment(-2) == "to"); - BOOST_TEST(u.encoded_segment(-3) == "path"); - BOOST_TEST(u.encoded_segment(-4) == ""); } // set_encoded_path diff --git a/test/unit/url_view.cpp b/test/unit/url_view.cpp index 531218d1..4a44e167 100644 --- a/test/unit/url_view.cpp +++ b/test/unit/url_view.cpp @@ -569,10 +569,6 @@ public: BOOST_TEST(u.encoded_segment(1) == "to"); BOOST_TEST(u.encoded_segment(2) == "file.txt"); BOOST_TEST(u.encoded_segment(3) == ""); - BOOST_TEST(u.encoded_segment(-1) == "file.txt"); - BOOST_TEST(u.encoded_segment(-2) == "to"); - BOOST_TEST(u.encoded_segment(-3) == "path"); - BOOST_TEST(u.encoded_segment(-4) == ""); } }