256 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			256 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/* 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
 |