diff options
author | Colin Basnett <cmbasnett@gmail.com> | 2022-09-17 03:50:37 +0300 |
---|---|---|
committer | Colin Basnett <cmbasnett@gmail.com> | 2022-09-17 03:50:37 +0300 |
commit | 564bda241a973396da51d7c3ccd9efd97d51728a (patch) | |
tree | 4d9aa4cccd82e2e606b47774afa5e1015bf14768 /source/blender/blenlib/BLI_virtual_array.hh | |
parent | 0fff238150d076576053c25b646f653d6e3b0edb (diff) | |
parent | 48d7ff68f0df209c77bbb081ab46fbc109fd825a (diff) |
Merge branch 'master' into feature-imformatfeature-imformat
Diffstat (limited to 'source/blender/blenlib/BLI_virtual_array.hh')
-rw-r--r-- | source/blender/blenlib/BLI_virtual_array.hh | 325 |
1 files changed, 186 insertions, 139 deletions
diff --git a/source/blender/blenlib/BLI_virtual_array.hh b/source/blender/blenlib/BLI_virtual_array.hh index 8f228ea188e..19ee2334bd9 100644 --- a/source/blender/blenlib/BLI_virtual_array.hh +++ b/source/blender/blenlib/BLI_virtual_array.hh @@ -23,6 +23,8 @@ * see of the increased compile time and binary size is worth it. */ +#include <optional> + #include "BLI_any.hh" #include "BLI_array.hh" #include "BLI_index_mask.hh" @@ -35,6 +37,36 @@ class GVArray; class GVMutableArray; /** + * Is used to quickly check if a varray is a span or single value. This struct also allows + * retrieving multiple pieces of data with a single virtual method call. + */ +struct CommonVArrayInfo { + enum class Type : uint8_t { + /* Is not one of the common special types below. */ + Any, + Span, + Single, + }; + + Type type = Type::Any; + + /** True when the #data becomes a dangling pointer when the virtual array is destructed. */ + bool may_have_ownership = true; + + /** + * Points either to nothing, a single value or array of values, depending on #type. + * If this is a span of a mutable virtual array, it is safe to cast away const. + */ + const void *data; + + CommonVArrayInfo() = default; + CommonVArrayInfo(const Type _type, const bool _may_have_ownership, const void *_data) + : type(_type), may_have_ownership(_may_have_ownership), data(_data) + { + } +}; + +/** * Implements the specifics of how the elements of a virtual array are accessed. It contains a * bunch of virtual methods that are wrapped by #VArray. */ @@ -65,65 +97,18 @@ template<typename T> class VArrayImpl { */ virtual T get(int64_t index) const = 0; - /** - * Return true when the virtual array is a plain array internally. - */ - virtual bool is_span() const - { - return false; - } - - /** - * Return the span of the virtual array. - * This invokes undefined behavior when #is_span returned false. - */ - virtual Span<T> get_internal_span() const + virtual CommonVArrayInfo common_info() const { - /* Provide a default implementation, so that subclasses don't have to provide it. This method - * should never be called because #is_span returns false by default. */ - BLI_assert_unreachable(); return {}; } /** - * Return true when the virtual array has the same value at every index. - */ - virtual bool is_single() const - { - return false; - } - - /** - * Return the value that is used at every index. - * This invokes undefined behavior when #is_single returned false. - */ - virtual T get_internal_single() const - { - /* Provide a default implementation, so that subclasses don't have to provide it. This method - * should never be called because #is_single returns false by default. */ - BLI_assert_unreachable(); - return T(); - } - - /** * Copy values from the virtual array into the provided span. The index of the value in the * virtual array is the same as the index in the span. */ virtual void materialize(IndexMask mask, MutableSpan<T> r_span) const { - T *dst = r_span.data(); - /* Optimize for a few different common cases. */ - if (this->is_span()) { - const T *src = this->get_internal_span().data(); - mask.foreach_index([&](const int64_t i) { dst[i] = src[i]; }); - } - else if (this->is_single()) { - const T single = this->get_internal_single(); - mask.foreach_index([&](const int64_t i) { dst[i] = single; }); - } - else { - mask.foreach_index([&](const int64_t i) { dst[i] = this->get(i); }); - } + mask.foreach_index([&](const int64_t i) { r_span[i] = this->get(i); }); } /** @@ -132,18 +117,7 @@ template<typename T> class VArrayImpl { virtual void materialize_to_uninitialized(IndexMask mask, MutableSpan<T> r_span) const { T *dst = r_span.data(); - /* Optimize for a few different common cases. */ - if (this->is_span()) { - const T *src = this->get_internal_span().data(); - mask.foreach_index([&](const int64_t i) { new (dst + i) T(src[i]); }); - } - else if (this->is_single()) { - const T single = this->get_internal_single(); - mask.foreach_index([&](const int64_t i) { new (dst + i) T(single); }); - } - else { - mask.foreach_index([&](const int64_t i) { new (dst + i) T(this->get(i)); }); - } + mask.foreach_index([&](const int64_t i) { new (dst + i) T(this->get(i)); }); } /** @@ -187,17 +161,6 @@ template<typename T> class VArrayImpl { } /** - * Return true when this virtual array may own any of the memory it references. This can be used - * for optimization purposes when converting or copying the virtual array. - */ - virtual bool may_have_ownership() const - { - /* Use true by default to be on the safe side. Subclasses that know for sure that they don't - * own anything can overwrite this with false. */ - return true; - } - - /** * Return true when the other virtual array should be considered to be the same, e.g. because it * shares the same underlying memory. */ @@ -222,10 +185,10 @@ template<typename T> class VMutableArrayImpl : public VArrayImpl<T> { */ virtual void set_all(Span<T> src) { - if (this->is_span()) { - const Span<T> const_span = this->get_internal_span(); - const MutableSpan<T> span{(T *)const_span.data(), const_span.size()}; - initialized_copy_n(src.data(), this->size_, span.data()); + const CommonVArrayInfo info = this->common_info(); + if (info.type == CommonVArrayInfo::Type::Span) { + initialized_copy_n( + src.data(), this->size_, const_cast<T *>(static_cast<const T *>(info.data))); } else { const int64_t size = this->size_; @@ -273,14 +236,9 @@ template<typename T> class VArrayImpl_For_Span : public VMutableArrayImpl<T> { data_[index] = value; } - bool is_span() const override + CommonVArrayInfo common_info() const override { - return true; - } - - Span<T> get_internal_span() const override - { - return Span<T>(data_, this->size_); + return CommonVArrayInfo(CommonVArrayInfo::Type::Span, true, data_); } bool is_same(const VArrayImpl<T> &other) const final @@ -288,15 +246,27 @@ template<typename T> class VArrayImpl_For_Span : public VMutableArrayImpl<T> { if (other.size() != this->size_) { return false; } - if (!other.is_span()) { + const CommonVArrayInfo other_info = other.common_info(); + if (other_info.type != CommonVArrayInfo::Type::Span) { return false; } - const Span<T> other_span = other.get_internal_span(); - return data_ == other_span.data(); + return data_ == static_cast<const T *>(other_info.data); + } + + void materialize(IndexMask mask, MutableSpan<T> r_span) const override + { + mask.foreach_index([&](const int64_t i) { r_span[i] = data_[i]; }); + } + + void materialize_to_uninitialized(IndexMask mask, MutableSpan<T> r_span) const override + { + T *dst = r_span.data(); + mask.foreach_index([&](const int64_t i) { new (dst + i) T(data_[i]); }); } void materialize_compressed(IndexMask mask, MutableSpan<T> r_span) const override { + BLI_assert(mask.size() == r_span.size()); mask.to_best_mask_type([&](auto best_mask) { for (const int64_t i : IndexRange(best_mask.size())) { r_span[i] = data_[best_mask[i]]; @@ -307,6 +277,7 @@ template<typename T> class VArrayImpl_For_Span : public VMutableArrayImpl<T> { void materialize_compressed_to_uninitialized(IndexMask mask, MutableSpan<T> r_span) const override { + BLI_assert(mask.size() == r_span.size()); T *dst = r_span.data(); mask.to_best_mask_type([&](auto best_mask) { for (const int64_t i : IndexRange(best_mask.size())) { @@ -324,13 +295,22 @@ template<typename T> class VArrayImpl_For_Span_final final : public VArrayImpl_F public: using VArrayImpl_For_Span<T>::VArrayImpl_For_Span; + VArrayImpl_For_Span_final(const Span<T> data) + /* Cast const away, because the implementation for const and non const spans is shared. */ + : VArrayImpl_For_Span<T>({const_cast<T *>(data.data()), data.size()}) + { + } + private: - bool may_have_ownership() const override + CommonVArrayInfo common_info() const final { - return false; + return CommonVArrayInfo(CommonVArrayInfo::Type::Span, false, this->data_); } }; +template<typename T> +inline constexpr bool is_trivial_extended_v<VArrayImpl_For_Span_final<T>> = true; + /** * A variant of `VArrayImpl_For_Span` that owns the underlying data. * The `Container` type has to implement a `size()` and `data()` method. @@ -371,24 +351,20 @@ template<typename T> class VArrayImpl_For_Single final : public VArrayImpl<T> { return value_; } - bool is_span() const override - { - return this->size_ == 1; - } - - Span<T> get_internal_span() const override + CommonVArrayInfo common_info() const override { - return Span<T>(&value_, 1); + return CommonVArrayInfo(CommonVArrayInfo::Type::Single, true, &value_); } - bool is_single() const override + void materialize(IndexMask mask, MutableSpan<T> r_span) const override { - return true; + r_span.fill_indices(mask, value_); } - T get_internal_single() const override + void materialize_to_uninitialized(IndexMask mask, MutableSpan<T> r_span) const override { - return value_; + T *dst = r_span.data(); + mask.foreach_index([&](const int64_t i) { new (dst + i) T(value_); }); } void materialize_compressed(IndexMask mask, MutableSpan<T> r_span) const override @@ -406,6 +382,9 @@ template<typename T> class VArrayImpl_For_Single final : public VArrayImpl<T> { } }; +template<typename T> +inline constexpr bool is_trivial_extended_v<VArrayImpl_For_Single<T>> = is_trivial_extended_v<T>; + /** * This class makes it easy to create a virtual array for an existing function or lambda. The * `GetFunc` should take a single `index` argument and return the value at that index. @@ -531,11 +510,6 @@ class VArrayImpl_For_DerivedSpan final : public VMutableArrayImpl<ElemT> { }); } - bool may_have_ownership() const override - { - return false; - } - bool is_same(const VArrayImpl<ElemT> &other) const override { if (other.size() != this->size_) { @@ -554,6 +528,13 @@ class VArrayImpl_For_DerivedSpan final : public VMutableArrayImpl<ElemT> { } }; +template<typename StructT, + typename ElemT, + ElemT (*GetFunc)(const StructT &), + void (*SetFunc)(StructT &, ElemT)> +inline constexpr bool + is_trivial_extended_v<VArrayImpl_For_DerivedSpan<StructT, ElemT, GetFunc, SetFunc>> = true; + namespace detail { /** @@ -768,11 +749,18 @@ template<typename T> class VArrayCommon { return IndexRange(this->size()); } + CommonVArrayInfo common_info() const + { + BLI_assert(*this); + return impl_->common_info(); + } + /** Return true when the virtual array is stored as a span internally. */ bool is_span() const { BLI_assert(*this); - return impl_->is_span(); + const CommonVArrayInfo info = impl_->common_info(); + return info.type == CommonVArrayInfo::Type::Span; } /** @@ -782,14 +770,16 @@ template<typename T> class VArrayCommon { Span<T> get_internal_span() const { BLI_assert(this->is_span()); - return impl_->get_internal_span(); + const CommonVArrayInfo info = impl_->common_info(); + return Span<T>(static_cast<const T *>(info.data), this->size()); } /** Return true when the virtual array returns the same value for every index. */ bool is_single() const { BLI_assert(*this); - return impl_->is_single(); + const CommonVArrayInfo info = impl_->common_info(); + return info.type == CommonVArrayInfo::Type::Single; } /** @@ -799,7 +789,20 @@ template<typename T> class VArrayCommon { T get_internal_single() const { BLI_assert(this->is_single()); - return impl_->get_internal_single(); + const CommonVArrayInfo info = impl_->common_info(); + return *static_cast<const T *>(info.data); + } + + /** + * Return the value that is returned for every index, if the array is stored as a single value. + */ + std::optional<T> get_if_single() const + { + const CommonVArrayInfo info = impl_->common_info(); + if (info.type != CommonVArrayInfo::Type::Single) { + return std::nullopt; + } + return *static_cast<const T *>(info.data); } /** @@ -861,12 +864,6 @@ template<typename T> class VArrayCommon { { return impl_->try_assign_GVArray(varray); } - - /** See #GVArrayImpl::may_have_ownership. */ - bool may_have_ownership() const - { - return impl_->may_have_ownership(); - } }; template<typename T> class VMutableArray; @@ -910,10 +907,7 @@ template<typename T> class VArray : public VArrayCommon<T> { VArray(varray_tag::span /* tag */, Span<T> span) { - /* Cast const away, because the virtual array implementation for const and non const spans is - * shared. */ - MutableSpan<T> mutable_span{const_cast<T *>(span.data()), span.size()}; - this->template emplace<VArrayImpl_For_Span_final<T>>(mutable_span); + this->template emplace<VArrayImpl_For_Span_final<T>>(span); } VArray(varray_tag::single /* tag */, T value, const int64_t size) @@ -1076,8 +1070,8 @@ template<typename T> class VMutableArray : public VArrayCommon<T> { MutableSpan<T> get_internal_span() const { BLI_assert(this->is_span()); - const Span<T> span = this->impl_->get_internal_span(); - return MutableSpan<T>(const_cast<T *>(span.data()), span.size()); + const CommonVArrayInfo info = this->get_impl()->common_info(); + return MutableSpan<T>(const_cast<T *>(static_cast<const T *>(info.data)), this->size()); } /** @@ -1132,19 +1126,23 @@ template<typename T> static constexpr bool is_VMutableArray_v<VMutableArray<T>> * from faster access. * - An API is called, that does not accept virtual arrays, but only spans. */ -template<typename T> class VArray_Span final : public Span<T> { +template<typename T> class VArraySpan final : public Span<T> { private: VArray<T> varray_; Array<T> owned_data_; public: - VArray_Span() = default; + VArraySpan() = default; - VArray_Span(VArray<T> varray) : Span<T>(), varray_(std::move(varray)) + VArraySpan(VArray<T> varray) : Span<T>(), varray_(std::move(varray)) { + if (!varray_) { + return; + } this->size_ = varray_.size(); - if (varray_.is_span()) { - this->data_ = varray_.get_internal_span().data(); + const CommonVArrayInfo info = varray_.common_info(); + if (info.type == CommonVArrayInfo::Type::Span) { + this->data_ = static_cast<const T *>(info.data); } else { owned_data_.~Array(); @@ -1154,12 +1152,16 @@ template<typename T> class VArray_Span final : public Span<T> { } } - VArray_Span(VArray_Span &&other) + VArraySpan(VArraySpan &&other) : varray_(std::move(other.varray_)), owned_data_(std::move(other.owned_data_)) { + if (!varray_) { + return; + } this->size_ = varray_.size(); - if (varray_.is_span()) { - this->data_ = varray_.get_internal_span().data(); + const CommonVArrayInfo info = varray_.common_info(); + if (info.type == CommonVArrayInfo::Type::Span) { + this->data_ = static_cast<const T *>(info.data); } else { this->data_ = owned_data_.data(); @@ -1168,25 +1170,25 @@ template<typename T> class VArray_Span final : public Span<T> { other.size_ = 0; } - VArray_Span &operator=(VArray_Span &&other) + VArraySpan &operator=(VArraySpan &&other) { if (this == &other) { return *this; } std::destroy_at(this); - new (this) VArray_Span(std::move(other)); + new (this) VArraySpan(std::move(other)); return *this; } }; /** - * Same as #VArray_Span, but for a mutable span. + * Same as #VArraySpan, but for a mutable span. * The important thing to note is that when changing this span, the results might not be * immediately reflected in the underlying virtual array (only when the virtual array is a span * internally). The #save method can be used to write all changes to the underlying virtual array, * if necessary. */ -template<typename T> class VMutableArray_Span final : public MutableSpan<T> { +template<typename T> class MutableVArraySpan final : public MutableSpan<T> { private: VMutableArray<T> varray_; Array<T> owned_data_; @@ -1194,14 +1196,21 @@ template<typename T> class VMutableArray_Span final : public MutableSpan<T> { bool show_not_saved_warning_ = true; public: + MutableVArraySpan() = default; + /* Create a span for any virtual array. This is cheap when the virtual array is a span itself. If * not, a new array has to be allocated as a wrapper for the underlying virtual array. */ - VMutableArray_Span(VMutableArray<T> varray, const bool copy_values_to_span = true) + MutableVArraySpan(VMutableArray<T> varray, const bool copy_values_to_span = true) : MutableSpan<T>(), varray_(std::move(varray)) { + if (!varray_) { + return; + } + this->size_ = varray_.size(); - if (varray_.is_span()) { - this->data_ = varray_.get_internal_span().data(); + const CommonVArrayInfo info = varray_.common_info(); + if (info.type == CommonVArrayInfo::Type::Span) { + this->data_ = const_cast<T *>(static_cast<const T *>(info.data)); } else { if (copy_values_to_span) { @@ -1216,15 +1225,53 @@ template<typename T> class VMutableArray_Span final : public MutableSpan<T> { } } - ~VMutableArray_Span() + MutableVArraySpan(MutableVArraySpan &&other) + : varray_(std::move(other.varray_)), + owned_data_(std::move(other.owned_data_)), + show_not_saved_warning_(other.show_not_saved_warning_) { - if (show_not_saved_warning_) { - if (!save_has_been_called_) { - std::cout << "Warning: Call `save()` to make sure that changes persist in all cases.\n"; + if (!varray_) { + return; + } + + this->size_ = varray_.size(); + const CommonVArrayInfo info = varray_.common_info(); + if (info.type == CommonVArrayInfo::Type::Span) { + this->data_ = static_cast<T *>(const_cast<void *>(info.data)); + } + else { + this->data_ = owned_data_.data(); + } + other.data_ = nullptr; + other.size_ = 0; + } + + ~MutableVArraySpan() + { + if (varray_) { + if (show_not_saved_warning_) { + if (!save_has_been_called_) { + std::cout << "Warning: Call `save()` to make sure that changes persist in all cases.\n"; + } } } } + MutableVArraySpan &operator=(MutableVArraySpan &&other) + { + if (this == &other) { + return *this; + } + std::destroy_at(this); + new (this) MutableVArraySpan(std::move(other)); + return *this; + } + + const VMutableArray<T> &varray() const + { + return varray_; + } + /* Write back all values from a temporary allocated array to the underlying virtual array. */ void save() { |