From 30448d2ec136609753da06565540d190e676ebc4 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Thu, 10 Sep 2020 11:05:19 +0300 Subject: [PATCH] Add util::array - an N-dim array with variable bounds --- libs/util/include/psemek/util/array.hpp | 301 ++++++++++++++++++++++++ 1 file changed, 301 insertions(+) create mode 100644 libs/util/include/psemek/util/array.hpp diff --git a/libs/util/include/psemek/util/array.hpp b/libs/util/include/psemek/util/array.hpp new file mode 100644 index 00000000..8a6d0211 --- /dev/null +++ b/libs/util/include/psemek/util/array.hpp @@ -0,0 +1,301 @@ +#pragma once + +#include + +#include +#include +#include + +namespace psemek::util +{ + + template + struct array + { + static_assert(N >= 1); + + using value_type = T; + static constexpr std::size_t dimension = N; + + using dims_type = std::array; + + array(); + array(dims_type const & dims); + array(dims_type const & dims, T const & value); + array(dims_type const & dims, std::unique_ptr 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 + T & operator()(Ixs ... ixs); + + template + 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 release(); + + bool empty() const; + + void clear(); + + void fill(T const & value); + + private: + std::unique_ptr data_; + std::array dims_; + + void resize_impl(std::unique_ptr data, dims_type const & dims); + }; + + namespace detail + { + + template + std::size_t product(std::array const & dims) + { + std::size_t r = 1; + for (auto const & d : dims) + r *= d; + return r; + } + + template + bool empty(std::array const & dims) + { + for (auto const & d : dims) + if (d == 0) return true; + return false; + } + + template + std::size_t index(std::array const & i, std::array 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 + bool next(std::array const & i, std::array 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 + array::array() + { + dims_.fill(0); + } + + template + array::array(dims_type const & dims) + : dims_{dims} + { + data_.reset(new T[size()]); + } + + template + array::array(dims_type const & dims, T const & value) + : array(dims) + { + fill(value); + } + + template + array::array(dims_type const & dims, std::unique_ptr data) + : dims_{dims} + { + data_ = std::move(data); + } + + template + array::array(array && other) + : data_{std::move(other.data_)} + , dims_{other.dims_} + { + other.dims_.fill(0); + } + + template + array & array::operator = (array && other) + { + if (this == &other) + return *this; + + data_ = std::move(other.data_); + dims_ = other.dims_; + other.dims_.fill(0); + + return *this; + } + + template + std::size_t array::size() const + { + return detail::product(dims_); + } + + template + T & array::operator()(dims_type const & index) + { + return data_[detail::index(index, dims_)]; + } + + template + T const & array::operator()(dims_type const & index) const + { + return data_[detail::index(index, dims_)]; + } + + template + template + T & array::operator()(Ixs ... ixs) + { + dims_type dims{ixs...}; + return (*this)(dims); + } + + template + template + T const & array::operator()(Ixs ... ixs) const + { + dims_type dims{ixs...}; + return (*this)(dims); + } + + template + array array::copy() const + { + std::unique_ptr data(new T[size()]); + std::copy(begin(), end(), data.get()); + return array{dims_, std::move(data)}; + } + + template + void array::resize(dims_type const & dims) + { + std::unique_ptr data(new T[detail::product(dims)]); + resize_impl(std::move(data), dims); + } + + template + void array::resize(dims_type const & dims, T const & value) + { + auto const size = detail::product(dims); + std::unique_ptr data(new T[size]); + std::fill(data.get(), data.get() + size, value); + resize_impl(std::move(data), dims); + } + + template + std::unique_ptr array::release() + { + dims_.fill(0); + return data_.release(); + } + + template + bool array::empty() const + { + return detail::empty(dims_); + } + + template + void array::clear() + { + data_.reset(); + dims_.fill(0); + } + + template + void array::fill(T const & value) + { + std::fill(data_.get(), data_.get() + size(), value); + } + + template + void array::resize_impl(std::unique_ptr data, dims_type const & dims) + { + std::array 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 i; + i.fill(0); + + do + { + data[detail::index(i, dims)] = (*this)(i); + } while (detail::next(i, min_dim)); + } + + dims_ = dims; + data_ = std::move(data); + } + +}