Blender V4.3
gsqueue.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
12#include <string.h>
13
14#include "MEM_guardedalloc.h"
15
16#include "BLI_gsqueue.h"
17#include "BLI_utildefines.h"
18
19#include "BLI_strict_flags.h" /* Keep last. */
20
21/* target chunk size: 64kb */
22#define CHUNK_SIZE_DEFAULT (1 << 16)
23/* ensure we get at least this many elems per chunk */
24#define CHUNK_ELEM_MIN 32
25
26struct QueueChunk {
28 char data[0];
29};
30
31struct _GSQueue {
32 struct QueueChunk *chunk_first; /* first active chunk to pop from */
33 struct QueueChunk *chunk_last; /* last active chunk to push onto */
34 struct QueueChunk *chunk_free; /* free chunks to reuse */
35 size_t chunk_first_index; /* index into 'chunk_first' */
36 size_t chunk_last_index; /* index into 'chunk_last' */
37 size_t chunk_elem_max; /* number of elements per chunk */
38 size_t elem_size; /* memory size of elements */
39 size_t elem_num; /* total number of elements */
40};
41
42static void *queue_get_first_elem(GSQueue *queue)
43{
44 return ((char *)(queue)->chunk_first->data) + ((queue)->elem_size * (queue)->chunk_first_index);
45}
46
47static void *queue_get_last_elem(GSQueue *queue)
48{
49 return ((char *)(queue)->chunk_last->data) + ((queue)->elem_size * (queue)->chunk_last_index);
50}
51
55static size_t queue_chunk_elem_max_calc(const size_t elem_size, size_t chunk_size)
56{
57 /* get at least this number of elems per chunk */
58 const size_t elem_size_min = elem_size * CHUNK_ELEM_MIN;
59
60 BLI_assert((elem_size != 0) && (chunk_size != 0));
61
62 while (UNLIKELY(chunk_size <= elem_size_min)) {
63 chunk_size <<= 1;
64 }
65
66 /* account for slop-space */
67 chunk_size -= (sizeof(struct QueueChunk) + MEM_SIZE_OVERHEAD);
68
69 return chunk_size / elem_size;
70}
71
72GSQueue *BLI_gsqueue_new(const size_t elem_size)
73{
74 GSQueue *queue = MEM_callocN(sizeof(*queue), "BLI_gsqueue_new");
75
76 queue->chunk_elem_max = queue_chunk_elem_max_calc(elem_size, CHUNK_SIZE_DEFAULT);
77 queue->elem_size = elem_size;
78 /* force init */
79 queue->chunk_last_index = queue->chunk_elem_max - 1;
80
81 return queue;
82}
83
84static void queue_free_chunk(struct QueueChunk *data)
85{
86 while (data) {
87 struct QueueChunk *data_next = data->next;
88 MEM_freeN(data);
89 data = data_next;
90 }
91}
92
94{
95 queue_free_chunk(queue->chunk_first);
96 queue_free_chunk(queue->chunk_free);
97 MEM_freeN(queue);
98}
99
100void BLI_gsqueue_push(GSQueue *queue, const void *item)
101{
102 queue->chunk_last_index++;
103 queue->elem_num++;
104
105 if (UNLIKELY(queue->chunk_last_index == queue->chunk_elem_max)) {
106 struct QueueChunk *chunk;
107 if (queue->chunk_free) {
108 chunk = queue->chunk_free;
109 queue->chunk_free = chunk->next;
110 }
111 else {
112 chunk = MEM_mallocN(sizeof(*chunk) + (queue->elem_size * queue->chunk_elem_max), __func__);
113 }
114
115 chunk->next = NULL;
116
117 if (queue->chunk_last == NULL) {
118 queue->chunk_first = chunk;
119 }
120 else {
121 queue->chunk_last->next = chunk;
122 }
123
124 queue->chunk_last = chunk;
125 queue->chunk_last_index = 0;
126 }
127
128 BLI_assert(queue->chunk_last_index < queue->chunk_elem_max);
129
130 /* Return last of queue */
131 memcpy(queue_get_last_elem(queue), item, queue->elem_size);
132}
133
134void BLI_gsqueue_pop(GSQueue *queue, void *r_item)
135{
136 BLI_assert(BLI_gsqueue_is_empty(queue) == false);
137
138 memcpy(r_item, queue_get_first_elem(queue), queue->elem_size);
139 queue->chunk_first_index++;
140 queue->elem_num--;
141
142 if (UNLIKELY(queue->chunk_first_index == queue->chunk_elem_max || queue->elem_num == 0)) {
143 struct QueueChunk *chunk_free = queue->chunk_first;
144
145 queue->chunk_first = queue->chunk_first->next;
146 queue->chunk_first_index = 0;
147 if (queue->chunk_first == NULL) {
148 queue->chunk_last = NULL;
149 queue->chunk_last_index = queue->chunk_elem_max - 1;
150 }
151
152 chunk_free->next = queue->chunk_free;
153 queue->chunk_free = chunk_free;
154 }
155}
156
157size_t BLI_gsqueue_len(const GSQueue *queue)
158{
159 return queue->elem_num;
160}
161
163{
164 return (queue->chunk_first == NULL);
165}
#define BLI_assert(a)
Definition BLI_assert.h:50
#define UNLIKELY(x)
Read Guarded memory(de)allocation.
#define MEM_SIZE_OVERHEAD
#define NULL
void BLI_gsqueue_free(GSQueue *queue)
Definition gsqueue.c:93
static void * queue_get_first_elem(GSQueue *queue)
Definition gsqueue.c:42
void BLI_gsqueue_push(GSQueue *queue, const void *item)
Definition gsqueue.c:100
void BLI_gsqueue_pop(GSQueue *queue, void *r_item)
Definition gsqueue.c:134
GSQueue * BLI_gsqueue_new(const size_t elem_size)
Definition gsqueue.c:72
#define CHUNK_ELEM_MIN
Definition gsqueue.c:24
static void * queue_get_last_elem(GSQueue *queue)
Definition gsqueue.c:47
static void queue_free_chunk(struct QueueChunk *data)
Definition gsqueue.c:84
bool BLI_gsqueue_is_empty(const GSQueue *queue)
Definition gsqueue.c:162
static size_t queue_chunk_elem_max_calc(const size_t elem_size, size_t chunk_size)
Definition gsqueue.c:55
#define CHUNK_SIZE_DEFAULT
Definition gsqueue.c:22
size_t BLI_gsqueue_len(const GSQueue *queue)
Definition gsqueue.c:157
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
struct QueueChunk * next
Definition gsqueue.c:27
size_t elem_size
Definition gsqueue.c:38
struct QueueChunk * chunk_free
Definition gsqueue.c:34
size_t chunk_first_index
Definition gsqueue.c:35
size_t chunk_elem_max
Definition gsqueue.c:37
size_t chunk_last_index
Definition gsqueue.c:36
struct QueueChunk * chunk_last
Definition gsqueue.c:33
size_t elem_num
Definition gsqueue.c:39
struct QueueChunk * chunk_first
Definition gsqueue.c:32