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