Blender  V2.93
BLI_stack.hh
Go to the documentation of this file.
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  */
16 
17 #pragma once
18 
42 #include "BLI_allocator.hh"
43 #include "BLI_memory_utils.hh"
44 #include "BLI_span.hh"
45 
46 namespace blender {
47 
52 template<typename T> struct StackChunk {
58  T *begin;
61 
62  int64_t capacity() const
63  {
64  return capacity_end - begin;
65  }
66 };
67 
68 template<
70  typename T,
76  int64_t InlineBufferCapacity = default_inline_buffer_capacity(sizeof(T)),
81  typename Allocator = GuardedAllocator>
82 class Stack {
83  public:
84  using value_type = T;
85  using pointer = T *;
86  using const_pointer = const T *;
87  using reference = T &;
88  using const_reference = const T &;
89  using size_type = int64_t;
90 
91  private:
92  using Chunk = StackChunk<T>;
93 
102  T *top_;
103 
105  Chunk *top_chunk_;
106 
110  int64_t size_;
111 
114 
119  Chunk inline_chunk_;
120 
122  Allocator allocator_;
123 
124  public:
128  Stack(Allocator allocator = {}) noexcept : allocator_(allocator)
129  {
130  inline_chunk_.below = nullptr;
131  inline_chunk_.above = nullptr;
132  inline_chunk_.begin = inline_buffer_;
133  inline_chunk_.capacity_end = inline_buffer_ + InlineBufferCapacity;
134 
135  top_ = inline_buffer_;
136  top_chunk_ = &inline_chunk_;
137  size_ = 0;
138  }
139 
140  Stack(NoExceptConstructor, Allocator allocator = {}) noexcept : Stack(allocator)
141  {
142  }
143 
148  Stack(Span<T> values, Allocator allocator = {}) : Stack(NoExceptConstructor(), allocator)
149  {
150  this->push_multiple(values);
151  }
152 
162  Stack(const std::initializer_list<T> &values, Allocator allocator = {})
163  : Stack(Span<T>(values), allocator)
164  {
165  }
166 
167  Stack(const Stack &other) : Stack(NoExceptConstructor(), other.allocator_)
168  {
169  for (const Chunk *chunk = &other.inline_chunk_; chunk; chunk = chunk->above) {
170  const T *begin = chunk->begin;
171  const T *end = (chunk == other.top_chunk_) ? other.top_ : chunk->capacity_end;
172  this->push_multiple(Span<T>(begin, end - begin));
173  }
174  }
175 
176  Stack(Stack &&other) noexcept(std::is_nothrow_move_constructible_v<T>)
177  : Stack(NoExceptConstructor(), other.allocator_)
178  {
179  uninitialized_relocate_n<T>(
180  other.inline_buffer_, std::min(other.size_, InlineBufferCapacity), inline_buffer_);
181 
182  inline_chunk_.above = other.inline_chunk_.above;
183  size_ = other.size_;
184 
185  if (inline_chunk_.above != nullptr) {
186  inline_chunk_.above->below = &inline_chunk_;
187  }
188 
189  if (size_ <= InlineBufferCapacity) {
190  top_chunk_ = &inline_chunk_;
191  top_ = inline_buffer_ + size_;
192  }
193  else {
194  top_chunk_ = other.top_chunk_;
195  top_ = other.top_;
196  }
197 
198  other.size_ = 0;
199  other.inline_chunk_.above = nullptr;
200  other.top_chunk_ = &other.inline_chunk_;
201  other.top_ = other.top_chunk_->begin;
202  }
203 
205  {
206  this->destruct_all_elements();
207  Chunk *above_chunk;
208  for (Chunk *chunk = inline_chunk_.above; chunk; chunk = above_chunk) {
209  above_chunk = chunk->above;
210  allocator_.deallocate(chunk);
211  }
212  }
213 
214  Stack &operator=(const Stack &other)
215  {
216  return copy_assign_container(*this, other);
217  }
218 
220  {
221  return move_assign_container(*this, std::move(other));
222  }
223 
227  void push(const T &value)
228  {
229  this->push_as(value);
230  }
231  void push(T &&value)
232  {
233  this->push_as(std::move(value));
234  }
235  template<typename ForwardT> void push_as(ForwardT &&value)
236  {
237  if (top_ == top_chunk_->capacity_end) {
238  this->activate_next_chunk(1);
239  }
240  try {
241  new (top_) T(std::forward<ForwardT>(value));
242  top_++;
243  size_++;
244  }
245  catch (...) {
246  this->move_top_pointer_back_to_below_chunk();
247  throw;
248  }
249  }
250 
255  T pop()
256  {
257  BLI_assert(size_ > 0);
258  T value = std::move(*(top_ - 1));
259  top_--;
260  top_->~T();
261  size_--;
262 
263  if (top_ == top_chunk_->begin) {
264  if (top_chunk_->below != nullptr) {
265  top_chunk_ = top_chunk_->below;
266  top_ = top_chunk_->capacity_end;
267  }
268  }
269  return value;
270  }
271 
276  T &peek()
277  {
278  BLI_assert(size_ > 0);
279  BLI_assert(top_ > top_chunk_->begin);
280  return *(top_ - 1);
281  }
282  const T &peek() const
283  {
284  BLI_assert(size_ > 0);
285  BLI_assert(top_ > top_chunk_->begin);
286  return *(top_ - 1);
287  }
288 
294  void push_multiple(Span<T> values)
295  {
296  Span<T> remaining_values = values;
297  while (!remaining_values.is_empty()) {
298  if (top_ == top_chunk_->capacity_end) {
299  this->activate_next_chunk(remaining_values.size());
300  }
301 
302  const int64_t remaining_capacity = top_chunk_->capacity_end - top_;
303  const int64_t amount = std::min(remaining_values.size(), remaining_capacity);
304  try {
305  uninitialized_copy_n(remaining_values.data(), amount, top_);
306  }
307  catch (...) {
308  this->move_top_pointer_back_to_below_chunk();
309  throw;
310  }
311  top_ += amount;
312  size_ += amount;
313 
314  remaining_values = remaining_values.drop_front(amount);
315  }
316  }
317 
321  bool is_empty() const
322  {
323  return size_ == 0;
324  }
325 
329  int64_t size() const
330  {
331  return size_;
332  }
333 
338  void clear()
339  {
340  this->destruct_all_elements();
341  top_chunk_ = &inline_chunk_;
342  top_ = top_chunk_->begin;
343  }
344 
345  /* This should only be called by unit tests. */
347  {
348  if (size_ == 0) {
349  return top_ == inline_chunk_.begin;
350  }
351  return top_ > top_chunk_->begin;
352  }
353 
354  private:
362  void activate_next_chunk(const int64_t size_hint)
363  {
364  BLI_assert(top_ == top_chunk_->capacity_end);
365  if (top_chunk_->above == nullptr) {
366  const int64_t new_capacity = std::max(size_hint, top_chunk_->capacity() * 2 + 10);
367 
368  /* Do a single memory allocation for the Chunk and the array it references. */
369  void *buffer = allocator_.allocate(
370  sizeof(Chunk) + sizeof(T) * new_capacity + alignof(T), alignof(Chunk), AT);
371  void *chunk_buffer = buffer;
372  void *data_buffer = reinterpret_cast<void *>(
373  (reinterpret_cast<uintptr_t>(buffer) + sizeof(Chunk) + alignof(T) - 1) &
374  ~(alignof(T) - 1));
375 
376  Chunk *new_chunk = new (chunk_buffer) Chunk();
377  new_chunk->begin = static_cast<T *>(data_buffer);
378  new_chunk->capacity_end = new_chunk->begin + new_capacity;
379  new_chunk->above = nullptr;
380  new_chunk->below = top_chunk_;
381  top_chunk_->above = new_chunk;
382  }
383  top_chunk_ = top_chunk_->above;
384  top_ = top_chunk_->begin;
385  }
386 
387  void move_top_pointer_back_to_below_chunk()
388  {
389  /* This makes sure that the invariant stays intact after a failed push. */
390  if (size_ == 0) {
391  top_ = inline_chunk_.begin;
392  }
393  else if (top_ == top_chunk_->begin) {
394  top_chunk_ = top_chunk_->below;
395  top_ = top_chunk_->capacity_end;
396  }
397  }
398 
399  void destruct_all_elements()
400  {
401  for (T *value = top_chunk_->begin; value != top_; value++) {
402  value->~T();
403  }
404  for (Chunk *chunk = top_chunk_->below; chunk; chunk = chunk->below) {
405  for (T *value = chunk->begin; value != chunk->capacity_end; value++) {
406  value->~T();
407  }
408  }
409  }
410 };
411 
416 template<typename T, int64_t InlineBufferCapacity = default_inline_buffer_capacity(sizeof(T))>
418 
419 } /* namespace blender */
#define BLI_assert(a)
Definition: BLI_assert.h:58
#define AT
constexpr Span drop_front(int64_t n) const
Definition: BLI_span.hh:173
constexpr const T * data() const
Definition: BLI_span.hh:217
constexpr int64_t size() const
Definition: BLI_span.hh:254
constexpr bool is_empty() const
Definition: BLI_span.hh:262
void push_as(ForwardT &&value)
Definition: BLI_stack.hh:235
bool is_invariant_maintained() const
Definition: BLI_stack.hh:346
Stack(Allocator allocator={}) noexcept
Definition: BLI_stack.hh:128
Stack(NoExceptConstructor, Allocator allocator={}) noexcept
Definition: BLI_stack.hh:140
Stack(Span< T > values, Allocator allocator={})
Definition: BLI_stack.hh:148
Stack & operator=(const Stack &other)
Definition: BLI_stack.hh:214
Stack & operator=(Stack &&other)
Definition: BLI_stack.hh:219
bool is_empty() const
Definition: BLI_stack.hh:321
const T & const_reference
Definition: BLI_stack.hh:88
Stack(Stack &&other) noexcept(std::is_nothrow_move_constructible_v< T >)
Definition: BLI_stack.hh:176
Stack(const std::initializer_list< T > &values, Allocator allocator={})
Definition: BLI_stack.hh:162
void push(T &&value)
Definition: BLI_stack.hh:231
void push(const T &value)
Definition: BLI_stack.hh:227
const T * const_pointer
Definition: BLI_stack.hh:86
void push_multiple(Span< T > values)
Definition: BLI_stack.hh:294
const T & peek() const
Definition: BLI_stack.hh:282
int64_t size_type
Definition: BLI_stack.hh:89
int64_t size() const
Definition: BLI_stack.hh:329
Stack(const Stack &other)
Definition: BLI_stack.hh:167
__kernel void ccl_constant KernelData ccl_global void ccl_global char ccl_global int ccl_global char ccl_global unsigned int ccl_global float * buffer
#define T
Container & move_assign_container(Container &dst, Container &&src) noexcept(std::is_nothrow_move_constructible_v< Container >)
Container & copy_assign_container(Container &dst, const Container &src)
constexpr int64_t default_inline_buffer_capacity(size_t element_size)
void uninitialized_copy_n(const T *src, int64_t n, T *dst)
#define min(a, b)
Definition: sort.c:51
_W64 unsigned int uintptr_t
Definition: stdint.h:122
__int64 int64_t
Definition: stdint.h:92
StackChunk * above
Definition: BLI_stack.hh:56
StackChunk * below
Definition: BLI_stack.hh:54
int64_t capacity() const
Definition: BLI_stack.hh:62
float max