Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/blenlib/BLI_generic_array.hh')
-rw-r--r--source/blender/blenlib/BLI_generic_array.hh255
1 files changed, 255 insertions, 0 deletions
diff --git a/source/blender/blenlib/BLI_generic_array.hh b/source/blender/blenlib/BLI_generic_array.hh
new file mode 100644
index 00000000000..e1b6b29874a
--- /dev/null
+++ b/source/blender/blenlib/BLI_generic_array.hh
@@ -0,0 +1,255 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#pragma once
+
+/** \file
+ * \ingroup bli
+ *
+ * This is a generic counterpart to #blender::Array, used when the type is not known at runtime.
+ *
+ * `GArray` should generally only be used for passing data around in dynamic contexts.
+ * It does not support a few things that #blender::Array supports:
+ * - Small object optimization / inline buffer.
+ * - Exception safety and various more specific constructors.
+ */
+
+#include "BLI_allocator.hh"
+#include "BLI_cpp_type.hh"
+#include "BLI_generic_span.hh"
+
+namespace blender {
+
+template<
+ /**
+ * The allocator used by this array. Should rarely be changed, except when you don't want that
+ * MEM_* functions are used internally.
+ */
+ typename Allocator = GuardedAllocator>
+class GArray {
+ protected:
+ /** The type of the data in the array, will be null after the array is default constructed,
+ * but a value should be assigned before any other interaction with the array. */
+ const CPPType *type_ = nullptr;
+ void *data_ = nullptr;
+ int64_t size_ = 0;
+
+ Allocator allocator_;
+
+ public:
+ /**
+ * The default constructor creates an empty array, the only situation in which the type is
+ * allowed to be null. This default constructor exists so `GArray` can be used in containers,
+ * but the type should be supplied before doing anything else to the array.
+ */
+ GArray(Allocator allocator = {}) noexcept : allocator_(allocator)
+ {
+ }
+
+ GArray(NoExceptConstructor, Allocator allocator = {}) noexcept : GArray(allocator)
+ {
+ }
+
+ /**
+ * Create and allocate a new array, with elements default constructed
+ * (which does not do anything for trivial types).
+ */
+ GArray(const CPPType &type, int64_t size, Allocator allocator = {}) : GArray(type, allocator)
+ {
+ BLI_assert(size >= 0);
+ size_ = size;
+ data_ = this->allocate(size_);
+ type_->default_construct_n(data_, size_);
+ }
+
+ /**
+ * Create an empty array with just a type.
+ */
+ GArray(const CPPType &type, Allocator allocator = {}) : GArray(allocator)
+ {
+ type_ = &type;
+ }
+
+ /**
+ * Take ownership of a buffer with a provided size. The buffer should be
+ * allocated with the same allocator provided to the constructor.
+ */
+ GArray(const CPPType &type, void *buffer, int64_t size, Allocator allocator = {})
+ : GArray(type, allocator)
+ {
+ BLI_assert(size >= 0);
+ BLI_assert(buffer != nullptr || size == 0);
+ BLI_assert(type_->pointer_has_valid_alignment(buffer));
+
+ data_ = buffer;
+ size_ = size;
+ }
+
+ /**
+ * Create an array by copying values from a generic span.
+ */
+ GArray(const GSpan span, Allocator allocator = {}) : GArray(span.type(), span.size(), allocator)
+ {
+ if (span.data() != nullptr) {
+ BLI_assert(span.size() != 0);
+ /* Use copy assign rather than construct since the memory is already initialized. */
+ type_->copy_assign_n(span.data(), data_, size_);
+ }
+ }
+
+ /**
+ * Create an array by copying values from another generic array.
+ */
+ GArray(const GArray &other) : GArray(other.as_span(), other.allocator())
+ {
+ }
+
+ /**
+ * Create an array by taking ownership of another array's data, clearing the data in the other.
+ */
+ GArray(GArray &&other) : GArray(other.type(), other.data(), other.size(), other.allocator())
+ {
+ other.data_ = nullptr;
+ other.size_ = 0;
+ }
+
+ ~GArray()
+ {
+ if (data_ != nullptr) {
+ type_->destruct_n(data_, size_);
+ this->deallocate(data_);
+ }
+ }
+
+ GArray &operator=(const GArray &other)
+ {
+ return copy_assign_container(*this, other);
+ }
+
+ GArray &operator=(GArray &&other)
+ {
+ return move_assign_container(*this, std::move(other));
+ }
+
+ const CPPType &type() const
+ {
+ BLI_assert(type_ != nullptr);
+ return *type_;
+ }
+
+ bool is_empty() const
+ {
+ return size_ == 0;
+ }
+
+ /**
+ * Return the number of elements in the array (not the size in bytes).
+ */
+ int64_t size() const
+ {
+ return size_;
+ }
+
+ /**
+ * Get a pointer to the beginning of the array.
+ */
+ const void *data() const
+ {
+ return data_;
+ }
+ void *data()
+ {
+ return data_;
+ }
+
+ const void *operator[](int64_t index) const
+ {
+ BLI_assert(index < size_);
+ return POINTER_OFFSET(data_, type_->size() * index);
+ }
+
+ void *operator[](int64_t index)
+ {
+ BLI_assert(index < size_);
+ return POINTER_OFFSET(data_, type_->size() * index);
+ }
+
+ operator GSpan() const
+ {
+ BLI_assert(type_ != nullptr);
+ return GSpan(*type_, data_, size_);
+ }
+
+ operator GMutableSpan()
+ {
+ BLI_assert(type_ != nullptr);
+ return GMutableSpan(*type_, data_, size_);
+ }
+
+ GSpan as_span() const
+ {
+ return *this;
+ }
+
+ GMutableSpan as_mutable_span()
+ {
+ return *this;
+ }
+
+ /**
+ * Access the allocator used by this array.
+ */
+ Allocator &allocator()
+ {
+ return allocator_;
+ }
+ const Allocator &allocator() const
+ {
+ return allocator_;
+ }
+
+ /**
+ * Destruct values and create a new array of the given size. The values in the new array are
+ * default constructed.
+ */
+ void reinitialize(const int64_t new_size)
+ {
+ BLI_assert(new_size >= 0);
+ int64_t old_size = size_;
+
+ type_->destruct_n(data_, size_);
+ size_ = 0;
+
+ if (new_size <= old_size) {
+ type_->default_construct_n(data_, new_size);
+ }
+ else {
+ void *new_data = this->allocate(new_size);
+ try {
+ type_->default_construct_n(new_data, new_size);
+ }
+ catch (...) {
+ this->deallocate(new_data);
+ throw;
+ }
+ this->deallocate(data_);
+ data_ = new_data;
+ }
+
+ size_ = new_size;
+ }
+
+ private:
+ void *allocate(int64_t size)
+ {
+ const int64_t item_size = type_->size();
+ const int64_t alignment = type_->alignment();
+ return allocator_.allocate(static_cast<size_t>(size) * item_size, alignment, AT);
+ }
+
+ void deallocate(void *ptr)
+ {
+ allocator_.deallocate(ptr);
+ }
+};
+
+} // namespace blender