From aafe6d2d4483409eea3ad5cabbe021e06d1ae63e Mon Sep 17 00:00:00 2001 From: Vinnie Falco Date: Tue, 7 Sep 2021 13:52:03 -0700 Subject: [PATCH] path_view --- include/boost/url.hpp | 1 + include/boost/url/impl/path_view.hpp | 96 +++++++++ include/boost/url/impl/path_view.ipp | 121 ++++++++++++ include/boost/url/impl/query_params_view.ipp | 5 +- include/boost/url/impl/url.hpp | 25 --- include/boost/url/impl/url.ipp | 17 ++ include/boost/url/impl/url_view.hpp | 26 --- include/boost/url/impl/url_view.ipp | 177 ++--------------- include/boost/url/path_view.hpp | 187 ++++++++++++++++++ .../rfc/detail/{paths.hpp => paths_bnf.hpp} | 0 include/boost/url/rfc/impl/hier_part_bnf.ipp | 2 +- .../boost/url/rfc/impl/relative_part_bnf.ipp | 2 +- include/boost/url/src.hpp | 1 + include/boost/url/url.hpp | 13 +- include/boost/url/url_view.hpp | 186 +---------------- test/CMakeLists.txt | 2 +- test/path_view.cpp | 62 ++++++ test/url.cpp | 12 +- 18 files changed, 520 insertions(+), 415 deletions(-) create mode 100644 include/boost/url/impl/path_view.hpp create mode 100644 include/boost/url/impl/path_view.ipp create mode 100644 include/boost/url/path_view.hpp rename include/boost/url/rfc/detail/{paths.hpp => paths_bnf.hpp} (100%) create mode 100644 test/path_view.cpp diff --git a/include/boost/url.hpp b/include/boost/url.hpp index 9651a63b..c4993ada 100644 --- a/include/boost/url.hpp +++ b/include/boost/url.hpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include diff --git a/include/boost/url/impl/path_view.hpp b/include/boost/url/impl/path_view.hpp new file mode 100644 index 00000000..e4ddeb60 --- /dev/null +++ b/include/boost/url/impl/path_view.hpp @@ -0,0 +1,96 @@ +// +// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/CPPAlliance/url +// + +#ifndef BOOST_URL_IMPL_PATH_VIEW_HPP +#define BOOST_URL_IMPL_PATH_VIEW_HPP + +#include + +namespace boost { +namespace urls { + +class path_view::iterator +{ + path_view::value_type v_; + char const* next_ = nullptr; + char const* end_ = nullptr; + + friend path_view; + + BOOST_URL_DECL + explicit + iterator( + string_view s); + + explicit + iterator( + char const* end) noexcept + : end_(end) + { + } + +public: + using value_type = + path_view::value_type; + using pointer = value_type const*; + using reference = value_type const&; + using iterator_category = + std::forward_iterator_tag; + + iterator() noexcept = default; + iterator( + iterator const&) noexcept = default; + iterator& operator=( + iterator const&) noexcept = default; + + value_type const& + operator*() const noexcept + { + return v_; + } + + value_type const* + operator->() const noexcept + { + return &v_; + } + + bool + operator==( + iterator other) const noexcept + { + return + next_ == other.next_ && + end_ == other.end_; + } + + bool + operator!=( + iterator other) const noexcept + { + return ! (*this == other); + } + + BOOST_URL_DECL + iterator& + operator++() noexcept; + + iterator + operator++(int) noexcept + { + auto tmp = *this; + ++*this; + return tmp; + } +}; + +} // urls +} // boost + +#endif diff --git a/include/boost/url/impl/path_view.ipp b/include/boost/url/impl/path_view.ipp new file mode 100644 index 00000000..0a7469d1 --- /dev/null +++ b/include/boost/url/impl/path_view.ipp @@ -0,0 +1,121 @@ +// +// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/CPPAlliance/url +// + +#ifndef BOOST_URL_IMPL_PATH_VIEW_IPP +#define BOOST_URL_IMPL_PATH_VIEW_IPP + +#include +#include +#include +#include +#include + +namespace boost { +namespace urls { + +path_view:: +path_view() noexcept + : s_("") + , n_(0) +{ +} + +path_view:: +iterator:: +iterator( + string_view s) + : next_(s.data()) + , end_(s.data() + s.size()) +{ + if(next_ == end_) + { + next_ = nullptr; + return; + } + error_code ec; + detail:: + path_abempty_bnf::begin( + next_, end_, ec, v_.s_); + if(ec) + detail::throw_system_error( + ec, BOOST_CURRENT_LOCATION); +} + +auto +path_view:: +iterator:: +operator++() noexcept -> + iterator& +{ + error_code ec; + detail:: + path_abempty_bnf::increment( + next_, end_, ec, v_.s_); + if(ec == error::end) + { + next_ = nullptr; + return *this; + } + if(ec) + detail::throw_system_error( + ec, BOOST_CURRENT_LOCATION); + return *this; +} + +//------------------------------------------------ + +auto +path_view:: +begin() const noexcept -> + iterator +{ + return iterator(s_); +} + +auto +path_view:: +end() const noexcept -> + iterator +{ + return iterator( + s_.data() + s_.size()); +} + +//------------------------------------------------ + +path_view +parse_path( + string_view s, + error_code& ec) +{ + using bnf::parse; + bnf::range< + pct_encoded_str> t; + if(! parse(s, ec, + detail::path_abempty_bnf{t})) + return {}; + return path_view( + t.str(), t.size()); +} + +path_view +parse_path( + string_view s) +{ + error_code ec; + auto p = parse_path(s, ec); + detail::maybe_throw(ec, + BOOST_CURRENT_LOCATION); + return p; +} + +} // urls +} // boost + +#endif diff --git a/include/boost/url/impl/query_params_view.ipp b/include/boost/url/impl/query_params_view.ipp index cd31c657..8a534963 100644 --- a/include/boost/url/impl/query_params_view.ipp +++ b/include/boost/url/impl/query_params_view.ipp @@ -189,9 +189,8 @@ parse_query_params( { error_code ec; auto qp = parse_query_params(s, ec); - if(ec.failed()) - detail::throw_system_error( - ec, BOOST_CURRENT_LOCATION); + detail::maybe_throw(ec, + BOOST_CURRENT_LOCATION); return qp; } diff --git a/include/boost/url/impl/url.hpp b/include/boost/url/impl/url.hpp index 5cfa2166..ef52b256 100644 --- a/include/boost/url/impl/url.hpp +++ b/include/boost/url/impl/url.hpp @@ -13,15 +13,6 @@ namespace boost { namespace urls { -url_view:: -segments_type:: -segments_type( - url const& v) noexcept - : s_(v.s_) - , pt_(&v.pt_) -{ -} - bool url:: segments_type:: @@ -38,22 +29,6 @@ operator==( off_ == other.off_; } -auto -url:: -segments() const noexcept -> - url_view::segments_type -{ - return url_view::segments_type(*this); -} - -auto -url:: -segments() noexcept -> - segments_type -{ - return segments_type(*this); -} - //---------------------------------------------------------- template diff --git a/include/boost/url/impl/url.ipp b/include/boost/url/impl/url.ipp index 75513e0d..152318d3 100644 --- a/include/boost/url/impl/url.ipp +++ b/include/boost/url/impl/url.ipp @@ -990,6 +990,23 @@ set_encoded_path( return *this; } +path_view +url:: +path() const noexcept +{ + return path_view( + pt_.get(detail::id_path, s_), + pt_.nseg); +} + +auto +url:: +path() noexcept -> + segments_type +{ + return segments_type(*this); +} + //---------------------------------------------------------- // // query diff --git a/include/boost/url/impl/url_view.hpp b/include/boost/url/impl/url_view.hpp index 3df8ba1e..fa1800e3 100644 --- a/include/boost/url/impl/url_view.hpp +++ b/include/boost/url/impl/url_view.hpp @@ -13,32 +13,6 @@ namespace boost { namespace urls { -bool -url_view:: -segments_type:: -iterator:: -operator==( - iterator other) const noexcept -{ - BOOST_ASSERT( - pt_ != other.pt_ || - off_ != other.off_ || - n_ == other.n_); - return - pt_ == other.pt_ && - off_ == other.off_; -} - -//---------------------------------------------------------- - -auto -url_view:: -segments() const noexcept -> - segments_type -{ - return segments_type(*this); -} - } // urls } // boost diff --git a/include/boost/url/impl/url_view.ipp b/include/boost/url/impl/url_view.ipp index c4cb9560..9eaec329 100644 --- a/include/boost/url/impl/url_view.ipp +++ b/include/boost/url/impl/url_view.ipp @@ -396,9 +396,15 @@ string_view url_view:: encoded_path() const noexcept { - return pt_.get( - id_path, - s_); + return get(id_path); +} + +path_view +url_view:: +path() const noexcept +{ + return path_view( + get(id_path), pt_.nseg); } //---------------------------------------------------------- @@ -480,167 +486,6 @@ encoded_fragment() const noexcept return s.substr(1); } -//---------------------------------------------------------- -// -// segments_type -// -//---------------------------------------------------------- - -url_view:: -segments_type:: -iterator:: -iterator() noexcept - : s_(nullptr) - , pt_(nullptr) - , off_(0) - , n_(0) -{ -} - -url_view:: -segments_type:: -iterator:: -iterator( - segments_type const* v, - bool end) noexcept - : s_(v->s_) - , pt_(v->pt_) -{ - if(! pt_) - { - off_ = 0; - n_ = 0; - } - else if( end || - pt_->nseg == 0) - { - off_ = pt_->offset[ - id_query]; - n_ = 0; - } - else - { - off_ = pt_->offset[ - id_path]; - parse(); - } -} - -auto -url_view:: -segments_type:: -iterator:: -operator*() const noexcept -> - value_type -{ - string_view s = { - s_ + off_, n_ }; - if(! s.empty() && - s.front() == '/') - s = s.substr(1); - return value_type(s); -} - -auto -url_view:: -segments_type:: -iterator:: -operator++() noexcept -> - iterator& -{ - BOOST_ASSERT( - off_ != pt_->offset[ - id_frag]); - off_ = off_ + n_; - if(off_ == pt_->offset[ - id_frag]) - { - // end - n_ = 0; - } - else - { - parse(); - } - return *this; -} - -auto -url_view:: -segments_type:: -iterator:: -operator--() noexcept -> - iterator& -{ - BOOST_ASSERT( - off_ != pt_->offset[ - id_path]); - auto const begin = - s_ + pt_->offset[ - id_path]; - auto p = s_ + off_; - while(--p > begin) - { - if(*p == '/') - { - off_ = p - s_; - parse(); - return *this; - } - } - // fails for relative-uri - //BOOST_ASSERT(*p == '/'); - auto const off = p - s_; - n_ = off_ - off; - off_ = off; - return *this; -} - -void -url_view:: -segments_type:: -iterator:: -parse() noexcept -{ - BOOST_ASSERT(off_ != - pt_->offset[ - id_frag]); - auto const end = - s_ + pt_->offset[ - id_frag]; - auto const p0 = s_ + off_; - auto p = p0; - if(*p == '/') - ++p; - while(p < end) - { - if(*p == '/') - break; - ++p; - } - n_ = p - p0; -} - -//---------------------------------------------------------- - -auto -url_view:: -segments_type:: -begin() const noexcept -> - iterator -{ - return iterator(this, false); -} - -auto -url_view:: -segments_type:: -end() const noexcept -> - iterator -{ - return iterator(this, true); -} - //------------------------------------------------ namespace detail { @@ -758,6 +603,7 @@ apply_path(parts& p, bnf::range< p.resize( part::id_path, t.str().size()); + p.nseg = t.size(); } static @@ -767,9 +613,12 @@ apply_query(parts& p, query_param>> const& t) { if(t.has_value()) + { p.resize( part::id_query, t->str().size() + 1); + p.nparam = t->size(); + } } static diff --git a/include/boost/url/path_view.hpp b/include/boost/url/path_view.hpp new file mode 100644 index 00000000..7dc5304d --- /dev/null +++ b/include/boost/url/path_view.hpp @@ -0,0 +1,187 @@ +// +// 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_PATH_VIEW_HPP +#define BOOST_URL_PATH_VIEW_HPP + +#include +#include +#include +#include +#include +#include + +namespace boost { +namespace urls { + +/** A ForwardRange view of read-only path segments +*/ +class path_view +{ + string_view s_; + std::size_t n_; + + friend class url; + friend class url_view; + + path_view( + string_view s, + std::size_t n) + : s_(s) + , n_(n) + { + } + +public: + class value_type; + class iterator; + + path_view( + path_view const&) = default; + path_view& operator=( + path_view const&) = default; + + BOOST_URL_DECL + path_view() noexcept; + + /** Return true if the range contains no elements + */ + bool + empty() const noexcept + { + return n_ == 0; + } + + /** Return the number of elements in the range + */ + std::size_t + size() const noexcept + { + return n_; + } + + /** Return an iterator to the beginning of the range + */ + BOOST_URL_DECL + iterator + begin() const noexcept; + + /** Return an iterator to the end of the range + */ + BOOST_URL_DECL + iterator + end() const noexcept; + + /** Parse the path string and return a view + */ + BOOST_URL_DECL + friend + path_view + parse_path( + string_view s, + error_code& ec); + + /** Parse the path string and return a view + */ + BOOST_URL_DECL + friend + path_view + parse_path( + string_view s); +}; + +BOOST_URL_DECL +path_view +parse_path( + string_view s, + error_code& ec); + +BOOST_URL_DECL +path_view +parse_path( + string_view s); + +//---------------------------------------------------------- + +/** The value type for query parameters +*/ +class path_view::value_type +{ + pct_encoded_str s_; + + friend class iterator; + friend class path_view; + +public: + value_type() = default; + value_type& operator=( + value_type const&) = default; + value_type( + value_type const&) = default; + + /** Return the segment + + This function returns the key as + a percent-encoded string. + + @see key + */ + string_view + encoded_segment() const noexcept + { + return s_.str; + } + + /** Return the segment + + This function returns the segment as a + string with percent-decoding applied. + + @par Exception Safety + + Strong guarantee. + Calls to allocate may throw. + + @param a An optional allocator the returned + string will use. If this parameter is omitted, + the default allocator is used, and the return + type of the function becomes `std::string`. + + @return A `std::basic_string` using the + specified allocator. + */ + template< + class Allocator = + std::allocator> + string_type + segment(Allocator const& a = {}) const + { + return pct_decode_unchecked( + s_.str, s_.decoded_size, a); + } + + value_type const* + operator->() const noexcept + { + return this; + } + + operator + std::string() const + { + return segment(); + } +}; + +} // urls +} // boost + +#include + +#endif diff --git a/include/boost/url/rfc/detail/paths.hpp b/include/boost/url/rfc/detail/paths_bnf.hpp similarity index 100% rename from include/boost/url/rfc/detail/paths.hpp rename to include/boost/url/rfc/detail/paths_bnf.hpp diff --git a/include/boost/url/rfc/impl/hier_part_bnf.ipp b/include/boost/url/rfc/impl/hier_part_bnf.ipp index 5842aad6..48cc55ea 100644 --- a/include/boost/url/rfc/impl/hier_part_bnf.ipp +++ b/include/boost/url/rfc/impl/hier_part_bnf.ipp @@ -12,7 +12,7 @@ #include #include -#include +#include namespace boost { namespace urls { diff --git a/include/boost/url/rfc/impl/relative_part_bnf.ipp b/include/boost/url/rfc/impl/relative_part_bnf.ipp index 9e1fc789..6d6e215b 100644 --- a/include/boost/url/rfc/impl/relative_part_bnf.ipp +++ b/include/boost/url/rfc/impl/relative_part_bnf.ipp @@ -12,7 +12,7 @@ #include #include -#include +#include namespace boost { namespace urls { diff --git a/include/boost/url/src.hpp b/include/boost/url/src.hpp index 4189f47a..e6a4400a 100644 --- a/include/boost/url/src.hpp +++ b/include/boost/url/src.hpp @@ -30,6 +30,7 @@ in a translation unit of the program. #include #include #include +#include #include #include #include diff --git a/include/boost/url/url.hpp b/include/boost/url/url.hpp index ab4b7c77..145c03e8 100644 --- a/include/boost/url/url.hpp +++ b/include/boost/url/url.hpp @@ -11,6 +11,7 @@ #define BOOST_URL_URL_HPP #include +#include #include #include #include @@ -44,8 +45,6 @@ namespace urls { */ class url { - friend class url_view::segments_type; - class modify; storage_ptr sp_; @@ -1142,9 +1141,9 @@ public: @see url_view::segments_type */ - inline - url_view::segments_type - segments() const noexcept; + BOOST_URL_DECL + path_view + path() const noexcept; /** Return the path. @@ -1159,9 +1158,9 @@ public: @see segments_type */ - inline + BOOST_URL_DECL segments_type - segments() noexcept; + path() noexcept; //------------------------------------------------------ // diff --git a/include/boost/url/url_view.hpp b/include/boost/url/url_view.hpp index ed09c00e..68faa97a 100644 --- a/include/boost/url/url_view.hpp +++ b/include/boost/url/url_view.hpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -65,8 +66,6 @@ class url_view std::size_t len(int id) const noexcept; public: - class segments_type; - /** Constructor */ // VFALCO DEPRECATED @@ -429,11 +428,11 @@ public: string_view encoded_path() const noexcept; - /** Return the path segments as a read-only range + /** Return the path segments as a read-only forward range */ - inline - segments_type - segments() const noexcept; + BOOST_URL_DECL + path_view + path() const noexcept; //------------------------------------------------------ // @@ -508,7 +507,7 @@ public: encoded_query(), a); } - /** Return the query parameters as a read-only container. + /** Return the query parameters as a read-only forward range */ BOOST_URL_DECL query_params_view @@ -665,179 +664,6 @@ url_view parse_relative_ref( string_view s); -//---------------------------------------------------------- - -/** A read-only view to the path segments. -*/ -class url_view::segments_type -{ - char const* s_ = nullptr; - detail::parts const* pt_ = nullptr; - -public: - class value_type; - class iterator; - using const_iterator = iterator; - - segments_type() = default; - segments_type(segments_type const&) = default; - segments_type& operator=( - segments_type const&) = default; - - explicit - segments_type(url_view const& v) noexcept - : s_(v.s_) - , pt_(&v.pt_) - { - } - - inline - explicit - segments_type( url const& v) noexcept; - - bool - empty() const noexcept - { - return size() == 0; - } - - std::size_t - size() const noexcept - { - return (pt_ == nullptr) ? 0 : - pt_->nseg; - } - - BOOST_URL_DECL - iterator - begin() const noexcept; - - BOOST_URL_DECL - iterator - end() const noexcept; -}; - -//---------------------------------------------------------- - -class url_view::segments_type::value_type -{ - string_view s_; - - friend class segments_type; - - explicit - value_type( - string_view s) noexcept - : s_(s) - { - } - -public: - value_type() = delete; - value_type& operator=( - value_type const&) = delete; - - value_type( - value_type const&) = default; - - string_view - encoded_string() const noexcept - { - return s_; - } - - template< - class Allocator = - std::allocator> - string_type - string(Allocator const& a = {}) const - { - return detail::decode( - encoded_string(), a); - } - - value_type const* - operator->() const noexcept - { - return this; - } -}; - -//---------------------------------------------------------- - -class url_view::segments_type::iterator -{ - friend segments_type; - - char const* s_; - detail::parts const* pt_; - std::size_t off_; - std::size_t n_; - - BOOST_URL_DECL - iterator( - segments_type const* v, - bool end) noexcept; - -public: - using value_type = - segments_type::value_type; - - BOOST_URL_DECL - iterator() noexcept; - - BOOST_URL_DECL - value_type - operator*() const noexcept; - - value_type - operator->() const noexcept - { - return operator*(); - } - - inline - bool - operator==( - iterator other) const noexcept; - - bool - operator!=( - iterator other) const noexcept - { - return !(*this == other); - } - - BOOST_URL_DECL - iterator& - operator++() noexcept; - - iterator - operator++(int) noexcept - { - auto tmp = *this; - ++*this; - return tmp; - } - - BOOST_URL_DECL - iterator& - operator--() noexcept; - - iterator - operator--(int) noexcept - { - auto tmp = *this; - --*this; - return tmp; - } - -private: - inline - void - parse() noexcept; -}; - } // urls } // boost diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 6d8c568e..2722ce47 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -8,7 +8,6 @@ # Official repository: https://github.com/CPPAlliance/url # - if(NOT TARGET tests) add_custom_target(tests) set_property(TARGET tests PROPERTY FOLDER _deps) @@ -27,6 +26,7 @@ set(BOOST_URL_TESTS_FILES ipv4_address.cpp ipv6_address.cpp optional.cpp + path_view.cpp query_params_view.cpp sandbox.cpp scheme.cpp diff --git a/test/path_view.cpp b/test/path_view.cpp new file mode 100644 index 00000000..76d7e372 --- /dev/null +++ b/test/path_view.cpp @@ -0,0 +1,62 @@ +// +// 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" +#include +#include + +namespace boost { +namespace urls { + +class path_view_test +{ +public: + void + testIterator() + { + BOOST_TEST( + path_view::iterator() == + path_view::iterator()); + path_view p; + BOOST_TEST(p.begin() != + path_view::iterator()); + BOOST_TEST(p.end() != + path_view::iterator()); + BOOST_TEST( + p.begin() == p.end()); + + auto it = p.begin(); + it = p.end(); + BOOST_TEST(it == p.begin()); + } + + void + testContents() + { + auto p = parse_path( + "/"); + } + + void + run() + { + testIterator(); + testContents(); + } +}; + +TEST_SUITE( + path_view_test, + "boost.url.path_view"); + +} // urls +} // boost diff --git a/test/url.cpp b/test/url.cpp index adcae4d4..2c107332 100644 --- a/test/url.cpp +++ b/test/url.cpp @@ -539,18 +539,16 @@ public: void testSegments() { - // segments() const + // path() const { url const v("/path/to/file.txt"); - auto ps = v.segments(); - static_assert( - std::is_same::value, ""); + auto ps = v.path(); + // ? } { url v("/path/to/file.txt"); - auto ps = v.segments(); + auto ps = v.path(); BOOST_TEST(! ps.empty()); BOOST_TEST(ps.size() == 3); BOOST_TEST(ps.begin() != ps.end()); @@ -578,7 +576,7 @@ public: } { url u("http://user:pass@example.com:80?k1=v1&k2=v2"); - auto ps = u.segments(); + auto ps = u.path(); BOOST_TEST(ps.empty()); ps.insert_encoded( ps.insert_encoded(