Blender  V2.93
BLI_memory_utils.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 
26 #include <memory>
27 #include <new>
28 #include <type_traits>
29 
30 #include "BLI_utildefines.h"
31 #include "MEM_guardedalloc.h"
32 
33 namespace blender {
34 
46 template<typename T> void destruct_n(T *ptr, int64_t n)
47 {
48  BLI_assert(n >= 0);
49 
50  static_assert(std::is_nothrow_destructible_v<T>,
51  "This should be true for all types. Destructors are noexcept by default.");
52 
53  /* This is not strictly necessary, because the loop below will be optimized away anyway. It is
54  * nice to make behavior this explicitly, though. */
55  if (std::is_trivially_destructible_v<T>) {
56  return;
57  }
58 
59  for (int64_t i = 0; i < n; i++) {
60  ptr[i].~T();
61  }
62 }
63 
75 template<typename T> void default_construct_n(T *ptr, int64_t n)
76 {
77  BLI_assert(n >= 0);
78 
79  /* This is not strictly necessary, because the loop below will be optimized away anyway. It is
80  * nice to make behavior this explicitly, though. */
81  if (std::is_trivially_constructible_v<T>) {
82  return;
83  }
84 
85  int64_t current = 0;
86  try {
87  for (; current < n; current++) {
88  new (static_cast<void *>(ptr + current)) T;
89  }
90  }
91  catch (...) {
92  destruct_n(ptr, current);
93  throw;
94  }
95 }
96 
109 template<typename T> void initialized_copy_n(const T *src, int64_t n, T *dst)
110 {
111  BLI_assert(n >= 0);
112 
113  for (int64_t i = 0; i < n; i++) {
114  dst[i] = src[i];
115  }
116 }
117 
130 template<typename T> void uninitialized_copy_n(const T *src, int64_t n, T *dst)
131 {
132  BLI_assert(n >= 0);
133 
134  int64_t current = 0;
135  try {
136  for (; current < n; current++) {
137  new (static_cast<void *>(dst + current)) T(src[current]);
138  }
139  }
140  catch (...) {
141  destruct_n(dst, current);
142  throw;
143  }
144 }
145 
158 template<typename From, typename To>
159 void uninitialized_convert_n(const From *src, int64_t n, To *dst)
160 {
161  BLI_assert(n >= 0);
162 
163  int64_t current = 0;
164  try {
165  for (; current < n; current++) {
166  new (static_cast<void *>(dst + current)) To(static_cast<To>(src[current]));
167  }
168  }
169  catch (...) {
170  destruct_n(dst, current);
171  throw;
172  }
173 }
174 
187 template<typename T> void initialized_move_n(T *src, int64_t n, T *dst)
188 {
189  BLI_assert(n >= 0);
190 
191  for (int64_t i = 0; i < n; i++) {
192  dst[i] = std::move(src[i]);
193  }
194 }
195 
208 template<typename T> void uninitialized_move_n(T *src, int64_t n, T *dst)
209 {
210  BLI_assert(n >= 0);
211 
212  int64_t current = 0;
213  try {
214  for (; current < n; current++) {
215  new (static_cast<void *>(dst + current)) T(std::move(src[current]));
216  }
217  }
218  catch (...) {
219  destruct_n(dst, current);
220  throw;
221  }
222 }
223 
237 template<typename T> void initialized_relocate_n(T *src, int64_t n, T *dst)
238 {
239  BLI_assert(n >= 0);
240 
241  initialized_move_n(src, n, dst);
242  destruct_n(src, n);
243 }
244 
258 template<typename T> void uninitialized_relocate_n(T *src, int64_t n, T *dst)
259 {
260  BLI_assert(n >= 0);
261 
262  uninitialized_move_n(src, n, dst);
263  destruct_n(src, n);
264 }
265 
276 template<typename T> void initialized_fill_n(T *dst, int64_t n, const T &value)
277 {
278  BLI_assert(n >= 0);
279 
280  for (int64_t i = 0; i < n; i++) {
281  dst[i] = value;
282  }
283 }
284 
295 template<typename T> void uninitialized_fill_n(T *dst, int64_t n, const T &value)
296 {
297  BLI_assert(n >= 0);
298 
299  int64_t current = 0;
300  try {
301  for (; current < n; current++) {
302  new (static_cast<void *>(dst + current)) T(value);
303  }
304  }
305  catch (...) {
306  destruct_n(dst, current);
307  throw;
308  }
309 }
310 
311 template<typename T> struct DestructValueAtAddress {
312  void operator()(T *ptr)
313  {
314  ptr->~T();
315  }
316 };
317 
322 template<typename T> using destruct_ptr = std::unique_ptr<T, DestructValueAtAddress<T>>;
323 
328 template<size_t Size, size_t Alignment> class alignas(Alignment) AlignedBuffer {
329  private:
330  /* Don't create an empty array. This causes problems with some compilers. */
331  char buffer_[(Size > 0) ? Size : 1];
332 
333  public:
334  operator void *()
335  {
336  return buffer_;
337  }
338 
339  operator const void *() const
340  {
341  return buffer_;
342  }
343 
344  void *ptr()
345  {
346  return buffer_;
347  }
348 
349  const void *ptr() const
350  {
351  return buffer_;
352  }
353 };
354 
360 template<typename T, int64_t Size = 1> class TypedBuffer {
361  private:
362  AlignedBuffer<sizeof(T) * (size_t)Size, alignof(T)> buffer_;
363 
364  public:
365  operator T *()
366  {
367  return static_cast<T *>(buffer_.ptr());
368  }
369 
370  operator const T *() const
371  {
372  return static_cast<const T *>(buffer_.ptr());
373  }
374 
376  {
377  return *static_cast<T *>(buffer_.ptr());
378  }
379 
380  const T &operator*() const
381  {
382  return *static_cast<const T *>(buffer_.ptr());
383  }
384 
385  T *ptr()
386  {
387  return static_cast<T *>(buffer_.ptr());
388  }
389 
390  const T *ptr() const
391  {
392  return static_cast<const T *>(buffer_.ptr());
393  }
394 
395  T &ref()
396  {
397  return *static_cast<T *>(buffer_.ptr());
398  }
399 
400  const T &ref() const
401  {
402  return *static_cast<const T *>(buffer_.ptr());
403  }
404 };
405 
406 /* A dynamic stack buffer can be used instead of #alloca when wants to allocate a dynamic amount of
407  * memory on the stack. Using this class has some advantages:
408  * - It falls back to heap allocation, when the size is too large.
409  * - It can be used in loops safely.
410  * - If the buffer is heap allocated, it is free automatically in the destructor.
411  */
412 template<size_t ReservedSize = 64, size_t ReservedAlignment = 64>
413 class alignas(ReservedAlignment) DynamicStackBuffer {
414  private:
415  /* Don't create an empty array. This causes problems with some compilers. */
416  char reserved_buffer_[(ReservedSize > 0) ? ReservedSize : 1];
417  void *buffer_;
418 
419  public:
420  DynamicStackBuffer(const int64_t size, const int64_t alignment)
421  {
422  BLI_assert(size >= 0);
423  BLI_assert(alignment >= 0);
424  if (size <= ReservedSize && alignment <= ReservedAlignment) {
425  buffer_ = reserved_buffer_;
426  }
427  else {
428  buffer_ = MEM_mallocN_aligned(size, alignment, __func__);
429  }
430  }
432  {
433  if (buffer_ != reserved_buffer_) {
434  MEM_freeN(buffer_);
435  }
436  }
437 
438  /* Don't allow any copying or moving of this type. */
439  DynamicStackBuffer(const DynamicStackBuffer &other) = delete;
443 
444  void *buffer() const
445  {
446  return buffer_;
447  }
448 };
449 
455 };
456 
464 };
465 
471 template<typename From, typename To>
472 inline constexpr bool is_convertible_pointer_v =
473  std::is_convertible_v<From, To> &&std::is_pointer_v<From> &&std::is_pointer_v<To>;
474 
481 template<typename From, typename To>
482 inline constexpr bool is_span_convertible_pointer_v =
483  /* Make sure we are working with pointers. */
484  std::is_pointer_v<From> &&std::is_pointer_v<To> &&
485  (/* No casting is necessary when both types are the same. */
486  std::is_same_v<From, To> ||
487  /* Allow adding const to the underlying type. */
488  std::is_same_v<const std::remove_pointer_t<From>, std::remove_pointer_t<To>> ||
489  /* Allow casting non-const pointers to void pointers. */
490  (!std::is_const_v<std::remove_pointer_t<From>> && std::is_same_v<To, void *>) ||
491  /* Allow casting any pointer to const void pointers. */
492  std::is_same_v<To, const void *>);
493 
498 inline constexpr int64_t default_inline_buffer_capacity(size_t element_size)
499 {
500  return (static_cast<int64_t>(element_size) < 100) ? 4 : 0;
501 }
502 
508 template<typename Container> Container &copy_assign_container(Container &dst, const Container &src)
509 {
510  if (&src == &dst) {
511  return dst;
512  }
513 
514  Container container_copy{src};
515  dst = std::move(container_copy);
516  return dst;
517 }
518 
524 template<typename Container>
525 Container &move_assign_container(Container &dst, Container &&src) noexcept(
526  std::is_nothrow_move_constructible_v<Container>)
527 {
528  if (&dst == &src) {
529  return dst;
530  }
531 
532  dst.~Container();
533  if constexpr (std::is_nothrow_move_constructible_v<Container>) {
534  new (&dst) Container(std::move(src));
535  }
536  else {
537  try {
538  new (&dst) Container(std::move(src));
539  }
540  catch (...) {
541  new (&dst) Container(NoExceptConstructor());
542  throw;
543  }
544  }
545  return dst;
546 }
547 
548 } // namespace blender
#define BLI_assert(a)
Definition: BLI_assert.h:58
Read Guarded memory(de)allocation.
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition: btDbvt.cpp:52
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
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:41
void *(* MEM_mallocN_aligned)(size_t len, size_t alignment, const char *str)
Definition: mallocn.c:49
#define T
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
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 int64_t default_inline_buffer_capacity(size_t element_size)
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)
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)
void uninitialized_move_n(T *src, int64_t n, T *dst)
__int64 int64_t
Definition: stdint.h:92
PointerRNA * ptr
Definition: wm_files.c:3157