Add util::array - an N-dim array with variable bounds
This commit is contained in:
parent
195867ac31
commit
30448d2ec1
1 changed files with 301 additions and 0 deletions
301
libs/util/include/psemek/util/array.hpp
Normal file
301
libs/util/include/psemek/util/array.hpp
Normal file
|
|
@ -0,0 +1,301 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/util/assert.hpp>
|
||||
|
||||
#include <memory>
|
||||
#include <array>
|
||||
#include <type_traits>
|
||||
|
||||
namespace psemek::util
|
||||
{
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
struct array
|
||||
{
|
||||
static_assert(N >= 1);
|
||||
|
||||
using value_type = T;
|
||||
static constexpr std::size_t dimension = N;
|
||||
|
||||
using dims_type = std::array<std::size_t, N>;
|
||||
|
||||
array();
|
||||
array(dims_type const & dims);
|
||||
array(dims_type const & dims, T const & value);
|
||||
array(dims_type const & dims, std::unique_ptr<T[]> data);
|
||||
array(array &&);
|
||||
|
||||
array(array const &) = delete;
|
||||
|
||||
array & operator = (array &&);
|
||||
|
||||
array & operator = (array const &) = delete;
|
||||
|
||||
std::size_t dim(std::size_t i) const
|
||||
{
|
||||
assert(i < N);
|
||||
return dims_[i];
|
||||
}
|
||||
|
||||
std::size_t size() const;
|
||||
|
||||
std::size_t width() const
|
||||
{
|
||||
static_assert(N >= 1);
|
||||
return dims_[0];
|
||||
}
|
||||
|
||||
std::size_t height()
|
||||
{
|
||||
static_assert(N >= 2);
|
||||
return dims_[1];
|
||||
}
|
||||
|
||||
std::size_t depth()
|
||||
{
|
||||
static_assert(N >= 3);
|
||||
return dims_[2];
|
||||
}
|
||||
|
||||
T & operator()(dims_type const & index);
|
||||
T const & operator()(dims_type const & index) const;
|
||||
|
||||
template <typename ... Ixs>
|
||||
T & operator()(Ixs ... ixs);
|
||||
|
||||
template <typename ... Ixs>
|
||||
T const & operator()(Ixs ... ixs) const;
|
||||
|
||||
array copy() const;
|
||||
|
||||
void resize(dims_type const & dims);
|
||||
|
||||
void resize(dims_type const & dims, T const & value);
|
||||
|
||||
T * data() { return data_.get(); }
|
||||
T const * data() const { return data_.get(); }
|
||||
|
||||
T * begin() { return data_.get(); }
|
||||
T const * begin() const { return data_.get(); }
|
||||
T const * cbegin() const { return data_.get(); }
|
||||
|
||||
T * end() { return data_.get() + size(); }
|
||||
T const * end() const { return data_.get() + size(); }
|
||||
T const * cend() const { return data_.get() + size(); }
|
||||
|
||||
std::unique_ptr<T[]> release();
|
||||
|
||||
bool empty() const;
|
||||
|
||||
void clear();
|
||||
|
||||
void fill(T const & value);
|
||||
|
||||
private:
|
||||
std::unique_ptr<T[]> data_;
|
||||
std::array<std::size_t, N> dims_;
|
||||
|
||||
void resize_impl(std::unique_ptr<T[]> data, dims_type const & dims);
|
||||
};
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template <std::size_t N>
|
||||
std::size_t product(std::array<std::size_t, N> const & dims)
|
||||
{
|
||||
std::size_t r = 1;
|
||||
for (auto const & d : dims)
|
||||
r *= d;
|
||||
return r;
|
||||
}
|
||||
|
||||
template <std::size_t N>
|
||||
bool empty(std::array<std::size_t, N> const & dims)
|
||||
{
|
||||
for (auto const & d : dims)
|
||||
if (d == 0) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
template <std::size_t N>
|
||||
std::size_t index(std::array<std::size_t, N> const & i, std::array<std::size_t, N> const & dims)
|
||||
{
|
||||
std::size_t r = 0;
|
||||
for (std::size_t d = N; d --> 0;)
|
||||
{
|
||||
assert(i[d] < dims[d]);
|
||||
r = i[d] + r * dims[d];
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
template <std::size_t N>
|
||||
bool next(std::array<std::size_t, N> const & i, std::array<std::size_t, N> const & dims)
|
||||
{
|
||||
for (std::size_t d = 0; d < N; ++d)
|
||||
{
|
||||
++i[d];
|
||||
if (i[d] < dims[d])
|
||||
return true;
|
||||
i[d] = 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
array<T, N>::array()
|
||||
{
|
||||
dims_.fill(0);
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
array<T, N>::array(dims_type const & dims)
|
||||
: dims_{dims}
|
||||
{
|
||||
data_.reset(new T[size()]);
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
array<T, N>::array(dims_type const & dims, T const & value)
|
||||
: array(dims)
|
||||
{
|
||||
fill(value);
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
array<T, N>::array(dims_type const & dims, std::unique_ptr<T[]> data)
|
||||
: dims_{dims}
|
||||
{
|
||||
data_ = std::move(data);
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
array<T, N>::array(array && other)
|
||||
: data_{std::move(other.data_)}
|
||||
, dims_{other.dims_}
|
||||
{
|
||||
other.dims_.fill(0);
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
array<T, N> & array<T, N>::operator = (array && other)
|
||||
{
|
||||
if (this == &other)
|
||||
return *this;
|
||||
|
||||
data_ = std::move(other.data_);
|
||||
dims_ = other.dims_;
|
||||
other.dims_.fill(0);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
std::size_t array<T, N>::size() const
|
||||
{
|
||||
return detail::product(dims_);
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
T & array<T, N>::operator()(dims_type const & index)
|
||||
{
|
||||
return data_[detail::index(index, dims_)];
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
T const & array<T, N>::operator()(dims_type const & index) const
|
||||
{
|
||||
return data_[detail::index(index, dims_)];
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
template <typename ... Ixs>
|
||||
T & array<T, N>::operator()(Ixs ... ixs)
|
||||
{
|
||||
dims_type dims{ixs...};
|
||||
return (*this)(dims);
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
template <typename ... Ixs>
|
||||
T const & array<T, N>::operator()(Ixs ... ixs) const
|
||||
{
|
||||
dims_type dims{ixs...};
|
||||
return (*this)(dims);
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
array<T, N> array<T, N>::copy() const
|
||||
{
|
||||
std::unique_ptr<T[]> data(new T[size()]);
|
||||
std::copy(begin(), end(), data.get());
|
||||
return array{dims_, std::move(data)};
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
void array<T, N>::resize(dims_type const & dims)
|
||||
{
|
||||
std::unique_ptr<T[]> data(new T[detail::product(dims)]);
|
||||
resize_impl(std::move(data), dims);
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
void array<T, N>::resize(dims_type const & dims, T const & value)
|
||||
{
|
||||
auto const size = detail::product(dims);
|
||||
std::unique_ptr<T[]> data(new T[size]);
|
||||
std::fill(data.get(), data.get() + size, value);
|
||||
resize_impl(std::move(data), dims);
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
std::unique_ptr<T[]> array<T, N>::release()
|
||||
{
|
||||
dims_.fill(0);
|
||||
return data_.release();
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
bool array<T, N>::empty() const
|
||||
{
|
||||
return detail::empty(dims_);
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
void array<T, N>::clear()
|
||||
{
|
||||
data_.reset();
|
||||
dims_.fill(0);
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
void array<T, N>::fill(T const & value)
|
||||
{
|
||||
std::fill(data_.get(), data_.get() + size(), value);
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
void array<T, N>::resize_impl(std::unique_ptr<T[]> data, dims_type const & dims)
|
||||
{
|
||||
std::array<std::size_t, N> min_dim;
|
||||
for (std::size_t d = 0; d < N; ++d)
|
||||
min_dim[d] = std::min(dims_[d], dims[d]);
|
||||
if (!detail::empty(min_dim))
|
||||
{
|
||||
std::array<std::size_t, N> i;
|
||||
i.fill(0);
|
||||
|
||||
do
|
||||
{
|
||||
data[detail::index(i, dims)] = (*this)(i);
|
||||
} while (detail::next(i, min_dim));
|
||||
}
|
||||
|
||||
dims_ = dims;
|
||||
data_ = std::move(data);
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue