Blender V4.5
BLI_memory_utils.hh
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#pragma once
6
10
11#include <algorithm>
12#include <memory>
13#include <type_traits>
14
15#include "BLI_utildefines.h"
16#include "MEM_guardedalloc.h"
17
18namespace blender {
19
24template<typename T> inline constexpr bool is_trivial_extended_v = std::is_trivial_v<T>;
25template<typename T>
27 std::is_trivially_destructible_v<T>;
28template<typename T>
30 is_trivial_extended_v<T> || std::is_trivially_copy_constructible_v<T>;
31template<typename T>
33 is_trivial_extended_v<T> || std::is_trivially_move_constructible_v<T>;
34
35template<typename T> void destruct_n(T *ptr, int64_t n)
36{
38 return;
39 }
40
41 std::destroy_n(ptr, n);
42}
43
44template<typename T> void default_construct_n(T *ptr, int64_t n)
45{
46 std::uninitialized_default_construct_n(ptr, n);
47}
48
49template<typename T> void initialized_copy_n(const T *src, int64_t n, T *dst)
50{
51 std::copy_n(src, n, dst);
52}
53
54template<typename T> void uninitialized_copy_n(const T *src, int64_t n, T *dst)
55{
56 std::uninitialized_copy_n(src, n, dst);
57}
58
59template<typename From, typename To>
60void uninitialized_convert_n(const From *src, int64_t n, To *dst)
61{
62 std::uninitialized_copy_n(src, n, dst);
63}
64
65template<typename T> void initialized_move_n(T *src, int64_t n, T *dst)
66{
67 std::copy_n(std::make_move_iterator(src), n, dst);
68}
69
70template<typename T> void uninitialized_move_n(T *src, int64_t n, T *dst)
71{
72 std::uninitialized_copy_n(std::make_move_iterator(src), n, dst);
73}
74
75template<typename T> void initialized_relocate_n(T *src, int64_t n, T *dst)
76{
77 initialized_move_n(src, n, dst);
78 destruct_n(src, n);
79}
80
81template<typename T> void uninitialized_relocate_n(T *src, int64_t n, T *dst)
82{
83 uninitialized_move_n(src, n, dst);
84 destruct_n(src, n);
85}
86
87template<typename T> void initialized_fill_n(T *dst, int64_t n, const T &value)
88{
89 std::fill_n(dst, n, value);
90}
91
92template<typename T> void uninitialized_fill_n(T *dst, int64_t n, const T &value)
93{
94 std::uninitialized_fill_n(dst, n, value);
95}
96
97template<typename T> struct DestructValueAtAddress {
99
100 template<typename U> DestructValueAtAddress(const U &) {}
101
103 {
104 ptr->~T();
105 }
106};
107
112template<typename T> using destruct_ptr = std::unique_ptr<T, DestructValueAtAddress<T>>;
113
118template<size_t Size, size_t Alignment> class AlignedBuffer {
119 struct Empty {};
120 struct alignas(Alignment) Sized {
121 /* Don't create an empty array. This causes problems with some compilers. */
122 std::byte buffer_[Size > 0 ? Size : 1];
123 };
124
125 using BufferType = std::conditional_t<Size == 0, Empty, Sized>;
126 BLI_NO_UNIQUE_ADDRESS BufferType buffer_;
127
128 public:
129 operator void *()
130 {
131 return this;
132 }
133
134 operator const void *() const
135 {
136 return this;
137 }
138
139 void *ptr()
140 {
141 return this;
142 }
143
144 const void *ptr() const
145 {
146 return this;
147 }
148};
149
155template<typename T, int64_t Size = 1> class TypedBuffer {
156 private:
158 static constexpr size_t get_size()
159 {
160 if constexpr (Size == 0) {
161 return 0;
162 }
163 else {
164 return sizeof(T) * size_t(Size);
165 }
166 }
167
169 static constexpr size_t get_alignment()
170 {
171 if constexpr (Size == 0) {
172 return 1;
173 }
174 else {
175 return alignof(T);
176 }
177 }
178
179 BLI_NO_UNIQUE_ADDRESS AlignedBuffer<get_size(), get_alignment()> buffer_;
180
181 public:
182 operator T *()
183 {
184 return static_cast<T *>(buffer_.ptr());
185 }
186
187 operator const T *() const
188 {
189 return static_cast<const T *>(buffer_.ptr());
190 }
191
193 {
194 return *static_cast<T *>(buffer_.ptr());
195 }
196
197 const T &operator*() const
198 {
199 return *static_cast<const T *>(buffer_.ptr());
200 }
201
203 {
204 return static_cast<T *>(buffer_.ptr());
205 }
206
207 const T *ptr() const
208 {
209 return static_cast<const T *>(buffer_.ptr());
210 }
211
213 {
214 return *static_cast<T *>(buffer_.ptr());
215 }
216
217 const T &ref() const
218 {
219 return *static_cast<const T *>(buffer_.ptr());
220 }
221};
222
223/* A dynamic stack buffer can be used instead of #alloca when wants to allocate a dynamic amount of
224 * memory on the stack. Using this class has some advantages:
225 * - It falls back to heap allocation, when the size is too large.
226 * - It can be used in loops safely.
227 * - If the buffer is heap allocated, it is free automatically in the destructor.
228 */
229template<size_t ReservedSize = 64, size_t ReservedAlignment = 64>
230class alignas(ReservedAlignment) DynamicStackBuffer {
231 private:
232 /* Don't create an empty array. This causes problems with some compilers. */
233 char reserved_buffer_[(ReservedSize > 0) ? ReservedSize : 1];
234 void *buffer_;
235
236 public:
237 DynamicStackBuffer(const int64_t size, const int64_t alignment)
238 {
239 BLI_assert(size >= 0);
240 BLI_assert(alignment >= 0);
241 if (size <= ReservedSize && alignment <= ReservedAlignment) {
242 buffer_ = reserved_buffer_;
243 }
244 else {
245 buffer_ = MEM_mallocN_aligned(size, alignment, __func__);
246 }
247 }
249 {
250 if (buffer_ != reserved_buffer_) {
251 MEM_freeN(buffer_);
252 }
253 }
254
255 /* Don't allow any copying or moving of this type. */
260
261 void *buffer() const
262 {
263 return buffer_;
264 }
265};
266
272
280
286template<typename From, typename To>
287inline constexpr bool is_convertible_pointer_v = std::is_convertible_v<From, To> &&
288 std::is_pointer_v<From> && std::is_pointer_v<To>;
289
296template<typename From, typename To>
297inline constexpr bool is_span_convertible_pointer_v =
298 /* Make sure we are working with pointers. */
299 std::is_pointer_v<From> && std::is_pointer_v<To> &&
300 (/* No casting is necessary when both types are the same. */
301 std::is_same_v<From, To> ||
302 /* Allow adding const to the underlying type. */
303 std::is_same_v<std::remove_pointer_t<From>, std::remove_const_t<std::remove_pointer_t<To>>> ||
304 /* Allow casting non-const pointers to void pointers. */
305 (!std::is_const_v<std::remove_pointer_t<From>> && std::is_same_v<To, void *>) ||
306 /* Allow casting any pointer to const void pointers. */
307 std::is_same_v<To, const void *>);
308
312template<typename T, typename... Args>
313inline constexpr bool is_same_any_v = (std::is_same_v<T, Args> || ...);
314
319constexpr int64_t default_inline_buffer_capacity(size_t element_size)
320{
321 return (int64_t(element_size) < 100) ? 4 : 0;
322}
323
329template<typename Container> Container &copy_assign_container(Container &dst, const Container &src)
330{
331 if (&src == &dst) {
332 return dst;
333 }
334
335 Container container_copy{src};
336 dst = std::move(container_copy);
337 return dst;
338}
339
345template<typename Container>
346Container &move_assign_container(Container &dst, Container &&src) noexcept(
347 std::is_nothrow_move_constructible_v<Container>)
348{
349 if (&dst == &src) {
350 return dst;
351 }
352
353 dst.~Container();
354 if constexpr (std::is_nothrow_move_constructible_v<Container>) {
355 new (&dst) Container(std::move(src));
356 }
357 else {
358 try {
359 new (&dst) Container(std::move(src));
360 }
361 catch (...) {
362 new (&dst) Container(NoExceptConstructor());
363 throw;
364 }
365 }
366 return dst;
367}
368
372template<typename T> inline bool assign_if_different(T &old_value, T new_value)
373{
374 if (old_value != new_value) {
375 old_value = std::move(new_value);
376 return true;
377 }
378 return false;
379}
380
381} // namespace blender
382
383namespace blender::detail {
384
385template<typename Func> struct ScopedDeferHelper {
386 Func func;
387
389 {
390 func();
391 }
392};
393
394} // namespace blender::detail
395
396#define BLI_SCOPED_DEFER_NAME1(a, b) a##b
397#define BLI_SCOPED_DEFER_NAME2(a, b) BLI_SCOPED_DEFER_NAME1(a, b)
398#define BLI_SCOPED_DEFER_NAME(a) BLI_SCOPED_DEFER_NAME2(_scoped_defer_##a##_, __LINE__)
399
406#define BLI_SCOPED_DEFER(function_to_defer) \
407 auto BLI_SCOPED_DEFER_NAME(func) = (function_to_defer); \
408 blender::detail::ScopedDeferHelper<decltype(BLI_SCOPED_DEFER_NAME(func))> \
409 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
Read Guarded memory(de)allocation.
#define U
long long int int64_t
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
const void * ptr() const
DynamicStackBuffer & operator=(const DynamicStackBuffer &other)=delete
DynamicStackBuffer & operator=(DynamicStackBuffer &&other)=delete
DynamicStackBuffer(const DynamicStackBuffer &other)=delete
DynamicStackBuffer(const int64_t size, const int64_t alignment)
DynamicStackBuffer(DynamicStackBuffer &&other)=delete
const T & ref() const
const T & operator*() const
const T * ptr() const
void * MEM_mallocN_aligned(size_t len, size_t alignment, const char *str)
Definition mallocn.cc:138
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
#define T
constexpr bool is_same_any_v
void default_construct_n(T *ptr, int64_t n)
void initialized_relocate_n(T *src, int64_t n, T *dst)
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)
Container & copy_assign_container(Container &dst, const Container &src)
constexpr bool is_convertible_pointer_v
constexpr bool is_trivially_move_constructible_extended_v
Container & move_assign_container(Container &dst, Container &&src) noexcept(std::is_nothrow_move_constructible_v< Container >)
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)
PointerRNA * ptr
Definition wm_files.cc:4226