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_array.hh')
-rw-r--r--source/blender/blenlib/BLI_array.hh193
1 files changed, 110 insertions, 83 deletions
diff --git a/source/blender/blenlib/BLI_array.hh b/source/blender/blenlib/BLI_array.hh
index b929d1220da..c7b4bdc977f 100644
--- a/source/blender/blenlib/BLI_array.hh
+++ b/source/blender/blenlib/BLI_array.hh
@@ -13,6 +13,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+
#ifndef __BLI_ARRAY_HH__
#define __BLI_ARRAY_HH__
@@ -27,8 +28,7 @@
* blender::Array should usually be used instead of blender::Vector whenever the number of elements
* is known at construction time. Note however, that blender::Array will default construct all
* elements when initialized with the size-constructor. For trivial types, this does nothing. In
- * all other cases, this adds overhead. If this becomes a problem, a different constructor which
- * does not do default construction can be added.
+ * all other cases, this adds overhead.
*
* A main benefit of using Array over Vector is that it expresses the intent of the developer
* better. It indicates that the size of the data structure is not expected to change. Furthermore,
@@ -53,11 +53,8 @@ template<
typename T,
/**
* The number of values that can be stored in the array, without doing a heap allocation.
- *
- * When T is large, the small buffer optimization is disabled by default to avoid large
- * unexpected allocations on the stack. It can still be enabled explicitely though.
*/
- uint InlineBufferCapacity = (sizeof(T) < 100) ? 4 : 0,
+ int64_t InlineBufferCapacity = default_inline_buffer_capacity(sizeof(T)),
/**
* The allocator used by this array. Should rarely be changed, except when you don't want that
* MEM_* functions are used internally.
@@ -66,16 +63,16 @@ template<
class Array {
private:
/** The beginning of the array. It might point into the inline buffer. */
- T *m_data;
+ T *data_;
/** Number of elements in the array. */
- uint m_size;
+ int64_t size_;
/** Used for allocations when the inline buffer is too small. */
- Allocator m_allocator;
+ Allocator allocator_;
/** A placeholder buffer that will remain uninitialized until it is used. */
- AlignedBuffer<sizeof(T) * InlineBufferCapacity, alignof(T)> m_inline_buffer;
+ TypedBuffer<T, InlineBufferCapacity> inline_buffer_;
public:
/**
@@ -83,23 +80,29 @@ class Array {
*/
Array()
{
- m_data = this->inline_buffer();
- m_size = 0;
+ data_ = inline_buffer_;
+ size_ = 0;
}
/**
* Create a new array that contains copies of all values.
*/
- Array(Span<T> values)
+ template<typename U, typename std::enable_if_t<std::is_convertible_v<U, T>> * = nullptr>
+ Array(Span<U> values, Allocator allocator = {}) : allocator_(allocator)
{
- m_size = values.size();
- m_data = this->get_buffer_for_size(values.size());
- uninitialized_copy_n(values.data(), m_size, m_data);
+ size_ = values.size();
+ data_ = this->get_buffer_for_size(values.size());
+ uninitialized_convert_n<U, T>(values.data(), size_, data_);
}
/**
* Create a new array that contains copies of all values.
*/
+ template<typename U, typename std::enable_if_t<std::is_convertible_v<U, T>> * = nullptr>
+ Array(const std::initializer_list<U> &values) : Array(Span<U>(values))
+ {
+ }
+
Array(const std::initializer_list<T> &values) : Array(Span<T>(values))
{
}
@@ -112,53 +115,69 @@ class Array {
* even for non-trivial types. This should not be the default though, because one can easily mess
* up when dealing with uninitialized memory.
*/
- explicit Array(uint size)
+ explicit Array(int64_t size)
{
- m_size = size;
- m_data = this->get_buffer_for_size(size);
- default_construct_n(m_data, size);
+ size_ = size;
+ data_ = this->get_buffer_for_size(size);
+ default_construct_n(data_, size);
}
/**
* Create a new array with the given size. All values will be initialized by copying the given
* default.
*/
- Array(uint size, const T &value)
+ Array(int64_t size, const T &value)
{
- m_size = size;
- m_data = this->get_buffer_for_size(size);
- uninitialized_fill_n(m_data, m_size, value);
+ BLI_assert(size >= 0);
+ size_ = size;
+ data_ = this->get_buffer_for_size(size);
+ uninitialized_fill_n(data_, size_, value);
}
- Array(const Array &other) : m_allocator(other.m_allocator)
+ /**
+ * Create a new array with uninitialized elements. The caller is responsible for constructing the
+ * elements. Moving, copying or destructing an Array with uninitialized elements invokes
+ * undefined behavior.
+ *
+ * This should be used very rarely. Note, that the normal size-constructor also does not
+ * initialize the elements when T is trivially constructible. Therefore, it only makes sense to
+ * use this with non trivially constructible types.
+ *
+ * Usage:
+ * Array<std::string> my_strings(10, NoInitialization());
+ */
+ Array(int64_t size, NoInitialization)
{
- m_size = other.size();
+ BLI_assert(size >= 0);
+ size_ = size;
+ data_ = this->get_buffer_for_size(size);
+ }
- m_data = this->get_buffer_for_size(other.size());
- uninitialized_copy_n(other.data(), m_size, m_data);
+ Array(const Array &other) : Array(other.as_span(), other.allocator_)
+ {
}
- Array(Array &&other) noexcept : m_allocator(other.m_allocator)
+ Array(Array &&other) noexcept : allocator_(other.allocator_)
{
- m_size = other.m_size;
+ size_ = other.size_;
if (!other.uses_inline_buffer()) {
- m_data = other.m_data;
+ data_ = other.data_;
}
else {
- m_data = this->get_buffer_for_size(m_size);
- uninitialized_relocate_n(other.m_data, m_size, m_data);
+ data_ = this->get_buffer_for_size(size_);
+ uninitialized_relocate_n(other.data_, size_, data_);
}
- other.m_data = other.inline_buffer();
- other.m_size = 0;
+ other.data_ = other.inline_buffer_;
+ other.size_ = 0;
}
~Array()
{
- destruct_n(m_data, m_size);
+ destruct_n(data_, size_);
if (!this->uses_inline_buffer()) {
- m_allocator.deallocate((void *)m_data);
+ allocator_.deallocate((void *)data_);
}
}
@@ -184,44 +203,58 @@ class Array {
return *this;
}
+ T &operator[](int64_t index)
+ {
+ BLI_assert(index >= 0);
+ BLI_assert(index < size_);
+ return data_[index];
+ }
+
+ const T &operator[](int64_t index) const
+ {
+ BLI_assert(index >= 0);
+ BLI_assert(index < size_);
+ return data_[index];
+ }
+
operator Span<T>() const
{
- return Span<T>(m_data, m_size);
+ return Span<T>(data_, size_);
}
operator MutableSpan<T>()
{
- return MutableSpan<T>(m_data, m_size);
+ return MutableSpan<T>(data_, size_);
}
- Span<T> as_span() const
+ template<typename U, typename std::enable_if_t<is_convertible_pointer_v<T, U>> * = nullptr>
+ operator Span<U>() const
{
- return *this;
+ return Span<U>(data_, size_);
}
- MutableSpan<T> as_mutable_span()
+ template<typename U, typename std::enable_if_t<is_convertible_pointer_v<T, U>> * = nullptr>
+ operator MutableSpan<U>()
{
- return *this;
+ return MutableSpan<U>(data_, size_);
}
- T &operator[](uint index)
+ Span<T> as_span() const
{
- BLI_assert(index < m_size);
- return m_data[index];
+ return *this;
}
- const T &operator[](uint index) const
+ MutableSpan<T> as_mutable_span()
{
- BLI_assert(index < m_size);
- return m_data[index];
+ return *this;
}
/**
* Returns the number of elements in the array.
*/
- uint size() const
+ int64_t size() const
{
- return m_size;
+ return size_;
}
/**
@@ -229,23 +262,15 @@ class Array {
*/
bool is_empty() const
{
- return m_size == 0;
+ return size_ == 0;
}
/**
- * Copies the value to all indices in the array.
+ * Copies the given value to every element in the array.
*/
- void fill(const T &value)
+ void fill(const T &value) const
{
- initialized_fill_n(m_data, m_size, value);
- }
-
- /**
- * Copies the value to the given indices in the array.
- */
- void fill_indices(Span<uint> indices, const T &value)
- {
- MutableSpan<T>(*this).fill_indices(indices, value);
+ initialized_fill_n(data_, size_, value);
}
/**
@@ -253,31 +278,31 @@ class Array {
*/
const T *data() const
{
- return m_data;
+ return data_;
}
T *data()
{
- return m_data;
+ return data_;
}
const T *begin() const
{
- return m_data;
+ return data_;
}
const T *end() const
{
- return m_data + m_size;
+ return data_ + size_;
}
T *begin()
{
- return m_data;
+ return data_;
}
T *end()
{
- return m_data + m_size;
+ return data_ + size_;
}
/**
@@ -285,7 +310,7 @@ class Array {
*/
IndexRange index_range() const
{
- return IndexRange(m_size);
+ return IndexRange(size_);
}
/**
@@ -294,7 +319,7 @@ class Array {
*/
void clear_without_destruct()
{
- m_size = 0;
+ size_ = 0;
}
/**
@@ -302,45 +327,47 @@ class Array {
*/
Allocator &allocator()
{
- return m_allocator;
+ return allocator_;
}
/**
* Get the value of the InlineBufferCapacity template argument. This is the number of elements
* that can be stored without doing an allocation.
*/
- static uint inline_buffer_capacity()
+ static int64_t inline_buffer_capacity()
{
return InlineBufferCapacity;
}
private:
- T *get_buffer_for_size(uint size)
+ T *get_buffer_for_size(int64_t size)
{
if (size <= InlineBufferCapacity) {
- return this->inline_buffer();
+ return inline_buffer_;
}
else {
return this->allocate(size);
}
}
- T *inline_buffer() const
- {
- return (T *)m_inline_buffer.ptr();
- }
-
- T *allocate(uint size)
+ T *allocate(int64_t size)
{
- return (T *)m_allocator.allocate(size * sizeof(T), alignof(T), AT);
+ return (T *)allocator_.allocate((size_t)size * sizeof(T), alignof(T), AT);
}
bool uses_inline_buffer() const
{
- return m_data == this->inline_buffer();
+ return data_ == inline_buffer_;
}
};
+/**
+ * Same as a normal Array, but does not use Blender's guarded allocator. This is useful when
+ * allocating memory with static storage duration.
+ */
+template<typename T, int64_t InlineBufferCapacity = default_inline_buffer_capacity(sizeof(T))>
+using RawArray = Array<T, InlineBufferCapacity, RawAllocator>;
+
} // namespace blender
#endif /* __BLI_ARRAY_HH__ */