Blender  V2.93
stack.c
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 
21 #include <stdlib.h> /* abort() */
22 #include <string.h>
23 
24 #include "BLI_utildefines.h"
25 #include "MEM_guardedalloc.h"
26 
27 #include "BLI_stack.h" /* own include */
28 
29 #include "BLI_strict_flags.h"
30 
31 #define USE_TOTELEM
32 
33 #define CHUNK_EMPTY ((size_t)-1)
34 /* target chunks size: 64kb */
35 #define CHUNK_SIZE_DEFAULT (1 << 16)
36 /* ensure we get at least this many elems per chunk */
37 #define CHUNK_ELEM_MIN 32
38 
39 struct StackChunk {
40  struct StackChunk *next;
41  char data[0];
42 };
43 
44 struct BLI_Stack {
45  struct StackChunk *chunk_curr; /* currently active chunk */
46  struct StackChunk *chunk_free; /* free chunks */
47  size_t chunk_index; /* index into 'chunk_curr' */
48  size_t chunk_elem_max; /* number of elements per chunk */
49  size_t elem_size;
50 #ifdef USE_TOTELEM
51  size_t totelem;
52 #endif
53 };
54 
55 static void *stack_get_last_elem(BLI_Stack *stack)
56 {
57  return ((char *)(stack)->chunk_curr->data) + ((stack)->elem_size * (stack)->chunk_index);
58 }
59 
63 static size_t stack_chunk_elem_max_calc(const size_t elem_size, size_t chunk_size)
64 {
65  /* get at least this number of elems per chunk */
66  const size_t elem_size_min = elem_size * CHUNK_ELEM_MIN;
67 
68  BLI_assert((elem_size != 0) && (chunk_size != 0));
69 
70  while (UNLIKELY(chunk_size <= elem_size_min)) {
71  chunk_size <<= 1;
72  }
73 
74  /* account for slop-space */
75  chunk_size -= (sizeof(struct StackChunk) + MEM_SIZE_OVERHEAD);
76 
77  return chunk_size / elem_size;
78 }
79 
80 BLI_Stack *BLI_stack_new_ex(const size_t elem_size,
81  const char *description,
82  const size_t chunk_size)
83 {
84  BLI_Stack *stack = MEM_callocN(sizeof(*stack), description);
85 
86  stack->chunk_elem_max = stack_chunk_elem_max_calc(elem_size, chunk_size);
87  stack->elem_size = elem_size;
88  /* force init */
89  stack->chunk_index = stack->chunk_elem_max - 1;
90 
91  return stack;
92 }
93 
97 BLI_Stack *BLI_stack_new(const size_t elem_size, const char *description)
98 {
99  return BLI_stack_new_ex(elem_size, description, CHUNK_SIZE_DEFAULT);
100 }
101 
102 static void stack_free_chunks(struct StackChunk *data)
103 {
104  while (data) {
105  struct StackChunk *data_next = data->next;
106  MEM_freeN(data);
107  data = data_next;
108  }
109 }
110 
115 {
118  MEM_freeN(stack);
119 }
120 
128 {
129  stack->chunk_index++;
130 
131  if (UNLIKELY(stack->chunk_index == stack->chunk_elem_max)) {
132  struct StackChunk *chunk;
133  if (stack->chunk_free) {
134  chunk = stack->chunk_free;
135  stack->chunk_free = chunk->next;
136  }
137  else {
138  chunk = MEM_mallocN(sizeof(*chunk) + (stack->elem_size * stack->chunk_elem_max), __func__);
139  }
140  chunk->next = stack->chunk_curr;
141  stack->chunk_curr = chunk;
142  stack->chunk_index = 0;
143  }
144 
145  BLI_assert(stack->chunk_index < stack->chunk_elem_max);
146 
147 #ifdef USE_TOTELEM
148  stack->totelem++;
149 #endif
150 
151  /* Return end of stack */
152  return stack_get_last_elem(stack);
153 }
154 
163 void BLI_stack_push(BLI_Stack *stack, const void *src)
164 {
165  void *dst = BLI_stack_push_r(stack);
166  memcpy(dst, src, stack->elem_size);
167 }
168 
175 void BLI_stack_pop(BLI_Stack *stack, void *dst)
176 {
177  BLI_assert(BLI_stack_is_empty(stack) == false);
178 
179  memcpy(dst, stack_get_last_elem(stack), stack->elem_size);
180 
181  BLI_stack_discard(stack);
182 }
183 
193 void BLI_stack_pop_n(BLI_Stack *stack, void *dst, unsigned int n)
194 {
195  BLI_assert(n <= BLI_stack_count(stack));
196 
197  while (n--) {
198  BLI_stack_pop(stack, dst);
199  dst = (void *)((char *)dst + stack->elem_size);
200  }
201 }
202 
208 void BLI_stack_pop_n_reverse(BLI_Stack *stack, void *dst, unsigned int n)
209 {
210  BLI_assert(n <= BLI_stack_count(stack));
211 
212  dst = (void *)((char *)dst + (stack->elem_size * n));
213 
214  while (n--) {
215  dst = (void *)((char *)dst - stack->elem_size);
216  BLI_stack_pop(stack, dst);
217  }
218 }
219 
221 {
222  BLI_assert(BLI_stack_is_empty(stack) == false);
223 
224  return stack_get_last_elem(stack);
225 }
226 
231 {
232  BLI_assert(BLI_stack_is_empty(stack) == false);
233 
234 #ifdef USE_TOTELEM
235  stack->totelem--;
236 #endif
237  if (UNLIKELY(--stack->chunk_index == CHUNK_EMPTY)) {
238  struct StackChunk *chunk_free;
239 
240  chunk_free = stack->chunk_curr;
241  stack->chunk_curr = stack->chunk_curr->next;
242 
243  chunk_free->next = stack->chunk_free;
244  stack->chunk_free = chunk_free;
245 
246  stack->chunk_index = stack->chunk_elem_max - 1;
247  }
248 }
249 
254 {
255 #ifdef USE_TOTELEM
256  if (UNLIKELY(stack->totelem == 0)) {
257  return;
258  }
259  stack->totelem = 0;
260 #else
261  if (UNLIKELY(stack->chunk_curr == NULL)) {
262  return;
263  }
264 #endif
265 
266  stack->chunk_index = stack->chunk_elem_max - 1;
267 
268  if (stack->chunk_free) {
269  if (stack->chunk_curr) {
270  /* move all used chunks into tail of free list */
271  struct StackChunk *chunk_free_last = stack->chunk_free;
272  while (chunk_free_last->next) {
273  chunk_free_last = chunk_free_last->next;
274  }
275  chunk_free_last->next = stack->chunk_curr;
276  stack->chunk_curr = NULL;
277  }
278  }
279  else {
280  stack->chunk_free = stack->chunk_curr;
281  stack->chunk_curr = NULL;
282  }
283 }
284 
285 size_t BLI_stack_count(const BLI_Stack *stack)
286 {
287 #ifdef USE_TOTELEM
288  return stack->totelem;
289 #else
290  struct StackChunk *data = stack->chunk_curr;
291  size_t totelem = stack->chunk_index + 1;
292  size_t i;
293  if (totelem != stack->chunk_elem_max) {
294  data = data->next;
295  }
296  else {
297  totelem = 0;
298  }
299  for (i = 0; data; data = data->next) {
300  i++;
301  }
302  totelem += stack->chunk_elem_max * i;
303  return totelem;
304 #endif
305 }
306 
310 bool BLI_stack_is_empty(const BLI_Stack *stack)
311 {
312 #ifdef USE_TOTELEM
313  BLI_assert((stack->chunk_curr == NULL) == (stack->totelem == 0));
314 #endif
315  return (stack->chunk_curr == NULL);
316 }
#define BLI_assert(a)
Definition: BLI_assert.h:58
Strict compiler flags for areas of code we want to ensure don't do conversions without us knowing abo...
#define UNLIKELY(x)
Read Guarded memory(de)allocation.
#define MEM_SIZE_OVERHEAD
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:41
void *(* MEM_callocN)(size_t len, const char *str)
Definition: mallocn.c:45
void *(* MEM_mallocN)(size_t len, const char *str)
Definition: mallocn.c:47
void BLI_stack_clear(BLI_Stack *stack)
Definition: stack.c:253
static void * stack_get_last_elem(BLI_Stack *stack)
Definition: stack.c:55
void * BLI_stack_push_r(BLI_Stack *stack)
Definition: stack.c:127
#define CHUNK_EMPTY
Definition: stack.c:33
void * BLI_stack_peek(BLI_Stack *stack)
Definition: stack.c:220
void BLI_stack_pop_n_reverse(BLI_Stack *stack, void *dst, unsigned int n)
Definition: stack.c:208
void BLI_stack_pop_n(BLI_Stack *stack, void *dst, unsigned int n)
Definition: stack.c:193
static void stack_free_chunks(struct StackChunk *data)
Definition: stack.c:102
static size_t stack_chunk_elem_max_calc(const size_t elem_size, size_t chunk_size)
Definition: stack.c:63
#define CHUNK_ELEM_MIN
Definition: stack.c:37
void BLI_stack_free(BLI_Stack *stack)
Definition: stack.c:114
void BLI_stack_discard(BLI_Stack *stack)
Definition: stack.c:230
BLI_Stack * BLI_stack_new(const size_t elem_size, const char *description)
Definition: stack.c:97
#define CHUNK_SIZE_DEFAULT
Definition: stack.c:35
size_t BLI_stack_count(const BLI_Stack *stack)
Definition: stack.c:285
void BLI_stack_pop(BLI_Stack *stack, void *dst)
Definition: stack.c:175
void BLI_stack_push(BLI_Stack *stack, const void *src)
Definition: stack.c:163
bool BLI_stack_is_empty(const BLI_Stack *stack)
Definition: stack.c:310
BLI_Stack * BLI_stack_new_ex(const size_t elem_size, const char *description, const size_t chunk_size)
Definition: stack.c:80
struct StackChunk * chunk_free
Definition: stack.c:46
size_t elem_size
Definition: stack.c:49
struct StackChunk * chunk_curr
Definition: stack.c:45
size_t chunk_elem_max
Definition: stack.c:48
size_t chunk_index
Definition: stack.c:47
size_t totelem
Definition: stack.c:51
struct StackChunk * next
Definition: stack.c:40
char data[0]
Definition: stack.c:41