blob: 88d2e3de3a6b5e0e1655f6072be5db4a0675cda8 [file] [log] [blame]
/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
* Copyright (C) 2020, Google Inc.
*
* span.h - C++20 std::span<> implementation for C++11
*/
#pragma once
#include <array>
#include <iterator>
#include <limits>
#include <stddef.h>
#include <type_traits>
namespace libcamera {
static constexpr std::size_t dynamic_extent = std::numeric_limits<std::size_t>::max();
template<typename T, std::size_t Extent = dynamic_extent>
class Span;
namespace details {
template<typename U>
struct is_array : public std::false_type {
};
template<typename U, std::size_t N>
struct is_array<std::array<U, N>> : public std::true_type {
};
template<typename U>
struct is_span : public std::false_type {
};
template<typename U, std::size_t Extent>
struct is_span<Span<U, Extent>> : public std::true_type {
};
} /* namespace details */
namespace utils {
template<typename C>
constexpr auto size(const C &c) -> decltype(c.size())
{
return c.size();
}
template<typename C>
constexpr auto data(const C &c) -> decltype(c.data())
{
return c.data();
}
template<typename C>
constexpr auto data(C &c) -> decltype(c.data())
{
return c.data();
}
template<class T, std::size_t N>
constexpr T *data(T (&array)[N]) noexcept
{
return array;
}
template<std::size_t I, typename T>
struct tuple_element;
template<std::size_t I, typename T, std::size_t N>
struct tuple_element<I, Span<T, N>> {
using type = T;
};
template<typename T>
struct tuple_size;
template<typename T, std::size_t N>
struct tuple_size<Span<T, N>> : public std::integral_constant<std::size_t, N> {
};
template<typename T>
struct tuple_size<Span<T, dynamic_extent>>;
} /* namespace utils */
template<typename T, std::size_t Extent>
class Span
{
public:
using element_type = T;
using value_type = typename std::remove_cv_t<T>;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
using pointer = T *;
using const_pointer = const T *;
using reference = T &;
using const_reference = const T &;
using iterator = pointer;
using const_iterator = const_pointer;
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
static constexpr std::size_t extent = Extent;
template<bool Dependent = false,
typename = std::enable_if_t<Dependent || Extent == 0>>
constexpr Span() noexcept
: data_(nullptr)
{
}
explicit constexpr Span(pointer ptr, [[maybe_unused]] size_type count)
: data_(ptr)
{
}
explicit constexpr Span(pointer first, [[maybe_unused]] pointer last)
: data_(first)
{
}
template<std::size_t N>
constexpr Span(element_type (&arr)[N],
std::enable_if_t<std::is_convertible<std::remove_pointer_t<decltype(utils::data(arr))> (*)[],
element_type (*)[]>::value &&
N == Extent,
std::nullptr_t> = nullptr) noexcept
: data_(arr)
{
}
template<std::size_t N>
constexpr Span(std::array<value_type, N> &arr,
std::enable_if_t<std::is_convertible<std::remove_pointer_t<decltype(utils::data(arr))> (*)[],
element_type (*)[]>::value &&
N == Extent,
std::nullptr_t> = nullptr) noexcept
: data_(arr.data())
{
}
template<std::size_t N>
constexpr Span(const std::array<value_type, N> &arr,
std::enable_if_t<std::is_convertible<std::remove_pointer_t<decltype(utils::data(arr))> (*)[],
element_type (*)[]>::value &&
N == Extent,
std::nullptr_t> = nullptr) noexcept
: data_(arr.data())
{
}
template<class Container>
explicit constexpr Span(Container &cont,
std::enable_if_t<!details::is_span<Container>::value &&
!details::is_array<Container>::value &&
!std::is_array<Container>::value &&
std::is_convertible<std::remove_pointer_t<decltype(utils::data(cont))> (*)[],
element_type (*)[]>::value,
std::nullptr_t> = nullptr)
: data_(utils::data(cont))
{
}
template<class Container>
explicit constexpr Span(const Container &cont,
std::enable_if_t<!details::is_span<Container>::value &&
!details::is_array<Container>::value &&
!std::is_array<Container>::value &&
std::is_convertible<std::remove_pointer_t<decltype(utils::data(cont))> (*)[],
element_type (*)[]>::value,
std::nullptr_t> = nullptr)
: data_(utils::data(cont))
{
static_assert(utils::size(cont) == Extent, "Size mismatch");
}
template<class U, std::size_t N>
explicit constexpr Span(const Span<U, N> &s,
std::enable_if_t<std::is_convertible<U (*)[], element_type (*)[]>::value &&
N == Extent,
std::nullptr_t> = nullptr) noexcept
: data_(s.data())
{
}
constexpr Span(const Span &other) noexcept = default;
constexpr Span &operator=(const Span &other) noexcept = default;
constexpr iterator begin() const { return data(); }
constexpr const_iterator cbegin() const { return begin(); }
constexpr iterator end() const { return data() + size(); }
constexpr const_iterator cend() const { return end(); }
constexpr reverse_iterator rbegin() const { return reverse_iterator(end()); }
constexpr const_reverse_iterator crbegin() const { return rbegin(); }
constexpr reverse_iterator rend() const { return reverse_iterator(begin()); }
constexpr const_reverse_iterator crend() const { return rend(); }
constexpr reference front() const { return *data(); }
constexpr reference back() const { return *(data() + size() - 1); }
constexpr reference operator[](size_type idx) const { return data()[idx]; }
constexpr pointer data() const noexcept { return data_; }
constexpr size_type size() const noexcept { return Extent; }
constexpr size_type size_bytes() const noexcept { return size() * sizeof(element_type); }
constexpr bool empty() const noexcept { return size() == 0; }
template<std::size_t Count>
constexpr Span<element_type, Count> first() const
{
static_assert(Count <= Extent, "Count larger than size");
return Span<element_type, Count>{ data(), Count };
}
constexpr Span<element_type, dynamic_extent> first(std::size_t Count) const
{
return Span<element_type, dynamic_extent>{ data(), Count };
}
template<std::size_t Count>
constexpr Span<element_type, Count> last() const
{
static_assert(Count <= Extent, "Count larger than size");
return Span<element_type, Count>{ data() + size() - Count, Count };
}
constexpr Span<element_type, dynamic_extent> last(std::size_t Count) const
{
return Span<element_type, dynamic_extent>{ data() + size() - Count, Count };
}
template<std::size_t Offset, std::size_t Count = dynamic_extent>
constexpr Span<element_type, Count != dynamic_extent ? Count : Extent - Offset> subspan() const
{
static_assert(Offset <= Extent, "Offset larger than size");
static_assert(Count == dynamic_extent || Count + Offset <= Extent,
"Offset + Count larger than size");
return Span<element_type, Count != dynamic_extent ? Count : Extent - Offset>{
data() + Offset,
Count == dynamic_extent ? size() - Offset : Count
};
}
constexpr Span<element_type, dynamic_extent>
subspan(std::size_t Offset, std::size_t Count = dynamic_extent) const
{
return Span<element_type, dynamic_extent>{
data() + Offset,
Count == dynamic_extent ? size() - Offset : Count
};
}
private:
pointer data_;
};
template<typename T>
class Span<T, dynamic_extent>
{
public:
using element_type = T;
using value_type = typename std::remove_cv_t<T>;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
using pointer = T *;
using const_pointer = const T *;
using reference = T &;
using const_reference = const T &;
using iterator = T *;
using const_iterator = const T *;
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
static constexpr std::size_t extent = dynamic_extent;
constexpr Span() noexcept
: data_(nullptr), size_(0)
{
}
constexpr Span(pointer ptr, size_type count)
: data_(ptr), size_(count)
{
}
constexpr Span(pointer first, pointer last)
: data_(first), size_(last - first)
{
}
template<std::size_t N>
constexpr Span(element_type (&arr)[N],
std::enable_if_t<std::is_convertible<std::remove_pointer_t<decltype(utils::data(arr))> (*)[],
element_type (*)[]>::value,
std::nullptr_t> = nullptr) noexcept
: data_(arr), size_(N)
{
}
template<std::size_t N>
constexpr Span(std::array<value_type, N> &arr,
std::enable_if_t<std::is_convertible<std::remove_pointer_t<decltype(utils::data(arr))> (*)[],
element_type (*)[]>::value,
std::nullptr_t> = nullptr) noexcept
: data_(utils::data(arr)), size_(N)
{
}
template<std::size_t N>
constexpr Span(const std::array<value_type, N> &arr) noexcept
: data_(utils::data(arr)), size_(N)
{
}
template<class Container>
constexpr Span(Container &cont,
std::enable_if_t<!details::is_span<Container>::value &&
!details::is_array<Container>::value &&
!std::is_array<Container>::value &&
std::is_convertible<std::remove_pointer_t<decltype(utils::data(cont))> (*)[],
element_type (*)[]>::value,
std::nullptr_t> = nullptr)
: data_(utils::data(cont)), size_(utils::size(cont))
{
}
template<class Container>
constexpr Span(const Container &cont,
std::enable_if_t<!details::is_span<Container>::value &&
!details::is_array<Container>::value &&
!std::is_array<Container>::value &&
std::is_convertible<std::remove_pointer_t<decltype(utils::data(cont))> (*)[],
element_type (*)[]>::value,
std::nullptr_t> = nullptr)
: data_(utils::data(cont)), size_(utils::size(cont))
{
}
template<class U, std::size_t N>
constexpr Span(const Span<U, N> &s,
std::enable_if_t<std::is_convertible<U (*)[], element_type (*)[]>::value,
std::nullptr_t> = nullptr) noexcept
: data_(s.data()), size_(s.size())
{
}
constexpr Span(const Span &other) noexcept = default;
constexpr Span &operator=(const Span &other) noexcept
{
data_ = other.data_;
size_ = other.size_;
return *this;
}
constexpr iterator begin() const { return data(); }
constexpr const_iterator cbegin() const { return begin(); }
constexpr iterator end() const { return data() + size(); }
constexpr const_iterator cend() const { return end(); }
constexpr reverse_iterator rbegin() const { return reverse_iterator(end()); }
constexpr const_reverse_iterator crbegin() const { return rbegin(); }
constexpr reverse_iterator rend() const { return reverse_iterator(begin()); }
constexpr const_reverse_iterator crend() const { return rend(); }
constexpr reference front() const { return *data(); }
constexpr reference back() const { return *(data() + size() - 1); }
constexpr reference operator[](size_type idx) const { return data()[idx]; }
constexpr pointer data() const noexcept { return data_; }
constexpr size_type size() const noexcept { return size_; }
constexpr size_type size_bytes() const noexcept { return size() * sizeof(element_type); }
constexpr bool empty() const noexcept { return size() == 0; }
template<std::size_t Count>
constexpr Span<element_type, Count> first() const
{
return Span<element_type, Count>{ data(), Count };
}
constexpr Span<element_type, dynamic_extent> first(std::size_t Count) const
{
return { data(), Count };
}
template<std::size_t Count>
constexpr Span<element_type, Count> last() const
{
return Span<element_type, Count>{ data() + size() - Count, Count };
}
constexpr Span<element_type, dynamic_extent> last(std::size_t Count) const
{
return Span<element_type, dynamic_extent>{ data() + size() - Count, Count };
}
template<std::size_t Offset, std::size_t Count = dynamic_extent>
constexpr Span<element_type, Count> subspan() const
{
return Span<element_type, Count>{
data() + Offset,
Count == dynamic_extent ? size() - Offset : Count
};
}
constexpr Span<element_type, dynamic_extent>
subspan(std::size_t Offset, std::size_t Count = dynamic_extent) const
{
return Span<element_type, dynamic_extent>{
data() + Offset,
Count == dynamic_extent ? size() - Offset : Count
};
}
private:
pointer data_;
size_type size_;
};
} /* namespace libcamera */