This repository has been archived on 2023-10-09. You can view files and clone it, but cannot push or open issues or pull requests.
Files
blender-archive/source/blender/functions/FN_generic_virtual_array.hh
2021-05-16 11:19:04 +05:30

915 lines
24 KiB
C++

/*
* 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 <optional>
#include "BLI_virtual_array.hh"
#include "FN_generic_span.hh"
namespace blender::fn {
template<typename T> class GVArray_Typed;
template<typename T> class GVMutableArray_Typed;
class GVArray;
class GVMutableArray;
using GVArrayPtr = std::unique_ptr<GVArray>;
using GVMutableArrayPtr = std::unique_ptr<GVMutableArray>;
/* A generically typed version of `VArray<T>`. */
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_;
}
/* 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_single_to_uninitialized(void *r_value) const
{
type_->construct_default(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<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();
}
/* Create a typed virtual array for this generic virtual array. */
template<typename T> GVArray_Typed<T> typed() const
{
return GVArray_Typed<T>(*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<const GVArray *>(this)->get_internal_span();
return GMutableSpan(span.type(), const_cast<void *>(span.data()), span.size());
}
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();
}
/* Create a typed virtual array for this generic virtual array. */
template<typename T> GVMutableArray_Typed<T> typed()
{
return GVMutableArray_Typed<T>(*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<typename T> class GVArray_For_VArray : public GVArray {
protected:
const VArray<T> *varray_ = nullptr;
public:
GVArray_For_VArray(const VArray<T> &varray)
: GVArray(CPPType::get<T>(), varray.size()), varray_(&varray)
{
}
protected:
GVArray_For_VArray(const int64_t size) : GVArray(CPPType::get<T>(), 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<typename T> class VArray_For_GVArray : public VArray<T> {
protected:
const GVArray *varray_ = nullptr;
public:
VArray_For_GVArray(const GVArray &varray) : VArray<T>(varray.size()), varray_(&varray)
{
BLI_assert(varray_->type().template is<T>());
}
protected:
VArray_For_GVArray(const int64_t size) : VArray<T>(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<T> get_internal_span_impl() const override
{
return varray_->get_internal_span().template typed<T>();
}
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<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
{
return varray_->get_internal_span().template typed<T>();
}
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<typename T> class GVMutableArray_For_VMutableArray : public GVMutableArray {
protected:
VMutableArray<T> *varray_ = nullptr;
public:
GVMutableArray_For_VMutableArray(VMutableArray<T> &varray)
: GVMutableArray(CPPType::get<T>(), varray.size()), varray_(&varray)
{
}
protected:
GVMutableArray_For_VMutableArray(const int64_t size) : GVMutableArray(CPPType::get<T>(), 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<T> 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<T> *)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<typename T> class GVArray_Span : public Span<T> {
private:
GVArray_GSpan varray_gspan_;
public:
GVArray_Span(const GVArray &varray) : varray_gspan_(varray)
{
BLI_assert(varray.type().is<T>());
this->data_ = (const T *)varray_gspan_.data();
this->size_ = varray_gspan_.size();
}
};
template<typename T> class GVArray_For_OwnedVArray : public GVArray_For_VArray<T> {
private:
VArrayPtr<T> owned_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))
{
}
};
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))
{
}
};
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))
{
}
};
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))
{
}
};
/* 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)...)
{
this->varray_ = &embedded_varray_;
}
};
/* 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)...)
{
this->varray_ = &embedded_varray_;
}
};
/* 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))
{
}
};
/* 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)
{
}
};
/* 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)
{
}
};
/* 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)
{
}
};
/* 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>> {
public:
GVMutableArray_For_MutableSpan(const MutableSpan<T> data)
: GVMutableArray_For_EmbeddedVMutableArray<T, VMutableArray_For_MutableSpan<T>>(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<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_;
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_;
}
}
/* 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<T> &operator*() const
{
return *varray_;
}
const VArray<T> *operator->() const
{
return varray_;
}
/* 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_;
}
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<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_;
}
}
explicit GVMutableArray_Typed(GVMutableArrayPtr gvarray) : GVMutableArray_Typed(*gvarray)
{
owned_gvarray_ = std::move(gvarray);
}
VMutableArray<T> &operator*()
{
return *varray_;
}
VMutableArray<T> *operator->()
{
return varray_;
}
operator VMutableArray<T> &()
{
return *varray_;
}
T operator[](const int64_t index) const
{
return varray_->get(index);
}
int64_t size() const
{
return varray_->size();
}
};
} // namespace blender::fn