|
|
@@ -15,127 +15,180 @@ |
|
|
|
// |
|
|
|
// The narray class |
|
|
|
// ———————————————— |
|
|
|
// An N-dimensional row-major array template class |
|
|
|
// An n-dimensional row-major array template class |
|
|
|
// |
|
|
|
|
|
|
|
#include <lol/vector> // lol::vec_t |
|
|
|
#include <vector> // std::vector |
|
|
|
#include <utility> // std::index_sequence |
|
|
|
|
|
|
|
namespace lol |
|
|
|
{ |
|
|
|
|
|
|
|
template<typename T, size_t N> |
|
|
|
class [[nodiscard]] narray |
|
|
|
template<typename T, size_t N, typename container_type> |
|
|
|
class [[nodiscard]] narray_base |
|
|
|
{ |
|
|
|
public: |
|
|
|
typedef T value_type; |
|
|
|
|
|
|
|
inline narray() = default; |
|
|
|
inline ~narray() = default; |
|
|
|
|
|
|
|
// Construct array with args: size1, size2, ..., sizen |
|
|
|
template<typename... I> inline narray(I... sizes) |
|
|
|
{ |
|
|
|
static_assert(N == sizeof...(I)); |
|
|
|
resize(sizes...); |
|
|
|
} |
|
|
|
using value_type = T; |
|
|
|
|
|
|
|
// Construct array with integer vec_t arg: |
|
|
|
inline narray(vec_t<int, N> const &sizes) |
|
|
|
// Return sizes of each dimension |
|
|
|
inline vec_t<int, N> sizes() const |
|
|
|
{ |
|
|
|
resize(sizes); |
|
|
|
return vec_t<int, N>(m_sizes); |
|
|
|
} |
|
|
|
|
|
|
|
// Empty array |
|
|
|
inline void clear() { resize(vec_t<size_t, N>(0)); } |
|
|
|
// Size in number of elements and in bytes |
|
|
|
inline size_t size() const { return size_helper(std::make_index_sequence<N>{}); } |
|
|
|
inline size_t bytes() const { return size() * sizeof(value_type); } |
|
|
|
|
|
|
|
// Access element i |
|
|
|
// Access element i in row-major fashion |
|
|
|
inline value_type &operator[](size_t i) |
|
|
|
{ |
|
|
|
return m_data[i]; |
|
|
|
return data()[i]; |
|
|
|
} |
|
|
|
|
|
|
|
inline value_type const &operator[](size_t i) const |
|
|
|
{ |
|
|
|
return m_data[i]; |
|
|
|
return data()[i]; |
|
|
|
} |
|
|
|
|
|
|
|
// Access element (i, j, .., z) in row-major fashion |
|
|
|
// Access element (i, j, .., z) |
|
|
|
template<typename... I> |
|
|
|
inline value_type &operator()(I... indices) |
|
|
|
{ |
|
|
|
static_assert(N == sizeof...(I)); |
|
|
|
return m_data[offset(indices...)]; |
|
|
|
return data()[offset(indices...)]; |
|
|
|
} |
|
|
|
|
|
|
|
template<typename... I> |
|
|
|
inline value_type const &operator()(I... indices) const |
|
|
|
{ |
|
|
|
static_assert(N == sizeof...(I)); |
|
|
|
return m_data[offset(indices...)]; |
|
|
|
return data()[offset(indices...)]; |
|
|
|
} |
|
|
|
|
|
|
|
inline value_type &operator()(vec_t<int, N> const &indices) |
|
|
|
{ |
|
|
|
return m_data[offset_helper(indices, std::make_index_sequence<N>{})]; |
|
|
|
return data()[offset_helper(indices, std::make_index_sequence<N>{})]; |
|
|
|
} |
|
|
|
|
|
|
|
inline value_type const &operator()(vec_t<int, N> const &indices) const |
|
|
|
{ |
|
|
|
return m_data[offset_helper(indices, std::make_index_sequence<N>{})]; |
|
|
|
return data()[offset_helper(indices, std::make_index_sequence<N>{})]; |
|
|
|
} |
|
|
|
|
|
|
|
// Resize array with a list of sizes (size1, size2, ..., sizen) |
|
|
|
template<typename... I> |
|
|
|
inline void resize(I... sizes) |
|
|
|
protected: |
|
|
|
template <size_t... I> |
|
|
|
inline size_t size_helper(std::index_sequence<I...>) const |
|
|
|
{ |
|
|
|
static_assert(N == sizeof...(I)); |
|
|
|
m_data.resize((size_t(sizes) * ... * 1)); |
|
|
|
m_sizes = { size_t(sizes)... }; |
|
|
|
return (size_t(m_sizes[I]) * ... * 1); |
|
|
|
} |
|
|
|
|
|
|
|
inline void resize(vec_t<int, N> const &sizes) |
|
|
|
template<typename... I> |
|
|
|
inline size_t offset(size_t i, I... indices) const |
|
|
|
{ |
|
|
|
resize_helper(sizes, std::make_index_sequence<N>{}); |
|
|
|
if constexpr(sizeof...(I) > 0) |
|
|
|
i += m_sizes[N - sizeof...(I) - 1] * offset(indices...); |
|
|
|
return i; |
|
|
|
} |
|
|
|
|
|
|
|
// Return sizes of each dimension |
|
|
|
inline vec_t<int, N> sizes() const |
|
|
|
template <size_t... I> |
|
|
|
inline size_t offset_helper(vec_t<int, N> const &sizes, std::index_sequence<I...>) const |
|
|
|
{ |
|
|
|
return vec_t<int, N>(this->m_sizes); |
|
|
|
return offset(size_t(sizes[I])...); |
|
|
|
} |
|
|
|
|
|
|
|
inline value_type *data() { return m_data.data(); } |
|
|
|
inline value_type const *data() const { return m_data.data(); } |
|
|
|
inline size_t size() const { return m_data.size(); } |
|
|
|
inline size_t bytes() const { return size() * sizeof(value_type); } |
|
|
|
vec_t<size_t, N> m_sizes { 0 }; |
|
|
|
|
|
|
|
private: |
|
|
|
template<typename... I> size_t offset(size_t i, I... indices) const |
|
|
|
// Use CRTP to access data() from the child class |
|
|
|
inline value_type *data() |
|
|
|
{ |
|
|
|
if constexpr(sizeof...(I) > 0) |
|
|
|
i += m_sizes[N - sizeof...(I) - 1] * offset(indices...); |
|
|
|
return i; |
|
|
|
return static_cast<container_type *>(this)->data(); |
|
|
|
} |
|
|
|
|
|
|
|
template <std::size_t... I> |
|
|
|
size_t offset_helper(vec_t<int, N> const &sizes, std::index_sequence<I...>) const |
|
|
|
inline value_type const *data() const |
|
|
|
{ |
|
|
|
return offset(size_t(sizes[I])...); |
|
|
|
return static_cast<container_type const *>(this)->data(); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
template<typename T, size_t N> |
|
|
|
class [[nodiscard]] narray : public narray_base<T, N, narray<T, N>> |
|
|
|
{ |
|
|
|
public: |
|
|
|
using value_type = T; |
|
|
|
|
|
|
|
template <std::size_t... I> |
|
|
|
void resize_helper(vec_t<int, N> const &sizes, std::index_sequence<I...>) |
|
|
|
inline narray() = default; |
|
|
|
inline ~narray() = default; |
|
|
|
|
|
|
|
// Construct array with args: size1, size2, ..., sizen |
|
|
|
template<typename... I> inline narray(I... sizes) |
|
|
|
{ |
|
|
|
resize(sizes[I]...); |
|
|
|
static_assert(N == sizeof...(I)); |
|
|
|
resize(sizes...); |
|
|
|
} |
|
|
|
|
|
|
|
vec_t<size_t, N> m_sizes { 0 }; |
|
|
|
// Construct array with integer vec_t arg: |
|
|
|
inline narray(vec_t<int, N> const &sizes) |
|
|
|
{ |
|
|
|
resize(sizes); |
|
|
|
} |
|
|
|
|
|
|
|
// Empty array |
|
|
|
inline void clear() { resize(vec_t<size_t, N>(0)); } |
|
|
|
|
|
|
|
// Resize array with a list of sizes (size1, size2, ..., sizen) |
|
|
|
template<typename... I> |
|
|
|
inline void resize(I... sizes) |
|
|
|
{ |
|
|
|
static_assert(N == sizeof...(I)); |
|
|
|
this->m_sizes = { size_t(sizes)... }; |
|
|
|
m_data.resize(this->size()); |
|
|
|
} |
|
|
|
|
|
|
|
template<typename U> |
|
|
|
inline void resize(vec_t<U, N> const &sizes) |
|
|
|
{ |
|
|
|
this->m_sizes = vec_t<size_t, N>(sizes); |
|
|
|
m_data.resize(this->size()); |
|
|
|
} |
|
|
|
|
|
|
|
// Access data directly |
|
|
|
inline value_type *data() { return m_data.data(); } |
|
|
|
inline value_type const *data() const { return m_data.data(); } |
|
|
|
|
|
|
|
private: |
|
|
|
std::vector<T> m_data; |
|
|
|
}; |
|
|
|
|
|
|
|
template<typename T> using array2d = narray<T, 2>; |
|
|
|
template<typename T> using array3d = narray<T, 3>; |
|
|
|
|
|
|
|
template<typename T, size_t N> |
|
|
|
class [[nodiscard]] narray_view : public narray_base<T, N, narray_view<T, N>> |
|
|
|
{ |
|
|
|
public: |
|
|
|
using value_type = T; |
|
|
|
|
|
|
|
template<typename U> |
|
|
|
inline narray_view(narray_base<T, N, U> &other) |
|
|
|
: m_data(&other[0]) |
|
|
|
{ |
|
|
|
this->m_sizes = vec_t<size_t, N>(other.sizes()); |
|
|
|
} |
|
|
|
|
|
|
|
// Access data directly |
|
|
|
inline value_type *data() { return m_data; } |
|
|
|
inline value_type const *data() const { return m_data; } |
|
|
|
|
|
|
|
private: |
|
|
|
T *m_data; |
|
|
|
}; |
|
|
|
|
|
|
|
template<typename T> using array2d_view = narray_view<T, 2>; |
|
|
|
template<typename T> using array3d_view = narray_view<T, 3>; |
|
|
|
|
|
|
|
} // namespace lol |
|
|
|
|