743 lines
13 KiB
C++
743 lines
13 KiB
C++
//
|
|
// Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot 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/boostorg/url
|
|
//
|
|
|
|
#ifndef BOOST_URL_GRAMMAR_IMPL_RANGE_HPP
|
|
#define BOOST_URL_GRAMMAR_IMPL_RANGE_HPP
|
|
|
|
#include <boost/url/detail/except.hpp>
|
|
#include <boost/url/grammar/error.hpp>
|
|
#include <boost/url/grammar/recycled.hpp>
|
|
#include <boost/core/empty_value.hpp>
|
|
#include <boost/assert.hpp>
|
|
#include <boost/static_assert.hpp>
|
|
#include <exception>
|
|
#include <iterator>
|
|
#include <new>
|
|
|
|
#include <stddef.h> // ::max_align_t
|
|
|
|
namespace boost {
|
|
namespace urls {
|
|
namespace grammar {
|
|
|
|
// VFALCO This could be reused for
|
|
// other things that need to type-erase
|
|
|
|
//------------------------------------------------
|
|
//
|
|
// any_rule
|
|
//
|
|
//------------------------------------------------
|
|
|
|
// base class for the type-erased rule pair
|
|
template<class T>
|
|
struct range<T>::
|
|
any_rule
|
|
{
|
|
virtual
|
|
~any_rule() = default;
|
|
|
|
virtual
|
|
void
|
|
move(void* dest) noexcept
|
|
{
|
|
::new(dest) any_rule(
|
|
std::move(*this));
|
|
}
|
|
|
|
virtual
|
|
void
|
|
copy(void* dest) const noexcept
|
|
{
|
|
::new(dest) any_rule(*this);
|
|
}
|
|
|
|
virtual
|
|
result<T>
|
|
first(
|
|
char const*&,
|
|
char const*) const noexcept
|
|
{
|
|
return error_code{};
|
|
}
|
|
|
|
virtual
|
|
result<T>
|
|
next(
|
|
char const*&,
|
|
char const*) const noexcept
|
|
{
|
|
return error_code{};
|
|
}
|
|
};
|
|
|
|
//------------------------------------------------
|
|
|
|
// small
|
|
template<class T>
|
|
template<class R, bool Small>
|
|
struct range<T>::impl1
|
|
: any_rule
|
|
, private empty_value<R>
|
|
{
|
|
explicit
|
|
impl1(R const& next) noexcept
|
|
: empty_value<R>(
|
|
empty_init,
|
|
next)
|
|
{
|
|
}
|
|
|
|
private:
|
|
impl1(impl1&&) noexcept = default;
|
|
impl1(impl1 const&) noexcept = default;
|
|
|
|
void
|
|
move(void* dest
|
|
) noexcept override
|
|
{
|
|
::new(dest) impl1(
|
|
std::move(*this));
|
|
}
|
|
|
|
void
|
|
copy(void* dest
|
|
) const noexcept override
|
|
{
|
|
::new(dest) impl1(*this);
|
|
}
|
|
|
|
result<T>
|
|
first(
|
|
char const*& it,
|
|
char const* end)
|
|
const noexcept override
|
|
{
|
|
return grammar::parse(
|
|
it, end, this->get());
|
|
}
|
|
|
|
result<T>
|
|
next(
|
|
char const*& it,
|
|
char const* end)
|
|
const noexcept override
|
|
{
|
|
return grammar::parse(
|
|
it, end, this->get());
|
|
}
|
|
};
|
|
|
|
//------------------------------------------------
|
|
|
|
// big
|
|
template<class T>
|
|
template<class R>
|
|
struct range<T>::impl1<R, false>
|
|
: any_rule
|
|
{
|
|
explicit
|
|
impl1(R const& next) noexcept
|
|
{
|
|
::new(p_->addr()) impl{next};
|
|
}
|
|
|
|
private:
|
|
struct impl
|
|
{
|
|
R r;
|
|
};
|
|
|
|
recycled_ptr<
|
|
aligned_storage<impl>> p_;
|
|
|
|
impl1(impl1&&) noexcept = default;
|
|
impl1(impl1 const&) noexcept = default;
|
|
|
|
impl const&
|
|
get() const noexcept
|
|
{
|
|
return *reinterpret_cast<
|
|
impl const*>(p_->addr());
|
|
}
|
|
|
|
~impl1()
|
|
{
|
|
if(p_)
|
|
get().~impl();
|
|
}
|
|
|
|
void
|
|
move(void* dest
|
|
) noexcept override
|
|
{
|
|
::new(dest) impl1(
|
|
std::move(*this));
|
|
}
|
|
|
|
void
|
|
copy(void* dest
|
|
) const noexcept override
|
|
{
|
|
::new(dest) impl1(*this);
|
|
}
|
|
|
|
result<T>
|
|
first(
|
|
char const*& it,
|
|
char const* end)
|
|
const noexcept override
|
|
{
|
|
return grammar::parse(
|
|
it, end, this->get().r);
|
|
}
|
|
|
|
result<T>
|
|
next(
|
|
char const*& it,
|
|
char const* end)
|
|
const noexcept override
|
|
{
|
|
return grammar::parse(
|
|
it, end, this->get().r);
|
|
}
|
|
};
|
|
|
|
//------------------------------------------------
|
|
|
|
// small
|
|
template<class T>
|
|
template<
|
|
class R0, class R1, bool Small>
|
|
struct range<T>::impl2
|
|
: any_rule
|
|
, private empty_value<R0, 0>
|
|
, private empty_value<R1, 1>
|
|
{
|
|
impl2(
|
|
R0 const& first,
|
|
R1 const& next) noexcept
|
|
: empty_value<R0,0>(
|
|
empty_init, first)
|
|
, empty_value<R1,1>(
|
|
empty_init, next)
|
|
{
|
|
}
|
|
|
|
private:
|
|
impl2(impl2&&) noexcept = default;
|
|
impl2(impl2 const&) noexcept = default;
|
|
|
|
void
|
|
move(void* dest
|
|
) noexcept override
|
|
{
|
|
::new(dest) impl2(
|
|
std::move(*this));
|
|
}
|
|
|
|
void
|
|
copy(void* dest
|
|
) const noexcept override
|
|
{
|
|
::new(dest) impl2(*this);
|
|
}
|
|
|
|
result<T>
|
|
first(
|
|
char const*& it,
|
|
char const* end)
|
|
const noexcept override
|
|
{
|
|
return grammar::parse(it, end,
|
|
empty_value<
|
|
R0,0>::get());
|
|
}
|
|
|
|
result<T>
|
|
next(
|
|
char const*& it,
|
|
char const* end)
|
|
const noexcept override
|
|
{
|
|
return grammar::parse(it, end,
|
|
empty_value<
|
|
R1,1>::get());
|
|
}
|
|
};
|
|
|
|
//------------------------------------------------
|
|
|
|
// big
|
|
template<class T>
|
|
template<
|
|
class R0, class R1>
|
|
struct range<T>::impl2<R0, R1, false>
|
|
: any_rule
|
|
{
|
|
impl2(
|
|
R0 const& first,
|
|
R1 const& next) noexcept
|
|
{
|
|
::new(p_->addr()) impl{
|
|
first, next};
|
|
}
|
|
|
|
private:
|
|
struct impl
|
|
{
|
|
R0 first;
|
|
R1 next;
|
|
};
|
|
|
|
recycled_ptr<
|
|
aligned_storage<impl>> p_;
|
|
|
|
impl2(impl2&&) noexcept = default;
|
|
impl2(impl2 const&) noexcept = default;
|
|
|
|
impl const&
|
|
get() const noexcept
|
|
{
|
|
return *reinterpret_cast<
|
|
impl const*>(p_->addr());
|
|
}
|
|
|
|
~impl2()
|
|
{
|
|
if(p_)
|
|
get().~impl();
|
|
}
|
|
|
|
void
|
|
move(void* dest
|
|
) noexcept override
|
|
{
|
|
::new(dest) impl2(
|
|
std::move(*this));
|
|
}
|
|
|
|
void
|
|
copy(void* dest
|
|
) const noexcept override
|
|
{
|
|
::new(dest) impl2(*this);
|
|
}
|
|
|
|
result<T>
|
|
first(
|
|
char const*& it,
|
|
char const* end)
|
|
const noexcept override
|
|
{
|
|
return grammar::parse(
|
|
it, end, get().first);
|
|
}
|
|
|
|
result<T>
|
|
next(
|
|
char const*& it,
|
|
char const* end)
|
|
const noexcept override
|
|
{
|
|
return grammar::parse(
|
|
it, end, get().next);
|
|
}
|
|
};
|
|
|
|
//------------------------------------------------
|
|
//
|
|
// iterator
|
|
//
|
|
//------------------------------------------------
|
|
|
|
template<class T>
|
|
class range<T>::
|
|
iterator
|
|
{
|
|
public:
|
|
using value_type = T;
|
|
using reference = T const&;
|
|
using pointer = void const*;
|
|
using difference_type =
|
|
std::ptrdiff_t;
|
|
using iterator_category =
|
|
std::forward_iterator_tag;
|
|
|
|
iterator() = default;
|
|
iterator(
|
|
iterator const&) = default;
|
|
iterator& operator=(
|
|
iterator const&) = default;
|
|
|
|
reference
|
|
operator*() const noexcept
|
|
{
|
|
return *rv_;
|
|
}
|
|
|
|
bool
|
|
operator==(
|
|
iterator const& other) const noexcept
|
|
{
|
|
// can't compare iterators
|
|
// from different containers!
|
|
BOOST_ASSERT(r_ == other.r_);
|
|
|
|
return p_ == other.p_;
|
|
}
|
|
|
|
bool
|
|
operator!=(
|
|
iterator const& other) const noexcept
|
|
{
|
|
return !(*this == other);
|
|
}
|
|
|
|
iterator&
|
|
operator++() noexcept
|
|
{
|
|
BOOST_ASSERT(
|
|
p_ != nullptr);
|
|
auto const end =
|
|
r_->s_.data() +
|
|
r_->s_.size();
|
|
rv_ = r_->get().next(p_, end);
|
|
if( !rv_ )
|
|
p_ = nullptr;
|
|
return *this;
|
|
}
|
|
|
|
iterator
|
|
operator++(int) noexcept
|
|
{
|
|
auto tmp = *this;
|
|
++*this;
|
|
return tmp;
|
|
}
|
|
|
|
private:
|
|
friend class range<T>;
|
|
|
|
range<T> const* r_ = nullptr;
|
|
char const* p_ = nullptr;
|
|
result<T> rv_;
|
|
|
|
iterator(
|
|
range<T> const& r) noexcept
|
|
: r_(&r)
|
|
, p_(r.s_.data())
|
|
{
|
|
auto const end =
|
|
r_->s_.data() +
|
|
r_->s_.size();
|
|
rv_ = r_->get().first(p_, end);
|
|
if( !rv_ )
|
|
p_ = nullptr;
|
|
}
|
|
|
|
constexpr
|
|
iterator(
|
|
range<T> const& r,
|
|
int) noexcept
|
|
: r_(&r)
|
|
, p_(nullptr)
|
|
{
|
|
}
|
|
};
|
|
|
|
//------------------------------------------------
|
|
|
|
template<class T>
|
|
template<class R>
|
|
range<T>::
|
|
range(
|
|
string_view s,
|
|
std::size_t n,
|
|
R const& next)
|
|
: s_(s)
|
|
, n_(n)
|
|
{
|
|
BOOST_STATIC_ASSERT(
|
|
sizeof(impl1<R, false>) <=
|
|
BufferSize);
|
|
|
|
::new(&get()) impl1<R,
|
|
sizeof(impl1<R, true>) <=
|
|
BufferSize>(next);
|
|
}
|
|
|
|
//------------------------------------------------
|
|
|
|
template<class T>
|
|
template<
|
|
class R0, class R1>
|
|
range<T>::
|
|
range(
|
|
string_view s,
|
|
std::size_t n,
|
|
R0 const& first,
|
|
R1 const& next)
|
|
: s_(s)
|
|
, n_(n)
|
|
{
|
|
BOOST_STATIC_ASSERT(
|
|
sizeof(impl2<R0, R1, false>) <=
|
|
BufferSize);
|
|
|
|
::new(&get()) impl2<R0, R1,
|
|
sizeof(impl2<R0, R1, true>
|
|
) <= BufferSize>(
|
|
first, next);
|
|
}
|
|
|
|
//------------------------------------------------
|
|
|
|
template<class T>
|
|
range<T>::
|
|
~range()
|
|
{
|
|
get().~any_rule();
|
|
}
|
|
|
|
template<class T>
|
|
range<T>::
|
|
range() noexcept
|
|
{
|
|
::new(&get()) any_rule{};
|
|
char const* it = nullptr;
|
|
get().first(it, nullptr);
|
|
get().next(it, nullptr);
|
|
}
|
|
|
|
template<class T>
|
|
range<T>::
|
|
range(
|
|
range&& other) noexcept
|
|
: s_(other.s_)
|
|
, n_(other.n_)
|
|
{
|
|
other.s_ = {};
|
|
other.n_ = {};
|
|
other.get().move(&get());
|
|
other.get().~any_rule();
|
|
::new(&other.get()) any_rule{};
|
|
}
|
|
|
|
template<class T>
|
|
range<T>::
|
|
range(
|
|
range const& other) noexcept
|
|
: s_(other.s_)
|
|
, n_(other.n_)
|
|
{
|
|
other.get().copy(&get());
|
|
}
|
|
|
|
template<class T>
|
|
auto
|
|
range<T>::
|
|
operator=(
|
|
range&& other) noexcept ->
|
|
range&
|
|
{
|
|
s_ = other.s_;
|
|
n_ = other.n_;
|
|
other.s_ = {};
|
|
other.n_ = 0;
|
|
// VFALCO we rely on nothrow move
|
|
// construction here, but if necessary we
|
|
// could move to a local buffer first.
|
|
get().~any_rule();
|
|
other.get().move(&get());
|
|
other.get().~any_rule();
|
|
::new(&other.get()) any_rule{};
|
|
return *this;
|
|
}
|
|
|
|
template<class T>
|
|
auto
|
|
range<T>::
|
|
operator=(
|
|
range const& other) noexcept ->
|
|
range&
|
|
{
|
|
s_ = other.s_;
|
|
n_ = other.n_;
|
|
// VFALCO we rely on nothrow copy
|
|
// construction here, but if necessary we
|
|
// could construct to a local buffer first.
|
|
get().~any_rule();
|
|
other.get().copy(&get());
|
|
return *this;
|
|
}
|
|
|
|
template<class T>
|
|
auto
|
|
range<T>::
|
|
begin() const noexcept ->
|
|
iterator
|
|
{
|
|
return { *this };
|
|
}
|
|
|
|
template<class T>
|
|
auto
|
|
range<T>::
|
|
end() const noexcept ->
|
|
iterator
|
|
{
|
|
return { *this, 0 };
|
|
}
|
|
|
|
//------------------------------------------------
|
|
|
|
template<class R>
|
|
auto
|
|
range_rule_t<R>::
|
|
parse(
|
|
char const*& it,
|
|
char const* end) const ->
|
|
result<value_type>
|
|
{
|
|
using T = typename R::value_type;
|
|
|
|
std::size_t n = 0;
|
|
auto const it0 = it;
|
|
auto it1 = it;
|
|
auto rv = (grammar::parse)(
|
|
it, end, next_);
|
|
if( !rv )
|
|
{
|
|
if(rv.error() != error::end_of_range)
|
|
{
|
|
// rewind unless error::end_of_range
|
|
it = it1;
|
|
}
|
|
if(n < N_)
|
|
{
|
|
// too few
|
|
BOOST_URL_RETURN_EC(
|
|
error::mismatch);
|
|
}
|
|
// good
|
|
return range<T>(
|
|
string_view(it0, it - it0),
|
|
n, next_);
|
|
}
|
|
for(;;)
|
|
{
|
|
++n;
|
|
it1 = it;
|
|
rv = (grammar::parse)(
|
|
it, end, next_);
|
|
if( !rv )
|
|
{
|
|
if(rv.error() != error::end_of_range)
|
|
{
|
|
// rewind unless error::end_of_range
|
|
it = it1;
|
|
}
|
|
break;
|
|
}
|
|
if(n >= M_)
|
|
{
|
|
// too many
|
|
BOOST_URL_RETURN_EC(
|
|
error::mismatch);
|
|
}
|
|
}
|
|
if(n < N_)
|
|
{
|
|
// too few
|
|
BOOST_URL_RETURN_EC(
|
|
error::mismatch);
|
|
}
|
|
// good
|
|
return range<T>(
|
|
string_view(it0, it - it0),
|
|
n, next_);
|
|
}
|
|
|
|
//------------------------------------------------
|
|
|
|
template<class R0, class R1>
|
|
auto
|
|
range_rule_t<R0, R1>::
|
|
parse(
|
|
char const*& it,
|
|
char const* end) const ->
|
|
result<range<typename
|
|
R0::value_type>>
|
|
{
|
|
using T = typename R0::value_type;
|
|
|
|
std::size_t n = 0;
|
|
auto const it0 = it;
|
|
auto it1 = it;
|
|
auto rv = (grammar::parse)(
|
|
it, end, first_);
|
|
if( !rv )
|
|
{
|
|
if(rv.error() != error::end_of_range)
|
|
{
|
|
// rewind unless error::end_of_range
|
|
it = it1;
|
|
}
|
|
if(n < N_)
|
|
{
|
|
// too few
|
|
BOOST_URL_RETURN_EC(
|
|
error::mismatch);
|
|
}
|
|
// good
|
|
return range<T>(
|
|
string_view(it0, it - it0),
|
|
n, first_, next_);
|
|
}
|
|
for(;;)
|
|
{
|
|
++n;
|
|
it1 = it;
|
|
rv = (grammar::parse)(
|
|
it, end, next_);
|
|
if( !rv )
|
|
{
|
|
if(rv.error() != error::end_of_range)
|
|
{
|
|
// rewind unless error::end_of_range
|
|
it = it1;
|
|
}
|
|
break;
|
|
}
|
|
if(n >= M_)
|
|
{
|
|
// too many
|
|
BOOST_URL_RETURN_EC(
|
|
error::mismatch);
|
|
}
|
|
}
|
|
if(n < N_)
|
|
{
|
|
// too few
|
|
BOOST_URL_RETURN_EC(
|
|
error::mismatch);
|
|
}
|
|
// good
|
|
return range<T>(
|
|
string_view(it0, it - it0),
|
|
n, first_, next_);
|
|
}
|
|
|
|
} // grammar
|
|
} // urls
|
|
} // boost
|
|
|
|
#endif
|