Add generic intrusive FIFO queue
We need several FIFO queues (a queue of packets, a queue of messages, etc.). Some of them are implemented using cbuf, a generic circular buffer. But for recording, we need to store the packets in an unbounded queue until they are written, so the queue was implemented manually. Create a generic implementation (using macros) to avoid reimplementing it every time.
This commit is contained in:
parent
26213f1031
commit
53b6ee2cf4
3 changed files with 115 additions and 0 deletions
|
@ -150,6 +150,9 @@ tests = [
|
||||||
'tests/test_device_msg_deserialize.c',
|
'tests/test_device_msg_deserialize.c',
|
||||||
'src/device_msg.c'
|
'src/device_msg.c'
|
||||||
]],
|
]],
|
||||||
|
['test_queue', [
|
||||||
|
'tests/test_queue.c',
|
||||||
|
]],
|
||||||
['test_strutil', [
|
['test_strutil', [
|
||||||
'tests/test_strutil.c',
|
'tests/test_strutil.c',
|
||||||
'src/str_util.c'
|
'src/str_util.c'
|
||||||
|
|
74
app/src/queue.h
Normal file
74
app/src/queue.h
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
// generic intrusive FIFO queue
|
||||||
|
#ifndef QUEUE_H
|
||||||
|
#define QUEUE_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <SDL2/SDL_assert.h>
|
||||||
|
|
||||||
|
// To define a queue type of "struct foo":
|
||||||
|
// struct queue_foo QUEUE(struct foo);
|
||||||
|
#define QUEUE(TYPE) { \
|
||||||
|
TYPE *first; \
|
||||||
|
TYPE *last; \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define queue_init(PQ) \
|
||||||
|
(void) ((PQ)->first = NULL)
|
||||||
|
|
||||||
|
#define queue_is_empty(PQ) \
|
||||||
|
!(PQ)->first
|
||||||
|
|
||||||
|
// NEXTFIELD is the field in the ITEM type used for intrusive linked-list
|
||||||
|
//
|
||||||
|
// For example:
|
||||||
|
// struct foo {
|
||||||
|
// int value;
|
||||||
|
// struct foo *next;
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// // define the type "struct my_queue"
|
||||||
|
// struct my_queue QUEUE(struct foo);
|
||||||
|
//
|
||||||
|
// struct my_queue queue;
|
||||||
|
// queue_init(&queue);
|
||||||
|
//
|
||||||
|
// struct foo v1 = { .value = 42 };
|
||||||
|
// struct foo v2 = { .value = 27 };
|
||||||
|
//
|
||||||
|
// queue_push(&queue, next, v1);
|
||||||
|
// queue_push(&queue, next, v2);
|
||||||
|
//
|
||||||
|
// struct foo *foo;
|
||||||
|
// queue_take(&queue, next, &foo);
|
||||||
|
// assert(foo->value == 42);
|
||||||
|
// queue_take(&queue, next, &foo);
|
||||||
|
// assert(foo->value == 27);
|
||||||
|
// assert(queue_is_empty(&queue));
|
||||||
|
//
|
||||||
|
|
||||||
|
// push a new item into the queue
|
||||||
|
#define queue_push(PQ, NEXTFIELD, ITEM) \
|
||||||
|
(void) ({ \
|
||||||
|
(ITEM)->NEXTFIELD = NULL; \
|
||||||
|
if (queue_is_empty(PQ)) { \
|
||||||
|
(PQ)->first = (PQ)->last = (ITEM); \
|
||||||
|
} else { \
|
||||||
|
(PQ)->last->NEXTFIELD = (ITEM); \
|
||||||
|
(PQ)->last = (ITEM); \
|
||||||
|
} \
|
||||||
|
})
|
||||||
|
|
||||||
|
// take the next item and remove it from the queue (the queue must not be empty)
|
||||||
|
// the result is stored in *(PITEM)
|
||||||
|
// (without typeof(), we could not store a local variable having the correct
|
||||||
|
// type so that we can "return" it)
|
||||||
|
#define queue_take(PQ, NEXTFIELD, PITEM) \
|
||||||
|
(void) ({ \
|
||||||
|
SDL_assert(!queue_is_empty(PQ)); \
|
||||||
|
*(PITEM) = (PQ)->first; \
|
||||||
|
(PQ)->first = (PQ)->first->NEXTFIELD; \
|
||||||
|
})
|
||||||
|
// no need to update (PQ)->last if the queue is left empty:
|
||||||
|
// (PQ)->last is undefined if !(PQ)->first anyway
|
||||||
|
|
||||||
|
#endif
|
38
app/tests/test_queue.c
Normal file
38
app/tests/test_queue.c
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include <queue.h>
|
||||||
|
|
||||||
|
struct foo {
|
||||||
|
int value;
|
||||||
|
struct foo *next;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void test_queue(void) {
|
||||||
|
struct my_queue QUEUE(struct foo) queue;
|
||||||
|
queue_init(&queue);
|
||||||
|
|
||||||
|
assert(queue_is_empty(&queue));
|
||||||
|
|
||||||
|
struct foo v1 = { .value = 42 };
|
||||||
|
struct foo v2 = { .value = 27 };
|
||||||
|
|
||||||
|
queue_push(&queue, next, &v1);
|
||||||
|
queue_push(&queue, next, &v2);
|
||||||
|
|
||||||
|
struct foo *foo;
|
||||||
|
|
||||||
|
assert(!queue_is_empty(&queue));
|
||||||
|
queue_take(&queue, next, &foo);
|
||||||
|
assert(foo->value == 42);
|
||||||
|
|
||||||
|
assert(!queue_is_empty(&queue));
|
||||||
|
queue_take(&queue, next, &foo);
|
||||||
|
assert(foo->value == 27);
|
||||||
|
|
||||||
|
assert(queue_is_empty(&queue));
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
test_queue();
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
Reference in a new issue