Listing the "Blender Foundation" as copyright holder implied the Blender Foundation holds copyright to files which may include work from many developers. While keeping copyright on headers makes sense for isolated libraries, Blender's own code may be refactored or moved between files in a way that makes the per file copyright holders less meaningful. Copyright references to the "Blender Foundation" have been replaced with "Blender Authors", with the exception of `./extern/` since these this contains libraries which are more isolated, any changed to license headers there can be handled on a case-by-case basis. Some directories in `./intern/` have also been excluded: - `./intern/cycles/` it's own `AUTHORS` file is planned. - `./intern/opensubdiv/`. An "AUTHORS" file has been added, using the chromium projects authors file as a template. Design task: #110784 Ref !110783.
256 lines
5.7 KiB
C
256 lines
5.7 KiB
C
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
/** \file
|
|
* \ingroup bli
|
|
*/
|
|
|
|
#include <stdlib.h> /* abort() */
|
|
#include <string.h>
|
|
|
|
#include "BLI_utildefines.h"
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
#include "BLI_stack.h" /* own include */
|
|
|
|
#include "BLI_strict_flags.h"
|
|
|
|
#define USE_TOTELEM
|
|
|
|
#define CHUNK_EMPTY ((size_t)-1)
|
|
/* 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 {
|
|
struct StackChunk *next;
|
|
char data[0];
|
|
};
|
|
|
|
struct BLI_Stack {
|
|
struct StackChunk *chunk_curr; /* currently active chunk */
|
|
struct StackChunk *chunk_free; /* free chunks */
|
|
size_t chunk_index; /* index into 'chunk_curr' */
|
|
size_t chunk_elem_max; /* number of elements per chunk */
|
|
size_t elem_size;
|
|
#ifdef USE_TOTELEM
|
|
size_t elem_num;
|
|
#endif
|
|
};
|
|
|
|
static void *stack_get_last_elem(BLI_Stack *stack)
|
|
{
|
|
return ((char *)(stack)->chunk_curr->data) + ((stack)->elem_size * (stack)->chunk_index);
|
|
}
|
|
|
|
/**
|
|
* \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 */
|
|
chunk_size -= (sizeof(struct StackChunk) + MEM_SIZE_OVERHEAD);
|
|
|
|
return chunk_size / elem_size;
|
|
}
|
|
|
|
BLI_Stack *BLI_stack_new_ex(const size_t elem_size,
|
|
const char *description,
|
|
const size_t chunk_size)
|
|
{
|
|
BLI_Stack *stack = MEM_callocN(sizeof(*stack), description);
|
|
|
|
stack->chunk_elem_max = stack_chunk_elem_max_calc(elem_size, chunk_size);
|
|
stack->elem_size = elem_size;
|
|
/* force init */
|
|
stack->chunk_index = stack->chunk_elem_max - 1;
|
|
|
|
return stack;
|
|
}
|
|
|
|
BLI_Stack *BLI_stack_new(const size_t elem_size, const char *description)
|
|
{
|
|
return BLI_stack_new_ex(elem_size, description, CHUNK_SIZE_DEFAULT);
|
|
}
|
|
|
|
static void stack_free_chunks(struct StackChunk *data)
|
|
{
|
|
while (data) {
|
|
struct StackChunk *data_next = data->next;
|
|
MEM_freeN(data);
|
|
data = data_next;
|
|
}
|
|
}
|
|
|
|
void BLI_stack_free(BLI_Stack *stack)
|
|
{
|
|
stack_free_chunks(stack->chunk_curr);
|
|
stack_free_chunks(stack->chunk_free);
|
|
MEM_freeN(stack);
|
|
}
|
|
|
|
void *BLI_stack_push_r(BLI_Stack *stack)
|
|
{
|
|
stack->chunk_index++;
|
|
|
|
if (UNLIKELY(stack->chunk_index == stack->chunk_elem_max)) {
|
|
struct StackChunk *chunk;
|
|
if (stack->chunk_free) {
|
|
chunk = stack->chunk_free;
|
|
stack->chunk_free = chunk->next;
|
|
}
|
|
else {
|
|
chunk = MEM_mallocN(sizeof(*chunk) + (stack->elem_size * stack->chunk_elem_max), __func__);
|
|
}
|
|
chunk->next = stack->chunk_curr;
|
|
stack->chunk_curr = chunk;
|
|
stack->chunk_index = 0;
|
|
}
|
|
|
|
BLI_assert(stack->chunk_index < stack->chunk_elem_max);
|
|
|
|
#ifdef USE_TOTELEM
|
|
stack->elem_num++;
|
|
#endif
|
|
|
|
/* Return end of stack */
|
|
return stack_get_last_elem(stack);
|
|
}
|
|
|
|
void BLI_stack_push(BLI_Stack *stack, const void *src)
|
|
{
|
|
void *dst = BLI_stack_push_r(stack);
|
|
memcpy(dst, src, stack->elem_size);
|
|
}
|
|
|
|
void BLI_stack_pop(BLI_Stack *stack, void *dst)
|
|
{
|
|
BLI_assert(BLI_stack_is_empty(stack) == false);
|
|
|
|
memcpy(dst, stack_get_last_elem(stack), stack->elem_size);
|
|
|
|
BLI_stack_discard(stack);
|
|
}
|
|
|
|
void BLI_stack_pop_n(BLI_Stack *stack, void *dst, uint n)
|
|
{
|
|
BLI_assert(n <= BLI_stack_count(stack));
|
|
|
|
while (n--) {
|
|
BLI_stack_pop(stack, dst);
|
|
dst = (void *)((char *)dst + stack->elem_size);
|
|
}
|
|
}
|
|
|
|
void BLI_stack_pop_n_reverse(BLI_Stack *stack, void *dst, uint n)
|
|
{
|
|
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);
|
|
}
|
|
}
|
|
|
|
void *BLI_stack_peek(BLI_Stack *stack)
|
|
{
|
|
BLI_assert(BLI_stack_is_empty(stack) == false);
|
|
|
|
return stack_get_last_elem(stack);
|
|
}
|
|
|
|
void BLI_stack_discard(BLI_Stack *stack)
|
|
{
|
|
BLI_assert(BLI_stack_is_empty(stack) == false);
|
|
|
|
#ifdef USE_TOTELEM
|
|
stack->elem_num--;
|
|
#endif
|
|
if (UNLIKELY(--stack->chunk_index == CHUNK_EMPTY)) {
|
|
struct StackChunk *chunk_free;
|
|
|
|
chunk_free = stack->chunk_curr;
|
|
stack->chunk_curr = stack->chunk_curr->next;
|
|
|
|
chunk_free->next = stack->chunk_free;
|
|
stack->chunk_free = chunk_free;
|
|
|
|
stack->chunk_index = stack->chunk_elem_max - 1;
|
|
}
|
|
}
|
|
|
|
void BLI_stack_clear(BLI_Stack *stack)
|
|
{
|
|
#ifdef USE_TOTELEM
|
|
if (UNLIKELY(stack->elem_num == 0)) {
|
|
return;
|
|
}
|
|
stack->elem_num = 0;
|
|
#else
|
|
if (UNLIKELY(stack->chunk_curr == NULL)) {
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
stack->chunk_index = stack->chunk_elem_max - 1;
|
|
|
|
if (stack->chunk_free) {
|
|
if (stack->chunk_curr) {
|
|
/* move all used chunks into tail of free list */
|
|
struct StackChunk *chunk_free_last = stack->chunk_free;
|
|
while (chunk_free_last->next) {
|
|
chunk_free_last = chunk_free_last->next;
|
|
}
|
|
chunk_free_last->next = stack->chunk_curr;
|
|
stack->chunk_curr = NULL;
|
|
}
|
|
}
|
|
else {
|
|
stack->chunk_free = stack->chunk_curr;
|
|
stack->chunk_curr = NULL;
|
|
}
|
|
}
|
|
|
|
size_t BLI_stack_count(const BLI_Stack *stack)
|
|
{
|
|
#ifdef USE_TOTELEM
|
|
return stack->elem_num;
|
|
#else
|
|
struct StackChunk *data = stack->chunk_curr;
|
|
size_t elem_num = stack->chunk_index + 1;
|
|
size_t i;
|
|
if (elem_num != stack->chunk_elem_max) {
|
|
data = data->next;
|
|
}
|
|
else {
|
|
elem_num = 0;
|
|
}
|
|
for (i = 0; data; data = data->next) {
|
|
i++;
|
|
}
|
|
elem_num += stack->chunk_elem_max * i;
|
|
return elem_num;
|
|
#endif
|
|
}
|
|
|
|
bool BLI_stack_is_empty(const BLI_Stack *stack)
|
|
{
|
|
#ifdef USE_TOTELEM
|
|
BLI_assert((stack->chunk_curr == NULL) == (stack->elem_num == 0));
|
|
#endif
|
|
return (stack->chunk_curr == NULL);
|
|
}
|