include/unifex/span.hpp (245 lines of code) (raw):
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* Licensed under the Apache License Version 2.0 with LLVM Exceptions
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* https://llvm.org/LICENSE.txt
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <array>
#include <cstddef>
#include <cstdint>
#include <type_traits>
#include <unifex/std_concepts.hpp>
#include <unifex/detail/prologue.hpp>
namespace unifex {
inline constexpr std::size_t dynamic_extent = static_cast<std::size_t>(-1);
template <typename T, std::size_t Extent = dynamic_extent>
struct span {
public:
using value_type = T;
using size_type = std::size_t;
using reference = std::add_lvalue_reference_t<T>;
using pointer = std::add_pointer_t<T>;
// QUESTION: Should we really have this constructor?
constexpr span() noexcept : data_(nullptr) {}
explicit constexpr span(T* data) noexcept : data_(data) {}
explicit constexpr span(const span<T, dynamic_extent>& other) noexcept
: data_(other.data()) {
UNIFEX_ASSERT(other.size() >= Extent);
}
template <std::size_t N>
constexpr span(T (&arr)[N]) noexcept : data_(&arr[0]) {
static_assert(N >= Extent);
}
template <std::size_t N>
constexpr span(std::array<T, N>& arr) noexcept : data_(arr.data()) {
static_assert(N >= Extent);
}
template <std::size_t OtherExtent>
constexpr span(const span<T, OtherExtent>& other) noexcept
: data_(other.data()) {
static_assert(
OtherExtent >= Extent,
"Cannot construct a larger span from a smaller one");
}
template(typename U)
(requires (!std::is_const_v<U>) AND same_as<const U, T>)
explicit constexpr span(const span<U, dynamic_extent>& other) noexcept
: data_(other.data()) {
UNIFEX_ASSERT(other.size() >= Extent);
}
template(std::size_t OtherExtent, typename U)
(requires (!std::is_const_v<U>) AND same_as<const U, T>)
constexpr span(const span<U, OtherExtent>& other) noexcept
: data_(other.data()) {
static_assert(
OtherExtent >= Extent,
"Cannot construct a larger span from a smaller one");
}
T& operator[](std::size_t index) const noexcept {
UNIFEX_ASSERT(index < size());
return data_[index];
}
constexpr T* data() const noexcept {
return data_;
}
constexpr std::size_t size() const noexcept {
return Extent;
}
constexpr bool empty() const noexcept {
return Extent == 0;
}
T* begin() const noexcept {
return data();
}
T* end() const noexcept {
return data() + size();
}
template <std::size_t N>
span<T, N> first() const noexcept {
static_assert(N != dynamic_extent);
static_assert(
N <= Extent,
"Cannot slide to more elements than were in original span");
return span<T, N>{data_};
}
span<T, dynamic_extent> first(std::size_t count) const noexcept;
template <std::size_t N>
span<T, N> last() const noexcept {
static_assert(N != dynamic_extent);
static_assert(
N <= Extent,
"Cannot slide to more elements than were in original span");
return span<T, N>{data_ + (Extent - N)};
}
span<T, dynamic_extent> last(std::size_t count) const noexcept;
template <std::size_t N>
span<T, Extent - N> after() const noexcept {
static_assert(N != dynamic_extent);
static_assert(
N <= Extent,
"Cannot slice to more elements than were in original span");
return span<T, Extent - N>{data_ + N};
}
span<T, dynamic_extent> after(std::size_t count) const noexcept;
private:
T* data_;
};
template <typename T>
struct span<T, dynamic_extent> {
public:
using value_type = T;
using size_type = std::size_t;
using reference = std::add_lvalue_reference_t<T>;
using pointer = std::add_pointer_t<T>;
constexpr span() noexcept : data_(nullptr), size_(0) {}
constexpr span(T* data, std::size_t size) noexcept
: data_(data), size_(size) {}
template <std::size_t N>
constexpr span(T (&arr)[N]) noexcept : data_(arr), size_(N) {}
template <std::size_t N>
constexpr span(std::array<T, N>& arr) noexcept
: data_(arr.data()), size_(N) {}
template(typename U, std::size_t OtherExtent)
(requires same_as<U, T> ||
(!std::is_const_v<U> && same_as<const U, T>))
constexpr span(const span<U, OtherExtent>& other) noexcept
: data_(other.data()), size_(other.size()) {}
T& operator[](std::size_t index) const noexcept {
UNIFEX_ASSERT(index < size());
return data_[index];
}
T* data() const noexcept {
return data_;
}
bool empty() const noexcept {
return size_ == 0;
}
std::size_t size() const noexcept {
return size_;
}
T* begin() const noexcept {
return data();
}
T* end() const noexcept {
return data() + size();
}
template <std::size_t N>
span<T, N> first() const noexcept {
static_assert(N != dynamic_extent);
UNIFEX_ASSERT(
N <= size() &&
"Cannot slice to more elements than were in original span");
return span<T, N>{data()};
}
span<T, dynamic_extent> first(std::size_t count) const noexcept {
UNIFEX_ASSERT(
count <= size() &&
"Cannot slice to more elements than were in original span");
return span<T, dynamic_extent>{data(), count};
}
template <std::size_t N>
span<T, N> last() const noexcept {
static_assert(N != dynamic_extent);
UNIFEX_ASSERT(
N <= size() &&
"Cannot slice to more elements than were in original span");
return span<T, N>{data() + (size() - N)};
}
span<T, dynamic_extent> last(std::size_t count) const noexcept {
UNIFEX_ASSERT(
count <= size() &&
"Cannot slice to more elements than were in original span");
return span<T, dynamic_extent>{data() + (size() - count), count};
}
template <std::size_t N>
span<T, dynamic_extent> after() const noexcept {
static_assert(N != dynamic_extent);
UNIFEX_ASSERT(
N <= size() &&
"Cannot slice to more elements than were in original span");
return span<T, dynamic_extent>{data() + N, size() - N};
}
span<T, dynamic_extent> after(std::size_t count) const noexcept {
UNIFEX_ASSERT(
count <= size() &&
"Cannot slice to more elements than were in original span");
return span<T, dynamic_extent>{data() + count, size() - count};
}
private:
T* data_;
std::size_t size_;
};
template <typename T, std::size_t Extent>
inline span<T, dynamic_extent> span<T, Extent>::first(std::size_t count) const
noexcept {
UNIFEX_ASSERT(count <= Extent);
return span<T, dynamic_extent>{data(), count};
}
template <typename T, std::size_t Extent>
inline span<T, dynamic_extent> span<T, Extent>::last(std::size_t count) const
noexcept {
UNIFEX_ASSERT(count <= Extent);
return span<T, dynamic_extent>{data() + (Extent - count), count};
}
template <typename T, std::size_t Extent>
inline span<T, dynamic_extent> span<T, Extent>::after(std::size_t count) const
noexcept {
UNIFEX_ASSERT(count <= Extent);
return span<T, dynamic_extent>{data() + count, Extent - count};
}
template <typename T, std::size_t N>
span(T (&)[N])->span<T, N>;
template <typename T, std::size_t N>
span(std::array<T, N>&)->span<T, N>;
template <typename T, std::size_t N>
span(const std::array<T, N>&)->span<const T, N>;
template <typename T>
span(T*, std::size_t)->span<T>;
template <typename T, std::size_t Extent>
span<const std::byte, Extent * sizeof(T)> as_bytes(
const span<T, Extent>& s) noexcept {
constexpr std::size_t maxSize = std::size_t(-1) / sizeof(T);
static_assert(Extent <= maxSize);
return span<const std::byte, Extent * sizeof(T)>{
reinterpret_cast<const std::byte*>(s.data())};
}
template <typename T>
span<const std::byte> as_bytes(const span<T>& s) noexcept {
[[maybe_unused]] constexpr std::size_t maxSize = std::size_t(-1) / sizeof(T);
UNIFEX_ASSERT(s.size() <= maxSize);
return span<const std::byte>{reinterpret_cast<const std::byte*>(s.data()),
s.size() * sizeof(T)};
}
template(typename T, std::size_t Extent)
(requires (!std::is_const_v<T>))
span<std::byte, Extent * sizeof(T)> as_writable_bytes(
const span<T, Extent>& s) noexcept {
constexpr std::size_t maxSize = std::size_t(-1) / sizeof(T);
static_assert(Extent <= maxSize);
return span<std::byte, Extent * sizeof(T)>{
reinterpret_cast<std::byte*>(s.data())};
}
template(typename T)
(requires (!std::is_const_v<T>))
span<std::byte> as_writable_bytes(const span<T>& s) noexcept {
[[maybe_unused]] constexpr std::size_t maxSize = std::size_t(-1) / sizeof(T);
UNIFEX_ASSERT(s.size() <= maxSize);
return span<std::byte>{reinterpret_cast<std::byte*>(s.data()),
s.size() * sizeof(T)};
}
} // namespace unifex
#include <unifex/detail/epilogue.hpp>