diff options
author | Jacques Lucke <jacques@blender.org> | 2022-03-19 10:26:29 +0300 |
---|---|---|
committer | Jacques Lucke <jacques@blender.org> | 2022-03-19 10:26:29 +0300 |
commit | 3e16f3b3ef4b8f385b30fe4a1e00860620f610ee (patch) | |
tree | cea8e2a3ea8a8a7dbce98263d166b4782d83721b /source/blender/blenlib/BLI_generic_virtual_array.hh | |
parent | c655146b87fe20853e52b87991b46732a04d749e (diff) |
BLI: move generic data structures to blenlib
This is a follow up to rB2252bc6a5527cd7360d1ccfe7a2d1bc640a8dfa6.
Diffstat (limited to 'source/blender/blenlib/BLI_generic_virtual_array.hh')
-rw-r--r-- | source/blender/blenlib/BLI_generic_virtual_array.hh | 877 |
1 files changed, 877 insertions, 0 deletions
diff --git a/source/blender/blenlib/BLI_generic_virtual_array.hh b/source/blender/blenlib/BLI_generic_virtual_array.hh new file mode 100644 index 00000000000..f4c9e745cf9 --- /dev/null +++ b/source/blender/blenlib/BLI_generic_virtual_array.hh @@ -0,0 +1,877 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +/** \file + * \ingroup bli + * + * A generic virtual array is the same as a virtual array, except for the fact that the data type + * is only known at runtime. + */ + +#include "BLI_generic_array.hh" +#include "BLI_generic_span.hh" +#include "BLI_timeit.hh" +#include "BLI_virtual_array.hh" + +namespace blender { + +/* -------------------------------------------------------------------- */ +/** \name #GVArrayImpl and #GVMutableArrayImpl. + * \{ */ + +class GVArray; +class GVArrayImpl; +class GVMutableArray; +class GVMutableArrayImpl; + +/* A generically typed version of #VArrayImpl. */ +class GVArrayImpl { + protected: + const CPPType *type_; + int64_t size_; + + public: + GVArrayImpl(const CPPType &type, int64_t size); + virtual ~GVArrayImpl() = default; + + const CPPType &type() const; + + int64_t size() const; + + virtual void get(int64_t index, void *r_value) const; + virtual void get_to_uninitialized(int64_t index, void *r_value) const = 0; + + virtual bool is_span() const; + virtual GSpan get_internal_span() const; + + virtual bool is_single() const; + virtual void get_internal_single(void *UNUSED(r_value)) const; + + virtual void materialize(const IndexMask mask, void *dst) const; + virtual void materialize_to_uninitialized(const IndexMask mask, void *dst) const; + + virtual bool try_assign_VArray(void *varray) const; + virtual bool may_have_ownership() const; +}; + +/* A generic version of #VMutableArrayImpl. */ +class GVMutableArrayImpl : public GVArrayImpl { + public: + GVMutableArrayImpl(const CPPType &type, int64_t size); + + virtual void set_by_copy(int64_t index, const void *value); + virtual void set_by_relocate(int64_t index, void *value); + virtual void set_by_move(int64_t index, void *value) = 0; + + virtual void set_all(const void *src); + + virtual bool try_assign_VMutableArray(void *varray) const; +}; + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #GVArray and #GVMutableArray + * \{ */ + +namespace detail { +struct GVArrayAnyExtraInfo { + const GVArrayImpl *(*get_varray)(const void *buffer) = + [](const void *UNUSED(buffer)) -> const GVArrayImpl * { return nullptr; }; + + template<typename StorageT> static GVArrayAnyExtraInfo get(); +}; +} // namespace detail + +class GVMutableArray; + +/** + * Utility class to reduce code duplication between #GVArray and #GVMutableArray. + * It pretty much follows #VArrayCommon. Don't use this class outside of this header. + */ +class GVArrayCommon { + protected: + /** + * See #VArrayCommon for more information. The inline buffer is a bit larger here, because + * generic virtual array implementations often require a bit more space than typed ones. + */ + using Storage = Any<detail::GVArrayAnyExtraInfo, 40, 8>; + + const GVArrayImpl *impl_ = nullptr; + Storage storage_; + + protected: + GVArrayCommon(); + GVArrayCommon(const GVArrayCommon &other); + GVArrayCommon(GVArrayCommon &&other) noexcept; + GVArrayCommon(const GVArrayImpl *impl); + GVArrayCommon(std::shared_ptr<const GVArrayImpl> impl); + ~GVArrayCommon(); + + template<typename ImplT, typename... Args> void emplace(Args &&...args); + + void copy_from(const GVArrayCommon &other); + void move_from(GVArrayCommon &&other) noexcept; + + const GVArrayImpl *impl_from_storage() const; + + public: + const CPPType &type() const; + operator bool() const; + + int64_t size() const; + bool is_empty() const; + IndexRange index_range() const; + + template<typename T> bool try_assign_VArray(VArray<T> &varray) const; + bool may_have_ownership() const; + + void materialize(void *dst) const; + void materialize(const IndexMask mask, void *dst) const; + + void materialize_to_uninitialized(void *dst) const; + void materialize_to_uninitialized(const IndexMask mask, void *dst) const; + + /** + * Returns true when the virtual array is stored as a span internally. + */ + bool is_span() const; + /** + * Returns the internally used span of the virtual array. This invokes undefined behavior if the + * virtual array is not stored as a span internally. + */ + GSpan get_internal_span() const; + + /** + * Returns true when the virtual array returns the same value for every index. + */ + bool is_single() const; + /** + * Copies the value that is used for every element into `r_value`, which is expected to point to + * initialized memory. This invokes undefined behavior if the virtual array would not return the + * same value for every index. + */ + void get_internal_single(void *r_value) const; + /** + * Same as `get_internal_single`, but `r_value` points to initialized memory. + */ + void get_internal_single_to_uninitialized(void *r_value) const; + + void get(int64_t index, void *r_value) const; + /** + * Returns a copy of the value at the given index. Usually a typed virtual array should + * be used instead, but sometimes this is simpler when only a few indices are needed. + */ + template<typename T> T get(int64_t index) const; + void get_to_uninitialized(int64_t index, void *r_value) const; +}; + +/** Generic version of #VArray. */ +class GVArray : public GVArrayCommon { + private: + friend GVMutableArray; + + public: + GVArray() = default; + + GVArray(const GVArray &other); + GVArray(GVArray &&other) noexcept; + GVArray(const GVArrayImpl *impl); + GVArray(std::shared_ptr<const GVArrayImpl> impl); + + template<typename T> GVArray(const VArray<T> &varray); + template<typename T> VArray<T> typed() const; + + template<typename ImplT, typename... Args> static GVArray For(Args &&...args); + + static GVArray ForSingle(const CPPType &type, int64_t size, const void *value); + static GVArray ForSingleRef(const CPPType &type, int64_t size, const void *value); + static GVArray ForSingleDefault(const CPPType &type, int64_t size); + static GVArray ForSpan(GSpan span); + static GVArray ForGArray(GArray<> array); + static GVArray ForEmpty(const CPPType &type); + + GVArray slice(IndexRange slice) const; + + GVArray &operator=(const GVArray &other); + GVArray &operator=(GVArray &&other) noexcept; + + const GVArrayImpl *get_implementation() const + { + return impl_; + } +}; + +/** Generic version of #VMutableArray. */ +class GVMutableArray : public GVArrayCommon { + public: + GVMutableArray() = default; + GVMutableArray(const GVMutableArray &other); + GVMutableArray(GVMutableArray &&other) noexcept; + GVMutableArray(GVMutableArrayImpl *impl); + GVMutableArray(std::shared_ptr<GVMutableArrayImpl> impl); + + template<typename T> GVMutableArray(const VMutableArray<T> &varray); + template<typename T> VMutableArray<T> typed() const; + + template<typename ImplT, typename... Args> static GVMutableArray For(Args &&...args); + + static GVMutableArray ForSpan(GMutableSpan span); + + operator GVArray() const &; + operator GVArray() &&noexcept; + + GVMutableArray &operator=(const GVMutableArray &other); + GVMutableArray &operator=(GVMutableArray &&other) noexcept; + + GMutableSpan get_internal_span() const; + + template<typename T> bool try_assign_VMutableArray(VMutableArray<T> &varray) const; + + void set_by_copy(int64_t index, const void *value); + void set_by_move(int64_t index, void *value); + void set_by_relocate(int64_t index, void *value); + + void fill(const void *value); + /** + * Copy the values from the source buffer to all elements in the virtual array. + */ + void set_all(const void *src); + + GVMutableArrayImpl *get_implementation() const; + + private: + GVMutableArrayImpl *get_impl() const; +}; + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #GVArray_GSpan and #GVMutableArray_GSpan. + * \{ */ + +/* A generic version of VArray_Span. */ +class GVArray_GSpan : public GSpan { + private: + GVArray varray_; + void *owned_data_ = nullptr; + + public: + GVArray_GSpan(GVArray varray); + ~GVArray_GSpan(); +}; + +/* A generic version of VMutableArray_Span. */ +class GVMutableArray_GSpan : public GMutableSpan { + private: + GVMutableArray varray_; + void *owned_data_ = nullptr; + bool save_has_been_called_ = false; + bool show_not_saved_warning_ = true; + + public: + GVMutableArray_GSpan(GVMutableArray varray, bool copy_values_to_span = true); + ~GVMutableArray_GSpan(); + + void save(); + void disable_not_applied_warning(); +}; + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Conversions between generic and typed virtual arrays. + * \{ */ + +/* Used to convert a typed virtual array into a generic one. */ +template<typename T> class GVArrayImpl_For_VArray : public GVArrayImpl { + protected: + VArray<T> varray_; + + public: + GVArrayImpl_For_VArray(VArray<T> varray) + : GVArrayImpl(CPPType::get<T>(), varray.size()), varray_(std::move(varray)) + { + } + + protected: + void get(const int64_t index, void *r_value) const override + { + *(T *)r_value = varray_[index]; + } + + void get_to_uninitialized(const int64_t index, void *r_value) const override + { + new (r_value) T(varray_[index]); + } + + bool is_span() const override + { + return varray_.is_span(); + } + + GSpan get_internal_span() const override + { + return GSpan(varray_.get_internal_span()); + } + + bool is_single() const override + { + return varray_.is_single(); + } + + void get_internal_single(void *r_value) const override + { + *(T *)r_value = varray_.get_internal_single(); + } + + void materialize(const IndexMask mask, void *dst) const override + { + varray_.materialize(mask, MutableSpan((T *)dst, mask.min_array_size())); + } + + void materialize_to_uninitialized(const IndexMask mask, void *dst) const override + { + varray_.materialize_to_uninitialized(mask, MutableSpan((T *)dst, mask.min_array_size())); + } + + bool try_assign_VArray(void *varray) const override + { + *(VArray<T> *)varray = varray_; + return true; + } + + bool may_have_ownership() const override + { + return varray_.may_have_ownership(); + } +}; + +/* Used to convert any generic virtual array into a typed one. */ +template<typename T> class VArrayImpl_For_GVArray : public VArrayImpl<T> { + protected: + GVArray varray_; + + public: + VArrayImpl_For_GVArray(GVArray varray) : VArrayImpl<T>(varray.size()), varray_(std::move(varray)) + { + BLI_assert(varray_); + BLI_assert(varray_.type().template is<T>()); + } + + protected: + T get(const int64_t index) const override + { + T value; + varray_.get(index, &value); + return value; + } + + bool is_span() const override + { + return varray_.is_span(); + } + + Span<T> get_internal_span() const override + { + return varray_.get_internal_span().template typed<T>(); + } + + bool is_single() const override + { + return varray_.is_single(); + } + + T get_internal_single() const override + { + T value; + varray_.get_internal_single(&value); + return value; + } + + bool try_assign_GVArray(GVArray &varray) const override + { + varray = varray_; + return true; + } + + bool may_have_ownership() const override + { + return varray_.may_have_ownership(); + } +}; + +/* Used to convert any typed virtual mutable array into a generic one. */ +template<typename T> class GVMutableArrayImpl_For_VMutableArray : public GVMutableArrayImpl { + protected: + VMutableArray<T> varray_; + + public: + GVMutableArrayImpl_For_VMutableArray(VMutableArray<T> varray) + : GVMutableArrayImpl(CPPType::get<T>(), varray.size()), varray_(std::move(varray)) + { + } + + protected: + void get(const int64_t index, void *r_value) const override + { + *(T *)r_value = varray_[index]; + } + + void get_to_uninitialized(const int64_t index, void *r_value) const override + { + new (r_value) T(varray_[index]); + } + + bool is_span() const override + { + return varray_.is_span(); + } + + GSpan get_internal_span() const override + { + Span<T> span = varray_.get_internal_span(); + return span; + } + + bool is_single() const override + { + return varray_.is_single(); + } + + void get_internal_single(void *r_value) const override + { + *(T *)r_value = varray_.get_internal_single(); + } + + void set_by_copy(const int64_t index, const void *value) override + { + const T &value_ = *(const T *)value; + varray_.set(index, value_); + } + + void set_by_relocate(const int64_t index, void *value) override + { + T &value_ = *(T *)value; + varray_.set(index, std::move(value_)); + value_.~T(); + } + + void set_by_move(const int64_t index, void *value) override + { + T &value_ = *(T *)value; + varray_.set(index, std::move(value_)); + } + + void set_all(const void *src) override + { + varray_.set_all(Span((T *)src, size_)); + } + + void materialize(const IndexMask mask, void *dst) const override + { + varray_.materialize(mask, MutableSpan((T *)dst, mask.min_array_size())); + } + + void materialize_to_uninitialized(const IndexMask mask, void *dst) const override + { + varray_.materialize_to_uninitialized(mask, MutableSpan((T *)dst, mask.min_array_size())); + } + + bool try_assign_VArray(void *varray) const override + { + *(VArray<T> *)varray = varray_; + return true; + } + + bool try_assign_VMutableArray(void *varray) const override + { + *(VMutableArray<T> *)varray = varray_; + return true; + } + + bool may_have_ownership() const override + { + return varray_.may_have_ownership(); + } +}; + +/* Used to convert an generic mutable virtual array into a typed one. */ +template<typename T> class VMutableArrayImpl_For_GVMutableArray : public VMutableArrayImpl<T> { + protected: + GVMutableArray varray_; + + public: + VMutableArrayImpl_For_GVMutableArray(GVMutableArray varray) + : VMutableArrayImpl<T>(varray.size()), varray_(varray) + { + BLI_assert(varray_); + BLI_assert(varray_.type().template is<T>()); + } + + private: + T get(const int64_t index) const override + { + T value; + varray_.get(index, &value); + return value; + } + + void set(const int64_t index, T value) override + { + varray_.set_by_relocate(index, &value); + } + + bool is_span() const override + { + return varray_.is_span(); + } + + Span<T> get_internal_span() const override + { + return varray_.get_internal_span().template typed<T>(); + } + + bool is_single() const override + { + return varray_.is_single(); + } + + T get_internal_single() const override + { + T value; + varray_.get_internal_single(&value); + return value; + } + + bool try_assign_GVArray(GVArray &varray) const override + { + varray = varray_; + return true; + } + + bool try_assign_GVMutableArray(GVMutableArray &varray) const override + { + varray = varray_; + return true; + } + + bool may_have_ownership() const override + { + return varray_.may_have_ownership(); + } +}; + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #GVArrayImpl_For_GSpan. + * \{ */ + +class GVArrayImpl_For_GSpan : public GVMutableArrayImpl { + protected: + void *data_ = nullptr; + const int64_t element_size_; + + public: + GVArrayImpl_For_GSpan(const GMutableSpan span); + + protected: + GVArrayImpl_For_GSpan(const CPPType &type, int64_t size); + + public: + void get(int64_t index, void *r_value) const override; + void get_to_uninitialized(int64_t index, void *r_value) const override; + + void set_by_copy(int64_t index, const void *value) override; + void set_by_move(int64_t index, void *value) override; + void set_by_relocate(int64_t index, void *value) override; + + bool is_span() const override; + GSpan get_internal_span() const override; +}; + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Inline methods for #GVArrayImpl. + * \{ */ + +inline GVArrayImpl::GVArrayImpl(const CPPType &type, const int64_t size) + : type_(&type), size_(size) +{ + BLI_assert(size_ >= 0); +} + +inline const CPPType &GVArrayImpl::type() const +{ + return *type_; +} + +inline int64_t GVArrayImpl::size() const +{ + return size_; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Inline methods for #GVMutableArrayImpl. + * \{ */ + +inline void GVMutableArray::set_by_copy(const int64_t index, const void *value) +{ + BLI_assert(index >= 0); + BLI_assert(index < this->size()); + this->get_impl()->set_by_copy(index, value); +} + +inline void GVMutableArray::set_by_move(const int64_t index, void *value) +{ + BLI_assert(index >= 0); + BLI_assert(index < this->size()); + this->get_impl()->set_by_move(index, value); +} + +inline void GVMutableArray::set_by_relocate(const int64_t index, void *value) +{ + BLI_assert(index >= 0); + BLI_assert(index < this->size()); + this->get_impl()->set_by_relocate(index, value); +} + +template<typename T> +inline bool GVMutableArray::try_assign_VMutableArray(VMutableArray<T> &varray) const +{ + BLI_assert(impl_->type().is<T>()); + return this->get_impl()->try_assign_VMutableArray(&varray); +} + +inline GVMutableArrayImpl *GVMutableArray::get_impl() const +{ + return (GVMutableArrayImpl *)impl_; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Inline methods for #GVArrayCommon. + * \{ */ + +template<typename ImplT, typename... Args> inline void GVArrayCommon::emplace(Args &&...args) +{ + static_assert(std::is_base_of_v<GVArrayImpl, ImplT>); + if constexpr (std::is_copy_constructible_v<ImplT> && Storage::template is_inline_v<ImplT>) { + impl_ = &storage_.template emplace<ImplT>(std::forward<Args>(args)...); + } + else { + std::shared_ptr<const GVArrayImpl> ptr = std::make_shared<ImplT>(std::forward<Args>(args)...); + impl_ = &*ptr; + storage_ = std::move(ptr); + } +} + +/* Copies the value at the given index into the provided storage. The `r_value` pointer is + * expected to point to initialized memory. */ +inline void GVArrayCommon::get(const int64_t index, void *r_value) const +{ + BLI_assert(index >= 0); + BLI_assert(index < this->size()); + impl_->get(index, r_value); +} + +template<typename T> inline T GVArrayCommon::get(const int64_t index) const +{ + BLI_assert(index >= 0); + BLI_assert(index < this->size()); + BLI_assert(this->type().is<T>()); + T value{}; + impl_->get(index, &value); + return value; +} + +/* Same as `get`, but `r_value` is expected to point to uninitialized memory. */ +inline void GVArrayCommon::get_to_uninitialized(const int64_t index, void *r_value) const +{ + BLI_assert(index >= 0); + BLI_assert(index < this->size()); + impl_->get_to_uninitialized(index, r_value); +} + +template<typename T> inline bool GVArrayCommon::try_assign_VArray(VArray<T> &varray) const +{ + BLI_assert(impl_->type().is<T>()); + return impl_->try_assign_VArray(&varray); +} + +inline const CPPType &GVArrayCommon::type() const +{ + return impl_->type(); +} + +inline GVArrayCommon::operator bool() const +{ + return impl_ != nullptr; +} + +inline int64_t GVArrayCommon::size() const +{ + if (impl_ == nullptr) { + return 0; + } + return impl_->size(); +} + +inline bool GVArrayCommon::is_empty() const +{ + return this->size() == 0; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Inline methods for #GVArray. + * \{ */ + +namespace detail { +template<typename StorageT> inline GVArrayAnyExtraInfo GVArrayAnyExtraInfo::get() +{ + static_assert(std::is_base_of_v<GVArrayImpl, StorageT> || + is_same_any_v<StorageT, const GVArrayImpl *, std::shared_ptr<const GVArrayImpl>>); + + if constexpr (std::is_base_of_v<GVArrayImpl, StorageT>) { + return {[](const void *buffer) { + return static_cast<const GVArrayImpl *>((const StorageT *)buffer); + }}; + } + else if constexpr (std::is_same_v<StorageT, const GVArrayImpl *>) { + return {[](const void *buffer) { return *(const StorageT *)buffer; }}; + } + else if constexpr (std::is_same_v<StorageT, std::shared_ptr<const GVArrayImpl>>) { + return {[](const void *buffer) { return ((const StorageT *)buffer)->get(); }}; + } + else { + BLI_assert_unreachable(); + return {}; + } +} +} // namespace detail + +template<typename ImplT, typename... Args> inline GVArray GVArray::For(Args &&...args) +{ + static_assert(std::is_base_of_v<GVArrayImpl, ImplT>); + GVArray varray; + varray.template emplace<ImplT>(std::forward<Args>(args)...); + return varray; +} + +template<typename T> inline GVArray::GVArray(const VArray<T> &varray) +{ + if (!varray) { + return; + } + if (varray.try_assign_GVArray(*this)) { + return; + } + /* Need to check this before the span/single special cases, because otherwise we might loose + * ownership to the referenced data when #varray goes out of scope. */ + if (varray.may_have_ownership()) { + *this = GVArray::For<GVArrayImpl_For_VArray<T>>(varray); + } + else if (varray.is_span()) { + Span<T> data = varray.get_internal_span(); + *this = GVArray::ForSpan(data); + } + else if (varray.is_single()) { + T value = varray.get_internal_single(); + *this = GVArray::ForSingle(CPPType::get<T>(), varray.size(), &value); + } + else { + *this = GVArray::For<GVArrayImpl_For_VArray<T>>(varray); + } +} + +template<typename T> inline VArray<T> GVArray::typed() const +{ + if (!*this) { + return {}; + } + BLI_assert(impl_->type().is<T>()); + VArray<T> varray; + if (this->try_assign_VArray(varray)) { + return varray; + } + if (this->may_have_ownership()) { + return VArray<T>::template For<VArrayImpl_For_GVArray<T>>(*this); + } + if (this->is_span()) { + const Span<T> span = this->get_internal_span().typed<T>(); + return VArray<T>::ForSpan(span); + } + if (this->is_single()) { + T value; + this->get_internal_single(&value); + return VArray<T>::ForSingle(value, this->size()); + } + return VArray<T>::template For<VArrayImpl_For_GVArray<T>>(*this); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Inline methods for #GVMutableArray. + * \{ */ + +template<typename ImplT, typename... Args> +inline GVMutableArray GVMutableArray::For(Args &&...args) +{ + static_assert(std::is_base_of_v<GVMutableArrayImpl, ImplT>); + GVMutableArray varray; + varray.emplace<ImplT>(std::forward<Args>(args)...); + return varray; +} + +template<typename T> inline GVMutableArray::GVMutableArray(const VMutableArray<T> &varray) +{ + if (!varray) { + return; + } + if (varray.try_assign_GVMutableArray(*this)) { + return; + } + if (varray.may_have_ownership()) { + *this = GVMutableArray::For<GVMutableArrayImpl_For_VMutableArray<T>>(varray); + } + else if (varray.is_span()) { + MutableSpan<T> data = varray.get_internal_span(); + *this = GVMutableArray::ForSpan(data); + } + else { + *this = GVMutableArray::For<GVMutableArrayImpl_For_VMutableArray<T>>(varray); + } +} + +template<typename T> inline VMutableArray<T> GVMutableArray::typed() const +{ + if (!*this) { + return {}; + } + BLI_assert(this->type().is<T>()); + VMutableArray<T> varray; + if (this->try_assign_VMutableArray(varray)) { + return varray; + } + if (this->may_have_ownership()) { + return VMutableArray<T>::template For<VMutableArrayImpl_For_GVMutableArray<T>>(*this); + } + if (this->is_span()) { + const MutableSpan<T> span = this->get_internal_span().typed<T>(); + return VMutableArray<T>::ForSpan(span); + } + return VMutableArray<T>::template For<VMutableArrayImpl_For_GVMutableArray<T>>(*this); +} + +/** \} */ + +} // namespace blender |