Blender V5.0
BLI_memiter.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
27
28#include <cstdlib>
29#include <cstring>
30
31#include "BLI_asan.h"
32#include "BLI_utildefines.h"
33
34#include "BLI_memiter.h" /* own include */
35
36#include "MEM_guardedalloc.h"
37
38#include "BLI_strict_flags.h" /* IWYU pragma: keep. Keep last. */
39
40/* TODO: Valgrind. */
41
42using data_t = uintptr_t;
43using offset_t = intptr_t;
44
45/* Write the chunk terminator on adding each element.
46 * typically we rely on the 'count' to avoid iterating past the end. */
47// #define USE_TERMINATE_PARANOID
48
49/* Currently totalloc isn't used. */
50// #define USE_TOTALLOC
51
52/* pad must be power of two */
53#define PADUP(num, pad) (((num) + ((pad) - 1)) & ~((pad) - 1))
54
59
70
72 /* A pointer to 'head' is needed so we can iterate in the order allocated. */
76 /* Used unless a large element is requested.
77 * (which should be very rare!). */
80#ifdef USE_TOTALLOC
81 uint totalloc;
82#endif
83};
84
86{
87 return PADUP(size, uint(sizeof(data_t))) / uint(sizeof(data_t));
88}
89
91{
93
95
96 elem->size = (offset_t)(((data_t *)mi->tail) - mi->data_curr);
97 BLI_assert(elem->size < 0);
98}
99
100static void memiter_init(BLI_memiter *mi)
101{
102 mi->head = nullptr;
103 mi->tail = nullptr;
104 mi->data_curr = nullptr;
105 mi->data_last = nullptr;
106 mi->count = 0;
107#ifdef USE_TOTALLOC
108 mi->totalloc = 0;
109#endif
110}
111
112/* -------------------------------------------------------------------- */
115
117{
118 BLI_memiter *mi = MEM_callocN<BLI_memiter>("BLI_memiter");
119 memiter_init(mi);
120
121 /* Small values are used for tests to check for correctness,
122 * but otherwise not that useful. */
123 const uint slop_space = (sizeof(BLI_memiter_chunk) + MEM_SIZE_OVERHEAD);
124 if (chunk_size_min >= 1024) {
125 /* As long as the input is a power of 2, this will give efficient sizes. */
126 chunk_size_min -= slop_space;
127 }
128
129 mi->chunk_size_in_bytes_min = chunk_size_min;
130 return mi;
131}
132
133void *BLI_memiter_alloc(BLI_memiter *mi, uint elem_size)
134{
135 const uint data_offset = data_offset_from_size(elem_size);
136 data_t *data_curr_next = LIKELY(mi->data_curr) ? mi->data_curr + (1 + data_offset) : nullptr;
137
138 if (UNLIKELY(mi->data_curr == nullptr) || (data_curr_next > mi->data_last)) {
139
140#ifndef USE_TERMINATE_PARANOID
141 if (mi->data_curr != nullptr) {
143 }
144#endif
145
146 uint chunk_size_in_bytes = mi->chunk_size_in_bytes_min;
147 if (UNLIKELY(chunk_size_in_bytes < elem_size + uint(sizeof(data_t[2])))) {
148 chunk_size_in_bytes = elem_size + uint(sizeof(data_t[2]));
149 }
150 uint chunk_size = data_offset_from_size(chunk_size_in_bytes);
151 BLI_memiter_chunk *chunk = static_cast<BLI_memiter_chunk *>(MEM_mallocN(
152 sizeof(BLI_memiter_chunk) + (chunk_size * sizeof(data_t)), "BLI_memiter_chunk"));
153
154 if (mi->head == nullptr) {
155 BLI_assert(mi->tail == nullptr);
156 mi->head = chunk;
157 }
158 else {
159 mi->tail->next = chunk;
160 }
161 mi->tail = chunk;
162 chunk->next = nullptr;
163
164 mi->data_curr = chunk->data;
165 mi->data_last = chunk->data + (chunk_size - 1);
166 data_curr_next = mi->data_curr + (1 + data_offset);
167
168 BLI_asan_poison(chunk->data, chunk_size * sizeof(data_t));
169 }
170
171 BLI_assert(data_curr_next <= mi->data_last);
172
174
175 BLI_asan_unpoison(elem, sizeof(BLI_memiter_elem) + elem_size);
176
177 elem->size = (offset_t)elem_size;
178 mi->data_curr = data_curr_next;
179
180#ifdef USE_TERMINATE_PARANOID
182#endif
183
184 mi->count += 1;
185
186#ifdef USE_TOTALLOC
187 mi->totalloc += elem_size;
188#endif
189
190 return elem->data;
191}
192
194{
195 void *data = BLI_memiter_alloc(mi, elem_size);
196 memset(data, 0, elem_size);
197 return data;
198}
199
200void BLI_memiter_alloc_from(BLI_memiter *mi, uint elem_size, const void *data_from)
201{
202 void *data = BLI_memiter_alloc(mi, elem_size);
203 memcpy(data, data_from, elem_size);
204}
205
207{
208 BLI_memiter_chunk *chunk = mi->head;
209 while (chunk) {
210 BLI_memiter_chunk *chunk_next = chunk->next;
211
212 /* Unpoison memory because MEM_freeN might overwrite it. */
213 BLI_asan_unpoison(chunk, MEM_allocN_len(chunk));
214
215 MEM_freeN(chunk);
216 chunk = chunk_next;
217 }
218}
219
221{
223 MEM_freeN(mi);
224}
225
227{
229 memiter_init(mi);
230}
231
233{
234 return mi->count;
235}
236
238
239/* -------------------------------------------------------------------- */
242
244{
245 if (mi->head != nullptr) {
246 BLI_memiter_chunk *chunk = mi->head;
247 BLI_memiter_elem *elem = (BLI_memiter_elem *)chunk->data;
248 return elem->data;
249 }
250 return nullptr;
251}
252
254{
255 if (mi->head != nullptr) {
256 BLI_memiter_chunk *chunk = mi->head;
257 BLI_memiter_elem *elem = (BLI_memiter_elem *)chunk->data;
258 *r_size = uint(elem->size);
259 return elem->data;
260 }
261 return nullptr;
262}
263
265
266/* -------------------------------------------------------------------- */
276
278{
279 iter->elem = mi->head ? (BLI_memiter_elem *)mi->head->data : nullptr;
280 iter->elem_left = mi->count;
281}
282
284{
285 return iter->elem_left != 0;
286}
287
289{
290 BLI_assert(iter->elem->size < 0);
291 BLI_memiter_chunk *chunk = (BLI_memiter_chunk *)(((data_t *)iter->elem) + iter->elem->size);
292 chunk = chunk->next;
293 iter->elem = chunk ? (BLI_memiter_elem *)chunk->data : nullptr;
294 BLI_assert(iter->elem == nullptr || iter->elem->size >= 0);
295}
296
298{
299 if (iter->elem_left != 0) {
300 iter->elem_left -= 1;
301 if (UNLIKELY(iter->elem->size < 0)) {
302 memiter_chunk_step(iter);
303 }
304 BLI_assert(iter->elem->size >= 0);
305 uint size = uint(iter->elem->size);
306 *r_size = size; /* <-- only difference */
307 data_t *data = iter->elem->data;
309 return (void *)data;
310 }
311 return nullptr;
312}
313
315{
316 if (iter->elem_left != 0) {
317 iter->elem_left -= 1;
318 if (UNLIKELY(iter->elem->size < 0)) {
319 memiter_chunk_step(iter);
320 }
321 BLI_assert(iter->elem->size >= 0);
322 uint size = uint(iter->elem->size);
323 data_t *data = iter->elem->data;
325 return (void *)data;
326 }
327 return nullptr;
328}
329
#define BLI_asan_unpoison(addr, size)
Definition BLI_asan.h:36
#define BLI_asan_poison(addr, size)
Definition BLI_asan.h:31
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_INLINE
static void memiter_set_rewind_offset(BLI_memiter *mi)
void BLI_memiter_clear(BLI_memiter *mi)
void * BLI_memiter_elem_first(BLI_memiter *mi)
void BLI_memiter_iter_init(BLI_memiter *mi, BLI_memiter_handle *iter)
#define PADUP(num, pad)
void * BLI_memiter_iter_step_size(BLI_memiter_handle *iter, uint *r_size)
static void memiter_init(BLI_memiter *mi)
void BLI_memiter_alloc_from(BLI_memiter *mi, uint elem_size, const void *data_from)
void * BLI_memiter_elem_first_size(BLI_memiter *mi, uint *r_size)
uint BLI_memiter_count(const BLI_memiter *mi)
bool BLI_memiter_iter_done(const BLI_memiter_handle *iter)
BLI_INLINE void memiter_chunk_step(BLI_memiter_handle *iter)
static void memiter_free_data(BLI_memiter *mi)
BLI_memiter * BLI_memiter_create(uint chunk_size_min)
void * BLI_memiter_iter_step(BLI_memiter_handle *iter)
void * BLI_memiter_alloc(BLI_memiter *mi, uint elem_size)
void BLI_memiter_destroy(BLI_memiter *mi)
BLI_INLINE uint data_offset_from_size(uint size)
void * BLI_memiter_calloc(BLI_memiter *mi, uint elem_size)
uintptr_t data_t
intptr_t offset_t
unsigned int uint
#define UNLIKELY(x)
#define LIKELY(x)
Read Guarded memory(de)allocation.
#define MEM_SIZE_OVERHEAD
BMesh const char void * data
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
void * MEM_mallocN(size_t len, const char *str)
Definition mallocn.cc:128
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
size_t(* MEM_allocN_len)(const void *vmemh)
Definition mallocn.cc:36
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
BLI_memiter_chunk * next
struct BLI_memiter_elem * elem
Definition BLI_memiter.h:55
data_t * data_last
BLI_memiter_chunk * tail
uint chunk_size_in_bytes_min
data_t * data_curr
BLI_memiter_chunk * head