Changeset View
Changeset View
Standalone View
Standalone View
source/blender/functions/FN_generic_array.hh
- This file was added.
| /* | |||||
| * 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 | |||||
| * | |||||
| * 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. | |||||
| * | |||||
| * Note that a generic vector is purposefully not added to Blender, since it would encourage use of | |||||
| * the `append` function, which would be inefficient for dynamic types. Dynamic types should | |||||
JacquesLucke: Not sure if this comment really belongs here, seems unrelated to the entire file. | |||||
| * usually be processed in batches. | |||||
| */ | |||||
| #include "BLI_allocator.hh" | |||||
| #include "FN_cpp_type.hh" | |||||
| #include "FN_generic_span.hh" | |||||
| namespace blender::fn { | |||||
| 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_; | |||||
| void *data_; | |||||
| int64_t size_; | |||||
| 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) | |||||
| { | |||||
| type_ = nullptr; | |||||
| data_ = nullptr; | |||||
| size_ = 0; | |||||
| } | |||||
| 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; | |||||
| data_ = nullptr; | |||||
| size_ = 0; | |||||
| } | |||||
| /** | |||||
| * 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); | |||||
| type_->copy_construct_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 and destructing it. | |||||
| */ | |||||
| 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); | |||||
| } | |||||
Done Inline ActionsThis expects that there is a NoExceptConstructor constructor. JacquesLucke: This expects that there is a `NoExceptConstructor` constructor. | |||||
| 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::fn | |||||
Not sure if this comment really belongs here, seems unrelated to the entire file.