ext-boost/boost/url/grammar/impl/range_rule.hpp
2023-05-22 18:45:02 +10:00

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