2023-08-16 00:20:26 +10:00
|
|
|
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
2023-05-31 16:19:06 +02:00
|
|
|
*
|
|
|
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
2012-08-05 23:29:43 +00:00
|
|
|
|
2019-02-18 08:08:12 +11:00
|
|
|
/** \file
|
|
|
|
|
* \ingroup bli
|
2012-08-06 08:01:20 +00:00
|
|
|
*/
|
|
|
|
|
|
2025-02-09 19:05:01 +01:00
|
|
|
#include <cstdlib> /* abort() */
|
|
|
|
|
#include <cstring>
|
2012-08-06 08:01:20 +00:00
|
|
|
|
2012-08-05 23:29:43 +00:00
|
|
|
#include "BLI_utildefines.h"
|
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
|
|
2014-06-28 23:07:32 +10:00
|
|
|
#include "BLI_stack.h" /* own include */
|
|
|
|
|
|
2025-01-26 20:07:57 +01:00
|
|
|
#include "BLI_strict_flags.h" /* IWYU pragma: keep. Keep last. */
|
2014-06-28 23:07:32 +10:00
|
|
|
|
2014-07-15 20:37:06 +10:00
|
|
|
#define USE_TOTELEM
|
2012-08-05 23:29:43 +00:00
|
|
|
|
2025-02-13 13:29:41 +11:00
|
|
|
#define CHUNK_EMPTY size_t(-1)
|
2014-06-30 11:44:28 +10:00
|
|
|
/* target chunks size: 64kb */
|
|
|
|
|
#define CHUNK_SIZE_DEFAULT (1 << 16)
|
|
|
|
|
/* ensure we get at least this many elems per chunk */
|
|
|
|
|
#define CHUNK_ELEM_MIN 32
|
|
|
|
|
|
|
|
|
|
struct StackChunk {
|
2025-02-09 19:05:01 +01:00
|
|
|
StackChunk *next;
|
2014-06-30 11:44:28 +10:00
|
|
|
char data[0];
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct BLI_Stack {
|
2025-02-09 19:05:01 +01:00
|
|
|
StackChunk *chunk_curr; /* currently active chunk */
|
|
|
|
|
StackChunk *chunk_free; /* free chunks */
|
|
|
|
|
size_t chunk_index; /* index into 'chunk_curr' */
|
|
|
|
|
size_t chunk_elem_max; /* number of elements per chunk */
|
2014-06-28 23:07:32 +10:00
|
|
|
size_t elem_size;
|
2014-06-30 11:44:28 +10:00
|
|
|
#ifdef USE_TOTELEM
|
2022-03-30 17:26:42 +11:00
|
|
|
size_t elem_num;
|
2014-06-30 11:44:28 +10:00
|
|
|
#endif
|
2012-08-06 13:31:28 +00:00
|
|
|
};
|
2012-08-05 23:29:43 +00:00
|
|
|
|
2019-10-01 16:35:17 +02:00
|
|
|
static void *stack_get_last_elem(BLI_Stack *stack)
|
|
|
|
|
{
|
|
|
|
|
return ((char *)(stack)->chunk_curr->data) + ((stack)->elem_size * (stack)->chunk_index);
|
|
|
|
|
}
|
|
|
|
|
|
2014-06-30 11:44:28 +10:00
|
|
|
/**
|
|
|
|
|
* \return number of elements per chunk, optimized for slop-space.
|
|
|
|
|
*/
|
|
|
|
|
static size_t stack_chunk_elem_max_calc(const size_t elem_size, size_t chunk_size)
|
|
|
|
|
{
|
|
|
|
|
/* get at least this number of elems per chunk */
|
|
|
|
|
const size_t elem_size_min = elem_size * CHUNK_ELEM_MIN;
|
|
|
|
|
|
|
|
|
|
BLI_assert((elem_size != 0) && (chunk_size != 0));
|
|
|
|
|
|
|
|
|
|
while (UNLIKELY(chunk_size <= elem_size_min)) {
|
|
|
|
|
chunk_size <<= 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* account for slop-space */
|
2025-02-09 19:05:01 +01:00
|
|
|
chunk_size -= (sizeof(StackChunk) + MEM_SIZE_OVERHEAD);
|
2014-06-30 11:44:28 +10:00
|
|
|
|
|
|
|
|
return chunk_size / elem_size;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BLI_Stack *BLI_stack_new_ex(const size_t elem_size,
|
|
|
|
|
const char *description,
|
|
|
|
|
const size_t chunk_size)
|
2012-08-05 23:29:43 +00:00
|
|
|
{
|
2025-03-05 16:35:09 +01:00
|
|
|
BLI_Stack *stack = MEM_callocN<BLI_Stack>(description);
|
2012-08-05 23:29:43 +00:00
|
|
|
|
2014-06-30 11:44:28 +10:00
|
|
|
stack->chunk_elem_max = stack_chunk_elem_max_calc(elem_size, chunk_size);
|
2012-08-05 23:29:43 +00:00
|
|
|
stack->elem_size = elem_size;
|
2014-06-30 11:44:28 +10:00
|
|
|
/* force init */
|
|
|
|
|
stack->chunk_index = stack->chunk_elem_max - 1;
|
|
|
|
|
|
2012-08-05 23:29:43 +00:00
|
|
|
return stack;
|
|
|
|
|
}
|
|
|
|
|
|
2014-06-30 11:44:28 +10:00
|
|
|
BLI_Stack *BLI_stack_new(const size_t elem_size, const char *description)
|
2012-08-05 23:29:43 +00:00
|
|
|
{
|
2014-06-30 11:44:28 +10:00
|
|
|
return BLI_stack_new_ex(elem_size, description, CHUNK_SIZE_DEFAULT);
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-09 19:05:01 +01:00
|
|
|
static void stack_free_chunks(StackChunk *data)
|
2014-06-30 11:44:28 +10:00
|
|
|
{
|
|
|
|
|
while (data) {
|
2025-02-09 19:05:01 +01:00
|
|
|
StackChunk *data_next = data->next;
|
2014-06-30 11:44:28 +10:00
|
|
|
MEM_freeN(data);
|
|
|
|
|
data = data_next;
|
2012-08-05 23:29:43 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-06-30 11:44:28 +10:00
|
|
|
void BLI_stack_free(BLI_Stack *stack)
|
|
|
|
|
{
|
|
|
|
|
stack_free_chunks(stack->chunk_curr);
|
|
|
|
|
stack_free_chunks(stack->chunk_free);
|
|
|
|
|
MEM_freeN(stack);
|
|
|
|
|
}
|
2012-08-05 23:29:43 +00:00
|
|
|
|
2014-07-15 20:37:06 +10:00
|
|
|
void *BLI_stack_push_r(BLI_Stack *stack)
|
2012-08-05 23:29:43 +00:00
|
|
|
{
|
2014-06-30 11:44:28 +10:00
|
|
|
stack->chunk_index++;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2014-06-30 11:44:28 +10:00
|
|
|
if (UNLIKELY(stack->chunk_index == stack->chunk_elem_max)) {
|
2025-02-09 19:05:01 +01:00
|
|
|
StackChunk *chunk;
|
2014-06-30 11:44:28 +10:00
|
|
|
if (stack->chunk_free) {
|
|
|
|
|
chunk = stack->chunk_free;
|
|
|
|
|
stack->chunk_free = chunk->next;
|
2012-08-05 23:29:43 +00:00
|
|
|
}
|
|
|
|
|
else {
|
2025-02-09 19:05:01 +01:00
|
|
|
chunk = static_cast<StackChunk *>(
|
|
|
|
|
MEM_mallocN(sizeof(*chunk) + (stack->elem_size * stack->chunk_elem_max), __func__));
|
2012-08-05 23:29:43 +00:00
|
|
|
}
|
2014-06-30 11:44:28 +10:00
|
|
|
chunk->next = stack->chunk_curr;
|
|
|
|
|
stack->chunk_curr = chunk;
|
|
|
|
|
stack->chunk_index = 0;
|
2012-08-05 23:29:43 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2014-06-30 11:44:28 +10:00
|
|
|
BLI_assert(stack->chunk_index < stack->chunk_elem_max);
|
2012-08-05 23:29:43 +00:00
|
|
|
|
2014-06-30 11:44:28 +10:00
|
|
|
#ifdef USE_TOTELEM
|
2022-03-30 17:26:42 +11:00
|
|
|
stack->elem_num++;
|
2014-06-30 11:44:28 +10:00
|
|
|
#endif
|
|
|
|
|
|
2014-07-15 20:37:06 +10:00
|
|
|
/* Return end of stack */
|
2019-10-01 16:35:17 +02:00
|
|
|
return stack_get_last_elem(stack);
|
2014-07-15 20:37:06 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void BLI_stack_push(BLI_Stack *stack, const void *src)
|
|
|
|
|
{
|
|
|
|
|
void *dst = BLI_stack_push_r(stack);
|
|
|
|
|
memcpy(dst, src, stack->elem_size);
|
2012-08-05 23:29:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void BLI_stack_pop(BLI_Stack *stack, void *dst)
|
|
|
|
|
{
|
2014-06-30 11:44:28 +10:00
|
|
|
BLI_assert(BLI_stack_is_empty(stack) == false);
|
|
|
|
|
|
2019-10-01 16:35:17 +02:00
|
|
|
memcpy(dst, stack_get_last_elem(stack), stack->elem_size);
|
2014-09-28 13:24:01 +10:00
|
|
|
|
|
|
|
|
BLI_stack_discard(stack);
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-25 17:04:52 +10:00
|
|
|
void BLI_stack_pop_n(BLI_Stack *stack, void *dst, uint n)
|
2014-09-28 13:24:01 +10:00
|
|
|
{
|
|
|
|
|
BLI_assert(n <= BLI_stack_count(stack));
|
|
|
|
|
|
|
|
|
|
while (n--) {
|
|
|
|
|
BLI_stack_pop(stack, dst);
|
|
|
|
|
dst = (void *)((char *)dst + stack->elem_size);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-26 10:56:05 +10:00
|
|
|
void BLI_stack_pop_n_reverse(BLI_Stack *stack, void *dst, uint n)
|
2015-06-19 17:40:35 +10:00
|
|
|
{
|
|
|
|
|
BLI_assert(n <= BLI_stack_count(stack));
|
|
|
|
|
|
|
|
|
|
dst = (void *)((char *)dst + (stack->elem_size * n));
|
|
|
|
|
|
|
|
|
|
while (n--) {
|
|
|
|
|
dst = (void *)((char *)dst - stack->elem_size);
|
|
|
|
|
BLI_stack_pop(stack, dst);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-28 13:24:01 +10:00
|
|
|
void *BLI_stack_peek(BLI_Stack *stack)
|
|
|
|
|
{
|
|
|
|
|
BLI_assert(BLI_stack_is_empty(stack) == false);
|
|
|
|
|
|
2019-10-01 16:35:17 +02:00
|
|
|
return stack_get_last_elem(stack);
|
2014-09-28 13:24:01 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void BLI_stack_discard(BLI_Stack *stack)
|
|
|
|
|
{
|
|
|
|
|
BLI_assert(BLI_stack_is_empty(stack) == false);
|
|
|
|
|
|
2014-06-30 11:44:28 +10:00
|
|
|
#ifdef USE_TOTELEM
|
2022-03-30 17:26:42 +11:00
|
|
|
stack->elem_num--;
|
2014-06-30 11:44:28 +10:00
|
|
|
#endif
|
2014-07-16 11:11:18 +10:00
|
|
|
if (UNLIKELY(--stack->chunk_index == CHUNK_EMPTY)) {
|
2025-02-09 19:05:01 +01:00
|
|
|
StackChunk *chunk_free;
|
2014-06-30 11:44:28 +10:00
|
|
|
|
2014-07-15 20:37:06 +10:00
|
|
|
chunk_free = stack->chunk_curr;
|
|
|
|
|
stack->chunk_curr = stack->chunk_curr->next;
|
2014-06-30 11:44:28 +10:00
|
|
|
|
2014-07-15 20:37:06 +10:00
|
|
|
chunk_free->next = stack->chunk_free;
|
|
|
|
|
stack->chunk_free = chunk_free;
|
2014-06-30 11:44:28 +10:00
|
|
|
|
2014-07-15 20:37:06 +10:00
|
|
|
stack->chunk_index = stack->chunk_elem_max - 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-06-03 18:06:47 +10:00
|
|
|
void BLI_stack_clear(BLI_Stack *stack)
|
|
|
|
|
{
|
|
|
|
|
#ifdef USE_TOTELEM
|
2022-03-30 17:26:42 +11:00
|
|
|
if (UNLIKELY(stack->elem_num == 0)) {
|
2015-06-03 18:06:47 +10:00
|
|
|
return;
|
|
|
|
|
}
|
2022-03-30 17:26:42 +11:00
|
|
|
stack->elem_num = 0;
|
2015-06-03 18:06:47 +10:00
|
|
|
#else
|
2025-02-09 19:05:01 +01:00
|
|
|
if (UNLIKELY(stack->chunk_curr == nullptr)) {
|
2015-06-03 18:06:47 +10:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
stack->chunk_index = stack->chunk_elem_max - 1;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2015-06-03 18:06:47 +10:00
|
|
|
if (stack->chunk_free) {
|
|
|
|
|
if (stack->chunk_curr) {
|
|
|
|
|
/* move all used chunks into tail of free list */
|
2025-02-09 19:05:01 +01:00
|
|
|
StackChunk *chunk_free_last = stack->chunk_free;
|
2015-06-03 18:06:47 +10:00
|
|
|
while (chunk_free_last->next) {
|
|
|
|
|
chunk_free_last = chunk_free_last->next;
|
|
|
|
|
}
|
|
|
|
|
chunk_free_last->next = stack->chunk_curr;
|
2025-02-09 19:05:01 +01:00
|
|
|
stack->chunk_curr = nullptr;
|
2015-06-03 18:06:47 +10:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
stack->chunk_free = stack->chunk_curr;
|
2025-02-09 19:05:01 +01:00
|
|
|
stack->chunk_curr = nullptr;
|
2015-06-03 18:06:47 +10:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-07-15 20:37:06 +10:00
|
|
|
size_t BLI_stack_count(const BLI_Stack *stack)
|
|
|
|
|
{
|
|
|
|
|
#ifdef USE_TOTELEM
|
2022-03-30 17:26:42 +11:00
|
|
|
return stack->elem_num;
|
2014-07-15 20:37:06 +10:00
|
|
|
#else
|
2025-02-09 19:05:01 +01:00
|
|
|
StackChunk *data = stack->chunk_curr;
|
2022-03-30 17:26:42 +11:00
|
|
|
size_t elem_num = stack->chunk_index + 1;
|
2014-07-15 20:37:06 +10:00
|
|
|
size_t i;
|
2022-03-30 17:26:42 +11:00
|
|
|
if (elem_num != stack->chunk_elem_max) {
|
2014-07-15 20:37:06 +10:00
|
|
|
data = data->next;
|
|
|
|
|
}
|
|
|
|
|
else {
|
2022-03-30 17:26:42 +11:00
|
|
|
elem_num = 0;
|
2014-07-15 20:37:06 +10:00
|
|
|
}
|
|
|
|
|
for (i = 0; data; data = data->next) {
|
|
|
|
|
i++;
|
|
|
|
|
}
|
2022-03-30 17:26:42 +11:00
|
|
|
elem_num += stack->chunk_elem_max * i;
|
|
|
|
|
return elem_num;
|
2014-07-15 20:37:06 +10:00
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
2014-06-28 23:07:32 +10:00
|
|
|
bool BLI_stack_is_empty(const BLI_Stack *stack)
|
2012-08-05 23:29:43 +00:00
|
|
|
{
|
2014-07-15 20:37:06 +10:00
|
|
|
#ifdef USE_TOTELEM
|
2025-02-09 19:05:01 +01:00
|
|
|
BLI_assert((stack->chunk_curr == nullptr) == (stack->elem_num == 0));
|
2014-07-15 20:37:06 +10:00
|
|
|
#endif
|
2025-02-09 19:05:01 +01:00
|
|
|
return (stack->chunk_curr == nullptr);
|
2012-08-05 23:29:43 +00:00
|
|
|
}
|