Blender  V3.3
BLI_memory_utils.hh
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 
3 #pragma once
4 
12 #include <memory>
13 #include <new>
14 #include <type_traits>
15 
16 #include "BLI_utildefines.h"
17 #include "MEM_guardedalloc.h"
18 
19 namespace blender {
20 
25 template<typename T> inline constexpr bool is_trivial_extended_v = std::is_trivial_v<T>;
26 template<typename T>
27 inline constexpr bool is_trivially_destructible_extended_v = is_trivial_extended_v<T> ||
28  std::is_trivially_destructible_v<T>;
29 template<typename T>
31  is_trivial_extended_v<T> || std::is_trivially_copy_constructible_v<T>;
32 template<typename T>
34  is_trivial_extended_v<T> || std::is_trivially_move_constructible_v<T>;
35 
47 template<typename T> void destruct_n(T *ptr, int64_t n)
48 {
49  BLI_assert(n >= 0);
50 
51  static_assert(std::is_nothrow_destructible_v<T>,
52  "This should be true for all types. Destructors are noexcept by default.");
53 
54  /* This is not strictly necessary, because the loop below will be optimized away anyway. It is
55  * nice to make behavior this explicitly, though. */
56  if (is_trivially_destructible_extended_v<T>) {
57  return;
58  }
59 
60  for (int64_t i = 0; i < n; i++) {
61  ptr[i].~T();
62  }
63 }
64 
76 template<typename T> void default_construct_n(T *ptr, int64_t n)
77 {
78  BLI_assert(n >= 0);
79 
80  /* This is not strictly necessary, because the loop below will be optimized away anyway. It is
81  * nice to make behavior this explicitly, though. */
82  if (std::is_trivially_constructible_v<T>) {
83  return;
84  }
85 
86  int64_t current = 0;
87  try {
88  for (; current < n; current++) {
89  new (static_cast<void *>(ptr + current)) T;
90  }
91  }
92  catch (...) {
93  destruct_n(ptr, current);
94  throw;
95  }
96 }
97 
110 template<typename T> void initialized_copy_n(const T *src, int64_t n, T *dst)
111 {
112  BLI_assert(n >= 0);
113 
114  for (int64_t i = 0; i < n; i++) {
115  dst[i] = src[i];
116  }
117 }
118 
131 template<typename T> void uninitialized_copy_n(const T *src, int64_t n, T *dst)
132 {
133  BLI_assert(n >= 0);
134 
135  int64_t current = 0;
136  try {
137  for (; current < n; current++) {
138  new (static_cast<void *>(dst + current)) T(src[current]);
139  }
140  }
141  catch (...) {
142  destruct_n(dst, current);
143  throw;
144  }
145 }
146 
159 template<typename From, typename To>
160 void uninitialized_convert_n(const From *src, int64_t n, To *dst)
161 {
162  BLI_assert(n >= 0);
163 
164  int64_t current = 0;
165  try {
166  for (; current < n; current++) {
167  new (static_cast<void *>(dst + current)) To(static_cast<To>(src[current]));
168  }
169  }
170  catch (...) {
171  destruct_n(dst, current);
172  throw;
173  }
174 }
175 
188 template<typename T> void initialized_move_n(T *src, int64_t n, T *dst)
189 {
190  BLI_assert(n >= 0);
191 
192  for (int64_t i = 0; i < n; i++) {
193  dst[i] = std::move(src[i]);
194  }
195 }
196 
209 template<typename T> void uninitialized_move_n(T *src, int64_t n, T *dst)
210 {
211  BLI_assert(n >= 0);
212 
213  int64_t current = 0;
214  try {
215  for (; current < n; current++) {
216  new (static_cast<void *>(dst + current)) T(std::move(src[current]));
217  }
218  }
219  catch (...) {
220  destruct_n(dst, current);
221  throw;
222  }
223 }
224 
238 template<typename T> void initialized_relocate_n(T *src, int64_t n, T *dst)
239 {
240  BLI_assert(n >= 0);
241 
242  initialized_move_n(src, n, dst);
243  destruct_n(src, n);
244 }
245 
259 template<typename T> void uninitialized_relocate_n(T *src, int64_t n, T *dst)
260 {
261  BLI_assert(n >= 0);
262 
263  uninitialized_move_n(src, n, dst);
264  destruct_n(src, n);
265 }
266 
277 template<typename T> void initialized_fill_n(T *dst, int64_t n, const T &value)
278 {
279  BLI_assert(n >= 0);
280 
281  for (int64_t i = 0; i < n; i++) {
282  dst[i] = value;
283  }
284 }
285 
296 template<typename T> void uninitialized_fill_n(T *dst, int64_t n, const T &value)
297 {
298  BLI_assert(n >= 0);
299 
300  int64_t current = 0;
301  try {
302  for (; current < n; current++) {
303  new (static_cast<void *>(dst + current)) T(value);
304  }
305  }
306  catch (...) {
307  destruct_n(dst, current);
308  throw;
309  }
310 }
311 
312 template<typename T> struct DestructValueAtAddress {
314 
315  template<typename U> DestructValueAtAddress(const U &)
316  {
317  }
318 
319  void operator()(T *ptr)
320  {
321  ptr->~T();
322  }
323 };
324 
329 template<typename T> using destruct_ptr = std::unique_ptr<T, DestructValueAtAddress<T>>;
330 
335 template<size_t Size, size_t Alignment> class AlignedBuffer {
336  struct Empty {
337  };
338  struct alignas(Alignment) Sized {
339  /* Don't create an empty array. This causes problems with some compilers. */
340  std::byte buffer_[Size > 0 ? Size : 1];
341  };
342 
343  using BufferType = std::conditional_t<Size == 0, Empty, Sized>;
344  BLI_NO_UNIQUE_ADDRESS BufferType buffer_;
345 
346  public:
347  operator void *()
348  {
349  return this;
350  }
351 
352  operator const void *() const
353  {
354  return this;
355  }
356 
357  void *ptr()
358  {
359  return this;
360  }
361 
362  const void *ptr() const
363  {
364  return this;
365  }
366 };
367 
373 template<typename T, int64_t Size = 1> class TypedBuffer {
374  private:
375  BLI_NO_UNIQUE_ADDRESS AlignedBuffer<sizeof(T) * (size_t)Size, alignof(T)> buffer_;
376 
377  public:
378  operator T *()
379  {
380  return static_cast<T *>(buffer_.ptr());
381  }
382 
383  operator const T *() const
384  {
385  return static_cast<const T *>(buffer_.ptr());
386  }
387 
389  {
390  return *static_cast<T *>(buffer_.ptr());
391  }
392 
393  const T &operator*() const
394  {
395  return *static_cast<const T *>(buffer_.ptr());
396  }
397 
398  T *ptr()
399  {
400  return static_cast<T *>(buffer_.ptr());
401  }
402 
403  const T *ptr() const
404  {
405  return static_cast<const T *>(buffer_.ptr());
406  }
407 
408  T &ref()
409  {
410  return *static_cast<T *>(buffer_.ptr());
411  }
412 
413  const T &ref() const
414  {
415  return *static_cast<const T *>(buffer_.ptr());
416  }
417 };
418 
419 /* A dynamic stack buffer can be used instead of #alloca when wants to allocate a dynamic amount of
420  * memory on the stack. Using this class has some advantages:
421  * - It falls back to heap allocation, when the size is too large.
422  * - It can be used in loops safely.
423  * - If the buffer is heap allocated, it is free automatically in the destructor.
424  */
425 template<size_t ReservedSize = 64, size_t ReservedAlignment = 64>
426 class alignas(ReservedAlignment) DynamicStackBuffer {
427  private:
428  /* Don't create an empty array. This causes problems with some compilers. */
429  char reserved_buffer_[(ReservedSize > 0) ? ReservedSize : 1];
430  void *buffer_;
431 
432  public:
433  DynamicStackBuffer(const int64_t size, const int64_t alignment)
434  {
435  BLI_assert(size >= 0);
436  BLI_assert(alignment >= 0);
437  if (size <= ReservedSize && alignment <= ReservedAlignment) {
438  buffer_ = reserved_buffer_;
439  }
440  else {
441  buffer_ = MEM_mallocN_aligned(size, alignment, __func__);
442  }
443  }
445  {
446  if (buffer_ != reserved_buffer_) {
447  MEM_freeN(buffer_);
448  }
449  }
450 
451  /* Don't allow any copying or moving of this type. */
452  DynamicStackBuffer(const DynamicStackBuffer &other) = delete;
456 
457  void *buffer() const
458  {
459  return buffer_;
460  }
461 };
462 
468 };
469 
477 };
478 
484 template<typename From, typename To>
485 inline constexpr bool is_convertible_pointer_v =
486  std::is_convertible_v<From, To> &&std::is_pointer_v<From> &&std::is_pointer_v<To>;
487 
494 template<typename From, typename To>
495 inline constexpr bool is_span_convertible_pointer_v =
496  /* Make sure we are working with pointers. */
497  std::is_pointer_v<From> &&std::is_pointer_v<To> &&
498  (/* No casting is necessary when both types are the same. */
499  std::is_same_v<From, To> ||
500  /* Allow adding const to the underlying type. */
501  std::is_same_v<const std::remove_pointer_t<From>, std::remove_pointer_t<To>> ||
502  /* Allow casting non-const pointers to void pointers. */
503  (!std::is_const_v<std::remove_pointer_t<From>> && std::is_same_v<To, void *>) ||
504  /* Allow casting any pointer to const void pointers. */
505  std::is_same_v<To, const void *>);
506 
510 template<typename T, typename... Args>
511 inline constexpr bool is_same_any_v = (std::is_same_v<T, Args> || ...);
512 
517 inline constexpr int64_t default_inline_buffer_capacity(size_t element_size)
518 {
519  return (static_cast<int64_t>(element_size) < 100) ? 4 : 0;
520 }
521 
527 template<typename Container> Container &copy_assign_container(Container &dst, const Container &src)
528 {
529  if (&src == &dst) {
530  return dst;
531  }
532 
533  Container container_copy{src};
534  dst = std::move(container_copy);
535  return dst;
536 }
537 
543 template<typename Container>
544 Container &move_assign_container(Container &dst, Container &&src) noexcept(
545  std::is_nothrow_move_constructible_v<Container>)
546 {
547  if (&dst == &src) {
548  return dst;
549  }
550 
551  dst.~Container();
552  if constexpr (std::is_nothrow_move_constructible_v<Container>) {
553  new (&dst) Container(std::move(src));
554  }
555  else {
556  try {
557  new (&dst) Container(std::move(src));
558  }
559  catch (...) {
560  new (&dst) Container(NoExceptConstructor());
561  throw;
562  }
563  }
564  return dst;
565 }
566 
570 template<typename T> inline bool assign_if_different(T &old_value, T new_value)
571 {
572  if (old_value != new_value) {
573  old_value = std::move(new_value);
574  return true;
575  }
576  return false;
577 }
578 
579 } // namespace blender
580 
581 namespace blender::detail {
582 
583 template<typename Func> struct ScopedDeferHelper {
584  Func func;
585 
587  {
588  func();
589  }
590 };
591 
592 } // namespace blender::detail
593 
594 #define BLI_SCOPED_DEFER_NAME1(a, b) a##b
595 #define BLI_SCOPED_DEFER_NAME2(a, b) BLI_SCOPED_DEFER_NAME1(a, b)
596 #define BLI_SCOPED_DEFER_NAME(a) BLI_SCOPED_DEFER_NAME2(_scoped_defer_##a##_, __LINE__)
597 
604 #define BLI_SCOPED_DEFER(function_to_defer) \
605  auto BLI_SCOPED_DEFER_NAME(func) = (function_to_defer); \
606  blender::detail::ScopedDeferHelper<decltype(BLI_SCOPED_DEFER_NAME(func))> \
607  BLI_SCOPED_DEFER_NAME(helper){std::move(BLI_SCOPED_DEFER_NAME(func))};
#define BLI_assert(a)
Definition: BLI_assert.h:46
#define BLI_NO_UNIQUE_ADDRESS
unsigned char byte
Read Guarded memory(de)allocation.
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition: btDbvt.cpp:52
unsigned int U
Definition: btGjkEpa3.h:78
const void * ptr() const
DynamicStackBuffer & operator=(const DynamicStackBuffer &other)=delete
DynamicStackBuffer(const DynamicStackBuffer &other)=delete
DynamicStackBuffer(const int64_t size, const int64_t alignment)
DynamicStackBuffer(DynamicStackBuffer &&other)=delete
DynamicStackBuffer & operator=(DynamicStackBuffer &&other)=delete
const T & operator*() const
const T & ref() const
const T * ptr() const
SyclQueue void void * src
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:27
void *(* MEM_mallocN_aligned)(size_t len, size_t alignment, const char *str)
Definition: mallocn.c:35
#define T
constexpr bool is_same_any_v
Container & move_assign_container(Container &dst, Container &&src) noexcept(std::is_nothrow_move_constructible_v< Container >)
void default_construct_n(T *ptr, int64_t n)
void initialized_relocate_n(T *src, int64_t n, T *dst)
Container & copy_assign_container(Container &dst, const Container &src)
std::unique_ptr< T, DestructValueAtAddress< T > > destruct_ptr
bool assign_if_different(T &old_value, T new_value)
void initialized_fill_n(T *dst, int64_t n, const T &value)
void initialized_move_n(T *src, int64_t n, T *dst)
constexpr bool is_convertible_pointer_v
constexpr bool is_trivially_move_constructible_extended_v
constexpr int64_t default_inline_buffer_capacity(size_t element_size)
constexpr bool is_trivially_destructible_extended_v
void uninitialized_fill_n(T *dst, int64_t n, const T &value)
void uninitialized_relocate_n(T *src, int64_t n, T *dst)
void initialized_copy_n(const T *src, int64_t n, T *dst)
void uninitialized_convert_n(const From *src, int64_t n, To *dst)
constexpr bool is_trivially_copy_constructible_extended_v
void destruct_n(T *ptr, int64_t n)
constexpr bool is_span_convertible_pointer_v
void uninitialized_copy_n(const T *src, int64_t n, T *dst)
constexpr bool is_trivial_extended_v
void uninitialized_move_n(T *src, int64_t n, T *dst)
__int64 int64_t
Definition: stdint.h:89
PointerRNA * ptr
Definition: wm_files.c:3480