/* * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #pragma once /** \file * \ingroup fn * * A generic virtual array is the same as a virtual array from blenlib, except for the fact that * the data type is only known at runtime. */ #include #include "BLI_virtual_array.hh" #include "FN_generic_span.hh" namespace blender::fn { template class GVArray_Typed; template class GVMutableArray_Typed; class GVArray; class GVMutableArray; using GVArrayPtr = std::unique_ptr; using GVMutableArrayPtr = std::unique_ptr; /* A generically typed version of `VArray`. */ class GVArray { protected: const CPPType *type_; int64_t size_; public: GVArray(const CPPType &type, const int64_t size) : type_(&type), size_(size) { BLI_assert(size_ >= 0); } virtual ~GVArray() = default; const CPPType &type() const { return *type_; } int64_t size() const { return size_; } bool is_empty() const { return size_ == 0; } /* 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); } /* 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); } /* 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(); } /* 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(); } /* 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(); } /* 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); } /* 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); } 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; template const VArray *try_get_internal_varray() const { BLI_assert(type_->is()); return (const VArray *)this->try_get_internal_varray_impl(); } /* Create a typed virtual array for this generic virtual array. */ template GVArray_Typed typed() const { return GVArray_Typed(*this); } GVArrayPtr shallow_copy() const; 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; virtual bool is_span_impl() const; virtual GSpan get_internal_span_impl() const; virtual bool is_single_impl() const; virtual void get_internal_single_impl(void *UNUSED(r_value)) const; virtual void materialize_impl(const IndexMask mask, void *dst) const; virtual void materialize_to_uninitialized_impl(const IndexMask mask, void *dst) const; virtual const void *try_get_internal_varray_impl() 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) { } 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); } 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); } 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); } GMutableSpan get_internal_span() { BLI_assert(this->is_span()); GSpan span = static_cast(this)->get_internal_span(); return GMutableSpan(span.type(), const_cast(span.data()), span.size()); } template VMutableArray *try_get_internal_mutable_varray() { BLI_assert(type_->is()); return (VMutableArray *)this->try_get_internal_mutable_varray_impl(); } /* Create a typed virtual array for this generic virtual array. */ template GVMutableArray_Typed typed() { return GVMutableArray_Typed(*this); } 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) { this->set_all_impl(src); } 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; virtual void set_all_impl(const void *src); virtual void *try_get_internal_mutable_varray_impl(); }; class GVArray_For_GSpan : public GVArray { protected: const void *data_ = nullptr; const int64_t element_size_; public: GVArray_For_GSpan(const GSpan span) : GVArray(span.type(), span.size()), data_(span.data()), element_size_(span.type().size()) { } protected: GVArray_For_GSpan(const CPPType &type, const int64_t size) : GVArray(type, size), element_size_(type.size()) { } 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; bool is_span_impl() const override; GSpan get_internal_span_impl() const override; }; class GVArray_For_Empty : public GVArray { public: GVArray_For_Empty(const CPPType &type) : GVArray(type, 0) { } protected: void get_to_uninitialized_impl(const int64_t UNUSED(index), void *UNUSED(r_value)) const override { BLI_assert(false); } }; class GVMutableArray_For_GMutableSpan : public GVMutableArray { protected: void *data_ = nullptr; const int64_t element_size_; public: GVMutableArray_For_GMutableSpan(const GMutableSpan span) : GVMutableArray(span.type(), span.size()), data_(span.data()), element_size_(span.type().size()) { } protected: GVMutableArray_For_GMutableSpan(const CPPType &type, const int64_t size) : GVMutableArray(type, size), element_size_(type.size()) { } 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; 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; bool is_span_impl() const override; GSpan get_internal_span_impl() const override; }; /* 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; public: GVArray_For_SingleValueRef(const CPPType &type, const int64_t size, const void *value) : GVArray(type, size), value_(value) { } protected: GVArray_For_SingleValueRef(const CPPType &type, const int64_t size) : GVArray(type, size) { } 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; 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; }; /* Same as GVArray_For_SingleValueRef, but the value is owned. */ class GVArray_For_SingleValue : public GVArray_For_SingleValueRef { public: GVArray_For_SingleValue(const CPPType &type, const int64_t size, const void *value); ~GVArray_For_SingleValue(); }; /* Used to convert a typed virtual array into a generic one. */ template class GVArray_For_VArray : public GVArray { protected: const VArray *varray_ = nullptr; public: GVArray_For_VArray(const VArray &varray) : GVArray(CPPType::get(), varray.size()), varray_(&varray) { } protected: GVArray_For_VArray(const int64_t size) : GVArray(CPPType::get(), size) { } void get_impl(const int64_t index, void *r_value) const override { *(T *)r_value = varray_->get(index); } void get_to_uninitialized_impl(const int64_t index, void *r_value) const override { new (r_value) T(varray_->get(index)); } bool is_span_impl() const override { return varray_->is_span(); } GSpan get_internal_span_impl() const override { return GSpan(varray_->get_internal_span()); } bool is_single_impl() const override { return varray_->is_single(); } void get_internal_single_impl(void *r_value) const override { *(T *)r_value = varray_->get_internal_single(); } void materialize_impl(const IndexMask mask, void *dst) const override { varray_->materialize(mask, MutableSpan((T *)dst, mask.min_array_size())); } void materialize_to_uninitialized_impl(const IndexMask mask, void *dst) const override { varray_->materialize_to_uninitialized(mask, MutableSpan((T *)dst, mask.min_array_size())); } const void *try_get_internal_varray_impl() const override { return varray_; } }; /* Used to convert any generic virtual array into a typed one. */ template class VArray_For_GVArray : public VArray { protected: const GVArray *varray_ = nullptr; public: VArray_For_GVArray(const GVArray &varray) : VArray(varray.size()), varray_(&varray) { BLI_assert(varray_->type().template is()); } protected: VArray_For_GVArray(const int64_t size) : VArray(size) { } T get_impl(const int64_t index) const override { T value; varray_->get(index, &value); return value; } bool is_span_impl() const override { return varray_->is_span(); } Span get_internal_span_impl() const override { return varray_->get_internal_span().template typed(); } bool is_single_impl() const override { return varray_->is_single(); } T get_internal_single_impl() const override { T value; varray_->get_internal_single(&value); return value; } }; /* Used to convert an generic mutable virtual array into a typed one. */ template class VMutableArray_For_GVMutableArray : public VMutableArray { protected: GVMutableArray *varray_ = nullptr; public: VMutableArray_For_GVMutableArray(GVMutableArray &varray) : VMutableArray(varray.size()), varray_(&varray) { BLI_assert(varray.type().template is()); } VMutableArray_For_GVMutableArray(const int64_t size) : VMutableArray(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 get_internal_span_impl() const override { return varray_->get_internal_span().template typed(); } bool is_single_impl() const override { return varray_->is_single(); } T get_internal_single_impl() const override { T value; varray_->get_internal_single(&value); return value; } }; /* Used to convert any typed virtual mutable array into a generic one. */ template class GVMutableArray_For_VMutableArray : public GVMutableArray { protected: VMutableArray *varray_ = nullptr; public: GVMutableArray_For_VMutableArray(VMutableArray &varray) : GVMutableArray(CPPType::get(), varray.size()), varray_(&varray) { } protected: GVMutableArray_For_VMutableArray(const int64_t size) : GVMutableArray(CPPType::get(), size) { } void get_impl(const int64_t index, void *r_value) const override { *(T *)r_value = varray_->get(index); } void get_to_uninitialized_impl(const int64_t index, void *r_value) const override { new (r_value) T(varray_->get(index)); } bool is_span_impl() const override { return varray_->is_span(); } GSpan get_internal_span_impl() const override { Span span = varray_->get_internal_span(); return span; } bool is_single_impl() const override { return varray_->is_single(); } void get_internal_single_impl(void *r_value) const override { *(T *)r_value = varray_->get_internal_single(); } void set_by_copy_impl(const int64_t index, const void *value) override { const T &value_ = *(const T *)value; varray_->set(index, value_); } void set_by_relocate_impl(const int64_t index, void *value) override { T &value_ = *(T *)value; varray_->set(index, std::move(value_)); value_.~T(); } void set_by_move_impl(const int64_t index, void *value) override { T &value_ = *(T *)value; varray_->set(index, std::move(value_)); } void set_all_impl(const void *src) override { varray_->set_all(Span((T *)src, size_)); } void materialize_impl(const IndexMask mask, void *dst) const override { varray_->materialize(mask, MutableSpan((T *)dst, mask.min_array_size())); } void materialize_to_uninitialized_impl(const IndexMask mask, void *dst) const override { varray_->materialize_to_uninitialized(mask, MutableSpan((T *)dst, mask.min_array_size())); } const void *try_get_internal_varray_impl() const override { return (const VArray *)varray_; } void *try_get_internal_mutable_varray_impl() override { return varray_; } }; /* 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 class GVArray_Span : public Span { private: GVArray_GSpan varray_gspan_; public: GVArray_Span(const GVArray &varray) : varray_gspan_(varray) { BLI_assert(varray.type().is()); this->data_ = (const T *)varray_gspan_.data(); this->size_ = varray_gspan_.size(); } }; template class GVArray_For_OwnedVArray : public GVArray_For_VArray { private: VArrayPtr owned_varray_; public: /* Takes ownership of varray and passes a reference to the base class. */ GVArray_For_OwnedVArray(VArrayPtr varray) : GVArray_For_VArray(*varray), owned_varray_(std::move(varray)) { } }; template class VArray_For_OwnedGVArray : public VArray_For_GVArray { 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(*varray), owned_varray_(std::move(varray)) { } }; template class GVMutableArray_For_OwnedVMutableArray : public GVMutableArray_For_VMutableArray { private: VMutableArrayPtr owned_varray_; public: /* Takes ownership of varray and passes a reference to the base class. */ GVMutableArray_For_OwnedVMutableArray(VMutableArrayPtr varray) : GVMutableArray_For_VMutableArray(*varray), owned_varray_(std::move(varray)) { } }; template class VMutableArray_For_OwnedGVMutableArray : public VMutableArray_For_GVMutableArray { 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(*varray), owned_varray_(std::move(varray)) { } }; /* 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 class GVArray_For_EmbeddedVArray : public GVArray_For_VArray { private: VArrayT embedded_varray_; public: template GVArray_For_EmbeddedVArray(const int64_t size, Args &&...args) : GVArray_For_VArray(size), embedded_varray_(std::forward(args)...) { this->varray_ = &embedded_varray_; } }; /* Same as GVArray_For_EmbeddedVArray, but for mutable virtual arrays. */ template class GVMutableArray_For_EmbeddedVMutableArray : public GVMutableArray_For_VMutableArray { private: VMutableArrayT embedded_varray_; public: template GVMutableArray_For_EmbeddedVMutableArray(const int64_t size, Args &&...args) : GVMutableArray_For_VMutableArray(size), embedded_varray_(std::forward(args)...) { this->varray_ = &embedded_varray_; } }; /* Same as VArray_For_ArrayContainer, but for a generic virtual array. */ template class GVArray_For_ArrayContainer : public GVArray_For_EmbeddedVArray> { public: GVArray_For_ArrayContainer(Container container) : GVArray_For_EmbeddedVArray>( container.size(), std::move(container)) { } }; /* Same as VArray_For_DerivedSpan, but for a generic virtual array. */ template class GVArray_For_DerivedSpan : public GVArray_For_EmbeddedVArray> { public: GVArray_For_DerivedSpan(const Span data) : GVArray_For_EmbeddedVArray>( data.size(), data) { } }; /* Same as VMutableArray_For_DerivedSpan, but for a generic virtual array. */ template class GVMutableArray_For_DerivedSpan : public GVMutableArray_For_EmbeddedVMutableArray< ElemT, VMutableArray_For_DerivedSpan> { public: GVMutableArray_For_DerivedSpan(const MutableSpan data) : GVMutableArray_For_EmbeddedVMutableArray< ElemT, VMutableArray_For_DerivedSpan>(data.size(), data) { } }; /* Same as VArray_For_Span, but for a generic virtual array. */ template class GVArray_For_Span : public GVArray_For_EmbeddedVArray> { public: GVArray_For_Span(const Span data) : GVArray_For_EmbeddedVArray>(data.size(), data) { } }; /* Same as VMutableArray_For_MutableSpan, but for a generic virtual array. */ template class GVMutableArray_For_MutableSpan : public GVMutableArray_For_EmbeddedVMutableArray> { public: GVMutableArray_For_MutableSpan(const MutableSpan data) : GVMutableArray_For_EmbeddedVMutableArray>(data.size(), data) { } }; /** * 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 class GVArray_Typed { private: const VArray *varray_; /* Of these optional virtual arrays, at most one is constructed at any time. */ std::optional> varray_span_; std::optional> varray_single_; std::optional> varray_any_; GVArrayPtr owned_gvarray_; public: explicit GVArray_Typed(const GVArray &gvarray) { BLI_assert(gvarray.type().is()); if (gvarray.is_span()) { const GSpan span = gvarray.get_internal_span(); varray_span_.emplace(span.typed()); 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 *internal_varray = gvarray.try_get_internal_varray()) { varray_ = internal_varray; } else { varray_any_.emplace(gvarray); varray_ = &*varray_any_; } } /* 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); } const VArray &operator*() const { return *varray_; } const VArray *operator->() const { return varray_; } /* Support implicit cast to the typed virtual array for convenience when `varray->typed()` is * used within an expression. */ operator const VArray &() const { return *varray_; } T operator[](const int64_t index) const { return varray_->get(index); } int64_t size() const { return varray_->size(); } IndexRange index_range() const { return IndexRange(this->size()); } }; /* Same as GVArray_Typed, but for mutable virtual arrays. */ template class GVMutableArray_Typed { private: VMutableArray *varray_; std::optional> varray_span_; std::optional> varray_any_; GVMutableArrayPtr owned_gvarray_; public: explicit GVMutableArray_Typed(GVMutableArray &gvarray) { BLI_assert(gvarray.type().is()); if (gvarray.is_span()) { const GMutableSpan span = gvarray.get_internal_span(); varray_span_.emplace(span.typed()); varray_ = &*varray_span_; } else if (VMutableArray *internal_varray = gvarray.try_get_internal_mutable_varray()) { varray_ = internal_varray; } else { varray_any_.emplace(gvarray); varray_ = &*varray_any_; } } explicit GVMutableArray_Typed(GVMutableArrayPtr gvarray) : GVMutableArray_Typed(*gvarray) { owned_gvarray_ = std::move(gvarray); } VMutableArray &operator*() { return *varray_; } VMutableArray *operator->() { return varray_; } operator VMutableArray &() { return *varray_; } T operator[](const int64_t index) const { return varray_->get(index); } int64_t size() const { return varray_->size(); } }; 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()); } /* 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 varray_span_; std::optional varray_any_; public: GVArray_Slice(const GVArray &varray, const IndexRange slice); const GVArray &operator*() { return *varray_; } const GVArray *operator->() { return varray_; } operator const GVArray &() { return *varray_; } }; } // namespace blender::fn