GCC Code Coverage Report


Directory: src/
File: src/support/queue_template.h
Date: 2024-11-22 17:07:10
Exec Total Coverage
Lines: 39 64 60.9%
Functions: 13 15 86.7%
Branches: 8 44 18.2%

Line Branch Exec Source
1 /*********************************************************************************/
2 /* Copyright 2009-2024 Barcelona Supercomputing Center */
3 /* */
4 /* This file is part of the DLB library. */
5 /* */
6 /* DLB is free software: you can redistribute it and/or modify */
7 /* it under the terms of the GNU Lesser General Public License as published by */
8 /* the Free Software Foundation, either version 3 of the License, or */
9 /* (at your option) any later version. */
10 /* */
11 /* DLB is distributed in the hope that it will be useful, */
12 /* but WITHOUT ANY WARRANTY; without even the implied warranty of */
13 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
14 /* GNU Lesser General Public License for more details. */
15 /* */
16 /* You should have received a copy of the GNU Lesser General Public License */
17 /* along with DLB. If not, see <https://www.gnu.org/licenses/>. */
18 /*********************************************************************************/
19
20 /* Credits: https://www.davidpriver.com/ctemplates.html */
21
22 /* Include this header multiple times to implement a fixed-size circular queue.
23 * Before inclusion define at least QUEUE_T and QUEUE_SIZE to the type the
24 * queue can hold.
25 *
26 * QUEUE_KEY_T can be defined to set the type of the primary key, which should
27 * be either a subtype of QUEUE_T if using structs, or QUEUE_T.
28 */
29
30 #ifndef QUEUE_TEMPLATE_H
31 #define QUEUE_TEMPLATE_H
32
33 #include "support/debug.h"
34
35 #include <stdlib.h>
36 #include <string.h>
37
38 #define QUEUE_IMPL(word) QUEUE_COMB1(QUEUE_PREFIX,word)
39 #define QUEUE_COMB1(pre, word) QUEUE_COMB2(pre, word)
40 #define QUEUE_COMB2(pre, word) pre##word
41
42 #endif /* QUEUE_TEMPLATE_H */
43
44 #ifndef QUEUE_T
45 #error "QUEUE_T must be defined"
46 #endif
47
48 #ifndef QUEUE_SIZE
49 #error "QUEUE_SIZE must be defined"
50 #endif
51
52 #ifndef QUEUE_KEY_T
53 #define QUEUE_KEY_T QUEUE_T
54 #endif
55
56 #define QUEUE_NAME QUEUE_COMB1(QUEUE_COMB1(queue,_), QUEUE_T)
57 #define QUEUE_PREFIX QUEUE_COMB1(QUEUE_NAME, _)
58
59 #define QUEUE_init QUEUE_IMPL(init)
60 #define QUEUE_clear QUEUE_IMPL(clear)
61 #define QUEUE_size QUEUE_IMPL(size)
62 #define QUEUE_empty QUEUE_IMPL(empty)
63 #define QUEUE_front QUEUE_IMPL(front)
64 #define QUEUE_back QUEUE_IMPL(back)
65 #define QUEUE_next QUEUE_IMPL(next)
66 #define QUEUE_enqueue QUEUE_IMPL(enqueue)
67 #define QUEUE_dequeue QUEUE_IMPL(dequeue)
68 #define QUEUE_delete QUEUE_IMPL(delete)
69 #define QUEUE_remove QUEUE_IMPL(remove)
70 #define QUEUE_replace QUEUE_IMPL(replace)
71 #define QUEUE_search QUEUE_IMPL(search)
72
73 /* Qeue template:
74 * - items is actually QUEUE_SIZE + 1 because we don't save rear, but next. FIXME
75 * (it simplifies some functions a little bit)
76 * - 'front' points to the first element
77 * - 'next' points to the next spot where to push
78 */
79
80 typedef struct QUEUE_NAME {
81 QUEUE_T items[QUEUE_SIZE];
82 size_t front; /* position to the first element */
83 size_t next; /* position to the last empty spot */
84 size_t capacity;
85 } QUEUE_NAME;
86
87
88 /* Initializers */
89
90 1124 static inline void QUEUE_init(QUEUE_NAME *queue) {
91 1124 *queue = (const QUEUE_NAME) {
92 .capacity = QUEUE_SIZE, /* mainly for debugging */
93 };
94 1124 }
95
96 460 static inline void QUEUE_clear(QUEUE_NAME *queue) {
97 460 queue->front = 0;
98 460 queue->next = 0;
99 460 }
100
101
102 /* Capacity */
103
104 318 static inline size_t QUEUE_size(const QUEUE_NAME *queue) {
105 318 return queue->next >= queue->front
106 318 ? queue->next - queue->front
107
1/2
✓ Branch 0 taken 159 times.
✗ Branch 1 not taken.
318 : QUEUE_SIZE - queue->front + queue->next;
108 }
109
110 850 static inline bool QUEUE_empty(const QUEUE_NAME *queue) {
111 850 return queue->front == queue->next;
112 }
113
114
115 /* Element access */
116
117 76 static inline QUEUE_T* QUEUE_front(QUEUE_NAME *queue) {
118
2/2
✓ Branch 1 taken 15 times.
✓ Branch 2 taken 23 times.
76 return QUEUE_empty(queue) ? NULL : &queue->items[queue->front];
119 }
120
121 static inline QUEUE_T* QUEUE_back(QUEUE_NAME *queue) {
122 size_t rear = queue->next > 0 ? queue->next-1 : QUEUE_SIZE-1;
123 return QUEUE_empty(queue) ? NULL : &queue->items[rear];
124 }
125
126 32 static inline QUEUE_T* QUEUE_next(QUEUE_NAME *queue, QUEUE_T *item) {
127
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 16 times.
32 if (QUEUE_empty(queue) ) {
128 return NULL;
129 }
130
1/2
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
32 else if (queue->next > queue->front) {
131 /* queue is linear */
132 32 size_t next_index = item + 1 - queue->items;
133
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 15 times.
32 return next_index < queue->next ? item + 1 : NULL;
134 }
135 else {
136 /* queue is circular */
137 size_t next_index = item + 1 < queue->items + QUEUE_SIZE
138 ? item + 1 - queue->items
139 : 0;
140 return next_index > queue->front || next_index < queue->next
141 ? &queue->items[next_index] : NULL;
142 }
143 }
144
145
146 /* Modifiers */
147
148 /* Enqueue item to the back of the queue */
149 10 static inline int QUEUE_enqueue(QUEUE_NAME *queue, QUEUE_T item) {
150 10 size_t new_next = (queue->next + 1) % QUEUE_SIZE;
151
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
10 if (unlikely(new_next == queue->front)) {
152 return -1;
153 }
154 10 queue->items[queue->next] = item;
155 10 queue->next = new_next;
156 10 return 0;
157 }
158
159 /* Dequeue item from the front of the queue */
160 static inline int QUEUE_dequeue(QUEUE_NAME *queue, QUEUE_T *item) {
161 if (QUEUE_empty(queue)) {
162 return -1;
163 }
164 *item = queue->items[queue->front];
165 queue->front = (queue->front + 1) % QUEUE_SIZE;
166 return 0;
167 }
168
169 /* Delete element pointed by argument */
170 static inline void QUEUE_delete(QUEUE_NAME *queue, const QUEUE_T *item) {
171 if (QUEUE_empty(queue)) {
172 return;
173 }
174
175 if (item == &queue->items[queue->front]) {
176 /* item is first element, just advance front */
177 queue->front = (queue->front + 1) % QUEUE_SIZE;
178 return;
179 }
180
181 size_t array_index = item - queue->items;
182
183 // FIXME: more efficient?
184 if (unlikely(
185 /* queue is linear */
186 (queue->next > queue->front
187 && (array_index < queue->front || array_index >= queue->next))
188 ||
189 /* queue is circular */
190 (queue->next <= queue->front
191 && (array_index >= queue->next && array_index < queue->front))
192 )) {
193 /* out of bounds */
194 return;
195 }
196
197 size_t write_index = array_index;
198 for (size_t read_index = (array_index + 1) % QUEUE_SIZE;
199 read_index != queue->next;
200 read_index = (read_index + 1) % QUEUE_SIZE) {
201 /* move items forward in the queue */
202 queue->items[write_index] = queue->items[read_index];
203 write_index = (write_index + 1) % QUEUE_SIZE;
204 }
205 queue->next = write_index;
206 }
207
208 /* Remove all items that match with key */
209 184 static inline void QUEUE_remove(QUEUE_NAME *queue, QUEUE_KEY_T key) {
210
211 /* advance front for every first item that matches key */
212 187 while (!QUEUE_empty(queue)
213 187 && memcmp(&queue->items[queue->front], &key, sizeof(QUEUE_KEY_T)) == 0) {
214 3 queue->front = (queue->front + 1) % QUEUE_SIZE;
215 }
216
217 184 if (QUEUE_empty(queue)) {
218 181 return;
219 }
220
221 /* front does not match, iterate from 2nd item */
222 3 size_t write_index = (queue->front + 1) % QUEUE_SIZE;
223 3 for (size_t read_index = write_index;
224 4 read_index != queue->next;
225 1 read_index = (read_index + 1) % QUEUE_SIZE) {
226 1 if (read_index != write_index) {
227 /* move items forward in the queue */
228 queue->items[write_index] = queue->items[read_index];
229 }
230 1 if (memcmp(&queue->items[read_index], &key, sizeof(QUEUE_KEY_T)) != 0) {
231 /* write_index only advances when element is not to be removed */
232 write_index = (write_index + 1) % QUEUE_SIZE;
233 }
234 }
235 3 queue->next = write_index;
236 }
237
238 /* Replace the first item that matches by key */
239 static inline void QUEUE_replace(QUEUE_NAME *queue, QUEUE_T item) {
240 for (size_t index = queue->front;
241 index != queue->next;
242 index = (index + 1) % QUEUE_SIZE) {
243 if (memcmp(&queue->items[index], &item, sizeof(QUEUE_KEY_T)) == 0) {
244 queue->items[index] = item;
245 return;
246 }
247 }
248 }
249
250 /* Return reference to the first item that matches by key */
251 static inline QUEUE_T* QUEUE_search(QUEUE_NAME *queue, QUEUE_KEY_T key) {
252 for (size_t index = queue->front;
253 index != queue->next;
254 index = (index + 1) % QUEUE_SIZE) {
255 if (memcmp(&queue->items[index], &key, sizeof(QUEUE_KEY_T)) == 0) {
256 return &queue->items[index];
257 }
258 }
259 return NULL;
260 }
261
262 #undef QUEUE_T
263 #undef QUEUE_SIZE
264 #undef QUEUE_KEY_T
265 #undef QUEUE_PREFIX
266 #undef QUEUE_NAME
267 #undef QUEUE_init
268 #undef QUEUE_clear
269 #undef QUEUE_size
270 #undef QUEUE_empty
271 #undef QUEUE_front
272 #undef QUEUE_back
273 #undef QUEUE_next
274 #undef QUEUE_enqueue
275 #undef QUEUE_dequeue
276 #undef QUEUE_delete
277 #undef QUEUE_remove
278 #undef QUEUE_replace
279 #undef QUEUE_search
280