2023-08-15 16:20:26 +02:00
|
|
|
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
2023-05-31 16:19:06 +02:00
|
|
|
*
|
|
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
2019-09-12 14:23:21 +02:00
|
|
|
|
2020-04-21 17:31:56 +02:00
|
|
|
#pragma once
|
2019-09-13 13:12:26 +02:00
|
|
|
|
2019-09-12 14:23:21 +02:00
|
|
|
/** \file
|
|
|
|
* \ingroup bli
|
|
|
|
*
|
2020-06-09 10:27:24 +02:00
|
|
|
* A `blender::Stack<T>` is a dynamically growing FILO (first-in, last-out) data structure. It is
|
2020-06-09 10:10:56 +02:00
|
|
|
* designed to be a more convenient and efficient replacement for `std::stack`.
|
|
|
|
*
|
|
|
|
* The improved efficiency is mainly achieved by supporting small buffer optimization. As long as
|
|
|
|
* the number of elements added to the stack stays below InlineBufferCapacity, no heap allocation
|
|
|
|
* is done. Consequently, values stored in the stack have to be movable and they might be moved,
|
|
|
|
* when the stack is moved.
|
|
|
|
*
|
|
|
|
* A Vector can be used to emulate a stack. However, this stack implementation is more efficient
|
|
|
|
* when all you have to do is to push and pop elements. That is because a vector guarantees that
|
|
|
|
* all elements are in a contiguous array. Therefore, it has to copy all elements to a new buffer
|
|
|
|
* when it grows. This stack implementation does not have to copy all previously pushed elements
|
|
|
|
* when it grows.
|
|
|
|
*
|
2020-06-09 10:27:24 +02:00
|
|
|
* blender::Stack is implemented using a double linked list of chunks. Each chunk contains an array
|
|
|
|
* of elements. The chunk size increases exponentially with every new chunk that is required. The
|
2020-06-09 10:10:56 +02:00
|
|
|
* lowest chunk, i.e. the one that is used for the first few pushed elements, is embedded into the
|
|
|
|
* stack.
|
2019-09-12 14:23:21 +02:00
|
|
|
*/
|
|
|
|
|
2020-06-09 10:10:56 +02:00
|
|
|
#include "BLI_allocator.hh"
|
|
|
|
#include "BLI_memory_utils.hh"
|
2020-06-09 11:58:47 +02:00
|
|
|
#include "BLI_span.hh"
|
2019-09-12 14:23:21 +02:00
|
|
|
|
2020-06-09 10:27:24 +02:00
|
|
|
namespace blender {
|
2019-09-12 14:23:21 +02:00
|
|
|
|
2020-06-09 10:10:56 +02:00
|
|
|
/**
|
|
|
|
* A StackChunk references a contiguous memory buffer. Multiple StackChunk instances are linked in
|
|
|
|
* a double linked list.
|
|
|
|
*/
|
|
|
|
template<typename T> struct StackChunk {
|
|
|
|
/** The below chunk contains the elements that have been pushed on the stack before. */
|
|
|
|
StackChunk *below;
|
|
|
|
/** The above chunk contains the elements that have been pushed on the stack afterwards. */
|
|
|
|
StackChunk *above;
|
|
|
|
/** Pointer to the first element of the referenced buffer. */
|
|
|
|
T *begin;
|
|
|
|
/** Pointer to one element past the end of the referenced buffer. */
|
|
|
|
T *capacity_end;
|
|
|
|
|
2020-07-20 12:16:20 +02:00
|
|
|
int64_t capacity() const
|
2020-06-09 10:10:56 +02:00
|
|
|
{
|
|
|
|
return capacity_end - begin;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
template<
|
|
|
|
/** Type of the elements that are stored in the stack. */
|
|
|
|
typename T,
|
|
|
|
/**
|
|
|
|
* The number of values that can be stored in this stack, without doing a heap allocation.
|
|
|
|
* Sometimes it can make sense to increase this value a lot. The memory in the inline buffer is
|
|
|
|
* not initialized when it is not needed.
|
|
|
|
*/
|
2020-07-20 16:00:20 +02:00
|
|
|
int64_t InlineBufferCapacity = default_inline_buffer_capacity(sizeof(T)),
|
2020-06-09 10:10:56 +02:00
|
|
|
/**
|
|
|
|
* The allocator used by this stack. Should rarely be changed, except when you don't want that
|
|
|
|
* MEM_* is used internally.
|
|
|
|
*/
|
|
|
|
typename Allocator = GuardedAllocator>
|
2020-04-23 20:05:53 +02:00
|
|
|
class Stack {
|
2021-03-20 15:42:35 +01:00
|
|
|
public:
|
|
|
|
using value_type = T;
|
|
|
|
using pointer = T *;
|
|
|
|
using const_pointer = const T *;
|
|
|
|
using reference = T &;
|
|
|
|
using const_reference = const T &;
|
|
|
|
using size_type = int64_t;
|
|
|
|
|
2019-09-12 14:23:21 +02:00
|
|
|
private:
|
2020-06-09 10:10:56 +02:00
|
|
|
using Chunk = StackChunk<T>;
|
2019-09-12 14:23:21 +02:00
|
|
|
|
2020-06-09 10:10:56 +02:00
|
|
|
/**
|
|
|
|
* Points to one element after top-most value in the stack.
|
|
|
|
*
|
|
|
|
* Invariant:
|
2020-07-03 14:15:05 +02:00
|
|
|
* If size_ == 0
|
|
|
|
* then: top_ == inline_chunk_.begin
|
|
|
|
* else: &peek() == top_ - 1;
|
2020-06-09 10:10:56 +02:00
|
|
|
*/
|
2020-07-03 14:15:05 +02:00
|
|
|
T *top_;
|
2020-06-09 10:10:56 +02:00
|
|
|
|
2020-07-03 14:15:05 +02:00
|
|
|
/** Points to the chunk that references the memory pointed to by top_. */
|
|
|
|
Chunk *top_chunk_;
|
2019-09-12 14:23:21 +02:00
|
|
|
|
|
|
|
/**
|
2020-06-09 10:10:56 +02:00
|
|
|
* Number of elements in the entire stack. The sum of initialized element counts in the chunks.
|
2019-09-12 14:23:21 +02:00
|
|
|
*/
|
2020-07-20 12:16:20 +02:00
|
|
|
int64_t size_;
|
2020-06-09 10:10:56 +02:00
|
|
|
|
|
|
|
/** The buffer used to implement small object optimization. */
|
2022-05-25 16:28:07 +02:00
|
|
|
BLI_NO_UNIQUE_ADDRESS TypedBuffer<T, InlineBufferCapacity> inline_buffer_;
|
2020-06-09 10:10:56 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* A chunk referencing the inline buffer. This is always the bottom-most chunk.
|
2020-07-03 14:15:05 +02:00
|
|
|
* So inline_chunk_.below == nullptr.
|
2020-06-09 10:10:56 +02:00
|
|
|
*/
|
2020-07-03 14:15:05 +02:00
|
|
|
Chunk inline_chunk_;
|
2019-09-12 14:23:21 +02:00
|
|
|
|
2020-06-09 10:10:56 +02:00
|
|
|
/** Used for allocations when the inline buffer is not large enough. */
|
2022-05-25 16:28:07 +02:00
|
|
|
BLI_NO_UNIQUE_ADDRESS Allocator allocator_;
|
2020-06-09 10:10:56 +02:00
|
|
|
|
|
|
|
public:
|
|
|
|
/**
|
|
|
|
* Initialize an empty stack. No heap allocation is done.
|
|
|
|
*/
|
2020-08-19 16:44:53 +02:00
|
|
|
Stack(Allocator allocator = {}) noexcept : allocator_(allocator)
|
2019-09-12 14:23:21 +02:00
|
|
|
{
|
2020-07-03 14:15:05 +02:00
|
|
|
inline_chunk_.below = nullptr;
|
|
|
|
inline_chunk_.above = nullptr;
|
2020-07-06 10:56:26 +02:00
|
|
|
inline_chunk_.begin = inline_buffer_;
|
|
|
|
inline_chunk_.capacity_end = inline_buffer_ + InlineBufferCapacity;
|
2020-06-09 10:10:56 +02:00
|
|
|
|
2020-07-06 10:56:26 +02:00
|
|
|
top_ = inline_buffer_;
|
2020-07-03 14:15:05 +02:00
|
|
|
top_chunk_ = &inline_chunk_;
|
|
|
|
size_ = 0;
|
2019-09-12 14:23:21 +02:00
|
|
|
}
|
|
|
|
|
2023-03-29 16:50:54 +02:00
|
|
|
Stack(NoExceptConstructor, Allocator allocator = {}) noexcept : Stack(allocator) {}
|
2020-08-19 16:44:53 +02:00
|
|
|
|
2019-09-12 14:23:21 +02:00
|
|
|
/**
|
2020-06-09 10:10:56 +02:00
|
|
|
* Create a new stack that contains the given elements. The values are pushed to the stack in
|
|
|
|
* the order they are in the array.
|
2019-09-12 14:23:21 +02:00
|
|
|
*/
|
2020-08-19 16:44:53 +02:00
|
|
|
Stack(Span<T> values, Allocator allocator = {}) : Stack(NoExceptConstructor(), allocator)
|
2019-09-12 14:23:21 +02:00
|
|
|
{
|
2020-06-09 10:10:56 +02:00
|
|
|
this->push_multiple(values);
|
2019-09-12 14:23:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2020-06-09 10:10:56 +02:00
|
|
|
* Create a new stack that contains the given elements. The values are pushed to the stack in the
|
|
|
|
* order they are in the array.
|
|
|
|
*
|
|
|
|
* Example:
|
|
|
|
* Stack<int> stack = {4, 5, 6};
|
|
|
|
* assert(stack.pop() == 6);
|
|
|
|
* assert(stack.pop() == 5);
|
2019-09-12 14:23:21 +02:00
|
|
|
*/
|
2020-08-19 16:44:53 +02:00
|
|
|
Stack(const std::initializer_list<T> &values, Allocator allocator = {})
|
|
|
|
: Stack(Span<T>(values), allocator)
|
2020-06-09 10:10:56 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2020-08-19 16:44:53 +02:00
|
|
|
Stack(const Stack &other) : Stack(NoExceptConstructor(), other.allocator_)
|
2020-06-09 10:10:56 +02:00
|
|
|
{
|
2020-07-03 14:15:05 +02:00
|
|
|
for (const Chunk *chunk = &other.inline_chunk_; chunk; chunk = chunk->above) {
|
2020-06-09 10:10:56 +02:00
|
|
|
const T *begin = chunk->begin;
|
2020-07-03 14:15:05 +02:00
|
|
|
const T *end = (chunk == other.top_chunk_) ? other.top_ : chunk->capacity_end;
|
2020-06-09 11:58:47 +02:00
|
|
|
this->push_multiple(Span<T>(begin, end - begin));
|
2020-06-09 10:10:56 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-19 16:44:53 +02:00
|
|
|
Stack(Stack &&other) noexcept(std::is_nothrow_move_constructible_v<T>)
|
|
|
|
: Stack(NoExceptConstructor(), other.allocator_)
|
2020-06-09 10:10:56 +02:00
|
|
|
{
|
2020-07-06 10:56:26 +02:00
|
|
|
uninitialized_relocate_n<T>(
|
|
|
|
other.inline_buffer_, std::min(other.size_, InlineBufferCapacity), inline_buffer_);
|
2020-06-09 10:10:56 +02:00
|
|
|
|
2020-07-03 14:15:05 +02:00
|
|
|
inline_chunk_.above = other.inline_chunk_.above;
|
|
|
|
size_ = other.size_;
|
2020-06-09 10:10:56 +02:00
|
|
|
|
2020-07-04 17:28:05 +02:00
|
|
|
if (inline_chunk_.above != nullptr) {
|
|
|
|
inline_chunk_.above->below = &inline_chunk_;
|
|
|
|
}
|
|
|
|
|
2020-07-03 14:15:05 +02:00
|
|
|
if (size_ <= InlineBufferCapacity) {
|
|
|
|
top_chunk_ = &inline_chunk_;
|
2020-07-06 10:56:26 +02:00
|
|
|
top_ = inline_buffer_ + size_;
|
2020-06-09 10:10:56 +02:00
|
|
|
}
|
|
|
|
else {
|
2020-07-03 14:15:05 +02:00
|
|
|
top_chunk_ = other.top_chunk_;
|
|
|
|
top_ = other.top_;
|
2020-06-09 10:10:56 +02:00
|
|
|
}
|
|
|
|
|
2020-07-03 14:15:05 +02:00
|
|
|
other.size_ = 0;
|
|
|
|
other.inline_chunk_.above = nullptr;
|
|
|
|
other.top_chunk_ = &other.inline_chunk_;
|
|
|
|
other.top_ = other.top_chunk_->begin;
|
2020-06-09 10:10:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
~Stack()
|
2019-09-12 14:23:21 +02:00
|
|
|
{
|
2020-06-09 10:10:56 +02:00
|
|
|
this->destruct_all_elements();
|
|
|
|
Chunk *above_chunk;
|
2020-07-03 14:15:05 +02:00
|
|
|
for (Chunk *chunk = inline_chunk_.above; chunk; chunk = above_chunk) {
|
2020-06-09 10:10:56 +02:00
|
|
|
above_chunk = chunk->above;
|
2020-07-03 14:15:05 +02:00
|
|
|
allocator_.deallocate(chunk);
|
2020-06-09 10:10:56 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-19 16:44:53 +02:00
|
|
|
Stack &operator=(const Stack &other)
|
2020-06-09 10:10:56 +02:00
|
|
|
{
|
2020-08-19 16:44:53 +02:00
|
|
|
return copy_assign_container(*this, other);
|
2020-06-09 10:10:56 +02:00
|
|
|
}
|
|
|
|
|
2020-08-19 16:44:53 +02:00
|
|
|
Stack &operator=(Stack &&other)
|
2020-06-09 10:10:56 +02:00
|
|
|
{
|
2020-08-19 16:44:53 +02:00
|
|
|
return move_assign_container(*this, std::move(other));
|
2019-09-12 14:23:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Add a new element to the top of the stack.
|
|
|
|
*/
|
|
|
|
void push(const T &value)
|
|
|
|
{
|
2020-08-19 16:44:53 +02:00
|
|
|
this->push_as(value);
|
2019-09-12 14:23:21 +02:00
|
|
|
}
|
|
|
|
void push(T &&value)
|
2020-08-19 16:44:53 +02:00
|
|
|
{
|
|
|
|
this->push_as(std::move(value));
|
|
|
|
}
|
2021-04-19 15:56:12 +02:00
|
|
|
/* This is similar to `std::stack::emplace`. */
|
2021-07-30 08:19:19 +02:00
|
|
|
template<typename... ForwardT> void push_as(ForwardT &&...value)
|
2019-09-12 14:23:21 +02:00
|
|
|
{
|
2020-07-03 14:15:05 +02:00
|
|
|
if (top_ == top_chunk_->capacity_end) {
|
2020-06-09 10:10:56 +02:00
|
|
|
this->activate_next_chunk(1);
|
|
|
|
}
|
2020-08-19 16:44:53 +02:00
|
|
|
try {
|
2021-04-17 19:02:30 +02:00
|
|
|
new (top_) T(std::forward<ForwardT>(value)...);
|
2020-08-19 16:44:53 +02:00
|
|
|
top_++;
|
|
|
|
size_++;
|
|
|
|
}
|
|
|
|
catch (...) {
|
|
|
|
this->move_top_pointer_back_to_below_chunk();
|
|
|
|
throw;
|
|
|
|
}
|
2020-02-10 13:54:57 +01:00
|
|
|
}
|
|
|
|
|
2019-09-12 14:23:21 +02:00
|
|
|
/**
|
2020-06-09 10:10:56 +02:00
|
|
|
* Remove and return the top-most element from the stack. This invokes undefined behavior when
|
|
|
|
* the stack is empty.
|
2019-09-12 14:23:21 +02:00
|
|
|
*/
|
|
|
|
T pop()
|
|
|
|
{
|
2020-07-03 14:15:05 +02:00
|
|
|
BLI_assert(size_ > 0);
|
2020-08-19 16:44:53 +02:00
|
|
|
T value = std::move(*(top_ - 1));
|
2020-07-03 14:15:05 +02:00
|
|
|
top_--;
|
|
|
|
top_->~T();
|
|
|
|
size_--;
|
|
|
|
|
|
|
|
if (top_ == top_chunk_->begin) {
|
|
|
|
if (top_chunk_->below != nullptr) {
|
|
|
|
top_chunk_ = top_chunk_->below;
|
|
|
|
top_ = top_chunk_->capacity_end;
|
2020-06-09 10:10:56 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return value;
|
2019-09-12 14:23:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2020-06-09 10:10:56 +02:00
|
|
|
* Get a reference to the top-most element without removing it from the stack. This invokes
|
|
|
|
* undefined behavior when the stack is empty.
|
2019-09-12 14:23:21 +02:00
|
|
|
*/
|
|
|
|
T &peek()
|
|
|
|
{
|
2020-07-03 14:15:05 +02:00
|
|
|
BLI_assert(size_ > 0);
|
|
|
|
BLI_assert(top_ > top_chunk_->begin);
|
|
|
|
return *(top_ - 1);
|
2019-09-12 14:23:21 +02:00
|
|
|
}
|
2020-06-09 10:10:56 +02:00
|
|
|
const T &peek() const
|
2019-09-12 14:23:21 +02:00
|
|
|
{
|
2020-07-03 14:15:05 +02:00
|
|
|
BLI_assert(size_ > 0);
|
|
|
|
BLI_assert(top_ > top_chunk_->begin);
|
|
|
|
return *(top_ - 1);
|
2019-09-12 14:23:21 +02:00
|
|
|
}
|
|
|
|
|
2020-06-09 10:10:56 +02:00
|
|
|
/**
|
|
|
|
* Add multiple elements to the stack. The values are pushed in the order they are in the array.
|
|
|
|
* This method is more efficient than pushing multiple elements individually and might cause less
|
|
|
|
* heap allocations.
|
|
|
|
*/
|
2020-06-09 11:58:47 +02:00
|
|
|
void push_multiple(Span<T> values)
|
2019-09-12 14:23:21 +02:00
|
|
|
{
|
2020-06-09 11:58:47 +02:00
|
|
|
Span<T> remaining_values = values;
|
2020-06-09 10:10:56 +02:00
|
|
|
while (!remaining_values.is_empty()) {
|
2020-07-03 14:15:05 +02:00
|
|
|
if (top_ == top_chunk_->capacity_end) {
|
2020-06-09 10:10:56 +02:00
|
|
|
this->activate_next_chunk(remaining_values.size());
|
|
|
|
}
|
|
|
|
|
2020-07-20 12:16:20 +02:00
|
|
|
const int64_t remaining_capacity = top_chunk_->capacity_end - top_;
|
|
|
|
const int64_t amount = std::min(remaining_values.size(), remaining_capacity);
|
2020-08-19 16:44:53 +02:00
|
|
|
try {
|
|
|
|
uninitialized_copy_n(remaining_values.data(), amount, top_);
|
|
|
|
}
|
|
|
|
catch (...) {
|
|
|
|
this->move_top_pointer_back_to_below_chunk();
|
|
|
|
throw;
|
|
|
|
}
|
2020-07-03 14:15:05 +02:00
|
|
|
top_ += amount;
|
2020-08-19 16:44:53 +02:00
|
|
|
size_ += amount;
|
2020-06-09 10:10:56 +02:00
|
|
|
|
|
|
|
remaining_values = remaining_values.drop_front(amount);
|
|
|
|
}
|
2019-09-12 14:23:21 +02:00
|
|
|
}
|
|
|
|
|
2020-06-09 10:10:56 +02:00
|
|
|
/**
|
|
|
|
* Returns true when the size is zero.
|
|
|
|
*/
|
|
|
|
bool is_empty() const
|
2019-09-12 14:23:21 +02:00
|
|
|
{
|
2020-07-03 14:15:05 +02:00
|
|
|
return size_ == 0;
|
2019-09-12 14:23:21 +02:00
|
|
|
}
|
|
|
|
|
2020-06-09 10:10:56 +02:00
|
|
|
/**
|
|
|
|
* Returns the number of elements in the stack.
|
|
|
|
*/
|
2020-07-20 12:16:20 +02:00
|
|
|
int64_t size() const
|
2019-09-12 14:23:21 +02:00
|
|
|
{
|
2020-07-03 14:15:05 +02:00
|
|
|
return size_;
|
2019-09-12 14:23:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2020-06-09 10:10:56 +02:00
|
|
|
* Removes all elements from the stack. The memory is not freed, so it is more efficient to reuse
|
|
|
|
* the stack than to create a new one.
|
2019-09-12 14:23:21 +02:00
|
|
|
*/
|
|
|
|
void clear()
|
|
|
|
{
|
2020-06-09 10:10:56 +02:00
|
|
|
this->destruct_all_elements();
|
2020-07-03 14:15:05 +02:00
|
|
|
top_chunk_ = &inline_chunk_;
|
|
|
|
top_ = top_chunk_->begin;
|
2019-09-12 14:23:21 +02:00
|
|
|
}
|
|
|
|
|
2022-12-09 12:00:37 +01:00
|
|
|
/**
|
|
|
|
* Removes all elements from the stack and frees any allocated memory.
|
|
|
|
*/
|
|
|
|
void clear_and_shrink()
|
|
|
|
{
|
|
|
|
std::destroy_at(this);
|
|
|
|
new (this) Stack(NoExceptConstructor{});
|
|
|
|
}
|
|
|
|
|
2020-08-19 16:44:53 +02:00
|
|
|
/* This should only be called by unit tests. */
|
|
|
|
bool is_invariant_maintained() const
|
|
|
|
{
|
|
|
|
if (size_ == 0) {
|
|
|
|
return top_ == inline_chunk_.begin;
|
|
|
|
}
|
|
|
|
return top_ > top_chunk_->begin;
|
|
|
|
}
|
|
|
|
|
2020-06-09 10:10:56 +02:00
|
|
|
private:
|
2019-09-12 14:23:21 +02:00
|
|
|
/**
|
2020-07-03 14:15:05 +02:00
|
|
|
* Changes top_chunk_ to point to a new chunk that is above the current one. The new chunk might
|
2020-06-09 10:10:56 +02:00
|
|
|
* be smaller than the given size_hint. This happens when a chunk that has been allocated before
|
|
|
|
* is reused. The size of the new chunk will be at least one.
|
|
|
|
*
|
|
|
|
* This invokes undefined behavior when the currently active chunk is not full.
|
2019-09-12 14:23:21 +02:00
|
|
|
*/
|
2020-07-20 12:16:20 +02:00
|
|
|
void activate_next_chunk(const int64_t size_hint)
|
2020-06-09 10:10:56 +02:00
|
|
|
{
|
2020-07-03 14:15:05 +02:00
|
|
|
BLI_assert(top_ == top_chunk_->capacity_end);
|
|
|
|
if (top_chunk_->above == nullptr) {
|
2020-07-20 12:16:20 +02:00
|
|
|
const int64_t new_capacity = std::max(size_hint, top_chunk_->capacity() * 2 + 10);
|
2020-06-09 10:10:56 +02:00
|
|
|
|
|
|
|
/* Do a single memory allocation for the Chunk and the array it references. */
|
2020-07-03 14:15:05 +02:00
|
|
|
void *buffer = allocator_.allocate(
|
2020-06-09 10:10:56 +02:00
|
|
|
sizeof(Chunk) + sizeof(T) * new_capacity + alignof(T), alignof(Chunk), AT);
|
|
|
|
void *chunk_buffer = buffer;
|
2020-08-07 18:24:59 +02:00
|
|
|
void *data_buffer = reinterpret_cast<void *>(
|
|
|
|
(reinterpret_cast<uintptr_t>(buffer) + sizeof(Chunk) + alignof(T) - 1) &
|
|
|
|
~(alignof(T) - 1));
|
2020-06-09 10:10:56 +02:00
|
|
|
|
|
|
|
Chunk *new_chunk = new (chunk_buffer) Chunk();
|
2020-08-07 18:24:59 +02:00
|
|
|
new_chunk->begin = static_cast<T *>(data_buffer);
|
2020-06-09 10:10:56 +02:00
|
|
|
new_chunk->capacity_end = new_chunk->begin + new_capacity;
|
|
|
|
new_chunk->above = nullptr;
|
2020-07-03 14:15:05 +02:00
|
|
|
new_chunk->below = top_chunk_;
|
|
|
|
top_chunk_->above = new_chunk;
|
2020-06-09 10:10:56 +02:00
|
|
|
}
|
2020-07-03 14:15:05 +02:00
|
|
|
top_chunk_ = top_chunk_->above;
|
|
|
|
top_ = top_chunk_->begin;
|
2020-06-09 10:10:56 +02:00
|
|
|
}
|
|
|
|
|
2020-08-19 16:44:53 +02:00
|
|
|
void move_top_pointer_back_to_below_chunk()
|
|
|
|
{
|
|
|
|
/* This makes sure that the invariant stays intact after a failed push. */
|
|
|
|
if (size_ == 0) {
|
|
|
|
top_ = inline_chunk_.begin;
|
|
|
|
}
|
|
|
|
else if (top_ == top_chunk_->begin) {
|
|
|
|
top_chunk_ = top_chunk_->below;
|
|
|
|
top_ = top_chunk_->capacity_end;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-09 10:10:56 +02:00
|
|
|
void destruct_all_elements()
|
2019-09-12 14:23:21 +02:00
|
|
|
{
|
2020-07-03 14:15:05 +02:00
|
|
|
for (T *value = top_chunk_->begin; value != top_; value++) {
|
2020-06-09 10:10:56 +02:00
|
|
|
value->~T();
|
|
|
|
}
|
2020-07-03 14:15:05 +02:00
|
|
|
for (Chunk *chunk = top_chunk_->below; chunk; chunk = chunk->below) {
|
2020-06-09 10:10:56 +02:00
|
|
|
for (T *value = chunk->begin; value != chunk->capacity_end; value++) {
|
|
|
|
value->~T();
|
|
|
|
}
|
|
|
|
}
|
2019-09-12 14:23:21 +02:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2020-07-20 16:00:20 +02:00
|
|
|
/**
|
|
|
|
* Same as a normal Stack, 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 RawStack = Stack<T, InlineBufferCapacity, RawAllocator>;
|
|
|
|
|
2020-06-09 10:27:24 +02:00
|
|
|
} /* namespace blender */
|