400 lines
11 KiB
C++
400 lines
11 KiB
C++
/*
|
|
Copyright 2019 Glen Joseph Fernandes
|
|
(glenjofe@gmail.com)
|
|
|
|
Distributed under the Boost Software License, Version 1.0.
|
|
(http://www.boost.org/LICENSE_1_0.txt)
|
|
*/
|
|
#ifndef BOOST_CORE_SPAN_HPP
|
|
#define BOOST_CORE_SPAN_HPP
|
|
|
|
#include <array>
|
|
#include <iterator>
|
|
#include <type_traits>
|
|
#include <cstddef>
|
|
|
|
namespace boost {
|
|
|
|
constexpr std::size_t dynamic_extent = static_cast<std::size_t>(-1);
|
|
|
|
template<class T, std::size_t E = dynamic_extent>
|
|
class span;
|
|
|
|
namespace detail {
|
|
|
|
template<class U, class T>
|
|
struct span_convertible {
|
|
static constexpr bool value = std::is_convertible<U(*)[], T(*)[]>::value;
|
|
};
|
|
|
|
template<std::size_t E, std::size_t N>
|
|
struct span_capacity {
|
|
static constexpr bool value = E == boost::dynamic_extent || E == N;
|
|
};
|
|
|
|
template<class T, std::size_t E, class U, std::size_t N>
|
|
struct span_compatible {
|
|
static constexpr bool value = span_capacity<E, N>::value &&
|
|
span_convertible<U, T>::value;
|
|
};
|
|
|
|
template<class T>
|
|
struct span_uncvref {
|
|
typedef typename std::remove_cv<typename
|
|
std::remove_reference<T>::type>::type type;
|
|
};
|
|
|
|
template<class>
|
|
struct span_is_span {
|
|
static constexpr bool value = false;
|
|
};
|
|
|
|
template<class T, std::size_t E>
|
|
struct span_is_span<boost::span<T, E> > {
|
|
static constexpr bool value = true;
|
|
};
|
|
|
|
template<class T>
|
|
struct span_is_array {
|
|
static constexpr bool value = false;
|
|
};
|
|
|
|
template<class T, std::size_t N>
|
|
struct span_is_array<std::array<T, N> > {
|
|
static constexpr bool value = true;
|
|
};
|
|
|
|
template<class, class = void>
|
|
struct span_data { };
|
|
|
|
template<class T>
|
|
struct span_data<T,
|
|
typename std::enable_if<std::is_pointer<decltype(std::declval<T
|
|
&>().data())>::value>::type> {
|
|
typedef typename std::remove_pointer<decltype(std::declval<T
|
|
&>().data())>::type type;
|
|
};
|
|
|
|
template<class, class, class = void>
|
|
struct span_has_data {
|
|
static constexpr bool value = false;
|
|
};
|
|
|
|
template<class R, class T>
|
|
struct span_has_data<R, T, typename std::enable_if<span_convertible<typename
|
|
span_data<R>::type, T>::value>::type> {
|
|
static constexpr bool value = true;
|
|
};
|
|
|
|
template<class, class = void>
|
|
struct span_has_size {
|
|
static constexpr bool value = false;
|
|
};
|
|
|
|
template<class R>
|
|
struct span_has_size<R, typename
|
|
std::enable_if<std::is_convertible<decltype(std::declval<R&>().size()),
|
|
std::size_t>::value>::type> {
|
|
static constexpr bool value = true;
|
|
};
|
|
|
|
template<class R, class T>
|
|
struct span_is_range {
|
|
static constexpr bool value = (std::is_const<T>::value ||
|
|
std::is_lvalue_reference<R>::value) &&
|
|
!span_is_span<typename span_uncvref<R>::type>::value &&
|
|
!span_is_array<typename span_uncvref<R>::type>::value &&
|
|
!std::is_array<typename span_uncvref<R>::type>::value &&
|
|
span_has_data<R, T>::value &&
|
|
span_has_size<R>::value;
|
|
};
|
|
|
|
template<std::size_t E, std::size_t N>
|
|
struct span_implicit {
|
|
static constexpr bool value = E == boost::dynamic_extent ||
|
|
N != boost::dynamic_extent;
|
|
};
|
|
|
|
template<class T, std::size_t E, class U, std::size_t N>
|
|
struct span_copyable {
|
|
static constexpr bool value = (N == boost::dynamic_extent ||
|
|
span_capacity<E, N>::value) && span_convertible<U, T>::value;
|
|
};
|
|
|
|
template<std::size_t E, std::size_t O>
|
|
struct span_sub {
|
|
static constexpr std::size_t value = E == boost::dynamic_extent ?
|
|
boost::dynamic_extent : E - O;
|
|
};
|
|
|
|
template<class T, std::size_t E>
|
|
struct span_store {
|
|
constexpr span_store(T* p_, std::size_t) noexcept
|
|
: p(p_) { }
|
|
static constexpr std::size_t n = E;
|
|
T* p;
|
|
};
|
|
|
|
template<class T>
|
|
struct span_store<T, boost::dynamic_extent> {
|
|
constexpr span_store(T* p_, std::size_t n_) noexcept
|
|
: p(p_)
|
|
, n(n_) { }
|
|
T* p;
|
|
std::size_t n;
|
|
};
|
|
|
|
template<class T, std::size_t E>
|
|
struct span_bytes {
|
|
static constexpr std::size_t value = sizeof(T) * E;
|
|
};
|
|
|
|
template<class T>
|
|
struct span_bytes<T, boost::dynamic_extent> {
|
|
static constexpr std::size_t value = boost::dynamic_extent;
|
|
};
|
|
|
|
} /* detail */
|
|
|
|
template<class T, std::size_t E>
|
|
class span {
|
|
public:
|
|
typedef T element_type;
|
|
typedef typename std::remove_cv<T>::type value_type;
|
|
typedef std::size_t size_type;
|
|
typedef std::ptrdiff_t difference_type;
|
|
typedef T* pointer;
|
|
typedef const T* const_pointer;
|
|
typedef T& reference;
|
|
typedef const T& const_reference;
|
|
typedef T* iterator;
|
|
typedef const T* const_iterator;
|
|
typedef std::reverse_iterator<T*> reverse_iterator;
|
|
typedef std::reverse_iterator<const T*> const_reverse_iterator;
|
|
|
|
static constexpr std::size_t extent = E;
|
|
|
|
template<std::size_t N = E,
|
|
typename std::enable_if<N == dynamic_extent || N == 0, int>::type = 0>
|
|
constexpr span() noexcept
|
|
: s_(0, 0) { }
|
|
|
|
template<class I,
|
|
typename std::enable_if<E == dynamic_extent &&
|
|
detail::span_convertible<I, T>::value, int>::type = 0>
|
|
constexpr span(I* f, size_type c)
|
|
: s_(f, c) { }
|
|
|
|
template<class I,
|
|
typename std::enable_if<E != dynamic_extent &&
|
|
detail::span_convertible<I, T>::value, int>::type = 0>
|
|
explicit constexpr span(I* f, size_type c)
|
|
: s_(f, c) { }
|
|
|
|
template<class I, class L,
|
|
typename std::enable_if<E == dynamic_extent &&
|
|
detail::span_convertible<I, T>::value, int>::type = 0>
|
|
constexpr span(I* f, L* l)
|
|
: s_(f, l - f) { }
|
|
|
|
template<class I, class L,
|
|
typename std::enable_if<E != dynamic_extent &&
|
|
detail::span_convertible<I, T>::value, int>::type = 0>
|
|
explicit constexpr span(I* f, L* l)
|
|
: s_(f, l - f) { }
|
|
|
|
template<std::size_t N,
|
|
typename std::enable_if<detail::span_capacity<E, N>::value,
|
|
int>::type = 0>
|
|
constexpr span(typename std::enable_if<true, T>::type (&a)[N]) noexcept
|
|
: s_(a, N) { }
|
|
|
|
template<class U, std::size_t N,
|
|
typename std::enable_if<detail::span_compatible<T, E, U, N>::value,
|
|
int>::type = 0>
|
|
constexpr span(std::array<U, N>& a) noexcept
|
|
: s_(a.data(), N) { }
|
|
|
|
template<class U, std::size_t N,
|
|
typename std::enable_if<detail::span_compatible<T, E, const U,
|
|
N>::value, int>::type = 0>
|
|
constexpr span(const std::array<U, N>& a) noexcept
|
|
: s_(a.data(), N) { }
|
|
|
|
template<class R,
|
|
typename std::enable_if<E == dynamic_extent &&
|
|
detail::span_is_range<R, T>::value, int>::type = 0>
|
|
constexpr span(R&& r) noexcept(noexcept(r.data()) && noexcept(r.size()))
|
|
: s_(r.data(), r.size()) { }
|
|
|
|
template<class R,
|
|
typename std::enable_if<E != dynamic_extent &&
|
|
detail::span_is_range<R, T>::value, int>::type = 0>
|
|
explicit constexpr span(R&& r) noexcept(noexcept(r.data()) &&
|
|
noexcept(r.size()))
|
|
: s_(r.data(), r.size()) { }
|
|
|
|
template<class U, std::size_t N,
|
|
typename std::enable_if<detail::span_implicit<E, N>::value &&
|
|
detail::span_copyable<T, E, U, N>::value, int>::type = 0>
|
|
constexpr span(const span<U, N>& s) noexcept
|
|
: s_(s.data(), s.size()) { }
|
|
|
|
template<class U, std::size_t N,
|
|
typename std::enable_if<!detail::span_implicit<E, N>::value &&
|
|
detail::span_copyable<T, E, U, N>::value, int>::type = 0>
|
|
explicit constexpr span(const span<U, N>& s) noexcept
|
|
: s_(s.data(), s.size()) { }
|
|
|
|
template<std::size_t C>
|
|
constexpr span<T, C> first() const {
|
|
static_assert(C <= E, "Count <= Extent");
|
|
return span<T, C>(s_.p, C);
|
|
}
|
|
|
|
template<std::size_t C>
|
|
constexpr span<T, C> last() const {
|
|
static_assert(C <= E, "Count <= Extent");
|
|
return span<T, C>(s_.p + (s_.n - C), C);
|
|
}
|
|
|
|
template<std::size_t O, std::size_t C = dynamic_extent>
|
|
constexpr typename std::enable_if<C == dynamic_extent,
|
|
span<T, detail::span_sub<E, O>::value> >::type subspan() const {
|
|
static_assert(O <= E, "Offset <= Extent");
|
|
return span<T, detail::span_sub<E, O>::value>(s_.p + O, s_.n - O);
|
|
}
|
|
|
|
template<std::size_t O, std::size_t C = dynamic_extent>
|
|
constexpr typename std::enable_if<C != dynamic_extent,
|
|
span<T, C> >::type subspan() const {
|
|
static_assert(O <= E && C <= E - O,
|
|
"Offset <= Extent && Count <= Extent - Offset");
|
|
return span<T, C>(s_.p + O, C);
|
|
}
|
|
|
|
constexpr span<T, dynamic_extent> first(size_type c) const {
|
|
return span<T, dynamic_extent>(s_.p, c);
|
|
}
|
|
|
|
constexpr span<T, dynamic_extent> last(size_type c) const {
|
|
return span<T, dynamic_extent>(s_.p + (s_.n - c), c);
|
|
}
|
|
|
|
constexpr span<T, dynamic_extent> subspan(size_type o,
|
|
size_type c = dynamic_extent) const {
|
|
return span<T, dynamic_extent>(s_.p + o,
|
|
c == dynamic_extent ? s_.n - o : c);
|
|
}
|
|
|
|
constexpr size_type size() const noexcept {
|
|
return s_.n;
|
|
}
|
|
|
|
constexpr size_type size_bytes() const noexcept {
|
|
return s_.n * sizeof(T);
|
|
}
|
|
|
|
constexpr bool empty() const noexcept {
|
|
return s_.n == 0;
|
|
}
|
|
|
|
constexpr reference operator[](size_type i) const {
|
|
return s_.p[i];
|
|
}
|
|
|
|
constexpr reference front() const {
|
|
return *s_.p;
|
|
}
|
|
|
|
constexpr reference back() const {
|
|
return s_.p[s_.n - 1];
|
|
}
|
|
|
|
constexpr pointer data() const noexcept {
|
|
return s_.p;
|
|
}
|
|
|
|
constexpr iterator begin() const noexcept {
|
|
return s_.p;
|
|
}
|
|
|
|
constexpr iterator end() const noexcept {
|
|
return s_.p + s_.n;
|
|
}
|
|
|
|
constexpr reverse_iterator rbegin() const noexcept {
|
|
return reverse_iterator(s_.p + s_.n);
|
|
}
|
|
|
|
constexpr reverse_iterator rend() const noexcept {
|
|
return reverse_iterator(s_.p);
|
|
}
|
|
|
|
constexpr const_iterator cbegin() const noexcept {
|
|
return s_.p;
|
|
}
|
|
|
|
constexpr const_iterator cend() const noexcept {
|
|
return s_.p + s_.n;
|
|
}
|
|
|
|
constexpr const_reverse_iterator crbegin() const noexcept {
|
|
return const_reverse_iterator(s_.p + s_.n);
|
|
}
|
|
|
|
constexpr const_reverse_iterator crend() const noexcept {
|
|
return const_reverse_iterator(s_.p);
|
|
}
|
|
|
|
private:
|
|
detail::span_store<T, E> s_;
|
|
};
|
|
|
|
template<class T, std::size_t E>
|
|
constexpr std::size_t span<T, E>::extent;
|
|
|
|
#ifdef __cpp_deduction_guides
|
|
template<class I, class L>
|
|
span(I*, L) -> span<I>;
|
|
|
|
template<class T, std::size_t N>
|
|
span(T(&)[N]) -> span<T, N>;
|
|
|
|
template<class T, std::size_t N>
|
|
span(std::array<T, N>&) -> span<T, N>;
|
|
|
|
template<class T, std::size_t N>
|
|
span(const std::array<T, N>&) -> span<const T, N>;
|
|
|
|
template<class R>
|
|
span(R&&) -> span<typename detail::span_data<R>::type>;
|
|
|
|
template<class T, std::size_t E>
|
|
span(span<T, E>) -> span<T, E>;
|
|
#endif
|
|
|
|
#ifdef __cpp_lib_byte
|
|
template<class T, std::size_t E>
|
|
inline span<const std::byte, detail::span_bytes<T, E>::value>
|
|
as_bytes(span<T, E> s) noexcept
|
|
{
|
|
return span<const std::byte, detail::span_bytes<T,
|
|
E>::value>(reinterpret_cast<const std::byte*>(s.data()),
|
|
s.size_bytes());
|
|
}
|
|
|
|
template<class T, std::size_t E>
|
|
inline typename std::enable_if<!std::is_const<T>::value,
|
|
span<std::byte, detail::span_bytes<T, E>::value> >::type
|
|
as_writable_bytes(span<T, E> s) noexcept
|
|
{
|
|
return span<std::byte, detail::span_bytes<T,
|
|
E>::value>(reinterpret_cast<std::byte*>(s.data()), s.size_bytes());
|
|
}
|
|
#endif
|
|
|
|
} /* boost */
|
|
|
|
#endif
|