From 144a87aa4996350b2a90ecfb7f6d112e2228d704 Mon Sep 17 00:00:00 2001 From: Vinnie Falco Date: Mon, 5 Sep 2022 13:07:38 -0700 Subject: [PATCH] segments refactor --- doc/HelpCard.odg | Bin 69590 -> 69593 bytes doc/images/HelpCard.svg | 2 +- doc/qbk/0.main.qbk | 4 +- doc/qbk/3.4.path.qbk | 2 +- doc/qbk/quickref.xml | 6 +- example/route/route.cpp | 2 +- include/boost/url.hpp | 15 +- include/boost/url/detail/any_params_iter.hpp | 215 ++--- include/boost/url/detail/any_path_iter.hpp | 298 ------- .../boost/url/detail/any_segments_iter.hpp | 297 +++++++ include/boost/url/detail/config.hpp | 4 - include/boost/url/detail/encode.hpp | 18 +- .../boost/url/detail/impl/any_params_iter.ipp | 192 ++--- .../boost/url/detail/impl/any_path_iter.ipp | 328 -------- .../url/detail/impl/any_segments_iter.ipp | 297 +++++++ .../impl/segments_encoded_iterator_impl.ipp | 131 --- .../url/detail/impl/segments_iter_impl.ipp | 141 ++++ .../detail/impl/segments_iterator_impl.ipp | 131 --- include/boost/url/detail/impl/url_impl.ipp | 66 ++ include/boost/url/detail/path.hpp | 33 +- .../detail/segments_encoded_iterator_impl.hpp | 72 -- .../boost/url/detail/segments_iter_impl.hpp | 79 ++ .../url/detail/segments_iterator_impl.hpp | 78 -- include/boost/url/detail/url_impl.hpp | 83 ++ .../url/grammar/detail/copied_strings.hpp | 149 ---- .../grammar/detail/impl/copied_strings.ipp | 99 --- include/boost/url/impl/decode_view.ipp | 17 - .../boost/url/impl/params_encoded_view.ipp | 4 +- include/boost/url/impl/parse_path.ipp | 66 ++ include/boost/url/impl/segments.hpp | 403 ---------- include/boost/url/impl/segments.ipp | 81 -- include/boost/url/impl/segments_base.hpp | 128 +++ include/boost/url/impl/segments_base.ipp | 122 +++ include/boost/url/impl/segments_encoded.hpp | 373 --------- include/boost/url/impl/segments_encoded.ipp | 104 --- .../boost/url/impl/segments_encoded_base.hpp | 103 +++ .../boost/url/impl/segments_encoded_base.ipp | 104 +++ .../boost/url/impl/segments_encoded_ref.hpp | 276 +++++++ .../boost/url/impl/segments_encoded_ref.ipp | 56 ++ .../boost/url/impl/segments_encoded_view.hpp | 180 ----- .../boost/url/impl/segments_encoded_view.ipp | 77 +- include/boost/url/impl/segments_ref.hpp | 240 ++++++ include/boost/url/impl/segments_ref.ipp | 63 ++ include/boost/url/impl/segments_view.hpp | 152 ---- include/boost/url/impl/segments_view.ipp | 61 -- include/boost/url/impl/url_base.ipp | 564 ++++++------- include/boost/url/param.hpp | 9 +- .../boost/url/params_const_encoded_view.hpp | 2 + include/boost/url/params_const_view.hpp | 2 + include/boost/url/params_encoded_view.hpp | 157 ++-- include/boost/url/parse_path.hpp | 55 ++ include/boost/url/pct_string_view.hpp | 11 +- include/boost/url/segments.hpp | 731 ----------------- include/boost/url/segments_base.hpp | 292 +++++++ include/boost/url/segments_encoded.hpp | 757 ------------------ include/boost/url/segments_encoded_base.hpp | 298 +++++++ include/boost/url/segments_encoded_ref.hpp | 681 ++++++++++++++++ include/boost/url/segments_encoded_view.hpp | 398 ++------- include/boost/url/segments_ref.hpp | 182 +++++ include/boost/url/segments_view.hpp | 319 ++------ include/boost/url/src.hpp | 16 +- include/boost/url/url.hpp | 20 - include/boost/url/url_base.hpp | 46 +- include/boost/url/url_view_base.hpp | 33 +- test/unit/CMakeLists.txt | 10 +- test/unit/Jamfile | 8 +- test/unit/doc_container.cpp | 2 +- test/unit/param.cpp | 8 +- test/unit/params_encoded_view.cpp | 9 +- test/unit/parse_path.cpp | 45 ++ test/unit/segments.cpp | 528 ------------ test/unit/segments_base.cpp | 169 ++++ test/unit/segments_encoded.cpp | 482 ----------- test/unit/segments_encoded_base.cpp | 166 ++++ test/unit/segments_encoded_ref.cpp | 718 +++++++++++++++++ test/unit/segments_encoded_view.cpp | 325 +------- test/unit/segments_ref.cpp | 128 +++ test/unit/segments_view.cpp | 341 +------- test/unit/snippets.cpp | 8 +- test/unit/url.cpp | 22 +- test/unit/url_base.cpp | 4 +- 81 files changed, 5797 insertions(+), 7101 deletions(-) delete mode 100644 include/boost/url/detail/any_path_iter.hpp create mode 100644 include/boost/url/detail/any_segments_iter.hpp delete mode 100644 include/boost/url/detail/impl/any_path_iter.ipp create mode 100644 include/boost/url/detail/impl/any_segments_iter.ipp delete mode 100644 include/boost/url/detail/impl/segments_encoded_iterator_impl.ipp create mode 100644 include/boost/url/detail/impl/segments_iter_impl.ipp delete mode 100644 include/boost/url/detail/impl/segments_iterator_impl.ipp delete mode 100644 include/boost/url/detail/segments_encoded_iterator_impl.hpp create mode 100644 include/boost/url/detail/segments_iter_impl.hpp delete mode 100644 include/boost/url/detail/segments_iterator_impl.hpp delete mode 100644 include/boost/url/grammar/detail/copied_strings.hpp delete mode 100644 include/boost/url/grammar/detail/impl/copied_strings.ipp create mode 100644 include/boost/url/impl/parse_path.ipp delete mode 100644 include/boost/url/impl/segments.hpp delete mode 100644 include/boost/url/impl/segments.ipp create mode 100644 include/boost/url/impl/segments_base.hpp create mode 100644 include/boost/url/impl/segments_base.ipp delete mode 100644 include/boost/url/impl/segments_encoded.hpp delete mode 100644 include/boost/url/impl/segments_encoded.ipp create mode 100644 include/boost/url/impl/segments_encoded_base.hpp create mode 100644 include/boost/url/impl/segments_encoded_base.ipp create mode 100644 include/boost/url/impl/segments_encoded_ref.hpp create mode 100644 include/boost/url/impl/segments_encoded_ref.ipp delete mode 100644 include/boost/url/impl/segments_encoded_view.hpp create mode 100644 include/boost/url/impl/segments_ref.hpp create mode 100644 include/boost/url/impl/segments_ref.ipp delete mode 100644 include/boost/url/impl/segments_view.ipp create mode 100644 include/boost/url/parse_path.hpp delete mode 100644 include/boost/url/segments.hpp create mode 100644 include/boost/url/segments_base.hpp delete mode 100644 include/boost/url/segments_encoded.hpp create mode 100644 include/boost/url/segments_encoded_base.hpp create mode 100644 include/boost/url/segments_encoded_ref.hpp create mode 100644 include/boost/url/segments_ref.hpp create mode 100644 test/unit/parse_path.cpp delete mode 100644 test/unit/segments.cpp create mode 100644 test/unit/segments_base.cpp delete mode 100644 test/unit/segments_encoded.cpp create mode 100644 test/unit/segments_encoded_base.cpp create mode 100644 test/unit/segments_encoded_ref.cpp create mode 100644 test/unit/segments_ref.cpp diff --git a/doc/HelpCard.odg b/doc/HelpCard.odg index bc0e09e4acb7598dfa05130666ec55b1ba42ca21..d7afa2a729e60b842dc79ea4a966c87af89bfa6c 100644 GIT binary patch delta 42933 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

F9j$Fo+< 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 C$PgL; delta 42830 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 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; }