From 3d3452f523f5ec7a736b311742d2dbcc30e0caad Mon Sep 17 00:00:00 2001 From: Abdelrahman Said Date: Sat, 24 Jan 2026 20:46:05 +0000 Subject: [PATCH] Update queue implementation to use ring buffer --- src/base/queue/queue.c | 85 ++++++++++++++++++++++---- src/base/queue/queue.h | 45 ++++++++++---- tests/queue/test_queue.c | 125 ++++++++++++++++++++++---------------- tests/queue/test_queue.cc | 125 ++++++++++++++++++++++---------------- tests/queue/test_queue.h | 5 +- tests/wapptest.c | 5 +- tests/wapptest.cc | 5 +- 7 files changed, 261 insertions(+), 134 deletions(-) diff --git a/src/base/queue/queue.c b/src/base/queue/queue.c index 8f85393..2527248 100644 --- a/src/base/queue/queue.c +++ b/src/base/queue/queue.c @@ -3,24 +3,83 @@ #include "queue.h" #include "../array/array.h" #include "../../common/assert/assert.h" +#include "../../common/misc/misc_utils.h" #include -void _queue_pop_front(GenericQueue *queue, void *output, u64 item_size) { - wapp_debug_assert(queue != NULL && output != NULL, "`queue` and `output` should not be NULL"); +void _queue_push(GenericQueue *queue, void *item, u64 item_size) { + wapp_debug_assert(queue != NULL, "`queue` should not be NULL"); wapp_runtime_assert(item_size == wapp_array_item_size(queue->items), "Invalid type"); - memcpy(output, queue->items, item_size); + u64 capacity = wapp_array_capacity(queue->items); + if (queue->count >= capacity) { return; } - // NOTE (Abdelrahman): Uses a while loop instead of a for loop to get rid of - // MSVC Spectre mitigation warnings - u64 new_count = wapp_array_count(queue->items) - 1; - u64 index = 0; - b8 running = index < new_count; - while (running) { - _array_set(queue->items, index, _array_get(queue->items, index + 1, item_size), item_size); - ++index; - running = index < new_count; + u64 index = (queue->back)++; + _array_set(queue->items, index, item, item_size); + ++(queue->count); + + if (queue->back >= capacity) { + queue->back = 0; + } +} + +GenericQueue *_queue_push_alloc(const Allocator *allocator, GenericQueue *queue, void *item, u64 item_size) { + wapp_debug_assert(allocator != NULL && queue != NULL && item != NULL, + "`allocator`, `queue` and `item` should not be NULL"); + wapp_runtime_assert(item_size == wapp_array_item_size(queue->items), "Invalid type"); + + GenericQueue *output = queue; + + u64 capacity = wapp_array_capacity(queue->items); + if (queue->count >= capacity) { + u64 new_capacity = wapp_misc_utils_u64_round_up_pow2(capacity * 2); + u64 array_size = _array_calc_alloc_size(new_capacity, item_size); + u64 alloc_size = sizeof(GenericQueue) + array_size; + void *buffer = wapp_mem_allocator_alloc(allocator, alloc_size); + if (!buffer) { + goto RETURN_QUEUE_PUSH_ALLOC; + } + + memset((void *)buffer, 0, alloc_size); + + output = (GenericQueue *)buffer; + output->items = _array_from_preallocated_buffer((void *)(output + 1), array_size, ARRAY_INIT_FILLED, item_size); + + // NOTE (Abdelrahman): When the queue is full, the front and back indices should + // always be the same + u64 front_count = capacity - queue->front; + u64 back_count = queue->back; + void *copy_boundary = (void *)((uptr)(queue->items) + (queue->front * item_size)); + + memcpy(output->items, copy_boundary, front_count * item_size); + if (back_count > 0) { + void *back_copy_dst = (void *)((uptr)(output->items) + (front_count * item_size)); + memcpy(back_copy_dst, queue->items, back_count * item_size); + } + + output->front = 0; + output->back = front_count + back_count; + output->count = queue->count; } - wapp_array_set_count(queue->items, new_count); + _queue_push(output, item, item_size); + +RETURN_QUEUE_PUSH_ALLOC: + return output; +} + +void *_queue_pop(GenericQueue *queue, u64 item_size) { + wapp_debug_assert(queue != NULL, "`queue` should not be NULL"); + wapp_runtime_assert(item_size == wapp_array_item_size(queue->items), "Invalid type"); + + if (queue->count == 0) { return NULL; } + + u64 index = (queue->front)++; + --(queue->count); + + u64 capacity = wapp_array_capacity(queue->items); + if (queue->front >= capacity) { + queue->front = 0; + } + + return _array_get(queue->items, index, item_size); } diff --git a/src/base/queue/queue.h b/src/base/queue/queue.h index 5dc189e..07e4a9d 100644 --- a/src/base/queue/queue.h +++ b/src/base/queue/queue.h @@ -4,6 +4,7 @@ #define QUEUE_H #include "../array/array.h" +#include "../mem/allocator/mem_allocator.h" #include "../../common/aliases/aliases.h" #include "../../common/platform/platform.h" @@ -13,6 +14,9 @@ BEGIN_C_LINKAGE typedef struct { GenericArray items; + u64 front; + u64 back; + u64 count; } GenericQueue; // NOTE (Abdelrahman): GenericQueue typedefs for readability @@ -38,41 +42,56 @@ typedef GenericQueue Str8Queue; #ifdef WAPP_PLATFORM_CPP #define wapp_queue(TYPE, CAPACITY) ([&]() { \ - wapp_persist GenericArray arr = wapp_array_with_capacity(TYPE, CAPACITY, ARRAY_INIT_NONE); \ - wapp_persist GenericQueue queue = {arr}; \ + wapp_persist GenericArray arr = wapp_array_with_capacity(TYPE, CAPACITY, ARRAY_INIT_FILLED); \ + wapp_persist GenericQueue queue = { \ + arr, \ + 0, \ + 0, \ + 0, \ + }; \ \ return queue; \ }()) #define wapp_queue_alloc(TYPE, ALLOCATOR_PTR, CAPACITY) ([&]() { \ wapp_persist GenericQueue queue = { \ - wapp_array_alloc_capacity(TYPE, ALLOCATOR_PTR, CAPACITY, ARRAY_INIT_NONE) \ + wapp_array_alloc_capacity(TYPE, ALLOCATOR_PTR, CAPACITY, ARRAY_INIT_FILLED) \ + 0, \ + 0, \ + 0, \ }; \ \ return queue; \ }()) #else #define wapp_queue(TYPE, CAPACITY) ((GenericQueue){ \ - .items = wapp_array_with_capacity(TYPE, CAPACITY, ARRAY_INIT_NONE) \ + .items = wapp_array_with_capacity(TYPE, CAPACITY, ARRAY_INIT_FILLED), \ + .front = 0, \ + .back = 0, \ + .count = 0, \ }) #define wapp_queue_alloc(TYPE, ALLOCATOR_PTR, CAPACITY) ((GenericQueue){ \ - .items = wapp_array_alloc_capacity(TYPE, ALLOCATOR_PTR, CAPACITY, ARRAY_INIT_NONE) \ + .items = wapp_array_alloc_capacity(TYPE, ALLOCATOR_PTR, CAPACITY, ARRAY_INIT_FILLED), \ + .front = 0, \ + .back = 0, \ + .count = 0, \ }) #endif // !WAPP_PLATFORM_CPP -#define wapp_queue_count(QUEUE_PTR) (wapp_array_count((QUEUE_PTR)->items)) #define wapp_queue_capacity(QUEUE_PTR) (wapp_array_capacity((QUEUE_PTR)->items)) #define wapp_queue_item_size(QUEUE_PTR) (wapp_array_item_size((QUEUE_PTR)->items)) -#define wapp_queue_push_back(TYPE, QUEUE_PTR, VALUE_PTR) ( \ - wapp_array_append_capped(TYPE, (QUEUE_PTR)->items, VALUE_PTR) \ +#define wapp_queue_push(TYPE, QUEUE_PTR, VALUE_PTR) ( \ + _queue_push(QUEUE_PTR, VALUE_PTR, sizeof(TYPE)) \ ) -#define wapp_queue_push_back_alloc(TYPE, ALLOCATOR_PTR, QUEUE_PTR, VALUE_PTR) ( \ - wapp_array_append_alloc(TYPE, ALLOCATOR_PTR, (QUEUE_PTR)->items, VALUE_PTR) \ +#define wapp_queue_push_alloc(TYPE, ALLOCATOR_PTR, QUEUE_PTR, VALUE_PTR) ( \ + _queue_push_alloc(ALLOCATOR_PTR, QUEUE_PTR, VALUE_PTR, sizeof(TYPE)) \ ) -#define wapp_queue_pop_front(TYPE, QUEUE_PTR, OUTPUT_PTR) ( \ - _queue_pop_front(QUEUE_PTR, OUTPUT_PTR, sizeof(TYPE)) \ +#define wapp_queue_pop(TYPE, QUEUE_PTR) ( \ + (TYPE *)_queue_pop(QUEUE_PTR, sizeof(TYPE)) \ ) -void _queue_pop_front(GenericQueue *queue, void *output, u64 item_size); +void _queue_push(GenericQueue *queue, void *item, u64 item_size); +GenericQueue *_queue_push_alloc(const Allocator *allocator, GenericQueue *queue, void *item, u64 item_size); +void *_queue_pop(GenericQueue *queue, u64 item_size); #ifdef WAPP_PLATFORM_CPP END_C_LINKAGE diff --git a/tests/queue/test_queue.c b/tests/queue/test_queue.c index 3c3908a..5dbe5df 100644 --- a/tests/queue/test_queue.c +++ b/tests/queue/test_queue.c @@ -3,73 +3,96 @@ #include "wapp.h" #include "test_queue.h" -#define SKIP 1 +#define CAPACITY 8 -#if !SKIP -wapp_persist Allocator arena = {0}; -wapp_persist I32Queue queue; -#endif +TestFuncResult test_queue_push(void) { + I32Queue queue = wapp_queue(i32, CAPACITY); -TestFuncResult test_queue_push_back(void) { -#if !SKIP - arena = wapp_mem_arena_allocator_init(MB(64)); + for (u64 i = 0; i < CAPACITY; ++i) { + i32 item = (i32)i; + wapp_queue_push(i32, &queue, &item); + } b8 result = true; - queue = wapp_queue_alloc(i32, &arena, 64); - - result = result && queue.items != NULL && wapp_queue_capacity(&queue) == 64 && wapp_queue_count(&queue) == 0; - - i32 n1 = 1; - i32 n2 = 2; - i32 n3 = 3; - i32 n4 = 4; - i32 n5 = 5; - - wapp_queue_push_back(i32, &queue, &n1); - result = result && wapp_queue_count(&queue) == 1; - - wapp_queue_push_back(i32, &queue, &n2); - result = result && wapp_queue_count(&queue) == 2; - - wapp_queue_push_back(i32, &queue, &n3); - result = result && wapp_queue_count(&queue) == 3; - - wapp_queue_push_back(i32, &queue, &n4); - result = result && wapp_queue_count(&queue) == 4; - - wapp_queue_push_back(i32, &queue, &n5); - result = result && wapp_queue_count(&queue) == 5; + for (u64 i = 0; i < CAPACITY; ++i) { + i32 *value = ((i32 *)(queue.items)) + i; + result = result && value != NULL && *value == (i32)i; + } return wapp_tester_result(result); -#else - return wapp_tester_result(true); -#endif } -TestFuncResult test_queue_pop_front(void) { -#if !SKIP +TestFuncResult test_queue_push_alloc(void) { + Allocator arena = wapp_mem_arena_allocator_init(MiB(64)); + I32Queue queue = wapp_queue(i32, CAPACITY); + + for (u64 i = 0; i < CAPACITY; ++i) { + i32 item = (i32)i; + wapp_queue_push(i32, &queue, &item); + } + b8 result = true; - i32 num; + i32 item = 8; + u64 new_capacity = CAPACITY * 2; + I32Queue *new_queue = wapp_queue_push_alloc(i32, &arena, &queue, &item); + if (new_queue && new_queue != &queue) { + queue = *new_queue; + } + u64 capacity = wapp_queue_capacity(&queue); + result = result && capacity == new_capacity; - wapp_queue_pop_front(i32, &queue, &num); - result = result && num == 1; + for (u64 i = 0; i < 2; ++i) { + wapp_queue_pop(i32, &queue); + } - wapp_queue_pop_front(i32, &queue, &num); - result = result && num == 2; + u64 remaining = wapp_queue_capacity(&queue) - queue.count; + for (u64 i = 0; i < remaining; ++i) { + item = remaining + i; + wapp_queue_push(i32, &queue, &item); + } - wapp_queue_pop_front(i32, &queue, &num); - result = result && num == 3; + ++item; + new_queue = wapp_queue_push_alloc(i32, &arena, &queue, &item); + if (new_queue && new_queue != &queue) { + queue = *new_queue; + } - wapp_queue_pop_front(i32, &queue, &num); - result = result && num == 4; + result = result && wapp_queue_capacity(&queue) == new_capacity * 2; - wapp_queue_pop_front(i32, &queue, &num); - result = result && num == 5; + i32 *arr = (i32 *)queue.items; + for (u64 i = 0; i < queue.count; ++i) { + // NOTE (Abdelrahman): First queue value is currently 2 + result = result && arr[i] == (i32)(2 + i); + } wapp_mem_arena_allocator_destroy(&arena); return wapp_tester_result(result); -#else - return wapp_tester_result(true); -#endif +} + +TestFuncResult test_queue_pop(void) { + I32Queue queue = wapp_queue(i32, CAPACITY); + for (u64 i = 0; i < CAPACITY; ++i) { + i32 item = (i32)i; + wapp_queue_push(i32, &queue, &item); + } + + b8 result = true; + u64 half_count = queue.count / 2; + for (u64 i = 0; i < half_count; ++i) { + i32 *value = wapp_queue_pop(i32, &queue); + result = result && value != NULL && *value == (i32)i; + } + + for (u64 i = 0; i < half_count; ++i) { + i32 item = (i32)i + CAPACITY; + wapp_queue_push(i32, &queue, &item); + } + + for (u64 i = 0; i < CAPACITY; ++i) { + i32 *value = wapp_queue_pop(i32, &queue); + result = result && value != NULL && *value == (i32)half_count + (i32)i; + } + + return wapp_tester_result(result); } diff --git a/tests/queue/test_queue.cc b/tests/queue/test_queue.cc index a5922e4..860801d 100644 --- a/tests/queue/test_queue.cc +++ b/tests/queue/test_queue.cc @@ -1,73 +1,96 @@ #include "wapp.h" #include "test_queue.h" -#define SKIP 1 +#define CAPACITY 8 -#if !SKIP -wapp_persist Allocator arena = {}; -wapp_persist I32Queue queue; -#endif +TestFuncResult test_queue_push(void) { + I32Queue queue = wapp_queue(i32, CAPACITY); -TestFuncResult test_queue_push_back(void) { -#if !SKIP - arena = wapp_mem_arena_allocator_init(MB(64)); + for (u64 i = 0; i < CAPACITY; ++i) { + i32 item = (i32)i; + wapp_queue_push(i32, &queue, &item); + } b8 result = true; - queue = wapp_queue_alloc(i32, &arena, 64); - - result = result && queue.items != NULL && wapp_queue_capacity(&queue) == 64 && wapp_queue_count(&queue) == 0; - - i32 n1 = 1; - i32 n2 = 2; - i32 n3 = 3; - i32 n4 = 4; - i32 n5 = 5; - - wapp_queue_push_back(i32, &queue, &n1); - result = result && wapp_queue_count(&queue) == 1; - - wapp_queue_push_back(i32, &queue, &n2); - result = result && wapp_queue_count(&queue) == 2; - - wapp_queue_push_back(i32, &queue, &n3); - result = result && wapp_queue_count(&queue) == 3; - - wapp_queue_push_back(i32, &queue, &n4); - result = result && wapp_queue_count(&queue) == 4; - - wapp_queue_push_back(i32, &queue, &n5); - result = result && wapp_queue_count(&queue) == 5; + for (u64 i = 0; i < CAPACITY; ++i) { + i32 *value = ((i32 *)(queue.items)) + i; + result = result && value != NULL && *value == (i32)i; + } return wapp_tester_result(result); -#else - return wapp_tester_result(true); -#endif } -TestFuncResult test_queue_pop_front(void) { -#if !SKIP +TestFuncResult test_queue_push_alloc(void) { + Allocator arena = wapp_mem_arena_allocator_init(MiB(64)); + I32Queue queue = wapp_queue(i32, CAPACITY); + + for (u64 i = 0; i < CAPACITY; ++i) { + i32 item = (i32)i; + wapp_queue_push(i32, &queue, &item); + } + b8 result = true; - i32 num; + i32 item = 8; + u64 new_capacity = CAPACITY * 2; + I32Queue *new_queue = wapp_queue_push_alloc(i32, &arena, &queue, &item); + if (new_queue && new_queue != &queue) { + queue = *new_queue; + } + u64 capacity = wapp_queue_capacity(&queue); + result = result && capacity == new_capacity; - wapp_queue_pop_front(i32, &queue, &num); - result = result && num == 1; + for (u64 i = 0; i < 2; ++i) { + wapp_queue_pop(i32, &queue); + } - wapp_queue_pop_front(i32, &queue, &num); - result = result && num == 2; + u64 remaining = wapp_queue_capacity(&queue) - queue.count; + for (u64 i = 0; i < remaining; ++i) { + item = remaining + i; + wapp_queue_push(i32, &queue, &item); + } - wapp_queue_pop_front(i32, &queue, &num); - result = result && num == 3; + ++item; + new_queue = wapp_queue_push_alloc(i32, &arena, &queue, &item); + if (new_queue && new_queue != &queue) { + queue = *new_queue; + } - wapp_queue_pop_front(i32, &queue, &num); - result = result && num == 4; + result = result && wapp_queue_capacity(&queue) == new_capacity * 2; - wapp_queue_pop_front(i32, &queue, &num); - result = result && num == 5; + i32 *arr = (i32 *)queue.items; + for (u64 i = 0; i < queue.count; ++i) { + // NOTE (Abdelrahman): First queue value is currently 2 + result = result && arr[i] == (i32)(2 + i); + } wapp_mem_arena_allocator_destroy(&arena); return wapp_tester_result(result); -#else - return wapp_tester_result(true); -#endif +} + +TestFuncResult test_queue_pop(void) { + I32Queue queue = wapp_queue(i32, CAPACITY); + for (u64 i = 0; i < CAPACITY; ++i) { + i32 item = (i32)i; + wapp_queue_push(i32, &queue, &item); + } + + b8 result = true; + u64 half_count = queue.count / 2; + for (u64 i = 0; i < half_count; ++i) { + i32 *value = wapp_queue_pop(i32, &queue); + result = result && value != NULL && *value == (i32)i; + } + + for (u64 i = 0; i < half_count; ++i) { + i32 item = (i32)i + CAPACITY; + wapp_queue_push(i32, &queue, &item); + } + + for (u64 i = 0; i < CAPACITY; ++i) { + i32 *value = wapp_queue_pop(i32, &queue); + result = result && value != NULL && *value == (i32)half_count + (i32)i; + } + + return wapp_tester_result(result); } diff --git a/tests/queue/test_queue.h b/tests/queue/test_queue.h index 15443c5..32382e2 100644 --- a/tests/queue/test_queue.h +++ b/tests/queue/test_queue.h @@ -5,7 +5,8 @@ #include "wapp.h" -TestFuncResult test_queue_push_back(void); -TestFuncResult test_queue_pop_front(void); +TestFuncResult test_queue_push(void); +TestFuncResult test_queue_push_alloc(void); +TestFuncResult test_queue_pop(void); #endif // !TEST_QUEUE_H diff --git a/tests/wapptest.c b/tests/wapptest.c index 24b6eb4..0846b98 100644 --- a/tests/wapptest.c +++ b/tests/wapptest.c @@ -44,8 +44,9 @@ int main(void) { test_i32_array_copy_alloc, test_i32_array_pop, test_i32_array_clear, - test_queue_push_back, - test_queue_pop_front, + test_queue_push, + test_queue_push_alloc, + test_queue_pop, test_str8_lit, test_str8_lit_ro, test_str8_buf, diff --git a/tests/wapptest.cc b/tests/wapptest.cc index 24b6eb4..0846b98 100644 --- a/tests/wapptest.cc +++ b/tests/wapptest.cc @@ -44,8 +44,9 @@ int main(void) { test_i32_array_copy_alloc, test_i32_array_pop, test_i32_array_clear, - test_queue_push_back, - test_queue_pop_front, + test_queue_push, + test_queue_push_alloc, + test_queue_pop, test_str8_lit, test_str8_lit_ro, test_str8_buf,