// vim:fileencoding=utf-8:foldmethod=marker #include "queue.h" #include "../array/array.h" #include "../../common/assert/assert.h" #include "../../common/misc/misc_utils.h" #include void _queuePush(WpQueue *queue, void *item, u64 item_size) { wpDebugAssert(queue != NULL, "`queue` should not be NULL"); wpRuntimeAssert(item_size == wpArrayItemSize(queue->items), "Invalid type"); u64 capacity = wpArrayCapacity(queue->items); if (queue->count >= capacity) { return; } u64 index = (queue->back)++; _arraySet(queue->items, index, item, item_size); ++(queue->count); if (queue->back >= capacity) { queue->back = 0; } } WpQueue *_queuePushAlloc(const WpAllocator *allocator, WpQueue *queue, void *item, u64 item_size) { wpDebugAssert(allocator != NULL && queue != NULL && item != NULL, "`allocator`, `queue` and `item` should not be NULL"); wpRuntimeAssert(item_size == wpArrayItemSize(queue->items), "Invalid type"); WpQueue *output = queue; u64 capacity = wpArrayCapacity(queue->items); // NOTE (Abdelrahman): Extracted into variable to fix MSVC error b8 queue_full = queue->count >= capacity; if (queue_full) { u64 new_capacity = wpMiscUtilsU64RoundUpPow2(capacity * 2); u64 array_size = _arrayCalcAllocSize(new_capacity, item_size); u64 alloc_size = sizeof(WpQueue) + array_size; void *buffer = wpMemAllocatorAlloc(allocator, alloc_size); if (!buffer) { goto RETURN_QUEUE_PUSH_ALLOC; } memset((void *)buffer, 0, alloc_size); output = (WpQueue *)buffer; output->items = _arrayFromPreallocatedBuffer((void *)(output + 1), array_size, WP_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); /** * NOTE (Abdelrahman): Since this is a ring buffer, the elements at the beginning of the array * aren't always the ones at the front of the queue. When that's the case, the memcpy above * will only copy a subset of the elements. This is why we need to copy the remaining ones. * * Example: Take a queue that looks like this with a capacity of 5 elements * * 0 1 | 2 3 4 * ---------------|----------------------- * | * | * | * | * | * | * ---------------|----------------------- * | * queue_front = 2 * queue_back = 2 * * In this case, the first memcpy will only copy elements 2-4. The memcpy below will be * responsible for copying elements 0-1. */ b8 items_left_to_copy = back_count > 0; if (items_left_to_copy) { 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; } _queuePush(output, item, item_size); RETURN_QUEUE_PUSH_ALLOC: return output; } void *_queuePop(WpQueue *queue, u64 item_size) { wpDebugAssert(queue != NULL, "`queue` should not be NULL"); wpRuntimeAssert(item_size == wpArrayItemSize(queue->items), "Invalid type"); if (queue->count == 0) { return NULL; } u64 index = (queue->front)++; --(queue->count); u64 capacity = wpArrayCapacity(queue->items); if (queue->front >= capacity) { queue->front = 0; } return _arrayGet(queue->items, index, item_size); }