#ifndef BASE_CONTAINERS_SPAN_H_
#define BASE_CONTAINERS_SPAN_H_
#include <stddef.h>
#include <stdint.h>
#include <array>
#include <iterator>
#include <limits>
#include <type_traits>
#include <utility>
#include "base/check.h"
#include "base/compiler_specific.h"
#include "base/containers/checked_iterators.h"
#include "base/containers/contiguous_iterator.h"
#include "base/cxx20_to_address.h"
#include "base/numerics/safe_math.h"
namespace base {
constexpr size_t dynamic_extent = std::numeric_limits<size_t>::max();
template <typename T, size_t Extent = dynamic_extent>
class span;
namespace internal {
template <size_t I>
using size_constant = std::integral_constant<size_t, I>;
template <typename T>
struct ExtentImpl : size_constant<dynamic_extent> {};
template <typename T, size_t N>
struct ExtentImpl<T[N]> : size_constant<N> {};
template <typename T, size_t N>
struct ExtentImpl<std::array<T, N>> : size_constant<N> {};
template <typename T, size_t N>
struct ExtentImpl<base::span<T, N>> : size_constant<N> {};
template <typename T>
using Extent = ExtentImpl<remove_cvref_t<T>>;
template <typename T>
struct IsSpanImpl : std::false_type {};
template <typename T, size_t Extent>
struct IsSpanImpl<span<T, Extent>> : std::true_type {};
template <typename T>
using IsNotSpan = std::negation<IsSpanImpl<std::decay_t<T>>>;
template <typename T>
struct IsStdArrayImpl : std::false_type {};
template <typename T, size_t N>
struct IsStdArrayImpl<std::array<T, N>> : std::true_type {};
template <typename T>
using IsNotStdArray = std::negation<IsStdArrayImpl<std::decay_t<T>>>;
template <typename T>
using IsNotCArray = std::negation<std::is_array<std::remove_reference_t<T>>>;
template <typename From, typename To>
using IsLegalDataConversion = std::is_convertible<From (*)[], To (*)[]>;
template <typename Iter, typename T>
using IteratorHasConvertibleReferenceType =
IsLegalDataConversion<std::remove_reference_t<iter_reference_t<Iter>>, T>;
template <typename Iter, typename T>
using EnableIfCompatibleContiguousIterator = std::enable_if_t<
std::conjunction<IsContiguousIterator<Iter>,
IteratorHasConvertibleReferenceType<Iter, T>>::value>;
template <typename Container, typename T>
using ContainerHasConvertibleData = IsLegalDataConversion<
std::remove_pointer_t<decltype(std::data(std::declval<Container>()))>,
T>;
template <typename Container>
using ContainerHasIntegralSize =
std::is_integral<decltype(std::size(std::declval<Container>()))>;
template <typename From, size_t FromExtent, typename To, size_t ToExtent>
using EnableIfLegalSpanConversion =
std::enable_if_t<(ToExtent == dynamic_extent || ToExtent == FromExtent) &&
IsLegalDataConversion<From, To>::value>;
template <typename Array, typename T, size_t Extent>
using EnableIfSpanCompatibleArray =
std::enable_if_t<(Extent == dynamic_extent ||
Extent == internal::Extent<Array>::value) &&
ContainerHasConvertibleData<Array, T>::value>;
template <typename Container, typename T>
using IsSpanCompatibleContainer =
std::conjunction<IsNotSpan<Container>,
IsNotStdArray<Container>,
IsNotCArray<Container>,
ContainerHasConvertibleData<Container, T>,
ContainerHasIntegralSize<Container>>;
template <typename Container, typename T>
using EnableIfSpanCompatibleContainer =
std::enable_if_t<IsSpanCompatibleContainer<Container, T>::value>;
template <typename Container, typename T, size_t Extent>
using EnableIfSpanCompatibleContainerAndSpanIsDynamic =
std::enable_if_t<IsSpanCompatibleContainer<Container, T>::value &&
Extent == dynamic_extent>;
template <size_t Extent>
class ExtentStorage {
public:
constexpr explicit ExtentStorage(size_t size) noexcept {}
constexpr size_t size() const noexcept { return Extent; }
};
template <>
struct ExtentStorage<dynamic_extent> {
constexpr explicit ExtentStorage(size_t size) noexcept : size_(size) {}
constexpr size_t size() const noexcept { return size_; }
private:
size_t size_;
};
template <size_t kExtent>
constexpr size_t must_not_be_dynamic_extent() {
static_assert(
kExtent != dynamic_extent,
"EXTENT should only be used for containers with a static extent.");
return kExtent;
}
}
template <typename T, size_t Extent>
class GSL_POINTER span : public internal::ExtentStorage<Extent> {
private:
using ExtentStorage = internal::ExtentStorage<Extent>;
public:
using element_type = T;
using value_type = std::remove_cv_t<T>;
using size_type = size_t;
using difference_type = ptrdiff_t;
using pointer = T*;
using const_pointer = const T*;
using reference = T&;
using const_reference = const T&;
using iterator = CheckedContiguousIterator<T>;
using const_iterator = iterator;
using reverse_iterator = std::reverse_iterator<iterator>;
static constexpr size_t extent = Extent;
constexpr span() noexcept : ExtentStorage(0), data_(nullptr) {
static_assert(Extent == dynamic_extent || Extent == 0, "Invalid Extent");
}
template <typename It,
typename = internal::EnableIfCompatibleContiguousIterator<It, T>>
constexpr span(It first, StrictNumeric<size_t> count) noexcept
: ExtentStorage(count),
data_(base::to_address(first)) {
CHECK(Extent == dynamic_extent || Extent == count);
}
template <
typename It,
typename End,
typename = internal::EnableIfCompatibleContiguousIterator<It, T>,
typename = std::enable_if_t<!std::is_convertible<End, size_t>::value>>
constexpr span(It begin, End end) noexcept
: span(begin, static_cast<size_t>(end - begin)) {
CHECK(begin <= end);
}
template <
size_t N,
typename = internal::EnableIfSpanCompatibleArray<T (&)[N], T, Extent>>
constexpr span(T (&array)[N]) noexcept : span(std::data(array), N) {}
template <
typename U,
size_t N,
typename =
internal::EnableIfSpanCompatibleArray<std::array<U, N>&, T, Extent>>
constexpr span(std::array<U, N>& array) noexcept
: span(std::data(array), N) {}
template <typename U,
size_t N,
typename = internal::
EnableIfSpanCompatibleArray<const std::array<U, N>&, T, Extent>>
constexpr span(const std::array<U, N>& array) noexcept
: span(std::data(array), N) {}
template <
typename Container,
typename =
internal::EnableIfSpanCompatibleContainerAndSpanIsDynamic<Container&,
T,
Extent>>
constexpr span(Container& container) noexcept
: span(std::data(container), std::size(container)) {}
template <
typename Container,
typename = internal::EnableIfSpanCompatibleContainerAndSpanIsDynamic<
const Container&,
T,
Extent>>
constexpr span(const Container& container) noexcept
: span(std::data(container), std::size(container)) {}
constexpr span(const span& other) noexcept = default;
template <
typename U,
size_t OtherExtent,
typename =
internal::EnableIfLegalSpanConversion<U, OtherExtent, T, Extent>>
constexpr span(const span<U, OtherExtent>& other)
: span(other.data(), other.size()) {}
constexpr span& operator=(const span& other) noexcept = default;
~span() noexcept = default;
template <size_t Count>
constexpr span<T, Count> first() const noexcept {
static_assert(Count <= Extent, "Count must not exceed Extent");
CHECK(Extent != dynamic_extent || Count <= size());
return {data(), Count};
}
template <size_t Count>
constexpr span<T, Count> last() const noexcept {
static_assert(Count <= Extent, "Count must not exceed Extent");
CHECK(Extent != dynamic_extent || Count <= size());
return {data() + (size() - Count), Count};
}
template <size_t Offset, size_t Count = dynamic_extent>
constexpr span<T,
(Count != dynamic_extent
? Count
: (Extent != dynamic_extent ? Extent - Offset
: dynamic_extent))>
subspan() const noexcept {
static_assert(Offset <= Extent, "Offset must not exceed Extent");
static_assert(Count == dynamic_extent || Count <= Extent - Offset,
"Count must not exceed Extent - Offset");
CHECK(Extent != dynamic_extent || Offset <= size());
CHECK(Extent != dynamic_extent || Count == dynamic_extent ||
Count <= size() - Offset);
return {data() + Offset, Count != dynamic_extent ? Count : size() - Offset};
}
constexpr span<T, dynamic_extent> first(size_t count) const noexcept {
CHECK(count <= size());
return {data(), count};
}
constexpr span<T, dynamic_extent> last(size_t count) const noexcept {
CHECK(count <= size());
return {data() + (size() - count), count};
}
constexpr span<T, dynamic_extent> subspan(size_t offset,
size_t count = dynamic_extent) const
noexcept {
CHECK(offset <= size());
CHECK(count == dynamic_extent || count <= size() - offset);
return {data() + offset, count != dynamic_extent ? count : size() - offset};
}
constexpr size_t size() const noexcept { return ExtentStorage::size(); }
constexpr size_t size_bytes() const noexcept { return size() * sizeof(T); }
[[nodiscard]] constexpr bool empty() const noexcept { return size() == 0; }
constexpr T& operator[](size_t idx) const noexcept {
CHECK(idx < size());
return *(data() + idx);
}
constexpr T& front() const noexcept {
static_assert(Extent == dynamic_extent || Extent > 0,
"Extent must not be 0");
CHECK(Extent != dynamic_extent || !empty());
return *data();
}
constexpr T& back() const noexcept {
static_assert(Extent == dynamic_extent || Extent > 0,
"Extent must not be 0");
CHECK(Extent != dynamic_extent || !empty());
return *(data() + size() - 1);
}
constexpr T* data() const noexcept { return data_; }
constexpr iterator begin() const noexcept {
return iterator(data_, data_ + size());
}
constexpr iterator end() const noexcept {
return iterator(data_, data_ + size(), data_ + size());
}
constexpr reverse_iterator rbegin() const noexcept {
return reverse_iterator(end());
}
constexpr reverse_iterator rend() const noexcept {
return reverse_iterator(begin());
}
private:
T* data_;
};
template <class T, size_t Extent>
constexpr size_t span<T, Extent>::extent;
template <typename T, size_t X>
span<const uint8_t, (X == dynamic_extent ? dynamic_extent : sizeof(T) * X)>
as_bytes(span<T, X> s) noexcept {
return {reinterpret_cast<const uint8_t*>(s.data()), s.size_bytes()};
}
template <typename T,
size_t X,
typename = std::enable_if_t<!std::is_const<T>::value>>
span<uint8_t, (X == dynamic_extent ? dynamic_extent : sizeof(T) * X)>
as_writable_bytes(span<T, X> s) noexcept {
return {reinterpret_cast<uint8_t*>(s.data()), s.size_bytes()};
}
template <int&... ExplicitArgumentBarrier, typename It>
constexpr auto make_span(It it, StrictNumeric<size_t> size) noexcept {
using T = std::remove_reference_t<iter_reference_t<It>>;
return span<T>(it, size);
}
template <int&... ExplicitArgumentBarrier,
typename It,
typename End,
typename = std::enable_if_t<!std::is_convertible_v<End, size_t>>>
constexpr auto make_span(It it, End end) noexcept {
using T = std::remove_reference_t<iter_reference_t<It>>;
return span<T>(it, end);
}
template <int&... ExplicitArgumentBarrier, typename Container>
constexpr auto make_span(Container&& container) noexcept {
using T =
std::remove_pointer_t<decltype(std::data(std::declval<Container>()))>;
using Extent = internal::Extent<Container>;
return span<T, Extent::value>(std::forward<Container>(container));
}
template <size_t N, int&... ExplicitArgumentBarrier, typename It>
constexpr auto make_span(It it, StrictNumeric<size_t> size) noexcept {
using T = std::remove_reference_t<iter_reference_t<It>>;
return span<T, N>(it, size);
}
template <size_t N,
int&... ExplicitArgumentBarrier,
typename It,
typename End,
typename = std::enable_if_t<!std::is_convertible_v<End, size_t>>>
constexpr auto make_span(It it, End end) noexcept {
using T = std::remove_reference_t<iter_reference_t<It>>;
return span<T, N>(it, end);
}
template <size_t N, int&... ExplicitArgumentBarrier, typename Container>
constexpr auto make_span(Container&& container) noexcept {
using T =
std::remove_pointer_t<decltype(std::data(std::declval<Container>()))>;
return span<T, N>(std::data(container), std::size(container));
}
}
#define EXTENT(x) \
::base::internal::must_not_be_dynamic_extent<decltype( \
::base::make_span(x))::extent>()
#endif