/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * Copyright (C) 2020, Google Inc. * * span.h - C++20 std::span<> implementation for C++11 */ #ifndef __LIBCAMERA_SPAN_H__ #define __LIBCAMERA_SPAN_H__ #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) { } constexpr Span(pointer ptr, [[maybe_unused]] size_type count) : data_(ptr) { } 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> 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> 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> 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(data() + size() - 1); } constexpr const_reverse_iterator crbegin() const { return rbegin(); } constexpr reverse_iterator rend() const { return reverse_iterator(data() - 1); } 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 { 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 { static_assert(Count <= Extent, "Count larger than size"); return { data() + size() - Count, Count }; } constexpr Span<element_type, dynamic_extent> last(std::size_t Count) const { return { 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 { 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 { 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(data() + size() - 1); } constexpr const_reverse_iterator crbegin() const { return rbegin(); } constexpr reverse_iterator rend() const { return reverse_iterator(data() - 1); } 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 { 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 { data() + size() - Count, Count }; } constexpr Span<element_type, dynamic_extent> last(std::size_t Count) const { return { data() + size() - Count, Count }; } template<std::size_t Offset, std::size_t Count = dynamic_extent> constexpr Span<element_type, Count> subspan() const { return { 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 { data() + Offset, Count == dynamic_extent ? size() - Offset : Count }; } private: pointer data_; size_type size_; }; } /* namespace libcamera */ #endif /* __LIBCAMERA_SPAN_H__ */