Blender V4.3
stack.c
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9#include <stdlib.h> /* abort() */
10#include <string.h>
11
12#include "BLI_utildefines.h"
13#include "MEM_guardedalloc.h"
14
15#include "BLI_stack.h" /* own include */
16
17#include "BLI_strict_flags.h" /* Keep last. */
18
19#define USE_TOTELEM
20
21#define CHUNK_EMPTY ((size_t)-1)
22/* target chunks size: 64kb */
23#define CHUNK_SIZE_DEFAULT (1 << 16)
24/* ensure we get at least this many elems per chunk */
25#define CHUNK_ELEM_MIN 32
26
27struct StackChunk {
29 char data[0];
30};
31
32struct BLI_Stack {
33 struct StackChunk *chunk_curr; /* currently active chunk */
34 struct StackChunk *chunk_free; /* free chunks */
35 size_t chunk_index; /* index into 'chunk_curr' */
36 size_t chunk_elem_max; /* number of elements per chunk */
37 size_t elem_size;
38#ifdef USE_TOTELEM
39 size_t elem_num;
40#endif
41};
42
43static void *stack_get_last_elem(BLI_Stack *stack)
44{
45 return ((char *)(stack)->chunk_curr->data) + ((stack)->elem_size * (stack)->chunk_index);
46}
47
51static size_t stack_chunk_elem_max_calc(const size_t elem_size, size_t chunk_size)
52{
53 /* get at least this number of elems per chunk */
54 const size_t elem_size_min = elem_size * CHUNK_ELEM_MIN;
55
56 BLI_assert((elem_size != 0) && (chunk_size != 0));
57
58 while (UNLIKELY(chunk_size <= elem_size_min)) {
59 chunk_size <<= 1;
60 }
61
62 /* account for slop-space */
63 chunk_size -= (sizeof(struct StackChunk) + MEM_SIZE_OVERHEAD);
64
65 return chunk_size / elem_size;
66}
67
68BLI_Stack *BLI_stack_new_ex(const size_t elem_size,
69 const char *description,
70 const size_t chunk_size)
71{
72 BLI_Stack *stack = MEM_callocN(sizeof(*stack), description);
73
74 stack->chunk_elem_max = stack_chunk_elem_max_calc(elem_size, chunk_size);
75 stack->elem_size = elem_size;
76 /* force init */
77 stack->chunk_index = stack->chunk_elem_max - 1;
78
79 return stack;
80}
81
82BLI_Stack *BLI_stack_new(const size_t elem_size, const char *description)
83{
84 return BLI_stack_new_ex(elem_size, description, CHUNK_SIZE_DEFAULT);
85}
86
87static void stack_free_chunks(struct StackChunk *data)
88{
89 while (data) {
90 struct StackChunk *data_next = data->next;
91 MEM_freeN(data);
92 data = data_next;
93 }
94}
95
97{
100 MEM_freeN(stack);
101}
102
104{
105 stack->chunk_index++;
106
107 if (UNLIKELY(stack->chunk_index == stack->chunk_elem_max)) {
108 struct StackChunk *chunk;
109 if (stack->chunk_free) {
110 chunk = stack->chunk_free;
111 stack->chunk_free = chunk->next;
112 }
113 else {
114 chunk = MEM_mallocN(sizeof(*chunk) + (stack->elem_size * stack->chunk_elem_max), __func__);
115 }
116 chunk->next = stack->chunk_curr;
117 stack->chunk_curr = chunk;
118 stack->chunk_index = 0;
119 }
120
121 BLI_assert(stack->chunk_index < stack->chunk_elem_max);
122
123#ifdef USE_TOTELEM
124 stack->elem_num++;
125#endif
126
127 /* Return end of stack */
128 return stack_get_last_elem(stack);
129}
130
131void BLI_stack_push(BLI_Stack *stack, const void *src)
132{
133 void *dst = BLI_stack_push_r(stack);
134 memcpy(dst, src, stack->elem_size);
135}
136
137void BLI_stack_pop(BLI_Stack *stack, void *dst)
138{
139 BLI_assert(BLI_stack_is_empty(stack) == false);
140
141 memcpy(dst, stack_get_last_elem(stack), stack->elem_size);
142
143 BLI_stack_discard(stack);
144}
145
146void BLI_stack_pop_n(BLI_Stack *stack, void *dst, uint n)
147{
148 BLI_assert(n <= BLI_stack_count(stack));
149
150 while (n--) {
151 BLI_stack_pop(stack, dst);
152 dst = (void *)((char *)dst + stack->elem_size);
153 }
154}
155
156void BLI_stack_pop_n_reverse(BLI_Stack *stack, void *dst, uint n)
157{
158 BLI_assert(n <= BLI_stack_count(stack));
159
160 dst = (void *)((char *)dst + (stack->elem_size * n));
161
162 while (n--) {
163 dst = (void *)((char *)dst - stack->elem_size);
164 BLI_stack_pop(stack, dst);
165 }
166}
167
169{
170 BLI_assert(BLI_stack_is_empty(stack) == false);
171
172 return stack_get_last_elem(stack);
173}
174
176{
177 BLI_assert(BLI_stack_is_empty(stack) == false);
178
179#ifdef USE_TOTELEM
180 stack->elem_num--;
181#endif
182 if (UNLIKELY(--stack->chunk_index == CHUNK_EMPTY)) {
183 struct StackChunk *chunk_free;
184
185 chunk_free = stack->chunk_curr;
186 stack->chunk_curr = stack->chunk_curr->next;
187
188 chunk_free->next = stack->chunk_free;
189 stack->chunk_free = chunk_free;
190
191 stack->chunk_index = stack->chunk_elem_max - 1;
192 }
193}
194
196{
197#ifdef USE_TOTELEM
198 if (UNLIKELY(stack->elem_num == 0)) {
199 return;
200 }
201 stack->elem_num = 0;
202#else
203 if (UNLIKELY(stack->chunk_curr == NULL)) {
204 return;
205 }
206#endif
207
208 stack->chunk_index = stack->chunk_elem_max - 1;
209
210 if (stack->chunk_free) {
211 if (stack->chunk_curr) {
212 /* move all used chunks into tail of free list */
213 struct StackChunk *chunk_free_last = stack->chunk_free;
214 while (chunk_free_last->next) {
215 chunk_free_last = chunk_free_last->next;
216 }
217 chunk_free_last->next = stack->chunk_curr;
218 stack->chunk_curr = NULL;
219 }
220 }
221 else {
222 stack->chunk_free = stack->chunk_curr;
223 stack->chunk_curr = NULL;
224 }
225}
226
227size_t BLI_stack_count(const BLI_Stack *stack)
228{
229#ifdef USE_TOTELEM
230 return stack->elem_num;
231#else
232 struct StackChunk *data = stack->chunk_curr;
233 size_t elem_num = stack->chunk_index + 1;
234 size_t i;
235 if (elem_num != stack->chunk_elem_max) {
236 data = data->next;
237 }
238 else {
239 elem_num = 0;
240 }
241 for (i = 0; data; data = data->next) {
242 i++;
243 }
244 elem_num += stack->chunk_elem_max * i;
245 return elem_num;
246#endif
247}
248
250{
251#ifdef USE_TOTELEM
252 BLI_assert((stack->chunk_curr == NULL) == (stack->elem_num == 0));
253#endif
254 return (stack->chunk_curr == NULL);
255}
#define BLI_assert(a)
Definition BLI_assert.h:50
#define BLI_stack_new(esize, descr)
unsigned int uint
#define UNLIKELY(x)
Read Guarded memory(de)allocation.
#define MEM_SIZE_OVERHEAD
#define NULL
void *(* MEM_mallocN)(size_t len, const char *str)
Definition mallocn.cc:44
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
void *(* MEM_callocN)(size_t len, const char *str)
Definition mallocn.cc:42
void BLI_stack_clear(BLI_Stack *stack)
Definition stack.c:195
static void * stack_get_last_elem(BLI_Stack *stack)
Definition stack.c:43
#define CHUNK_EMPTY
Definition stack.c:21
BLI_Stack * BLI_stack_new_ex(const size_t elem_size, const char *description, const size_t chunk_size)
Definition stack.c:68
static void stack_free_chunks(struct StackChunk *data)
Definition stack.c:87
static size_t stack_chunk_elem_max_calc(const size_t elem_size, size_t chunk_size)
Definition stack.c:51
void BLI_stack_pop_n_reverse(BLI_Stack *stack, void *dst, uint n)
Definition stack.c:156
#define CHUNK_ELEM_MIN
Definition stack.c:25
void BLI_stack_free(BLI_Stack *stack)
Definition stack.c:96
void BLI_stack_discard(BLI_Stack *stack)
Definition stack.c:175
void * BLI_stack_push_r(BLI_Stack *stack)
Definition stack.c:103
#define CHUNK_SIZE_DEFAULT
Definition stack.c:23
size_t BLI_stack_count(const BLI_Stack *stack)
Definition stack.c:227
void BLI_stack_pop_n(BLI_Stack *stack, void *dst, uint n)
Definition stack.c:146
void BLI_stack_pop(BLI_Stack *stack, void *dst)
Definition stack.c:137
void BLI_stack_push(BLI_Stack *stack, const void *src)
Definition stack.c:131
bool BLI_stack_is_empty(const BLI_Stack *stack)
Definition stack.c:249
void * BLI_stack_peek(BLI_Stack *stack)
Definition stack.c:168
struct StackChunk * chunk_free
Definition stack.c:34
size_t elem_size
Definition stack.c:37
size_t elem_num
Definition stack.c:39
struct StackChunk * chunk_curr
Definition stack.c:33
size_t chunk_elem_max
Definition stack.c:36
size_t chunk_index
Definition stack.c:35
struct StackChunk * next
Definition stack.c:28