diff options
Diffstat (limited to 'source/blender/functions/FN_generic_virtual_array.hh')
-rw-r--r-- | source/blender/functions/FN_generic_virtual_array.hh | 1252 |
1 files changed, 578 insertions, 674 deletions
diff --git a/source/blender/functions/FN_generic_virtual_array.hh b/source/blender/functions/FN_generic_virtual_array.hh index 8aad017e68b..5d33a05a693 100644 --- a/source/blender/functions/FN_generic_virtual_array.hh +++ b/source/blender/functions/FN_generic_virtual_array.hh @@ -23,8 +23,7 @@ * the data type is only known at runtime. */ -#include <optional> - +#include "BLI_timeit.hh" #include "BLI_virtual_array.hh" #include "FN_generic_array.hh" @@ -32,940 +31,845 @@ namespace blender::fn { -template<typename T> class GVArray_Typed; -template<typename T> class GVMutableArray_Typed; +/* -------------------------------------------------------------------- */ +/** \name #GVArrayImpl and #GVMutableArrayImpl. + * \{ */ class GVArray; +class GVArrayImpl; class GVMutableArray; +class GVMutableArrayImpl; -using GVArrayPtr = std::unique_ptr<GVArray>; -using GVMutableArrayPtr = std::unique_ptr<GVMutableArray>; - -/* A generically typed version of `VArray<T>`. */ -class GVArray { +/* A generically typed version of #VArrayImpl. */ +class GVArrayImpl { protected: const CPPType *type_; int64_t size_; public: - GVArray(const CPPType &type, const int64_t size) : type_(&type), size_(size) - { - BLI_assert(size_ >= 0); - } + GVArrayImpl(const CPPType &type, const int64_t size); + virtual ~GVArrayImpl() = default; - virtual ~GVArray() = default; + const CPPType &type() const; - const CPPType &type() const - { - return *type_; - } + int64_t size() const; - int64_t size() const - { - return size_; - } + virtual void get(const int64_t index, void *r_value) const; + virtual void get_to_uninitialized(const int64_t index, void *r_value) const = 0; - bool is_empty() const - { - return size_ == 0; - } + virtual bool is_span() const; + virtual GSpan get_internal_span() const; - /* Copies the value at the given index into the provided storage. The `r_value` pointer is - * expected to point to initialized memory. */ - void get(const int64_t index, void *r_value) const - { - BLI_assert(index >= 0); - BLI_assert(index < size_); - this->get_impl(index, r_value); - } + virtual bool is_single() const; + virtual void get_internal_single(void *UNUSED(r_value)) const; - /* Same as `get`, but `r_value` is expected to point to uninitialized memory. */ - void get_to_uninitialized(const int64_t index, void *r_value) const - { - BLI_assert(index >= 0); - BLI_assert(index < size_); - this->get_to_uninitialized_impl(index, r_value); - } + virtual void materialize(const IndexMask mask, void *dst) const; + virtual 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 - { - if (size_ == 0) { - return true; - } - return this->is_span_impl(); - } + virtual bool try_assign_VArray(void *varray) const; + virtual bool may_have_ownership() const; +}; - /* Returns the internally used span of the virtual array. This invokes undefined behavior is the - * virtual array is not stored as a span internally. */ - GSpan get_internal_span() const - { - BLI_assert(this->is_span()); - if (size_ == 0) { - return GSpan(*type_); - } - return this->get_internal_span_impl(); - } +/* A generic version of #VMutableArrayImpl. */ +class GVMutableArrayImpl : public GVArrayImpl { + public: + GVMutableArrayImpl(const CPPType &type, const int64_t size); - /* Returns true when the virtual array returns the same value for every index. */ - bool is_single() const - { - if (size_ == 1) { - return true; - } - return this->is_single_impl(); - } + virtual void set_by_copy(const int64_t index, const void *value); + virtual void set_by_relocate(const int64_t index, void *value); + virtual void set_by_move(const int64_t index, void *value) = 0; - /* 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 - { - BLI_assert(this->is_single()); - if (size_ == 1) { - this->get(0, r_value); - return; - } - this->get_internal_single_impl(r_value); - } + virtual void set_all(const void *src); - /* Same as `get_internal_single`, but `r_value` points to initialized memory. */ - void get_internal_single_to_uninitialized(void *r_value) const - { - type_->default_construct(r_value); - this->get_internal_single(r_value); - } + virtual bool try_assign_VMutableArray(void *varray) 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; +/* -------------------------------------------------------------------- */ +/** \name #GVArray and #GVMutableArray + * \{ */ - template<typename T> const VArray<T> *try_get_internal_varray() const - { - BLI_assert(type_->is<T>()); - return (const VArray<T> *)this->try_get_internal_varray_impl(); - } +namespace detail { +struct GVArrayAnyExtraInfo { + const GVArrayImpl *(*get_varray)(const void *buffer) = + [](const void *UNUSED(buffer)) -> const GVArrayImpl * { return nullptr; }; - /* Create a typed virtual array for this generic virtual array. */ - template<typename T> GVArray_Typed<T> typed() const - { - return GVArray_Typed<T>(*this); - } + template<typename StorageT> static GVArrayAnyExtraInfo get(); +}; +} // namespace detail - GVArrayPtr shallow_copy() const; +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: - virtual void get_impl(const int64_t index, void *r_value) const; - virtual void get_to_uninitialized_impl(const int64_t index, void *r_value) const = 0; + /** + * 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>; - virtual bool is_span_impl() const; - virtual GSpan get_internal_span_impl() const; + const GVArrayImpl *impl_ = nullptr; + Storage storage_; - virtual bool is_single_impl() const; - virtual void get_internal_single_impl(void *UNUSED(r_value)) const; + protected: + GVArrayCommon(); + GVArrayCommon(const GVArrayCommon &other); + GVArrayCommon(GVArrayCommon &&other) noexcept; + GVArrayCommon(const GVArrayImpl *impl); + GVArrayCommon(std::shared_ptr<const GVArrayImpl> impl); + ~GVArrayCommon(); - virtual void materialize_impl(const IndexMask mask, void *dst) const; - virtual void materialize_to_uninitialized_impl(const IndexMask mask, void *dst) const; + template<typename ImplT, typename... Args> void emplace(Args &&...args); - virtual const void *try_get_internal_varray_impl() const; -}; + void copy_from(const GVArrayCommon &other); + void move_from(GVArrayCommon &&other) noexcept; + + const GVArrayImpl *impl_from_storage() const; -/* Similar to GVArray, but supports changing the elements in the virtual array. */ -class GVMutableArray : public GVArray { public: - GVMutableArray(const CPPType &type, const int64_t size) : GVArray(type, size) - { - } + const CPPType &type() const; + operator bool() const; - void set_by_copy(const int64_t index, const void *value) - { - BLI_assert(index >= 0); - BLI_assert(index < size_); - this->set_by_copy_impl(index, value); - } + int64_t size() const; + bool is_empty() const; + IndexRange index_range() const; - void set_by_move(const int64_t index, void *value) - { - BLI_assert(index >= 0); - BLI_assert(index < size_); - this->set_by_move_impl(index, value); - } + template<typename T> bool try_assign_VArray(VArray<T> &varray) const; + bool may_have_ownership() const; - void set_by_relocate(const int64_t index, void *value) - { - BLI_assert(index >= 0); - BLI_assert(index < size_); - this->set_by_relocate_impl(index, value); - } + void materialize(void *dst) const; + void materialize(const IndexMask mask, void *dst) const; - GMutableSpan get_internal_span() - { - BLI_assert(this->is_span()); - GSpan span = static_cast<const GVArray *>(this)->get_internal_span(); - return GMutableSpan(span.type(), const_cast<void *>(span.data()), span.size()); - } + void materialize_to_uninitialized(void *dst) const; + void materialize_to_uninitialized(const IndexMask mask, void *dst) const; - template<typename T> VMutableArray<T> *try_get_internal_mutable_varray() - { - BLI_assert(type_->is<T>()); - return (VMutableArray<T> *)this->try_get_internal_mutable_varray_impl(); - } + bool is_span() const; + GSpan get_internal_span() const; - /* Create a typed virtual array for this generic virtual array. */ - template<typename T> GVMutableArray_Typed<T> typed() - { - return GVMutableArray_Typed<T>(*this); - } + bool is_single() const; + void get_internal_single(void *r_value) const; + void get_internal_single_to_uninitialized(void *r_value) const; - void fill(const void *value); + void get(const int64_t index, void *r_value) const; + void get_to_uninitialized(const int64_t index, void *r_value) const; +}; - /* Copy the values from the source buffer to all elements in the virtual array. */ - void set_all(const void *src) - { - this->set_all_impl(src); - } +/** Generic version of #VArray. */ +class GVArray : public GVArrayCommon { + private: + friend GVMutableArray; - protected: - virtual void set_by_copy_impl(const int64_t index, const void *value); - virtual void set_by_relocate_impl(const int64_t index, void *value); - virtual void set_by_move_impl(const int64_t index, void *value) = 0; + public: + GVArray() = default; - virtual void set_all_impl(const void *src); + GVArray(const GVArray &other); + GVArray(GVArray &&other) noexcept; + GVArray(const GVArrayImpl *impl); + GVArray(std::shared_ptr<const GVArrayImpl> impl); - virtual void *try_get_internal_mutable_varray_impl(); -}; + template<typename T> GVArray(const VArray<T> &varray); + template<typename T> VArray<T> typed() const; -class GVArray_For_GSpan : public GVArray { - protected: - const void *data_ = nullptr; - const int64_t element_size_; + template<typename ImplT, typename... Args> static GVArray For(Args &&...args); - public: - GVArray_For_GSpan(const GSpan span) - : GVArray(span.type(), span.size()), data_(span.data()), element_size_(span.type().size()) - { - } + static GVArray ForSingle(const CPPType &type, const int64_t size, const void *value); + static GVArray ForSingleRef(const CPPType &type, const int64_t size, const void *value); + static GVArray ForSingleDefault(const CPPType &type, const int64_t size); + static GVArray ForSpan(GSpan span); + static GVArray ForGArray(GArray<> array); + static GVArray ForEmpty(const CPPType &type); - protected: - GVArray_For_GSpan(const CPPType &type, const int64_t size) - : GVArray(type, size), element_size_(type.size()) - { - } + GVArray slice(IndexRange slice) const; - void get_impl(const int64_t index, void *r_value) const override; - void get_to_uninitialized_impl(const int64_t index, void *r_value) const override; + GVArray &operator=(const GVArray &other); + GVArray &operator=(GVArray &&other) noexcept; - bool is_span_impl() const override; - GSpan get_internal_span_impl() const override; + const GVArrayImpl *get_implementation() const + { + return impl_; + } }; -class GVArray_For_Empty : public GVArray { +/** Generic version of #VMutableArray. */ +class GVMutableArray : public GVArrayCommon { public: - GVArray_For_Empty(const CPPType &type) : GVArray(type, 0) - { - } + GVMutableArray() = default; + GVMutableArray(const GVMutableArray &other); + GVMutableArray(GVMutableArray &&other) noexcept; + GVMutableArray(GVMutableArrayImpl *impl); + GVMutableArray(std::shared_ptr<GVMutableArrayImpl> impl); - protected: - void get_to_uninitialized_impl(const int64_t UNUSED(index), void *UNUSED(r_value)) const override - { - BLI_assert(false); - } -}; + template<typename T> GVMutableArray(const VMutableArray<T> &varray); + template<typename T> VMutableArray<T> typed() const; -class GVMutableArray_For_GMutableSpan : public GVMutableArray { - protected: - void *data_ = nullptr; - const int64_t element_size_; + template<typename ImplT, typename... Args> static GVMutableArray For(Args &&...args); - public: - GVMutableArray_For_GMutableSpan(const GMutableSpan span) - : GVMutableArray(span.type(), span.size()), - data_(span.data()), - element_size_(span.type().size()) - { - } + static GVMutableArray ForSpan(GMutableSpan span); - protected: - GVMutableArray_For_GMutableSpan(const CPPType &type, const int64_t size) - : GVMutableArray(type, size), element_size_(type.size()) - { - } + operator GVArray() const &; + operator GVArray() &&noexcept; - void get_impl(const int64_t index, void *r_value) const override; - void get_to_uninitialized_impl(const int64_t index, void *r_value) const override; + GVMutableArray &operator=(const GVMutableArray &other); + GVMutableArray &operator=(GVMutableArray &&other) noexcept; - void set_by_copy_impl(const int64_t index, const void *value) override; - void set_by_move_impl(const int64_t index, void *value) override; - void set_by_relocate_impl(const int64_t index, void *value) override; + GMutableSpan get_internal_span() const; - bool is_span_impl() const override; - GSpan get_internal_span_impl() const override; -}; + template<typename T> bool try_assign_VMutableArray(VMutableArray<T> &varray) const; -/* Generic virtual array where each element has the same value. The value is not owned. */ -class GVArray_For_SingleValueRef : public GVArray { - protected: - const void *value_ = nullptr; + void set_by_copy(const int64_t index, const void *value); + void set_by_move(const int64_t index, void *value); + void set_by_relocate(const int64_t index, void *value); - public: - GVArray_For_SingleValueRef(const CPPType &type, const int64_t size, const void *value) - : GVArray(type, size), value_(value) - { - } + void fill(const void *value); + void set_all(const void *src); - protected: - GVArray_For_SingleValueRef(const CPPType &type, const int64_t size) : GVArray(type, size) - { - } + GVMutableArrayImpl *get_implementation() const; - void get_impl(const int64_t index, void *r_value) const override; - void get_to_uninitialized_impl(const int64_t index, void *r_value) const override; + private: + GVMutableArrayImpl *get_impl() const; +}; - bool is_span_impl() const override; - GSpan get_internal_span_impl() const override; +/** \} */ - bool is_single_impl() const override; - void get_internal_single_impl(void *r_value) const override; +/* -------------------------------------------------------------------- */ +/** \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(); }; -/* Same as GVArray_For_SingleValueRef, but the value is owned. */ -class GVArray_For_SingleValue : public GVArray_For_SingleValueRef { +/* 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: - GVArray_For_SingleValue(const CPPType &type, const int64_t size, const void *value); - ~GVArray_For_SingleValue(); + 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 GVArray_For_VArray : public GVArray { +template<typename T> class GVArrayImpl_For_VArray : public GVArrayImpl { protected: - const VArray<T> *varray_ = nullptr; + VArray<T> varray_; public: - GVArray_For_VArray(const VArray<T> &varray) - : GVArray(CPPType::get<T>(), varray.size()), varray_(&varray) + GVArrayImpl_For_VArray(VArray<T> varray) + : GVArrayImpl(CPPType::get<T>(), varray.size()), varray_(std::move(varray)) { } protected: - GVArray_For_VArray(const int64_t size) : GVArray(CPPType::get<T>(), size) + void get(const int64_t index, void *r_value) const override { + *(T *)r_value = varray_[index]; } - void get_impl(const int64_t index, void *r_value) const override + void get_to_uninitialized(const int64_t index, void *r_value) const override { - *(T *)r_value = varray_->get(index); + new (r_value) T(varray_[index]); } - void get_to_uninitialized_impl(const int64_t index, void *r_value) const override + bool is_span() const override { - new (r_value) T(varray_->get(index)); + return varray_.is_span(); } - bool is_span_impl() const override + GSpan get_internal_span() const override { - return varray_->is_span(); + return GSpan(varray_.get_internal_span()); } - GSpan get_internal_span_impl() const override + bool is_single() const override { - return GSpan(varray_->get_internal_span()); + return varray_.is_single(); } - bool is_single_impl() const override + void get_internal_single(void *r_value) const override { - return varray_->is_single(); + *(T *)r_value = varray_.get_internal_single(); } - void get_internal_single_impl(void *r_value) const override + void materialize(const IndexMask mask, void *dst) const override { - *(T *)r_value = varray_->get_internal_single(); + varray_.materialize(mask, MutableSpan((T *)dst, mask.min_array_size())); } - void materialize_impl(const IndexMask mask, void *dst) const override + void materialize_to_uninitialized(const IndexMask mask, void *dst) const override { - varray_->materialize(mask, MutableSpan((T *)dst, mask.min_array_size())); + varray_.materialize_to_uninitialized(mask, MutableSpan((T *)dst, mask.min_array_size())); } - void materialize_to_uninitialized_impl(const IndexMask mask, void *dst) const override + bool try_assign_VArray(void *varray) const override { - varray_->materialize_to_uninitialized(mask, MutableSpan((T *)dst, mask.min_array_size())); + *(VArray<T> *)varray = varray_; + return true; } - const void *try_get_internal_varray_impl() const override - { - return varray_; - } -}; - -class GVArray_For_GArray : public GVArray_For_GSpan { - protected: - GArray<> array_; - - public: - GVArray_For_GArray(GArray<> array) : GVArray_For_GSpan(array.as_span()), array_(std::move(array)) + 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 VArray_For_GVArray : public VArray<T> { +template<typename T> class VArrayImpl_For_GVArray : public VArrayImpl<T> { protected: - const GVArray *varray_ = nullptr; + GVArray varray_; public: - VArray_For_GVArray(const GVArray &varray) : VArray<T>(varray.size()), varray_(&varray) + VArrayImpl_For_GVArray(GVArray varray) : VArrayImpl<T>(varray.size()), varray_(std::move(varray)) { - BLI_assert(varray_->type().template is<T>()); + BLI_assert(varray_); + BLI_assert(varray_.type().is<T>()); } protected: - VArray_For_GVArray(const int64_t size) : VArray<T>(size) - { - } - - T get_impl(const int64_t index) const override + T get(const int64_t index) const override { T value; - varray_->get(index, &value); + varray_.get(index, &value); return value; } - bool is_span_impl() const override + bool is_span() const override { - return varray_->is_span(); + return varray_.is_span(); } - Span<T> get_internal_span_impl() const override + Span<T> get_internal_span() const override { - return varray_->get_internal_span().template typed<T>(); + return varray_.get_internal_span().template typed<T>(); } - bool is_single_impl() const override + bool is_single() const override { - return varray_->is_single(); + return varray_.is_single(); } - T get_internal_single_impl() const override + T get_internal_single() const override { T value; - varray_->get_internal_single(&value); + varray_.get_internal_single(&value); return value; } -}; - -/* Used to convert an generic mutable virtual array into a typed one. */ -template<typename T> class VMutableArray_For_GVMutableArray : public VMutableArray<T> { - protected: - GVMutableArray *varray_ = nullptr; - - public: - VMutableArray_For_GVMutableArray(GVMutableArray &varray) - : VMutableArray<T>(varray.size()), varray_(&varray) - { - BLI_assert(varray.type().template is<T>()); - } - - VMutableArray_For_GVMutableArray(const int64_t size) : VMutableArray<T>(size) - { - } - - private: - T get_impl(const int64_t index) const override - { - T value; - varray_->get(index, &value); - return value; - } - - void set_impl(const int64_t index, T value) override - { - varray_->set_by_relocate(index, &value); - } - - bool is_span_impl() const override - { - return varray_->is_span(); - } - Span<T> get_internal_span_impl() const override + bool try_assign_GVArray(GVArray &varray) const override { - return varray_->get_internal_span().template typed<T>(); + varray = varray_; + return true; } - bool is_single_impl() const override + bool may_have_ownership() const override { - return varray_->is_single(); - } - - T get_internal_single_impl() const override - { - T value; - varray_->get_internal_single(&value); - return value; + return varray_.may_have_ownership(); } }; /* Used to convert any typed virtual mutable array into a generic one. */ -template<typename T> class GVMutableArray_For_VMutableArray : public GVMutableArray { +template<typename T> class GVMutableArrayImpl_For_VMutableArray : public GVMutableArrayImpl { protected: - VMutableArray<T> *varray_ = nullptr; + VMutableArray<T> varray_; public: - GVMutableArray_For_VMutableArray(VMutableArray<T> &varray) - : GVMutableArray(CPPType::get<T>(), varray.size()), varray_(&varray) + GVMutableArrayImpl_For_VMutableArray(VMutableArray<T> varray) + : GVMutableArrayImpl(CPPType::get<T>(), varray.size()), varray_(std::move(varray)) { } protected: - GVMutableArray_For_VMutableArray(const int64_t size) : GVMutableArray(CPPType::get<T>(), size) + void get(const int64_t index, void *r_value) const override { + *(T *)r_value = varray_[index]; } - void get_impl(const int64_t index, void *r_value) const override + void get_to_uninitialized(const int64_t index, void *r_value) const override { - *(T *)r_value = varray_->get(index); + new (r_value) T(varray_[index]); } - void get_to_uninitialized_impl(const int64_t index, void *r_value) const override + bool is_span() const override { - new (r_value) T(varray_->get(index)); + return varray_.is_span(); } - bool is_span_impl() const override + GSpan get_internal_span() const override { - return varray_->is_span(); - } - - GSpan get_internal_span_impl() const override - { - Span<T> span = varray_->get_internal_span(); + Span<T> span = varray_.get_internal_span(); return span; } - bool is_single_impl() const override + bool is_single() const override { - return varray_->is_single(); + return varray_.is_single(); } - void get_internal_single_impl(void *r_value) const override + void get_internal_single(void *r_value) const override { - *(T *)r_value = varray_->get_internal_single(); + *(T *)r_value = varray_.get_internal_single(); } - void set_by_copy_impl(const int64_t index, const void *value) override + void set_by_copy(const int64_t index, const void *value) override { const T &value_ = *(const T *)value; - varray_->set(index, value_); + varray_.set(index, value_); } - void set_by_relocate_impl(const int64_t index, void *value) override + void set_by_relocate(const int64_t index, void *value) override { T &value_ = *(T *)value; - varray_->set(index, std::move(value_)); + varray_.set(index, std::move(value_)); value_.~T(); } - void set_by_move_impl(const int64_t index, void *value) override + void set_by_move(const int64_t index, void *value) override { T &value_ = *(T *)value; - varray_->set(index, std::move(value_)); + varray_.set(index, std::move(value_)); } - void set_all_impl(const void *src) override + void set_all(const void *src) override { - varray_->set_all(Span((T *)src, size_)); + varray_.set_all(Span((T *)src, size_)); } - void materialize_impl(const IndexMask mask, void *dst) const override + void materialize(const IndexMask mask, void *dst) const override { - varray_->materialize(mask, MutableSpan((T *)dst, mask.min_array_size())); + varray_.materialize(mask, MutableSpan((T *)dst, mask.min_array_size())); } - void materialize_to_uninitialized_impl(const IndexMask mask, void *dst) const override + void materialize_to_uninitialized(const IndexMask mask, void *dst) const override { - varray_->materialize_to_uninitialized(mask, MutableSpan((T *)dst, mask.min_array_size())); + varray_.materialize_to_uninitialized(mask, MutableSpan((T *)dst, mask.min_array_size())); } - const void *try_get_internal_varray_impl() const override + bool try_assign_VArray(void *varray) const override { - return (const VArray<T> *)varray_; + *(VArray<T> *)varray = varray_; + return true; } - void *try_get_internal_mutable_varray_impl() override + bool try_assign_VMutableArray(void *varray) const override { - return varray_; + *(VMutableArray<T> *)varray = varray_; + return true; } -}; - -/* A generic version of VArray_Span. */ -class GVArray_GSpan : public GSpan { - private: - const GVArray &varray_; - void *owned_data_ = nullptr; - - public: - GVArray_GSpan(const 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(); -}; - -/* Similar to GVArray_GSpan, but the resulting span is typed. */ -template<typename T> class GVArray_Span : public Span<T> { - private: - GVArray_GSpan varray_gspan_; - public: - GVArray_Span(const GVArray &varray) : varray_gspan_(varray) + bool may_have_ownership() const override { - BLI_assert(varray.type().is<T>()); - this->data_ = (const T *)varray_gspan_.data(); - this->size_ = varray_gspan_.size(); + return varray_.may_have_ownership(); } }; -template<typename T> class GVArray_For_OwnedVArray : public GVArray_For_VArray<T> { - private: - VArrayPtr<T> owned_varray_; +/* 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: - /* Takes ownership of varray and passes a reference to the base class. */ - GVArray_For_OwnedVArray(VArrayPtr<T> varray) - : GVArray_For_VArray<T>(*varray), owned_varray_(std::move(varray)) + VMutableArrayImpl_For_GVMutableArray(GVMutableArray varray) + : VMutableArrayImpl<T>(varray.size()), varray_(varray) { + BLI_assert(varray_); + BLI_assert(varray_.type().is<T>()); } -}; -template<typename T> class VArray_For_OwnedGVArray : public VArray_For_GVArray<T> { private: - GVArrayPtr owned_varray_; - - public: - /* Takes ownership of varray and passes a reference to the base class. */ - VArray_For_OwnedGVArray(GVArrayPtr varray) - : VArray_For_GVArray<T>(*varray), owned_varray_(std::move(varray)) + T get(const int64_t index) const override { + T value; + varray_.get(index, &value); + return value; } -}; -template<typename T> -class GVMutableArray_For_OwnedVMutableArray : public GVMutableArray_For_VMutableArray<T> { - private: - VMutableArrayPtr<T> owned_varray_; - - public: - /* Takes ownership of varray and passes a reference to the base class. */ - GVMutableArray_For_OwnedVMutableArray(VMutableArrayPtr<T> varray) - : GVMutableArray_For_VMutableArray<T>(*varray), owned_varray_(std::move(varray)) + void set(const int64_t index, T value) override { + varray_.set_by_relocate(index, &value); } -}; -template<typename T> -class VMutableArray_For_OwnedGVMutableArray : public VMutableArray_For_GVMutableArray<T> { - private: - GVMutableArrayPtr owned_varray_; - - public: - /* Takes ownership of varray and passes a reference to the base class. */ - VMutableArray_For_OwnedGVMutableArray(GVMutableArrayPtr varray) - : VMutableArray_For_GVMutableArray<T>(*varray), owned_varray_(std::move(varray)) + bool is_span() const override { + return varray_.is_span(); } -}; - -/* Utility to embed a typed virtual array into a generic one. This avoids one allocation and give - * the compiler more opportunity to optimize the generic virtual array. */ -template<typename T, typename VArrayT> -class GVArray_For_EmbeddedVArray : public GVArray_For_VArray<T> { - private: - VArrayT embedded_varray_; - public: - template<typename... Args> - GVArray_For_EmbeddedVArray(const int64_t size, Args &&...args) - : GVArray_For_VArray<T>(size), embedded_varray_(std::forward<Args>(args)...) + Span<T> get_internal_span() const override { - this->varray_ = &embedded_varray_; + return varray_.get_internal_span().template typed<T>(); } -}; -/* Same as GVArray_For_EmbeddedVArray, but for mutable virtual arrays. */ -template<typename T, typename VMutableArrayT> -class GVMutableArray_For_EmbeddedVMutableArray : public GVMutableArray_For_VMutableArray<T> { - private: - VMutableArrayT embedded_varray_; - - public: - template<typename... Args> - GVMutableArray_For_EmbeddedVMutableArray(const int64_t size, Args &&...args) - : GVMutableArray_For_VMutableArray<T>(size), embedded_varray_(std::forward<Args>(args)...) + bool is_single() const override { - this->varray_ = &embedded_varray_; + return varray_.is_single(); } -}; -/* Same as VArray_For_ArrayContainer, but for a generic virtual array. */ -template<typename Container, typename T = typename Container::value_type> -class GVArray_For_ArrayContainer - : public GVArray_For_EmbeddedVArray<T, VArray_For_ArrayContainer<Container, T>> { - public: - GVArray_For_ArrayContainer(Container container) - : GVArray_For_EmbeddedVArray<T, VArray_For_ArrayContainer<Container, T>>( - container.size(), std::move(container)) + T get_internal_single() const override { + T value; + varray_.get_internal_single(&value); + return value; } -}; -/* Same as VArray_For_DerivedSpan, but for a generic virtual array. */ -template<typename StructT, typename ElemT, ElemT (*GetFunc)(const StructT &)> -class GVArray_For_DerivedSpan - : public GVArray_For_EmbeddedVArray<ElemT, VArray_For_DerivedSpan<StructT, ElemT, GetFunc>> { - public: - GVArray_For_DerivedSpan(const Span<StructT> data) - : GVArray_For_EmbeddedVArray<ElemT, VArray_For_DerivedSpan<StructT, ElemT, GetFunc>>( - data.size(), data) + bool try_assign_GVArray(GVArray &varray) const override { + varray = varray_; + return true; } -}; -/* Same as VMutableArray_For_DerivedSpan, but for a generic virtual array. */ -template<typename StructT, - typename ElemT, - ElemT (*GetFunc)(const StructT &), - void (*SetFunc)(StructT &, ElemT)> -class GVMutableArray_For_DerivedSpan - : public GVMutableArray_For_EmbeddedVMutableArray< - ElemT, - VMutableArray_For_DerivedSpan<StructT, ElemT, GetFunc, SetFunc>> { - public: - GVMutableArray_For_DerivedSpan(const MutableSpan<StructT> data) - : GVMutableArray_For_EmbeddedVMutableArray< - ElemT, - VMutableArray_For_DerivedSpan<StructT, ElemT, GetFunc, SetFunc>>(data.size(), data) + bool try_assign_GVMutableArray(GVMutableArray &varray) const override { + varray = varray_; + return true; } -}; -/* Same as VArray_For_Span, but for a generic virtual array. */ -template<typename T> -class GVArray_For_Span : public GVArray_For_EmbeddedVArray<T, VArray_For_Span<T>> { - public: - GVArray_For_Span(const Span<T> data) - : GVArray_For_EmbeddedVArray<T, VArray_For_Span<T>>(data.size(), data) + bool may_have_ownership() const override { + return varray_.may_have_ownership(); } }; -/* Same as VMutableArray_For_MutableSpan, but for a generic virtual array. */ -template<typename T> -class GVMutableArray_For_MutableSpan - : public GVMutableArray_For_EmbeddedVMutableArray<T, VMutableArray_For_MutableSpan<T>> { +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #GVArrayImpl_For_GSpan and #GVMutableArrayImpl_For_GMutableSpan. + * \{ */ + +class GVArrayImpl_For_GSpan : public GVArrayImpl { + protected: + const void *data_ = nullptr; + const int64_t element_size_; + public: - GVMutableArray_For_MutableSpan(const MutableSpan<T> data) - : GVMutableArray_For_EmbeddedVMutableArray<T, VMutableArray_For_MutableSpan<T>>(data.size(), - data) - { - } + GVArrayImpl_For_GSpan(const GSpan span); + + protected: + GVArrayImpl_For_GSpan(const CPPType &type, const int64_t size); + + void get(const int64_t index, void *r_value) const override; + void get_to_uninitialized(const int64_t index, void *r_value) const override; + + bool is_span() const override; + GSpan get_internal_span() const override; }; -/** - * Utility class to create the "best" typed virtual array for a given generic virtual array. - * In most cases we don't just want to use VArray_For_GVArray, because it adds an additional - * indirection on element-access that can be avoided in many cases (e.g. when the virtual array is - * just a span or single value). - * - * This is not a virtual array itself, but is used to get a virtual array. - */ -template<typename T> class GVArray_Typed { - private: - const VArray<T> *varray_; - /* Of these optional virtual arrays, at most one is constructed at any time. */ - std::optional<VArray_For_Span<T>> varray_span_; - std::optional<VArray_For_Single<T>> varray_single_; - std::optional<VArray_For_GVArray<T>> varray_any_; - GVArrayPtr owned_gvarray_; +class GVMutableArrayImpl_For_GMutableSpan : public GVMutableArrayImpl { + protected: + void *data_ = nullptr; + const int64_t element_size_; public: - explicit GVArray_Typed(const GVArray &gvarray) - { - BLI_assert(gvarray.type().is<T>()); - if (gvarray.is_span()) { - const GSpan span = gvarray.get_internal_span(); - varray_span_.emplace(span.typed<T>()); - varray_ = &*varray_span_; - } - else if (gvarray.is_single()) { - T single_value; - gvarray.get_internal_single(&single_value); - varray_single_.emplace(single_value, gvarray.size()); - varray_ = &*varray_single_; - } - else if (const VArray<T> *internal_varray = gvarray.try_get_internal_varray<T>()) { - varray_ = internal_varray; - } - else { - varray_any_.emplace(gvarray); - varray_ = &*varray_any_; - } - } + GVMutableArrayImpl_For_GMutableSpan(const GMutableSpan span); - /* Same as the constructor above, but also takes ownership of the passed in virtual array. */ - explicit GVArray_Typed(GVArrayPtr gvarray) : GVArray_Typed(*gvarray) - { - owned_gvarray_ = std::move(gvarray); - } + protected: + GVMutableArrayImpl_For_GMutableSpan(const CPPType &type, const int64_t size); - const VArray<T> &operator*() const - { - return *varray_; - } + public: + void get(const int64_t index, void *r_value) const override; + void get_to_uninitialized(const int64_t index, void *r_value) const override; - const VArray<T> *operator->() const - { - return varray_; - } + void set_by_copy(const int64_t index, const void *value) override; + void set_by_move(const int64_t index, void *value) override; + void set_by_relocate(const int64_t index, void *value) override; - /* Support implicit cast to the typed virtual array for convenience when `varray->typed<T>()` is - * used within an expression. */ - operator const VArray<T> &() const - { - return *varray_; - } + bool is_span() const override; + GSpan get_internal_span() const override; +}; - T operator[](const int64_t index) const - { - return varray_->get(index); - } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \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); +} - int64_t size() const - { - return varray_->size(); +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); +} + +/* 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> || + std::is_same_v<StorageT, const GVArrayImpl *> || + std::is_same_v<StorageT, 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); } - - IndexRange index_range() const - { - return IndexRange(this->size()); + 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); +} -/* Same as GVArray_Typed, but for mutable virtual arrays. */ -template<typename T> class GVMutableArray_Typed { - private: - VMutableArray<T> *varray_; - std::optional<VMutableArray_For_MutableSpan<T>> varray_span_; - std::optional<VMutableArray_For_GVMutableArray<T>> varray_any_; - GVMutableArrayPtr owned_gvarray_; +/** \} */ - public: - explicit GVMutableArray_Typed(GVMutableArray &gvarray) - { - BLI_assert(gvarray.type().is<T>()); - if (gvarray.is_span()) { - const GMutableSpan span = gvarray.get_internal_span(); - varray_span_.emplace(span.typed<T>()); - varray_ = &*varray_span_; - } - else if (VMutableArray<T> *internal_varray = gvarray.try_get_internal_mutable_varray<T>()) { - varray_ = internal_varray; - } - else { - varray_any_.emplace(gvarray); - varray_ = &*varray_any_; - } - } +/* -------------------------------------------------------------------- */ +/** \name Inline methods for #GVMutableArray. + * \{ */ - explicit GVMutableArray_Typed(GVMutableArrayPtr gvarray) : GVMutableArray_Typed(*gvarray) - { - owned_gvarray_ = std::move(gvarray); - } +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; +} - VMutableArray<T> &operator*() - { - return *varray_; +template<typename T> inline GVMutableArray::GVMutableArray(const VMutableArray<T> &varray) +{ + if (!varray) { + return; } - - VMutableArray<T> *operator->() - { - return varray_; + if (varray.try_assign_GVMutableArray(*this)) { + return; } - - operator VMutableArray<T> &() - { - return *varray_; + if (varray.may_have_ownership()) { + *this = GVMutableArray::For<GVMutableArrayImpl_For_VMutableArray<T>>(varray); } - - T operator[](const int64_t index) const - { - return varray_->get(index); + else if (varray.is_span()) { + MutableSpan<T> data = varray.get_internal_span(); + *this = GVMutableArray::ForSpan(data); } - - int64_t size() const - { - return varray_->size(); + else { + *this = GVMutableArray::For<GVMutableArrayImpl_For_VMutableArray<T>>(varray); } -}; - -class GVArray_For_SlicedGVArray : public GVArray { - protected: - const GVArray &varray_; - int64_t offset_; +} - public: - GVArray_For_SlicedGVArray(const GVArray &varray, const IndexRange slice) - : GVArray(varray.type(), slice.size()), varray_(varray), offset_(slice.start()) - { - BLI_assert(slice.one_after_last() <= varray.size()); +template<typename T> inline VMutableArray<T> GVMutableArray::typed() const +{ + if (!*this) { + return {}; } - - /* TODO: Add #materialize method. */ - void get_impl(const int64_t index, void *r_value) const override; - void get_to_uninitialized_impl(const int64_t index, void *r_value) const override; -}; - -/** - * Utility class to create the "best" sliced virtual array. - */ -class GVArray_Slice { - private: - const GVArray *varray_; - /* Of these optional virtual arrays, at most one is constructed at any time. */ - std::optional<GVArray_For_GSpan> varray_span_; - std::optional<GVArray_For_SlicedGVArray> varray_any_; - - public: - GVArray_Slice(const GVArray &varray, const IndexRange slice); - - const GVArray &operator*() - { - return *varray_; + BLI_assert(this->type().is<T>()); + VMutableArray<T> varray; + if (this->try_assign_VMutableArray(varray)) { + return varray; } - - const GVArray *operator->() - { - return varray_; + if (this->may_have_ownership()) { + return VMutableArray<T>::template For<VMutableArrayImpl_For_GVMutableArray<T>>(*this); } - - operator const GVArray &() - { - return *varray_; + 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::fn |