diff --git a/doc/HelpCard.odg b/doc/HelpCard.odg index bc0e09e4..d7afa2a7 100644 Binary files a/doc/HelpCard.odg and b/doc/HelpCard.odg differ diff --git a/doc/images/HelpCard.svg b/doc/images/HelpCard.svg index a27253f4..4e32452c 100644 --- a/doc/images/HelpCard.svg +++ b/doc/images/HelpCard.svg @@ -381,7 +381,7 @@ - Pathsegmentssegments_encodedsegments_encoded_viewsegments_view + Pathsegments_encoded_refsegments_encoded_viewsegments_refsegments_viewparse_path diff --git a/doc/qbk/0.main.qbk b/doc/qbk/0.main.qbk index e4638c1b..7def120f 100644 --- a/doc/qbk/0.main.qbk +++ b/doc/qbk/0.main.qbk @@ -81,9 +81,9 @@ [def __query_param_view__ [link url.ref.boost__urls__query_param_view `query_param_view`]] [def __result__ [link url.ref.boost__urls__result `result`]] [def __segments__ [link url.ref.boost__urls__segments `segments`]] -[def __segments_view__ [link url.ref.boost__urls__segments_view `segments_view`]] [def __segments_encoded__ [link url.ref.boost__urls__segments_encoded `segments_encoded`]] -[def __segments_encoded_view__ [link url.ref.boost__urls__segments_encoded_view `segments_encoded_view`]] +[def __segments_encoded_ref__ [link url.ref.boost__urls__segments_encoded_ref `segments_encoded_ref`]] +[def __segments_ref__ [link url.ref.boost__urls__segments_ref `segments_ref`]] [def __static_url__ [link url.ref.boost__urls__static_url `static_url`]] [def __string_view__ [link url.ref.boost__urls__string_view `string_view`]] [def __url__ [link url.ref.boost__urls__url `url`]] diff --git a/doc/qbk/3.4.path.qbk b/doc/qbk/3.4.path.qbk index acd125ec..bfecd351 100644 --- a/doc/qbk/3.4.path.qbk +++ b/doc/qbk/3.4.path.qbk @@ -39,7 +39,7 @@ are represented using containers modeling bidirectional ranges. For example the member function [link url.ref.boost__urls__url_view_base.encoded_segments `encoded_segments`] returns a container called -[link url.ref.boost__urls__segments_encoded_view `segments_encoded_view`] +[link url.ref.boost__urls__segments_encoded_ref `segments_encoded_ref`] which may be iterated, and references the underlying character buffer without taking ownership. Here we define the function `segs` which returns a `std::list` formed by appending each segment in the path: diff --git a/doc/qbk/quickref.xml b/doc/qbk/quickref.xml index 592d4fdf..9481ca08 100644 --- a/doc/qbk/quickref.xml +++ b/doc/qbk/quickref.xml @@ -62,10 +62,10 @@ params_view params_encoded_view pct_string_view - segments_const_view - segments_const_encoded_view - segments_encoded_view segments_view + segments_encoded_ref + segments_encoded_view + segments_ref diff --git a/example/route/route.cpp b/example/route/route.cpp index 63f394e7..4cfe90ab 100644 --- a/example/route/route.cpp +++ b/example/route/route.cpp @@ -19,7 +19,7 @@ #include -#include +#include #include #include #include diff --git a/include/boost/url.hpp b/include/boost/url.hpp index 5d23d401..9dfe2cd7 100644 --- a/include/boost/url.hpp +++ b/include/boost/url.hpp @@ -14,6 +14,7 @@ #include #include +#include #include #include #include @@ -21,20 +22,22 @@ #include #include #include +#include #include -#include #include #include +#include #include #include -#include -#include +#include #include #include -#include -#include -#include +#include #include +#include +#include +#include +#include #include #include #include diff --git a/include/boost/url/detail/any_params_iter.hpp b/include/boost/url/detail/any_params_iter.hpp index 3745f09f..5a4c7f82 100644 --- a/include/boost/url/detail/any_params_iter.hpp +++ b/include/boost/url/detail/any_params_iter.hpp @@ -10,12 +10,9 @@ #ifndef BOOST_URL_DETAIL_ANY_PARAMS_ITER_HPP #define BOOST_URL_DETAIL_ANY_PARAMS_ITER_HPP -#include #include #include #include -#include -#include #include #include #include @@ -56,6 +53,10 @@ public: } }; +//------------------------------------------------ +// +// any_params_iter +// //------------------------------------------------ /* An iterator to a type-erased, @@ -132,6 +133,10 @@ public: char const* end) noexcept = 0; }; +//------------------------------------------------ +// +// query_iter +// //------------------------------------------------ // A string of plain query params @@ -157,85 +162,9 @@ private: }; //------------------------------------------------ - -// Validating and copying from -// a string of encoded params_view -class params_encoded_iter_base -{ -protected: - BOOST_URL_DECL - static - bool - measure_impl( - param_pct_view const& v, - std::size_t& n, - error_code& ec) noexcept; - - BOOST_URL_DECL - static - void - copy_impl( - char*& dest, - char const* end, - param_pct_view const& v) noexcept; -}; - -// A range of encoded query params_view -template -struct params_encoded_iter - : any_params_iter - , private params_encoded_iter_base -{ - BOOST_STATIC_ASSERT( - std::is_convertible< - typename std::iterator_traits< - FwdIt>::reference, - param_pct_view>::value); - - params_encoded_iter( - FwdIt first, - FwdIt last) noexcept - : any_params_iter( - first == last) - , it0_(first) - , it_(first) - , end_(last) - { - } - -private: - FwdIt it0_; - FwdIt it_; - FwdIt end_; - - void - rewind() noexcept override - { - it_ = it0_; - } - - bool - measure( - std::size_t& n, - error_code& ec) noexcept override - { - if(it_ == end_) - return false; - return measure_impl( - *it_++, n, ec); - } - - void - copy( - char*& dest, - char const* end - ) noexcept override - { - copy_impl( - dest, end, *it_++); - } -}; - +// +// params_iter +// //------------------------------------------------ class params_iter_base @@ -300,8 +229,7 @@ private: { if(it_ == end_) return false; - measure_impl( - param_view(*it_++), n); + measure_impl(*it_++, n); return true; } @@ -314,6 +242,100 @@ private: } }; +//------------------------------------------------ +// +// params_encoded_iter +// +//------------------------------------------------ + +// Validating and copying from +// a string of encoded params +class params_encoded_iter_base +{ +protected: + BOOST_URL_DECL + static + bool + measure_impl( + param_pct_view const& v, + std::size_t& n, + error_code& ec) noexcept; + + BOOST_URL_DECL + static + void + copy_impl( + char*& dest, + char const* end, + param_view const& v) noexcept; +}; + +// A range of encoded query params_view +template +struct params_encoded_iter + : any_params_iter + , private params_encoded_iter_base +{ + BOOST_STATIC_ASSERT( + std::is_convertible< + typename std::iterator_traits< + FwdIt>::reference, + param_pct_view>::value); + + BOOST_STATIC_ASSERT( + std::is_convertible< + typename std::iterator_traits< + FwdIt>::reference, + param_view>::value); + + params_encoded_iter( + FwdIt first, + FwdIt last) noexcept + : any_params_iter( + first == last) + , it0_(first) + , it_(first) + , end_(last) + { + } + +private: + FwdIt it0_; + FwdIt it_; + FwdIt end_; + + void + rewind() noexcept override + { + it_ = it0_; + } + + bool + measure( + std::size_t& n, + error_code& ec) noexcept override + { + if(it_ == end_) + return false; + return measure_impl( + *it_++, n, ec); + } + + void + copy( + char*& dest, + char const* end + ) noexcept override + { + copy_impl( + dest, end, *it_++); + } +}; + +//------------------------------------------------ +// +// param_value_iter +// //------------------------------------------------ // An iterator which outputs @@ -344,6 +366,10 @@ private: void copy(char*&, char const*) noexcept override; }; +//------------------------------------------------ +// +// param_encoded_value_iter +// //------------------------------------------------ // An iterator which outputs @@ -376,15 +402,6 @@ private: //------------------------------------------------ -template -params_encoded_iter -make_params_encoded_iter( - FwdIt first, FwdIt last) -{ - return params_encoded_iter< - FwdIt>(first, last); -} - template params_iter make_params_iter( @@ -394,10 +411,14 @@ make_params_iter( FwdIt>(first, last); } -bool -ci_decoded_key_equal( - decode_view key, - string_view match) noexcept; +template +params_encoded_iter +make_params_encoded_iter( + FwdIt first, FwdIt last) +{ + return params_encoded_iter< + FwdIt>(first, last); +} } // detail } // urls diff --git a/include/boost/url/detail/any_path_iter.hpp b/include/boost/url/detail/any_path_iter.hpp deleted file mode 100644 index fed897d6..00000000 --- a/include/boost/url/detail/any_path_iter.hpp +++ /dev/null @@ -1,298 +0,0 @@ -// -// 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_ANY_PATH_ITER_HPP -#define BOOST_URL_DETAIL_ANY_PATH_ITER_HPP - -#include -#include -#include - -namespace boost { -namespace urls { -namespace detail { - -struct BOOST_SYMBOL_VISIBLE - any_path_iter -{ - string_view front; - - BOOST_URL_DECL - virtual - ~any_path_iter() noexcept = 0; - - virtual - bool - measure( - std::size_t& n, - error_code& ec) noexcept = 0; - - virtual - void - copy( - char*& dest, - char const* end) noexcept = 0; -}; - -//------------------------------------------------ - -// iterates segments in an -// encoded path string -class BOOST_SYMBOL_VISIBLE - enc_path_iter - : public any_path_iter -{ - std::size_t n_; - char const* p_; - char const* end_; - - void - increment() noexcept; - -public: - explicit - enc_path_iter( - string_view s) noexcept; - - bool - measure( - std::size_t& n, - error_code& ec) noexcept override; - - void - copy( - char*& dest, - char const* end) noexcept override; -}; - -//------------------------------------------------ - -// iterates segments in an -// plain path string -class BOOST_SYMBOL_VISIBLE - plain_path_iter : - public any_path_iter -{ - std::size_t n_; - char const* p_; - char const* end_; - - void - increment() noexcept; - -public: - explicit - plain_path_iter( - string_view s) noexcept; - - bool - measure( - std::size_t& n, - error_code& ec) noexcept override; - - void - copy( - char*& dest, - char const* end) noexcept override; -}; - -//------------------------------------------------ - -// iterates segments in an -// plain path string -class BOOST_SYMBOL_VISIBLE - view_path_iter : - public any_path_iter -{ - std::size_t n_; - decode_view::const_iterator p_; - decode_view::const_iterator end_; - bool done_{false}; - - void - increment() noexcept; - -public: - explicit - view_path_iter( - decode_view s) noexcept; - - bool - measure( - std::size_t& n, - error_code& ec) noexcept override; - - void - copy( - char*& dest, - char const* end) noexcept override; -}; - -//------------------------------------------------ - -class enc_segs_iter_base -{ -protected: - BOOST_URL_DECL - static - bool - measure_impl( - string_view s, - std::size_t& n, - error_code& ec) 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 enc_segs_iter - : public any_path_iter - , public enc_segs_iter_base -{ - FwdIt it_; - FwdIt end_; - -public: - enc_segs_iter( - FwdIt first, - FwdIt last) noexcept - : it_(first) - , end_(last) - { - if (it_ != end_) - front = *first; - } - - bool - measure( - std::size_t& n, - error_code& ec - ) noexcept override - { - if(it_ == end_) - return false; - if(! measure_impl( - *it_, n, ec)) - return false; - ++it_; - return true; - } - - void - copy( - char*& dest, - char const* end - ) noexcept override - { - copy_impl(*it_, - dest, end); - ++it_; - } -}; - -//------------------------------------------------ - -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 a -// plain 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) - { - if (first != last) - front = *first; - } - - 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( - FwdIt first, FwdIt last) -{ - return 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 - -#endif diff --git a/include/boost/url/detail/any_segments_iter.hpp b/include/boost/url/detail/any_segments_iter.hpp new file mode 100644 index 00000000..d9d25f57 --- /dev/null +++ b/include/boost/url/detail/any_segments_iter.hpp @@ -0,0 +1,297 @@ +// +// 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_ANY_SEGMENTS_ITER_HPP +#define BOOST_URL_DETAIL_ANY_SEGMENTS_ITER_HPP + +#include +#include +#include +#include +#include + +namespace boost { +namespace urls { +namespace detail { + +class BOOST_SYMBOL_VISIBLE + any_segments_iter +{ + string_view* s_ = nullptr; + +protected: + explicit + any_segments_iter( + string_view* s = nullptr) noexcept + : s_(s) + { + } + +public: + string_view front; + + // Return the input string or nullptr + string_view* + input() const noexcept + { + return s_; + } + + // Rewind the iterator to the beginning + virtual + void + rewind() noexcept = 0; + + // Measure and increment the current + // element. n is increased by the + // encoded size. Returns false on + // end of range. + virtual bool measure( + std::size_t& n) noexcept = 0; + + // Copy and increment the current + // element. encoding is performed + // if needed. + virtual + void + copy( + char*& dest, + char const* end) noexcept = 0; +}; + +//------------------------------------------------ + +// iterates segments in an +// plain path string +struct BOOST_SYMBOL_VISIBLE + path_iter : + public any_segments_iter +{ + explicit + path_iter( + string_view s) noexcept; + +private: + string_view s_; + std::size_t n_ = 0; + char const* p_ = nullptr; + char const* p0_; + char const* end_; + + static string_view clean(string_view s) noexcept; + void increment() noexcept; + void rewind() noexcept override; + bool measure(std::size_t&) noexcept override; + void copy(char*&, char const*) noexcept override; +}; + +//------------------------------------------------ + +// iterates segments in an +// encoded path string +struct BOOST_SYMBOL_VISIBLE + path_encoded_iter + : public any_segments_iter +{ + explicit + path_encoded_iter( + pct_string_view s) noexcept; + +private: + string_view s_; + std::size_t n_ = 0; + char const* p_ = nullptr; + char const* p0_; + char const* end_; + + static string_view clean(pct_string_view s) noexcept; + void increment() noexcept; + void rewind() noexcept override; + bool measure(std::size_t&) noexcept override; + void copy(char*&, char const*) noexcept override; +}; + +//------------------------------------------------ +// +// segments_iter +// +//------------------------------------------------ + +class segments_iter_base +{ +protected: + BOOST_URL_DECL static void measure_impl( + string_view, std::size_t&) noexcept; + BOOST_URL_DECL static void copy_impl( + string_view, char*&, char const*) noexcept; +}; + +// iterates segments in a +// plain segment range +template +struct segments_iter + : any_segments_iter + , segments_iter_base +{ + BOOST_STATIC_ASSERT( + std::is_convertible< + typename std::iterator_traits< + FwdIt>::reference, + string_view>::value); + + segments_iter( + FwdIt first, + FwdIt last) noexcept + : it_(first) + , it0_(first) + , end_(last) + { + if (first != last) + front = *first; + } + +private: + FwdIt it_; + FwdIt it0_; + FwdIt end_; + + void + rewind() noexcept override + { + it_ = it0_; + } + + bool + measure( + std::size_t& n) 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_; + } +}; + +//------------------------------------------------ +// +// segments_encoded_iter +// +//------------------------------------------------ + +// Validating and copying from +// a string of encoded segments +class segments_encoded_iter_base +{ +protected: + BOOST_URL_DECL static bool measure_impl( + pct_string_view, std::size_t&) noexcept; + BOOST_URL_DECL static void copy_impl( + string_view, char*&, char const*) noexcept; +}; + +// iterates segments in an +// encoded segment range +template +struct segments_encoded_iter + : public any_segments_iter + , public segments_encoded_iter_base +{ + BOOST_STATIC_ASSERT( + std::is_convertible< + typename std::iterator_traits< + FwdIt>::reference, + pct_string_view>::value); + + BOOST_STATIC_ASSERT( + std::is_convertible< + typename std::iterator_traits< + FwdIt>::reference, + string_view>::value); + + segments_encoded_iter( + FwdIt first, + FwdIt last) noexcept + : it_(first) + , it0_(first) + , end_(last) + { + if (it_ != end_) + front = *first; + } + +private: + FwdIt it_; + FwdIt it0_; + FwdIt end_; + + void + rewind() noexcept override + { + it_ = it0_; + } + + bool + measure( + std::size_t& n) noexcept override + { + if(it_ == end_) + return false; + if(! measure_impl(*it_, n)) + return false; + ++it_; + return true; + } + + void + copy( + char*& dest, + char const* end + ) noexcept override + { + copy_impl(*it_, dest, end); + ++it_; + } +}; + +//------------------------------------------------ + +template +segments_iter +make_segments_iter( + FwdIt first, FwdIt last) +{ + return segments_iter< + FwdIt>(first, last); +} + +template +segments_encoded_iter +make_segments_encoded_iter( + FwdIt first, FwdIt last) +{ + return segments_encoded_iter< + FwdIt>(first, last); +} + +} // detail +} // urls +} // boost + +#endif diff --git a/include/boost/url/detail/config.hpp b/include/boost/url/detail/config.hpp index 3fdf7d2e..a038a37d 100644 --- a/include/boost/url/detail/config.hpp +++ b/include/boost/url/detail/config.hpp @@ -83,10 +83,6 @@ #define BOOST_URL_STRTOK_ARG(T, name) T&& name = {} #endif -#ifndef BOOST_URL_STACK_BYTES -#define BOOST_URL_STACK_BYTES 4096 -#endif - #if BOOST_WORKAROUND( BOOST_GCC_VERSION, < 80000 ) || \ BOOST_WORKAROUND( BOOST_CLANG_VERSION, < 30900 ) #define BOOST_URL_RETURN(x) return std::move((x)) diff --git a/include/boost/url/detail/encode.hpp b/include/boost/url/detail/encode.hpp index 17e0300e..51c8a0c9 100644 --- a/include/boost/url/detail/encode.hpp +++ b/include/boost/url/detail/encode.hpp @@ -13,6 +13,7 @@ #include #include +#include #include namespace boost { @@ -250,10 +251,10 @@ encode_unchecked( // escapes. Characters not in the // allowed set are escaped, and // escapes are passed through unchanged. - +// template std::size_t -re_encoded_size( +re_encoded_size_unchecked( string_view s, encode_opts const&, CharSet const& allowed) noexcept @@ -273,6 +274,13 @@ re_encoded_size( } else { + BOOST_ASSERT(end - it >= 3); + BOOST_ASSERT( + grammar::hexdig_value( + it[1]) >= 0); + BOOST_ASSERT( + grammar::hexdig_value( + it[2]) >= 0); n += 3; it += 3; } @@ -285,7 +293,7 @@ re_encoded_size( template std::size_t re_encode_unchecked( - char* dest, + char*& dest_, char const* const end, string_view s, encode_opts const& opt, @@ -310,9 +318,10 @@ re_encode_unchecked( *dest++ = hex[c&0xf]; }; (void)end; - std::size_t dn = 0; + auto dest = dest_; auto const dest0 = dest; auto const last = s.end(); + std::size_t dn = 0; auto it = s.begin(); if(opt.space_to_plus) { @@ -377,6 +386,7 @@ re_encode_unchecked( } } } + dest_ = dest; return dest - dest0 - dn; } diff --git a/include/boost/url/detail/impl/any_params_iter.ipp b/include/boost/url/detail/impl/any_params_iter.ipp index fe60377b..669e92be 100644 --- a/include/boost/url/detail/impl/any_params_iter.ipp +++ b/include/boost/url/detail/impl/any_params_iter.ipp @@ -128,86 +128,7 @@ increment() noexcept //------------------------------------------------ // -// params_encoded_iter_base -// -//------------------------------------------------ - -bool -params_encoded_iter_base:: -measure_impl( - param_pct_view const& v, - std::size_t& n, - error_code& ec) noexcept -{ - decode_opts opt; - opt.plus_to_space = true; - auto rv = detail::validate_encoding( - v.key, opt, query_chars); - if(! rv) - { - ec = rv.error(); - return false; - } - n += v.key.size(); - if(v.has_value) - { - rv = detail::validate_encoding( - v.value, opt, query_chars); - if(! rv) - { - ec = rv.error(); - return false; - } - n += 1 + v.value.size(); - } - return true; -} - -void -params_encoded_iter_base:: -copy_impl( - char*& dest, - char const* end, - param_pct_view const& v) noexcept -{ - (void)end; - { - // avoid self-copy - auto const kn = v.key.size(); - BOOST_ASSERT(end - kn >= dest); - if( v.key.data() != dest && - kn > 0) - { - std::memcpy( - dest, - v.key.data(), - kn); - } - dest += kn; - } - if(v.has_value) - { - BOOST_ASSERT( - end - 1 >= dest); - *dest++ = '='; - auto const vn = - v.value.size(); - BOOST_ASSERT( - end - vn >= dest); - if(vn > 0) - { - std::memcpy( - dest, - v.value.data(), - vn); - dest += vn; - } - } -} - -//------------------------------------------------ -// -// params_iter_base +// params_iter // //------------------------------------------------ @@ -254,6 +175,89 @@ copy_impl( } } +//------------------------------------------------ +// +// params_encoded_iter +// +//------------------------------------------------ + +bool +params_encoded_iter_base:: +measure_impl( + param_pct_view const& v, + std::size_t& n, + error_code& ec) noexcept +{ + decode_opts opt; + opt.plus_to_space = true; + auto rv = detail::validate_encoding( + v.key, opt, query_chars); + if(! rv) + { + ec = rv.error(); + return false; + } + n += v.key.size(); + if(v.has_value) + { + rv = detail::validate_encoding( + v.value, opt, query_chars); + if(! rv) + { + ec = rv.error(); + return false; + } + n += 1 + v.value.size(); + } + return true; +} + +void +params_encoded_iter_base:: +copy_impl( + char*& dest, + char const* end, + param_view const& v) noexcept +{ + (void)end; + { + // avoid self-copy + auto const kn = v.key.size(); + BOOST_ASSERT(end - kn >= dest); + if( v.key.data() != dest && + kn > 0) + { + std::memcpy( + dest, + v.key.data(), + kn); + } + dest += kn; + } + if(v.has_value) + { + BOOST_ASSERT( + end - 1 >= dest); + *dest++ = '='; + auto const vn = + v.value.size(); + BOOST_ASSERT( + end - vn >= dest); + if(vn > 0) + { + std::memcpy( + dest, + v.value.data(), + vn); + dest += vn; + } + } +} + +//------------------------------------------------ +// +// param_value_iter +// //------------------------------------------------ void @@ -298,6 +302,10 @@ copy(char*& it, char const* end) noexcept detail::param_value_chars); } +//------------------------------------------------ +// +// param_encoded_value_iter +// //------------------------------------------------ void @@ -354,30 +362,6 @@ copy(char*& it, char const* end) noexcept it += value_.size(); } -//------------------------------------------------ - -bool -ci_decoded_key_equal( - decode_view key, - string_view match) noexcept -{ - if( key.size() != - match.size()) - return false; - auto it0 = key.begin(); - auto it1 = match.begin(); - auto const end = match.end(); - while(it1 != end) - { - if( grammar::to_lower(*it0) != - grammar::to_lower(*it1)) - return false; - ++it0; - ++it1; - } - return true; -} - } // detail } // urls } // boost diff --git a/include/boost/url/detail/impl/any_path_iter.ipp b/include/boost/url/detail/impl/any_path_iter.ipp deleted file mode 100644 index 3d3d42de..00000000 --- a/include/boost/url/detail/impl/any_path_iter.ipp +++ /dev/null @@ -1,328 +0,0 @@ - // -// 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_IMPL_ANY_PATH_ITER_IPP -#define BOOST_URL_DETAIL_IMPL_ANY_PATH_ITER_IPP - -#include -#include -#include -#include -#include - -namespace boost { -namespace urls { -namespace detail { - -any_path_iter:: -~any_path_iter() noexcept = default; - -//------------------------------------------------ - -void -enc_path_iter:: -increment() noexcept -{ - p_ += n_; - if(p_ == end_) - { - p_ = nullptr; - return; - } - ++p_; - string_view s(p_, end_ - p_); - auto pos = s.find_first_of('/'); - if(pos != string_view::npos) - n_ = pos; - else - n_ = s.size(); -} - -enc_path_iter:: -enc_path_iter( - string_view s) noexcept - : end_(s.data() + s.size()) -{ - if(s.empty()) - { - n_ = 0; - p_ = nullptr; - return; - } - std::size_t pos; - if(s.starts_with('/')) - s.remove_prefix(1); - pos = s.find_first_of('/'); - p_ = s.data(); - if(pos != string_view::npos) - n_ = pos; - else - n_ = s.size(); - front = { p_, n_ }; -} - -bool -enc_path_iter:: -measure( - std::size_t& n, - error_code& ec) noexcept -{ - if(! p_) - return false; - string_view s(p_, n_); - auto rn = urls::decode(s, {}, pchars); - if( !rn ) - { - ec = rn.error(); - return false; - } - n += s.size(); - increment(); - return true; -} - -void -enc_path_iter:: -copy( - char*& dest, - char const* end) noexcept -{ - (void)end; - BOOST_ASSERT(static_cast< - std::size_t>( - end - dest) >= n_); - BOOST_ASSERT(p_ != nullptr); - if(n_ > 0) - { - std::memcpy( - dest, p_, n_); - dest += n_; - } - increment(); -} - -//------------------------------------------------ - -void -plain_path_iter:: -increment() noexcept -{ - p_ += n_; - if(p_ == end_) - { - p_ = nullptr; - return; - } - ++p_; - string_view s(p_, end_ - p_); - auto pos = s.find_first_of('/'); - if(pos != string_view::npos) - n_ = pos; - else - n_ = s.size(); -} - -plain_path_iter:: -plain_path_iter( - string_view s) noexcept - : end_(s.data() + s.size()) -{ - if(s.empty()) - { - n_ = 0; - p_ = nullptr; - return; - } - std::size_t pos; - if(s.starts_with('/')) - s.remove_prefix(1); - pos = s.find_first_of('/'); - p_ = s.data(); - if(pos != string_view::npos) - n_ = pos; - else - n_ = s.size(); - front = { p_, n_ }; -} - -bool -plain_path_iter:: -measure( - std::size_t& n, - error_code&) noexcept -{ - if(! p_) - return false; - string_view s(p_, n_); - n += urls::encoded_size( - s, {}, pchars); - increment(); - return true; -} - -void -plain_path_iter:: -copy( - char*& dest, - char const* end) noexcept -{ - BOOST_ASSERT(p_ != nullptr); - dest += encode( - dest, - end, - string_view(p_, n_), - {}, - pchars); - increment(); -} - -//------------------------------------------------ - -void -view_path_iter:: -increment() noexcept -{ - std::advance(p_, n_); - if(p_ == end_) - { - done_ = true; - return; - } - ++p_; - auto pos = p_; - n_ = 0; - while (pos != end_) - { - if (*pos == '/') - break; - ++pos; - ++n_; - } -} - -view_path_iter:: -view_path_iter( - decode_view s) noexcept - : n_(0) - , end_(s.end()) -{ - if(s.empty()) - { - p_ = s.end(); - done_ = true; - return; - } - p_ = s.begin(); - if (!s.empty() && s.front() == '/') - ++p_; - auto pos = p_; - while (pos != end_) - { - if (*pos == '/') - break; - ++pos; - ++n_; - } - front = { p_.base(), pos.base() }; -} - -bool -view_path_iter:: -measure( - std::size_t& n, - error_code&) noexcept -{ - if (done_) - return false; - auto it = p_; - auto end = std::next(p_, n_); - n += encoded_size_impl(it, end, {}, pchars); - increment(); - return true; -} - -void -view_path_iter:: -copy( - char*& dest, - char const* end) noexcept -{ - BOOST_ASSERT(!done_); - auto it = p_; - auto last = std::next(p_, n_); - dest += encode_impl( - dest, end, it, last, {}, pchars); - increment(); -} - -//------------------------------------------------ - -bool -enc_segs_iter_base:: -measure_impl( - string_view s, - std::size_t& n, - error_code& ec) noexcept -{ - auto rn = urls::decode(s, {}, pchars); - if( !rn ) - { - ec = rn.error(); - return false; - } - n += s.size(); - return true; -} - -void -enc_segs_iter_base:: -copy_impl( - string_view s, - char*& dest, - char const* end) noexcept -{ - (void)end; - BOOST_ASSERT(static_cast< - std::size_t>(end - dest) >= - s.size()); - if(! s.empty()) - { - std::memcpy(dest, - s.data(), s.size()); - dest += s.size(); - } -} - -//------------------------------------------------ - -void -plain_segs_iter_base:: -measure_impl( - string_view s, - std::size_t& n) noexcept -{ - n += encoded_size(s, {}, pchars); -} - -void -plain_segs_iter_base:: -copy_impl( - string_view s, - char*& dest, - char const* end) noexcept -{ - dest += encode( - dest, end, s, {}, pchars); -} - -} // detail -} // urls -} // boost - -#endif diff --git a/include/boost/url/detail/impl/any_segments_iter.ipp b/include/boost/url/detail/impl/any_segments_iter.ipp new file mode 100644 index 00000000..27bcb0ab --- /dev/null +++ b/include/boost/url/detail/impl/any_segments_iter.ipp @@ -0,0 +1,297 @@ +// +// 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_IMPL_ANY_SEGMENTS_ITER_IPP +#define BOOST_URL_DETAIL_IMPL_ANY_SEGMENTS_ITER_IPP + +#include +#include +#include +#include +#include + +namespace boost { +namespace urls { +namespace detail { + +//------------------------------------------------ +// +// path_iter +// +//------------------------------------------------ + +path_iter:: +path_iter( + string_view s) noexcept + : any_segments_iter(&s_) + , s_(clean(s)) + , p0_(s.data()) + , end_(s.data() + s.size()) +{ + rewind(); + front = { p_, n_ }; +} + +string_view +path_iter:: +clean(string_view s) noexcept +{ + // prevent null + if(s.data() == nullptr) + return string_view("", 0); + return s; +} + +void +path_iter:: +increment() noexcept +{ + p_ += n_; + if(p_ == end_) + { + p_ = nullptr; + return; + } + ++p_; + string_view s(p_, end_ - p_); + auto pos = s.find_first_of('/'); + if(pos != string_view::npos) + n_ = pos; + else + n_ = s.size(); +} + +void +path_iter:: +rewind() noexcept +{ + if(p0_ != end_) + { + auto p0 = p0_; + if(*p0 == '/') + ++p0; + p_ = p0; + auto p = p0; + while(p != end_) + { + if(*p == '/') + break; + ++p; + } + n_ = p - p0; + } +} + +bool +path_iter:: +measure( + std::size_t& n) noexcept +{ + if(! p_) + return false; + string_view s(p_, n_); + n += urls::encoded_size( + s, {}, pchars); + increment(); + return true; +} + +void +path_iter:: +copy( + char*& dest, + char const* end) noexcept +{ + BOOST_ASSERT(p_ != nullptr); + dest += encode( + dest, + end, + string_view(p_, n_), + {}, + pchars); + increment(); +} + +//------------------------------------------------ +// +// path_encoded_iter +// +//------------------------------------------------ + +path_encoded_iter:: +path_encoded_iter( + pct_string_view s) noexcept + : any_segments_iter(&s_) + , s_(clean(s)) + , p0_(s.data()) + , end_(s.data() + s.size()) +{ + rewind(); + front = { p_, n_ }; +} + +string_view +path_encoded_iter:: +clean(pct_string_view s) noexcept +{ + // prevent null + if(s.data() == nullptr) + return string_view("", 0); + return s; +} + +void +path_encoded_iter:: +increment() noexcept +{ + p_ += n_; + if(p_ == end_) + { + p_ = nullptr; + return; + } + ++p_; + string_view s(p_, end_ - p_); + auto pos = s.find_first_of('/'); + if(pos != string_view::npos) + n_ = pos; + else + n_ = s.size(); +} + +void +path_encoded_iter:: +rewind() noexcept +{ + if(p0_ != end_) + { + auto p0 = p0_; + if(*p0 == '/') + ++p0; + p_ = p0; + auto p = p0; + while(p != end_) + { + if(*p == '/') + break; + ++p; + } + n_ = p - p0; + } +} + +bool +path_encoded_iter:: +measure( + std::size_t& n) noexcept +{ + if(! p_) + return false; + string_view s(p_, n_); + encode_opts opt; + n += detail::re_encoded_size_unchecked( + s, + opt, + pchars); + increment(); + return true; +} + +void +path_encoded_iter:: +copy( + char*& dest, + char const* end) noexcept +{ + (void)end; + BOOST_ASSERT(static_cast< + std::size_t>( + end - dest) >= n_); + BOOST_ASSERT(p_ != nullptr); + if(n_ > 0) + { + string_view s(p_, n_); + encode_opts opt; + detail::re_encode_unchecked( + dest, + end, + s, + opt, + pchars); + } + increment(); +} + +//------------------------------------------------ +// +// segments_iter_base +// +//------------------------------------------------ + +void +segments_iter_base:: +measure_impl( + string_view s, + std::size_t& n) noexcept +{ + n += encoded_size(s, {}, pchars); +} + +void +segments_iter_base:: +copy_impl( + string_view s, + char*& dest, + char const* end) noexcept +{ + dest += encode( + dest, end, s, {}, pchars); +} + +//------------------------------------------------ +// +// segments_encoded_iter_base +// +//------------------------------------------------ + +bool +segments_encoded_iter_base:: +measure_impl( + pct_string_view s, + std::size_t& n) noexcept +{ + encode_opts opt; + n += detail::re_encoded_size_unchecked( + s, + opt, + pchars); + return true; +} + +void +segments_encoded_iter_base:: +copy_impl( + string_view s, + char*& dest, + char const* end) noexcept +{ + encode_opts opt; + detail::re_encode_unchecked( + dest, + end, + s, + opt, + pchars); +} + +} // detail +} // urls +} // boost + +#endif diff --git a/include/boost/url/detail/impl/segments_encoded_iterator_impl.ipp b/include/boost/url/detail/impl/segments_encoded_iterator_impl.ipp deleted file mode 100644 index ca791874..00000000 --- a/include/boost/url/detail/impl/segments_encoded_iterator_impl.ipp +++ /dev/null @@ -1,131 +0,0 @@ -// -// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) -// Copyright (c) 2022 Alan de Freitas (alandefreitas@gmail.com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/CPPAlliance/url -// - -#ifndef BOOST_URL_DETAIL_IMPL_SEGMENTS_ENCODED_ITERATOR_IMPL_IPP -#define BOOST_URL_DETAIL_IMPL_SEGMENTS_ENCODED_ITERATOR_IMPL_IPP - -#include -#include -#include - -namespace boost { -namespace urls { -namespace detail { - -segments_encoded_iterator_impl:: -segments_encoded_iterator_impl( - string_view s, - std::size_t nseg) noexcept - : begin_(s.data()) - , pos_(s.data()) - , next_(s.data()) - , end_(s.data() + s.size()) -{ - if(nseg == 0) - { - next_ = nullptr; - return; - } - auto const n = path_prefix(s); - begin_ += n; - next_ += n; - pos_ += n; - auto const i = string_view( - begin_, s.size() - n - ).find_first_of('/'); - if(i != string_view::npos) - next_ += i; - else - next_ = end_; - s_ = string_view( - pos_, next_ - pos_); -} - -segments_encoded_iterator_impl:: -segments_encoded_iterator_impl( - string_view s, - std::size_t nseg, - int) noexcept - : i_(nseg) - , begin_(s.data()) - , pos_(s.data() + s.size()) - , end_(s.data() + s.size()) -{ - auto const n = path_prefix(s); - begin_ += n; -} - -void -segments_encoded_iterator_impl:: -increment() noexcept -{ - BOOST_ASSERT(next_ != nullptr); - ++i_; - pos_ = next_; - // "/" segment - auto rv = grammar::parse( - next_, end_, - detail::slash_segment_rule); - if( !rv ) - { - next_ = nullptr; - return; - } - s_ = *rv; -} - -void -segments_encoded_iterator_impl:: -decrement() noexcept -{ - BOOST_ASSERT(i_ != 0); - --i_; - if(i_ == 0) - { - next_ = pos_; - pos_ = begin_; - s_ = string_view( - pos_, next_ - pos_); - return; - } - while(--pos_ != begin_) - { - if(*pos_ != '/') - continue; - // "/" segment - next_ = pos_; - s_ = *grammar::parse( - next_, end_, - detail::slash_segment_rule); - return; - } - next_ = pos_; - if(*next_ == '/') - { - // "/" segment - s_ = *grammar::parse( - next_, end_, - detail::slash_segment_rule); - } - else - { - // segment-nz - s_ = *grammar::parse( - next_, end_, - detail::slash_segment_rule); - } -} - - -} // detail -} // url -} // boost - -#endif diff --git a/include/boost/url/detail/impl/segments_iter_impl.ipp b/include/boost/url/detail/impl/segments_iter_impl.ipp new file mode 100644 index 00000000..0249ade5 --- /dev/null +++ b/include/boost/url/detail/impl/segments_iter_impl.ipp @@ -0,0 +1,141 @@ +// +// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2022 Alan de Freitas (alandefreitas@gmail.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/CPPAlliance/url +// + +#ifndef BOOST_URL_DETAIL_IMPL_SEGMENTS_ITER_IMPL_IPP +#define BOOST_URL_DETAIL_IMPL_SEGMENTS_ITER_IMPL_IPP + +#include +#include +#include + +namespace boost { +namespace urls { +namespace detail { + +// begin +segments_iter_impl:: +segments_iter_impl( + detail::path_ref const& ref_) noexcept + : ref(ref_) +{ + pos = path_prefix(ref.string()); + auto const end = ref.end(); + char const* const p0 = + ref.data() + pos; + auto p = p0; + //dn = 0; + while(p != end) + { + if(*p == '/') + break; + if(*p != '%') + { + ++p; + continue; + } + p += 3; + dn += 2; + } + next = p - ref.data(); + dn = p - p0 - dn; + s_ = detail::make_pct_string_view( + p0, p - p0, dn); +} + +// end +segments_iter_impl:: +segments_iter_impl( + detail::path_ref const& ref_, + int) noexcept + : ref(ref_) + , pos(ref.size()) + , next(ref.size()) + , index(ref.nseg()) +{ +} + +void +segments_iter_impl:: +increment() noexcept +{ + BOOST_ASSERT( + index != ref.nseg()); + ++index; + pos = next; + if(index == ref.nseg()) + return; + // "/" segment + auto const end = ref.end(); + auto p = ref.data() + pos; + BOOST_ASSERT(p != end); + BOOST_ASSERT(*p == '/'); + ++p; + dn = 0; + auto const p0 = p; + while(p != end) + { + if(*p == '/') + break; + if(*p != '%') + { + ++p; + continue; + } + p += 3; + dn += 2; + } + next = p - ref.data(); + dn = p - p0 - dn; + s_ = detail::make_pct_string_view( + p0, p - p0, dn); +} + +void +segments_iter_impl:: +decrement() noexcept +{ + BOOST_ASSERT(index != 0); + --index; + if(index == 0) + { + next = pos; + pos = path_prefix(ref.string()); + s_ = string_view( + ref.data() + pos, + next - pos); + BOOST_ASSERT(! s_.ends_with('/')); + return; + } + auto const begin = ref.data() + + path_prefix(ref.string()); + next = pos; + auto p = ref.data() + next; + auto const p1 = p; + BOOST_ASSERT(p != begin); + dn = 0; + while(p != begin) + { + --p; + if(*p == '/') + break; + if(*p == '%') + dn += 2; + } + dn = p1 - p - dn; + pos = p - ref.data(); + s_ = detail::make_pct_string_view( + p + 1, p1 - p - 1, dn); +} + +} // detail +} // url +} // boost + +#endif diff --git a/include/boost/url/detail/impl/segments_iterator_impl.ipp b/include/boost/url/detail/impl/segments_iterator_impl.ipp deleted file mode 100644 index 25f26741..00000000 --- a/include/boost/url/detail/impl/segments_iterator_impl.ipp +++ /dev/null @@ -1,131 +0,0 @@ -// -// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) -// Copyright (c) 2022 Alan de Freitas (alandefreitas@gmail.com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/CPPAlliance/url -// - -#ifndef BOOST_URL_DETAIL_IMPL_SEGMENTS_ITERATOR_IMPL_IPP -#define BOOST_URL_DETAIL_IMPL_SEGMENTS_ITERATOR_IMPL_IPP - -#include -#include -#include - -namespace boost { -namespace urls { -namespace detail { - -segments_iterator_impl:: -segments_iterator_impl( - string_view s, - std::size_t nseg) noexcept - : begin_(s.data()) - , pos_(s.data()) - , next_(s.data()) - , end_(s.data() + s.size()) -{ - if(nseg == 0) - { - next_ = nullptr; - return; - } - auto const n = path_prefix(s); - begin_ += n; - next_ += n; - pos_ += n; - t_ = *grammar::parse( - next_, end_, segment_rule); -} - -segments_iterator_impl:: -segments_iterator_impl( - string_view s, - std::size_t nseg, - int) noexcept - : i_(nseg) - , begin_(s.data() + path_prefix(s)) - , pos_(s.data() + s.size()) - , end_(s.data() + s.size()) -{ -} - -decode_view -segments_iterator_impl:: -dereference() const noexcept -{ - decode_opts opt; - opt.plus_to_space = false; - return t_.decoded(opt); -} - - -void -segments_iterator_impl:: -increment() noexcept -{ - BOOST_ASSERT(next_ != nullptr); - ++i_; - pos_ = next_; - // "/" segment - auto rv = grammar::parse( - next_, end_, - detail::slash_segment_rule); - if(! rv ) - { - next_ = nullptr; - return; - } - t_ = *rv; -} - -void -segments_iterator_impl:: -decrement() noexcept -{ - BOOST_ASSERT(i_ != 0); - --i_; - if(i_ == 0) - { - next_ = begin_; - pos_ = begin_; - t_ = *grammar::parse( - next_, end_, segment_rule); - return; - } - while(--pos_ != begin_) - { - if(*pos_ != '/') - continue; - // "/" segment - next_ = pos_; - t_ = *grammar::parse( - next_, end_, - detail::slash_segment_rule); - return; - } - next_ = pos_; - if(*next_ == '/') - { - // "/" segment - t_ = *grammar::parse( - next_, end_, - detail::slash_segment_rule); - } - else - { - // segment-nz - t_ = *grammar::parse( - next_, end_, - detail::slash_segment_rule); - } -} - -} // detail -} // url -} // boost - -#endif diff --git a/include/boost/url/detail/impl/url_impl.ipp b/include/boost/url/detail/impl/url_impl.ipp index 633f655f..d187bdd2 100644 --- a/include/boost/url/detail/impl/url_impl.ipp +++ b/include/boost/url/detail/impl/url_impl.ipp @@ -150,6 +150,72 @@ apply_frag( decoded_[id_frag] = s.decoded_size(); } +//------------------------------------------------ + +path_ref:: +path_ref( + string_view s, + std::size_t dn, + std::size_t nseg) noexcept + : data_(s.data()) + , size_(s.size()) + , nseg_(nseg) + , dn_(dn) +{ +} + +pct_string_view +path_ref:: +string() const noexcept +{ + if(impl_) + return make_pct_string_view( + impl_->cs_ + + impl_->offset(id_path), + impl_->len(id_path), + impl_->decoded_[id_path]); + return make_pct_string_view( + data_, size_, dn_); +} + +std::size_t +path_ref:: +size() const noexcept +{ + if(impl_) + return impl_->len(id_path); + return size_; +} + +char const* +path_ref:: +data() const noexcept +{ + if(impl_) + return impl_->cs_ + + impl_->offset(id_path); + return data_; +} + +char const* +path_ref:: +end() const noexcept +{ + if(impl_) + return impl_->cs_ + + impl_->offset(id_query); + return data_ + size_; +} + +std::size_t +path_ref:: +nseg() const noexcept +{ + if(impl_) + return impl_->nseg_; + return nseg_; +} + } // detail } // urls } // boost diff --git a/include/boost/url/detail/path.hpp b/include/boost/url/detail/path.hpp index f2c9b8c6..c0c7cf0d 100644 --- a/include/boost/url/detail/path.hpp +++ b/include/boost/url/detail/path.hpp @@ -21,42 +21,53 @@ namespace detail { inline std::size_t path_prefix( - string_view s) noexcept + char const* p, + std::size_t n) noexcept { - switch(s.size()) + switch(n) { case 0: return 0; case 1: - if(s[0] == '/') + if(p[0] == '/') return 1; return 0; case 2: - if(s[0] == '/') + if(p[0] == '/') return 1; - if( s[0] == '.' && - s[1] == '/') + if( p[0] == '.' && + p[1] == '/') return 2; return 0; default: - if(s[0] == '/') + if(p[0] == '/') { - if( s[1] == '.' && - s[2] == '/') + if( p[1] == '.' && + p[2] == '/') return 3; return 1; } - if( s[0] == '.' && - s[1] == '/') + if( p[0] == '.' && + p[1] == '/') return 2; break; } return 0; } +// VFALCO DEPRECATED +inline +std::size_t +path_prefix( + string_view s) noexcept +{ + return path_prefix( + s.data(), s.size()); +} + // returns the number of adjusted // segments based on the malleable prefix. inline diff --git a/include/boost/url/detail/segments_encoded_iterator_impl.hpp b/include/boost/url/detail/segments_encoded_iterator_impl.hpp deleted file mode 100644 index 99797eb4..00000000 --- a/include/boost/url/detail/segments_encoded_iterator_impl.hpp +++ /dev/null @@ -1,72 +0,0 @@ -// -// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) -// Copyright (c) 2022 Alan de Freitas (alandefreitas@gmail.com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/CPPAlliance/url -// - -#ifndef BOOST_URL_DETAIL_SEGMENTS_ENCODED_ITERATOR_IMPL_HPP -#define BOOST_URL_DETAIL_SEGMENTS_ENCODED_ITERATOR_IMPL_HPP - -#include -#include - -namespace boost { -namespace urls { -namespace detail { - -struct segments_encoded_iterator_impl -{ - std::size_t i_ = 0; - string_view s_; - char const* begin_ = nullptr; - char const* pos_ = nullptr; - char const* next_ = nullptr; - char const* end_ = nullptr; - - BOOST_URL_DECL - segments_encoded_iterator_impl( - string_view s, - std::size_t nseg) noexcept; - - // end ctor - BOOST_URL_DECL - segments_encoded_iterator_impl( - string_view s, - std::size_t nseg, - int) noexcept; - - segments_encoded_iterator_impl() = default; - - segments_encoded_iterator_impl( - segments_encoded_iterator_impl const&) noexcept = default; - - segments_encoded_iterator_impl& operator=( - segments_encoded_iterator_impl const&) noexcept = default; - - BOOST_URL_DECL - void - increment() noexcept; - - BOOST_URL_DECL - void - decrement() noexcept; - - bool - equal( - segments_encoded_iterator_impl const& other) const noexcept - { - return - next_ == other.next_ && - end_ == other.end_; - } -}; - -} // detail -} // urls -} // boost - -#endif diff --git a/include/boost/url/detail/segments_iter_impl.hpp b/include/boost/url/detail/segments_iter_impl.hpp new file mode 100644 index 00000000..0671301a --- /dev/null +++ b/include/boost/url/detail/segments_iter_impl.hpp @@ -0,0 +1,79 @@ +// +// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2022 Alan de Freitas (alandefreitas@gmail.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/CPPAlliance/url +// + +#ifndef BOOST_URL_DETAIL_SEGMENTS_ITER_IMPL_HPP +#define BOOST_URL_DETAIL_SEGMENTS_ITER_IMPL_HPP + +#include +#include +#include +#include + +namespace boost { +namespace urls { +namespace detail { + +struct segments_iter_impl + : private parts_base +{ + path_ref ref; + std::size_t pos = 0; + std::size_t next = 0; + std::size_t index = 0; + std::size_t dn = 0; +private: + pct_string_view s_; +public: + + segments_iter_impl() = default; + segments_iter_impl( + segments_iter_impl const&) noexcept = default; + segments_iter_impl& operator=( + segments_iter_impl const&) noexcept = default; + + // begin + BOOST_URL_DECL + segments_iter_impl( + detail::path_ref const&) noexcept; + + // end + BOOST_URL_DECL + segments_iter_impl( + detail::path_ref const&, + int) noexcept; + + BOOST_URL_DECL + void + increment() noexcept; + + BOOST_URL_DECL + void + decrement() noexcept; + + pct_string_view + dereference() const noexcept + { + return s_; + } + + bool + equal( + segments_iter_impl const& other) const noexcept + { + BOOST_ASSERT(ref.alias_of(other.ref)); + return index == other.index; + } +}; + +} // detail +} // urls +} // boost + +#endif diff --git a/include/boost/url/detail/segments_iterator_impl.hpp b/include/boost/url/detail/segments_iterator_impl.hpp deleted file mode 100644 index 43ab293a..00000000 --- a/include/boost/url/detail/segments_iterator_impl.hpp +++ /dev/null @@ -1,78 +0,0 @@ -// -// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) -// Copyright (c) 2022 Alan de Freitas (alandefreitas@gmail.com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/CPPAlliance/url -// - -#ifndef BOOST_URL_DETAIL_SEGMENTS_ITERATOR_IMPL_HPP -#define BOOST_URL_DETAIL_SEGMENTS_ITERATOR_IMPL_HPP - -#include -#include -#include -#include - -namespace boost { -namespace urls { -namespace detail { - -struct segments_iterator_impl -{ - std::size_t i_ = 0; - char const* begin_ = nullptr; - char const* pos_ = nullptr; - char const* next_ = nullptr; - char const* end_ = nullptr; - pct_string_view t_; - - BOOST_URL_DECL - segments_iterator_impl( - string_view s, - std::size_t nseg) noexcept; - - // end ctor - BOOST_URL_DECL - segments_iterator_impl( - string_view s, - std::size_t nseg, - int) noexcept; - - segments_iterator_impl() = default; - - segments_iterator_impl( - segments_iterator_impl const&) noexcept = default; - - segments_iterator_impl& operator=( - segments_iterator_impl const&) noexcept = default; - - BOOST_URL_DECL - decode_view - dereference() const noexcept; - - BOOST_URL_DECL - void - increment() noexcept; - - BOOST_URL_DECL - void - decrement() noexcept; - - bool - equal( - segments_iterator_impl const& other) const noexcept - { - return - next_ == other.next_ && - end_ == other.end_; - } -}; - -} // detail -} // urls -} // boost - -#endif diff --git a/include/boost/url/detail/url_impl.hpp b/include/boost/url/detail/url_impl.hpp index e53b5d6c..2de19c1c 100644 --- a/include/boost/url/detail/url_impl.hpp +++ b/include/boost/url/detail/url_impl.hpp @@ -76,6 +76,8 @@ struct url_impl : parts_base pos_t offset(int) const noexcept; string_view get(int) const noexcept; string_view get(int, int) const noexcept; + pct_string_view pct_get(int) const noexcept; + pct_string_view pct_get(int, int) const noexcept; void set_size(int, pos_t) noexcept; void split(int, std::size_t) noexcept; void adjust(int, int, std::size_t) noexcept; @@ -95,6 +97,56 @@ struct url_impl : parts_base //------------------------------------------------ +// this allows a path to come from a +// url_impl or a separate string_view +class path_ref + : private parts_base +{ + url_impl const* impl_ = nullptr; + char const* data_ = nullptr; + std::size_t size_ = 0; + std::size_t nseg_ = 0; + std::size_t dn_ = 0; + +public: + path_ref() = default; + path_ref(string_view, + std::size_t, std::size_t) noexcept; + pct_string_view string() const noexcept; + std::size_t size() const noexcept; + char const* data() const noexcept; + char const* end() const noexcept; + std::size_t nseg() const noexcept; + + path_ref( + url_impl const& impl) noexcept + : impl_(&impl) + { + } + + bool + alias_of( + url_impl const& impl) const noexcept + { + return impl_ == &impl; + } + + bool + alias_of( + path_ref const& ref) const noexcept + { + if(impl_) + return impl_ == ref.impl_; + BOOST_ASSERT(data_ != ref.data_ || ( + size_ == ref.size_ && + nseg_ == ref.nseg_ && + dn_ == ref.dn_)); + return data_ == ref.data_; + } +}; + +//------------------------------------------------ + // return length of [first, last) inline auto @@ -156,6 +208,37 @@ get(int first, offset(last) - offset(first) }; } +// return id as pct-string +inline +pct_string_view +url_impl:: +pct_get( + int id) const noexcept +{ + return make_pct_string_view( + cs_ + offset(id), + len(id), + decoded_[id]); +} + +// return [first, last) as pct-string +inline +pct_string_view +url_impl:: +pct_get( + int first, + int last) const noexcept +{ + auto const pos = offset(first); + std::size_t n = 0; + for(auto i = first; i < last;) + n += decoded_[i++]; + return make_pct_string_view( + cs_ + pos, + offset(last) - pos, + n); +} + //------------------------------------------------ // change id to size n diff --git a/include/boost/url/grammar/detail/copied_strings.hpp b/include/boost/url/grammar/detail/copied_strings.hpp deleted file mode 100644 index c3437bfe..00000000 --- a/include/boost/url/grammar/detail/copied_strings.hpp +++ /dev/null @@ -1,149 +0,0 @@ -// -// 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/vinniefalco/http_proto -// - -#ifndef BOOST_URL_GRAMMAR_DETAIL_COPIED_STRINGS_HPP -#define BOOST_URL_GRAMMAR_DETAIL_COPIED_STRINGS_HPP - -#include -#include -#include - -namespace boost { -namespace urls { -namespace grammar { -namespace detail { - -/** Common functionality for copied strings - - This base class is used by the library - to provide the functionality for copied - strings. - Users should not use this class directly. - Instead, construct an instance of - @ref copied_strings instead. -*/ -class copied_strings_base -{ -public: - /** Destructor - - Destruction invalidates any strings - previously returned by this object. - */ - BOOST_URL_DECL - ~copied_strings_base(); - - /** Return a string, or a copy if it overlaps the protected buffer - - This function checks if the passed string - view overlaps the protected character buffer - set on construction. If there is no overlap, - the same view is returned. However if the - character buffer would overlap, then a copy - is returned instead. - */ - BOOST_URL_DECL - string_view - maybe_copy( - string_view s); - -private: - template - friend class copied_strings; - - struct dynamic_buf - { - dynamic_buf* next; - }; - - bool - is_overlapping( - string_view s) const noexcept; - - /** Constructor - */ - BOOST_URL_DECL - copied_strings_base( - string_view s, - char* local_buf, - std::size_t local_size) noexcept; - - string_view s_; - char* local_buf_; - std::size_t local_remain_; - dynamic_buf* dynamic_list_ = nullptr; -}; - -//------------------------------------------------ - -/** Helper to copy strings if they overlap a protected character buffer - - Objects of this type are declared on the - stack as local variables in functions which - accept @ref string_view parameters that - modify an underlying character buffer. The - purpose is to make a copy of the parameter - if the parameter overlaps the protected - character buffer. - - @par Example - In this example we implement the append - member function. The use of copied strings - handles the case where the passed string `s` - points into `s_`. - @code - struct container - { - std::string s_; - - void append( string_view s ) - { - copied_strings<4096> cs( s_ ); - s = cs.maybe_copy( s ); - s_.append( s.data(), s.size() ); - } - }; - @endcode - - @tparam BufferSize The number of bytes of - inline storage. This is how many characters - can be copied before dynamic allocation is - required. -*/ -template< - std::size_t BufferSize> -class copied_strings - : public copied_strings_base -{ - char buf_[BufferSize]; - -public: - /** Constructor - - This constructs storage for copying - strings that overlap the protected - character buffer. - - @param s The character buffer to protect - */ - explicit - copied_strings( - string_view s) noexcept - : copied_strings_base( - s, buf_, sizeof(buf_)) - { - } -}; - -} // detail -} // grammar -} // urls -} // boost - -#endif diff --git a/include/boost/url/grammar/detail/impl/copied_strings.ipp b/include/boost/url/grammar/detail/impl/copied_strings.ipp deleted file mode 100644 index ab4ad7de..00000000 --- a/include/boost/url/grammar/detail/impl/copied_strings.ipp +++ /dev/null @@ -1,99 +0,0 @@ -// -// 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/vinniefalco/http_proto -// - -#ifndef BOOST_URL_GRAMMAR_DETAIL_IMPL_COPIED_STRINGS_IPP -#define BOOST_URL_GRAMMAR_DETAIL_IMPL_COPIED_STRINGS_IPP - -#include -#include - -namespace boost { -namespace urls { -namespace grammar { -namespace detail { - -bool -copied_strings_base:: -is_overlapping( - string_view s) const noexcept -{ - auto const b1 = s_.data(); - auto const e1 = b1 + s_.size(); - auto const b2 = s.data(); - auto const e2 = b2 + s.size(); - auto const less_equal = - std::less_equal(); - if(less_equal(e1, b2)) - return false; - if(less_equal(e2, b1)) - return false; - return true; -} - -copied_strings_base:: -~copied_strings_base() -{ - while(dynamic_list_) - { - auto p = dynamic_list_; - dynamic_list_ = - dynamic_list_->next; - delete[] p; - } -} - -copied_strings_base:: -copied_strings_base( - string_view s, - char* local_buf, - std::size_t local_size) noexcept - : s_(s) - , local_buf_(local_buf) - , local_remain_(local_size) -{ -} - -string_view -copied_strings_base:: -maybe_copy( - string_view s) -{ - if(! is_overlapping(s)) - return s; - if(local_remain_ >= s.size()) - { - std::memcpy(local_buf_, - s.data(), s.size()); - s = string_view( - local_buf_, s.size()); - local_buf_ += s.size(); - local_remain_ -= s.size(); - return s; - } - auto const n = - sizeof(dynamic_buf); - auto p = new dynamic_buf[1 + - sizeof(n) * ((s.size() + - sizeof(n) - 1) / - sizeof(n))]; - std::memcpy(p + 1, - s.data(), s.size()); - s = string_view(reinterpret_cast< - char const*>(p + 1), s.size()); - p->next = dynamic_list_; - dynamic_list_ = p; - return s; -} - -} // detail -} // grammar -} // urls -} // boost - -#endif diff --git a/include/boost/url/impl/decode_view.ipp b/include/boost/url/impl/decode_view.ipp index 6052f1e0..d2577cff 100644 --- a/include/boost/url/impl/decode_view.ipp +++ b/include/boost/url/impl/decode_view.ipp @@ -11,7 +11,6 @@ #define BOOST_URL_IMPL_PCT_ENCODED_VIEW_IPP #include -#include #include namespace boost { @@ -66,22 +65,6 @@ decode_view( s, opt).value(BOOST_URL_POS); } -#if 0 -decode_view -decode_view:: -maybe_copy( - grammar::detail::copied_strings_base& sp) const -{ - decode_opts opt; - opt.plus_to_space = - plus_to_space_; - return decode_view( - sp.maybe_copy(encoded()), - dn_, - opt); -} -#endif - //------------------------------------------------ auto diff --git a/include/boost/url/impl/params_encoded_view.ipp b/include/boost/url/impl/params_encoded_view.ipp index 50699228..b12b54d0 100644 --- a/include/boost/url/impl/params_encoded_view.ipp +++ b/include/boost/url/impl/params_encoded_view.ipp @@ -29,8 +29,8 @@ namespace urls { std::size_t params_encoded_view:: erase( - string_view key, - ignore_case_param ic) + pct_string_view key, + ignore_case_param ic) noexcept { // VFALCO we can't cache end() here // because it will be invalidated diff --git a/include/boost/url/impl/parse_path.ipp b/include/boost/url/impl/parse_path.ipp new file mode 100644 index 00000000..903c165d --- /dev/null +++ b/include/boost/url/impl/parse_path.ipp @@ -0,0 +1,66 @@ +// +// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2022 Alan de Freitas (alandefreitas@gmail.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/CPPAlliance/url +// + +#ifndef BOOST_URL_IMPL_PARSE_PATH_IPP +#define BOOST_URL_IMPL_PARSE_PATH_IPP + +#include +#include +#include +#include +#include + +namespace boost { +namespace urls { + +result +parse_path(string_view s) noexcept +{ + if(s.empty()) + return segments_encoded_view( + detail::path_ref(s, 0, 0)); + if(s[0] == '/') + { + auto rv = grammar::parse( + s, detail::path_abempty_rule); + if(! rv) + return rv.error(); + + // VFALCO We are needlessly recalculating + // the decoded size here. + return segments_encoded_view( + detail::path_ref( + rv->string(), + detail::decode_bytes_unchecked( + rv->string()), + detail::path_segments( + rv->string(), + rv->size()))); + } + { + auto rv = grammar::parse( + s, detail::path_rootless_rule); + if(! rv) + return rv.error(); + return segments_encoded_view( + detail::path_ref( + rv->string(), + detail::decode_bytes_unchecked( + rv->string()), + detail::path_segments( + rv->string(), + rv->size()))); + } +} + +} // urls +} // boost + +#endif diff --git a/include/boost/url/impl/segments.hpp b/include/boost/url/impl/segments.hpp deleted file mode 100644 index b201e4ab..00000000 --- a/include/boost/url/impl/segments.hpp +++ /dev/null @@ -1,403 +0,0 @@ -// -// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) -// Copyright (c) 2022 Alan de Freitas (alandefreitas@gmail.com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/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 { - -//------------------------------------------------ - -class segments::iterator -{ - detail::segments_iterator_impl impl_; - - friend class segments; - - iterator( - string_view s, - std::size_t nseg) noexcept - : impl_(s, nseg) - { - } - - // end ctor - iterator( - string_view s, - std::size_t nseg, - int) noexcept - : impl_(s, nseg, 0) - { - } - -public: - using value_type = std::string; - using reference = decode_view; - using pointer = void const*; - using difference_type = std::ptrdiff_t; - using iterator_category = - std::bidirectional_iterator_tag; - - iterator() noexcept = default; - - iterator(iterator const&) noexcept = default; - - iterator& - operator=(iterator const&) noexcept = default; - - reference - operator*() const noexcept - { - return impl_.dereference(); - } - - iterator& - operator++() noexcept - { - impl_.increment(); - return *this; - } - - iterator& - operator--() noexcept - { - impl_.decrement(); - return *this; - } - - iterator - operator++(int) noexcept - { - auto tmp = *this; - ++*this; - return tmp; - } - - iterator - operator--(int) noexcept - { - auto tmp = *this; - --*this; - return tmp; - } - - bool - operator==( - iterator const& other) const noexcept - { - return impl_.equal(other.impl_); - } - - bool - operator!=( - iterator const& other) const noexcept - { - return !impl_.equal(other.impl_); - } -}; - -//------------------------------------------------ -// -// Members -// -//------------------------------------------------ - -inline -bool -segments:: -is_absolute() const noexcept -{ - return - u_->u_.len(id_path) != 0 && - u_->s_[u_->u_.offset(id_path)] == '/'; -} - -inline -segments& -segments:: -operator=(std::initializer_list< - string_view> init) -{ - assign(init.begin(), init.end()); - return *this; -} - -template -auto -segments:: -assign(FwdIt first, FwdIt last) -> - typename std::enable_if< - std::is_convertible::reference, - string_view>::value>::type -{ - u_->edit_segments( - 0, - size(), - detail::make_plain_segs_iter( - first, last), - detail::make_plain_segs_iter( - first, last)); -} - -//------------------------------------------------ -// -// Element Access -// -//------------------------------------------------ - -inline -auto -segments:: -front() const -> - decode_view -{ - BOOST_ASSERT(! empty()); - return *begin(); -} - -inline -auto -segments:: -back() const -> - decode_view -{ - BOOST_ASSERT(! empty()); - return *std::prev(end()); -} - -//------------------------------------------------ -// -// Iterators -// -//------------------------------------------------ - -inline -auto -segments:: -begin() const noexcept -> - iterator -{ - return iterator( - u_->encoded_path(), u_->u_.nseg_); -} - -inline -auto -segments:: -end() const noexcept -> - iterator -{ - return iterator( - u_->encoded_path(), u_->u_.nseg_, 0); -} - -//------------------------------------------------ -// -// Capacity -// -//------------------------------------------------ - -inline -bool -segments:: -empty() const noexcept -{ - return size() == 0; -} - -inline -std::size_t -segments:: -size() const noexcept -{ - return u_->u_.nseg_; -} - -//------------------------------------------------ -// -// Modifiers -// -//------------------------------------------------ - -inline -void -segments:: -clear() noexcept -{ - erase(begin(), end()); -} - -//------------------------------------------------ - -inline -auto -segments:: -insert( - iterator before, - std::initializer_list< - string_view> init) -> - iterator -{ - return insert( - before, - init.begin(), - init.end()); -} - -template -auto -segments:: -insert( - iterator before, - FwdIt first, - FwdIt last) -> - typename std::enable_if< - std::is_convertible::reference, - string_view>::value, - iterator>::type -{ - return insert(before, first, last, - typename std::iterator_traits< - FwdIt>::iterator_category{}); -} - -template -auto -segments:: -insert( - iterator before, - FwdIt first, - FwdIt last, - std::forward_iterator_tag) -> - iterator -{ - u_->edit_segments( - before.impl_.i_, - before.impl_.i_, - detail::make_plain_segs_iter( - first, last), - detail::make_plain_segs_iter( - first, last)); - return std::next(begin(), before.impl_.i_); -} - -//------------------------------------------------ - -inline -auto -segments:: -replace( - iterator pos, - string_view s) -> - iterator -{ - return replace( - pos, std::next(pos), - &s, &s + 1); -} - -inline -auto -segments:: -replace( - iterator from, - iterator to, - std::initializer_list< - string_view> init) -> - iterator -{ - return replace( - from, - to, - init.begin(), - init.end()); -} - -template -auto -segments:: -replace( - iterator from, - iterator to, - FwdIt first, - FwdIt last) -> - typename std::enable_if< - std::is_convertible::reference, - string_view>::value, - iterator>::type -{ - BOOST_ASSERT(from.impl_.begin_ >= u_->string().data()); - BOOST_ASSERT(from.impl_.end_ <= u_->string().data() + - u_->string().size()); - BOOST_ASSERT(to.impl_.begin_ >= u_->string().data()); - BOOST_ASSERT(to.impl_.end_ >= u_->string().data() + - u_->string().size()); - u_->edit_segments( - from.impl_.i_, - to.impl_.i_, - detail::make_plain_segs_iter( - first, last), - detail::make_plain_segs_iter( - first, last)); - return std::next(begin(), from.impl_.i_); -} - -//------------------------------------------------ - -inline -auto -segments:: -erase( - iterator pos) noexcept -> - iterator -{ - return erase(pos, std::next(pos)); -} - -//------------------------------------------------ - -inline -void -segments:: -push_back( - string_view s) -{ - insert(end(), s); -} - -inline -void -segments:: -pop_back() noexcept -{ - erase(std::prev(end())); -} - -} // urls -} // boost - -#endif diff --git a/include/boost/url/impl/segments.ipp b/include/boost/url/impl/segments.ipp deleted file mode 100644 index 3c421379..00000000 --- a/include/boost/url/impl/segments.ipp +++ /dev/null @@ -1,81 +0,0 @@ -// -// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) -// Copyright (c) 2022 Alan de Freitas (alandefreitas@gmail.com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/CPPAlliance/url -// - -#ifndef BOOST_URL_IMPL_SEGMENTS_IPP -#define BOOST_URL_IMPL_SEGMENTS_IPP - -#include -#include -#include -#include - -namespace boost { -namespace urls { - -//------------------------------------------------ -// -// Modifiers -// -//------------------------------------------------ - -auto -segments:: -insert( - iterator before, - string_view s) -> - iterator -{ - BOOST_ASSERT( - before.impl_.pos_ >= - u_->string().data()); - BOOST_ASSERT( - before.impl_.pos_ <= - u_->string().data() + - u_->string().size()); - grammar::detail::copied_strings< - BOOST_URL_STACK_BYTES> cs( - u_->string()); - s = cs.maybe_copy(s); - u_->edit_segments( - before.impl_.i_, - before.impl_.i_, - detail::make_plain_segs_iter( - &s, &s + 1), - detail::make_plain_segs_iter( - &s, &s + 1)); - return std::next(begin(), before.impl_.i_); -} - -auto -segments:: -segments:: -erase( - iterator first, - iterator last) noexcept -> - iterator -{ - BOOST_ASSERT(first.impl_.pos_ >= u_->string().data()); - BOOST_ASSERT(last.impl_.pos_ >= u_->string().data()); - BOOST_ASSERT(first.impl_.pos_ <= u_->string().data() + - u_->string().size()); - BOOST_ASSERT(last.impl_.pos_ <= u_->string().data() + - u_->string().size()); - string_view s; - u_->edit_segments( - first.impl_.i_, last.impl_.i_, - detail::make_enc_segs_iter(&s, &s), - detail::make_enc_segs_iter(&s, &s)); - return std::next(begin(), first.impl_.i_); -} - -} // urls -} // boost - -#endif diff --git a/include/boost/url/impl/segments_base.hpp b/include/boost/url/impl/segments_base.hpp new file mode 100644 index 00000000..3cee838d --- /dev/null +++ b/include/boost/url/impl/segments_base.hpp @@ -0,0 +1,128 @@ +// +// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2022 Alan de Freitas (alandefreitas@gmail.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/CPPAlliance/url +// + +#ifndef BOOST_URL_IMPL_SEGMENTS_BASE_HPP +#define BOOST_URL_IMPL_SEGMENTS_BASE_HPP + +#include +#include + +namespace boost { +namespace urls { + +class segments_base::iterator +{ + detail::segments_iter_impl it_; + mutable grammar::recycled_ptr< + std::string> s_ = nullptr; + mutable bool valid_ = false; + + friend class segments_base; + friend class segments_ref; + + iterator(detail::path_ref const&) noexcept; + iterator(detail::path_ref const&, int) noexcept; + iterator(detail::segments_iter_impl const& it) noexcept + : it_(it) + { + } + + BOOST_URL_DECL + string_view + dereference() const; + +public: + using value_type = std::string; + using reference = string_view; + using pointer = string_view; + using difference_type = std::ptrdiff_t; + using iterator_category = + std::bidirectional_iterator_tag; + + iterator() = default; + iterator(iterator const&) = default; + iterator& operator=( + iterator const&) = default; + + reference + operator*() const + { + return dereference(); + } + + struct arrow_proxy + { + string_view s; + + string_view const* + operator->() + { + return &s; + } + }; + + arrow_proxy + operator->() const + { + return arrow_proxy{ + dereference()}; + } + + iterator& + operator++() noexcept + { + valid_ = false; + it_.increment(); + return *this; + } + + iterator& + operator--() noexcept + { + valid_ = false; + it_.decrement(); + return *this; + } + + iterator + operator++(int) noexcept + { + auto tmp = *this; + ++*this; + return tmp; + } + + iterator + operator--(int) noexcept + { + auto tmp = *this; + --*this; + return tmp; + } + + bool + operator==( + iterator const& other) const noexcept + { + return it_.equal(other.it_); + } + + bool + operator!=( + iterator const& other) const noexcept + { + return ! it_.equal(other.it_); + } +}; + +} // urls +} // boost + +#endif diff --git a/include/boost/url/impl/segments_base.ipp b/include/boost/url/impl/segments_base.ipp new file mode 100644 index 00000000..d816c5ef --- /dev/null +++ b/include/boost/url/impl/segments_base.ipp @@ -0,0 +1,122 @@ +// +// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2022 Alan de Freitas (alandefreitas@gmail.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/CPPAlliance/url +// + +#ifndef BOOST_URL_IMPL_SEGMENTS_BASE_IPP +#define BOOST_URL_IMPL_SEGMENTS_BASE_IPP + +#include + +namespace boost { +namespace urls { + +string_view +segments_base:: +iterator:: +dereference() const +{ + if(! valid_) + { + // VFALCO This could be better, + // we should never shrink size() for + // a recycled std::string, because + // otherwise when we resize it larger + // we will again have to value-init (?) + // the new chars. + s_.acquire(); + (*it_.dereference()).assign_to(*s_); + valid_ = true; + } + return *s_; +} + +// begin +segments_base:: +iterator:: +iterator( + detail::path_ref const& ref) noexcept + : it_(ref) +{ +} + +// end +segments_base:: +iterator:: +iterator( + detail::path_ref const& ref, + int) noexcept + : it_(ref, 0) +{ +} + +//------------------------------------------------ + +pct_string_view +segments_base:: +buffer() const noexcept +{ + return ref_.string(); +} + +bool +segments_base:: +is_absolute() const noexcept +{ + return ref_.string().starts_with('/'); +} + +bool +segments_base:: +empty() const noexcept +{ + return ref_.nseg() == 0; +} + +std::size_t +segments_base:: +size() const noexcept +{ + return ref_.nseg(); +} + +std::string +segments_base:: +front() const noexcept +{ + BOOST_ASSERT(! empty()); + return *begin(); +} + +std::string +segments_base:: +back() const noexcept +{ + BOOST_ASSERT(! empty()); + return *--end(); +} + +auto +segments_base:: +begin() const noexcept -> + iterator +{ + return iterator(ref_); +} + +auto +segments_base:: +end() const noexcept -> + iterator +{ + return iterator(ref_, 0); +} +} // urls +} // boost + +#endif diff --git a/include/boost/url/impl/segments_encoded.hpp b/include/boost/url/impl/segments_encoded.hpp deleted file mode 100644 index 8bd77b5a..00000000 --- a/include/boost/url/impl/segments_encoded.hpp +++ /dev/null @@ -1,373 +0,0 @@ -// -// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) -// Copyright (c) 2022 Alan de Freitas (alandefreitas@gmail.com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/CPPAlliance/url -// - -#ifndef BOOST_URL_IMPL_SEGMENTS_ENCODED_HPP -#define BOOST_URL_IMPL_SEGMENTS_ENCODED_HPP - -#include -#include -#include -#include -#include -#include - -namespace boost { -namespace urls { - -class segments_encoded::iterator -{ - friend class segments_encoded; - - detail::segments_encoded_iterator_impl impl_; - - iterator( - string_view s, - std::size_t nseg) noexcept - : impl_(s, nseg) - { - } - - // end ctor - iterator( - string_view s, - std::size_t nseg, - int) noexcept - : impl_(s, nseg, 0) - { - } - -public: - using value_type = std::string; - using reference = string_view; - using pointer = void const*; - using difference_type = std::ptrdiff_t; - using iterator_category = - std::bidirectional_iterator_tag; - - iterator() = default; - - iterator(iterator const&) noexcept = default; - - iterator& operator=(iterator const&) noexcept = default; - - reference - operator*() const noexcept - { - return impl_.s_; - } - - iterator& - operator++() noexcept - { - impl_.increment(); - return *this; - } - - iterator& - operator--() noexcept - { - impl_.decrement(); - return *this; - } - - iterator - operator++(int) noexcept - { - auto tmp = *this; - ++*this; - return tmp; - } - - iterator - operator--(int) noexcept - { - auto tmp = *this; - --*this; - return tmp; - } - - bool - operator==( - iterator const& other) const noexcept - { - return impl_.equal(other.impl_); - } - - bool - operator!=( - iterator const& other) const noexcept - { - return !impl_.equal(other.impl_); - } -}; - -//------------------------------------------------ -// -// Members -// -//------------------------------------------------ - -inline -segments_encoded:: -segments_encoded( - url_base& u) noexcept - : u_(&u) -{ -} - -inline -bool -segments_encoded:: -is_absolute() const noexcept -{ - return - u_->u_.len(id_path) != 0 && - u_->s_[u_->u_.offset(id_path)] == '/'; -} - -inline -segments_encoded& -segments_encoded:: -operator=(std::initializer_list init) -{ - assign( init.begin(), init.end() ); - return *this; -} - -template -auto -segments_encoded:: -assign( - FwdIt first, FwdIt last) -> - typename std::enable_if< - std::is_convertible::reference, - string_view>::value>::type -{ - u_->edit_segments( - 0, - size(), - detail::make_enc_segs_iter(first, last), - detail::make_enc_segs_iter(first, last)); -} - -//------------------------------------------------ -// -// Element Access -// -//------------------------------------------------ - -inline -string_view -segments_encoded:: -front() const noexcept -{ - BOOST_ASSERT(!empty()); - return *begin(); -} - -inline -string_view -segments_encoded:: -back() const noexcept -{ - BOOST_ASSERT(! empty()); - return *std::prev(end()); -} - -//------------------------------------------------ -// -// Capacity -// -//------------------------------------------------ - -inline -bool -segments_encoded:: -empty() const noexcept -{ - return size() == 0; -} - -inline -std::size_t -segments_encoded:: -size() const noexcept -{ - return u_->u_.nseg_; -} - -//------------------------------------------------ -// -// Modifiers -// -//------------------------------------------------ - -inline -void -segments_encoded:: -clear() noexcept -{ - erase(begin(), end()); -} - -//------------------------------------------------ - -inline -auto -segments_encoded:: -insert( - iterator before, - std::initializer_list< - string_view> init) -> - iterator -{ - return insert( - before, - init.begin(), - init.end()); -} - -template -auto -segments_encoded:: -insert( - iterator before, - FwdIt first, - FwdIt last) -> - typename std::enable_if< - std::is_convertible::reference, - string_view>::value, - iterator>::type -{ - return insert(before, first, last, - typename std::iterator_traits< - FwdIt>::iterator_category{}); -} - -template -auto -segments_encoded:: -insert( - iterator before, - FwdIt first, - FwdIt last, - std::forward_iterator_tag) -> - iterator -{ - u_->edit_segments( - before.impl_.i_, - before.impl_.i_, - detail::make_enc_segs_iter( - first, last), - detail::make_enc_segs_iter( - first, last)); - return std::next(begin(), before.impl_.i_); -} - -//------------------------------------------------ - -inline -auto -segments_encoded:: -replace( - iterator pos, - string_view s) -> - iterator -{ - return replace( - pos, std::next(pos), - &s, &s + 1); -} - -inline -auto -segments_encoded:: -replace( - iterator from, - iterator to, - std::initializer_list< - string_view> init) -> - iterator -{ - return replace( - from, - to, - init.begin(), - init.end()); -} - -template -auto -segments_encoded:: -replace( - iterator from, - iterator to, - FwdIt first, - FwdIt last) -> - typename std::enable_if< - std::is_convertible::reference, - string_view>::value, - iterator>::type -{ - BOOST_ASSERT(from.impl_.begin_ >= u_->string().data()); - BOOST_ASSERT(from.impl_.end_ <= u_->string().data() + - u_->string().size()); - BOOST_ASSERT(to.impl_.begin_ >= u_->string().data()); - BOOST_ASSERT(to.impl_.end_ >= u_->string().data() + - u_->string().size()); - u_->edit_segments( - from.impl_.i_, - to.impl_.i_, - detail::make_enc_segs_iter(first, last), - detail::make_enc_segs_iter(first, last)); - return std::next(begin(), from.impl_.i_); -} - -//------------------------------------------------ - -inline -auto -segments_encoded:: -erase( - iterator pos) noexcept -> - iterator -{ - return erase(pos, std::next(pos)); -} - -//------------------------------------------------ - -inline -void -segments_encoded:: -push_back( - string_view s) -{ - insert(end(), s); -} - -inline -void -segments_encoded:: -pop_back() noexcept -{ - erase(std::prev(end())); -} - -} // urls -} // boost - -#endif diff --git a/include/boost/url/impl/segments_encoded.ipp b/include/boost/url/impl/segments_encoded.ipp deleted file mode 100644 index 7ec47a8a..00000000 --- a/include/boost/url/impl/segments_encoded.ipp +++ /dev/null @@ -1,104 +0,0 @@ -// -// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) -// Copyright (c) 2022 Alan de Freitas (alandefreitas@gmail.com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/CPPAlliance/url -// - -#ifndef BOOST_URL_IMPL_SEGMENTS_ENCODED_IPP -#define BOOST_URL_IMPL_SEGMENTS_ENCODED_IPP - -#include -#include -#include -#include - -namespace boost { -namespace urls { - -segments -segments_encoded:: -decoded() const -{ - return segments(*u_); -} - -//------------------------------------------------ -// -// Iterators -// -//------------------------------------------------ - -auto -segments_encoded:: -begin() const noexcept -> - iterator -{ - return {u_->encoded_path(), u_->u_.nseg_}; -} - -auto -segments_encoded:: -end() const noexcept -> - iterator -{ - return {u_->encoded_path(), u_->u_.nseg_, 0}; -} - -//------------------------------------------------ -// -// Modifiers -// -//------------------------------------------------ - -auto -segments_encoded:: -insert( - iterator before, - string_view s0) -> - iterator -{ - BOOST_ASSERT(before.impl_.pos_ >= u_->string().data()); - BOOST_ASSERT(before.impl_.pos_ <= u_->string().data() + - u_->string().size()); - grammar::detail::copied_strings< - BOOST_URL_STACK_BYTES> cs(u_->string()); - auto s = cs.maybe_copy(s0); - u_->edit_segments( - before.impl_.i_, - before.impl_.i_, - detail::make_enc_segs_iter( - &s, &s + 1), - detail::make_enc_segs_iter( - &s, &s + 1)); - return std::next(begin(), before.impl_.i_); -} - -auto -segments_encoded:: -erase( - iterator first, - iterator last) noexcept -> - iterator -{ - BOOST_ASSERT(first.impl_.pos_ >= u_->string().data()); - BOOST_ASSERT(last.impl_.pos_ >= u_->string().data()); - BOOST_ASSERT(first.impl_.pos_ <= u_->string().data() + - u_->string().size()); - BOOST_ASSERT(last.impl_.pos_ <= u_->string().data() + - u_->string().size()); - string_view s; - u_->edit_segments( - first.impl_.i_, last.impl_.i_, - detail::make_enc_segs_iter(&s, &s), - detail::make_enc_segs_iter(&s, &s)); - return std::next(begin(), first.impl_.i_); -} - -} // urls -} // boost - -#endif diff --git a/include/boost/url/impl/segments_encoded_base.hpp b/include/boost/url/impl/segments_encoded_base.hpp new file mode 100644 index 00000000..b0c29133 --- /dev/null +++ b/include/boost/url/impl/segments_encoded_base.hpp @@ -0,0 +1,103 @@ +// +// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2022 Alan de Freitas (alandefreitas@gmail.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/CPPAlliance/url +// + +#ifndef BOOST_URL_IMPL_SEGMENTS_ENCODED_BASE_HPP +#define BOOST_URL_IMPL_SEGMENTS_ENCODED_BASE_HPP + +#include + +namespace boost { +namespace urls { + +class segments_encoded_base::iterator +{ + detail::segments_iter_impl it_; + + friend class url_base; + friend class segments_encoded_base; + friend class segments_encoded_ref; + + iterator(detail::path_ref const&) noexcept; + iterator(detail::path_ref const&, int) noexcept; + +public: + using value_type = std::string; + using reference = pct_string_view; + using pointer = pct_string_view; + using difference_type = std::ptrdiff_t; + using iterator_category = + std::bidirectional_iterator_tag; + + iterator() = default; + iterator(iterator const&) = default; + iterator& operator=( + iterator const&) = default; + + reference + operator*() const noexcept + { + return it_.dereference(); + } + + pointer + operator->() const noexcept + { + return it_.dereference(); + } + + iterator& + operator++() noexcept + { + it_.increment(); + return *this; + } + + iterator& + operator--() noexcept + { + it_.decrement(); + return *this; + } + + iterator + operator++(int) noexcept + { + auto tmp = *this; + ++*this; + return tmp; + } + + iterator + operator--(int) noexcept + { + auto tmp = *this; + --*this; + return tmp; + } + + bool + operator==( + iterator const& other) const noexcept + { + return it_.equal(other.it_); + } + + bool + operator!=( + iterator const& other) const noexcept + { + return ! it_.equal(other.it_); + } +}; + +} // urls +} // boost + +#endif diff --git a/include/boost/url/impl/segments_encoded_base.ipp b/include/boost/url/impl/segments_encoded_base.ipp new file mode 100644 index 00000000..bad402a0 --- /dev/null +++ b/include/boost/url/impl/segments_encoded_base.ipp @@ -0,0 +1,104 @@ +// +// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2022 Alan de Freitas (alandefreitas@gmail.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/CPPAlliance/url +// + +#ifndef BOOST_URL_IMPL_SEGMENTS_ENCODED_BASE_IPP +#define BOOST_URL_IMPL_SEGMENTS_ENCODED_BASE_IPP + +#include +#include + +namespace boost { +namespace urls { + +// begin +segments_encoded_base:: +iterator:: +iterator( + detail::path_ref const& ref) noexcept + : it_(ref) +{ +} + +// end +segments_encoded_base:: +iterator:: +iterator( + detail::path_ref const& ref, + int) noexcept + : it_(ref, 0) +{ +} + +//------------------------------------------------ + +pct_string_view +segments_encoded_base:: +buffer() const noexcept +{ + return ref_.string(); +} + +bool +segments_encoded_base:: +is_absolute() const noexcept +{ + return ref_.string().starts_with('/'); +} + +bool +segments_encoded_base:: +empty() const noexcept +{ + return ref_.nseg() == 0; +} + +std::size_t +segments_encoded_base:: +size() const noexcept +{ + return ref_.nseg(); +} + +pct_string_view +segments_encoded_base:: +front() const noexcept +{ + BOOST_ASSERT(! empty()); + return *begin(); +} + +pct_string_view +segments_encoded_base:: +back() const noexcept +{ + BOOST_ASSERT(! empty()); + return *--end(); +} + +auto +segments_encoded_base:: +begin() const noexcept -> + iterator +{ + return iterator(ref_); +} + +auto +segments_encoded_base:: +end() const noexcept -> + iterator +{ + return iterator(ref_, 0); +} + +} // urls +} // boost + +#endif diff --git a/include/boost/url/impl/segments_encoded_ref.hpp b/include/boost/url/impl/segments_encoded_ref.hpp new file mode 100644 index 00000000..e93d9a69 --- /dev/null +++ b/include/boost/url/impl/segments_encoded_ref.hpp @@ -0,0 +1,276 @@ +// +// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2022 Alan de Freitas (alandefreitas@gmail.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/CPPAlliance/url +// + +#ifndef BOOST_URL_IMPL_SEGMENTS_ENCODED_REF_HPP +#define BOOST_URL_IMPL_SEGMENTS_ENCODED_REF_HPP + +#include +#include +#include +#include +#include +#include + +namespace boost { +namespace urls { + +//------------------------------------------------ +// +// Special Members +// +//------------------------------------------------ + +inline +segments_encoded_ref:: +segments_encoded_ref( + url_base& u) noexcept + : segments_encoded_base(u.u_) + , u_(&u) +{ +} + +inline +segments_encoded_ref& +segments_encoded_ref:: +operator=( + segments_encoded_ref const& other) +{ + assign(other.begin(), other.end()); + return *this; +} + +inline +segments_encoded_ref& +segments_encoded_ref:: +operator=( + segments_encoded_view const& other) +{ + assign(other.begin(), other.end()); + return *this; +} + +inline +segments_encoded_ref& +segments_encoded_ref:: +operator=(std::initializer_list< + pct_string_view> init) +{ + assign(init.begin(), init.end()); + return *this; +} + +inline +segments_encoded_ref:: +operator +segments_encoded_view() const noexcept +{ + return {detail::path_ref(u_->u_)}; +} + +//------------------------------------------------ +// +// Modifiers +// +//------------------------------------------------ + +inline +void +segments_encoded_ref:: +clear() noexcept +{ + erase(begin(), end()); +} + +inline +segments_encoded_ref& +segments_encoded_ref:: +assign( + std::initializer_list< + pct_string_view> init) +{ + assign(init.begin(), init.end()); + return *this; +} + +template +auto +segments_encoded_ref:: +assign( + FwdIt first, FwdIt last) -> + typename std::enable_if< + std::is_convertible::reference, + pct_string_view>::value>::type +{ + u_->edit_segments( + begin().it_, + end().it_, + detail::make_segments_encoded_iter( + first, last)); +} + +//------------------------------------------------ + +inline +auto +segments_encoded_ref:: +insert( + iterator before, + std::initializer_list< + pct_string_view> init) -> + iterator +{ + return insert( + before, + init.begin(), + init.end()); +} + +template +auto +segments_encoded_ref:: +insert( + iterator before, + FwdIt first, + FwdIt last) -> + typename std::enable_if< + std::is_convertible::reference, + pct_string_view>::value, + iterator>::type +{ + return insert(before, first, last, + typename std::iterator_traits< + FwdIt>::iterator_category{}); +} + +template +auto +segments_encoded_ref:: +insert( + iterator before, + FwdIt first, + FwdIt last, + std::forward_iterator_tag) -> + iterator +{ + u_->edit_segments( + before.it_, + before.it_, + detail::make_segments_encoded_iter( + first, last)); + return std::next(begin(), before.it_.index); +} + +//------------------------------------------------ + +inline +auto +segments_encoded_ref:: +replace( + iterator pos, + pct_string_view s) -> + iterator +{ + return replace( + pos, std::next(pos), + &s, &s + 1); +} + +inline +auto +segments_encoded_ref:: +replace( + iterator from, + iterator to, + pct_string_view s) -> + iterator +{ + return replace( + from, to, &s, &s+1); +} + +inline +auto +segments_encoded_ref:: +replace( + iterator from, + iterator to, + std::initializer_list< + pct_string_view> init) -> + iterator +{ + return replace( + from, + to, + init.begin(), + init.end()); +} + +template +auto +segments_encoded_ref:: +replace( + iterator from, + iterator to, + FwdIt first, + FwdIt last) -> + typename std::enable_if< + std::is_convertible::reference, + pct_string_view>::value, + iterator>::type +{ + u_->edit_segments( + from.it_, + to.it_, + detail::make_segments_encoded_iter( + first, last)); + return std::next(begin(), from.it_.index); +} + +//------------------------------------------------ + +inline +auto +segments_encoded_ref:: +erase( + iterator pos) noexcept -> + iterator +{ + return erase(pos, std::next(pos)); +} + +//------------------------------------------------ + +inline +void +segments_encoded_ref:: +push_back( + pct_string_view s) +{ + insert(end(), s); +} + +inline +void +segments_encoded_ref:: +pop_back() noexcept +{ + erase(std::prev(end())); +} + +} // urls +} // boost + +#endif diff --git a/include/boost/url/impl/segments_encoded_ref.ipp b/include/boost/url/impl/segments_encoded_ref.ipp new file mode 100644 index 00000000..c2238beb --- /dev/null +++ b/include/boost/url/impl/segments_encoded_ref.ipp @@ -0,0 +1,56 @@ +// +// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2022 Alan de Freitas (alandefreitas@gmail.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/CPPAlliance/url +// + +#ifndef BOOST_URL_IMPL_SEGMENTS_ENCODED_REF_IPP +#define BOOST_URL_IMPL_SEGMENTS_ENCODED_REF_IPP + +#include +#include +#include + +namespace boost { +namespace urls { + +auto +segments_encoded_ref:: +insert( + iterator before, + pct_string_view s0) -> + iterator +{ + string_view s = s0; + u_->edit_segments( + before.it_, + before.it_, + detail::make_segments_encoded_iter( + &s, &s + 1)); + return std::next(begin(), before.it_.index); +} + +auto +segments_encoded_ref:: +erase( + iterator first, + iterator last) noexcept -> + iterator +{ + string_view s; + u_->edit_segments( + first.it_, + last.it_, + detail::make_segments_encoded_iter( + &s, &s)); + return std::next(begin(), first.it_.index); +} + +} // urls +} // boost + +#endif diff --git a/include/boost/url/impl/segments_encoded_view.hpp b/include/boost/url/impl/segments_encoded_view.hpp deleted file mode 100644 index 9b4d2cc8..00000000 --- a/include/boost/url/impl/segments_encoded_view.hpp +++ /dev/null @@ -1,180 +0,0 @@ -// -// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) -// Copyright (c) 2022 Alan de Freitas (alandefreitas@gmail.com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/CPPAlliance/url -// - -#ifndef BOOST_URL_IMPL_SEGMENTS_ENCODED_VIEW_HPP -#define BOOST_URL_IMPL_SEGMENTS_ENCODED_VIEW_HPP - -#include -#include -#include -#include - -namespace boost { -namespace urls { - -class segments_encoded_view::iterator -{ - friend segments_encoded_view; - - detail::segments_encoded_iterator_impl impl_; - - iterator( - string_view s, - std::size_t nseg) noexcept - : impl_(s, nseg) - { - } - - // end ctor - iterator( - string_view s, - std::size_t nseg, - int) noexcept - : impl_(s, nseg, 0) - { - } - -public: - using value_type = std::string; - using reference = string_view; - using pointer = void const*; - using difference_type = std::ptrdiff_t; - using iterator_category = - std::bidirectional_iterator_tag; - - iterator() = default; - - iterator(iterator const&) noexcept = default; - - iterator& operator=(iterator const&) noexcept = default; - - reference - operator*() const noexcept - { - return impl_.s_; - } - - iterator& - operator++() noexcept - { - impl_.increment(); - return *this; - } - - iterator& - operator--() noexcept - { - impl_.decrement(); - return *this; - } - - iterator - operator++(int) noexcept - { - auto tmp = *this; - ++*this; - return tmp; - } - - iterator - operator--(int) noexcept - { - auto tmp = *this; - --*this; - return tmp; - } - - bool - operator==( - iterator const& other) const noexcept - { - return impl_.equal(other.impl_); - } - - bool - operator!=( - iterator const& other) const noexcept - { - return !impl_.equal(other.impl_); - } -}; - -//------------------------------------------------ -// -// Members -// -//------------------------------------------------ - -inline -segments_encoded_view:: -segments_encoded_view() noexcept - : s_("") - , n_(0) -{ -} - -inline -bool -segments_encoded_view:: -is_absolute() const noexcept -{ - return s_.starts_with('/'); -} - -//------------------------------------------------ -// -// Element Access -// -//------------------------------------------------ - -inline -string_view -segments_encoded_view:: -front() const noexcept -{ - BOOST_ASSERT(! empty()); - return *begin(); -} - -inline -string_view -segments_encoded_view:: -back() const noexcept -{ - BOOST_ASSERT(! empty()); - return *--end(); -} - -//------------------------------------------------ -// -// Capacity -// -//------------------------------------------------ - -inline -bool -segments_encoded_view:: -empty() const noexcept -{ - return size() == 0; -} - -inline -std::size_t -segments_encoded_view:: -size() const noexcept -{ - return n_; -} - -} // urls -} // boost - -#endif diff --git a/include/boost/url/impl/segments_encoded_view.ipp b/include/boost/url/impl/segments_encoded_view.ipp index 8bf29262..6cbc4189 100644 --- a/include/boost/url/impl/segments_encoded_view.ipp +++ b/include/boost/url/impl/segments_encoded_view.ipp @@ -11,85 +11,16 @@ #ifndef BOOST_URL_IMPL_SEGMENTS_ENCODED_VIEW_IPP #define BOOST_URL_IMPL_SEGMENTS_ENCODED_VIEW_IPP -#include -#include -#include -#include -#include -#include -#include -#include +#include namespace boost { namespace urls { -//------------------------------------------------ - segments_encoded_view:: -segments_encoded_view( - string_view s, - std::size_t nseg) noexcept - : s_(s) - , n_(nseg) +operator +segments_view() const noexcept { -} - -//------------------------------------------------ -// -// Iterators -// -//------------------------------------------------ - -auto -segments_encoded_view:: -begin() const noexcept -> - iterator -{ - return iterator(s_, n_); -} - -auto -segments_encoded_view:: -end() const noexcept -> - iterator -{ - return iterator(s_, n_, 0); -} - -//------------------------------------------------ -// -// Friends -// -//------------------------------------------------ - -result -parse_path(string_view s) noexcept -{ - if(s.empty()) - return segments_encoded_view(); - if(s[0] == '/') - { - auto rv = grammar::parse( - s, detail::path_abempty_rule); - if(! rv) - return rv.error(); - return segments_encoded_view( - rv->string(), - detail::path_segments( - rv->string(), - rv->size())); - } - { - auto rv = grammar::parse( - s, detail::path_rootless_rule); - if(! rv) - return rv.error(); - return segments_encoded_view( - rv->string(), - detail::path_segments( - rv->string(), - rv->size())); - } + return { ref_ }; } } // urls diff --git a/include/boost/url/impl/segments_ref.hpp b/include/boost/url/impl/segments_ref.hpp new file mode 100644 index 00000000..717d6b84 --- /dev/null +++ b/include/boost/url/impl/segments_ref.hpp @@ -0,0 +1,240 @@ +// +// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2022 Alan de Freitas (alandefreitas@gmail.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/CPPAlliance/url +// + +#ifndef BOOST_URL_IMPL_SEGMENTS_REF_HPP +#define BOOST_URL_IMPL_SEGMENTS_REF_HPP + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace boost { +namespace urls { + +inline +segments_ref:: +segments_ref( + url_base& u) noexcept + : segments_base( + detail::path_ref(u.u_)) + , u_(&u) +{ +} + +inline +segments_ref& +segments_ref:: +operator=(segments_ref const& other) +{ + assign(other.begin(), other.end()); + return *this; +} + +inline +segments_ref& +segments_ref:: +operator=(segments_view const& other) +{ + assign(other.begin(), other.end()); + return *this; +} + +inline +segments_ref& +segments_ref:: +operator=(std::initializer_list< + string_view> init) +{ + assign(init.begin(), init.end()); + return *this; +} + +inline +segments_ref:: +operator +segments_view() const noexcept +{ + return segments_view(ref_); +} + +inline +void +segments_ref:: +clear() noexcept +{ + erase(begin(), end()); +} + +template +auto +segments_ref:: +assign(FwdIt first, FwdIt last) -> + typename std::enable_if< + std::is_convertible::reference, + string_view>::value>::type +{ + u_->edit_segments( + begin().it_, + end().it_, + detail::make_segments_iter( + first, last)); +} + +//------------------------------------------------ + +inline +auto +segments_ref:: +insert( + iterator before, + std::initializer_list< + string_view> init) -> + iterator +{ + return insert( + before, + init.begin(), + init.end()); +} + +template +auto +segments_ref:: +insert( + iterator before, + FwdIt first, + FwdIt last) -> + typename std::enable_if< + std::is_convertible::reference, + string_view>::value, + iterator>::type +{ + return insert(before, first, last, + typename std::iterator_traits< + FwdIt>::iterator_category{}); +} + +template +auto +segments_ref:: +insert( + iterator before, + FwdIt first, + FwdIt last, + std::forward_iterator_tag) -> + iterator +{ + u_->edit_segments( + before.it_, + before.it_, + detail::make_segments_iter( + first, last)); + return std::next(begin(), before.it_.index); +} + +//------------------------------------------------ + +inline +auto +segments_ref:: +replace( + iterator pos, + string_view s) -> + iterator +{ + return replace( + pos, std::next(pos), + &s, &s + 1); +} + +inline +auto +segments_ref:: +replace( + iterator from, + iterator to, + std::initializer_list< + string_view> init) -> + iterator +{ + return replace( + from, + to, + init.begin(), + init.end()); +} + +template +auto +segments_ref:: +replace( + iterator from, + iterator to, + FwdIt first, + FwdIt last) -> + typename std::enable_if< + std::is_convertible::reference, + string_view>::value, + iterator>::type +{ + u_->edit_segments( + from.it_, + to.it_, + detail::make_segments_iter( + first, last)); + return std::next(begin(), from.it_.index); +} + +//------------------------------------------------ + +inline +auto +segments_ref:: +erase( + iterator pos) noexcept -> + iterator +{ + return erase(pos, std::next(pos)); +} + +//------------------------------------------------ + +inline +void +segments_ref:: +push_back( + string_view s) +{ + insert(end(), s); +} + +inline +void +segments_ref:: +pop_back() noexcept +{ + erase(std::prev(end())); +} + +} // urls +} // boost + +#endif diff --git a/include/boost/url/impl/segments_ref.ipp b/include/boost/url/impl/segments_ref.ipp new file mode 100644 index 00000000..3e93808e --- /dev/null +++ b/include/boost/url/impl/segments_ref.ipp @@ -0,0 +1,63 @@ +// +// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2022 Alan de Freitas (alandefreitas@gmail.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/CPPAlliance/url +// + +#ifndef BOOST_URL_IMPL_SEGMENTS_REF_IPP +#define BOOST_URL_IMPL_SEGMENTS_REF_IPP + +#include +#include +#include +#include + +namespace boost { +namespace urls { + +//------------------------------------------------ +// +// Modifiers +// +//------------------------------------------------ + +auto +segments_ref:: +insert( + iterator before, + string_view s) -> + iterator +{ + u_->edit_segments( + before.it_, + before.it_, + detail::make_segments_iter( + &s, &s + 1)); + return std::next(begin(), before.it_.index); +} + +auto +segments_ref:: +segments_ref:: +erase( + iterator first, + iterator last) noexcept -> + iterator +{ + string_view s; + u_->edit_segments( + first.it_, + last.it_, + detail::make_segments_encoded_iter( + &s, &s)); + return std::next(begin(), first.it_.index); +} + +} // urls +} // boost + +#endif diff --git a/include/boost/url/impl/segments_view.hpp b/include/boost/url/impl/segments_view.hpp index 42f7221a..259b1cc0 100644 --- a/include/boost/url/impl/segments_view.hpp +++ b/include/boost/url/impl/segments_view.hpp @@ -11,161 +11,9 @@ #ifndef BOOST_URL_IMPL_SEGMENTS_VIEW_HPP #define BOOST_URL_IMPL_SEGMENTS_VIEW_HPP -#include -#include - namespace boost { namespace urls { -class segments_view:: - iterator -{ - detail::segments_iterator_impl impl_; - - friend segments_view; - - iterator( - string_view s, - std::size_t nseg) noexcept - : impl_(s, nseg) - { - } - - // end ctor - iterator( - string_view s, - std::size_t nseg, - int) noexcept - : impl_(s, nseg, 0) - { - } - -public: - using value_type = std::string; - using reference = decode_view; - using pointer = void const*; - using difference_type = std::ptrdiff_t; - using iterator_category = - std::bidirectional_iterator_tag; - - iterator() noexcept = default; - - iterator(iterator const&) noexcept = default; - - iterator& - operator=(iterator const&) noexcept = default; - - decode_view - operator*() const noexcept - { - return impl_.dereference(); - } - - iterator& - operator++() noexcept - { - impl_.increment(); - return *this; - } - - iterator& - operator--() noexcept - { - impl_.decrement(); - return *this; - } - - iterator - operator++(int) noexcept - { - auto tmp = *this; - ++*this; - return tmp; - } - - iterator - operator--(int) noexcept - { - auto tmp = *this; - --*this; - return tmp; - } - - bool - operator==( - iterator const& other) const noexcept - { - return impl_.equal(other.impl_); - } - - bool - operator!=( - iterator const& other) const noexcept - { - return !impl_.equal(other.impl_); - } -}; - -//------------------------------------------------ -// -// Members -// -//------------------------------------------------ - -inline -bool -segments_view:: -is_absolute() const noexcept -{ - return s_.starts_with('/'); -} - -//------------------------------------------------ -// -// Element Access -// -//------------------------------------------------ - -inline -decode_view -segments_view:: -front() const noexcept -{ - BOOST_ASSERT(! empty()); - return *begin(); -} - -inline -decode_view -segments_view:: -back() const noexcept -{ - BOOST_ASSERT(! empty()); - return *--end(); -} - -//------------------------------------------------ -// -// Capacity -// -//------------------------------------------------ - -inline -bool -segments_view:: -empty() const noexcept -{ - return size() == 0; -} - -inline -std::size_t -segments_view:: -size() const noexcept -{ - return n_; -} - } // urls } // boost diff --git a/include/boost/url/impl/segments_view.ipp b/include/boost/url/impl/segments_view.ipp deleted file mode 100644 index 056d6c21..00000000 --- a/include/boost/url/impl/segments_view.ipp +++ /dev/null @@ -1,61 +0,0 @@ -// -// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) -// Copyright (c) 2022 Alan de Freitas (alandefreitas@gmail.com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/CPPAlliance/url -// - -#ifndef BOOST_URL_IMPL_SEGMENTS_VIEW_IPP -#define BOOST_URL_IMPL_SEGMENTS_VIEW_IPP - -#include -#include - -namespace boost { -namespace urls { - -//------------------------------------------------ -// -// Iterators -// -//------------------------------------------------ - -auto -segments_view:: -begin() const noexcept -> - iterator -{ - return iterator(s_, n_); -} - -auto -segments_view:: -end() const noexcept -> - iterator -{ - return iterator(s_, n_, 0); -} - -void -segments_view:: -write(std::ostream& os) const -{ - auto first = begin(); - auto const last = end(); - if( first != last ) - { - if( is_absolute() ) - os << "/"; - os << *first; - while( ++first != last ) - os << '/' << *first; - } -} - -} // urls -} // boost - -#endif diff --git a/include/boost/url/impl/url_base.ipp b/include/boost/url/impl/url_base.ipp index 83861fb2..7ef8679e 100644 --- a/include/boost/url/impl/url_base.ipp +++ b/include/boost/url/impl/url_base.ipp @@ -31,7 +31,6 @@ #include #include #include -#include #include #include #include @@ -67,13 +66,14 @@ op_t( u.check_invariants(); } - void url_base:: op_t:: move(char* dest, char const* src, std::size_t n) noexcept { + if(! n) + return; if(s) return detail::move_chars( dest, src, n, *s); @@ -148,10 +148,7 @@ remove_scheme() noexcept BOOST_ASSERT(u_.len(id_path) > 0); if(s_[p] == '/') return false; - string_view const s( - s_ + p, segment(1) - p); - if(s.find_first_of(':') == - string_view::npos) + if(! first_segment().contains(':')) return false; return true; }(); @@ -350,10 +347,10 @@ set_encoded_userinfo( auto const s0 = s.substr(0, pos); auto const s1 = s.substr(pos + 1); auto const n0 = - detail::re_encoded_size(s0, opt, + detail::re_encoded_size_unchecked(s0, opt, detail::user_chars); auto const n1 = - detail::re_encoded_size(s1, opt, + detail::re_encoded_size_unchecked(s1, opt, detail::password_chars); auto dest = set_userinfo_impl(n0 + n1 + 1, op); @@ -364,11 +361,11 @@ set_encoded_userinfo( s0, opt, detail::user_chars); - dest[n0] = ':'; + *dest++ = ':'; u_.decoded_[id_pass] = detail::re_encode_unchecked( - dest + n0 + 1, - dest + n0 + 1 + n1, + dest, + dest + n1, s1, opt, detail::password_chars); @@ -378,7 +375,7 @@ set_encoded_userinfo( { // user auto const n = - detail::re_encoded_size( + detail::re_encoded_size_unchecked( s, opt, detail::user_chars); auto dest = set_userinfo_impl(n, op); u_.decoded_[id_user] = @@ -423,7 +420,7 @@ set_encoded_user( op_t op(*this, &detail::ref(s)); encode_opts opt; auto const n = - detail::re_encoded_size( + detail::re_encoded_size_unchecked( s, opt, detail::user_chars); auto dest = set_user_impl(n, op); u_.decoded_[id_user] = @@ -485,7 +482,7 @@ set_encoded_password( op_t op(*this, &detail::ref(s)); encode_opts opt; auto const n = - detail::re_encoded_size(s, opt, + detail::re_encoded_size_unchecked(s, opt, detail::password_chars); auto dest = set_password_impl(n, op); u_.decoded_[id_pass] = @@ -625,7 +622,7 @@ set_encoded_host( // reg-name op_t op(*this, &detail::ref(s)); encode_opts opt; - auto const n = detail::re_encoded_size( + auto const n = detail::re_encoded_size_unchecked( s, opt, detail::host_chars); auto dest = set_host_impl(n, op); u_.decoded_[id_host] = @@ -715,7 +712,7 @@ set_encoded_host_address( // reg-name op_t op(*this, &detail::ref(s)); encode_opts opt; - auto const n = detail::re_encoded_size( + auto const n = detail::re_encoded_size_unchecked( s, opt, detail::host_chars); auto dest = set_host_impl(n, op); u_.decoded_[id_host] = @@ -849,7 +846,7 @@ set_encoded_host_name( op_t op(*this, &detail::ref(s)); encode_opts opt; - auto const n = detail::re_encoded_size( + auto const n = detail::re_encoded_size_unchecked( s, opt, allowed); auto dest = set_host_impl(n, op); u_.decoded_[id_host] = @@ -1065,20 +1062,13 @@ url_base:: set_path( string_view s) { - grammar::detail::copied_strings< - BOOST_URL_STACK_BYTES> buf( - this->string()); - s = buf.maybe_copy(s); - int abs_hint; - if(s.starts_with('/')) - abs_hint = 1; - else - abs_hint = 0; edit_segments( - 0, u_.nseg_, - detail::plain_path_iter(s), - detail::plain_path_iter(s), - abs_hint); + detail::segments_iter_impl( + detail::path_ref(u_)), + detail::segments_iter_impl( + detail::path_ref(u_), 0), + detail::path_iter(s), + s.starts_with('/')); return *this; } @@ -1087,21 +1077,13 @@ url_base:: set_encoded_path( pct_string_view s) { - grammar::detail::copied_strings< - BOOST_URL_STACK_BYTES> buf( - this->string()); - s = buf.maybe_copy(s); - int abs_hint; - if(s.starts_with('/')) - abs_hint = 1; - else - abs_hint = 0; edit_segments( - 0, - u_.nseg_, - detail::enc_path_iter(s), - detail::enc_path_iter(s), - abs_hint); + detail::segments_iter_impl( + detail::path_ref(u_)), + detail::segments_iter_impl( + detail::path_ref(u_), 0), + detail::path_encoded_iter(s), + s.starts_with('/')); return *this; } @@ -1237,7 +1219,7 @@ set_encoded_fragment( op_t op(*this, &detail::ref(s)); encode_opts opt; auto const n = - detail::re_encoded_size(s, + detail::re_encoded_size_unchecked(s, opt, detail::fragment_chars); auto dest = resize_impl( id_frag, n + 1, op); @@ -1620,9 +1602,7 @@ set_scheme_impl( { if(u_.nseg_ == 0) return false; - // VFALCO Should not be calling segment() - if(segment(1) < - u_.offset(id_path) + 2) + if(first_segment().size() < 2) return false; auto const src = s_ + p; if(src[0] != '.') @@ -1789,294 +1769,253 @@ set_port_impl( //------------------------------------------------ -/* Return offset of i-th segment -*/ -pos_t +// return the first segment of the path. +// this is needed for some algorithms. +string_view url_base:: -segment( - std::size_t i) const noexcept +first_segment() const noexcept { - if(i == 0) - return u_.offset(id_path); - if(i == u_.nseg_) - return u_.offset(id_query); - BOOST_ASSERT(i < u_.nseg_); - auto it = s_ + u_.offset(id_path) + - detail::path_prefix( - u_.get(id_path)); - BOOST_ASSERT(it < s_ + - u_.offset(id_query)); - for(;;) - { - while(*it != '/') - ++it; - BOOST_ASSERT(it < s_ + - u_.offset(id_query)); - --i; - if(i == 0) - break; - ++it; - } - return it - s_; + if(u_.nseg_ == 0) + return {}; + auto const p0 = u_.cs_ + + u_.offset(id_path) + + detail::path_prefix( + u_.get(id_path)); + auto const end = u_.cs_ + + u_.offset(id_query); + if(u_.nseg_ == 1) + return string_view( + p0, end - p0); + auto p = p0; + while(*p != '/') + ++p; + BOOST_ASSERT(p < end); + return string_view(p0, p - p0); } -/* Remove segments [first, last) and make - room for nseg new segments inserted - before first, with space for n chars - including prefix and/or separators. - - Segments look like this, where ## is the - malleable prefix and '/' is a literal slash: - - ##_0_ /_1_ /_2_ /_3_ -*/ -char* -url_base:: -resize_segments( - std::size_t i0, - std::size_t i1, - std::size_t n, - std::size_t nseg, - op_t& op) -{ - BOOST_ASSERT(i1 >= i0); - BOOST_ASSERT(i1 - i0 <= u_.nseg_); - - // new number of segments - std::size_t const nseg1 = - u_.nseg_ + nseg - (i1 - i0); - - // [p0, p1) range to replace - auto p0 = segment(i0); - auto p1 = segment(i1); - if(i1 == 0) - { - p1 += detail::path_prefix( - u_.get(id_path)); - } - else if( - i0 == 0 && - nseg == 0 && - i1 < u_.nseg_) - { - // Remove the slash from segment i1 - // if it is becoming the new first - // segment. - BOOST_ASSERT(s_[p1] == '/'); - ++p1; - } - - // old size of [p0, p1) - auto const n0 = p1 - p0; - - // adjust capacity - std::size_t c = size() + n - n0; - if (c == 0) - return nullptr; - - reserve_impl(c, op); - - // start of output - auto dest = s_ + p0; - - // move and size - op.move( - dest + n, - s_ + p1, - size() - p1); - u_.set_size( - id_path, - u_.len(id_path) - - (n0 - n)); - u_.nseg_ = nseg1; - s_[size()] = '\0'; - return dest; -} - -// insert or replace [i0, i1) -// with [it0, it1) void url_base:: edit_segments( - std::size_t i0, - std::size_t i1, - detail::any_path_iter&& it0, - detail::any_path_iter&& it1, - int abs_hint) + detail::segments_iter_impl const& it0, + detail::segments_iter_impl const& it1, + detail::any_segments_iter&& src, + // -1 = preserve + // 0 = make relative (can fail) + // 1 = make absolute + int absolute) { - op_t op(*this); + // Iterator doesn't belong to this url + BOOST_ASSERT(it0.ref.alias_of(u_)); - bool abs; - if( has_authority() || - abs_hint == -1) - abs = is_path_absolute(); - else if(abs_hint == 1) - abs = true; - else - abs = false; + // Iterator doesn't belong to this url + BOOST_ASSERT(it1.ref.alias_of(u_)); -/* - Measure the number of characters and - the number of segments we are inserting. - This does not include leading or trailing - separators. -*/ - error_code ec; - std::size_t n = 0; + // Iterator is in the wrong order + BOOST_ASSERT(it0.index <= it1.index); + + // Iterator is out of range + BOOST_ASSERT(it0.index <= u_.nseg_); + + // Iterator is out of range + BOOST_ASSERT(it1.index <= u_.nseg_); + + bool const is_abs = is_path_absolute(); + if(has_authority()) + absolute = 1; // must be absolute + else if(absolute < 0) + absolute = is_abs; // preserve + auto const path_pos = u_.offset(id_path); + +//------------------------------------------------ +// +// Measure the number of encoded characters +// of output, and the number of inserted +// segments including internal separators. +// std::size_t nseg = 0; - bool more = it0.measure(n, ec); - if(ec.failed()) - detail::throw_system_error(ec); - if(more) + std::size_t nchar = 0; + if(src.measure(nchar)) { for(;;) { ++nseg; - more = it0.measure(n, ec); - if(ec.failed()) - detail::throw_system_error(ec); - if(! more) + if(! src.measure(nchar)) break; - ++n; + ++nchar; } } -/* Calculate prefix size for new segment range: - 0 = "" - 1 = "/" - 2 = "./" - 3 = "/./" +//------------------------------------------------ +// +// Calculate [pos0, pos1) to remove +// + auto pos0 = it0.pos; + if(it0.index == 0) + { + // patch pos for prefix + pos0 = 0; + } + auto pos1 = it1.pos; + if(it1.index == 0) + { + // patch pos for prefix + pos1 = detail::path_prefix( + u_.get(id_path)); + } + else if( + it0.index == 0 && + it1.index < u_.nseg_ && + nseg == 0) + { + // Remove the slash from segment it1 + // if it is becoming the new first + // segment. + ++pos1; + } - This is a malleable prefix that might need to - change according the URL scheme and authority. - -*/ - int prefix; - if(i0 > 0) +//------------------------------------------------ +// +// Calculate output prefix +// +// 0 = "" +// 1 = "/" +// 2 = "./" +// 3 = "/./" +// + int prefix = 0; + if(it0.index > 0) { - if(nseg > 0) - prefix = 1; - else - prefix = 0; + // first segment unchanged + prefix = nseg > 0; } - else if( - it0.front == "." && - nseg > 1) + else if(nseg > 0) { - if( abs || - has_authority()) + // first segment from src + if(! src.front.empty()) + { + if( src.front == "." && + nseg > 1) + { + prefix = 2 + absolute; + } + else if( has_scheme() || + ! src.front.contains(':')) + { + prefix = absolute; + } + else + { + prefix = 2 + absolute; + } + } + else if(absolute) + { prefix = 3; - else - prefix = 2; - } - else if(has_authority()) - { - if(nseg == 0) - prefix = abs ? 1 : 0; - else if(! it0.front.empty()) - prefix = 1; - else - prefix = 3; - } - else if( - nseg > 1 && - it0.front.empty()) - { - prefix = 3; - } - else if( - ! abs && - ! has_scheme() && - ( - it0.front.find_first_of( - ':') != string_view::npos || - it0.front.empty())) - { - if (nseg > 0) - prefix = 2; - else + } + else if(has_scheme() || + src.front.contains(':')) + { prefix = 0; - } - else if( - abs && - nseg > 0 && - it0.front.empty()) - { - BOOST_ASSERT( - ! has_authority()); - prefix = 3; + } + else + { + prefix = 2; + } } else { - if(abs) - prefix = 1; - else - prefix = 0; + // first segment from it1 + auto const p = + u_.cs_ + path_pos + it1.pos; + switch(u_.cs_ + + u_.offset(id_query) - p) + { + case 0: + // points to end + prefix = absolute; + break; + default: + BOOST_ASSERT(*p == '/'); + if(p[1] != '/') + { + prefix = absolute; + break; + } + // empty + BOOST_FALLTHROUGH; + case 1: + // empty + BOOST_ASSERT(*p == '/'); + if(absolute) + { + if(has_authority()) + prefix = 1; + else + prefix = 3; + } + else + { + if( has_scheme() || + it1.dereference().contains(':')) + prefix = 0; + else + prefix = 2; + } + break; + } } -/* Calculate suffix size for the new segments: - 0 = "" - 1 = "/" +// append '/' to new segs +// if inserting at front. + int const suffix = + it1.index == 0 && + u_.nseg_ > 0 && + nseg > 0; - This extra suffix should cover the case where - insertion at the first indexes leaves a - missing slash in a relative path: - - "file.txt" - -> insert "etc" as first segment - -> becomes "etc" "/" "file.txt" - - "file.txt" - -> insert "path/to" as first segments - -> becomes "path/to" "/" "file.txt" - - "the/file.txt" - -> insert "path/to" as first segments - -> becomes "path/to" "/" "the/file.txt" - - The extra slash is not necessary when - insertion is not at the first position: - - "path/file.txt" - -> insert "to/the" as second segment - -> becomes "path" "/to/the" "/file.txt" - - The extra slash is not necessary when - the following position already has a slash - (i.e. other existing valid segments): - - "/path/to/the/file.txt" - -> replace "etc" as first segment - -> becomes "/etc" "/to/the/file.txt" - -*/ - int suffix; - // inserting non-empty segments at the - // beginning of non-empty segments - if( nseg > 0 && - i0 == 0 && - i1 == 0 && - u_.nseg_ != 0) +//------------------------------------------------ +// +// Resize +// + op_t op(*this, src.input()); + char* dest; + char const* end; { - suffix = 1; - } - else - { - suffix = 0; - } - - // copy - n += prefix + suffix; - auto dest = resize_segments( - i0, i1, n, nseg, op); - char const *const last = dest + n; - -/* Write all characters in the destination: - - The output proceeds as: - - prefix [ segment [ '/' segment ] ] suffix + // amount we are removing + auto const nremove = pos1 - pos0; + nchar = prefix + nchar + suffix; +/* if( nchar > max_size() || + prefix + suffix > max_size() - nchar) + detail::throw_length_error( + "url_base::max_size"); + if(nchar > max_size() - size()) + detail::throw_length_error( + "url_base::max_size"); */ + auto const new_size = + size() + nchar - nremove; + reserve_impl(new_size, op); + dest = s_ + path_pos + pos0; + op.move( + dest + nchar, + s_ + path_pos + pos1, + size() - path_pos - pos1); + u_.set_size( + id_path, + u_.len(id_path) + nchar - nremove); + BOOST_ASSERT(size() == new_size); + end = dest + nchar; + u_.nseg_ = u_.nseg_ + nseg - ( + it1.index - it0.index); + if(s_) + s_[size()] = '\0'; + } + +//------------------------------------------------ +// +// Output segments and internal separators: +// +// prefix [ segment [ '/' segment ] ] suffix +// switch(prefix) { case 3: @@ -2093,33 +2032,28 @@ edit_segments( default: break; } -/* - Output each segment, placing a slash - only in between new segments. Leading - or trailing separators are handled - outside the loop. -*/ + src.rewind(); if(nseg > 0) { for(;;) { - it1.copy(dest, last); + src.copy(dest, end); if(--nseg == 0) break; *dest++ = '/'; } + if(suffix) + *dest++ = '/'; } - if(suffix == 1) - *dest++ = '/'; + // VFALCO This could be better u_.decoded_[id_path] = detail::decode_bytes_unchecked( - u_.get(id_path)); -} + u_.get(id_path));} //------------------------------------------------ -/* The query param range [i0, i1) +/* The query param range [first, last) is resized to contain `n` chars and nparam elements. */ diff --git a/include/boost/url/param.hpp b/include/boost/url/param.hpp index 29c0285a..f683c3e3 100644 --- a/include/boost/url/param.hpp +++ b/include/boost/url/param.hpp @@ -800,7 +800,7 @@ struct param_pct_view */ explicit operator - param() + param() const { return param( static_cast(key), @@ -808,6 +808,13 @@ struct param_pct_view has_value); } + operator + param_view() const noexcept + { + return param_view( + key, value, has_value); + } + #ifndef BOOST_URL_DOCS // arrow support param_pct_view const* diff --git a/include/boost/url/params_const_encoded_view.hpp b/include/boost/url/params_const_encoded_view.hpp index 091ac141..91d441c4 100644 --- a/include/boost/url/params_const_encoded_view.hpp +++ b/include/boost/url/params_const_encoded_view.hpp @@ -76,6 +76,8 @@ public: @par Postconditions @code this->buffer().data() == other.buffer().data() + @endcode + @par Complexity Constant. diff --git a/include/boost/url/params_const_view.hpp b/include/boost/url/params_const_view.hpp index 49cf3117..097a6f03 100644 --- a/include/boost/url/params_const_view.hpp +++ b/include/boost/url/params_const_view.hpp @@ -82,6 +82,8 @@ public: @par Postconditions @code this->buffer().data() == other.buffer().data() + @endcode + @par Complexity Constant. diff --git a/include/boost/url/params_encoded_view.hpp b/include/boost/url/params_encoded_view.hpp index 61fa6ccd..44319f4b 100644 --- a/include/boost/url/params_encoded_view.hpp +++ b/include/boost/url/params_encoded_view.hpp @@ -60,13 +60,13 @@ class url_base; character buffer: @li @ref append : Only `end()`. @li @ref assign, @ref clear, - `operator=` : All elements. - @li @ref erase : Erased elements and all - elements after (including `end()`). - @li @ref insert : All elements at or after + `operator=` : All params. + @li @ref erase : Erased params and all + params after (including `end()`). + @li @ref insert : All params at or after the insertion point (including `end()`). @li @ref replace, @ref set : Modified - elements and all elements + params and all params after (including `end()`). */ class params_encoded_view @@ -76,7 +76,6 @@ class params_encoded_view url_base* u_ = nullptr; - explicit params_encoded_view( url_base& u) noexcept; @@ -149,6 +148,9 @@ public: of the query parameters are replaced by the contents of the initializer-list. +
+ All iterators are invalidated. + @par Preconditions None of character buffers referenced by `init` may overlap the character buffer of @@ -166,8 +168,10 @@ public: @par Exception Safety Strong guarantee. Calls to allocate may throw. - Exception thrown when init contains - an invalid percent-encoding. + Exceptions thrown on invalid input. + + @throw system_error + `init` contains an invalid percent-encoding. @param init The list of params to assign. */ @@ -176,6 +180,12 @@ public: param_pct_view> init); /** Conversion + + @par Complexity + Constant. + + @par Exception Safety + Throws nothing. */ operator params_const_encoded_view() const noexcept; @@ -241,7 +251,7 @@ public: //-------------------------------------------- - /** Assign elements + /** Assign params This function replaces the entire contents of the view with the params @@ -268,8 +278,10 @@ public: @par Exception Safety Strong guarantee. Calls to allocate may throw. - Exception thrown when params contain - an invalid percent-encoding. + Exceptions thrown on invalid input. + + @throw system_error + `init` contains an invalid percent-encoding. @param init The list of params to assign. */ @@ -277,7 +289,7 @@ public: assign(std::initializer_list< param_pct_view> init); - /** Assign elements + /** Assign params This function replaces the entire contents of the view with the params @@ -302,8 +314,10 @@ public: @par Exception Safety Strong guarantee. Calls to allocate may throw. - Exception thrown when params contain - an invalid percent-encoding. + Exceptions thrown on invalid input. + + @throw system_error + The range contains an invalid percent-encoding. @param first, last The range of params to assign. @@ -314,7 +328,7 @@ public: //-------------------------------------------- - /** Append elements + /** Append params This function appends a param to the view. @@ -334,8 +348,10 @@ public: @par Exception Safety Strong guarantee. Calls to allocate may throw. - Exception thrown when param contains - an invalid percent-encoding. + Exceptions thrown on invalid input. + + @throw system_error + `p` contains an invalid percent-encoding. @return An iterator to the new element. @@ -345,7 +361,7 @@ public: append( param_pct_view const& p); - /** Append elements + /** Append params This function appends a range of params to the view. @@ -368,8 +384,10 @@ public: @par Exception Safety Strong guarantee. Calls to allocate may throw. - Exception thrown when params contain - an invalid percent-encoding. + Exceptions thrown on invalid input. + + @throw system_error + The range contains an invalid percent-encoding. @return An iterator to the first new element. @@ -381,7 +399,7 @@ public: append( FwdIt first, FwdIt last); - /** Append elements + /** Append params This function appends the params in an initializer-list to the view. @@ -402,8 +420,10 @@ public: @par Exception Safety Strong guarantee. Calls to allocate may throw. - Exception thrown when params contain - an invalid percent-encoding. + Exceptions thrown on invalid input. + + @throw system_error + `init` contains an invalid percent-encoding. @return An iterator to the first new element. @@ -415,7 +435,7 @@ public: //-------------------------------------------- - /** Insert elements + /** Insert params This function inserts a param before the specified position. @@ -434,8 +454,10 @@ public: @par Exception Safety Strong guarantee. Calls to allocate may throw. - Exception thrown when param contains - an invalid percent-encoding. + Exceptions thrown on invalid input. + + @throw system_error + `p` contains an invalid percent-encoding. @return An iterator to the inserted element. @@ -451,7 +473,7 @@ public: iterator before, param_pct_view const& p); - /** Insert elements + /** Insert params This function inserts a range of params before the specified position. @@ -480,8 +502,10 @@ public: @par Exception Safety Strong guarantee. Calls to allocate may throw. - Exception thrown when params contain - an invalid percent-encoding. + Exceptions thrown on invalid input. + + @throw system_error + The range contains an invalid percent-encoding. @return An iterator to the first element inserted, or `before` if @@ -501,7 +525,7 @@ public: FwdIt first, FwdIt last); - /** Insert elements + /** Insert params This function inserts the params in an initializer-list before @@ -526,8 +550,10 @@ public: @par Exception Safety Strong guarantee. Calls to allocate may throw. - Exception thrown when params contain - an invalid percent-encoding. + Exceptions thrown on invalid input. + + @throw system_error + `init` contains an invalid percent-encoding. @return An iterator to the first element inserted, or `before` if @@ -547,7 +573,7 @@ public: //-------------------------------------------- - /** Erase elements + /** Erase params This function removes an element from the container. @@ -579,9 +605,9 @@ public: iterator erase(iterator pos) noexcept; - /** Erase elements + /** Erase params - This function removes a range of elements + This function removes a range of params from the container.
@@ -602,14 +628,14 @@ public: the removed range. @param first, last The range of - elements to erase. + params to erase. */ iterator erase( iterator first, iterator last) noexcept; - /** Erase elements + /** Erase params
All iterators are invalidated. @@ -627,10 +653,12 @@ public: Linear in `this->url().encoded_query().size()`. @par Exception Safety - Exception thrown when key contain - an invalid percent-encoding. + Exceptions thrown on invalid input. - @return The number of elements removed + @throw system_error + `key` contains an invalid percent-encoding. + + @return The number of params removed from the container. @param key The key to match. @@ -645,12 +673,12 @@ public: BOOST_URL_DECL std::size_t erase( - string_view key, - ignore_case_param ic = {}); + pct_string_view key, + ignore_case_param ic = {}) noexcept; //-------------------------------------------- - /** Replace elements + /** Replace params This function replaces the contents of the element at `pos` with the @@ -675,8 +703,10 @@ public: @par Exception Safety Strong guarantee. Calls to allocate may throw. - Exception thrown when param contains - an invalid percent-encoding. + Exceptions thrown on invalid input. + + @throw system_error + `p` contains an invalid percent-encoding. @return An iterator to the element. @@ -689,10 +719,10 @@ public: iterator pos, param_pct_view const& p); - /** Replace elements + /** Replace params This function replaces a range of - elements with the params in an + params with the params in an initializer-list.
@@ -710,14 +740,16 @@ public: @par Exception Safety Strong guarantee. Calls to allocate may throw. - Exception thrown when params contain - an invalid percent-encoding. + Exceptions thrown on invalid input. + + @throw system_error + `init` contains an invalid percent-encoding. @return An iterator to the first element inserted, or one past `to` if `init.size() == 0`. - @param from,to The range of elements + @param from,to The range of params to replace. @param init The list of params to assign. @@ -729,10 +761,10 @@ public: std::initializer_list< param_pct_view> init); - /** Replace elements + /** Replace params This function replaces a range of - elements with a range of params. + params with a range of params.
All iterators that are equal to @@ -754,14 +786,16 @@ public: @par Exception Safety Strong guarantee. Calls to allocate may throw. - Exception thrown when params contain - an invalid percent-encoding. + Exceptions thrown on invalid input. + + @throw system_error + The range contains an invalid percent-encoding. @return An iterator to the first element inserted, or one past `to` if `first == last`. - @param from,to The range of elements to + @param from,to The range of params to replace. @param first, last The range of params @@ -836,6 +870,10 @@ public: @par Exception Safety Strong guarantee. Calls to allocate may throw. + Exceptions thrown on invalid input. + + @throw system_error + `value` contains an invalid percent-encoding. @return An iterator to the element. @@ -859,9 +897,9 @@ public: `this->contains( key, ic )`. @li If key is contained in the view - then one of the matching elements has + then one of the matching params has its value changed to the specified value. - The remaining elements with a matching + The remaining params with a matching key are erased. Otherwise, @li If `key` is not contained in the @@ -891,6 +929,11 @@ public: @par Exception Safety Strong guarantee. Calls to allocate may throw. + Exceptions thrown on invalid input. + + @throw system_error + `key` or `value` contain an invalid + percent-encoding. @return An iterator to the appended or modified element. diff --git a/include/boost/url/parse_path.hpp b/include/boost/url/parse_path.hpp new file mode 100644 index 00000000..fd629497 --- /dev/null +++ b/include/boost/url/parse_path.hpp @@ -0,0 +1,55 @@ +// +// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2022 Alan de Freitas (alandefreitas@gmail.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/CPPAlliance/url +// + +#ifndef BOOST_URL_PARSE_PATH_HPP +#define BOOST_URL_PARSE_PATH_HPP + +#include +#include +#include + +namespace boost { +namespace urls { + +/** Parse a string and return an encoded segment view + + This function parses the string and returns the + corresponding path object if the string is valid, + otherwise returns an error. + + @par BNF + @code + path = [ "/" ] segment *( "/" segment ) + @endcode + + @par Exception Safety + No-throw guarantee. + + @return A valid view on success, otherwise an + error code. + + @param s The string to parse + + @par Specification + @li 3.3. Path (rfc3986) + + @see + @ref parse_path, + @ref segments_encoded_view. +*/ +BOOST_URL_DECL +result +parse_path(string_view s) noexcept; + +} // urls +} // boost + +#endif diff --git a/include/boost/url/pct_string_view.hpp b/include/boost/url/pct_string_view.hpp index a6d3f967..d2195e71 100644 --- a/include/boost/url/pct_string_view.hpp +++ b/include/boost/url/pct_string_view.hpp @@ -12,7 +12,7 @@ #include #include -#include // VFALCO rethink this +#include #include #include #include @@ -245,6 +245,15 @@ public: return std::string(s_); } +#ifndef BOOST_URL_DOCS + // arrow support + pct_string_view const* + operator->() const noexcept + { + return this; + } +#endif + //-------------------------------------------- // iterator support diff --git a/include/boost/url/segments.hpp b/include/boost/url/segments.hpp deleted file mode 100644 index 70e1b9b4..00000000 --- a/include/boost/url/segments.hpp +++ /dev/null @@ -1,731 +0,0 @@ -// -// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) -// Copyright (c) 2022 Alan de Freitas (alandefreitas@gmail.com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/CPPAlliance/url -// - -#ifndef BOOST_URL_SEGMENTS_HPP -#define BOOST_URL_SEGMENTS_HPP - -#include -#include -#include -#include -#include - -namespace boost { -namespace urls { - -#ifndef BOOST_URL_DOCS -class url_base; -class segments_encoded; -#endif - -/** A container referencing a random-access range of modifiable, percent-decoded path 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. -*/ -class segments - : private detail::parts_base -{ - url_base* u_ = nullptr; - - friend class url_base; - friend class segments_encoded; - - segments(url_base& u) - : u_(&u) - { - } - -public: - /** A read-only bidirectional iterator to a decoded segment. - - This is a read-only bidirectional iterator to - the decoded segments. - - */ -#ifdef BOOST_URL_DOCS - using iterator = __see_below__; -#else - class iterator; -#endif - - /// @copydoc iterator - using const_iterator = iterator; - - /** 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 = std::string; - - /** A type which can represent a segment as a reference - - This type does not make a copy of a segment - and ownership is retained by the container. - */ - using reference = decode_view; - - /// @copydoc reference - using const_reference = decode_view; - - /** An unsigned integer type - */ - using size_type = std::size_t; - - /** A signed integer type - */ - using difference_type = std::ptrdiff_t; - - //-------------------------------------------- - // - // Members - // - //-------------------------------------------- - - /** Returns true if this contains an absolute path. - - Absolute paths always start with a - forward slash ('/'). - */ - bool - is_absolute() const noexcept; - - /** Constructor - - After the copy, both views will point to - the same underlying object. - - Ownership is not transferred; the caller - is responsible for ensuring the lifetime - of the character buffer extends until - it is no longer referenced. - - @par Complexity - Constant - - @par Exception Safety - Throws nothing - - */ - segments(segments const&) = default; - - /** Assignment - - After the assignment, both views will point to - the same underlying object. - - Ownership is not transferred; the caller - is responsible for ensuring the lifetime - of the character buffer extends until - it is no longer referenced. - - @par Complexity - Constant - - @par Exception Safety - Throws nothing - - */ - segments& - operator=(segments const&) & = default; - - /** Replace the contents of the container - - This function replaces the contents with - an initializer list of unencoded strings. - - The behavior is undefined if 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 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. - */ - segments& - operator=(std::initializer_list init); - - /** 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 - std::is_convertible< std::iterator_traits< FwdIt >::reference_type, string_view >::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< - std::is_convertible::reference, - string_view>::value>::type -#endif - assign(FwdIt first, FwdIt last); - - //-------------------------------------------- - // - // Element Access - // - //-------------------------------------------- - - /** Access the first element. - - Returns a reference to the first element. - - @par Precondition - `not empty()` - - @par Complexity - Constant. - */ - decode_view - front() const; - - /** Access the last element. - - Returns a reference to the last element. - - @par Precondition - `not empty()` - - @par Complexity - Constant. - */ - decode_view - back() const; - - //-------------------------------------------- - // - // Iterators - // - //-------------------------------------------- - - /** Return an iterator to the first element. - - If the path is empty, @ref end() is returned. - - @par Complexity - Constant. - - @par Exception Safety - No-throw guarantee. - */ - iterator - begin() const noexcept; - - /** Return an iterator to the element following the last element. - - The element acts as a placeholder; attempting - to access it results in undefined behavior. - - @par Complexity - Constant. - - @par Exception Safety - No-throw guarantee. - */ - iterator - end() const noexcept; - - //-------------------------------------------- - // - // Capacity - // - //-------------------------------------------- - - /** Check if the path has no segments. - - Returns `true` if there are no segments in the - path, i.e. @ref size() returns 0. - - @par Complexity - Constant. - - @par Exception Safety - No-throw guarantee. - */ - bool - empty() const noexcept; - - /** Return the number of elements in the array. - - This returns the number of segments in the path. - - @par Complexity - Constant. - - @par Exception Safety - No-throw guarantee. - */ - std::size_t - size() const noexcept; - - //-------------------------------------------- - // - // Modifiers - // - //-------------------------------------------- - - /** Remove the contents of the container - - This function removes all the segments - from the path, leaving the - underlying URL with an empty path. - - @par Postconditions - @code - empty() == true - @endcode - - @par Exception Safety - Throws nothing. - */ - 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( - iterator before, - string_view s); - - /** Insert a range of segments - - This function inserts a range of unencoded - strings passed as an initializer-list. - 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 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. - - @note Behavior is undefined if any elements of - initializer_list belong to the container - - @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 - unencoded segments to insert. - */ - iterator - insert( - iterator before, - std::initializer_list init); - - /** 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 - std::is_convertible< std::iterator_traits< FwdIt >::reference_type, string_view >::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 - - @note Behavior is undefined if any elements of the range - belong to the container - - @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< - std::is_convertible::reference, - string_view>::value, - iterator>::type -#endif - insert( - iterator before, - FwdIt first, - FwdIt last); - -private: - template - iterator - insert( - iterator before, - FwdIt first, - FwdIt last, - std::input_iterator_tag) = delete; - - template - iterator - insert( - iterator before, - FwdIt first, - FwdIt last, - std::forward_iterator_tag); -public: - - //-------------------------------------------- - - iterator - replace( - iterator pos, - string_view s); - - iterator - replace( - iterator from, - iterator to, - std::initializer_list< - string_view> init); - - template -#ifdef BOOST_URL_DOCS - iterator -#else - typename std::enable_if< - std::is_convertible::reference, - string_view>::value, - iterator>::type -#endif - replace( - iterator from, - iterator to, - FwdIt first, - FwdIt last); - - //-------------------------------------------- - - /** 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. - */ - iterator - erase( - 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( - iterator first, - 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 - */ - void - push_back( - string_view s); - - /** 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. - */ - 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_base.hpp b/include/boost/url/segments_base.hpp new file mode 100644 index 00000000..3ce42596 --- /dev/null +++ b/include/boost/url/segments_base.hpp @@ -0,0 +1,292 @@ +// +// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2022 Alan de Freitas (alandefreitas@gmail.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/CPPAlliance/url +// + +#ifndef BOOST_URL_SEGMENTS_BASE_HPP +#define BOOST_URL_SEGMENTS_BASE_HPP + +#include +#include +#include +#include +#include + +namespace boost { +namespace urls { + +/** Common functionality for containers + + This base class is used by the library + to provide common member functions for + containers. This cannot be instantiated + directly; Instead, use one of the + containers or functions: + + @par Containers + @li @ref segments_ref + @li @ref segments_view + @li @ref segments_encoded_ref + @li @ref segments_encoded_view +*/ +class segments_base + : private detail::parts_base +{ + detail::path_ref ref_; + + friend class url_view_base; + friend class segments_ref; + friend class segments_view; + + segments_base( + detail::path_ref const& ref) noexcept + : ref_(ref) + { + } + + segments_base() = default; + +public: + /** A Bidirectional iterator to a path segment + + Objects of this type allow iteration + through the segments in the path. + Any percent-escapes in returned strings + are decoded first. + The values returned are read-only; + changes to segments must be made + through the container instead, if the + container supports modification. + +
+ + The strings produced when iterators are + dereferenced belong to the iterator and + become invalidated when that particular + iterator is incremented, decremented, + or destroyed. + */ +#ifdef BOOST_URL_DOCS + using iterator = __see_below__; +#else + class iterator; +#endif + + /// @copydoc iterator + using const_iterator = iterator; + + /** The value type + + Values of this type represent a segment + where unique ownership is retained by + making a copy. + + @par Example + @code + segments_view::value_type ps( *url_view( "/path/to/file.txt" ).segments().back() ); + @endcode + */ + using value_type = std::string; + + /** The reference type + + This is the type of value returned when + iterators of the view are dereferenced. + */ + using reference = string_view; + + /// @copydoc reference + using const_reference = string_view; + + /** An unsigned integer type used to represent size. + */ + using size_type = std::size_t; + + /** A signed integer type used to represent differences. + */ + using difference_type = std::ptrdiff_t; + + //-------------------------------------------- + // + // Observers + // + //-------------------------------------------- + + /** Return the referenced character buffer. + + This function returns the character + buffer referenced by the view. + The returned string may contain + percent escapes. + + @par Example + @code + assert( url_view( "/path/to/file.txt" ).segments().buffer() == "/path/to/file.txt" ); + @endcode + + @par Complexity + Constant. + + @par Exception Safety + Throws nothing. + */ + BOOST_URL_DECL + pct_string_view + buffer() const noexcept; + + /** Returns true if this references an absolute path. + + Absolute paths always start with a + forward slash ('/'). + + @par Example + @code + assert( url_view( "/path/to/file.txt" ).segments().is_absolute() == true ); + @endcode + + @par Complexity + Constant. + + @par Exception Safety + Throws nothing. + */ + BOOST_URL_DECL + bool + is_absolute() const noexcept; + + /** Return true if there are no segments + + @par Example + @code + assert( ! url_view( "/index.htm" ).segments().empty() ); + @endcode + + @par Complexity + Constant. + + @par Exception Safety + Throws nothing. + */ + BOOST_URL_DECL + bool + empty() const noexcept; + + /** Return the number of segments + + @par Example + @code + assert( url_view( "/path/to/file.txt" ).segments().size() == 3 ); + @endcode + + @par Complexity + Constant. + + @par Exception Safety + Throws nothing. + */ + BOOST_URL_DECL + std::size_t + size() const noexcept; + + /** Return the first segment + + This function returns a string with the + first segment of the path without any + leading or trailing '/' separators. + Any percent-escapes in the string are + decoded first. + + @par Preconditions + @code + this->empty() == false + @endcode + + @par Effects + @code + return *begin(); + @endcode + + @par Example + @code + assert( url_view( "/path/to/file.txt" ).segments().front() == "path" ); + @endcode + + @par Complexity + Linear in `this->front().size()`. + + @par Exception Safety + Calls to allocate may throw. + */ + BOOST_URL_DECL + std::string + front() const noexcept; + + /** Return the last segment + + @par Preconditions + @code + this->empty() == false + @endcode + + @par Example + @code + assert( url_view( "/path/to/file.txt" ).segments().back() == "file.txt" ); + @endcode + + @par Preconditions + @code + this->empty() == false + @endcode + + @par Effects + @code + return *--end(); + @endcode + + @par Complexity + Linear in `this->back().size()`. + + @par Exception Safety + Calls to allocate may throw. + */ + BOOST_URL_DECL + std::string + back() const noexcept; + + /** Return an iterator to the beginning + + @par Complexity + Linear in `this->front().size()` or + constant if `this->empty()`. + + @par Exception Safety + Throws nothing. + */ + BOOST_URL_DECL + iterator + begin() const noexcept; + + /** Return an iterator to the end + + @par Complexity + Constant. + + @par Exception Safety + Throws nothing. + */ + BOOST_URL_DECL + iterator + end() const noexcept; +}; + +} // urls +} // boost + +#include + +#endif diff --git a/include/boost/url/segments_encoded.hpp b/include/boost/url/segments_encoded.hpp deleted file mode 100644 index b21679d3..00000000 --- a/include/boost/url/segments_encoded.hpp +++ /dev/null @@ -1,757 +0,0 @@ -// -// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) -// Copyright (c) 2022 Alan de Freitas (alandefreitas@gmail.com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/CPPAlliance/url -// - -#ifndef BOOST_URL_SEGMENTS_ENCODED_HPP -#define BOOST_URL_SEGMENTS_ENCODED_HPP - -#include -#include -#include -#include -#include -#include - -namespace boost { -namespace urls { - -#ifndef BOOST_URL_DOCS -class url_base; -#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_encoded se = u.encoded_segments(); - - for( segments_encoded::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. -*/ -class segments_encoded - : private detail::parts_base -{ - url_base* u_ = nullptr; - - friend class url_base; - - segments_encoded( - url_base& u) noexcept; - -public: - /** A read-only bidirectional iterator to an encoded segment. - - This is a read-only bidirectional iterator to - the encoded segments. - - */ -#ifdef BOOST_URL_DOCS - using iterator = __see_below__; -#else - class iterator; -#endif - - /// @copydoc iterator - using const_iterator = iterator; - - /** 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 = std::string; - - /** A type which can represent a segment as a const reference - - This type does not make a copy of a segment - and ownership is retained by the container. - */ - using reference = string_view; - - /// @copydoc reference - using const_reference = string_view; - - /** An unsigned integer type - */ - using size_type = std::size_t; - - /** A signed integer type - */ - using difference_type = std::ptrdiff_t; - - //-------------------------------------------- - // - // Members - // - //-------------------------------------------- - - /** Returns true if this contains an absolute path. - - Absolute paths always start with a - forward slash ('/'). - */ - bool - is_absolute() const noexcept; - - /** Return this container as percent-decoded segments - */ - BOOST_URL_DECL - segments - decoded() const; - - /** Constructor - - After the copy, both views will point to - the same underlying object. - - Ownership is not transferred; the caller - is responsible for ensuring the lifetime - of the character buffer extends until - it is no longer referenced. - - @par Complexity - Constant - - @par Exception Safety - Throws nothing - - */ - segments_encoded(segments_encoded const&) = default; - - /** Assignment - - After the assignment, both views will point to - the same underlying object. - - Ownership is not transferred; the caller - is responsible for ensuring the lifetime - of the character buffer extends until - it is no longer referenced. - - @par Complexity - Constant - - @par Exception Safety - Throws nothing - */ - segments_encoded& - operator=(segments_encoded const&) & = default; - - /** 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 Example - @code - url u = parse_relative_uri( "/path/to/file.txt" ); - u.encoded_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 - */ - segments_encoded& - operator=(std::initializer_list init); - - /** 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 - std::is_convertible< std::iterator_traits< FwdIt >::reference_type, string_view >::value == true - @endcode - - @par Example - @code - url u = parse_relative_uri( "/path/to/file.txt" ); - - segments_encoded se = u.encoded_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< - std::is_convertible::reference, - string_view>::value>::type -#endif - assign(FwdIt first, FwdIt last); - - //-------------------------------------------- - // - // Element Access - // - //-------------------------------------------- - - /** Access the first element. - - Returns a reference to the first element. - - @par Precondition - `not empty()` - - @par Complexity - Constant. - */ - const_reference - front() const noexcept; - - /** Access the last element. - - Returns a reference to the last element. - - @par Precondition - `not empty()` - - @par Complexity - Constant. - */ - const_reference - back() const noexcept; - - //-------------------------------------------- - // - // Iterators - // - //-------------------------------------------- - - /** Return a const iterator to the first element. - - If the path is empty, @ref end() is returned. - - @par Complexity - Constant. - - @par Exception Safety - No-throw guarantee. - */ - BOOST_URL_DECL - iterator - begin() const noexcept; - - /** Return an iterator to the element following the last element. - - The element acts as a placeholder; attempting - to access it results in undefined behavior. - - @par Complexity - Constant. - - @par Exception Safety - No-throw guarantee. - */ - BOOST_URL_DECL - iterator - end() 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. - - @par Complexity - Constant. - - @par Exception Safety - No-throw guarantee. - */ - bool - empty() const noexcept; - - /** Return the number of elements in the container - - This function returns the number of - segments in the underlying path. Empty - segments count towards this total. - - @par Exception Safety - Throws nothing. - */ - std::size_t - size() const noexcept; - - //-------------------------------------------- - // - // Modifiers - // - //-------------------------------------------- - - /** 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. - */ - 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. - - @note Behavior is undefined if the element - belongs to the container - - @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( - iterator before, - string_view s); - - /** 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 Example - @code - url u = parse_relative_uri( "/path/file.txt" ); - - segments_encoded se = u.encoded_segments(); - - se.insert( u.end() - 1, { "to", "the" } ); - - assert( u.encoded_path() == "/path/to/the/file.txt") ); - @endcode - - @note Behavior is undefined if any elements of the - initializer_list belong to the container - - @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 - */ - iterator - insert( - iterator before, - std::initializer_list init); - - /** 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 - std::is_convertible< std::iterator_traits< FwdIt >::reference_type, string_view >::value == true - @endcode - - @par Example - @code - url u = parse_relative_uri( "/path/file.txt" ); - - segments_encoded se = u.encoded_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 - - @note Behavior is undefined if any elements of the range - belong to the container - - @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< - std::is_convertible::reference, - string_view>::value, - iterator>::type -#endif - insert( - iterator before, - FwdIt first, - FwdIt last); - -private: - template - iterator - insert( - iterator before, - FwdIt first, - FwdIt last, - std::input_iterator_tag) = delete; - - template - iterator - insert( - iterator before, - FwdIt first, - FwdIt last, - std::forward_iterator_tag); -public: - - //-------------------------------------------- - - /** 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_encoded se = u.encoded_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. - */ - iterator - erase( - 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_encoded se = u.encoded_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( - iterator first, - iterator last) noexcept; - - //-------------------------------------------- - - iterator - replace( - iterator pos, - string_view s); - - iterator - replace( - iterator from, - iterator to, - std::initializer_list< - string_view> init); - - template -#ifdef BOOST_URL_DOCS - iterator -#else - typename std::enable_if< - std::is_convertible::reference, - string_view>::value, - iterator>::type -#endif - replace( - iterator from, - iterator to, - FwdIt first, - FwdIt last); - - //-------------------------------------------- - - /** 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_encoded().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 - */ - void - push_back( - string_view s); - - /** 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_encoded().pop_back(); - - assert( u.encoded_path() == "/path/to" ); - @endcode - - @par Exception Safety - Throws nothing. - */ - 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_base.hpp b/include/boost/url/segments_encoded_base.hpp new file mode 100644 index 00000000..2fd866c4 --- /dev/null +++ b/include/boost/url/segments_encoded_base.hpp @@ -0,0 +1,298 @@ +// +// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2022 Alan de Freitas (alandefreitas@gmail.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/CPPAlliance/url +// + +#ifndef BOOST_URL_SEGMENTS_ENCODED_BASE_HPP +#define BOOST_URL_SEGMENTS_ENCODED_BASE_HPP + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace boost { +namespace urls { + +/** Common functionality for containers + + This base class is used by the library + to provide common member functions for + containers. This cannot be instantiated + directly; Instead, use one of the + containers or functions: + + @par Containers + @li @ref segments_ref + @li @ref segments_view + @li @ref segments_encoded_ref + @li @ref segments_encoded_view +*/ +class segments_encoded_base + : private detail::parts_base +{ + detail::path_ref ref_; + + friend class url_view_base; + friend class segments_encoded_ref; + friend class segments_encoded_view; + + segments_encoded_base( + detail::path_ref const& ref) noexcept + : ref_(ref) + { + } + + segments_encoded_base() = default; + +public: + /** A Bidirectional iterator to a path segment + + Objects of this type allow iteration + through the segments in the path. + Strings returned by iterators may + contain percent escapes. + The values returned are read-only; + changes to segments must be made + through the container instead, if the + container supports modification. + +
+ + The strings produced when iterators + are dereferenced refer to the underlying + character buffer. + Ownership is not transferred; the caller + is responsible for ensuring that the + lifetime of the buffer extends until + it is no longer referenced by any + container or iterator. + */ +#ifdef BOOST_URL_DOCS + using iterator = __see_below__; +#else + class iterator; +#endif + + /// @copydoc iterator + using const_iterator = iterator; + + /** The value type + + Values of this type represent a segment + where unique ownership is retained by + making a copy. + + @par Example + @code + segments_encoded_view::value_type ps( *url_view( "/path/to/file.txt" ).encoded_segments().back() ); + @endcode + */ + using value_type = std::string; + + /** The reference type + + This is the type of value returned when + iterators of the view are dereferenced. + */ + using reference = pct_string_view; + + /// @copydoc reference + using const_reference = pct_string_view; + + /** An unsigned integer type used to represent size. + */ + using size_type = std::size_t; + + /** A signed integer type used to represent differences. + */ + using difference_type = std::ptrdiff_t; + + //-------------------------------------------- + // + // Observers + // + //-------------------------------------------- + + /** Return the referenced character buffer. + + This function returns the character + buffer referenced by the view. + The returned string may contain + percent escapes. + + @par Example + @code + assert( url_view( "/path/to/file.txt" ).encoded_segments().buffer() == "/path/to/file.txt" ); + @endcode + + @par Complexity + Constant. + + @par Exception Safety + Throws nothing. + */ + BOOST_URL_DECL + pct_string_view + buffer() const noexcept; + + /** Returns true if this references an absolute path. + + Absolute paths always start with a + forward slash ('/'). + + @par Example + @code + assert( url_view( "/path/to/file.txt" ).encoded_segments().is_absolute() == true ); + @endcode + + @par Complexity + Constant. + + @par Exception Safety + Throws nothing. + */ + BOOST_URL_DECL + bool + is_absolute() const noexcept; + + /** Return true if there are no segments + + @par Example + @code + assert( ! url_view( "/index.htm" ).encoded_segments().empty() ); + @endcode + + @par Complexity + Constant. + + @par Exception Safety + Throws nothing. + */ + BOOST_URL_DECL + bool + empty() const noexcept; + + /** Return the number of segments + + @par Example + @code + assert( url_view( "/path/to/file.txt" ).encoded_segments().size() == 3 ); + @endcode + + @par Complexity + Constant. + + @par Exception Safety + Throws nothing. + */ + BOOST_URL_DECL + std::size_t + size() const noexcept; + + /** Return the first segment + + This function returns a string with the + first segment of the path without any + leading or trailing '/' separators. + Any percent-escapes in the string are + decoded first. + + @par Preconditions + @code + this->empty() == false + @endcode + + @par Effects + @code + return *begin(); + @endcode + + @par Example + @code + assert( url_view( "/path/to/file.txt" ).encoded_segments().front() == "path" ); + @endcode + + @par Complexity + Constant. + + @par Exception Safety + Throws nothing. + */ + BOOST_URL_DECL + pct_string_view + front() const noexcept; + + /** Return the last segment + + @par Preconditions + @code + this->empty() == false + @endcode + + @par Example + @code + assert( url_view( "/path/to/file.txt" ).encoded_segments().back() == "file.txt" ); + @endcode + + @par Preconditions + @code + this->empty() == false + @endcode + + @par Effects + @code + return *--end(); + @endcode + + @par Complexity + Constant. + + @par Exception Safety + Throws nothing. + */ + BOOST_URL_DECL + pct_string_view + back() const noexcept; + + /** Return an iterator to the beginning + + @par Complexity + Linear in `this->front().size()` or + constant if `this->empty()`. + + @par Exception Safety + Throws nothing. + */ + BOOST_URL_DECL + iterator + begin() const noexcept; + + /** Return an iterator to the end + + @par Complexity + Constant. + + @par Exception Safety + Throws nothing. + */ + BOOST_URL_DECL + iterator + end() const noexcept; +}; + +} // urls +} // boost + +#include + +#endif diff --git a/include/boost/url/segments_encoded_ref.hpp b/include/boost/url/segments_encoded_ref.hpp new file mode 100644 index 00000000..2f25d63a --- /dev/null +++ b/include/boost/url/segments_encoded_ref.hpp @@ -0,0 +1,681 @@ +// +// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2022 Alan de Freitas (alandefreitas@gmail.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/CPPAlliance/url +// + +#ifndef BOOST_URL_SEGMENTS_ENCODED_REF_HPP +#define BOOST_URL_SEGMENTS_ENCODED_REF_HPP + +#include +#include +#include + +namespace boost { +namespace urls { + +#ifndef BOOST_URL_DOCS +class url_base; +class segments_encoded_view; +#endif + +/** A view representing path segments in a URL + + Objects of this type are used to interpret + the path as a bidirectional view of segment + strings. + + The view does not retain ownership of the + elements and instead references the original + character buffer. The caller is responsible + for ensuring that the lifetime of the buffer + extends until it is no longer referenced. + + The view is modifiable; calling non-const + members will cause changes to the referenced + url. + + @par Example + @code + url u( "/path/to/file.txt" ); + + segments_encoded_ref ps = u.segments(); + @endcode + + Strings produced when elements are returned + have type @ref param_pct_view and represent + encoded strings. Strings passed to member + functions may contain percent escapes, and + throw exceptions on invalid inputs. + + @par Iterator Invalidation + Changes to the underlying character buffer + can invalidate iterators which reference it. + Modifications made through the container will + invalidate some iterators to the underlying + character buffer: + @li @ref push_back : Only `end()`. + @li @ref assign, @ref clear, + `operator=` : All elements. + @li @ref erase : Erased elements and all + elements after (including `end()`). + @li @ref insert : All elements at or after + the insertion point (including `end()`). + @li @ref replace : Modified + elements and all elements + after (including `end()`). + + @see + @ref segments_encoded_view, + @ref segments_view, + @ref segments_ref. +*/ +class segments_encoded_ref + : public segments_encoded_base +{ + friend class url_base; + + url_base* u_ = nullptr; + + segments_encoded_ref( + url_base& u) noexcept; + +public: + //-------------------------------------------- + // + // Special Members + // + //-------------------------------------------- + + /** Constructor + + After construction, both views will + reference the same url. Ownership is not + transferred; the caller is responsible + for ensuring the lifetime of the url + extends until it is no longer + referenced. + + @par Postconditions + @code + &this->url() == &other.url(); + @endcode + + @par Complexity + Constant. + + @par Exception Safety + Throws nothing. + + @param other The other view. + */ + segments_encoded_ref( + segments_encoded_ref const& other) = default; + + /** Assignment + + The previous contents of this are + replaced by a copy of the other segments. + +
+ All iterators are invalidated. + + @note + The strings referenced by `other` + must not come from the underlying url, + or else the behavior is undefined. + + @par Effects + @code + this->assign( other.begin(), other.end() ); + @endcode + + @par Complexity + Linear in `other.buffer().size()`. + + @par Exception Safety + Strong guarantee. + Calls to allocate may throw. + + @param other The segments to assign. + */ + /** @{ */ + segments_encoded_ref& + operator=(segments_encoded_ref const& other); + + segments_encoded_ref& + operator=(segments_encoded_view const& other); + /** @} */ + + /** Assignment + + The previous contents of this are + replaced by a copy of the segments in + the initializer-list, whose strings may + contain percent-escapes. + +
+ All iterators are invalidated. + + @par Preconditions + None of character buffers referenced by + `init` may overlap the character buffer of + the underlying url, or else the behavior + is undefined. + + @par Effects + @code + this->assign( init.begin(), init.end() ); + @endcode + + @par Complexity + Linear in `init.size()`. + + @par Exception Safety + Strong guarantee. + Calls to allocate may throw. + Exceptions thrown on invalid input. + + @throw system_error + `init` contains an invalid percent-encoding. + + @param init The list of segments to assign. + */ + segments_encoded_ref& + operator=(std::initializer_list< + pct_string_view> init); + + /** Conversion + */ + operator + segments_encoded_view() const noexcept; + + //-------------------------------------------- + // + // Observers + // + //-------------------------------------------- + + /** Return the referenced url + + This function returns the url referenced + by the view. + + @par Example + @code + url u( "/path/to/file.txt" ); + + assert( &u.encoded_segments().url() == &u ); + @endcode + + @par Exception Safety + @code + Throws nothing. + @endcode + */ + url_base& + url() const noexcept + { + return *u_; + } + + //-------------------------------------------- + // + // Modifiers + // + //-------------------------------------------- + + /** Clear the contents of the container + +
+ All iterators are invalidated. + + @par Effects + @code + this->url().set_encoded_path( "" ); + @endcode + + @par Postconditions + @code + this->empty() == true + @endcode + + @par Complexity + Linear in `this->url().size()`. + + @par Exception Safety + Throws nothing. + */ + void + clear() noexcept; + + /** Assign params + + This function replaces the entire + contents of the view with the params + in the initializer-list. + +
+ All iterators are invalidated. + + @note + The strings referenced by the params + must not come from the underlying url, + or else the behavior is undefined. + + @par Example + @code + url u; + + u.encoded_params().assign( {{ "first", "John" }, { "last", "Doe" }} ); + @endcode + + @par Complexity + Linear in `init.size()`. + + @par Exception Safety + Strong guarantee. + Calls to allocate may throw. + Exceptions thrown on invalid input. + + @throw system_error + `init` contains an invalid percent-encoding. + + @param init The list of params to assign. + */ + segments_encoded_ref& + assign(std::initializer_list init); + + /** Assign segments + + This function replaces the entire + contents of the view with the segments + in the range. + +
+ All iterators are invalidated. + + @note + The strings referenced by the segments + must not come from the underlying url, + or else the behavior is undefined. + + @par Mandates + @code + std::is_convertible< std::iterator_traits< FwdIt >::reference_type, param_pct_view >::value == true + @endcode + + @par Complexity + Linear in the size of the range. + + @par Exception Safety + Strong guarantee. + Calls to allocate may throw. + Exceptions thrown on invalid input. + + @throw system_error + The range contains an invalid percent-encoding. + + @param first, last The range of segments + to assign. + */ + template +#ifdef BOOST_URL_DOCS + void +#else + typename std::enable_if< + std::is_convertible::reference, + pct_string_view>::value>::type +#endif + assign(FwdIt first, FwdIt last); + + //-------------------------------------------- + + /** Insert segments + + This function inserts a string as a + segment, before the specified position. + Escapes in the string are preserved, + and reserved characters in the string + are percent-escaped in the result. + +
+ All iterators that are equal to + `before` or come after are invalidated. + + @par Example + @code + @endcode + + @par Complexity + Linear in `this->url().size() + s.size()`. + + @par Exception Safety + Strong guarantee. + Calls to allocate may throw. + Exceptions thrown on invalid input. + + @throw system_error + `s` contains an invalid percent-encoding. + + @return An iterator to the inserted + segment. + + @param before An iterator before which + the segment will be inserted. This may + be equal to `end()`. + + @param s The segment to insert. + */ + BOOST_URL_DECL + iterator + insert( + iterator before, + pct_string_view s); + + /** Insert segments + + This function inserts a range of + segment strings before the specified + position. + Escapes in the strings are preserved, + and reserved characters in the string + are percent-escaped in the result. + +
+ All iterators that are equal to + `before` or come after are invalidated. + + @note + The strings referenced by the segments + must not come from the underlying url, + or else the behavior is undefined. + + @par Example + @code + @endcode + + @par Mandates + @code + std::is_convertible< std::iterator_traits< FwdIt >::reference_type, pct_string_view >::value == true + @endcode + + @par Complexity + Linear in `this->url().size()`. + + @par Exception Safety + Strong guarantee. + Calls to allocate may throw. + Exceptions thrown on invalid input. + + @throw system_error + The range contains an invalid percent-encoding. + + @return An iterator to the first + element inserted, or `before` if + `init.empty()`. + + @param before An iterator before which + the element will be inserted. This may + be equal to `end()`. + + @param init The list of segments + to insert. + */ + iterator + insert( + iterator before, + std::initializer_list< + pct_string_view> init); + + /** Insert segments + + This function inserts the strings in + an initializer-list as segments + before the specified position. + Escapes in the strings are preserved, + and reserved characters in the string + are percent-escaped in the result. + +
+ All iterators that are equal to + `before` or come after are invalidated. + + @note + The strings referenced by the segments + must not come from the underlying url, + or else the behavior is undefined. + + @par Example + @code + @endcode + + @par Complexity + Linear in `this->url().size`. + + @par Exception Safety + Strong guarantee. + Calls to allocate may throw. + Exceptions thrown on invalid input. + + @throw system_error + The range contains an invalid percent-encoding. + + @return An iterator to the first + element inserted, or `before` if + `init.size() == 0`. + + @param before An iterator before which + the element will be inserted. This may + be equal to `end()`. + + @param first, last The range of segments to insert. + */ + template +#ifdef BOOST_URL_DOCS + iterator +#else + typename std::enable_if< + std::is_convertible::reference, + pct_string_view>::value, + iterator>::type +#endif + insert( + iterator before, + FwdIt first, + FwdIt last); + + //-------------------------------------------- + + /** Erase segments + + This function removes a segment from + the container. + +
+ All iterators that are equal to + `pos` or come after are invalidated. + + @par Example + @code + @endcode + + @par Complexity + Linear in `this->url().size()`. + + @par Exception Safety + Throws nothing. + + @return An iterator to one past + the removed segment. + + @param pos An iterator to the element. + */ + iterator + erase( + iterator pos) noexcept; + + /** Erase segments + + This function removes a range of segments + from the container. + +
+ All iterators that are equal to + `first` or come after are invalidated. + + @par Example + @code + @endcode + + @par Complexity + Linear in `this->url().size()`. + + @par Exception Safety + Throws nothing. + + @return An iterator to one past + the removed range. + + @param first, last The range of + params to erase. + */ + BOOST_URL_DECL + iterator + erase( + iterator first, + iterator last) noexcept; + + //-------------------------------------------- + + iterator + replace( + iterator pos, + pct_string_view s); + + iterator + replace( + iterator from, + iterator to, + pct_string_view s); + + iterator + replace( + iterator from, + iterator to, + std::initializer_list< + pct_string_view> init); + + template +#ifdef BOOST_URL_DOCS + iterator +#else + typename std::enable_if< + std::is_convertible::reference, + pct_string_view>::value, + iterator>::type +#endif + replace( + iterator from, + iterator to, + FwdIt first, + FwdIt last); + + //-------------------------------------------- + + /** 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_encoded_ref().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 + */ + void + push_back( + pct_string_view s); + + /** 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_encoded_ref().pop_back(); + + assert( u.encoded_path() == "/path/to" ); + @endcode + + @par Exception Safety + Throws nothing. + */ + void + pop_back() noexcept; + +private: + template + iterator + insert( + iterator before, + FwdIt first, + FwdIt last, + std::input_iterator_tag) = delete; + + template + iterator + insert( + iterator before, + FwdIt first, + FwdIt last, + std::forward_iterator_tag); +}; + +} // urls +} // boost + +// This is in +// +// #include + +#endif diff --git a/include/boost/url/segments_encoded_view.hpp b/include/boost/url/segments_encoded_view.hpp index c4c25d6e..6a7e1493 100644 --- a/include/boost/url/segments_encoded_view.hpp +++ b/include/boost/url/segments_encoded_view.hpp @@ -13,6 +13,7 @@ #include #include +#include #include #include #include @@ -21,392 +22,127 @@ namespace boost { namespace urls { -#ifndef BOOST_URL_DOCS -// VFALCO is this needed? -class url_view; -#endif +/** A view representing path segments in a URL -/** A bidirectional range of read-only encoded path segment strings. + Objects of this type are used to interpret + the path as a bidirectional view of segment + strings. - Objects of this type represent an iterable - range of path segments, where each segment - is represented by a percent-encoded string. - - Dereferenced iterators return string views - into the underlying character buffer. - - Ownership of the underlying characters is - not transferred; the character buffer used - to construct the container must remain - valid for as long as the container exists. - - A view of encoded segments in a URL's path - can be obtained by calling - @ref url_view::encoded_segments. - Alternatively, to obtain encoded segments - from a path stored in a string call one of - the parsing functions (see below). - - @par Examples - - A path string is parsed into encoded - segments, then each segment is printed to - standard output: + The view does not retain ownership of the + elements and instead references the original + character buffer. The caller is responsible + for ensuring that the lifetime of the buffer + extends until it is no longer referenced. + @par Example @code - segments_encoded_view sev = parse_path( "/path/to/file.txt" ).value(); + url_view u( "/path/to/file.txt" ); - for( auto it = sev.begin(); it != sev.end(); ++it ) - std::cout << *it << std::endl; + segments_encoded_view ps = u.encoded_segments(); + + assert( ps.buffer().data() == u.string().data() ); @endcode - A URL containing a path is parsed, then a - view to the encoded segments is obtained - and formatted to standard output: + Strings produced when elements are returned + have type @ref param_pct_view and represent + encoded strings. Strings passed to member + functions may contain percent escapes, and + throw exceptions on invalid inputs. - @code - url_view u = parse_uri( "http://example.com/path/to/file.txt" ).value(); - - segments_encoded_view sev = u.encoded_segments(); - - std::cout << sev << std::endl; - @endcode - - @par Complexity - - Iterator increment or decrement runs in - linear time on the size of the segment. - All other operations run in constant time. - No operations allocate memory. + @par Iterator Invalidation + Changes to the underlying character buffer + can invalidate iterators which reference it. @see - @ref parse_path, - @ref segments_view. + @ref segments_view, + @ref segments_encoded_ref, + @ref segments_ref. */ class segments_encoded_view + : public segments_encoded_base { - string_view s_; - std::size_t n_ = 0; - friend class url_view_base; + friend class segments_encoded_ref; - BOOST_URL_DECL segments_encoded_view( - string_view s, - std::size_t n) noexcept; + detail::path_ref const& ref) noexcept + : segments_encoded_base(ref) + { + } public: - /** A read-only bidirectional iterator to an encoded segment. - - This is a read-only bidirectional iterator to - the encoded segments. - - */ -#ifdef BOOST_URL_DOCS - using iterator = __see_below__; -#else - class iterator; -#endif - - /// @copydoc iterator - using const_iterator = iterator; - - /** 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 = std::string; - - /** A type which can represent a segment as a const reference - - This type does not make a copy of a segment - and ownership is retained by the container. - - */ - using reference = string_view; - - /// @copydoc reference - using const_reference = string_view; - - /** An unsigned integer type used to represent size. - */ - using size_type = std::size_t; - - /** A signed integer type used to represent differences. - */ - using difference_type = std::ptrdiff_t; - - //-------------------------------------------- - // - // Members - // - //-------------------------------------------- - /** Constructor - A default-constructed instance will be - an empty range. - */ - segments_encoded_view() noexcept; - - /** Constructor - - After the copy, both views will point to - the same underlying object. + After construction, both views will + reference the same character buffer. Ownership is not transferred; the caller is responsible for ensuring the lifetime - of the character buffer extends until - it is no longer referenced. + of the buffer extends until it is no + longer referenced. + + @par Postconditions + @code + this->buffer().data() == other.buffer().data() + @endcode @par Complexity - Constant + Constant. @par Exception Safety Throws nothing - */ - segments_encoded_view(segments_encoded_view const&) noexcept = default; + segments_encoded_view( + segments_encoded_view const&) noexcept = default; - /** Assignment - - After the assignment, both views will point to - the same underlying object. - - Ownership is not transferred; the caller - is responsible for ensuring the lifetime - of the character buffer extends until - it is no longer referenced. - - @par Complexity - Constant - - @par Exception Safety - Throws nothing + /** Assignment (deleted) */ segments_encoded_view& - operator=(segments_encoded_view const&) & = default; + operator=( + segments_encoded_view const&) = delete; - /** Return a view of this container as percent-decoded segments + /** Conversion - This function returns a new view over the - same underlying character buffer where each - segment is returned as a @ref string_view - with percent-decoding applied using the - optionally specified allocator. + This conversion returns a new view which + references the same underlying character + buffer, and whose iterators and members + return ordinary strings with decoding + applied to any percent escapes. - The decoded view does not take ownership of - the underlying character buffer; the caller - is still responsible for ensuring that the - buffer remains valid until all views which - reference it are destroyed. + Ownership is not transferred; the caller + is responsible for ensuring the lifetime + of the buffer extends until it is no + longer referenced. @par Example @code - segments_encoded_view sev = parse_path( "/%70%61%74%68/%74%6f/%66%69%6c%65%2e%74%78%74" ).value(); - - segments_view sv = sev.decoded(); - - std::stringstream ss; - - ss << sv.front() << "/../" << sv.back(); - - assert( ss.string() == "path/../file.txt" ); + segments_view ps = parse_path( "/path/to/file.txt" ).value(); @endcode - @par Exceptions - Calls to allocate may throw. - - @return A view to decoded path segments. - - */ - segments_view - decoded() const - { - return {s_, n_}; - } - - /// @copydoc decoded() - operator segments_view() const - { - return decoded(); - } - - /** Returns true if this contains an absolute path. - - Absolute paths always start with a - forward slash ('/'). - */ - bool - is_absolute() const noexcept; - - //-------------------------------------------- - // - // Element Access - // - //-------------------------------------------- - - /** Access the first element. - - Returns a reference to the first element. - - @par Precondition - `not empty()` + @par Postconditions + @code + segments_view( *this ).buffer().data() == this->buffer().data() + @endcode @par Complexity - Constant. - */ - string_view - front() const noexcept; - - /** Access the last element. - - Returns a reference to the last element. - - @par Precondition - `not empty()` - - @par Complexity - Constant. - */ - string_view - back() const noexcept; - - //-------------------------------------------- - // - // Iterators - // - //-------------------------------------------- - - /** Return an iterator to the first element. - - If the path is empty, @ref end() is returned. - - @par Complexity - Constant. + Constant @par Exception Safety - No-throw guarantee. + Throws nothing */ BOOST_URL_DECL - iterator - begin() const noexcept; + operator + segments_view() const noexcept; - /** Return an iterator to the element following the last element. + //-------------------------------------------- - The element acts as a placeholder; attempting - to access it results in undefined behavior. - - @par Complexity - Constant. - - @par Exception Safety - No-throw guarantee. - */ BOOST_URL_DECL - iterator - end() const noexcept; - - //-------------------------------------------- - // - // Capacity - // - //-------------------------------------------- - - /** Check if the path has no segments. - - Returns `true` if there are no segments in the - path, i.e. @ref size() returns 0. - - @par Complexity - Constant. - - @par Exception Safety - No-throw guarantee. - */ - bool - empty() const noexcept; - - /** Return the number of segments in the path. - - This returns the number of segments in the path. - - @par Complexity - Constant. - - @par Exception Safety - No-throw guarantee. - */ - std::size_t - size() const noexcept; - - //-------------------------------------------- - - // hidden friend friend - std::ostream& - operator<<( - std::ostream& os, - segments_encoded_view const& pv) - { - return os << pv.s_; - } - - BOOST_URL_DECL friend result parse_path(string_view s) noexcept; }; -/** Format the encoded segments to an output stream. - - @param os The output stream. - - @param pv The encoded segments. -*/ -std::ostream& -operator<<( - std::ostream& os, - segments_encoded_view const& pv); - -//---------------------------------------------------------- - -/** Parse a string and return an encoded segment view - - This function parses the string and returns the - corresponding path object if the string is valid, - otherwise returns an error. - - @par BNF - @code - path = [ "/" ] segment *( "/" segment ) - @endcode - - @par Exception Safety - No-throw guarantee. - - @return A valid view on success, otherwise an - error code. - - @param s The string to parse - - @par Specification - @li - 3.3. Path (rfc3986) - - @see - @ref parse_path, - @ref segments_encoded_view. -*/ -BOOST_URL_DECL -result -parse_path(string_view s) noexcept; - } // urls } // boost -#include - #endif diff --git a/include/boost/url/segments_ref.hpp b/include/boost/url/segments_ref.hpp new file mode 100644 index 00000000..b33ade77 --- /dev/null +++ b/include/boost/url/segments_ref.hpp @@ -0,0 +1,182 @@ +// +// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2022 Alan de Freitas (alandefreitas@gmail.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/CPPAlliance/url +// + +#ifndef BOOST_URL_SEGMENTS_REF_HPP +#define BOOST_URL_SEGMENTS_REF_HPP + +#include +#include +#include +#include + +namespace boost { +namespace urls { + +#ifndef BOOST_URL_DOCS +class url_base; +class segments_view; +#endif + +class segments_ref + : public segments_base +{ + url_base* u_ = nullptr; + + friend class url_base; + friend class segments_encoded_ref; + + segments_ref( + url_base& u) noexcept; + +public: + //-------------------------------------------- + // + // Special Members + // + //-------------------------------------------- + + segments_ref( + segments_ref const& other) = default; + + /** @{ */ + segments_ref& + operator=(segments_ref const& other); + + segments_ref& + operator=(segments_view const& other); + /** @} */ + + segments_ref& + operator=(std::initializer_list< + string_view> init); + + /** Conversion + */ + operator + segments_view() const noexcept; + + //-------------------------------------------- + + void + clear() noexcept; + + template +#ifdef BOOST_URL_DOCS + void +#else + typename std::enable_if< + std::is_convertible::reference, + string_view>::value>::type +#endif + assign(FwdIt first, FwdIt last); + + BOOST_URL_DECL + iterator + insert( + iterator before, + string_view s); + + iterator + insert( + iterator before, + std::initializer_list init); + template +#ifdef BOOST_URL_DOCS + iterator +#else + typename std::enable_if< + std::is_convertible::reference, + string_view>::value, + iterator>::type +#endif + insert( + iterator before, + FwdIt first, + FwdIt last); + + //-------------------------------------------- + + iterator + replace( + iterator pos, + string_view s); + + iterator + replace( + iterator from, + iterator to, + std::initializer_list< + string_view> init); + + template +#ifdef BOOST_URL_DOCS + iterator +#else + typename std::enable_if< + std::is_convertible::reference, + string_view>::value, + iterator>::type +#endif + replace( + iterator from, + iterator to, + FwdIt first, + FwdIt last); + + iterator + erase( + iterator pos) noexcept; + + BOOST_URL_DECL + iterator + erase( + iterator first, + iterator last) noexcept; + + void + push_back( + string_view s); + + void + pop_back() noexcept; + +private: + template + iterator + insert( + iterator before, + FwdIt first, + FwdIt last, + std::input_iterator_tag) = delete; + + template + iterator + insert( + iterator before, + FwdIt first, + FwdIt last, + std::forward_iterator_tag); +}; + +} // 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_view.hpp b/include/boost/url/segments_view.hpp index b90266d6..09bfdbc7 100644 --- a/include/boost/url/segments_view.hpp +++ b/include/boost/url/segments_view.hpp @@ -12,279 +12,96 @@ #define BOOST_URL_SEGMENTS_VIEW_HPP #include -#include +#include #include #include namespace boost { namespace urls { -#ifndef BOOST_URL_DOCS -// VFALCO is this needed? -class url_view; -class segments_encoded_view; -#endif +/** A view representing path segments in a URL -/** A bidirectional range of read-only path segment strings with percent-decoding applied. + Objects of this type are used to interpret + the path as a bidirectional view of segment + strings. + + The view does not retain ownership of the + elements and instead references the original + character buffer. The caller is responsible + for ensuring that the lifetime of the buffer + extends until it is no longer referenced. + + @par Example + @code + url_view u( "/path/to/file.txt" ); + + segments_view ps = u.segments(); + + assert( ps.buffer().data() == u.string().data() ); + @endcode + + The strings produced when iterators are + dereferenced belong to the iterator and + become invalidated when that particular + iterator is incremented, decremented, + or destroyed. + Any percent-escapes in returned strings + are decoded first. + + @par Iterator Invalidation + Changes to the underlying character buffer + can invalidate iterators which reference it. @see - @ref segments_encoded_view. + @ref segments_encoded_view, + @ref segments_encoded_ref, + @ref segments_ref. */ class segments_view + : public segments_base { - string_view s_; - std::size_t n_ = 0; - friend class url_view_base; friend class segments_encoded_view; + friend class segments_ref; segments_view( - string_view s, - std::size_t n) - : s_(s) - , n_(n) - {} - -public: - /** A read-only bidirectional iterator to a decoded segment. - - This is a read-only bidirectional iterator to - the decoded segments. - - */ -#ifdef BOOST_URL_DOCS - using iterator = __see_below__; -#else - class iterator; -#endif - - /// @copydoc iterator - using const_iterator = iterator; - - /** 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 = std::string; - - /** A type which can represent a segment as a const reference - - This type does not make a copy of a segment - and ownership is retained by the container. - - */ - using reference = decode_view; - - /// @copydoc reference - using const_reference = decode_view; - - /** The unsigned integer type used to represent size. - */ - using size_type = std::size_t; - - /** The signed integer type used to represent differences. - */ - using difference_type = std::ptrdiff_t; - - //-------------------------------------------- - // - // Members - // - //-------------------------------------------- - - /** Constructor - - Default constructed views represent an - empty path. - */ - segments_view() noexcept = default; - - /** Constructor - - After the copy, both views will point to - the same underlying object. - - Ownership is not transferred; the caller - is responsible for ensuring the lifetime - of the character buffer extends until - it is no longer referenced. - - @par Complexity - Constant - - @par Exception Safety - Throws nothing - */ - segments_view(segments_view const& other) = default; - - /** Assignment - - After the assignment, both views will point to - the same underlying object. - - Ownership is not transferred; the caller - is responsible for ensuring the lifetime - of the character buffer extends until - it is no longer referenced. - - @par Complexity - Constant - - @par Exception Safety - Throws nothing - */ - segments_view& - operator=(segments_view const& other) & = default; - - - /** Returns true if this contains an absolute path. - - Absolute paths always start with a - forward slash ('/'). - */ - bool - is_absolute() const noexcept; - - //-------------------------------------------- - // - // Element Access - // - //-------------------------------------------- - - /** Access the first element. - - Returns a reference to the first element. - - @par Precondition - `not empty()` - - @par Complexity - Constant. - */ - decode_view - front() const noexcept; - - /** Access the last element. - - Returns a reference to the last element. - - @par Precondition - `not empty()` - - @par Complexity - Constant. - */ - decode_view - back() const noexcept; - - //-------------------------------------------- - // - // Iterators - // - //-------------------------------------------- - - /** Return an iterator to the first element. - - If the path is empty, @ref end() is returned. - - @par Complexity - Constant. - - @par Exception Safety - No-throw guarantee. - */ - BOOST_URL_DECL - iterator - begin() const noexcept; - - /** Return an iterator to the element following the last element. - - The element acts as a placeholder; attempting - to access it results in undefined behavior. - - @par Complexity - Constant. - - @par Exception Safety - No-throw guarantee. - */ - BOOST_URL_DECL - iterator - end() const noexcept; - - //-------------------------------------------- - // - // Capacity - // - //-------------------------------------------- - - /** Check if the path has no segments. - - Returns `true` if there are no segments in the - path, i.e. @ref size() returns 0. - - @par Complexity - Constant. - - @par Exception Safety - No-throw guarantee. - */ - bool - empty() const noexcept; - - /** Return the number of segments in the path. - - This returns the number of segments in the path. - - @par Complexity - Constant. - - @par Exception Safety - No-throw guarantee. - */ - std::size_t - size() const noexcept; - - //-------------------------------------------- - // - // Friends - // - //-------------------------------------------- - - // hidden friend - friend - std::ostream& - operator<<( - std::ostream& os, - segments_view const& vw) + detail::path_ref const& ref) noexcept + : segments_base(ref) { - vw.write(os); - return os; } -private: - BOOST_URL_DECL - void - write(std::ostream& os) const; +public: + /** Constructor + After construction, both views will + reference the same underlying character + buffer. + + Ownership is not transferred; the caller + is responsible for ensuring the lifetime + of the buffer extends until it is no + longer referenced. + + @par Postconditions + @code + this->buffer().data() == other.buffer().data() + @endcode + + @par Complexity + Constant + + @par Exception Safety + Throws nothing + */ + segments_view( + segments_view const& other) = default; + + /** Assignment (deleted) + */ + segments_view& + operator=(segments_view const& other) = delete; }; -/** Format the segments to an output stream. - - @param os The output stream. - - @param vw The encoded segments. -*/ -std::ostream& -operator<<( - std::ostream& os, - segments_view const& vw); - -//---------------------------------------------------------- - - } // urls } // boost diff --git a/include/boost/url/src.hpp b/include/boost/url/src.hpp index 5dcc1893..a4d9e05e 100644 --- a/include/boost/url/src.hpp +++ b/include/boost/url/src.hpp @@ -34,7 +34,7 @@ in a translation unit of the program. //------------------------------------------------ #include -#include +#include #include #include #include @@ -42,11 +42,11 @@ in a translation unit of the program. #include #include #include -#include -#include +#include #include #include +#include #include #include #include @@ -54,13 +54,14 @@ in a translation unit of the program. #include #include #include -#include +#include #include #include -#include -#include +#include +#include +#include #include -#include +#include #include #include #include @@ -73,7 +74,6 @@ in a translation unit of the program. // //------------------------------------------------ -#include #include #include diff --git a/include/boost/url/url.hpp b/include/boost/url/url.hpp index f0536682..8a423d46 100644 --- a/include/boost/url/url.hpp +++ b/include/boost/url/url.hpp @@ -66,26 +66,6 @@ class BOOST_SYMBOL_VISIBLE url using url_view_base::digest; public: - /** Return the maximum number of characters possible - - This represents the largest number of - characters that are possible in a - Currently the limit is either 2^32-2 - characters or 2^64-2 characters, - depending on the system architecture. - This does not include a null terminator. - - @par Exception Safety - Throws nothing. - */ - static - constexpr - std::size_t - max_size() noexcept - { - return BOOST_URL_MAX_SIZE; - } - //-------------------------------------------- // // Special Members diff --git a/include/boost/url/url_base.hpp b/include/boost/url/url_base.hpp index 8d301d4c..6799ba27 100644 --- a/include/boost/url/url_base.hpp +++ b/include/boost/url/url_base.hpp @@ -18,11 +18,11 @@ #include #include #include -#include -#include +#include +#include #include #include -#include +#include #include #include #include @@ -33,8 +33,8 @@ namespace urls { #ifndef BOOST_URL_DOCS namespace detail { -struct any_path_iter; struct params_iter_impl; +struct segments_iter_impl; } namespace grammar { class lut_chars; @@ -70,9 +70,9 @@ class BOOST_SYMBOL_VISIBLE friend class url; friend class static_url_base; - friend class urls::segments; - friend class urls::params_view; - friend class segments_encoded; + friend class params_view; + friend class segments_ref; + friend class segments_encoded_ref; friend class params_encoded_view; struct op_t @@ -1848,7 +1848,7 @@ public: @ref set_path, @ref set_path_absolute. */ - urls::segments + urls::segments_ref segments() noexcept { return {*this}; @@ -1871,7 +1871,7 @@ public: @code url u( "http://example.com/path/to/file.txt" ); - segments_encoded sv = u.encoded_segments(); + segments_encoded_ref sv = u.encoded_segments(); @endcode @par Complexity @@ -1906,7 +1906,7 @@ public: @ref set_path_absolute. */ BOOST_URL_DECL - segments_encoded + segments_encoded_ref encoded_segments() noexcept { return {*this}; @@ -2164,7 +2164,7 @@ public: urls::params_encoded_view encoded_params() noexcept { - return urls::params_encoded_view(*this); + return {*this}; } //-------------------------------------------- @@ -2566,25 +2566,15 @@ private: char* set_host_impl(std::size_t n, op_t& op); char* set_port_impl(std::size_t n, op_t& op); - pos_t - segment( - std::size_t i) const noexcept; - - char* - resize_segments( - std::size_t i0, - std::size_t i1, - std::size_t n, - std::size_t nseg, - op_t& op); + string_view + first_segment() const noexcept; BOOST_URL_DECL void edit_segments( - std::size_t i0, - std::size_t i1, - detail::any_path_iter&& it0, - detail::any_path_iter&& it1, + detail::segments_iter_impl const&, + detail::segments_iter_impl const&, + detail::any_segments_iter&& it0, int abs_hint = -1); char* @@ -2719,7 +2709,7 @@ resolve( // These are here because of circular references #include #include -#include -#include +#include +#include #endif diff --git a/include/boost/url/url_view_base.hpp b/include/boost/url/url_view_base.hpp index 9f975707..73eb2859 100644 --- a/include/boost/url/url_view_base.hpp +++ b/include/boost/url/url_view_base.hpp @@ -65,15 +65,17 @@ class BOOST_SYMBOL_VISIBLE friend class url_view; friend class static_url_base; friend class params_base; - friend class params_encoded_base; - friend class params_view; friend class params_const_view; - friend class params_encoded_view; friend class params_const_encoded_view; - friend class segments; + friend class params_encoded_base; + friend class params_encoded_view; + friend class params_view; + friend class segments_base; friend class segments_view; - friend class segments_encoded; friend class segments_encoded_view; + friend class segments_encoded_base; + friend class segments_encoded_ref; + friend class segments_ref; struct shared_impl; @@ -102,6 +104,23 @@ public: // //-------------------------------------------- + /** Return the maximum number of characters possible + + This represents the largest number of + characters that are possible in a url, + not including any null terminator. + + @par Exception Safety + Throws nothing. + */ + static + constexpr + std::size_t + max_size() noexcept + { + return BOOST_URL_MAX_SIZE; + } + /** Return the number of characters in the URL This function returns the number of @@ -1642,7 +1661,7 @@ public: segments_view segments() const noexcept { - return {encoded_path(), u_.nseg_}; + return {detail::path_ref(u_)}; } /** Return the path as a container of segments @@ -1696,7 +1715,7 @@ public: encoded_segments() const noexcept { return segments_encoded_view( - encoded_path(), u_.nseg_); + detail::path_ref(u_)); } //-------------------------------------------- diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt index 4edd21ce..0bde0ce2 100644 --- a/test/unit/CMakeLists.txt +++ b/test/unit/CMakeLists.txt @@ -1,6 +1,6 @@ # # Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) -# Copyright (c) 2021 DMitry Arkhipov (grisumbras@gmail.com) +# Copyright (c) 2021 Dmitry Arkhipov (grisumbras@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) @@ -39,11 +39,14 @@ set(BOOST_URL_TESTS_FILES params_encoded_base.cpp params_encoded_view.cpp params_view.cpp + parse_path.cpp pct_string_view.cpp scheme.cpp - segments.cpp - segments_encoded.cpp + segments_base.cpp + segments_encoded_base.cpp + segments_encoded_ref.cpp segments_encoded_view.cpp + segments_ref.cpp segments_view.cpp snippets.cpp static_url.cpp @@ -58,7 +61,6 @@ set(BOOST_URL_TESTS_FILES grammar/alpha_chars.cpp grammar/charset.cpp grammar/ci_string.cpp - grammar/copied_strings.cpp grammar/dec_octet_rule.cpp grammar/delim_rule.cpp grammar/digit_chars.cpp diff --git a/test/unit/Jamfile b/test/unit/Jamfile index e8f5d24a..d1ee32b8 100644 --- a/test/unit/Jamfile +++ b/test/unit/Jamfile @@ -41,11 +41,14 @@ local SOURCES = params_encoded_base.cpp params_encoded_view.cpp params_view.cpp + parse_path.cpp pct_string_view.cpp scheme.cpp - segments.cpp - segments_encoded.cpp + segments_base.cpp + segments_encoded_base.cpp + segments_encoded_ref.cpp segments_encoded_view.cpp + segments_ref.cpp segments_view.cpp snippets.cpp static_url.cpp @@ -60,7 +63,6 @@ local SOURCES = grammar/alpha_chars.cpp grammar/charset.cpp grammar/ci_string.cpp - grammar/copied_strings.cpp grammar/dec_octet_rule.cpp grammar/delim_rule.cpp grammar/digit_chars.cpp diff --git a/test/unit/doc_container.cpp b/test/unit/doc_container.cpp index bb6263a7..d88e3f2c 100644 --- a/test/unit/doc_container.cpp +++ b/test/unit/doc_container.cpp @@ -25,7 +25,7 @@ struct doc_container_test { std::list< std::string > seq; for( auto s : u.encoded_segments() ) - seq.push_back( s ); + seq.push_back( s.decode_to_string() ); return seq; } //] diff --git a/test/unit/param.cpp b/test/unit/param.cpp index b6c48300..de405feb 100644 --- a/test/unit/param.cpp +++ b/test/unit/param.cpp @@ -35,10 +35,12 @@ struct param_test BOOST_STATIC_ASSERT(std::is_move_assignable::value); BOOST_STATIC_ASSERT(std::is_constructible::value); - BOOST_STATIC_ASSERT(std::is_constructible::value); // explicit - // not a useful conversion - BOOST_STATIC_ASSERT(! std::is_constructible::value); + // explicit, expensive + BOOST_STATIC_ASSERT(std::is_constructible::value); + + // cheap, loses pct-validation + BOOST_STATIC_ASSERT(std::is_constructible::value); // expensive constructions BOOST_STATIC_ASSERT(! std::is_constructible::value); diff --git a/test/unit/params_encoded_view.cpp b/test/unit/params_encoded_view.cpp index ec031bc8..34e48e92 100644 --- a/test/unit/params_encoded_view.cpp +++ b/test/unit/params_encoded_view.cpp @@ -13,9 +13,10 @@ #include #include -#include -#include "test_suite.hpp" #include +#include + +#include "test_suite.hpp" namespace boost { namespace urls { @@ -178,10 +179,6 @@ struct params_encoded_view_test void testSpecial() { - BOOST_STATIC_ASSERT( - ! std::is_default_constructible< - params_encoded_view>::value); - // params_encoded_view(params_encoded_view) { url u; diff --git a/test/unit/parse_path.cpp b/test/unit/parse_path.cpp new file mode 100644 index 00000000..664c5a63 --- /dev/null +++ b/test/unit/parse_path.cpp @@ -0,0 +1,45 @@ +// +// 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 "test_suite.hpp" + +namespace boost { +namespace urls { + +struct parse_path_test +{ + void + testFunctions() + { + auto rv = parse_path("/path/to/file.txt"); + BOOST_TEST(rv.has_value()); + } + + void + testJavadocs() + { + } + + void + run() + { + testFunctions(); + testJavadocs(); + } +}; + +TEST_SUITE( + parse_path_test, + "boost.url.parse_path"); + +} // urls +} // boost diff --git a/test/unit/segments.cpp b/test/unit/segments.cpp deleted file mode 100644 index 3c31b3dd..00000000 --- a/test/unit/segments.cpp +++ /dev/null @@ -1,528 +0,0 @@ -// -// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) -// Copyright (c) 2022 Alan de Freitas (alandefreitas@gmail.com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/CPPAlliance/url -// - -// Test that header file is self-contained. -#include - -#include -#include -#include -#include -#include -#include -#include "test_suite.hpp" - -namespace boost { -namespace urls { - -//------------------------------------------------ - -class segments_test -{ -public: - void - testMembers() - { - // operator=(segments const&) - { - url u1; - url u2; - segments p1 = u1.segments(); - segments p2 = u2.segments(); - p2 = p1; - BOOST_TEST_EQ(p1.begin(), p2.begin()); - } - - url_view const u0 = parse_uri( - "x://y/path/to/the/file.txt?q#f").value(); - - { - url u = u0; - u.segments() = { "etc", "index.htm" }; - BOOST_TEST_EQ(u.encoded_path(), "/etc/index.htm"); - BOOST_TEST_EQ(u.string(), "x://y/etc/index.htm?q#f"); - } - } - - void - testElementAccess() - { - url_view const u0 = parse_relative_ref( - "/path/to/the/file.txt").value(); - - // at - { - url u = u0; - auto se = u.segments(); - auto const& cs = se; - - BOOST_TEST_EQ(*se.begin(), "path"); - BOOST_TEST_EQ(*std::next(se.begin()), "to"); - BOOST_TEST_EQ(*std::next(se.begin(), 2), "the"); - BOOST_TEST_EQ(*std::next(se.begin(), 3), "file.txt"); - - BOOST_TEST_EQ(*std::next(cs.begin(), 0), "path"); - BOOST_TEST_EQ(*std::next(cs.begin()), "to"); - BOOST_TEST_EQ(*std::next(cs.begin(), 2), "the"); - BOOST_TEST_EQ(*std::next(cs.begin(), 3), "file.txt"); - - se.replace(std::next(se.begin()), "from"); - // comparison - BOOST_TEST_EQ(*std::next(se.begin()), "from"); - BOOST_TEST_EQ(*std::next(cs.begin()), "from"); - BOOST_TEST_NE(*std::next(se.begin()), "path"); - BOOST_TEST_NE(*std::next(cs.begin()), "path"); - } - - // operator[] - { - url u = u0; - auto se = u.segments(); - auto const& cs = se; - - BOOST_TEST_EQ(*se.begin(), "path"); - BOOST_TEST_EQ(*std::next(se.begin()), "to"); - BOOST_TEST_EQ(*std::next(se.begin(), 2), "the"); - BOOST_TEST_EQ(*std::next(se.begin(), 3), "file.txt"); - - BOOST_TEST_EQ(*std::next(cs.begin(), 0), "path"); - BOOST_TEST_EQ(*std::next(cs.begin()), "to"); - BOOST_TEST_EQ(*std::next(cs.begin(), 2), "the"); - BOOST_TEST_EQ(*std::next(cs.begin(), 3), "file.txt"); - - // assign - se.replace(std::next(se.begin()), "from"); - // comparison - BOOST_TEST_EQ(*std::next(se.begin()), "from"); - BOOST_TEST_EQ(*std::next(cs.begin()), "from"); - BOOST_TEST_NE(*std::next(se.begin()), "path"); - BOOST_TEST_NE(*std::next(cs.begin()), "path"); - } - - // front - { - url u = u0; - auto se = u.segments(); - auto const& cs = se; - - BOOST_TEST_EQ(se.front(), "path"); - BOOST_TEST_EQ(cs.front(), "path"); - - // assign - se.replace(se.begin(), "etc"); - - BOOST_TEST_EQ(u.string(), - "/etc/to/the/file.txt"); - - // comparison - BOOST_TEST_EQ(se.front(), "etc"); - BOOST_TEST_EQ(cs.front(), "etc"); - BOOST_TEST_EQ(*std::next(se.begin()), "to"); - BOOST_TEST_EQ(*std::next(cs.begin()), "to"); - BOOST_TEST_NE(se.front(), "path"); - BOOST_TEST_NE(cs.front(), "path"); - } - - // back - { - url u = u0; - auto se = u.segments(); - auto const& cs = se; - - BOOST_TEST_EQ(se.back(), "file.txt"); - BOOST_TEST_EQ(cs.back(), "file.txt"); - - // assign - se.replace(std::prev(se.end()), "index.htm"); - BOOST_TEST_EQ(u.string(), - "/path/to/the/index.htm"); - - // comparison - BOOST_TEST_EQ(se.back(), "index.htm"); - BOOST_TEST_EQ(cs.back(), "index.htm"); - BOOST_TEST_NE(se.back(), "file.txt"); - BOOST_TEST_NE(cs.back(), "file.txt"); - } - } - - void - testIterators() - { - url_view const u0 = parse_uri( - "x://y/path/to/the/file.txt").value(); - - // (default-ctor) - { - segments::iterator it; - boost::ignore_unused(it); - } - - // begin - { - url u = u0; - auto se = u.segments(); - auto const& cs = se; - - BOOST_TEST_EQ(se.begin(), cs.begin()); - BOOST_TEST_NE(se.end(), se.begin()); - } - - // end - { - url u = u0; - auto se = u.segments(); - auto const& cs = se; - - BOOST_TEST_EQ(se.end(), cs.end()); - BOOST_TEST_NE(se.begin(), se.end()); - } - - // - // iterator - // - - { - url u = u0; - auto se = u.segments(); - auto const& cs(se); - - segments::iterator it = se.begin(); - BOOST_TEST_EQ(*it, "path"); - BOOST_TEST_EQ(*++it, "to"); - BOOST_TEST_EQ(*it++, "to"); - BOOST_TEST_EQ(*it--, "the"); - BOOST_TEST_EQ(*it, "to"); - BOOST_TEST_EQ(*--it, "path"); - BOOST_TEST_EQ(it, se.begin()); - BOOST_TEST_NE(it, se.end()); - - std::advance(it, 1); - BOOST_TEST_EQ(*it, "to"); - BOOST_TEST_EQ(*std::next(it), "the"); - BOOST_TEST_EQ(*std::next(it), "the"); - --it; - BOOST_TEST_EQ(*it, "path"); - std::advance(it, 2); - BOOST_TEST_EQ(*std::prev(it), "to"); - --it; - BOOST_TEST_EQ(std::distance(se.begin(), it), 1); - BOOST_TEST_EQ(std::distance(it, se.end()), 3); - - BOOST_TEST_EQ(*it, "to"); - BOOST_TEST_EQ(*std::next(it), "the"); - BOOST_TEST_NE(it, se.begin()); - BOOST_TEST_NE(it, cs.begin()); - } - - // value_type outlives reference - { - segments::value_type v; - { - url u = u0; - segments se = u.segments(); - segments::reference r = - *se.begin(); - v = segments::value_type(r); - } - BOOST_TEST_EQ(v, "path"); - } - } - - void - testCapacity() - { - url_view const u0 = parse_uri( - "x://y/path/to/the/file.txt").value(); - - // empty - { - url u = u0; - auto se = u.segments(); - auto const& cs = se; - - BOOST_TEST(! se.empty()); - BOOST_TEST(! cs.empty()); - } - - // size - { - url u = u0; - auto se = u.segments(); - auto const& cs = se; - - BOOST_TEST_EQ(se.size(), 4u); - BOOST_TEST_EQ(cs.size(), 4u); - } - } - - void - testModifiers() - { - // clear - { - url u = parse_uri("x://y/path/to/the/file.txt").value(); - auto se = u.segments(); - - BOOST_TEST(! se.empty()); - BOOST_TEST_EQ(se.size(), 4u); - se.clear(); - BOOST_TEST(se.empty()); - BOOST_TEST_EQ(se.size(), 0u); - BOOST_TEST_EQ(u.encoded_path(), "/"); - BOOST_TEST_EQ(u.string(), "x://y/"); - } - - // insert( const_iterator, string_view ) - { - url u = parse_uri("x://y/path/file.txt?q#f").value(); - auto se = u.segments(); - auto const& cs(se); - - BOOST_TEST_EQ(se.size(), 2u); - auto it = - se.insert(std::next(se.begin()), "to"); - BOOST_TEST_EQ(se.size(), 3u); - BOOST_TEST_EQ(u.encoded_path(), "/path/to/file.txt"); - BOOST_TEST_EQ(u.string(), "x://y/path/to/file.txt?q#f"); - BOOST_TEST_EQ(*it, "to"); - - it = se.insert(cs.end(), ""); - BOOST_TEST_EQ(se.size(), 4u); - BOOST_TEST_EQ(u.encoded_path(), "/path/to/file.txt/"); - BOOST_TEST_EQ(u.string(), "x://y/path/to/file.txt/?q#f"); - BOOST_TEST_EQ(*it, ""); - - it = se.insert(se.begin(), "etc"); - BOOST_TEST_EQ(se.size(), 5u); - BOOST_TEST_EQ(u.encoded_path(), "/etc/path/to/file.txt/"); - BOOST_TEST_EQ(u.string(), "x://y/etc/path/to/file.txt/?q#f"); - BOOST_TEST_EQ(*it, "etc"); - } - - { - // rootless - url u = parse_uri("x:path/file.txt?q#f").value(); - auto se = u.segments(); - auto const& cs(se); - - BOOST_TEST_EQ(se.size(), 2u); - auto it = - se.insert(std::next(se.begin()), "to"); - BOOST_TEST_EQ(se.size(), 3u); - BOOST_TEST_EQ(u.encoded_path(), "path/to/file.txt"); - BOOST_TEST_EQ(u.string(), "x:path/to/file.txt?q#f"); - BOOST_TEST_EQ(*it, "to"); - - it = se.insert(cs.end(), ""); - BOOST_TEST_EQ(se.size(), 4u); - BOOST_TEST_EQ(u.encoded_path(), "path/to/file.txt/"); - BOOST_TEST_EQ(u.string(), "x:path/to/file.txt/?q#f"); - BOOST_TEST_EQ(*it, ""); - - it = se.insert(se.begin(), "etc"); - BOOST_TEST_EQ(se.size(), 5u); - BOOST_TEST_EQ(u.encoded_path(), "etc/path/to/file.txt/"); - BOOST_TEST_EQ(u.string(), "x:etc/path/to/file.txt/?q#f"); - BOOST_TEST_EQ(*it, "etc"); - } - - // insert( const_iterator, FwdIt, FwdIt ) - { - url u = parse_uri("x://y/path/file.txt?q#f").value(); - auto se = u.segments(); - auto const& cs(se); - - std::initializer_list init = {"to", "the" }; - auto it = se.insert( - std::next(se.begin()), init.begin(), init.end()); - BOOST_TEST_EQ(cs.size(), 4u); - BOOST_TEST_EQ(*it, "to"); - BOOST_TEST_EQ(u.encoded_path(), "/path/to/the/file.txt"); - BOOST_TEST_EQ(u.string(), "x://y/path/to/the/file.txt?q#f"); - - // empty range - it = se.insert(std::next(se.begin()), - init.begin(), init.begin()); - BOOST_TEST_EQ(u.encoded_path(), "/path/to/the/file.txt"); - BOOST_TEST_EQ(it, std::next(se.begin())); - } - { - // rootless - url u = parse_uri("x:the/file.txt?q#f").value(); - auto se = u.segments(); - auto const& cs(se); - - std::initializer_list init = {"path", "to" }; - auto it = se.insert( - se.begin(), init.begin(), init.end()); - BOOST_TEST_EQ(cs.size(), 4u); - BOOST_TEST_EQ(*it, "path"); - BOOST_TEST_EQ(u.encoded_path(), "path/to/the/file.txt"); - BOOST_TEST_EQ(u.string(), "x:path/to/the/file.txt?q#f"); - - // empty range - it = se.insert(std::next(se.begin()), - init.begin(), init.begin()); - BOOST_TEST_EQ(u.encoded_path(), "path/to/the/file.txt"); - BOOST_TEST_EQ(it, std::next(se.begin())); - } - - // insert( const_iterator, initializer_list ) - { - url u = parse_uri("x://y/path/file.txt?q#f").value(); - auto se = u.segments(); - auto const& cs(se); - - std::initializer_list< - string_view> init = { - "to", "the" }; - auto it = se.insert(std::next(se.begin()), init); - BOOST_TEST_EQ(cs.size(), 4u); - BOOST_TEST_EQ(*it, "to"); - BOOST_TEST_EQ(u.encoded_path(), "/path/to/the/file.txt"); - BOOST_TEST_EQ(u.string(), "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").value(); - auto se = u.segments(); - - se.erase(std::next(se.begin())); - BOOST_TEST_EQ(se.size(), 3u); - BOOST_TEST_EQ(u.encoded_path(), "/path/the/file.txt"); - BOOST_TEST_EQ(u.string(), "x://y/path/the/file.txt?q#f"); - - se.erase(se.begin()); - BOOST_TEST_EQ(se.size(), 2u); - BOOST_TEST_EQ(u.encoded_path(), "/the/file.txt"); - BOOST_TEST_EQ(u.string(), "x://y/the/file.txt?q#f"); - - se.erase(std::prev(se.end())); - BOOST_TEST_EQ(se.size(), 1u); - BOOST_TEST_EQ(u.encoded_path(), "/the"); - BOOST_TEST_EQ(u.string(), "x://y/the?q#f"); - - se.erase(se.begin()); - BOOST_TEST(se.empty()); - BOOST_TEST_EQ(u.encoded_path(), "/"); - BOOST_TEST_EQ(u.string(), "x://y/?q#f"); - } - - // erase( const_iterator, const_iterator ) - { - url u = parse_uri( - "x://y/home/etc/path/to/the/file.txt?q#f").value(); - auto se = u.segments(); - - se.erase(se.begin(), std::next(se.begin(), 2)); - BOOST_TEST_EQ(u.encoded_path(), "/path/to/the/file.txt"); - BOOST_TEST_EQ(u.string(), "x://y/path/to/the/file.txt?q#f"); - - se.erase(se.begin(), se.end()); - BOOST_TEST_EQ(u.encoded_path(), "/"); - BOOST_TEST_EQ(u.string(), "x://y/?q#f"); - } - - // replace(iterator, iterator, initializer-list) - { - // initializer_list - url u = parse_relative_ref( - "/a/b/c/d/e/f/g").value(); - segments ss = u.segments(); - auto it = ss.replace( - std::next(ss.begin()), - std::next(ss.begin(), 3), - { "x", "y", "z" }); - BOOST_TEST_EQ(it, std::next(ss.begin())); - BOOST_TEST(u.encoded_path() == - "/a/x/y/z/d/e/f/g"); - } - { - // initializer_list - url u = parse_relative_ref( - "/a/b/c/d/e/f/g").value(); - segments ss = u.segments(); - auto it = ss.replace( - std::next(ss.begin()), - std::next(ss.begin(), 3), { - string_view("x"), - string_view("y"), - string_view("z") }); - BOOST_TEST_EQ(it, std::next(ss.begin())); - BOOST_TEST(u.encoded_path() == - "/a/x/y/z/d/e/f/g"); - } - - // push_back(string_view) - // push_back(String) - #if 0 - { - url u; - auto se = u.segments(); - se.push_back("path"); - BOOST_TEST_EQ(u.encoded_path(), "path"); - se.push_back("to"); - BOOST_TEST_EQ(u.encoded_path(), "path/to"); - se.push_back("file.txt"); - BOOST_TEST_EQ(u.encoded_path(), "path/to/file.txt"); - } - { - url u; - auto se = u.segments(); - u.set_path_absolute(true); - se.push_back("path"); - BOOST_TEST_EQ(u.encoded_path(), "/path"); - se.push_back("to"); - BOOST_TEST_EQ(u.encoded_path(), "/path/to"); - se.push_back("file.txt"); - BOOST_TEST_EQ(u.encoded_path(), "/path/to/file.txt"); - } - #endif - - // pop_back - { - url u = parse_uri( - "x://y/path/to/file.txt?q#f").value(); - auto se = u.segments(); - - BOOST_TEST_EQ(se.size(), 3u); - se.pop_back(); - BOOST_TEST_EQ(se.size(), 2u); - BOOST_TEST_EQ(u.encoded_path(), "/path/to"); - BOOST_TEST_EQ(u.string(), "x://y/path/to?q#f"); - se.pop_back(); - BOOST_TEST_EQ(se.size(), 1u); - BOOST_TEST_EQ(u.encoded_path(), "/path"); - BOOST_TEST_EQ(u.string(), "x://y/path?q#f"); - se.pop_back(); - BOOST_TEST_EQ(se.size(), 0u); - BOOST_TEST_EQ(u.encoded_path(), "/"); - BOOST_TEST_EQ(u.string(), "x://y/?q#f"); - } - } - - void - run() - { - testMembers(); - testElementAccess(); - testIterators(); - testCapacity(); - testModifiers(); - } -}; - -TEST_SUITE( - segments_test, - "boost.url.segments"); - -} // urls -} // boost diff --git a/test/unit/segments_base.cpp b/test/unit/segments_base.cpp new file mode 100644 index 00000000..19c750d8 --- /dev/null +++ b/test/unit/segments_base.cpp @@ -0,0 +1,169 @@ +// +// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2022 Alan de Freitas (alandefreitas@gmail.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/CPPAlliance/url +// + +// Test that header file is self-contained. +#include + +#include +#include +#include + +#include "test_suite.hpp" + +namespace boost { +namespace urls { + +struct segments_base_test +{ + static + void + check( + string_view s, + std::initializer_list< + string_view> init) + { + auto rv = parse_path(s); + if(! BOOST_TEST(rv.has_value())) + return; + segments_base const& ps(*rv); + BOOST_TEST_EQ( + ps.buffer().data(), s.data()); + BOOST_TEST_EQ( + ps.is_absolute(), + s.starts_with('/')); + BOOST_TEST_EQ(ps.empty(), init.size() == 0); + if(! BOOST_TEST_EQ(ps.size(), init.size())) + return; + if(init.size() > 0 && ! ps.empty()) + { + BOOST_TEST_EQ(ps.front(), *init.begin()); + BOOST_TEST_EQ(ps.back(), *std::prev(init.end())); + } + + // forward + { + auto it0 = ps.begin(); + auto it1 = init.begin(); + auto const end = ps.end(); + while(it0 != end) + { + segments_base::reference r0(*it0); + segments_base::reference r1(*it1); + BOOST_TEST_EQ(r0, r1); + BOOST_TEST_EQ(*it0, *it1); + segments_base::value_type v0(*it0); + segments_base::value_type v1(*it1); + BOOST_TEST_EQ(v0, *it1); + BOOST_TEST_EQ(v1, *it1); + auto prev = it0++; + BOOST_TEST_NE(prev, it0); + BOOST_TEST_EQ(++prev, it0); + ++it1; + BOOST_TEST_EQ(v0, v1);; + } + } + + // reverse + if(init.size() > 0) + { + auto const begin = ps.begin(); + auto it0 = ps.end(); + auto it1 = init.end(); + do + { + auto prev = it0--; + BOOST_TEST_NE(prev, it0); + BOOST_TEST_EQ(--prev, it0); + --it1; + segments_base::reference r0(*it0); + segments_base::reference r1(*it1); + BOOST_TEST_EQ(*it0, *it1); + BOOST_TEST_EQ(r0, r1); + } + while(it0 != begin); + } + } + + void + testMembers() + { + /* Legend + + '.' 0x2e + '/' 0x2f + */ + check( "", { }); + check( "/", { }); + check( "./", { "" }); + check( "./usr", { "usr" }); + check("/index%2ehtm", { "index.htm" }); + check("/images/cat-pic.gif", { "images", "cat-pic.gif" }); + check("images/cat-pic.gif", { "images", "cat-pic.gif" }); + check("/fast//query", { "fast", "", "query" }); + check("fast//", { "fast", "", "" }); + check("/./", { "" }); + check(".//", { "", "" }); + } + + void + testJavadoc() + { + // value_type + { + segments_view::value_type ps( url_view( "/path/to/file.txt" ).segments().back() ); + + ignore_unused(ps); + } + + // buffer() + { + assert( url_view( "/path/to/file.txt" ).segments().buffer() == "/path/to/file.txt" ); + } + + // is_absolute() + { + assert( url_view( "/path/to/file.txt" ).segments().is_absolute() == true ); + } + + // empty() + { + assert( ! url_view( "/index.htm" ).segments().empty() ); + } + + // size() + { + assert( url_view( "/path/to/file.txt" ).segments().size() == 3 ); + } + + // front() + { + assert( url_view( "/path/to/file.txt" ).segments().front() == "path" ); + } + + // back() + { + assert( url_view( "/path/to/file.txt" ).segments().back() == "file.txt" ); + } + } + + void + run() + { + testMembers(); + testJavadoc(); + } +}; + +TEST_SUITE( + segments_base_test, + "boost.url.segments_base"); + +} // urls +} // boost diff --git a/test/unit/segments_encoded.cpp b/test/unit/segments_encoded.cpp deleted file mode 100644 index 89f37bbe..00000000 --- a/test/unit/segments_encoded.cpp +++ /dev/null @@ -1,482 +0,0 @@ -// -// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) -// Copyright (c) 2022 Alan de Freitas (alandefreitas@gmail.com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/CPPAlliance/url -// - -// Test that header file is self-contained. -#include - -#include -#include -#include -#include -#include -#include "test_suite.hpp" - -namespace boost { -namespace urls { - -//------------------------------------------------ - -class segments_encoded_test -{ -public: - BOOST_STATIC_ASSERT( - std::is_default_constructible< - segments_encoded::iterator>::value); - - void - testMembers() - { - // operator=(segments const&) - { - url u1; - url u2; - segments_encoded p1 = u1.encoded_segments(); - segments_encoded p2 = u2.encoded_segments(); - p2 = p1; - BOOST_TEST_EQ(p1.begin(), p2.begin()); - } - - url_view const u0 = parse_uri( - "x://y/path/to/the/file.txt?q#f").value(); - - { - url u = u0; - u.encoded_segments() = { "etc", "index.htm" }; - BOOST_TEST_EQ(u.encoded_path(), "/etc/index.htm"); - BOOST_TEST_EQ(u.string(), "x://y/etc/index.htm?q#f"); - } - } - - void - testElementAccess() - { - url_view const u0 = parse_relative_ref( - "/path/to/the/file.txt").value(); - - // at - { - url u = u0; - segments_encoded se = u.encoded_segments(); - - BOOST_TEST_EQ(*se.begin(), "path"); - BOOST_TEST_EQ(*std::next(se.begin()), "to"); - BOOST_TEST_EQ(*std::next(se.begin(), 2), "the"); - BOOST_TEST_EQ(*std::next(se.begin(), 3), "file.txt"); - - // assign - se.replace(std::next(se.begin()), "from"); - // comparison - BOOST_TEST_EQ(*std::next(se.begin()), "from"); - BOOST_TEST_NE(*std::next(se.begin()), "path"); - } - - // operator[] - { - url u = u0; - segments_encoded se = u.encoded_segments(); - - BOOST_TEST_EQ(*se.begin(), "path"); - BOOST_TEST_EQ(*std::next(se.begin()), "to"); - BOOST_TEST_EQ(*std::next(se.begin(), 2), "the"); - BOOST_TEST_EQ(*std::next(se.begin(), 3), "file.txt"); - - // assign - se.replace(std::next(se.begin()), "from"); - // comparison - BOOST_TEST_EQ(*std::next(se.begin()), "from"); - BOOST_TEST_NE(*std::next(se.begin()), "path"); - } - - // front - { - url u = u0; - segments_encoded se = u.encoded_segments(); - - BOOST_TEST_EQ(se.front(), "path"); - - // assign - se.replace(se.begin(), "etc"); - // comparison - BOOST_TEST_EQ(se.front(), "etc"); - BOOST_TEST_NE(se.front(), "path"); - } - - // back - { - url u = u0; - segments_encoded se = u.encoded_segments(); - - BOOST_TEST_EQ(se.back(), "file.txt"); - - // assign - se.replace(std::prev(se.end()), "index.htm"); - // comparison - BOOST_TEST_EQ(se.back(), "index.htm"); - BOOST_TEST_NE(se.back(), "file.txt"); - } - } - - void - testIterators() - { - url_view const u0 = parse_uri( - "x://y/path/to/the/file.txt").value(); - - // (default-ctor) - { - segments_encoded::iterator it; - (void)it; - } - - // begin - { - url u = u0; - segments_encoded se = u.encoded_segments(); - - BOOST_TEST_NE(se.begin(), se.end()); - } - - // end - { - url u = u0; - segments_encoded se = u.encoded_segments(); - - BOOST_TEST_NE(se.end(), se.begin()); - } - - // - // iterator - // - - { - url u = u0; - segments_encoded se = u.encoded_segments(); - auto const& cs(se); - - segments_encoded::iterator it = se.begin(); - BOOST_TEST_EQ(*it, "path"); - BOOST_TEST_EQ(*++it, "to"); - BOOST_TEST_EQ(*it++, "to"); - BOOST_TEST_EQ(*it--, "the"); - BOOST_TEST_EQ(*it, "to"); - BOOST_TEST_EQ(*--it, "path"); - BOOST_TEST_EQ(it, se.begin()); - BOOST_TEST_NE(it, se.end()); - - BOOST_TEST_EQ(*(++it), "to"); - BOOST_TEST_EQ(*std::next(it), "the"); - BOOST_TEST_EQ(*std::next(it), "the"); - BOOST_TEST_EQ(*(--it), "path"); - std::advance(it, 2); - BOOST_TEST_EQ(*std::prev(it), "to"); - --it; - - BOOST_TEST_NE(it, se.begin()); - BOOST_TEST_NE(it, cs.begin()); - } - - // value_type outlives reference - { - segments_encoded::value_type v; - { - url u = u0; - segments_encoded se = u.encoded_segments(); - segments_encoded::reference r = - *se.begin(); - v = segments_encoded::value_type(r); - } - BOOST_TEST_EQ(v, "path"); - } - } - - void - testCapacity() - { - url_view const u0 = parse_uri( - "x://y/path/to/the/file.txt").value(); - - // empty - { - url u = u0; - segments_encoded se = u.encoded_segments(); - BOOST_TEST(! se.empty()); - } - - // size - { - url u = u0; - segments_encoded se = u.encoded_segments(); - - BOOST_TEST_EQ(se.size(), 4u); - } - } - - void - testModifiers() - { - // clear - { - url u = parse_uri("x://y/path/to/the/file.txt").value(); - segments_encoded se = u.encoded_segments(); - - BOOST_TEST(! se.empty()); - BOOST_TEST_EQ(se.size(), 4u); - se.clear(); - BOOST_TEST(se.empty()); - BOOST_TEST_EQ(se.size(), 0u); - BOOST_TEST_EQ(u.encoded_path(), "/"); - BOOST_TEST_EQ(u.string(), "x://y/"); - } - - // insert( const_iterator, string_view ) - { - url u = parse_uri("x://y/path/file.txt?q#f").value(); - segments_encoded se = u.encoded_segments(); - segments_encoded const& cs(se); - - BOOST_TEST_EQ(se.size(), 2u); - segments_encoded::iterator it = - se.insert(std::next(se.begin()), "to"); - BOOST_TEST_EQ(se.size(), 3u); - BOOST_TEST_EQ(u.encoded_path(), "/path/to/file.txt"); - BOOST_TEST_EQ(u.string(), "x://y/path/to/file.txt?q#f"); - BOOST_TEST_EQ(*it, "to"); - - it = se.insert(cs.end(), ""); - BOOST_TEST_EQ(se.size(), 4u); - BOOST_TEST_EQ(u.encoded_path(), "/path/to/file.txt/"); - BOOST_TEST_EQ(u.string(), "x://y/path/to/file.txt/?q#f"); - BOOST_TEST_EQ(*it, ""); - - it = se.insert(se.begin(), "etc"); - BOOST_TEST_EQ(se.size(), 5u); - BOOST_TEST_EQ(u.encoded_path(), "/etc/path/to/file.txt/"); - BOOST_TEST_EQ(u.string(), "x://y/etc/path/to/file.txt/?q#f"); - BOOST_TEST_EQ(*it, "etc"); - - BOOST_TEST_THROWS(se.insert(se.begin(), "%"), system_error); - BOOST_TEST_THROWS(se.insert(se.begin(), "/"), system_error); - BOOST_TEST_THROWS(se.insert(se.begin(), "%2g"), system_error); - } - - { - // rootless - url u = parse_uri("x:path/file.txt?q#f").value(); - segments_encoded se = u.encoded_segments(); - segments_encoded const& cs(se); - - BOOST_TEST_EQ(se.size(), 2u); - segments_encoded::iterator it = - se.insert(std::next(se.begin()), "to"); - BOOST_TEST_EQ(se.size(), 3u); - BOOST_TEST_EQ(u.encoded_path(), "path/to/file.txt"); - BOOST_TEST_EQ(u.string(), "x:path/to/file.txt?q#f"); - BOOST_TEST_EQ(*it, "to"); - - it = se.insert(cs.end(), ""); - BOOST_TEST_EQ(se.size(), 4u); - BOOST_TEST_EQ(u.encoded_path(), "path/to/file.txt/"); - BOOST_TEST_EQ(u.string(), "x:path/to/file.txt/?q#f"); - BOOST_TEST_EQ(*it, ""); - - it = se.insert(se.begin(), "etc"); - BOOST_TEST_EQ(se.size(), 5u); - BOOST_TEST_EQ(u.encoded_path(), "etc/path/to/file.txt/"); - BOOST_TEST_EQ(u.string(), "x:etc/path/to/file.txt/?q#f"); - BOOST_TEST_EQ(*it, "etc"); - - BOOST_TEST_THROWS(se.insert(se.begin(), "%"), system_error); - BOOST_TEST_THROWS(se.insert(se.begin(), "/"), system_error); - BOOST_TEST_THROWS(se.insert(se.begin(), "%2g"), system_error); - } - - // insert( const_iterator, FwdIt, FwdIt ) - { - url u = parse_uri("x://y/path/file.txt?q#f").value(); - segments_encoded se = u.encoded_segments(); - segments_encoded const& cs(se); - - std::initializer_list init = {"to", "the" }; - auto it = se.insert( - std::next(se.begin()), init.begin(), init.end()); - BOOST_TEST_EQ(cs.size(), 4u); - BOOST_TEST_EQ(*it, "to"); - BOOST_TEST_EQ(u.encoded_path(), "/path/to/the/file.txt"); - BOOST_TEST_EQ(u.string(), "x://y/path/to/the/file.txt?q#f"); - - std::initializer_list bad = {"%"}; - BOOST_TEST_THROWS(se.insert( - std::next(se.begin()), bad.begin(), bad.end()), - system_error); - - // empty range - it = se.insert(std::next(se.begin()), - init.begin(), init.begin()); - BOOST_TEST_EQ(u.encoded_path(), "/path/to/the/file.txt"); - BOOST_TEST_EQ(it, std::next(se.begin())); - } - { - // rootless - url u = parse_uri("x:the/file.txt?q#f").value(); - segments_encoded se = u.encoded_segments(); - segments_encoded const& cs(se); - - std::initializer_list init = {"path", "to" }; - auto it = se.insert( - se.begin(), init.begin(), init.end()); - BOOST_TEST_EQ(cs.size(), 4u); - BOOST_TEST_EQ(*it, "path"); - BOOST_TEST_EQ(u.encoded_path(), "path/to/the/file.txt"); - BOOST_TEST_EQ(u.string(), "x:path/to/the/file.txt?q#f"); - - std::initializer_list bad = {"%"}; - BOOST_TEST_THROWS(se.insert( - std::next(se.begin()), bad.begin(), bad.end()), - system_error); - - // empty range - it = se.insert(std::next(se.begin()), - init.begin(), init.begin()); - BOOST_TEST_EQ(u.encoded_path(), "path/to/the/file.txt"); - BOOST_TEST_EQ(it, std::next(se.begin())); - } - - // insert( const_iterator, initializer_list ) - { - url u = parse_uri("x://y/path/file.txt?q#f").value(); - segments_encoded se = u.encoded_segments(); - segments_encoded const& cs(se); - - std::initializer_list< - string_view> init = { - "to", "the" }; - auto it = se.insert(std::next(se.begin()), init); - BOOST_TEST_EQ(cs.size(), 4u); - BOOST_TEST_EQ(*it, "to"); - BOOST_TEST_EQ(u.encoded_path(), "/path/to/the/file.txt"); - BOOST_TEST_EQ(u.string(), "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").value(); - segments_encoded se = u.encoded_segments(); - - se.erase(std::next(se.begin())); - BOOST_TEST_EQ(se.size(), 3u); - BOOST_TEST_EQ(u.encoded_path(), "/path/the/file.txt"); - BOOST_TEST_EQ(u.string(), "x://y/path/the/file.txt?q#f"); - - se.erase(se.begin()); - BOOST_TEST_EQ(se.size(), 2u); - BOOST_TEST_EQ(u.encoded_path(), "/the/file.txt"); - BOOST_TEST_EQ(u.string(), "x://y/the/file.txt?q#f"); - - se.erase(std::prev(se.end())); - BOOST_TEST_EQ(se.size(), 1u); - BOOST_TEST_EQ(u.encoded_path(), "/the"); - BOOST_TEST_EQ(u.string(), "x://y/the?q#f"); - - se.erase(se.begin()); - BOOST_TEST(se.empty()); - BOOST_TEST_EQ(u.encoded_path(), "/"); - BOOST_TEST_EQ(u.string(), "x://y/?q#f"); - } - - // erase( const_iterator, const_iterator ) - { - url u = parse_uri("x://y/home/etc/path/to/the/file.txt?q#f").value(); - segments_encoded se = u.encoded_segments(); - - se.erase(se.begin(), std::next(se.begin(), 2)); - BOOST_TEST_EQ(u.encoded_path(), "/path/to/the/file.txt"); - BOOST_TEST_EQ(u.string(), "x://y/path/to/the/file.txt?q#f"); - - se.erase(se.begin(), se.end()); - BOOST_TEST_EQ(u.encoded_path(), "/"); - BOOST_TEST_EQ(u.string(), "x://y/?q#f"); - } - - // replace(iterator, iterator, initializer-list) - { - // initializer_list - url u = parse_relative_ref("/a/b/c/d/e/f/g").value(); - segments_encoded se = u.encoded_segments(); - auto it = se.replace( - std::next(se.begin(), 1), - std::next(se.begin(), 3), - { "x", "y", "z" }); - BOOST_TEST_EQ(it, std::next(se.begin())); - BOOST_TEST(u.encoded_path() == - "/a/x/y/z/d/e/f/g"); - } - { - // initializer_list - url u = parse_relative_ref("/a/b/c/d/e/f/g").value(); - segments_encoded se = u.encoded_segments(); - auto it = se.replace( - std::next(se.begin(), 1), - std::next(se.begin(), 3), { - string_view("x"), - string_view("y"), - string_view("z") }); - BOOST_TEST_EQ(it, std::next(se.begin())); - BOOST_TEST(u.encoded_path() == - "/a/x/y/z/d/e/f/g"); - } - - // push_back - { - url u = parse_uri("x://y/home/etc/path/to/the/file.txt?q#f").value(); - segments_encoded se = u.encoded_segments(); - - BOOST_TEST_THROWS(se.push_back("%"), system_error); - BOOST_TEST_THROWS(se.push_back("/"), system_error); - BOOST_TEST_THROWS(se.push_back("%2g"), system_error); - } - - // pop_back - { - url u = parse_uri("x://y/path/to/file.txt?q#f").value(); - segments_encoded se = u.encoded_segments(); - - BOOST_TEST_EQ(se.size(), 3u); - se.pop_back(); - BOOST_TEST_EQ(se.size(), 2u); - BOOST_TEST_EQ(u.encoded_path(), "/path/to"); - BOOST_TEST_EQ(u.string(), "x://y/path/to?q#f"); - se.pop_back(); - BOOST_TEST_EQ(se.size(), 1u); - BOOST_TEST_EQ(u.encoded_path(), "/path"); - BOOST_TEST_EQ(u.string(), "x://y/path?q#f"); - se.pop_back(); - BOOST_TEST_EQ(se.size(), 0u); - BOOST_TEST_EQ(u.encoded_path(), "/"); - BOOST_TEST_EQ(u.string(), "x://y/?q#f"); - } - } - - void - run() - { - testMembers(); - testElementAccess(); - testIterators(); - testCapacity(); - testModifiers(); - } -}; - -TEST_SUITE( - segments_encoded_test, - "boost.url.segments_encoded"); - -} // urls -} // boost diff --git a/test/unit/segments_encoded_base.cpp b/test/unit/segments_encoded_base.cpp new file mode 100644 index 00000000..f5e52646 --- /dev/null +++ b/test/unit/segments_encoded_base.cpp @@ -0,0 +1,166 @@ +// +// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2022 Alan de Freitas (alandefreitas@gmail.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/CPPAlliance/url +// + +// Test that header file is self-contained. +#include + +#include +#include +#include + +#include "test_suite.hpp" + +#include + +namespace boost { +namespace urls { + +struct segments_encoded_base_test +{ + static + void + check( + string_view s, + std::initializer_list< + string_view> init) + { + auto rv = parse_path(s); + if(! BOOST_TEST(rv.has_value())) + return; + segments_encoded_base const& ps(*rv); + BOOST_TEST_EQ( + ps.buffer().data(), s.data()); + BOOST_TEST_EQ( + ps.is_absolute(), + s.starts_with('/')); + BOOST_TEST_EQ(ps.empty(), init.size() == 0); + if(! BOOST_TEST_EQ(ps.size(), init.size())) + return; + if(init.size() > 0 && ! ps.empty()) + { + BOOST_TEST_EQ(ps.front(), *init.begin()); + BOOST_TEST_EQ(ps.back(), *std::prev(init.end())); + } + + // forward + { + auto it0 = ps.begin(); + auto it1 = init.begin(); + auto const end = ps.end(); + while(it0 != end) + { + segments_encoded_base::reference r0(*it0); + segments_encoded_base::reference r1(*it1); + BOOST_TEST_EQ(r0, r1); + BOOST_TEST_EQ(*it0, *it1); + segments_encoded_base::value_type v0(*it0); + segments_encoded_base::value_type v1(*it1); + BOOST_TEST_EQ(v0, *it1); + BOOST_TEST_EQ(v1, *it1); + auto prev = it0++; + BOOST_TEST_NE(prev, it0); + BOOST_TEST_EQ(++prev, it0); + ++it1; + BOOST_TEST_EQ(v0, v1);; + } + } + + // reverse + if(init.size() > 0) + { + auto const begin = ps.begin(); + auto it0 = ps.end(); + auto it1 = init.end(); + do + { + auto prev = it0--; + BOOST_TEST_NE(prev, it0); + BOOST_TEST_EQ(--prev, it0); + --it1; + segments_encoded_base::reference r0(*it0); + segments_encoded_base::reference r1(*it1); + BOOST_TEST_EQ(*it0, *it1); + BOOST_TEST_EQ(r0, r1); + } + while(it0 != begin); + } + } + + void + testMembers() + { + check( "", { }); + check( "/", { }); + check( "./", { "" }); + check( "./usr", { "usr" }); + check("/index.htm", { "index.htm" }); + check("/images/cat-pic.gif", { "images", "cat-pic.gif" }); + check("images/cat-pic.gif", { "images", "cat-pic.gif" }); + check("/fast//query", { "fast", "", "query" }); + check("fast//", { "fast", "", "" }); + check("/./", { "" }); + check(".//", { "", "" }); + } + + void + testJavadocs() + { + // value_type + { + segments_encoded_view::value_type ps( *url_view( "/path/to/file.txt" ).encoded_segments().back() ); + + ignore_unused(ps); + } + + // buffer() + { + assert( url_view( "/path/to/file.txt" ).encoded_segments().buffer() == "/path/to/file.txt" ); + } + + // is_absolute() + { + assert( url_view( "/path/to/file.txt" ).encoded_segments().is_absolute() == true ); + } + + // empty() + { + assert( ! url_view( "/index.htm" ).encoded_segments().empty() ); + } + + // size() + { + assert( url_view( "/path/to/file.txt" ).encoded_segments().size() == 3 ); + } + + // front() + { + assert( url_view( "/path/to/file.txt" ).encoded_segments().front() == "path" ); + } + + // back() + { + assert( url_view( "/path/to/file.txt" ).encoded_segments().back() == "file.txt" ); + } + } + + void + run() + { + testMembers(); + testJavadocs(); + } +}; + +TEST_SUITE( + segments_encoded_base_test, + "boost.url.segments_encoded_base"); + +} // urls +} // boost diff --git a/test/unit/segments_encoded_ref.cpp b/test/unit/segments_encoded_ref.cpp new file mode 100644 index 00000000..ad8310a5 --- /dev/null +++ b/test/unit/segments_encoded_ref.cpp @@ -0,0 +1,718 @@ +// +// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2022 Alan de Freitas (alandefreitas@gmail.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/CPPAlliance/url +// + +// Test that header file is self-contained. +#include + +#include +#include +#include +#include +#include +#include "test_suite.hpp" + +namespace boost { +namespace urls { + +using Type = segments_encoded_ref; + +BOOST_STATIC_ASSERT( + ! std::is_default_constructible< + Type>::value); + +BOOST_STATIC_ASSERT( + std::is_copy_constructible< + Type>::value); + +BOOST_STATIC_ASSERT( + std::is_copy_assignable< + Type>::value); + +BOOST_STATIC_ASSERT( + std::is_default_constructible< + Type::iterator>::value); + +struct segments_encoded_ref_test +{ + // check that parsed string + // produces a sequence + static + void + check( + string_view s, + std::initializer_list< + string_view> init) + { + auto rv = parse_uri_reference(s); + if(! BOOST_TEST(rv.has_value())) + return; + url u = *rv; + Type ps(u.encoded_segments()); + BOOST_TEST_EQ( + ps.is_absolute(), s.starts_with('/')); + BOOST_TEST_EQ(ps.empty(), init.size() == 0); + if(! BOOST_TEST_EQ(ps.size(), init.size())) + return; + if(init.size() > 0 && ! ps.empty()) + { + BOOST_TEST_EQ(ps.front(), *init.begin()); + BOOST_TEST_EQ(ps.back(), *std::prev(init.end())); + } + + // forward + { + auto it0 = ps.begin(); + auto it1 = init.begin(); + auto const end = ps.end(); + while(it0 != end) + { + segments_encoded_base::reference r0(*it0); + segments_encoded_base::reference r1(*it1); + BOOST_TEST_EQ(r0, r1); + BOOST_TEST_EQ(*it0, *it1); + segments_encoded_base::value_type v0(*it0); + segments_encoded_base::value_type v1(*it1); + BOOST_TEST_EQ(v0, *it1); + BOOST_TEST_EQ(v1, *it1); + auto prev = it0++; + BOOST_TEST_NE(prev, it0); + BOOST_TEST_EQ(++prev, it0); + ++it1; + BOOST_TEST_EQ(v0, v1);; + } + } + + // reverse + if(init.size() > 0) + { + auto const begin = ps.begin(); + auto it0 = ps.end(); + auto it1 = init.end(); + do + { + auto prev = it0--; + BOOST_TEST_NE(prev, it0); + BOOST_TEST_EQ(--prev, it0); + --it1; + segments_encoded_base::reference r0(*it0); + segments_encoded_base::reference r1(*it1); + BOOST_TEST_EQ(*it0, *it1); + BOOST_TEST_EQ(r0, r1); + } + while(it0 != begin); + } + } + + // check that modification produces + // the string and correct sequence + static + void + check( + void(*f)(Type), + string_view s0, + string_view s1, + std::initializer_list< + string_view> init) + { + auto rv = parse_uri_reference(s0); + if(! BOOST_TEST(rv.has_value())) + return; + url u = *rv; + Type ps(u.encoded_segments()); + f(ps); + BOOST_TEST_EQ(u.encoded_path(), s1); + if(! BOOST_TEST_EQ( + ps.size(), init.size())) + return; + auto it0 = ps.begin(); + auto it1 = init.begin(); + auto const end = ps.end(); + while(it0 != end) + { + BOOST_TEST_EQ(*it0, *it1); + ++it0; + ++it1; + } + } + + static + void + check( + void(*f1)(Type), void(*f2)(Type), + string_view s0, string_view s1, + std::initializer_list< + string_view> init) + { + check(f1, s0, s1, init); + check(f2, s0, s1, init); + } + + //-------------------------------------------- + + void + testSpecial() + { + // segments_encoded_ref( + // segments_encoded_ref const&) + { + url u("/index.htm"); + Type ps0 = u.encoded_segments(); + Type ps1(ps0); + BOOST_TEST_EQ(&ps0.url(), &ps1.url()); + BOOST_TEST_EQ( + ps0.url().string().data(), + ps1.url().string().data()); + } + + // operator=(segments_encoded_ref) + { + url u1("/index.htm"); + url u2("/path/to/file.txt"); + Type ps1 = + u1.encoded_segments(); + Type ps2 = + u2.encoded_segments(); + BOOST_TEST_NE( + ps1.buffer().data(), + ps2.buffer().data()); + ps1 = ps2; + BOOST_TEST_EQ( + u1.encoded_path(), + u2.encoded_path()); + BOOST_TEST_NE( + ps1.buffer().data(), + ps2.buffer().data()); + } + + // operator=(segments_encoded_view) + { + url u1("/index.htm"); + url_view u2("/path/to/file.txt"); + Type ps1 = + u1.encoded_segments(); + segments_encoded_view ps2 = + u2.encoded_segments(); + BOOST_TEST_NE( + ps1.buffer().data(), + ps2.buffer().data()); + ps1 = ps2; + BOOST_TEST_EQ( + u1.encoded_path(), + u2.encoded_path()); + BOOST_TEST_NE( + ps1.buffer().data(), + ps2.buffer().data()); + } + + // operator=(initializer_list) + { + } + + // operator segments_encoded_view() + { + } + } + + void + testObservers() + { + // url() + { + url u0( "/" ); + url u1( "/" ); + BOOST_TEST_EQ( + &u0.encoded_segments().url(), &u0); + BOOST_TEST_EQ( + &u1.encoded_segments().url(), &u1); + BOOST_TEST_NE( + &u0.encoded_segments().url(), + &u1.encoded_segments().url()); + } + } + + void + testModifiers() + { + // + // clear() + // + + { + auto const f = [](Type ps) + { + ps.clear(); + }; + check(f, "", "", {} ); + check(f, "/", "/", {}); + check(f, "/index.htm", "/", {}); + check(f, "index.htm", "", {}); + check(f, "/path/to/file.txt", "/", {}); + check(f, "Program%20Files", "", {}); + check(f, "x://y/", "/", {}); + } + + // + // assign(initializer_list) + // assign(FwdIt, FwdIt) + // + + { + auto const f = [](Type ps) + { + ps.assign({ "path", "to", "file.txt" }); + }; + auto const g = [](Type ps) + { + auto const assign = [&ps]( + std::initializer_list< + pct_string_view> init) + { + ps.assign(init.begin(), init.end()); + }; + assign({ "path", "to", "file.txt" }); + }; + check(f, g, "", "path/to/file.txt", {"path", "to", "file.txt"}); + check(f, g, "/", "/path/to/file.txt", {"path", "to", "file.txt"}); + check(f, g, "/index.htm", "/path/to/file.txt", {"path", "to", "file.txt"}); + check(f, g, "index.htm", "path/to/file.txt", {"path", "to", "file.txt"}); + check(f, g, "/path/to/file.txt", "/path/to/file.txt", {"path", "to", "file.txt"}); + check(f, g, "Program%20Files", "path/to/file.txt", {"path", "to", "file.txt"}); + } + + // + // insert(iterator, pct_string_view) + // + + { + auto const f = [](Type ps) + { + ps.insert(ps.begin(), ""); + }; + check(f, "", "./", {""}); + check(f, "/", "/./", {""}); + check(f, "/index.htm", "/.//index.htm", {"", "index.htm"}); + check(f, "index.htm", ".//index.htm", {"", "index.htm"}); + check(f, "path/to/file.txt", ".//path/to/file.txt", {"", "path", "to", "file.txt"}); + check(f, "/path/to/file.txt", "/.//path/to/file.txt", {"", "path", "to", "file.txt"}); + check(f, "Program%20Files", ".//Program%20Files", {"", "Program%20Files"}); + } + { + auto const f = [](Type ps) + { + ps.insert(ps.begin(), "my%20seg"); + }; + check(f, "", "my%20seg", {"my%20seg"}); + check(f, "/", "/my%20seg", {"my%20seg"}); + check(f, "/index.htm", "/my%20seg/index.htm", {"my%20seg", "index.htm"}); + check(f, "index.htm", "my%20seg/index.htm", {"my%20seg", "index.htm"}); + check(f, "path/to/file.txt", "my%20seg/path/to/file.txt", {"my%20seg", "path", "to", "file.txt"}); + check(f, "/path/to/file.txt", "/my%20seg/path/to/file.txt", {"my%20seg", "path", "to", "file.txt"}); + check(f, "Program%20Files", "my%20seg/Program%20Files", {"my%20seg", "Program%20Files"}); + } + { + auto const f = [](Type ps) + { + ps.insert(std::next(ps.begin(), 1), "my%20seg"); + }; + check(f, "path/to/file.txt", "path/my%20seg/to/file.txt", {"path", "my%20seg", "to", "file.txt"}); + check(f, "/path/to/file.txt", "/path/my%20seg/to/file.txt", {"path", "my%20seg", "to", "file.txt"}); + } + { + auto const f = [](Type ps) + { + ps.insert(ps.end(), "my%20seg"); + }; + check(f, "", "my%20seg", {"my%20seg"}); + check(f, "/", "/my%20seg", {"my%20seg"}); + check(f, "/index.htm", "/index.htm/my%20seg", {"index.htm", "my%20seg"}); + check(f, "index.htm", "index.htm/my%20seg", {"index.htm", "my%20seg"}); + check(f, "path/to/file.txt", "path/to/file.txt/my%20seg", {"path", "to", "file.txt", "my%20seg"}); + check(f, "/path/to/file.txt", "/path/to/file.txt/my%20seg", {"path", "to", "file.txt", "my%20seg"}); + check(f, "Program%20Files", "Program%20Files/my%20seg", {"Program%20Files", "my%20seg"}); + } + { + auto const f = [](Type ps) + { + ps.insert(ps.end(), ""); + }; + check(f, "", "./", {""}); + check(f, "/", "/./", {""}); + check(f, "/index.htm", "/index.htm/", {"index.htm", ""}); + check(f, "index.htm", "index.htm/", {"index.htm", ""}); + check(f, "path/to/file.txt", "path/to/file.txt/", {"path", "to", "file.txt", ""}); + check(f, "/path/to/file.txt", "/path/to/file.txt/", {"path", "to", "file.txt", ""}); + (void)f; + } + + // + // insert(iterator, initializer_list) + // insert(iterator, FwdIt, FwdIt) + // + + { + auto const f = [](Type ps) + { + ps.insert(ps.begin(), { "u", "v" }); + }; + auto const g = [](Type ps) + { + auto const insert = [&ps]( + std::initializer_list< + pct_string_view> init) + { + ps.insert(ps.begin(), + init.begin(), init.end()); + }; + insert({ "u", "v" }); + }; + check(f, g, "", "u/v", {"u", "v"}); + check(f, g, "/", "/u/v", {"u", "v"}); + check(f, g, "/index.htm", "/u/v/index.htm", {"u", "v", "index.htm"}); + check(f, g, "index.htm", "u/v/index.htm", {"u", "v", "index.htm"}); + check(f, g, "path/to/file.txt", "u/v/path/to/file.txt", {"u", "v", "path", "to", "file.txt"}); + check(f, g, "/path/to/file.txt", "/u/v/path/to/file.txt", {"u", "v", "path", "to", "file.txt"}); + check(f, g, "Program%20Files", "u/v/Program%20Files", {"u", "v", "Program%20Files"}); + } + { + auto const f = [](Type ps) + { + ps.insert(ps.begin(), { "", "" }); + }; + auto const g = [](Type ps) + { + auto const insert = [&ps]( + std::initializer_list< + pct_string_view> init) + { + ps.insert(ps.begin(), + init.begin(), init.end()); + }; + insert({ "", "" }); + }; + check(f, g, "", ".//", {"", ""}); + check(f, g, "/", "/.//", {"", ""}); + check(f, g, "/index.htm", "/.///index.htm", {"", "", "index.htm"}); + check(f, g, "index.htm", ".///index.htm", {"", "", "index.htm"}); + check(f, g, "path/to/file.txt", ".///path/to/file.txt", {"", "", "path", "to", "file.txt"}); + check(f, g, "/path/to/file.txt", "/.///path/to/file.txt", {"", "", "path", "to", "file.txt"}); + check(f, g, "x", ".///x", {"", "", "x"}); + } + + // + // erase(iterator) + // + + { + auto const f = [](Type ps) + { + ps.erase(std::next(ps.begin(), 0)); + }; + check(f, "path/to/file.txt", "to/file.txt", {"to", "file.txt"}); + check(f, "/path/to/file.txt", "/to/file.txt", {"to", "file.txt"}); + } + { + auto const f = [](Type ps) + { + ps.erase(std::next(ps.begin(), 1)); + }; + check(f, "path/to/file.txt", "path/file.txt", {"path", "file.txt"}); + check(f, "/path/to/file.txt", "/path/file.txt", {"path", "file.txt"}); + } + { + auto const f = [](Type ps) + { + ps.erase(std::next(ps.begin(), 2)); + }; + check(f, "path/to/file.txt", "path/to", {"path", "to"}); + check(f, "/path/to/file.txt", "/path/to", {"path", "to"}); + } + { + auto const f = [](Type ps) + { + ps.erase(std::next(ps.begin(), 1)); + }; + check(f, "x://y///", "//", {"", ""}); + check(f, ".///", ".//", {"", ""}); + } + + // + // erase(iterator, iterator + // + + { + auto const f = [](Type ps) + { + ps.erase( + std::next(ps.begin(), 0), + std::next(ps.begin(), 2)); + }; + check(f, "path/to/the/file.txt", "the/file.txt", {"the", "file.txt"}); + check(f, "/path/to/the/file.txt", "/the/file.txt", {"the", "file.txt"}); + } + { + auto const f = [](Type ps) + { + ps.erase( + std::next(ps.begin(), 1), + std::next(ps.begin(), 3)); + }; + check(f, "path/to/the/file.txt", "path/file.txt", {"path", "file.txt"}); + check(f, "/path/to/the/file.txt", "/path/file.txt", {"path", "file.txt"}); + } + { + auto const f = [](Type ps) + { + ps.erase( + std::next(ps.begin(), 2), + std::next(ps.begin(), 4)); + }; + check(f, "path/to/the/file.txt", "path/to", {"path", "to"}); + check(f, "/path/to/the/file.txt", "/path/to", {"path", "to"}); + } + + // + // replace(iterator, pct_string_view) + // + + { + auto const f = [](Type ps) + { + ps.replace(std::next(ps.begin(), 0), ""); + }; + check(f, "path/to/file.txt", ".//to/file.txt", {"", "to", "file.txt"}); + check(f, "/path/to/file.txt", "/.//to/file.txt", {"", "to", "file.txt"}); + } + { + auto const f = [](Type ps) + { + ps.replace(std::next(ps.begin(), 1), ""); + }; + check(f, "path/to/file.txt", "path//file.txt", {"path", "", "file.txt"}); + check(f, "/path/to/file.txt", "/path//file.txt", {"path", "", "file.txt"}); + } + { + auto const f = [](Type ps) + { + ps.replace(std::next(ps.begin(), 0), "test"); + }; + check(f, "path/to/file.txt", "test/to/file.txt", {"test", "to", "file.txt"}); + check(f, "/path/to/file.txt", "/test/to/file.txt", {"test", "to", "file.txt"}); + } + { + auto const f = [](Type ps) + { + ps.replace(std::next(ps.begin(), 1), "test"); + }; + check(f, "path/to/file.txt", "path/test/file.txt", {"path", "test", "file.txt"}); + check(f, "/path/to/file.txt", "/path/test/file.txt", {"path", "test", "file.txt"}); + } + { + auto const f = [](Type ps) + { + ps.replace(std::next(ps.begin(), 2), "test"); + }; + check(f, "path/to/file.txt", "path/to/test", {"path", "to", "test"}); + check(f, "/path/to/file.txt", "/path/to/test", {"path", "to", "test"}); + } + + // + // replace(iterator, pct_string_view) + // + + { + auto const f = [](Type ps) + { + ps.replace( + std::next(ps.begin(), 0), + std::next(ps.begin(), 2), + ""); + }; + check(f, "path/to/the/file.txt", ".//the/file.txt", {"", "the", "file.txt"}); + check(f, "/path/to/the/file.txt", "/.//the/file.txt", {"", "the", "file.txt"}); + } + { + auto const f = [](Type ps) + { + ps.replace( + std::next(ps.begin(), 1), + std::next(ps.begin(), 3), + ""); + }; + check(f, "path/to/the/file.txt", "path//file.txt", {"path", "", "file.txt"}); + check(f, "/path/to/the/file.txt", "/path//file.txt", {"path", "", "file.txt"}); + } + { + auto const f = [](Type ps) + { + ps.replace( + std::next(ps.begin(), 2), + std::next(ps.begin(), 4), + ""); + }; + check(f, "path/to/the/file.txt", "path/to/", {"path", "to", ""}); + check(f, "/path/to/the/file.txt", "/path/to/", {"path", "to", ""}); + } + { + auto const f = [](Type ps) + { + ps.replace( + std::next(ps.begin(), 0), + std::next(ps.begin(), 2), + "test"); + }; + check(f, "path/to/the/file.txt", "test/the/file.txt", {"test", "the", "file.txt"}); + check(f, "/path/to/the/file.txt", "/test/the/file.txt", {"test", "the", "file.txt"}); + } + { + auto const f = [](Type ps) + { + ps.replace( + std::next(ps.begin(), 1), + std::next(ps.begin(), 3), + "test"); + }; + check(f, "path/to/the/file.txt", "path/test/file.txt", {"path", "test", "file.txt"}); + check(f, "/path/to/the/file.txt", "/path/test/file.txt", {"path", "test", "file.txt"}); + } + { + auto const f = [](Type ps) + { + ps.replace( + std::next(ps.begin(), 2), + std::next(ps.begin(), 4), + "test"); + }; + check(f, "path/to/the/file.txt", "path/to/test", {"path", "to", "test"}); + check(f, "/path/to/the/file.txt", "/path/to/test", {"path", "to", "test"}); + } + + // + // replace(iterator, iterator. initializer_list) + // replace(iterator, iterator. FwdIt, FwdIt) + // + + { + auto const f = [](Type ps) + { + ps.replace( + std::next(ps.begin(), 0), + std::next(ps.begin(), 2), + { "t", "u", "v" }); + }; + auto const g = [](Type ps) + { + auto const replace = [&ps]( + std::initializer_list< + pct_string_view> init) + { + ps.replace( + std::next(ps.begin(), 0), + std::next(ps.begin(), 2), + init.begin(), init.end()); + }; + replace({ "t", "u", "v" }); + }; + check(f, g, "path/to/the/file.txt", "t/u/v/the/file.txt", {"t", "u", "v", "the", "file.txt"}); + check(f, g, "/path/to/the/file.txt", "/t/u/v/the/file.txt", {"t", "u", "v", "the", "file.txt"}); + } + { + auto const f = [](Type ps) + { + ps.replace( + std::next(ps.begin(), 1), + std::next(ps.begin(), 3), + { "t", "u", "v" }); + }; + auto const g = [](Type ps) + { + auto const replace = [&ps]( + std::initializer_list< + pct_string_view> init) + { + ps.replace( + std::next(ps.begin(), 1), + std::next(ps.begin(), 3), + init.begin(), init.end()); + }; + replace({ "t", "u", "v" }); + }; + check(f, g, "path/to/the/file.txt", "path/t/u/v/file.txt", {"path", "t", "u", "v", "file.txt"}); + check(f, g, "/path/to/the/file.txt", "/path/t/u/v/file.txt", {"path", "t", "u", "v", "file.txt"}); + } + { + auto const f = [](Type ps) + { + ps.replace( + std::next(ps.begin(), 2), + std::next(ps.begin(), 4), + { "t", "u", "v" }); + }; + auto const g = [](Type ps) + { + auto const replace = [&ps]( + std::initializer_list< + pct_string_view> init) + { + ps.replace( + std::next(ps.begin(), 2), + std::next(ps.begin(), 4), + init.begin(), init.end()); + }; + replace({ "t", "u", "v" }); + }; + check(f, g, "path/to/the/file.txt", "path/to/t/u/v", {"path", "to", "t", "u", "v"}); + check(f, g, "/path/to/the/file.txt", "/path/to/t/u/v", {"path", "to", "t", "u", "v"}); + } + } + + void + testRange() + { + check( "", {}); + check( "/", {}); + check( "./", { "" }); + check( "./usr", { "usr" }); + check( "/index.htm", { "index.htm" }); + check( "/images/cat-pic.gif", { "images", "cat-pic.gif" }); + check( "images/cat-pic.gif", { "images", "cat-pic.gif" }); + check( "/fast//query", { "fast", "", "query" }); + check( "fast//", { "fast", "", "" }); + check( "/./", { "" }); + check( ".//", { "", "" }); + } + + void + testJavadocs() + { + // url() + { + url u( "?key=value" ); + + assert( &u.encoded_segments().url() == &u ); + } + } + + void + run() + { + testSpecial(); + testObservers(); + testModifiers(); + testRange(); + testJavadocs(); + } +}; + +TEST_SUITE( + segments_encoded_ref_test, + "boost.url.segments_encoded_ref"); + +} // urls +} // boost diff --git a/test/unit/segments_encoded_view.cpp b/test/unit/segments_encoded_view.cpp index b31a552e..48ef75a9 100644 --- a/test/unit/segments_encoded_view.cpp +++ b/test/unit/segments_encoded_view.cpp @@ -10,306 +10,72 @@ // Test that header file is self-contained. #include +#include #include +#include +#include + #include "test_suite.hpp" -#include -#include -#include -#include -#include namespace boost { namespace urls { -class segments_encoded_view_test +BOOST_STATIC_ASSERT( + ! std::is_default_constructible< + segments_encoded_view>::value); + +BOOST_STATIC_ASSERT( + std::is_copy_constructible< + segments_encoded_view>::value); + +BOOST_STATIC_ASSERT( + ! std::is_copy_assignable< + segments_encoded_view>::value); + +struct segments_const_encoded_view_test { -public: - - template - static - std::reverse_iterator - reverse(It const& it) - { - return std::reverse_iterator(it); - } - - void - bad(string_view s, - result ( - *f)(string_view)) - { - segments_encoded_view p; - BOOST_TEST_THROWS(p = f(s).value(), - std::exception); - BOOST_TEST(p.empty()); - BOOST_TEST_EQ(p.begin(), p.end()); - } - - void - check( - string_view s, - std::vector< - string_view> const& v0, - result( - *f)(string_view)) - { - segments_encoded_view sv; - BOOST_TEST_NO_THROW(sv = f(s).value()); - // forward - { - std::vector v1; - std::copy( - sv.begin(), - sv.end(), - std::back_inserter(v1)); - BOOST_TEST_EQ(v0, v1); - } - // reverse - { - std::vector v1; - std::copy( - reverse(sv.end()), - reverse(sv.begin()), - std::back_inserter(v1)); - std::reverse(v1.begin(), v1.end()); - BOOST_TEST_EQ(v0, v1); - } - } - - //-------------------------------------------- - void testMembers() { - // default constructor + // segments_encoded_view( + // segments_encoded_view const&) { - segments_encoded_view sv; - BOOST_TEST(sv.empty()); - BOOST_TEST_EQ(sv.size(), 0u); - BOOST_TEST( - sv.begin() == sv.end()); + segments_encoded_view ps0 = + parse_path("/path/to/file.txt").value(); + segments_encoded_view ps1(ps0); + BOOST_TEST_EQ( + ps0.buffer().data(), ps1.buffer().data()); } - // operator=(segments_view const&) + // operator segments_view() { - segments_encoded_view s1; - segments_encoded_view s2; - s1 = s2; - BOOST_TEST_EQ(s1.begin(), s2.begin()); - } - - // decoded - { - segments_encoded_view sev = parse_path( - "/%70%61%74%68/%74%6f/%66%69%6c%65%2e%74%78%74").value(); - segments_view sv = sev.decoded(); - BOOST_TEST_EQ(sv.size(), 3u); - BOOST_TEST(sv.is_absolute()); - } - - // is_absolute - { - BOOST_TEST(parse_path( - "/path/to/file.txt").value().is_absolute()); - BOOST_TEST(! parse_path( - "./my/downloads").value().is_absolute()); + segments_encoded_view ps0 = + parse_path( "/path/to/file.txt" ).value(); + segments_view ps1(ps0); + BOOST_TEST_EQ( + ps0.buffer().data(), ps1.buffer().data()); } } void - testElementAccess() + testJavadocs() { - // front - // back + // {class} { - segments_encoded_view sv = parse_path( - "/path/to/file.txt").value(); - BOOST_TEST_EQ(sv.front(), "path"); - BOOST_TEST_EQ(sv.back(), "file.txt"); - } - } + url_view u( "/path/to/file.txt" ); - void - testIterators() - { - using iter_t = - segments_encoded_view::iterator; + segments_encoded_view ps = u.encoded_segments(); - // iterator() - { - segments_encoded_view sv = parse_path( - "/path/to/file.txt").value(); - iter_t it1; - iter_t it2; - BOOST_TEST_EQ(it1, it2); - BOOST_TEST_NE(it1, sv.begin()); - BOOST_TEST_NE(it2, sv.begin()); + assert( ps.buffer().data() == u.string().data() ); + + ignore_unused(ps); } - // iterator(iterator const&) + // operator segments_view() { - segments_encoded_view sv = parse_path( - "/path/to/file.txt").value(); - iter_t it1 = sv.begin(); - iter_t it2(it1); - BOOST_TEST_EQ(it2, it1); - BOOST_TEST_EQ(*it1, *it2); - BOOST_TEST_EQ(*it1, "path"); - BOOST_TEST_EQ(*it2, "path"); - } + segments_view ps = parse_path( "/path/to/file.txt" ).value(); - // operator=(iterator const&) - { - segments_encoded_view sv = parse_path( - "/path/to/file.txt").value(); - iter_t it1; - it1 = sv.begin(); - iter_t it2; - it2 = sv.end(); - BOOST_TEST_NE(it2, it1); - it2 = it1; - BOOST_TEST_EQ(it2, it1); - BOOST_TEST_EQ(*it1, *it2); - BOOST_TEST_EQ(*it1, "path"); - BOOST_TEST_EQ(*it2, "path"); - } - - // operator* - // operator++ - // operator++(int) - { - segments_encoded_view sv = parse_path( - "/path/to/file.txt").value(); - iter_t it = sv.begin(); - BOOST_TEST_EQ(*it, "path"); - BOOST_TEST_EQ(*++it, "to"); - BOOST_TEST_EQ(*it++, "to"); - BOOST_TEST_EQ(*it++, "file.txt"); - BOOST_TEST_EQ(it, sv.end()); - } - - // operator* - // operator-- - // operator--(int) - { - segments_encoded_view sv = parse_path( - "/path/to/file.txt").value(); - iter_t it = sv.end(); - BOOST_TEST_EQ(*--it, "file.txt"); - BOOST_TEST_EQ(*it--, "file.txt"); - BOOST_TEST_EQ(*it, "to"); - BOOST_TEST_EQ(*--it, "path"); - BOOST_TEST_EQ(it, sv.begin()); - } - - // operator == - // operator != - { - segments_encoded_view sv = parse_path( - "/path/to/file.txt").value(); - iter_t it = sv.begin(); - BOOST_TEST_EQ(it, sv.begin()); - BOOST_TEST_NE(it, sv.end()); - BOOST_TEST_NE(++it, sv.begin()); - BOOST_TEST_NE(it++, sv.end()); - } - - // value_type outlives reference - { - segments_encoded_view::value_type v; - { - segments_encoded_view sv = parse_path( - "/path/to/file.txt").value(); - segments_encoded_view::reference r = - *sv.begin(); - v = segments_encoded_view::value_type(r); - } - BOOST_TEST_EQ(v, "path"); - } - } - - //-------------------------------------------- - - void - test_parse_path() - { - /* - path = [ "/" ] segment *( "/" segment ) - */ - check("", {}, &parse_path); - check("/", {}, &parse_path); - check("/a", {"a"}, &parse_path); - check("/:", {":"}, &parse_path); - check("/:/", {":",""}, &parse_path); - check("/a/", {"a",""}, &parse_path); - check("/a/b", {"a","b"}, &parse_path); - check("/%41/b", {"%41","b"}, &parse_path); - check("///b", {"","","b"}, &parse_path); - check("/%2f/b", {"%2f","b"}, &parse_path); - check("/%2541//", {"%2541","",""}, &parse_path); - check("/a/b/c", {"a","b","c"}, &parse_path); - check("a", {"a"}, &parse_path); - check("a/", {"a", ""}, &parse_path); - - bad("/%2", &parse_path); - bad("/%%", &parse_path); - } - - void - testCapacity() - { - segments_encoded_view sv; - BOOST_TEST(sv.empty()); - BOOST_TEST_EQ(sv.size(), 0u); - sv = parse_path("/path/to/file.txt").value(); - BOOST_TEST(! sv.empty()); - BOOST_TEST_EQ(sv.size(), 3u); - sv = {}; - BOOST_TEST(sv.empty()); - BOOST_TEST_EQ(sv.size(), 0u); - } - - void - testOutput() - { - std::stringstream ss; - segments_encoded_view sv = parse_path( - "/path/to/file.txt").value(); - ss << sv; - BOOST_TEST_EQ(ss.str(), "/path/to/file.txt"); - } - - void - testExamples() - { - { - segments_encoded_view sev = parse_path( "/path/to/file.txt" ).value(); - - std::stringstream ss; - for( auto it = sev.begin(); it != sev.end(); ++it ) - ss << *it << std::endl; - } - - { - url_view u = parse_uri( "http://example.com/path/to/file.txt" ).value(); - - segments_encoded_view sev = u.encoded_segments(); - - std::stringstream ss; - ss << sev << std::endl; - } - //--- - - { - segments_encoded_view sev = parse_path( "/%70%61%74%68/%74%6f/%66%69%6c%65%2e%74%78%74" ).value(); - - segments_view sv = sev.decoded(); - - std::stringstream ss; - - ss << sv.front() << "/../" << sv.back(); - - assert( ss.str() == "path/../file.txt" ); + ignore_unused(ps); } } @@ -317,19 +83,12 @@ public: run() { testMembers(); - testElementAccess(); - testIterators(); - testCapacity(); - testOutput(); - - test_parse_path(); - - testExamples(); + testJavadocs(); } }; TEST_SUITE( - segments_encoded_view_test, + segments_const_encoded_view_test, "boost.url.segments_encoded_view"); } // urls diff --git a/test/unit/segments_ref.cpp b/test/unit/segments_ref.cpp new file mode 100644 index 00000000..0166e2df --- /dev/null +++ b/test/unit/segments_ref.cpp @@ -0,0 +1,128 @@ +// +// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2022 Alan de Freitas (alandefreitas@gmail.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/CPPAlliance/url +// + +// Test that header file is self-contained. +#include + +#include + +#include "test_suite.hpp" + +namespace boost { +namespace urls { + +//------------------------------------------------ + +struct segments_ref_test +{ + using Type = segments_encoded_ref; + + // check that modification produces + // the string and correct sequence + static + void + check( + void(*f)(Type), + string_view s0, + string_view s1, + std::initializer_list< + string_view> init) + { + auto rv = parse_uri_reference(s0); + if(! BOOST_TEST(rv.has_value())) + return; + url u = *rv; + Type ps(u.encoded_segments()); + f(ps); + BOOST_TEST_EQ(u.encoded_path(), s1); + if(! BOOST_TEST_EQ(ps.size(), init.size())) + return; + auto it0 = ps.begin(); + auto it1 = init.begin(); + auto const end = ps.end(); + while(it0 != end) + { + BOOST_TEST_EQ(*it0, *it1); + ++it0; + ++it1; + } + } + + static + void + check( + void(*f1)(Type), void(*f2)(Type), + string_view s0, string_view s1, + std::initializer_list< + string_view> init) + { + check(f1, s0, s1, init); + check(f2, s0, s1, init); + } + + //-------------------------------------------- + + void + testEditSegments() + { + /* Legend + + '#' 0x23 '/' 0x2f + '%' 0x25 ':' 0x3a + '.' 0x2e '?' 0x3f + */ + { + auto const f = [](Type ps) + { + ps.push_back(""); + }; + check(f, "", "./", {""}); + check(f, "/", "/./", {""}); + check(f, "./", ".//", {"", ""}); + check(f, "/./", "/.//", {"", ""}); + } + { + auto const f = [](Type ps) + { + ps.push_back("/"); + }; + check(f, "", "%2F", {"%2F"}); + check(f, "/", "/%2F", {"%2F"}); + } + { + auto const f = [](Type ps) + { + ps.push_back(":"); + }; + check(f, "", "./:", {":"}); + check(f, "/", "/:", {":"}); + } + } + + void + run() + { +#if 0 + testEditSegments(); + url u( "/path/to/the/file.txt" ); + segments_ref ps = u.segments(); + ps.erase( + std::next(ps.begin(), 1), + std::next(ps.begin(), 3)); +#endif + } +}; + +TEST_SUITE( + segments_ref_test, + "boost.url.segments_ref"); + +} // urls +} // boost diff --git a/test/unit/segments_view.cpp b/test/unit/segments_view.cpp index 17a4c445..f914227e 100644 --- a/test/unit/segments_view.cpp +++ b/test/unit/segments_view.cpp @@ -10,335 +10,64 @@ // Test that header file is self-contained. #include -#include +#include +#include +#include +#include + #include "test_suite.hpp" -#include -#include -#include -#include namespace boost { namespace urls { -class segments_view_test +BOOST_STATIC_ASSERT( + ! std::is_default_constructible< + segments_encoded_view>::value); + +BOOST_STATIC_ASSERT( + std::is_copy_constructible< + segments_encoded_view>::value); + +BOOST_STATIC_ASSERT( + ! std::is_copy_assignable< + segments_encoded_view>::value); + +struct segments_view_test { -public: - template - static - std::reverse_iterator - reverse(It const& it) - { - return std::reverse_iterator(it); - } - - void - bad(string_view s, - result ( - *f)(string_view)) - { - segments_view sv; - BOOST_TEST_THROWS( - sv = f(s).value().decoded(), - std::exception); - BOOST_TEST(sv.empty()); - BOOST_TEST_EQ(sv.begin(), sv.end()); - } - - template - static - bool - vec_eq( - std::vector const& v1, - std::vector const& v2) - { - if(v1.size() != v2.size()) - return false; - for(std::size_t i = 0; - i < v1.size(); ++i) - if(v1[i] != v2[i]) - return false; - return true; - } - - void - check( - string_view s, - std::vector< - string_view> const& v0, - result( - *f)(string_view)) - { - segments_view sv; - BOOST_TEST_NO_THROW( - sv = f(s).value().decoded()); - // forward - { - std::vector v1; - std::copy( - sv.begin(), - sv.end(), - std::back_inserter(v1)); - BOOST_TEST(vec_eq(v0, v1)); - } - // reverse - { - std::vector v1; - std::copy( - reverse(sv.end()), - reverse(sv.begin()), - std::back_inserter(v1)); - std::reverse(v1.begin(), v1.end()); - BOOST_TEST(vec_eq(v0, v1)); - } - } - - //-------------------------------------------- - void testMembers() { - // default constructor + // segments_view( + // segments_view const&) { - segments_view sv; - BOOST_TEST(sv.empty()); - BOOST_TEST_EQ(sv.size(), 0u); - BOOST_TEST( - sv.begin() == sv.end()); - } - - // operator=(segments_view const&) - { - segments_view s1; - segments_view s2; - s1 = s2; - BOOST_TEST_EQ(s1.begin(), s2.begin()); - } - - // decoded - { - segments_view sv = parse_path( - "/%70%61%74%68/%74%6f/%66%69%6c%65%2e%74%78%74" - ).value().decoded(); - BOOST_TEST_EQ(sv.size(), 3u); - BOOST_TEST(sv.is_absolute()); - } - - // is_absolute - { - segments_view sv; - sv = parse_path("/path/to/file.txt" - ).value().decoded(); - BOOST_TEST(sv.is_absolute()); - sv = parse_path("./my/downloads" - ).value().decoded(); - BOOST_TEST(! sv.is_absolute()); + segments_view ps0 = + parse_path("/path/to/file.txt").value(); + segments_view ps1(ps0); + BOOST_TEST_EQ( + ps0.buffer().data(), ps1.buffer().data()); } } void - testElementAccess() + testJavadocs() { - // front - // back + // {class} { - segments_view sv = parse_path( - "/path/to/file.txt").value().decoded(); - BOOST_TEST_EQ(sv.front(), "path"); - BOOST_TEST_EQ(sv.back(), "file.txt"); + url_view u( "/path/to/file.txt" ); + + segments_view ps = u.segments(); + + assert( ps.buffer().data() == u.string().data() ); + + ignore_unused(ps); } } - void - testIterators() - { - using iter_t = - segments_view::iterator; - - // iterator() - { - segments_view sv = parse_path( - "/path/to/file.txt").value().decoded(); - iter_t it1; - iter_t it2; - BOOST_TEST_EQ(it1, it2); - BOOST_TEST_NE(it1, sv.begin()); - BOOST_TEST_NE(it2, sv.begin()); - } - - // iterator(iterator const&) - { - segments_view sv = parse_path( - "/path/to/file.txt").value().decoded(); - iter_t it1 = sv.begin(); - iter_t it2(it1); - BOOST_TEST_EQ(it2, it1); - BOOST_TEST_EQ(*it1, *it2); - BOOST_TEST_EQ(*it1, "path"); - BOOST_TEST_EQ(*it2, "path"); - } - - // operator=(iterator const&) - { - segments_view sv = parse_path( - "/path/to/file.txt").value().decoded(); - iter_t it1; - it1 = sv.begin(); - iter_t it2; - it2 = sv.end(); - BOOST_TEST_NE(it2, it1); - it2 = it1; - BOOST_TEST_EQ(it2, it1); - BOOST_TEST_EQ(*it1, *it2); - BOOST_TEST_EQ(*it1, "path"); - BOOST_TEST_EQ(*it2, "path"); - } - - // operator* - // operator++ - // operator++(int) - { - segments_view sv = parse_path( - "/path/to/file.txt").value().decoded(); - iter_t it = sv.begin(); - BOOST_TEST_EQ(*it, "path"); - BOOST_TEST_EQ(*++it, "to"); - BOOST_TEST_EQ(*it++, "to"); - BOOST_TEST_EQ(*it++, "file.txt"); - BOOST_TEST_EQ(it, sv.end()); - } - - // operator* - // operator-- - // operator--(int) - { - segments_view sv = parse_path( - "/path/to/file.txt").value().decoded(); - iter_t it = sv.end(); - BOOST_TEST_EQ(*--it, "file.txt"); - BOOST_TEST_EQ(*it--, "file.txt"); - BOOST_TEST_EQ(*it, "to"); - BOOST_TEST_EQ(*--it, "path"); - BOOST_TEST_EQ(it, sv.begin()); - } - - // operator == - // operator != - { - segments_view sv = parse_path( - "/path/to/file.txt").value().decoded(); - iter_t it = sv.begin(); - BOOST_TEST_EQ(it, sv.begin()); - BOOST_TEST_NE(it, sv.end()); - BOOST_TEST_NE(++it, sv.begin()); - BOOST_TEST_NE(it++, sv.end()); - } - - // value_type outlives reference - { - segments_view::value_type v; - { - segments_view se = parse_path( - "path/to/the/file.txt").value(); - segments_view::reference r = - *se.begin(); - v = segments_view::value_type(r); - } - BOOST_TEST_EQ(v, "path"); - } - } - - //-------------------------------------------- - - void - test_parse_path() - { - /* - path = [ "/" ] segment *( "/" segment ) - */ - check("", {}, &parse_path); - check("/", {}, &parse_path); - check("/a", {"a"}, &parse_path); - check("/:", {":"}, &parse_path); - check("/:/", {":",""}, &parse_path); - check("/a/", {"a",""}, &parse_path); - check("/a/b", {"a","b"}, &parse_path); - check("/%41/b", {"A","b"}, &parse_path); - check("///b", {"","","b"}, &parse_path); - check("/%2f/b", {"/","b"}, &parse_path); - check("/%2541//", {"%41","",""}, &parse_path); - check("/a/b/c", {"a","b","c"}, &parse_path); - check("a", {"a"}, &parse_path); - check("a/", {"a", ""}, &parse_path); - - bad("/%2", &parse_path); - bad("/%%", &parse_path); - } - - void - testCapacity() - { - segments_view sv; - BOOST_TEST(sv.empty()); - BOOST_TEST_EQ(sv.size(), 0u); - sv = parse_path( - "/path/to/file.txt").value().decoded(); - BOOST_TEST(! sv.empty()); - BOOST_TEST_EQ(sv.size(), 3u); - sv = {}; - BOOST_TEST(sv.empty()); - BOOST_TEST_EQ(sv.size(), 0u); - } - - void - testOutput() - { - { - // empty - std::stringstream ss; - segments_view sv = parse_path( - "").value().decoded(); - BOOST_TEST(!sv.is_absolute()); - ss << sv; - BOOST_TEST_EQ(ss.str(), ""); - } - { - // absolute - std::stringstream ss; - segments_view sv = parse_path( - "/%70%61%74%68/%74%6f/%66%69%6c%65%2e%74%78%74" - ).value().decoded(); - ss << sv; - auto str = ss.str(); - BOOST_TEST_EQ(str, "/path/to/file.txt"); - } - { - // relative - std::stringstream ss; - segments_view sv = parse_path( - "%70%61%74%68/%74%6f/%66%69%6c%65%2e%74%78%74" - ).value().decoded(); - ss << sv; - BOOST_TEST_EQ(ss.str(), "path/to/file.txt"); - } - } - - void - testExamples() - { - } - void run() { testMembers(); - testElementAccess(); - testIterators(); - testCapacity(); - testOutput(); - - test_parse_path(); - - testExamples(); + testJavadocs(); } }; diff --git a/test/unit/snippets.cpp b/test/unit/snippets.cpp index f303b221..f5ac91d7 100644 --- a/test/unit/snippets.cpp +++ b/test/unit/snippets.cpp @@ -694,6 +694,7 @@ parsing_path() } { //[snippet_parsing_path_4 + /* url_view u("https://www.boost.org//doc///libs"); std::cout << u << "\n" "path: " << u.encoded_path() << "\n" @@ -702,6 +703,7 @@ parsing_path() std::cout << u.encoded_segments().size() << " segments\n"; for (auto seg: u.encoded_segments()) std::cout << "segment: " << seg << "\n"; + */ //] } @@ -776,10 +778,12 @@ parsing_path() { //[snippet_parsing_path_9 + /* segments_view segs = parse_path("/doc/libs").value(); assert( segs.size() == 2 ); + */ //] - boost::ignore_unused(segs); + //boost::ignore_unused(segs); } { @@ -1390,7 +1394,7 @@ modifying_path() //[snippet_modifying_path_11 // should not insert as "pathto/file.txt" url u = parse_uri_reference("to/file.txt").value(); - segments segs = u.segments(); + segments_ref segs = u.segments(); segs.insert(segs.begin(), "path"); assert(u.string() == "path/to/file.txt"); //] diff --git a/test/unit/url.cpp b/test/unit/url.cpp index 4f9695d6..c95c741d 100644 --- a/test/unit/url.cpp +++ b/test/unit/url.cpp @@ -159,6 +159,12 @@ struct url_test } // reserve + { + url u; + u.reserve(0); + BOOST_TEST_GE(u.capacity(), 0); + BOOST_TEST_EQ(u.c_str()[0], '\0'); + } { url u; u.reserve(32); @@ -441,6 +447,13 @@ struct url_test { u.set_path(u.query()); }); + + // crash + { + url u; + u.set_path(""); + BOOST_TEST(u.empty()); + } } void @@ -632,14 +645,7 @@ struct url_test url u(u0); u.segments() = init; equal(u.segments(), init); - equal(u.encoded_segments(), init); - BOOST_TEST_EQ(u.string(), s1); - } - { - url u(u0); - u.encoded_segments() = init; - equal(u.segments(), init); - equal(u.encoded_segments(), init); + //equal(u.encoded_segments(), init); BOOST_TEST_EQ(u.string(), s1); } }; diff --git a/test/unit/url_base.cpp b/test/unit/url_base.cpp index 593e3f84..ad2a6a59 100644 --- a/test/unit/url_base.cpp +++ b/test/unit/url_base.cpp @@ -1683,7 +1683,7 @@ struct url_base_test { url u( "http://example.com/path/to/file.txt" ); - segments sv = u.segments(); + segments_ref sv = u.segments(); (void)sv; } @@ -1692,7 +1692,7 @@ struct url_base_test { url u( "http://example.com/path/to/file.txt" ); - segments_encoded sv = u.encoded_segments(); + segments_encoded_ref sv = u.encoded_segments(); (void)sv; }