2
0
mirror of https://github.com/boostorg/url.git synced 2026-01-31 20:52:13 +00:00

path_view

This commit is contained in:
Vinnie Falco
2021-09-07 13:52:03 -07:00
parent d16e39bc44
commit aafe6d2d44
18 changed files with 520 additions and 415 deletions

View File

@@ -15,6 +15,7 @@
#include <boost/url/ipv4_address.hpp>
#include <boost/url/ipv6_address.hpp>
#include <boost/url/optional.hpp>
#include <boost/url/path_view.hpp>
#include <boost/url/query_params_view.hpp>
#include <boost/url/scheme.hpp>
#include <boost/url/static_pool.hpp>

View File

@@ -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 <boost/url/detail/except.hpp>
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

View File

@@ -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 <boost/url/path_view.hpp>
#include <boost/url/error.hpp>
#include <boost/url/rfc/query_bnf.hpp>
#include <boost/url/rfc/detail/paths_bnf.hpp>
#include <boost/url/detail/except.hpp>
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

View File

@@ -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;
}

View File

@@ -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<class Allocator>

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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 <boost/url/detail/config.hpp>
#include <boost/url/string.hpp>
#include <boost/url/detail/char_type.hpp>
#include <boost/url/rfc/pct_encoded_bnf.hpp>
#include <boost/url/rfc/pct_encoding.hpp>
#include <utility>
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<char>>
string_type<Allocator>
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 <boost/url/impl/path_view.hpp>
#endif

View File

@@ -12,7 +12,7 @@
#include <boost/url/rfc/hier_part_bnf.hpp>
#include <boost/url/bnf/parse.hpp>
#include <boost/url/rfc/detail/paths.hpp>
#include <boost/url/rfc/detail/paths_bnf.hpp>
namespace boost {
namespace urls {

View File

@@ -12,7 +12,7 @@
#include <boost/url/rfc/relative_part_bnf.hpp>
#include <boost/url/bnf/parse.hpp>
#include <boost/url/rfc/detail/paths.hpp>
#include <boost/url/rfc/detail/paths_bnf.hpp>
namespace boost {
namespace urls {

View File

@@ -30,6 +30,7 @@ in a translation unit of the program.
#include <boost/url/impl/error.ipp>
#include <boost/url/impl/ipv4_address.ipp>
#include <boost/url/impl/ipv6_address.ipp>
#include <boost/url/impl/path_view.ipp>
#include <boost/url/impl/query_params_view.ipp>
#include <boost/url/impl/scheme.ipp>
#include <boost/url/impl/static_pool.ipp>

View File

@@ -11,6 +11,7 @@
#define BOOST_URL_URL_HPP
#include <boost/url/detail/config.hpp>
#include <boost/url/path_view.hpp>
#include <boost/url/query_params_view.hpp>
#include <boost/url/url_view.hpp>
#include <boost/url/detail/char_type.hpp>
@@ -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;
//------------------------------------------------------
//

View File

@@ -15,6 +15,7 @@
#include <boost/url/ipv6_address.hpp>
#include <boost/url/query_params_view.hpp>
#include <boost/url/optional.hpp>
#include <boost/url/path_view.hpp>
#include <boost/url/detail/parts.hpp>
#include <boost/url/detail/char_type.hpp>
#include <cstdint>
@@ -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<char>>
string_type<Allocator>
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

View File

@@ -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

62
test/path_view.cpp Normal file
View File

@@ -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 <boost/url/path_view.hpp>
#include "test_suite.hpp"
#include <map>
#include <utility>
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

View File

@@ -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<decltype(ps),
url_view::segments_type>::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(